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

Last change on this file since 13251 was 13251, checked in by mkommend, 6 years ago

#2442: Adapted locking in the symbolic expression trees interpreters when incrementing the evaluated solutions.

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