source: branches/2974_Constants_Optimization/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionCompiledTreeInterpreter.cs @ 17193

Last change on this file since 17193 was 17193, checked in by mkommend, 6 weeks ago

#2974: Merged trunk changes into branch.

File size: 33.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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.Condition(Expression.LessThan(arg, Expression.Constant(0.0)),
250              Expression.Negate(Expression.Power(Expression.Negate(arg), Expression.Constant(1.0 / 3.0))),
251              Expression.Power(arg, Expression.Constant(1.0 / 3.0)));
252          }
253        case OpCodes.Root: {
254            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
255            var power = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
256            return Expression.Power(arg, Expression.Divide(Expression.Constant(1.0), Expression.Call(Round, power)));
257          }
258        case OpCodes.Exp: {
259            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
260            return Expression.Call(Exp, arg);
261          }
262        case OpCodes.Log: {
263            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
264            return Expression.Call(Log, arg);
265          }
266        case OpCodes.Gamma: {
267            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
268            var isNaN = Expression.Call(IsNaN, arg);
269
270            var result = Expression.Variable(typeof(double));
271            var expr = Expression.Block(
272              new[] { result },
273              Expression.IfThenElse(
274                isNaN,
275                Expression.Assign(result, Expression.Constant(double.NaN)),
276                Expression.Assign(result, Expression.Call(Gamma, arg))
277                ),
278              result
279              );
280            return expr;
281          }
282        case OpCodes.Psi: {
283            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
284            var isNaN = Expression.Call(IsNaN, arg);
285
286            var result = Expression.Variable(typeof(double));
287            var floor = Expression.Call(Floor, arg);
288            var expr = Expression.Block(
289              new[] { result },
290              Expression.IfThenElse(
291                isNaN,
292                Expression.Assign(result, Expression.Constant(double.NaN)),
293                Expression.IfThenElse(
294                  Expression.AndAlso(
295                    Expression.LessThanOrEqual(arg, Expression.Constant(0.0)),
296                    Expression.Call(IsAlmost, Expression.Subtract(floor, arg), Expression.Constant(0.0))),
297                  Expression.Assign(result, Expression.Constant(double.NaN)),
298                  Expression.Assign(result, Expression.Call(Psi, arg)))
299                ),
300              result);
301
302            return expr;
303          }
304        case OpCodes.Dawson: {
305            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
306            var isNaN = Expression.Call(IsNaN, arg);
307            var result = Expression.Variable(typeof(double));
308            var expr = Expression.Block(
309              new[] { result },
310              Expression.IfThenElse(isNaN,
311                Expression.Assign(result, Expression.Constant(double.NaN)),
312                Expression.Assign(result, Expression.Call(DawsonIntegral, arg))),
313              result
314              );
315
316            return expr;
317          }
318        case OpCodes.ExponentialIntegralEi: {
319            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
320            var isNaN = Expression.Call(IsNaN, arg);
321            var result = Expression.Variable(typeof(double));
322            var expr = Expression.Block(
323              new[] { result },
324              Expression.IfThenElse(isNaN,
325                Expression.Assign(result, Expression.Constant(double.NaN)),
326                Expression.Assign(result, Expression.Call(ExponentialIntegralEi, arg))),
327              result
328              );
329
330            return expr;
331          }
332        case OpCodes.SineIntegral: {
333            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
334            var isNaN = Expression.Call(IsNaN, arg);
335            var si = Expression.Variable(typeof(double));
336            var ci = Expression.Variable(typeof(double));
337            var block = Expression.Block(
338              new[] { si, ci },
339              Expression.Call(SineCosineIntegrals, arg, si, ci),
340              si
341              );
342            var result = Expression.Variable(typeof(double));
343            var expr = Expression.Block(new[] { result },
344              Expression.IfThenElse(isNaN,
345                Expression.Assign(result, Expression.Constant(double.NaN)),
346                Expression.Assign(result, block)),
347              result
348              );
349
350            return expr;
351          }
352        case OpCodes.CosineIntegral: {
353            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
354            var isNaN = Expression.Call(IsNaN, arg);
355            var si = Expression.Variable(typeof(double));
356            var ci = Expression.Variable(typeof(double));
357            var block = Expression.Block(
358              new[] { si, ci },
359              Expression.Call(SineCosineIntegrals, arg, si, ci),
360              ci
361              );
362            var result = Expression.Variable(typeof(double));
363            var expr = Expression.Block(new[] { result },
364              Expression.IfThenElse(isNaN,
365                Expression.Assign(result, Expression.Constant(double.NaN)),
366                Expression.Assign(result, block)),
367              result
368              );
369
370            return expr;
371          }
372        case OpCodes.HyperbolicSineIntegral: {
373            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
374            var isNaN = Expression.Call(IsNaN, arg);
375            var shi = Expression.Variable(typeof(double));
376            var chi = Expression.Variable(typeof(double));
377            var block = Expression.Block(
378              new[] { shi, chi },
379              Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi),
380              shi
381              );
382            var result = Expression.Variable(typeof(double));
383            var expr = Expression.Block(new[] { result },
384              Expression.IfThenElse(isNaN,
385                Expression.Assign(result, Expression.Constant(double.NaN)),
386                Expression.Assign(result, block)),
387              result
388              );
389
390            return expr;
391          }
392        case OpCodes.HyperbolicCosineIntegral: {
393            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
394            var isNaN = Expression.Call(IsNaN, arg);
395            var shi = Expression.Variable(typeof(double));
396            var chi = Expression.Variable(typeof(double));
397            var block = Expression.Block(
398              new[] { shi, chi },
399              Expression.Call(HyperbolicSineCosineIntegrals, arg, shi, chi),
400              chi
401              );
402            var result = Expression.Variable(typeof(double));
403            var expr = Expression.Block(new[] { result },
404              Expression.IfThenElse(isNaN,
405                Expression.Assign(result, Expression.Constant(double.NaN)),
406                Expression.Assign(result, block)),
407              result
408              );
409
410            return expr;
411          }
412        case OpCodes.FresnelSineIntegral: {
413            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
414            var isNaN = Expression.Call(IsNaN, arg);
415            var s = Expression.Variable(typeof(double));
416            var c = Expression.Variable(typeof(double));
417            var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), s);
418            var result = Expression.Variable(typeof(double));
419            var expr = Expression.Block(new[] { result },
420              Expression.IfThenElse(isNaN,
421                Expression.Assign(result, Expression.Constant(double.NaN)),
422                Expression.Assign(result, block)),
423              result
424              );
425
426            return expr;
427          }
428        case OpCodes.FresnelCosineIntegral: {
429            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
430            var isNaN = Expression.Call(IsNaN, arg);
431            var s = Expression.Variable(typeof(double));
432            var c = Expression.Variable(typeof(double));
433            var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), c);
434            var result = Expression.Variable(typeof(double));
435            var expr = Expression.Block(new[] { result },
436              Expression.IfThenElse(isNaN,
437                Expression.Assign(result, Expression.Constant(double.NaN)),
438                Expression.Assign(result, block)),
439              result
440              );
441
442            return expr;
443          }
444        case OpCodes.AiryA: {
445            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
446            var isNaN = Expression.Call(IsNaN, arg);
447            var ai = Expression.Variable(typeof(double));
448            var aip = Expression.Variable(typeof(double));
449            var bi = Expression.Variable(typeof(double));
450            var bip = Expression.Variable(typeof(double));
451            var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), ai);
452            var result = Expression.Variable(typeof(double));
453            var expr = Expression.Block(new[] { result },
454              Expression.IfThenElse(isNaN,
455                Expression.Assign(result, Expression.Constant(double.NaN)),
456                Expression.Assign(result, block)),
457              result
458              );
459
460            return expr;
461          }
462        case OpCodes.AiryB: {
463            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
464            var isNaN = Expression.Call(IsNaN, arg);
465            var ai = Expression.Variable(typeof(double));
466            var aip = Expression.Variable(typeof(double));
467            var bi = Expression.Variable(typeof(double));
468            var bip = Expression.Variable(typeof(double));
469            var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), bi);
470            var result = Expression.Variable(typeof(double));
471            var expr = Expression.Block(new[] { result },
472              Expression.IfThenElse(isNaN,
473                Expression.Assign(result, Expression.Constant(double.NaN)),
474                Expression.Assign(result, block)),
475              result
476              );
477
478            return expr;
479          }
480        case OpCodes.Norm: {
481            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
482            var result = Expression.Variable(typeof(double));
483            return Expression.Block(
484              new[] { result },
485              Expression.IfThenElse(
486                Expression.Call(IsNaN, arg),
487                Expression.Assign(result, arg),
488                Expression.Assign(result, Expression.Call(NormalDistribution, arg))),
489              result);
490          }
491        case OpCodes.Erf: {
492            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
493            var isNaN = Expression.Call(IsNaN, arg);
494            var result = Expression.Variable(typeof(double));
495            return Expression.Block(
496              new[] { result },
497              Expression.IfThenElse(
498                isNaN,
499                Expression.Assign(result, Expression.Constant(double.NaN)),
500                Expression.Assign(result, Expression.Call(ErrorFunction, arg))),
501              result);
502          }
503        case OpCodes.Bessel: {
504            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
505            var isNaN = Expression.Call(IsNaN, arg);
506            var result = Expression.Variable(typeof(double));
507            return Expression.Block(
508              new[] { result },
509              Expression.IfThenElse(
510                isNaN,
511                Expression.Assign(result, Expression.Constant(double.NaN)),
512                Expression.Assign(result, Expression.Call(Bessel, arg))),
513              result);
514          }
515        case OpCodes.AnalyticQuotient: {
516            var x1 = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
517            var x2 = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
518            return Expression.Divide(x1,
519              Expression.Call(Sqrt,
520              Expression.Add(
521                Expression.Constant(1.0),
522                Expression.Multiply(x2, x2))));
523          }
524        case OpCodes.IfThenElse: {
525            var test = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
526            var result = Expression.Variable(typeof(double));
527            var condition = Expression.IfThenElse(Expression.GreaterThan(test, Expression.Constant(0.0)),
528              Expression.Assign(result, MakeExpr(node.GetSubtree(1), variableIndices, row, columns)),
529              Expression.Assign(result, MakeExpr(node.GetSubtree(2), variableIndices, row, columns)));
530            return Expression.Block(new[] { result }, condition, result);
531          }
532        case OpCodes.AND: {
533            var result = Expression.Variable(typeof(double));
534            var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
535
536            for (int i = 1; i < node.SubtreeCount; ++i) {
537              expr = Expression.Block(new[] { result },
538                Expression.IfThenElse(
539                  Expression.GreaterThan(expr, Expression.Constant(0.0)),
540                  Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)),
541                  Expression.Assign(result, expr)),
542                result
543                );
544            }
545
546            return Expression.Block(
547              new[] { result },
548              Expression.Assign(result, expr),
549              Expression.IfThenElse(
550                Expression.GreaterThan(result, Expression.Constant(0.0)),
551                Expression.Assign(result, Expression.Constant(1.0)),
552                Expression.Assign(result, Expression.Constant(-1.0))
553                ),
554              result
555              );
556          }
557        case OpCodes.OR: {
558            var result = Expression.Variable(typeof(double));
559            var expr = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
560
561            for (int i = 1; i < node.SubtreeCount; ++i) {
562              expr = Expression.Block(new[] { result },
563                Expression.IfThenElse(
564                  Expression.LessThanOrEqual(expr, Expression.Constant(0.0)),
565                  Expression.Assign(result, MakeExpr(node.GetSubtree(i), variableIndices, row, columns)),
566                  Expression.Assign(result, expr)),
567                result
568                );
569            }
570
571            return Expression.Block(
572              new[] { result },
573              Expression.Assign(result, expr),
574              Expression.IfThenElse(
575                Expression.GreaterThan(result, Expression.Constant(0.0)),
576                Expression.Assign(result, Expression.Constant(1.0)),
577                Expression.Assign(result, Expression.Constant(-1.0))
578                ),
579              result
580              );
581          }
582        case OpCodes.NOT: {
583            var value = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
584            var result = Expression.Variable(typeof(double));
585            var condition = Expression.IfThenElse(Expression.GreaterThan(value, Expression.Constant(0.0)),
586              Expression.Assign(result, Expression.Constant(-1.0)),
587              Expression.Assign(result, Expression.Constant(1.0)));
588            return Expression.Block(new[] { result }, condition, result);
589          }
590        case OpCodes.XOR: {
591            var ps = Expression.Variable(typeof(int));
592            var block = Expression.Block(
593              new[] { ps },
594              Expression.Assign(ps, Expression.Constant(0)),
595              ps
596              );
597
598            foreach (var subtree in node.Subtrees) {
599              var expr = MakeExpr(subtree, variableIndices, row, columns);
600              block = Expression.Block(
601                new[] { ps },
602                Expression.Assign(ps, block),
603                Expression.IfThen(Expression.GreaterThan(expr, Expression.Constant(0.0)),
604                  Expression.PostIncrementAssign(ps)),
605                ps
606                );
607            }
608
609            var result = Expression.Variable(typeof(double));
610            var xorExpr = Expression.Block(
611              new[] { result },
612              Expression.IfThenElse(
613                Expression.Equal(Expression.Modulo(block, Expression.Constant(2)), Expression.Constant(0)),
614                Expression.Assign(result, Expression.Constant(-1.0)),
615                Expression.Assign(result, Expression.Constant(1.0))
616                ),
617              result
618              );
619            return xorExpr;
620          }
621        case OpCodes.GT: {
622            var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
623            var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
624            var result = Expression.Variable(typeof(double));
625
626            var condition = Expression.IfThenElse(Expression.GreaterThan(left, right),
627              Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)));
628            return Expression.Block(
629              new[] { result },
630              condition,
631              result);
632          }
633        case OpCodes.LT: {
634            var left = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
635            var right = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
636            var result = Expression.Variable(typeof(double));
637
638            var condition = Expression.IfThenElse(Expression.LessThan(left, right),
639              Expression.Assign(result, Expression.Constant(1.0)), Expression.Assign(result, Expression.Constant(-1.0)));
640            return Expression.Block(new[] { result }, condition, result);
641          }
642        case OpCodes.VariableCondition: {
643            var variableConditionTreeNode = (VariableConditionTreeNode)node;
644            if (variableConditionTreeNode.Symbol.IgnoreSlope) throw new NotSupportedException("Strict variable conditionals are not supported");
645            var variableName = variableConditionTreeNode.VariableName;
646            var indexExpr = Expression.Constant(variableIndices[variableName]);
647            var valuesExpr = Expression.ArrayIndex(columns, indexExpr);
648            var variableValue = Expression.Property(valuesExpr, Indexer, row);
649            var variableThreshold = Expression.Constant(variableConditionTreeNode.Threshold);
650            var variableSlope = Expression.Constant(variableConditionTreeNode.Slope);
651
652            var x = Expression.Subtract(variableValue, variableThreshold);
653            var xSlope = Expression.Multiply(Expression.Negate(variableSlope), x);
654            var xSlopeExp = Expression.Call(Exp, xSlope);
655            var p = Expression.Divide(Expression.Constant(1), Expression.Add(Expression.Constant(1), xSlopeExp));
656            var trueBranch = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
657            var falseBranch = MakeExpr(node.GetSubtree(1), variableIndices, row, columns);
658            return Expression.Add(
659              Expression.Multiply(trueBranch, p),
660              Expression.Multiply(falseBranch, Expression.Subtract(Expression.Constant(1), p))
661              );
662          }
663        default:
664          throw new NotSupportedException("Unsupported symbol: " + node.Symbol);
665      }
666      #endregion
667    }
668  }
669}
Note: See TracBrowser for help on using the repository browser.