Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 13141 was 13141, checked in by bburlacu, 7 years ago

#2442: Merged files from trunk and updated project file. Implemented missing operations in the CompiledTreeInterpreter: Integral, Derivative, Lag, TimeLag. Adapted lambda signature to accept an array of List<double> in order to make it easier to work with compiled trees. Changed value parameters to fixed value parameters and adjusted interpreter constructors and after serialization hooks. Removed function symbol.

From the performance point of view, compiling the tree into a lambda accepting a double[][] parameter (an array of arrays for the values of each double variable), accessed with Expression.ArrayIndex is the fastest, but it can be cumbersome to provide the data as a double[][]. Therefore the variant with List<double>[] was chosen. Internally, for each variable node the List's underlying double array is used, result in an overall decent speed compromise.

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