#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Parameters; using HEAL.Attic; namespace HeuristicLab.Problems.DataAnalysis.Symbolic { [StorableType("DFA06F28-E224-4D93-9907-69792D24D1F9")] [Item("SymbolicDataAnalysisExpressionCompiledTreeInterpreter", "Interpreter that converts the tree into a Linq.Expression then compiles it.")] public sealed class SymbolicDataAnalysisExpressionCompiledTreeInterpreter : ParameterizedNamedItem, ISymbolicDataAnalysisExpressionTreeInterpreter { private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic"; private const string CheckExpressionsWithIntervalArithmeticParameterDescription = "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression."; private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions"; #region method info for the commonly called functions private static readonly MethodInfo Abs = typeof(Math).GetMethod("Abs", new[] { typeof(double) }); private static readonly MethodInfo Sin = typeof(Math).GetMethod("Sin", new[] { typeof(double) }); private static readonly MethodInfo Cos = typeof(Math).GetMethod("Cos", new[] { typeof(double) }); private static readonly MethodInfo Tan = typeof(Math).GetMethod("Tan", new[] { typeof(double) }); private static readonly MethodInfo Tanh = typeof(Math).GetMethod("Tanh", new[] { typeof(double) }); private static readonly MethodInfo Sqrt = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) }); private static readonly MethodInfo Floor = typeof(Math).GetMethod("Floor", new[] { typeof(double) }); private static readonly MethodInfo Round = typeof(Math).GetMethod("Round", new[] { typeof(double) }); private static readonly MethodInfo Exp = typeof(Math).GetMethod("Exp", new[] { typeof(double) }); private static readonly MethodInfo Log = typeof(Math).GetMethod("Log", new[] { typeof(double) }); private static readonly MethodInfo IsNaN = typeof(double).GetMethod("IsNaN"); private static readonly MethodInfo IsAlmost = typeof(DoubleExtensions).GetMethod("IsAlmost"); private static readonly MethodInfo Gamma = typeof(alglib).GetMethod("gammafunction", new[] { typeof(double) }); private static readonly MethodInfo Psi = typeof(alglib).GetMethod("psi", new[] { typeof(double) }); private static readonly MethodInfo DawsonIntegral = typeof(alglib).GetMethod("dawsonintegral", new[] { typeof(double) }); private static readonly MethodInfo ExponentialIntegralEi = typeof(alglib).GetMethod("exponentialintegralei", new[] { typeof(double) }); private static readonly MethodInfo SineCosineIntegrals = typeof(alglib).GetMethod("sinecosineintegrals", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() }); private static readonly MethodInfo HyperbolicSineCosineIntegrals = typeof(alglib).GetMethod("hyperbolicsinecosineintegrals", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() }); private static readonly MethodInfo FresnelIntegral = typeof(alglib).GetMethod("fresnelintegral", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() }); private static readonly MethodInfo Airy = typeof(alglib).GetMethod("airy", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType(), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() }); private static readonly MethodInfo NormalDistribution = typeof(alglib).GetMethod("normaldistribution", new[] { typeof(double) }); private static readonly MethodInfo ErrorFunction = typeof(alglib).GetMethod("errorfunction", new[] { typeof(double) }); private static readonly MethodInfo Bessel = typeof(alglib).GetMethod("besseli0", new[] { typeof(double) }); #endregion public override bool CanChangeName { get { return false; } } public override bool CanChangeDescription { get { return false; } } #region parameter properties public IFixedValueParameter CheckExpressionsWithIntervalArithmeticParameter { get { return (IFixedValueParameter)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; } } public IFixedValueParameter EvaluatedSolutionsParameter { get { return (IFixedValueParameter)Parameters[EvaluatedSolutionsParameterName]; } } #endregion #region properties public bool CheckExpressionsWithIntervalArithmetic { get { return CheckExpressionsWithIntervalArithmeticParameter.Value.Value; } set { CheckExpressionsWithIntervalArithmeticParameter.Value.Value = value; } } public int EvaluatedSolutions { get { return EvaluatedSolutionsParameter.Value.Value; } set { EvaluatedSolutionsParameter.Value.Value = value; } } #endregion public override IDeepCloneable Clone(Cloner cloner) { return new SymbolicDataAnalysisExpressionCompiledTreeInterpreter(this, cloner); } private SymbolicDataAnalysisExpressionCompiledTreeInterpreter(SymbolicDataAnalysisExpressionCompiledTreeInterpreter original, Cloner cloner) : base(original, cloner) { } [StorableConstructor] private SymbolicDataAnalysisExpressionCompiledTreeInterpreter(StorableConstructorFlag _) : base(_) { } public SymbolicDataAnalysisExpressionCompiledTreeInterpreter() : base("SymbolicDataAnalysisExpressionCompiledTreeInterpreter", "Interpreter which compiles the tree into a lambda") { Parameters.Add(new FixedValueParameter(CheckExpressionsWithIntervalArithmeticParameterName, CheckExpressionsWithIntervalArithmeticParameterDescription, new BoolValue(false))); Parameters.Add(new FixedValueParameter(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0))); } public SymbolicDataAnalysisExpressionCompiledTreeInterpreter(string name, string description) : base(name, description) { Parameters.Add(new FixedValueParameter(CheckExpressionsWithIntervalArithmeticParameterName, CheckExpressionsWithIntervalArithmeticParameterDescription, new BoolValue(false))); Parameters.Add(new FixedValueParameter(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0))); } public void InitializeState() { EvaluatedSolutions = 0; } public void ClearState() { } private readonly object syncRoot = new object(); public IEnumerable GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset, IEnumerable rows) { if (CheckExpressionsWithIntervalArithmetic) throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter."); lock (syncRoot) { EvaluatedSolutions++; // increment the evaluated solutions counter } var columns = dataset.DoubleVariables.Select(x => (IList)dataset.GetReadOnlyDoubleValues(x)).ToArray(); var compiled = CompileTree(tree, dataset); return rows.Select(x => compiled(x, columns)); } public static Func[], double> CompileTree(ISymbolicExpressionTree tree, IDataset dataset) { var lambda = CreateDelegate(tree, dataset); return lambda.Compile(); } public static Expression[], double>> CreateDelegate(ISymbolicExpressionTree tree, IDataset dataset) { var row = Expression.Parameter(typeof(int)); var columns = Expression.Parameter(typeof(IList[])); var variableIndices = dataset.DoubleVariables.Select((x, i) => new { x, i }).ToDictionary(e => e.x, e => e.i); var expr = MakeExpr(tree, variableIndices, row, columns); var lambda = Expression.Lambda[], double>>(expr, row, columns); return lambda; } private static Expression MakeExpr(ISymbolicExpressionTree tree, Dictionary variableIndices, Expression row, Expression columns) { var actualRoot = tree.Root.GetSubtree(0).GetSubtree(0); return MakeExpr(actualRoot, variableIndices, row, columns); } private static readonly PropertyInfo Indexer = typeof(IList).GetProperty("Item"); private static Expression MakeExpr(ISymbolicExpressionTreeNode node, Dictionary variableIndices, Expression row, Expression columns) { var opcode = OpCode.MapSymbolToOpCode(node); #region switch opcode switch (opcode) { case OpCode.Constant: { var constantTreeNode = (ConstantTreeNode)node; return Expression.Constant(constantTreeNode.Value); } case OpCode.Variable: { var variableTreeNode = (VariableTreeNode)node; var variableWeight = Expression.Constant(variableTreeNode.Weight); var variableName = variableTreeNode.VariableName; var indexExpr = Expression.Constant(variableIndices[variableName]); var valuesExpr = Expression.ArrayIndex(columns, indexExpr); return Expression.Multiply(variableWeight, Expression.Property(valuesExpr, Indexer, row)); } case OpCode.Add: { Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); for (int i = 1; i < node.SubtreeCount; ++i) { result = Expression.Add(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)); } return result; } case OpCode.Sub: { Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); if (node.SubtreeCount == 1) return Expression.Negate(result); for (int i = 1; i < node.SubtreeCount; ++i) { result = Expression.Subtract(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)); } return result; } case OpCode.Mul: { Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); for (int i = 1; i < node.SubtreeCount; ++i) { result = Expression.Multiply(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)); } return result; } case OpCode.Div: { Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); if (node.SubtreeCount == 1) return Expression.Divide(Expression.Constant(1.0), result); for (int i = 1; i < node.SubtreeCount; ++i) { result = Expression.Divide(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)); } return result; } case OpCode.Average: { Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); for (int i = 1; i < node.SubtreeCount; ++i) { result = Expression.Add(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)); } return Expression.Divide(result, Expression.Constant((double)node.SubtreeCount)); } case OpCode.Absolute: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Abs, arg); } case OpCode.Cos: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Cos, arg); } case OpCode.Sin: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Sin, arg); } case OpCode.Tan: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Tan, arg); } case OpCode.Tanh: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Tanh, arg); } case OpCode.Square: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Power(arg, Expression.Constant(2.0)); } case OpCode.Cube: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Power(arg, Expression.Constant(3.0)); } case OpCode.Power: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var power = MakeExpr(node.GetSubtree(1), variableIndices, row, columns); return Expression.Power(arg, Expression.Call(Round, power)); } case OpCode.SquareRoot: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Sqrt, arg); } case OpCode.CubeRoot: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Power(arg, Expression.Constant(1.0 / 3.0)); } case OpCode.Root: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var power = MakeExpr(node.GetSubtree(1), variableIndices, row, columns); return Expression.Power(arg, Expression.Divide(Expression.Constant(1.0), Expression.Call(Round, power))); } case OpCode.Exp: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Exp, arg); } case OpCode.Log: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); return Expression.Call(Log, arg); } case OpCode.Gamma: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var result = Expression.Variable(typeof(double)); var expr = Expression.Block( new[] { result }, Expression.IfThenElse( isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, Expression.Call(Gamma, arg)) ), result ); return expr; } case OpCode.Psi: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var result = Expression.Variable(typeof(double)); var floor = Expression.Call(Floor, arg); var expr = Expression.Block( new[] { result }, Expression.IfThenElse( isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.IfThenElse( Expression.AndAlso( Expression.LessThanOrEqual(arg, Expression.Constant(0.0)), Expression.Call(IsAlmost, Expression.Subtract(floor, arg), Expression.Constant(0.0))), Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, Expression.Call(Psi, arg))) ), result); return expr; } case OpCode.Dawson: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var result = Expression.Variable(typeof(double)); var expr = Expression.Block( new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, Expression.Call(DawsonIntegral, arg))), result ); return expr; } case OpCode.ExponentialIntegralEi: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var result = Expression.Variable(typeof(double)); var expr = Expression.Block( new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, Expression.Call(ExponentialIntegralEi, arg))), result ); return expr; } case OpCode.SineIntegral: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var si = Expression.Variable(typeof(double)); var ci = Expression.Variable(typeof(double)); var block = Expression.Block( new[] { si, ci }, Expression.Call(SineCosineIntegrals, arg, si, ci), si ); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.CosineIntegral: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var si = Expression.Variable(typeof(double)); var ci = Expression.Variable(typeof(double)); var block = Expression.Block( new[] { si, ci }, Expression.Call(SineCosineIntegrals, arg, si, ci), ci ); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.HyperbolicSineIntegral: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var shi = Expression.Variable(typeof(double)); var chi = Expression.Variable(typeof(double)); var block = Expression.Block( new[] { shi, chi }, Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi), shi ); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.HyperbolicCosineIntegral: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var shi = Expression.Variable(typeof(double)); var chi = Expression.Variable(typeof(double)); var block = Expression.Block( new[] { shi, chi }, Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi), chi ); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.FresnelSineIntegral: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var s = Expression.Variable(typeof(double)); var c = Expression.Variable(typeof(double)); var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), s); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.FresnelCosineIntegral: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var s = Expression.Variable(typeof(double)); var c = Expression.Variable(typeof(double)); var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), c); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.AiryA: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var ai = Expression.Variable(typeof(double)); var aip = Expression.Variable(typeof(double)); var bi = Expression.Variable(typeof(double)); var bip = Expression.Variable(typeof(double)); var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), ai); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.AiryB: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var ai = Expression.Variable(typeof(double)); var aip = Expression.Variable(typeof(double)); var bi = Expression.Variable(typeof(double)); var bip = Expression.Variable(typeof(double)); var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), bi); var result = Expression.Variable(typeof(double)); var expr = Expression.Block(new[] { result }, Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, block)), result ); return expr; } case OpCode.Norm: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var result = Expression.Variable(typeof(double)); return Expression.Block( new[] { result }, Expression.IfThenElse( Expression.Call(IsNaN, arg), Expression.Assign(result, arg), Expression.Assign(result, Expression.Call(NormalDistribution, arg))), result); } case OpCode.Erf: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var result = Expression.Variable(typeof(double)); return Expression.Block( new[] { result }, Expression.IfThenElse( isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, Expression.Call(ErrorFunction, arg))), result); } case OpCode.Bessel: { var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var isNaN = Expression.Call(IsNaN, arg); var result = Expression.Variable(typeof(double)); return Expression.Block( new[] { result }, Expression.IfThenElse( isNaN, Expression.Assign(result, Expression.Constant(double.NaN)), Expression.Assign(result, Expression.Call(Bessel, arg))), result); } case OpCode.AnalyticQuotient: { var x1 = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var x2 = MakeExpr(node.GetSubtree(1), variableIndices, row, columns); return Expression.Divide(x1, Expression.Call(Sqrt, Expression.Add( Expression.Constant(1.0), Expression.Multiply(x2, x2)))); } case OpCode.IfThenElse: { var test = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var result = Expression.Variable(typeof(double)); var condition = Expression.IfThenElse(Expression.GreaterThan(test, Expression.Constant(0.0)), Expression.Assign(result, MakeExpr(node.GetSubtree(1), variableIndices, row, columns)), Expression.Assign(result, MakeExpr(node.GetSubtree(2), variableIndices, row, columns))); return Expression.Block(new[] { result }, condition, result); } case OpCode.AND: { var result = Expression.Variable(typeof(double)); var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); for (int i = 1; i < node.SubtreeCount; ++i) { expr = Expression.Block(new[] { result }, Expression.IfThenElse( Expression.GreaterThan(expr, Expression.Constant(0.0)), Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)), Expression.Assign(result, expr)), result ); } return Expression.Block( new[] { result }, Expression.Assign(result, expr), Expression.IfThenElse( Expression.GreaterThan(result, Expression.Constant(0.0)), Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)) ), result ); } case OpCode.OR: { var result = Expression.Variable(typeof(double)); var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); for (int i = 1; i < node.SubtreeCount; ++i) { expr = Expression.Block(new[] { result }, Expression.IfThenElse( Expression.LessThanOrEqual(expr, Expression.Constant(0.0)), Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)), Expression.Assign(result, expr)), result ); } return Expression.Block( new[] { result }, Expression.Assign(result, expr), Expression.IfThenElse( Expression.GreaterThan(result, Expression.Constant(0.0)), Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)) ), result ); } case OpCode.NOT: { var value = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var result = Expression.Variable(typeof(double)); var condition = Expression.IfThenElse(Expression.GreaterThan(value, Expression.Constant(0.0)), Expression.Assign(result, Expression.Constant(-1.0)), Expression.Assign(result, Expression.Constant(1.0))); return Expression.Block(new[] { result }, condition, result); } case OpCode.XOR: { var ps = Expression.Variable(typeof(int)); var block = Expression.Block( new[] { ps }, Expression.Assign(ps, Expression.Constant(0)), ps ); foreach (var subtree in node.Subtrees) { var expr = MakeExpr(subtree, variableIndices, row, columns); block = Expression.Block( new[] { ps }, Expression.Assign(ps, block), Expression.IfThen(Expression.GreaterThan(expr, Expression.Constant(0.0)), Expression.PostIncrementAssign(ps)), ps ); } var result = Expression.Variable(typeof(double)); var xorExpr = Expression.Block( new[] { result }, Expression.IfThenElse( Expression.Equal(Expression.Modulo(block, Expression.Constant(2)), Expression.Constant(0)), Expression.Assign(result, Expression.Constant(-1.0)), Expression.Assign(result, Expression.Constant(1.0)) ), result ); return xorExpr; } case OpCode.GT: { var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns); var result = Expression.Variable(typeof(double)); var condition = Expression.IfThenElse(Expression.GreaterThan(left, right), Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0))); return Expression.Block( new[] { result }, condition, result); } case OpCode.LT: { var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns); var result = Expression.Variable(typeof(double)); var condition = Expression.IfThenElse(Expression.LessThan(left, right), Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0))); return Expression.Block(new[] { result }, condition, result); } case OpCode.VariableCondition: { var variableConditionTreeNode = (VariableConditionTreeNode)node; if (variableConditionTreeNode.Symbol.IgnoreSlope) throw new NotSupportedException("Strict variable conditionals are not supported"); var variableName = variableConditionTreeNode.VariableName; var indexExpr = Expression.Constant(variableIndices[variableName]); var valuesExpr = Expression.ArrayIndex(columns, indexExpr); var variableValue = Expression.Property(valuesExpr, Indexer, row); var variableThreshold = Expression.Constant(variableConditionTreeNode.Threshold); var variableSlope = Expression.Constant(variableConditionTreeNode.Slope); var x = Expression.Subtract(variableValue, variableThreshold); var xSlope = Expression.Multiply(Expression.Negate(variableSlope), x); var xSlopeExp = Expression.Call(Exp, xSlope); var p = Expression.Divide(Expression.Constant(1), Expression.Add(Expression.Constant(1), xSlopeExp)); var trueBranch = MakeExpr(node.GetSubtree(0), variableIndices, row, columns); var falseBranch = MakeExpr(node.GetSubtree(1), variableIndices, row, columns); return Expression.Add( Expression.Multiply(trueBranch, p), Expression.Multiply(falseBranch, Expression.Subtract(Expression.Constant(1), p)) ); } default: throw new NotSupportedException("Unsupported symbol: " + node.Symbol); } #endregion } } }