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

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

#2442: Added new interpreter as described above. The new function symbol accepts lambdas in the constructor and gets intitialized with a fixed arity. The MethodInfo of the provided lambda is stored internally and used by the interpreter. The MethodInfo field should be [Storable] but this would require a small change to the serializer (relatively trivial I think). Currently it works with the programmable problem and the SymbolicExpressionTreeEncoding (see attached example).

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