Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3040_VectorBasedGP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeInterpreter.cs @ 17369

Last change on this file since 17369 was 17369, checked in by pfleck, 5 years ago

#3040 Added Vector symbols to TypeCoherentExpressionGrammar & fixes.

File size: 26.0 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 HeuristicLab.Common;
25using HeuristicLab.Core;
26using HeuristicLab.Data;
27using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
28using HeuristicLab.Parameters;
29using HEAL.Attic;
30
31namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
32  [StorableType("FB94F333-B32A-44FB-A561-CBDE76693D20")]
33  [Item("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.")]
34  public class SymbolicDataAnalysisExpressionTreeInterpreter : ParameterizedNamedItem,
35    ISymbolicDataAnalysisExpressionTreeInterpreter {
36    private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic";
37    private const string CheckExpressionsWithIntervalArithmeticParameterDescription = "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.";
38    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
39
40    public override bool CanChangeName {
41      get { return false; }
42    }
43
44    public override bool CanChangeDescription {
45      get { return false; }
46    }
47
48    #region parameter properties
49    public IFixedValueParameter<BoolValue> CheckExpressionsWithIntervalArithmeticParameter {
50      get { return (IFixedValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
51    }
52
53    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter {
54      get { return (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
55    }
56    #endregion
57
58    #region properties
59    public bool CheckExpressionsWithIntervalArithmetic {
60      get { return CheckExpressionsWithIntervalArithmeticParameter.Value.Value; }
61      set { CheckExpressionsWithIntervalArithmeticParameter.Value.Value = value; }
62    }
63
64    public int EvaluatedSolutions {
65      get { return EvaluatedSolutionsParameter.Value.Value; }
66      set { EvaluatedSolutionsParameter.Value.Value = value; }
67    }
68    #endregion
69
70    [StorableConstructor]
71    protected SymbolicDataAnalysisExpressionTreeInterpreter(StorableConstructorFlag _) : base(_) { }
72
73    protected SymbolicDataAnalysisExpressionTreeInterpreter(SymbolicDataAnalysisExpressionTreeInterpreter original,
74      Cloner cloner)
75      : base(original, cloner) { }
76
77    public override IDeepCloneable Clone(Cloner cloner) {
78      return new SymbolicDataAnalysisExpressionTreeInterpreter(this, cloner);
79    }
80
81    public SymbolicDataAnalysisExpressionTreeInterpreter()
82      : base("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.") {
83      Parameters.Add(new FixedValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false)));
84      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
85    }
86
87    protected SymbolicDataAnalysisExpressionTreeInterpreter(string name, string description)
88      : base(name, description) {
89      Parameters.Add(new FixedValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false)));
90      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
91    }
92
93    [StorableHook(HookType.AfterDeserialization)]
94    private void AfterDeserialization() {
95      var evaluatedSolutions = new IntValue(0);
96      var checkExpressionsWithIntervalArithmetic = new BoolValue(false);
97      if (Parameters.ContainsKey(EvaluatedSolutionsParameterName)) {
98        var evaluatedSolutionsParameter = (IValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName];
99        evaluatedSolutions = evaluatedSolutionsParameter.Value;
100        Parameters.Remove(EvaluatedSolutionsParameterName);
101      }
102      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", evaluatedSolutions));
103      if (Parameters.ContainsKey(CheckExpressionsWithIntervalArithmeticParameterName)) {
104        var checkExpressionsWithIntervalArithmeticParameter = (IValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName];
105        Parameters.Remove(CheckExpressionsWithIntervalArithmeticParameterName);
106        checkExpressionsWithIntervalArithmetic = checkExpressionsWithIntervalArithmeticParameter.Value;
107      }
108      Parameters.Add(new FixedValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, CheckExpressionsWithIntervalArithmeticParameterDescription, checkExpressionsWithIntervalArithmetic));
109    }
110
111    #region IStatefulItem
112    public void InitializeState() {
113      EvaluatedSolutions = 0;
114    }
115
116    public void ClearState() { }
117    #endregion
118
119    private readonly object syncRoot = new object();
120    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset,
121      IEnumerable<int> rows) {
122      if (CheckExpressionsWithIntervalArithmetic) {
123        throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
124      }
125
126      lock (syncRoot) {
127        EvaluatedSolutions++; // increment the evaluated solutions counter
128      }
129      var state = PrepareInterpreterState(tree, dataset);
130
131      foreach (var rowEnum in rows) {
132        int row = rowEnum;
133        yield return Evaluate(dataset, ref row, state);
134        state.Reset();
135      }
136    }
137
138    private static InterpreterState PrepareInterpreterState(ISymbolicExpressionTree tree, IDataset dataset) {
139      Instruction[] code = SymbolicExpressionTreeCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
140      int necessaryArgStackSize = 0;
141      foreach (Instruction instr in code) {
142        if (instr.opCode == OpCodes.Variable) {
143          var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
144          instr.data = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
145        } else if (instr.opCode == OpCodes.FactorVariable) {
146          var factorTreeNode = instr.dynamicNode as FactorVariableTreeNode;
147          instr.data = dataset.GetReadOnlyStringValues(factorTreeNode.VariableName);
148        } else if (instr.opCode == OpCodes.BinaryFactorVariable) {
149          var factorTreeNode = instr.dynamicNode as BinaryFactorVariableTreeNode;
150          instr.data = dataset.GetReadOnlyStringValues(factorTreeNode.VariableName);
151        } else if (instr.opCode == OpCodes.VectorVariable) {
152          var vectorVariableTreeNode = (VectorVariableTreeNode)instr.dynamicNode;
153          instr.data = dataset.GetReadOnlyDoubleVectorValues(vectorVariableTreeNode.VariableName);
154        } else if (instr.opCode == OpCodes.LagVariable) {
155          var laggedVariableTreeNode = (LaggedVariableTreeNode)instr.dynamicNode;
156          instr.data = dataset.GetReadOnlyDoubleValues(laggedVariableTreeNode.VariableName);
157        } else if (instr.opCode == OpCodes.VariableCondition) {
158          var variableConditionTreeNode = (VariableConditionTreeNode)instr.dynamicNode;
159          instr.data = dataset.GetReadOnlyDoubleValues(variableConditionTreeNode.VariableName);
160        } else if (instr.opCode == OpCodes.Call) {
161          necessaryArgStackSize += instr.nArguments + 1;
162        }
163      }
164      return new InterpreterState(code, necessaryArgStackSize);
165    }
166
167    public virtual double Evaluate(IDataset dataset, ref int row, InterpreterState state) {
168      Instruction currentInstr = state.NextInstruction();
169      switch (currentInstr.opCode) {
170        case OpCodes.Add: {
171            double s = Evaluate(dataset, ref row, state);
172            for (int i = 1; i < currentInstr.nArguments; i++) {
173              s += Evaluate(dataset, ref row, state);
174            }
175            return s;
176          }
177        case OpCodes.Sub: {
178            double s = Evaluate(dataset, ref row, state);
179            for (int i = 1; i < currentInstr.nArguments; i++) {
180              s -= Evaluate(dataset, ref row, state);
181            }
182            if (currentInstr.nArguments == 1) { s = -s; }
183            return s;
184          }
185        case OpCodes.Mul: {
186            double p = Evaluate(dataset, ref row, state);
187            for (int i = 1; i < currentInstr.nArguments; i++) {
188              p *= Evaluate(dataset, ref row, state);
189            }
190            return p;
191          }
192        case OpCodes.Div: {
193            double p = Evaluate(dataset, ref row, state);
194            for (int i = 1; i < currentInstr.nArguments; i++) {
195              p /= Evaluate(dataset, ref row, state);
196            }
197            if (currentInstr.nArguments == 1) { p = 1.0 / p; }
198            return p;
199          }
200        case OpCodes.Average: {
201            double sum = Evaluate(dataset, ref row, state);
202            for (int i = 1; i < currentInstr.nArguments; i++) {
203              sum += Evaluate(dataset, ref row, state);
204            }
205            return sum / currentInstr.nArguments;
206          }
207        case OpCodes.Absolute: {
208            return Math.Abs(Evaluate(dataset, ref row, state));
209          }
210        case OpCodes.Tanh: {
211            return Math.Tanh(Evaluate(dataset, ref row, state));
212          }
213        case OpCodes.Cos: {
214            return Math.Cos(Evaluate(dataset, ref row, state));
215          }
216        case OpCodes.Sin: {
217            return Math.Sin(Evaluate(dataset, ref row, state));
218          }
219        case OpCodes.Tan: {
220            return Math.Tan(Evaluate(dataset, ref row, state));
221          }
222        case OpCodes.Square: {
223            return Math.Pow(Evaluate(dataset, ref row, state), 2);
224          }
225        case OpCodes.Cube: {
226            return Math.Pow(Evaluate(dataset, ref row, state), 3);
227          }
228        case OpCodes.Power: {
229            double x = Evaluate(dataset, ref row, state);
230            double y = Math.Round(Evaluate(dataset, ref row, state));
231            return Math.Pow(x, y);
232          }
233        case OpCodes.SquareRoot: {
234            return Math.Sqrt(Evaluate(dataset, ref row, state));
235          }
236        case OpCodes.CubeRoot: {
237            var arg = Evaluate(dataset, ref row, state);
238            return arg < 0 ? -Math.Pow(-arg, 1.0 / 3.0) : Math.Pow(arg, 1.0 / 3.0);
239          }
240        case OpCodes.Root: {
241            double x = Evaluate(dataset, ref row, state);
242            double y = Math.Round(Evaluate(dataset, ref row, state));
243            return Math.Pow(x, 1 / y);
244          }
245        case OpCodes.Exp: {
246            return Math.Exp(Evaluate(dataset, ref row, state));
247          }
248        case OpCodes.Log: {
249            return Math.Log(Evaluate(dataset, ref row, state));
250          }
251        case OpCodes.Gamma: {
252            var x = Evaluate(dataset, ref row, state);
253            if (double.IsNaN(x)) { return double.NaN; } else { return alglib.gammafunction(x); }
254          }
255        case OpCodes.Psi: {
256            var x = Evaluate(dataset, ref row, state);
257            if (double.IsNaN(x)) return double.NaN;
258            else if (x <= 0 && (Math.Floor(x) - x).IsAlmost(0)) return double.NaN;
259            return alglib.psi(x);
260          }
261        case OpCodes.Dawson: {
262            var x = Evaluate(dataset, ref row, state);
263            if (double.IsNaN(x)) { return double.NaN; }
264            return alglib.dawsonintegral(x);
265          }
266        case OpCodes.ExponentialIntegralEi: {
267            var x = Evaluate(dataset, ref row, state);
268            if (double.IsNaN(x)) { return double.NaN; }
269            return alglib.exponentialintegralei(x);
270          }
271        case OpCodes.SineIntegral: {
272            double si, ci;
273            var x = Evaluate(dataset, ref row, state);
274            if (double.IsNaN(x)) return double.NaN;
275            else {
276              alglib.sinecosineintegrals(x, out si, out ci);
277              return si;
278            }
279          }
280        case OpCodes.CosineIntegral: {
281            double si, ci;
282            var x = Evaluate(dataset, ref row, state);
283            if (double.IsNaN(x)) return double.NaN;
284            else {
285              alglib.sinecosineintegrals(x, out si, out ci);
286              return ci;
287            }
288          }
289        case OpCodes.HyperbolicSineIntegral: {
290            double shi, chi;
291            var x = Evaluate(dataset, ref row, state);
292            if (double.IsNaN(x)) return double.NaN;
293            else {
294              alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
295              return shi;
296            }
297          }
298        case OpCodes.HyperbolicCosineIntegral: {
299            double shi, chi;
300            var x = Evaluate(dataset, ref row, state);
301            if (double.IsNaN(x)) return double.NaN;
302            else {
303              alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
304              return chi;
305            }
306          }
307        case OpCodes.FresnelCosineIntegral: {
308            double c = 0, s = 0;
309            var x = Evaluate(dataset, ref row, state);
310            if (double.IsNaN(x)) return double.NaN;
311            else {
312              alglib.fresnelintegral(x, ref c, ref s);
313              return c;
314            }
315          }
316        case OpCodes.FresnelSineIntegral: {
317            double c = 0, s = 0;
318            var x = Evaluate(dataset, ref row, state);
319            if (double.IsNaN(x)) return double.NaN;
320            else {
321              alglib.fresnelintegral(x, ref c, ref s);
322              return s;
323            }
324          }
325        case OpCodes.AiryA: {
326            double ai, aip, bi, bip;
327            var x = Evaluate(dataset, ref row, state);
328            if (double.IsNaN(x)) return double.NaN;
329            else {
330              alglib.airy(x, out ai, out aip, out bi, out bip);
331              return ai;
332            }
333          }
334        case OpCodes.AiryB: {
335            double ai, aip, bi, bip;
336            var x = Evaluate(dataset, ref row, state);
337            if (double.IsNaN(x)) return double.NaN;
338            else {
339              alglib.airy(x, out ai, out aip, out bi, out bip);
340              return bi;
341            }
342          }
343        case OpCodes.Norm: {
344            var x = Evaluate(dataset, ref row, state);
345            if (double.IsNaN(x)) return double.NaN;
346            else return alglib.normaldistribution(x);
347          }
348        case OpCodes.Erf: {
349            var x = Evaluate(dataset, ref row, state);
350            if (double.IsNaN(x)) return double.NaN;
351            else return alglib.errorfunction(x);
352          }
353        case OpCodes.Bessel: {
354            var x = Evaluate(dataset, ref row, state);
355            if (double.IsNaN(x)) return double.NaN;
356            else return alglib.besseli0(x);
357          }
358
359        case OpCodes.AnalyticQuotient: {
360            var x1 = Evaluate(dataset, ref row, state);
361            var x2 = Evaluate(dataset, ref row, state);
362            return x1 / Math.Pow(1 + x2 * x2, 0.5);
363          }
364        case OpCodes.IfThenElse: {
365            double condition = Evaluate(dataset, ref row, state);
366            double result;
367            if (condition > 0.0) {
368              result = Evaluate(dataset, ref row, state); state.SkipInstructions();
369            } else {
370              state.SkipInstructions(); result = Evaluate(dataset, ref row, state);
371            }
372            return result;
373          }
374        case OpCodes.AND: {
375            double result = Evaluate(dataset, ref row, state);
376            for (int i = 1; i < currentInstr.nArguments; i++) {
377              if (result > 0.0) result = Evaluate(dataset, ref row, state);
378              else {
379                state.SkipInstructions();
380              }
381            }
382            return result > 0.0 ? 1.0 : -1.0;
383          }
384        case OpCodes.OR: {
385            double result = Evaluate(dataset, ref row, state);
386            for (int i = 1; i < currentInstr.nArguments; i++) {
387              if (result <= 0.0) result = Evaluate(dataset, ref row, state);
388              else {
389                state.SkipInstructions();
390              }
391            }
392            return result > 0.0 ? 1.0 : -1.0;
393          }
394        case OpCodes.NOT: {
395            return Evaluate(dataset, ref row, state) > 0.0 ? -1.0 : 1.0;
396          }
397        case OpCodes.XOR: {
398            //mkommend: XOR on multiple inputs is defined as true if the number of positive signals is odd
399            // this is equal to a consecutive execution of binary XOR operations.
400            int positiveSignals = 0;
401            for (int i = 0; i < currentInstr.nArguments; i++) {
402              if (Evaluate(dataset, ref row, state) > 0.0) { positiveSignals++; }
403            }
404            return positiveSignals % 2 != 0 ? 1.0 : -1.0;
405          }
406        case OpCodes.GT: {
407            double x = Evaluate(dataset, ref row, state);
408            double y = Evaluate(dataset, ref row, state);
409            if (x > y) { return 1.0; } else { return -1.0; }
410          }
411        case OpCodes.LT: {
412            double x = Evaluate(dataset, ref row, state);
413            double y = Evaluate(dataset, ref row, state);
414            if (x < y) { return 1.0; } else { return -1.0; }
415          }
416        case OpCodes.TimeLag: {
417            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
418            row += timeLagTreeNode.Lag;
419            double result = Evaluate(dataset, ref row, state);
420            row -= timeLagTreeNode.Lag;
421            return result;
422          }
423        case OpCodes.Integral: {
424            int savedPc = state.ProgramCounter;
425            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
426            double sum = 0.0;
427            for (int i = 0; i < Math.Abs(timeLagTreeNode.Lag); i++) {
428              row += Math.Sign(timeLagTreeNode.Lag);
429              sum += Evaluate(dataset, ref row, state);
430              state.ProgramCounter = savedPc;
431            }
432            row -= timeLagTreeNode.Lag;
433            sum += Evaluate(dataset, ref row, state);
434            return sum;
435          }
436
437        //mkommend: derivate calculation taken from:
438        //http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/
439        //one sided smooth differentiatior, N = 4
440        // y' = 1/8h (f_i + 2f_i-1, -2 f_i-3 - f_i-4)
441        case OpCodes.Derivative: {
442            int savedPc = state.ProgramCounter;
443            double f_0 = Evaluate(dataset, ref row, state); row--;
444            state.ProgramCounter = savedPc;
445            double f_1 = Evaluate(dataset, ref row, state); row -= 2;
446            state.ProgramCounter = savedPc;
447            double f_3 = Evaluate(dataset, ref row, state); row--;
448            state.ProgramCounter = savedPc;
449            double f_4 = Evaluate(dataset, ref row, state);
450            row += 4;
451
452            return (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
453          }
454        case OpCodes.Call: {
455            // evaluate sub-trees
456            double[] argValues = new double[currentInstr.nArguments];
457            for (int i = 0; i < currentInstr.nArguments; i++) {
458              argValues[i] = Evaluate(dataset, ref row, state);
459            }
460            // push on argument values on stack
461            state.CreateStackFrame(argValues);
462
463            // save the pc
464            int savedPc = state.ProgramCounter;
465            // set pc to start of function 
466            state.ProgramCounter = (ushort)currentInstr.data;
467            // evaluate the function
468            double v = Evaluate(dataset, ref row, state);
469
470            // delete the stack frame
471            state.RemoveStackFrame();
472
473            // restore the pc => evaluation will continue at point after my subtrees 
474            state.ProgramCounter = savedPc;
475            return v;
476          }
477        case OpCodes.Arg: {
478            return state.GetStackFrameValue((ushort)currentInstr.data);
479          }
480        case OpCodes.Variable: {
481            if (row < 0 || row >= dataset.Rows) return double.NaN;
482            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
483            return ((IList<double>)currentInstr.data)[row] * variableTreeNode.Weight;
484          }
485        case OpCodes.BinaryFactorVariable: {
486            if (row < 0 || row >= dataset.Rows) return double.NaN;
487            var factorVarTreeNode = currentInstr.dynamicNode as BinaryFactorVariableTreeNode;
488            return ((IList<string>)currentInstr.data)[row] == factorVarTreeNode.VariableValue ? factorVarTreeNode.Weight : 0;
489          }
490        case OpCodes.FactorVariable: {
491            if (row < 0 || row >= dataset.Rows) return double.NaN;
492            var factorVarTreeNode = currentInstr.dynamicNode as FactorVariableTreeNode;
493            return factorVarTreeNode.GetValue(((IList<string>)currentInstr.data)[row]);
494          }
495        case OpCodes.LagVariable: {
496            var laggedVariableTreeNode = (LaggedVariableTreeNode)currentInstr.dynamicNode;
497            int actualRow = row + laggedVariableTreeNode.Lag;
498            if (actualRow < 0 || actualRow >= dataset.Rows) { return double.NaN; }
499            return ((IList<double>)currentInstr.data)[actualRow] * laggedVariableTreeNode.Weight;
500          }
501        case OpCodes.Constant: {
502            var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
503            return constTreeNode.Value;
504          }
505
506        //mkommend: this symbol uses the logistic function f(x) = 1 / (1 + e^(-alpha * x) )
507        //to determine the relative amounts of the true and false branch see http://en.wikipedia.org/wiki/Logistic_function
508        case OpCodes.VariableCondition: {
509            if (row < 0 || row >= dataset.Rows) return double.NaN;
510            var variableConditionTreeNode = (VariableConditionTreeNode)currentInstr.dynamicNode;
511            if (!variableConditionTreeNode.Symbol.IgnoreSlope) {
512              double variableValue = ((IList<double>)currentInstr.data)[row];
513              double x = variableValue - variableConditionTreeNode.Threshold;
514              double p = 1 / (1 + Math.Exp(-variableConditionTreeNode.Slope * x));
515
516              double trueBranch = Evaluate(dataset, ref row, state);
517              double falseBranch = Evaluate(dataset, ref row, state);
518
519              return trueBranch * p + falseBranch * (1 - p);
520            } else {
521              // strict threshold
522              double variableValue = ((IList<double>)currentInstr.data)[row];
523              if (variableValue <= variableConditionTreeNode.Threshold) {
524                var left = Evaluate(dataset, ref row, state);
525                state.SkipInstructions();
526                return left;
527              } else {
528                state.SkipInstructions();
529                return Evaluate(dataset, ref row, state);
530              }
531            }
532          }
533
534        case OpCodes.VectorSum: {
535            DoubleVector v = VectorEvaluate(dataset, ref row, state);
536            return v.Sum();
537          }
538        case OpCodes.VectorAvg: {
539            DoubleVector v = VectorEvaluate(dataset, ref row, state);
540            return v.Average();
541          }
542
543        default:
544          throw new NotSupportedException();
545      }
546    }
547
548    public virtual DoubleVector VectorEvaluate(IDataset dataset, ref int row, InterpreterState state) {
549      Instruction currentInstr = state.NextInstruction();
550      switch (currentInstr.opCode) {
551        case OpCodes.VectorAdd: {
552            DoubleVector s = VectorEvaluate(dataset, ref row, state);
553            for (int i = 1; i < currentInstr.nArguments; i++) {
554              s += VectorEvaluate(dataset, ref row, state);
555            }
556            return s;
557          }
558        case OpCodes.VectorSub: {
559            DoubleVector s = VectorEvaluate(dataset, ref row, state);
560            for (int i = 1; i < currentInstr.nArguments; i++) {
561              s -= VectorEvaluate(dataset, ref row, state);
562            }
563            return s;
564          }
565        case OpCodes.VectorMul: {
566            DoubleVector s = VectorEvaluate(dataset, ref row, state);
567            for (int i = 1; i < currentInstr.nArguments; i++) {
568              s *= VectorEvaluate(dataset, ref row, state);
569            }
570            return s;
571          }
572        case OpCodes.VectorDiv: {
573            DoubleVector s = VectorEvaluate(dataset, ref row, state);
574            for (int i = 1; i < currentInstr.nArguments; i++) {
575              s /= VectorEvaluate(dataset, ref row, state);
576            }
577            return s;
578          }
579
580        case OpCodes.VectorVariable: {
581            if (row < 0 || row >= dataset.Rows) return new DoubleVector(new[] { double.NaN });
582            var vectorVarTreeNode = currentInstr.dynamicNode as VectorVariableTreeNode;
583            return ((IList<DoubleVector>)currentInstr.data)[row] * vectorVarTreeNode.Weight;
584          }
585        default:
586          throw new NotSupportedException();
587      }
588    }
589  }
590}
Note: See TracBrowser for help on using the repository browser.