source: branches/HeuristicLab.LinqExpressionTreeInterpreter/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionCompiledTreeInterpreter.cs @ 13140

Last change on this file since 13140 was 13140, checked in by bburlacu, 6 years ago

#2442: Commit code version using a List instead of double array (makes code slightly simpler since the underlying double array need not be retrieved from the list)

File size: 32.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Linq.Expressions;
26using System.Reflection;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Data;
30using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
31using HeuristicLab.Parameters;
32using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
33
34namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
35  [StorableClass]
36  [Item("SymbolicDataAnalysisExpressionCompiledTreeInterpreter", "Interpreter that converts the tree into a Linq.Expression then compiles it.")]
37  public sealed class SymbolicDataAnalysisExpressionCompiledTreeInterpreter : ParameterizedNamedItem, ISymbolicDataAnalysisExpressionTreeInterpreter {
38    private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic";
39    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
40
41    #region method info for the commonly called functions
42    private static readonly MethodInfo Sin = typeof(Math).GetMethod("Sin", new[] { typeof(double) });
43    private static readonly MethodInfo Cos = typeof(Math).GetMethod("Cos", new[] { typeof(double) });
44    private static readonly MethodInfo Tan = typeof(Math).GetMethod("Tan", new[] { typeof(double) });
45    private static readonly MethodInfo Sqrt = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
46    private static readonly MethodInfo Floor = typeof(Math).GetMethod("Floor", new[] { typeof(double) });
47    private static readonly MethodInfo Exp = typeof(Math).GetMethod("Exp", new[] { typeof(double) });
48    private static readonly MethodInfo Log = typeof(Math).GetMethod("Log", new[] { typeof(double) });
49    private static readonly MethodInfo IsNaN = typeof(double).GetMethod("IsNaN");
50    private static readonly MethodInfo Gamma = typeof(alglib).GetMethod("gammafunction", new[] { typeof(double) });
51    private static readonly MethodInfo Psi = typeof(alglib).GetMethod("psi", new[] { typeof(double) });
52    private static readonly MethodInfo DawsonIntegral = typeof(alglib).GetMethod("dawsonintegral", new[] { typeof(double) });
53    private static readonly MethodInfo ExponentialIntegralEi = typeof(alglib).GetMethod("exponentialintegralei", new[] { typeof(double) });
54    private static readonly MethodInfo SineCosineIntegrals = typeof(alglib).GetMethod("sinecosineintegrals", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
55    private static readonly MethodInfo HyperbolicSineCosineIntegrals = typeof(alglib).GetMethod("hyperbolicsinecosineintegrals", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
56    private static readonly MethodInfo FresnelIntegral = typeof(alglib).GetMethod("fresnelintegral", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
57    private static readonly MethodInfo Airy = typeof(alglib).GetMethod("airy", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType(), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
58    private static readonly MethodInfo NormalDistribution = typeof(alglib).GetMethod("normaldistribution", new[] { typeof(double) });
59    private static readonly MethodInfo ErrorFunction = typeof(alglib).GetMethod("errorfunction", new[] { typeof(double) });
60    private static readonly MethodInfo Bessel = typeof(alglib).GetMethod("besseli0", new[] { typeof(double) });
61    #endregion
62
63    private static readonly MethodInfo ListGetValue = typeof(IList<double>).GetProperty("Item", new Type[] { typeof(int) }).GetGetMethod();
64
65    public override bool CanChangeName { get { return false; } }
66
67    public override bool CanChangeDescription { get { return false; } }
68
69    #region parameter properties
70    public IValueParameter<BoolValue> CheckExpressionsWithIntervalArithmeticParameter {
71      get { return (IValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
72    }
73
74    public IValueParameter<IntValue> EvaluatedSolutionsParameter {
75      get { return (IValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
76    }
77    #endregion
78
79    #region properties
80    public BoolValue CheckExpressionsWithIntervalArithmetic {
81      get { return CheckExpressionsWithIntervalArithmeticParameter.Value; }
82      set { CheckExpressionsWithIntervalArithmeticParameter.Value = value; }
83    }
84    public IntValue EvaluatedSolutions {
85      get { return EvaluatedSolutionsParameter.Value; }
86      set { EvaluatedSolutionsParameter.Value = value; }
87    }
88    #endregion
89
90    public override IDeepCloneable Clone(Cloner cloner) {
91      return new SymbolicDataAnalysisExpressionCompiledTreeInterpreter(this, cloner);
92    }
93
94    private SymbolicDataAnalysisExpressionCompiledTreeInterpreter(SymbolicDataAnalysisExpressionCompiledTreeInterpreter original, Cloner cloner)
95      : base(original, cloner) {
96    }
97
98    [StorableConstructor]
99    private SymbolicDataAnalysisExpressionCompiledTreeInterpreter(bool deserializing)
100      : base(deserializing) {
101    }
102
103    public SymbolicDataAnalysisExpressionCompiledTreeInterpreter() {
104      Parameters.Add(new ValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false)));
105      Parameters.Add(new ValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
106    }
107
108    public void InitializeState() {
109      EvaluatedSolutions.Value = 0;
110    }
111
112    public void ClearState() {
113      EvaluatedSolutions.Value = 0;
114    }
115
116    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset, IEnumerable<int> rows) {
117      if (CheckExpressionsWithIntervalArithmetic.Value)
118        throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
119
120      lock (EvaluatedSolutions) {
121        EvaluatedSolutions.Value++; // increment the evaluated solutions counter
122      }
123
124      var columns = dataset.DoubleVariables.Select(x => (IList<double>)dataset.GetReadOnlyDoubleValues(x)).ToArray();
125      var compiled = CompileTree(tree, dataset);
126      return rows.Select(x => compiled(x, columns));
127    }
128
129    public static Func<int, IList<double>[], double> CompileTree(ISymbolicExpressionTree tree, IDataset dataset) {
130      var row = Expression.Parameter(typeof(int));
131      var columns = Expression.Parameter(typeof(IList<double>[]));
132      var variableIndices = dataset.DoubleVariables.Select((x, i) => new { x, i }).ToDictionary(e => e.x, e => e.i);
133      var expr = MakeExpr(tree, variableIndices, row, columns);
134      var lambda = Expression.Lambda<Func<int, IList<double>[], double>>(expr, row, columns);
135      return lambda.Compile();
136    }
137
138    private static Expression MakeExpr(ISymbolicExpressionTree tree, Dictionary<string, int> variableIndices, ParameterExpression row, ParameterExpression columns) {
139      var actualRoot = tree.Root.GetSubtree(0).GetSubtree(0);
140      return MakeExpr(actualRoot, variableIndices, row, columns);
141    }
142
143    private static Expression MakeExpr(ISymbolicExpressionTreeNode node, Dictionary<string, int> variableIndices, ParameterExpression row, ParameterExpression columns) {
144      var opcode = OpCodes.MapSymbolToOpCode(node);
145      switch (opcode) {
146        case OpCodes.UserDefinedFunction:
147          {
148            var functionSymbol = (FunctionSymbol)node.Symbol;
149            return Expression.Call(functionSymbol.MethodInfo, node.Subtrees.Select(x => MakeExpr(x, variableIndices, row, columns)));
150          }
151        case OpCodes.Constant:
152          {
153            var constantTreeNode = (ConstantTreeNode)node;
154            return Expression.Constant(constantTreeNode.Value);
155          }
156        case OpCodes.Variable:
157          {
158            var variableTreeNode = (VariableTreeNode)node;
159            var variableWeight = Expression.Constant(variableTreeNode.Weight);
160            var variableName = variableTreeNode.VariableName;
161            var indexExpr = Expression.Constant(variableIndices[variableName]);
162            var valuesExpr = Expression.ArrayIndex(columns, indexExpr);
163            var variableValue = Expression.Call(valuesExpr, ListGetValue, row);
164            return Expression.Multiply(variableWeight, variableValue);
165          }
166        case OpCodes.Add:
167          {
168            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
169            for (int i = 1; i < node.SubtreeCount; ++i) {
170              result = Expression.Add(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
171            }
172            return result;
173          }
174        case OpCodes.Sub:
175          {
176            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
177            if (node.SubtreeCount == 1)
178              return Expression.Negate(result);
179            for (int i = 1; i < node.SubtreeCount; ++i) {
180              result = Expression.Subtract(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
181            }
182            return result;
183          }
184        case OpCodes.Mul:
185          {
186            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
187            for (int i = 1; i < node.SubtreeCount; ++i) {
188              result = Expression.Multiply(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
189            }
190            return result;
191          }
192        case OpCodes.Div:
193          {
194            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
195            if (node.SubtreeCount == 1)
196              return Expression.Divide(Expression.Constant(1.0), result);
197            for (int i = 1; i < node.SubtreeCount; ++i) {
198              result = Expression.Divide(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
199            }
200            return result;
201          }
202        case OpCodes.Average:
203          {
204            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
205            for (int i = 1; i < node.SubtreeCount; ++i) {
206              result = Expression.Add(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
207            }
208            return Expression.Divide(result, Expression.Constant((double)node.SubtreeCount));
209          }
210        case OpCodes.Cos:
211          {
212            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
213            return Expression.Call(Cos, arg);
214          }
215        case OpCodes.Sin:
216          {
217            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
218            return Expression.Call(Sin, arg);
219          }
220        case OpCodes.Tan:
221          {
222            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
223            return Expression.Call(Tan, arg);
224          }
225        case OpCodes.Square:
226          {
227            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
228            return Expression.Power(arg, Expression.Constant(2));
229          }
230        case OpCodes.Power:
231          {
232            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
233            var power = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
234            return Expression.Power(arg, Expression.Call(Floor, power));
235          }
236        case OpCodes.SquareRoot:
237          {
238            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
239            return Expression.Call(Sqrt, arg);
240          }
241        case OpCodes.Root:
242          {
243            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
244            var power = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
245            return Expression.Power(arg, Expression.Divide(Expression.Constant(1.0), power));
246          }
247        case OpCodes.Exp:
248          {
249            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
250            return Expression.Call(Exp, arg);
251          }
252        case OpCodes.Log:
253          {
254            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
255            return Expression.Call(Log, arg);
256          }
257        case OpCodes.Gamma:
258          {
259            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
260            var isNaN = Expression.Call(IsNaN, arg);
261            var gamma = Expression.Call(Gamma, arg);
262
263            var result = Expression.Variable(typeof(double));
264            var expr = Expression.Block(
265              new[] { result },
266              Expression.IfThenElse(
267                isNaN,
268                Expression.Assign(result, Expression.Constant(double.NaN)),
269                Expression.Assign(result, gamma)
270                ),
271              result
272              );
273            return expr;
274          }
275        case OpCodes.Psi:
276          {
277            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
278            var isNaN = Expression.Call(IsNaN, arg);
279            var psi = Expression.Call(Psi, arg);
280
281            var result = Expression.Variable(typeof(double));
282            var floor = Expression.Call(Floor, arg);
283            var expr = Expression.Block(
284              new[] { result },
285              Expression.IfThenElse(
286                isNaN,
287                Expression.Assign(result, Expression.Constant(double.NaN)),
288                Expression.IfThenElse(
289                  Expression.AndAlso(Expression.LessThanOrEqual(arg, Expression.Constant(0.0)),
290                    Expression.Equal(Expression.Subtract(floor, arg), Expression.Constant(0.0))),
291                  Expression.Assign(result, Expression.Constant(double.NaN)),
292                  Expression.Assign(result, psi))
293                ),
294              result);
295
296            return expr;
297          }
298        case OpCodes.Dawson:
299          {
300            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
301            var isNaN = Expression.Call(IsNaN, arg);
302            var exprDawsonIntegral = Expression.Call(DawsonIntegral, arg);
303            var result = Expression.Variable(typeof(double));
304
305            var expr = Expression.Block(
306              new[] { result },
307              Expression.IfThenElse(isNaN,
308                Expression.Assign(result, Expression.Constant(double.NaN)),
309                Expression.Assign(result, exprDawsonIntegral)),
310              result
311              );
312
313            return expr;
314          }
315        case OpCodes.ExponentialIntegralEi:
316          {
317            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
318            var isNaN = Expression.Call(IsNaN, arg);
319            var expIntegrapEi =
320              Expression.Call(ExponentialIntegralEi, arg);
321            var result = Expression.Variable(typeof(double));
322            var expr = Expression.Block(
323              new[] { result },
324              Expression.IfThenElse(isNaN,
325                Expression.Assign(result, Expression.Constant(double.NaN)),
326                Expression.Assign(result, expIntegrapEi)),
327              result
328              );
329
330            return expr;
331          }
332        case OpCodes.SineIntegral:
333          {
334            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
335            var isNaN = Expression.Call(IsNaN, arg);
336            var si = Expression.Variable(typeof(double));
337            var ci = Expression.Variable(typeof(double));
338            var sinCosIntegrals = Expression.Call(SineCosineIntegrals, arg, si, ci);
339            var block = Expression.Block(
340              new[] { si, ci },
341              sinCosIntegrals,
342              si
343              );
344            var result = Expression.Variable(typeof(double));
345            var expr = Expression.Block(new[] { result },
346              Expression.IfThenElse(isNaN,
347                Expression.Assign(result, Expression.Constant(double.NaN)),
348                Expression.Assign(result, block)),
349              result
350              );
351
352            return expr;
353          }
354        case OpCodes.CosineIntegral:
355          {
356            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
357            var isNaN = Expression.Call(IsNaN, arg);
358            var si = Expression.Variable(typeof(double));
359            var ci = Expression.Variable(typeof(double));
360            var sinCosIntegrals = Expression.Call(SineCosineIntegrals, arg, si, ci);
361            var block = Expression.Block(
362              new[] { si, ci },
363              sinCosIntegrals,
364              ci
365              );
366            var result = Expression.Variable(typeof(double));
367            var expr = Expression.Block(new[] { result },
368              Expression.IfThenElse(isNaN,
369                Expression.Assign(result, Expression.Constant(double.NaN)),
370                Expression.Assign(result, block)),
371              result
372              );
373
374            return expr;
375          }
376        case OpCodes.HyperbolicSineIntegral:
377          {
378            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
379            var isNaN = Expression.Call(IsNaN, arg);
380            var shi = Expression.Variable(typeof(double));
381            var chi = Expression.Variable(typeof(double));
382            var hypSinCosIntegrals = Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi);
383            var block = Expression.Block(
384              new[] { shi, chi },
385              hypSinCosIntegrals,
386              shi
387              );
388            var result = Expression.Variable(typeof(double));
389            var expr = Expression.Block(new[] { result },
390              Expression.IfThenElse(isNaN,
391                Expression.Assign(result, Expression.Constant(double.NaN)),
392                Expression.Assign(result, block)),
393              result
394              );
395
396            return expr;
397          }
398        case OpCodes.HyperbolicCosineIntegral:
399          {
400            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
401            var isNaN = Expression.Call(IsNaN, arg);
402            var shi = Expression.Variable(typeof(double));
403            var chi = Expression.Variable(typeof(double));
404            var hypSinCosIntegrals = Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi);
405            var block = Expression.Block(
406              new[] { shi, chi },
407              hypSinCosIntegrals,
408              chi
409              );
410            var result = Expression.Variable(typeof(double));
411            var expr = Expression.Block(new[] { result },
412              Expression.IfThenElse(isNaN,
413                Expression.Assign(result, Expression.Constant(double.NaN)),
414                Expression.Assign(result, block)),
415              result
416              );
417
418            return expr;
419          }
420        case OpCodes.FresnelSineIntegral:
421          {
422            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
423            var isNaN = Expression.Call(IsNaN, arg);
424            var s = Expression.Variable(typeof(double));
425            var c = Expression.Variable(typeof(double));
426            var fresnel = Expression.Call(FresnelIntegral, arg, c, s);
427            var block = Expression.Block(new[] { s, c }, fresnel, s);
428            var result = Expression.Variable(typeof(double));
429            var expr = Expression.Block(new[] { result },
430              Expression.IfThenElse(isNaN,
431                Expression.Assign(result, Expression.Constant(double.NaN)),
432                Expression.Assign(result, block)),
433              result
434              );
435
436            return expr;
437          }
438        case OpCodes.FresnelCosineIntegral:
439          {
440            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
441            var isNaN = Expression.Call(IsNaN, arg);
442            var s = Expression.Variable(typeof(double));
443            var c = Expression.Variable(typeof(double));
444            var fresnel = Expression.Call(FresnelIntegral, arg, c, s);
445            var block = Expression.Block(new[] { s, c }, fresnel, c);
446            var result = Expression.Variable(typeof(double));
447            var expr = Expression.Block(new[] { result },
448              Expression.IfThenElse(isNaN,
449                Expression.Assign(result, Expression.Constant(double.NaN)),
450                Expression.Assign(result, block)),
451              result
452              );
453
454            return expr;
455          }
456        case OpCodes.AiryA:
457          {
458            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
459            var isNaN = Expression.Call(IsNaN, arg);
460            var ai = Expression.Variable(typeof(double));
461            var aip = Expression.Variable(typeof(double));
462            var bi = Expression.Variable(typeof(double));
463            var bip = Expression.Variable(typeof(double));
464            var airy = Expression.Call(Airy, arg, ai, aip, bi, bip);
465            var block = Expression.Block(new[] { ai, aip, bi, bip }, airy, ai);
466            var result = Expression.Variable(typeof(double));
467            var expr = Expression.Block(new[] { result },
468              Expression.IfThenElse(isNaN,
469                Expression.Assign(result, Expression.Constant(double.NaN)),
470                Expression.Assign(result, block)),
471              result
472              );
473
474            return expr;
475          }
476        case OpCodes.AiryB:
477          {
478            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
479            var isNaN = Expression.Call(IsNaN, arg);
480            var ai = Expression.Variable(typeof(double));
481            var aip = Expression.Variable(typeof(double));
482            var bi = Expression.Variable(typeof(double));
483            var bip = Expression.Variable(typeof(double));
484            var airy = Expression.Call(Airy, arg, ai, aip, bi, bip);
485            var block = Expression.Block(new[] { ai, aip, bi, bip }, airy, bi);
486            var result = Expression.Variable(typeof(double));
487            var expr = Expression.Block(new[] { result },
488              Expression.IfThenElse(isNaN,
489                Expression.Assign(result, Expression.Constant(double.NaN)),
490                Expression.Assign(result, block)),
491              result
492              );
493
494            return expr;
495          }
496        case OpCodes.Norm:
497          {
498            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
499            var isNaN = Expression.Call(IsNaN, arg);
500            var result = Expression.Variable(typeof(double));
501            var norm = Expression.Call(NormalDistribution, arg);
502
503            var expr = Expression.Block(new[] { result },
504              Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)),
505                Expression.Assign(result, norm)), result);
506
507            return expr;
508          }
509        case OpCodes.Erf:
510          {
511            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
512            var isNaN = Expression.Call(IsNaN, arg);
513            var result = Expression.Variable(typeof(double));
514            var erf = Expression.Call(ErrorFunction, arg);
515
516            var expr = Expression.Block(new[] { result },
517              Expression.IfThenElse(isNaN, Expression.Assign(result, Expression.Constant(double.NaN)),
518                Expression.Assign(result, erf)), result);
519
520            return expr;
521          }
522        case OpCodes.Bessel:
523          {
524            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
525            var isNaN = Expression.Call(IsNaN, arg);
526            var result = Expression.Variable(typeof(double));
527            var bessel = Expression.Call(Bessel, arg);
528            var expr = Expression.Block(
529              new[] { result },
530              Expression.IfThenElse(
531                isNaN,
532                Expression.Assign(result, Expression.Constant(double.NaN)),
533                Expression.Assign(result, bessel)),
534              result);
535
536            return expr;
537          }
538        case OpCodes.IfThenElse:
539          {
540            var test = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
541            var result = Expression.Variable(typeof(double));
542            var condition = Expression.IfThenElse(Expression.GreaterThan(test, Expression.Constant(0.0)),
543              Expression.Assign(result, MakeExpr(node.GetSubtree(1), variableIndices, row, columns)),
544              Expression.Assign(result, MakeExpr(node.GetSubtree(2), variableIndices, row, columns)));
545            return Expression.Block(new[] { result }, condition, result);
546          }
547        case OpCodes.AND:
548          {
549            var result = Expression.Variable(typeof(double));
550            var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
551
552            for (int i = 1; i < node.SubtreeCount; ++i) {
553              expr = Expression.Block(new[] { result },
554                Expression.IfThenElse(
555                  Expression.GreaterThan(expr, Expression.Constant(0.0)),
556                  Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)),
557                  Expression.Assign(result, expr)),
558                result
559                );
560            }
561
562            return Expression.Block(
563              new[] { result },
564              Expression.Assign(result, expr),
565              Expression.IfThenElse(
566                Expression.GreaterThan(result, Expression.Constant(0.0)),
567                Expression.Assign(result, Expression.Constant(1.0)),
568                Expression.Assign(result, Expression.Constant(-1.0))
569                ),
570              result
571              );
572          }
573        case OpCodes.OR:
574          {
575            var result = Expression.Variable(typeof(double));
576            var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
577
578            for (int i = 1; i < node.SubtreeCount; ++i) {
579              expr = Expression.Block(new[] { result },
580                Expression.IfThenElse(
581                  Expression.LessThanOrEqual(expr, Expression.Constant(0.0)),
582                  Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)),
583                  Expression.Assign(result, expr)),
584                result
585                );
586            }
587
588            return Expression.Block(
589              new[] { result },
590              Expression.Assign(result, expr),
591              Expression.IfThenElse(
592                Expression.GreaterThan(result, Expression.Constant(0.0)),
593                Expression.Assign(result, Expression.Constant(1.0)),
594                Expression.Assign(result, Expression.Constant(-1.0))
595                ),
596              result
597              );
598          }
599        case OpCodes.NOT:
600          {
601            var value = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
602            var result = Expression.Variable(typeof(double));
603            var condition = Expression.IfThenElse(Expression.GreaterThan(value, Expression.Constant(0.0)),
604              Expression.Assign(result, Expression.Constant(-1.0)),
605              Expression.Assign(result, Expression.Constant(1.0)));
606            return Expression.Block(new[] { result }, condition, result);
607          }
608        case OpCodes.XOR:
609          {
610            var ps = Expression.Variable(typeof(int));
611            var block = Expression.Block(
612              new[] { ps },
613              Expression.Assign(ps, Expression.Constant(0)),
614              ps
615              );
616
617            foreach (var subtree in node.Subtrees) {
618              var expr = MakeExpr(subtree, variableIndices, row, columns);
619              block = Expression.Block(
620                new[] { ps },
621                Expression.Assign(ps, block),
622                Expression.IfThen(Expression.GreaterThan(expr, Expression.Constant(0.0)),
623                  Expression.PostIncrementAssign(ps)),
624                ps
625                );
626            }
627
628            var result = Expression.Variable(typeof(double));
629            var xorExpr = Expression.Block(
630              new[] { result },
631              Expression.IfThenElse(
632                Expression.Equal(Expression.Modulo(block, Expression.Constant(2)), Expression.Constant(0)),
633                Expression.Assign(result, Expression.Constant(-1.0)),
634                Expression.Assign(result, Expression.Constant(1.0))
635                ),
636              result
637              );
638            return xorExpr;
639          }
640        case OpCodes.GT:
641          {
642            var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
643            var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
644            var result = Expression.Variable(typeof(double));
645
646            var condition = Expression.IfThenElse(Expression.GreaterThan(left, right),
647              Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)));
648            return Expression.Block(
649              new[] { result },
650              condition,
651              result);
652          }
653        case OpCodes.LT:
654          {
655            var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
656            var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
657            var result = Expression.Variable(typeof(double));
658
659            var condition = Expression.IfThenElse(Expression.LessThan(left, right),
660              Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)));
661            return Expression.Block(new[] { result }, condition, result);
662          }
663        case OpCodes.VariableCondition:
664          {
665            var variableConditionTreeNode = (VariableConditionTreeNode)node;
666            var variableName = variableConditionTreeNode.VariableName;
667            var indexExpr = Expression.Constant(variableIndices[variableName]);
668            var valuesExpr = Expression.ArrayIndex(columns, indexExpr);
669            var variableValue = Expression.Call(valuesExpr, ListGetValue, row);
670            var variableThreshold = Expression.Constant(variableConditionTreeNode.Threshold);
671            var variableSlope = Expression.Constant(variableConditionTreeNode.Slope);
672
673            var x = Expression.Subtract(variableValue, variableThreshold);
674            var xSlope = Expression.Multiply(Expression.Negate(variableSlope), x);
675            var xSlopeExp = Expression.Call(Exp, xSlope);
676            var p = Expression.Divide(Expression.Constant(1), Expression.Add(Expression.Constant(1), xSlopeExp));
677            var trueBranch = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
678            var falseBranch = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
679            return Expression.Add(
680              Expression.Multiply(trueBranch, p),
681              Expression.Multiply(falseBranch, Expression.Subtract(Expression.Constant(1), p))
682              );
683          }
684        default:
685          throw new NotSupportedException("Unsupported symbol: " + node.Symbol);
686      }
687    }
688    // util stuff
689    private static Func<T, R> GetField<T, R>(string fieldName) {
690      ParameterExpression param = Expression.Parameter(typeof(T), "arg");
691      MemberExpression member = Expression.Field(param, fieldName);
692      LambdaExpression lambda = Expression.Lambda(typeof(Func<T, R>), member, param);
693      Func<T, R> compiled = (Func<T, R>)lambda.Compile();
694      return compiled;
695    }
696  }
697}
Note: See TracBrowser for help on using the repository browser.