Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionCompiledTreeInterpreter.cs @ 15529

Last change on this file since 15529 was 15127, checked in by gkronber, 7 years ago

#2690: merged r14345, r14346, r14368 from trunk to stable

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