#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 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.Collections.ObjectModel;
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 HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
[StorableClass]
[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 EvaluatedSolutionsParameterName = "EvaluatedSolutions";
#region method info for the commonly called functions
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 Sqrt = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
private static readonly MethodInfo Floor = typeof(Math).GetMethod("Floor", 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 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
private static readonly Func, IList> ReadOnlyCollectionRetrieveList = GetField, IList>("list"); // retrieve underlying field of type List from a ReadOnlyCollection
private static readonly Func, double[]> ListRetrieveItems = GetField, double[]>("_items"); // retrieve underlying field of type double[] from a List
public override bool CanChangeName { get { return false; } }
public override bool CanChangeDescription { get { return false; } }
#region parameter properties
public IValueParameter CheckExpressionsWithIntervalArithmeticParameter {
get { return (IValueParameter)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
}
public IValueParameter EvaluatedSolutionsParameter {
get { return (IValueParameter)Parameters[EvaluatedSolutionsParameterName]; }
}
#endregion
#region properties
public BoolValue CheckExpressionsWithIntervalArithmetic {
get { return CheckExpressionsWithIntervalArithmeticParameter.Value; }
set { CheckExpressionsWithIntervalArithmeticParameter.Value = value; }
}
public IntValue EvaluatedSolutions {
get { return EvaluatedSolutionsParameter.Value; }
set { EvaluatedSolutionsParameter.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(bool deserializing)
: base(deserializing) {
}
public SymbolicDataAnalysisExpressionCompiledTreeInterpreter() {
Parameters.Add(new ValueParameter(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false)));
Parameters.Add(new ValueParameter(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
}
public void InitializeState() {
EvaluatedSolutions.Value = 0;
}
public void ClearState() {
EvaluatedSolutions.Value = 0;
}
public IEnumerable GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset, IEnumerable rows) {
if (CheckExpressionsWithIntervalArithmetic.Value)
throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
lock (EvaluatedSolutions) {
EvaluatedSolutions.Value++; // increment the evaluated solutions counter
}
var columns = dataset.DoubleVariables.Select(x => ListRetrieveItems((List)ReadOnlyCollectionRetrieveList(dataset.GetReadOnlyDoubleValues(x)))).ToArray();
var compiled = CompileTree(tree, dataset);
return rows.Select(x => compiled(x, columns));
}
public static Func CompileTree(ISymbolicExpressionTree tree, IDataset dataset) {
var row = Expression.Parameter(typeof(int));
var columns = Expression.Parameter(typeof(double[][]));
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>(expr, row, columns);
return lambda.Compile();
}
private static Expression MakeExpr(ISymbolicExpressionTree tree, Dictionary variableIndices, ParameterExpression row, ParameterExpression columns) {
var actualRoot = tree.Root.GetSubtree(0).GetSubtree(0);
return MakeExpr(actualRoot, variableIndices, row, columns);
}
private static Expression MakeExpr(ISymbolicExpressionTreeNode node, Dictionary variableIndices, ParameterExpression row, ParameterExpression columns) {
var opcode = OpCodes.MapSymbolToOpCode(node);
switch (opcode) {
case OpCodes.UserDefinedFunction:
{
var functionSymbol = (FunctionSymbol)node.Symbol;
return Expression.Call(functionSymbol.MethodInfo, node.Subtrees.Select(x => MakeExpr(x, variableIndices, row, columns)));
}
case OpCodes.Constant:
{
var constantTreeNode = (ConstantTreeNode)node;
return Expression.Constant(constantTreeNode.Value);
}
case OpCodes.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);
var variableValue = Expression.ArrayIndex(valuesExpr, row);
return Expression.Multiply(variableWeight, variableValue);
}
case OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.Cos:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
return Expression.Call(Cos, arg);
}
case OpCodes.Sin:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
return Expression.Call(Sin, arg);
}
case OpCodes.Tan:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
return Expression.Call(Tan, arg);
}
case OpCodes.Square:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
return Expression.Power(arg, Expression.Constant(2));
}
case OpCodes.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(Floor, power));
}
case OpCodes.SquareRoot:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
return Expression.Call(Sqrt, arg);
}
case OpCodes.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), power));
}
case OpCodes.Exp:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
return Expression.Call(Exp, arg);
}
case OpCodes.Log:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
return Expression.Call(Log, arg);
}
case OpCodes.Gamma:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
var isNaN = Expression.Call(IsNaN, arg);
var gamma = Expression.Call(Gamma, 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, gamma)
),
result
);
return expr;
}
case OpCodes.Psi:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
var isNaN = Expression.Call(IsNaN, arg);
var psi = Expression.Call(Psi, 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.Equal(Expression.Subtract(floor, arg), Expression.Constant(0.0))),
Expression.Assign(result, Expression.Constant(double.NaN)),
Expression.Assign(result, psi))
),
result);
return expr;
}
case OpCodes.Dawson:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
var isNaN = Expression.Call(IsNaN, arg);
var exprDawsonIntegral = Expression.Call(DawsonIntegral, 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, exprDawsonIntegral)),
result
);
return expr;
}
case OpCodes.ExponentialIntegralEi:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
var isNaN = Expression.Call(IsNaN, arg);
var expIntegrapEi =
Expression.Call(ExponentialIntegralEi, 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, expIntegrapEi)),
result
);
return expr;
}
case OpCodes.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 sinCosIntegrals = Expression.Call(SineCosineIntegrals, arg, si, ci);
var block = Expression.Block(
new[] { si, ci },
sinCosIntegrals,
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 OpCodes.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 sinCosIntegrals = Expression.Call(SineCosineIntegrals, arg, si, ci);
var block = Expression.Block(
new[] { si, ci },
sinCosIntegrals,
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 OpCodes.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 hypSinCosIntegrals = Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi);
var block = Expression.Block(
new[] { shi, chi },
hypSinCosIntegrals,
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 OpCodes.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 hypSinCosIntegrals = Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi);
var block = Expression.Block(
new[] { shi, chi },
hypSinCosIntegrals,
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 OpCodes.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 fresnel = Expression.Call(FresnelIntegral, arg, c, s);
var block = Expression.Block(new[] { s, c }, fresnel, 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 OpCodes.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 fresnel = Expression.Call(FresnelIntegral, arg, c, s);
var block = Expression.Block(new[] { s, c }, fresnel, 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 OpCodes.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 airy = Expression.Call(Airy, arg, ai, aip, bi, bip);
var block = Expression.Block(new[] { ai, aip, bi, bip }, airy, 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 OpCodes.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 airy = Expression.Call(Airy, arg, ai, aip, bi, bip);
var block = Expression.Block(new[] { ai, aip, bi, bip }, airy, 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 OpCodes.Norm:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
var isNaN = Expression.Call(IsNaN, arg);
var result = Expression.Variable(typeof(double));
var norm = Expression.Call(NormalDistribution, arg);
var expr = Expression.Block(new[] { result },
Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)),
Expression.Assign(result, norm)), result);
return expr;
}
case OpCodes.Erf:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
var isNaN = Expression.Call(IsNaN, arg);
var result = Expression.Variable(typeof(double));
var erf = Expression.Call(ErrorFunction, arg);
var expr = Expression.Block(new[] { result },
Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)),
Expression.Assign(result, erf)), result);
return expr;
}
case OpCodes.Bessel:
{
var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
var isNaN = Expression.Call(IsNaN, arg);
var result = Expression.Variable(typeof(double));
var bessel = Expression.Call(Bessel, arg);
var expr = Expression.Block(
new[] { result },
Expression.IfThenElse(
isNaN,
Expression.Assign(result, Expression.Constant(double.NaN)),
Expression.Assign(result, bessel)),
result);
return expr;
}
case OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.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 OpCodes.VariableCondition:
{
var variableConditionTreeNode = (VariableConditionTreeNode)node;
var variableName = variableConditionTreeNode.VariableName;
var indexExpr = Expression.Constant(variableIndices[variableName]);
var valuesExpr = Expression.ArrayIndex(columns, indexExpr);
var variableValue = Expression.ArrayIndex(valuesExpr, 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);
}
}
// util stuff
private static Func GetField(string fieldName) {
ParameterExpression param = Expression.Parameter(typeof(T), "arg");
MemberExpression member = Expression.Field(param, fieldName);
LambdaExpression lambda = Expression.Lambda(typeof(Func), member, param);
Func compiled = (Func)lambda.Compile();
return compiled;
}
}
}