Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 16668 was 16668, checked in by gkronber, 5 years ago

#2866: added support for tanh to the remaining interpreters (native is missing)

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