Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionCompiledTreeInterpreter.cs @ 14774

Last change on this file since 14774 was 14345, checked in by gkronber, 8 years ago

#2690: implemented methods to generate symbolic expression tree solutions for decision tree models (random forest and gradient boosted) as well as views which make it possible to inspect each of the individual trees in a GBT and RF solution

File size: 32.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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 CheckExpressionsWithIntervalArithmeticParameterDescription = "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.";
40    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
41
42    #region method info for the commonly called functions
43    private static readonly MethodInfo Sin = typeof(Math).GetMethod("Sin", new[] { typeof(double) });
44    private static readonly MethodInfo Cos = typeof(Math).GetMethod("Cos", new[] { typeof(double) });
45    private static readonly MethodInfo Tan = typeof(Math).GetMethod("Tan", new[] { typeof(double) });
46    private static readonly MethodInfo Sqrt = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
47    private static readonly MethodInfo Floor = typeof(Math).GetMethod("Floor", new[] { typeof(double) });
48    private static readonly MethodInfo Round = typeof(Math).GetMethod("Round", new[] { typeof(double) });
49    private static readonly MethodInfo Exp = typeof(Math).GetMethod("Exp", new[] { typeof(double) });
50    private static readonly MethodInfo Log = typeof(Math).GetMethod("Log", new[] { typeof(double) });
51    private static readonly MethodInfo IsNaN = typeof(double).GetMethod("IsNaN");
52    private static readonly MethodInfo IsAlmost = typeof(DoubleExtensions).GetMethod("IsAlmost");
53    private static readonly MethodInfo Gamma = typeof(alglib).GetMethod("gammafunction", new[] { typeof(double) });
54    private static readonly MethodInfo Psi = typeof(alglib).GetMethod("psi", new[] { typeof(double) });
55    private static readonly MethodInfo DawsonIntegral = typeof(alglib).GetMethod("dawsonintegral", new[] { typeof(double) });
56    private static readonly MethodInfo ExponentialIntegralEi = typeof(alglib).GetMethod("exponentialintegralei", new[] { typeof(double) });
57    private static readonly MethodInfo SineCosineIntegrals = typeof(alglib).GetMethod("sinecosineintegrals", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
58    private static readonly MethodInfo HyperbolicSineCosineIntegrals = typeof(alglib).GetMethod("hyperbolicsinecosineintegrals", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
59    private static readonly MethodInfo FresnelIntegral = typeof(alglib).GetMethod("fresnelintegral", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
60    private static readonly MethodInfo Airy = typeof(alglib).GetMethod("airy", new[] { typeof(double), typeof(double).MakeByRefType(), typeof(double).MakeByRefType(), typeof(double).MakeByRefType(), typeof(double).MakeByRefType() });
61    private static readonly MethodInfo NormalDistribution = typeof(alglib).GetMethod("normaldistribution", new[] { typeof(double) });
62    private static readonly MethodInfo ErrorFunction = typeof(alglib).GetMethod("errorfunction", new[] { typeof(double) });
63    private static readonly MethodInfo Bessel = typeof(alglib).GetMethod("besseli0", new[] { typeof(double) });
64    #endregion
65
66    public override bool CanChangeName { get { return false; } }
67    public override bool CanChangeDescription { get { return false; } }
68
69    #region parameter properties
70    public IFixedValueParameter<BoolValue> CheckExpressionsWithIntervalArithmeticParameter {
71      get { return (IFixedValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
72    }
73
74    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter {
75      get { return (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
76    }
77    #endregion
78
79    #region properties
80    public bool CheckExpressionsWithIntervalArithmetic {
81      get { return CheckExpressionsWithIntervalArithmeticParameter.Value.Value; }
82      set { CheckExpressionsWithIntervalArithmeticParameter.Value.Value = value; }
83    }
84    public int EvaluatedSolutions {
85      get { return EvaluatedSolutionsParameter.Value.Value; }
86      set { EvaluatedSolutionsParameter.Value.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      base("SymbolicDataAnalysisExpressionCompiledTreeInterpreter", "Interpreter which compiles the tree into a lambda") {
105      Parameters.Add(new FixedValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, CheckExpressionsWithIntervalArithmeticParameterDescription, new BoolValue(false)));
106      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
107    }
108
109    public SymbolicDataAnalysisExpressionCompiledTreeInterpreter(string name, string description) :
110      base(name, description) {
111      Parameters.Add(new FixedValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, CheckExpressionsWithIntervalArithmeticParameterDescription, new BoolValue(false)));
112      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
113    }
114
115    public void InitializeState() {
116      EvaluatedSolutions = 0;
117    }
118
119    public void ClearState() { }
120
121    private readonly object syncRoot = new object();
122    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset, IEnumerable<int> rows) {
123      if (CheckExpressionsWithIntervalArithmetic)
124        throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
125
126      lock (syncRoot) {
127        EvaluatedSolutions++; // increment the evaluated solutions counter
128      }
129      var columns = dataset.DoubleVariables.Select(x => (IList<double>)dataset.GetReadOnlyDoubleValues(x)).ToArray();
130      var compiled = CompileTree(tree, dataset);
131      return rows.Select(x => compiled(x, columns));
132    }
133
134    public static Func<int, IList<double>[], double> CompileTree(ISymbolicExpressionTree tree, IDataset dataset) {
135      var lambda = CreateDelegate(tree, dataset);
136      return lambda.Compile();
137    }
138
139    public static Expression<Func<int, IList<double>[], double>> CreateDelegate(ISymbolicExpressionTree tree, IDataset dataset) {
140      var row = Expression.Parameter(typeof(int));
141      var columns = Expression.Parameter(typeof(IList<double>[]));
142      var variableIndices = dataset.DoubleVariables.Select((x, i) => new { x, i }).ToDictionary(e => e.x, e => e.i);
143      var expr = MakeExpr(tree, variableIndices, row, columns);
144      var lambda = Expression.Lambda<Func<int, IList<double>[], double>>(expr, row, columns);
145      return lambda;
146    }
147
148    private static Expression MakeExpr(ISymbolicExpressionTree tree, Dictionary<string, int> variableIndices, Expression row, Expression columns) {
149      var actualRoot = tree.Root.GetSubtree(0).GetSubtree(0);
150      return MakeExpr(actualRoot, variableIndices, row, columns);
151    }
152
153    private static readonly PropertyInfo Indexer = typeof(IList<double>).GetProperty("Item");
154    private static Expression MakeExpr(ISymbolicExpressionTreeNode node, Dictionary<string, int> variableIndices, Expression row, Expression columns) {
155      var opcode = OpCodes.MapSymbolToOpCode(node);
156      #region switch opcode
157      switch (opcode) {
158        case OpCodes.Constant: {
159            var constantTreeNode = (ConstantTreeNode)node;
160            return Expression.Constant(constantTreeNode.Value);
161          }
162        case OpCodes.Variable: {
163            var variableTreeNode = (VariableTreeNode)node;
164            var variableWeight = Expression.Constant(variableTreeNode.Weight);
165            var variableName = variableTreeNode.VariableName;
166            var indexExpr = Expression.Constant(variableIndices[variableName]);
167            var valuesExpr = Expression.ArrayIndex(columns, indexExpr);
168            return Expression.Multiply(variableWeight, Expression.Property(valuesExpr, Indexer, row));
169          }
170        case OpCodes.Add: {
171            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
172            for (int i = 1; i < node.SubtreeCount; ++i) {
173              result = Expression.Add(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
174            }
175            return result;
176          }
177        case OpCodes.Sub: {
178            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
179            if (node.SubtreeCount == 1)
180              return Expression.Negate(result);
181            for (int i = 1; i < node.SubtreeCount; ++i) {
182              result = Expression.Subtract(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
183            }
184            return result;
185          }
186        case OpCodes.Mul: {
187            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
188            for (int i = 1; i < node.SubtreeCount; ++i) {
189              result = Expression.Multiply(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
190            }
191            return result;
192          }
193        case OpCodes.Div: {
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            Expression result = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
204            for (int i = 1; i < node.SubtreeCount; ++i) {
205              result = Expression.Add(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns));
206            }
207            return Expression.Divide(result, Expression.Constant((double)node.SubtreeCount));
208          }
209        case OpCodes.Cos: {
210            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
211            return Expression.Call(Cos, arg);
212          }
213        case OpCodes.Sin: {
214            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
215            return Expression.Call(Sin, arg);
216          }
217        case OpCodes.Tan: {
218            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
219            return Expression.Call(Tan, arg);
220          }
221        case OpCodes.Square: {
222            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
223            return Expression.Power(arg, Expression.Constant(2.0));
224          }
225        case OpCodes.Power: {
226            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
227            var power = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
228            return Expression.Power(arg, Expression.Call(Round, power));
229          }
230        case OpCodes.SquareRoot: {
231            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
232            return Expression.Call(Sqrt, arg);
233          }
234        case OpCodes.Root: {
235            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
236            var power = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
237            return Expression.Power(arg, Expression.Divide(Expression.Constant(1.0), Expression.Call(Round, power)));
238          }
239        case OpCodes.Exp: {
240            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
241            return Expression.Call(Exp, arg);
242          }
243        case OpCodes.Log: {
244            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
245            return Expression.Call(Log, arg);
246          }
247        case OpCodes.Gamma: {
248            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
249            var isNaN = Expression.Call(IsNaN, arg);
250
251            var result = Expression.Variable(typeof(double));
252            var expr = Expression.Block(
253              new[] { result },
254              Expression.IfThenElse(
255                isNaN,
256                Expression.Assign(result, Expression.Constant(double.NaN)),
257                Expression.Assign(result, Expression.Call(Gamma, arg))
258                ),
259              result
260              );
261            return expr;
262          }
263        case OpCodes.Psi: {
264            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
265            var isNaN = Expression.Call(IsNaN, arg);
266
267            var result = Expression.Variable(typeof(double));
268            var floor = Expression.Call(Floor, arg);
269            var expr = Expression.Block(
270              new[] { result },
271              Expression.IfThenElse(
272                isNaN,
273                Expression.Assign(result, Expression.Constant(double.NaN)),
274                Expression.IfThenElse(
275                  Expression.AndAlso(
276                    Expression.LessThanOrEqual(arg, Expression.Constant(0.0)),
277                    Expression.Call(IsAlmost, Expression.Subtract(floor, arg), Expression.Constant(0.0))),
278                  Expression.Assign(result, Expression.Constant(double.NaN)),
279                  Expression.Assign(result, Expression.Call(Psi, arg)))
280                ),
281              result);
282
283            return expr;
284          }
285        case OpCodes.Dawson: {
286            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
287            var isNaN = Expression.Call(IsNaN, arg);
288            var result = Expression.Variable(typeof(double));
289            var expr = Expression.Block(
290              new[] { result },
291              Expression.IfThenElse(isNaN,
292                Expression.Assign(result, Expression.Constant(double.NaN)),
293                Expression.Assign(result, Expression.Call(DawsonIntegral, arg))),
294              result
295              );
296
297            return expr;
298          }
299        case OpCodes.ExponentialIntegralEi: {
300            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
301            var isNaN = Expression.Call(IsNaN, arg);
302            var result = Expression.Variable(typeof(double));
303            var expr = Expression.Block(
304              new[] { result },
305              Expression.IfThenElse(isNaN,
306                Expression.Assign(result, Expression.Constant(double.NaN)),
307                Expression.Assign(result, Expression.Call(ExponentialIntegralEi, arg))),
308              result
309              );
310
311            return expr;
312          }
313        case OpCodes.SineIntegral: {
314            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
315            var isNaN = Expression.Call(IsNaN, arg);
316            var si = Expression.Variable(typeof(double));
317            var ci = Expression.Variable(typeof(double));
318            var block = Expression.Block(
319              new[] { si, ci },
320              Expression.Call(SineCosineIntegrals, arg, si, ci),
321              si
322              );
323            var result = Expression.Variable(typeof(double));
324            var expr = Expression.Block(new[] { result },
325              Expression.IfThenElse(isNaN,
326                Expression.Assign(result, Expression.Constant(double.NaN)),
327                Expression.Assign(result, block)),
328              result
329              );
330
331            return expr;
332          }
333        case OpCodes.CosineIntegral: {
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 block = Expression.Block(
339              new[] { si, ci },
340              Expression.Call(SineCosineIntegrals, arg, si, ci),
341              ci
342              );
343            var result = Expression.Variable(typeof(double));
344            var expr = Expression.Block(new[] { result },
345              Expression.IfThenElse(isNaN,
346                Expression.Assign(result, Expression.Constant(double.NaN)),
347                Expression.Assign(result, block)),
348              result
349              );
350
351            return expr;
352          }
353        case OpCodes.HyperbolicSineIntegral: {
354            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
355            var isNaN = Expression.Call(IsNaN, arg);
356            var shi = Expression.Variable(typeof(double));
357            var chi = Expression.Variable(typeof(double));
358            var block = Expression.Block(
359              new[] { shi, chi },
360              Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi),
361              shi
362              );
363            var result = Expression.Variable(typeof(double));
364            var expr = Expression.Block(new[] { result },
365              Expression.IfThenElse(isNaN,
366                Expression.Assign(result, Expression.Constant(double.NaN)),
367                Expression.Assign(result, block)),
368              result
369              );
370
371            return expr;
372          }
373        case OpCodes.HyperbolicCosineIntegral: {
374            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
375            var isNaN = Expression.Call(IsNaN, arg);
376            var shi = Expression.Variable(typeof(double));
377            var chi = Expression.Variable(typeof(double));
378            var block = Expression.Block(
379              new[] { shi, chi },
380              Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi),
381              chi
382              );
383            var result = Expression.Variable(typeof(double));
384            var expr = Expression.Block(new[] { result },
385              Expression.IfThenElse(isNaN,
386                Expression.Assign(result, Expression.Constant(double.NaN)),
387                Expression.Assign(result, block)),
388              result
389              );
390
391            return expr;
392          }
393        case OpCodes.FresnelSineIntegral: {
394            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
395            var isNaN = Expression.Call(IsNaN, arg);
396            var s = Expression.Variable(typeof(double));
397            var c = Expression.Variable(typeof(double));
398            var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), s);
399            var result = Expression.Variable(typeof(double));
400            var expr = Expression.Block(new[] { result },
401              Expression.IfThenElse(isNaN,
402                Expression.Assign(result, Expression.Constant(double.NaN)),
403                Expression.Assign(result, block)),
404              result
405              );
406
407            return expr;
408          }
409        case OpCodes.FresnelCosineIntegral: {
410            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
411            var isNaN = Expression.Call(IsNaN, arg);
412            var s = Expression.Variable(typeof(double));
413            var c = Expression.Variable(typeof(double));
414            var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), c);
415            var result = Expression.Variable(typeof(double));
416            var expr = Expression.Block(new[] { result },
417              Expression.IfThenElse(isNaN,
418                Expression.Assign(result, Expression.Constant(double.NaN)),
419                Expression.Assign(result, block)),
420              result
421              );
422
423            return expr;
424          }
425        case OpCodes.AiryA: {
426            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
427            var isNaN = Expression.Call(IsNaN, arg);
428            var ai = Expression.Variable(typeof(double));
429            var aip = Expression.Variable(typeof(double));
430            var bi = Expression.Variable(typeof(double));
431            var bip = Expression.Variable(typeof(double));
432            var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), ai);
433            var result = Expression.Variable(typeof(double));
434            var expr = Expression.Block(new[] { result },
435              Expression.IfThenElse(isNaN,
436                Expression.Assign(result, Expression.Constant(double.NaN)),
437                Expression.Assign(result, block)),
438              result
439              );
440
441            return expr;
442          }
443        case OpCodes.AiryB: {
444            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
445            var isNaN = Expression.Call(IsNaN, arg);
446            var ai = Expression.Variable(typeof(double));
447            var aip = Expression.Variable(typeof(double));
448            var bi = Expression.Variable(typeof(double));
449            var bip = Expression.Variable(typeof(double));
450            var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), bi);
451            var result = Expression.Variable(typeof(double));
452            var expr = Expression.Block(new[] { result },
453              Expression.IfThenElse(isNaN,
454                Expression.Assign(result, Expression.Constant(double.NaN)),
455                Expression.Assign(result, block)),
456              result
457              );
458
459            return expr;
460          }
461        case OpCodes.Norm: {
462            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
463            var result = Expression.Variable(typeof(double));
464            return Expression.Block(
465              new[] { result },
466              Expression.IfThenElse(
467                Expression.Call(IsNaN, arg),
468                Expression.Assign(result, arg),
469                Expression.Assign(result, Expression.Call(NormalDistribution, arg))),
470              result);
471          }
472        case OpCodes.Erf: {
473            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
474            var isNaN = Expression.Call(IsNaN, arg);
475            var result = Expression.Variable(typeof(double));
476            return Expression.Block(
477              new[] { result },
478              Expression.IfThenElse(
479                isNaN,
480                Expression.Assign(result, Expression.Constant(double.NaN)),
481                Expression.Assign(result, Expression.Call(ErrorFunction, arg))),
482              result);
483          }
484        case OpCodes.Bessel: {
485            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
486            var isNaN = Expression.Call(IsNaN, arg);
487            var result = Expression.Variable(typeof(double));
488            return Expression.Block(
489              new[] { result },
490              Expression.IfThenElse(
491                isNaN,
492                Expression.Assign(result, Expression.Constant(double.NaN)),
493                Expression.Assign(result, Expression.Call(Bessel, arg))),
494              result);
495          }
496        case OpCodes.IfThenElse: {
497            var test = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
498            var result = Expression.Variable(typeof(double));
499            var condition = Expression.IfThenElse(Expression.GreaterThan(test, Expression.Constant(0.0)),
500              Expression.Assign(result, MakeExpr(node.GetSubtree(1), variableIndices, row, columns)),
501              Expression.Assign(result, MakeExpr(node.GetSubtree(2), variableIndices, row, columns)));
502            return Expression.Block(new[] { result }, condition, result);
503          }
504        case OpCodes.AND: {
505            var result = Expression.Variable(typeof(double));
506            var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
507
508            for (int i = 1; i < node.SubtreeCount; ++i) {
509              expr = Expression.Block(new[] { result },
510                Expression.IfThenElse(
511                  Expression.GreaterThan(expr, Expression.Constant(0.0)),
512                  Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)),
513                  Expression.Assign(result, expr)),
514                result
515                );
516            }
517
518            return Expression.Block(
519              new[] { result },
520              Expression.Assign(result, expr),
521              Expression.IfThenElse(
522                Expression.GreaterThan(result, Expression.Constant(0.0)),
523                Expression.Assign(result, Expression.Constant(1.0)),
524                Expression.Assign(result, Expression.Constant(-1.0))
525                ),
526              result
527              );
528          }
529        case OpCodes.OR: {
530            var result = Expression.Variable(typeof(double));
531            var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
532
533            for (int i = 1; i < node.SubtreeCount; ++i) {
534              expr = Expression.Block(new[] { result },
535                Expression.IfThenElse(
536                  Expression.LessThanOrEqual(expr, Expression.Constant(0.0)),
537                  Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)),
538                  Expression.Assign(result, expr)),
539                result
540                );
541            }
542
543            return Expression.Block(
544              new[] { result },
545              Expression.Assign(result, expr),
546              Expression.IfThenElse(
547                Expression.GreaterThan(result, Expression.Constant(0.0)),
548                Expression.Assign(result, Expression.Constant(1.0)),
549                Expression.Assign(result, Expression.Constant(-1.0))
550                ),
551              result
552              );
553          }
554        case OpCodes.NOT: {
555            var value = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
556            var result = Expression.Variable(typeof(double));
557            var condition = Expression.IfThenElse(Expression.GreaterThan(value, Expression.Constant(0.0)),
558              Expression.Assign(result, Expression.Constant(-1.0)),
559              Expression.Assign(result, Expression.Constant(1.0)));
560            return Expression.Block(new[] { result }, condition, result);
561          }
562        case OpCodes.XOR: {
563            var ps = Expression.Variable(typeof(int));
564            var block = Expression.Block(
565              new[] { ps },
566              Expression.Assign(ps, Expression.Constant(0)),
567              ps
568              );
569
570            foreach (var subtree in node.Subtrees) {
571              var expr = MakeExpr(subtree, variableIndices, row, columns);
572              block = Expression.Block(
573                new[] { ps },
574                Expression.Assign(ps, block),
575                Expression.IfThen(Expression.GreaterThan(expr, Expression.Constant(0.0)),
576                  Expression.PostIncrementAssign(ps)),
577                ps
578                );
579            }
580
581            var result = Expression.Variable(typeof(double));
582            var xorExpr = Expression.Block(
583              new[] { result },
584              Expression.IfThenElse(
585                Expression.Equal(Expression.Modulo(block, Expression.Constant(2)), Expression.Constant(0)),
586                Expression.Assign(result, Expression.Constant(-1.0)),
587                Expression.Assign(result, Expression.Constant(1.0))
588                ),
589              result
590              );
591            return xorExpr;
592          }
593        case OpCodes.GT: {
594            var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
595            var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
596            var result = Expression.Variable(typeof(double));
597
598            var condition = Expression.IfThenElse(Expression.GreaterThan(left, right),
599              Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)));
600            return Expression.Block(
601              new[] { result },
602              condition,
603              result);
604          }
605        case OpCodes.LT: {
606            var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
607            var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
608            var result = Expression.Variable(typeof(double));
609
610            var condition = Expression.IfThenElse(Expression.LessThan(left, right),
611              Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)));
612            return Expression.Block(new[] { result }, condition, result);
613          }
614        case OpCodes.VariableCondition: {
615            var variableConditionTreeNode = (VariableConditionTreeNode)node;
616            if (variableConditionTreeNode.Symbol.IgnoreSlope) throw new NotSupportedException("Strict variable conditionals are not supported");
617            var variableName = variableConditionTreeNode.VariableName;
618            var indexExpr = Expression.Constant(variableIndices[variableName]);
619            var valuesExpr = Expression.ArrayIndex(columns, indexExpr);
620            var variableValue = Expression.Property(valuesExpr, Indexer, row);
621            var variableThreshold = Expression.Constant(variableConditionTreeNode.Threshold);
622            var variableSlope = Expression.Constant(variableConditionTreeNode.Slope);
623
624            var x = Expression.Subtract(variableValue, variableThreshold);
625            var xSlope = Expression.Multiply(Expression.Negate(variableSlope), x);
626            var xSlopeExp = Expression.Call(Exp, xSlope);
627            var p = Expression.Divide(Expression.Constant(1), Expression.Add(Expression.Constant(1), xSlopeExp));
628            var trueBranch = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
629            var falseBranch = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
630            return Expression.Add(
631              Expression.Multiply(trueBranch, p),
632              Expression.Multiply(falseBranch, Expression.Subtract(Expression.Constant(1), p))
633              );
634          }
635        default:
636          throw new NotSupportedException("Unsupported symbol: " + node.Symbol);
637      }
638      #endregion
639    }
640  }
641}
Note: See TracBrowser for help on using the repository browser.