Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.LinqExpressionTreeInterpreter/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeInterpreter.cs @ 13222

Last change on this file since 13222 was 13222, checked in by bburlacu, 8 years ago

#2442: Removed unused code, fixed formatting, fixed bug in AfterDeserializationHook.

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