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

Last change on this file since 13248 was 13248, checked in by mkommend, 4 years ago

#2442: Reintegrated branch for compiled symbolic expression tree interpreter.

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