Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TimeSeries/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/old_SymbolicDataAnalysisExpressionTreeInterpreter.cs @ 7930

Last change on this file since 7930 was 7930, checked in by mkommend, 12 years ago

#1081: Refactored symbolic expression tree interpreter in preparation for autoregressive single variate prognosis.

File size: 29.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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 HeuristicLab.Common;
26using HeuristicLab.Core;
27using HeuristicLab.Data;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
29using HeuristicLab.Parameters;
30using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
31
32namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
33  [StorableClass]
34  [Item("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.")]
35  public sealed class old_SymbolicDataAnalysisExpressionTreeInterpreter : ParameterizedNamedItem,
36    ISymbolicDataAnalysisExpressionTreeInterpreter, ISymbolicTimeSeriesPrognosisExpressionTreeInterpreter {
37    private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic";
38    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
39    #region private classes
40    private class InterpreterState {
41      private double[] argumentStack;
42      private int argumentStackPointer;
43      private Instruction[] code;
44      private int pc;
45      public int ProgramCounter {
46        get { return pc; }
47        set { pc = value; }
48      }
49      internal InterpreterState(Instruction[] code, int argumentStackSize) {
50        this.code = code;
51        this.pc = 0;
52        if (argumentStackSize > 0) {
53          this.argumentStack = new double[argumentStackSize];
54        }
55        this.argumentStackPointer = 0;
56      }
57
58      internal void Reset() {
59        this.pc = 0;
60        this.argumentStackPointer = 0;
61      }
62
63      internal Instruction NextInstruction() {
64        return code[pc++];
65      }
66      private void Push(double val) {
67        argumentStack[argumentStackPointer++] = val;
68      }
69      private double Pop() {
70        return argumentStack[--argumentStackPointer];
71      }
72
73      internal void CreateStackFrame(double[] argValues) {
74        // push in reverse order to make indexing easier
75        for (int i = argValues.Length - 1; i >= 0; i--) {
76          argumentStack[argumentStackPointer++] = argValues[i];
77        }
78        Push(argValues.Length);
79      }
80
81      internal void RemoveStackFrame() {
82        int size = (int)Pop();
83        argumentStackPointer -= size;
84      }
85
86      internal double GetStackFrameValue(ushort index) {
87        // layout of stack:
88        // [0]   <- argumentStackPointer
89        // [StackFrameSize = N + 1]
90        // [Arg0] <- argumentStackPointer - 2 - 0
91        // [Arg1] <- argumentStackPointer - 2 - 1
92        // [...]
93        // [ArgN] <- argumentStackPointer - 2 - N
94        // <Begin of stack frame>
95        return argumentStack[argumentStackPointer - index - 2];
96      }
97    }
98    private class OpCodes {
99      public const byte Add = 1;
100      public const byte Sub = 2;
101      public const byte Mul = 3;
102      public const byte Div = 4;
103
104      public const byte Sin = 5;
105      public const byte Cos = 6;
106      public const byte Tan = 7;
107
108      public const byte Log = 8;
109      public const byte Exp = 9;
110
111      public const byte IfThenElse = 10;
112
113      public const byte GT = 11;
114      public const byte LT = 12;
115
116      public const byte AND = 13;
117      public const byte OR = 14;
118      public const byte NOT = 15;
119
120
121      public const byte Average = 16;
122
123      public const byte Call = 17;
124
125      public const byte Variable = 18;
126      public const byte LagVariable = 19;
127      public const byte Constant = 20;
128      public const byte Arg = 21;
129
130      public const byte Power = 22;
131      public const byte Root = 23;
132      public const byte TimeLag = 24;
133      public const byte Integral = 25;
134      public const byte Derivative = 26;
135
136      public const byte VariableCondition = 27;
137
138      public const byte Square = 28;
139      public const byte SquareRoot = 29;
140      public const byte Gamma = 30;
141      public const byte Psi = 31;
142      public const byte Dawson = 32;
143      public const byte ExponentialIntegralEi = 33;
144      public const byte CosineIntegral = 34;
145      public const byte SineIntegral = 35;
146      public const byte HyperbolicCosineIntegral = 36;
147      public const byte HyperbolicSineIntegral = 37;
148      public const byte FresnelCosineIntegral = 38;
149      public const byte FresnelSineIntegral = 39;
150      public const byte AiryA = 40;
151      public const byte AiryB = 41;
152      public const byte Norm = 42;
153      public const byte Erf = 43;
154      public const byte Bessel = 44;
155    }
156    #endregion
157
158    private Dictionary<Type, byte> symbolToOpcode = new Dictionary<Type, byte>() {
159      { typeof(Addition), OpCodes.Add },
160      { typeof(Subtraction), OpCodes.Sub },
161      { typeof(Multiplication), OpCodes.Mul },
162      { typeof(Division), OpCodes.Div },
163      { typeof(Sine), OpCodes.Sin },
164      { typeof(Cosine), OpCodes.Cos },
165      { typeof(Tangent), OpCodes.Tan },
166      { typeof(Logarithm), OpCodes.Log },
167      { typeof(Exponential), OpCodes.Exp },
168      { typeof(IfThenElse), OpCodes.IfThenElse },
169      { typeof(GreaterThan), OpCodes.GT },
170      { typeof(LessThan), OpCodes.LT },
171      { typeof(And), OpCodes.AND },
172      { typeof(Or), OpCodes.OR },
173      { typeof(Not), OpCodes.NOT},
174      { typeof(Average), OpCodes.Average},
175      { typeof(InvokeFunction), OpCodes.Call },
176      { typeof(Variable), OpCodes.Variable },
177      { typeof(LaggedVariable), OpCodes.LagVariable },
178      { typeof(Constant), OpCodes.Constant },
179      { typeof(Argument), OpCodes.Arg },
180      { typeof(Power),OpCodes.Power},
181      { typeof(Root),OpCodes.Root},
182      { typeof(TimeLag), OpCodes.TimeLag},
183      { typeof(Integral), OpCodes.Integral},
184      { typeof(Derivative), OpCodes.Derivative},
185      { typeof(VariableCondition),OpCodes.VariableCondition},
186      { typeof(Square),OpCodes.Square},
187      { typeof(SquareRoot),OpCodes.SquareRoot},
188      { typeof(Gamma), OpCodes.Gamma },
189      { typeof(Psi), OpCodes.Psi },
190      { typeof(Dawson), OpCodes.Dawson},
191      { typeof(ExponentialIntegralEi), OpCodes.ExponentialIntegralEi },
192      { typeof(CosineIntegral), OpCodes.CosineIntegral },
193      { typeof(SineIntegral), OpCodes.SineIntegral },
194      { typeof(HyperbolicCosineIntegral), OpCodes.HyperbolicCosineIntegral },
195      { typeof(HyperbolicSineIntegral), OpCodes.HyperbolicSineIntegral },
196      { typeof(FresnelCosineIntegral), OpCodes.FresnelCosineIntegral },
197      { typeof(FresnelSineIntegral), OpCodes.FresnelSineIntegral },
198      { typeof(AiryA), OpCodes.AiryA },
199      { typeof(AiryB), OpCodes.AiryB },
200      { typeof(Norm), OpCodes.Norm},
201      { typeof(Erf), OpCodes.Erf},
202      { typeof(Bessel), OpCodes.Bessel}     
203    };
204
205    public override bool CanChangeName {
206      get { return false; }
207    }
208    public override bool CanChangeDescription {
209      get { return false; }
210    }
211
212    #region parameter properties
213    public IValueParameter<BoolValue> CheckExpressionsWithIntervalArithmeticParameter {
214      get { return (IValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
215    }
216
217    public IValueParameter<IntValue> EvaluatedSolutionsParameter {
218      get { return (IValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
219    }
220    #endregion
221
222    #region properties
223    public BoolValue CheckExpressionsWithIntervalArithmetic {
224      get { return CheckExpressionsWithIntervalArithmeticParameter.Value; }
225      set { CheckExpressionsWithIntervalArithmeticParameter.Value = value; }
226    }
227
228    public IntValue EvaluatedSolutions {
229      get { return EvaluatedSolutionsParameter.Value; }
230      set { EvaluatedSolutionsParameter.Value = value; }
231    }
232    #endregion
233
234    [StorableConstructor]
235    private old_SymbolicDataAnalysisExpressionTreeInterpreter(bool deserializing) : base(deserializing) { }
236    private old_SymbolicDataAnalysisExpressionTreeInterpreter(old_SymbolicDataAnalysisExpressionTreeInterpreter original, Cloner cloner) : base(original, cloner) { }
237    public override IDeepCloneable Clone(Cloner cloner) {
238      return new old_SymbolicDataAnalysisExpressionTreeInterpreter(this, cloner);
239    }
240
241    public old_SymbolicDataAnalysisExpressionTreeInterpreter()
242      : base("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.") {
243      Parameters.Add(new ValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false)));
244      Parameters.Add(new ValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
245    }
246
247    [StorableHook(HookType.AfterDeserialization)]
248    private void AfterDeserialization() {
249      if (!Parameters.ContainsKey(EvaluatedSolutionsParameterName))
250        Parameters.Add(new ValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
251    }
252
253    #region IStatefulItem
254    public void InitializeState() {
255      EvaluatedSolutions.Value = 0;
256    }
257
258    public void ClearState() {
259    }
260    #endregion
261
262    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, IEnumerable<int> rows) {
263      return GetSymbolicExpressionTreeValues(tree, dataset, new string[] { "#NOTHING#" }, rows);
264    }
265
266    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, string[] targetVariables, IEnumerable<int> rows) {
267      return GetSymbolicExpressionTreeValues(tree, dataset, targetVariables, rows, 1);
268    }
269
270
271    // for each row for each horizon for each target variable one value
272    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, string[] targetVariables, IEnumerable<int> rows, int horizon) {
273      if (CheckExpressionsWithIntervalArithmetic.Value)
274        throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
275      EvaluatedSolutions.Value++; // increment the evaluated solutions counter     
276      Instruction[] code = SymbolicExpressionTreeCompiler.Compile(tree, MapSymbolToOpCode);
277      int necessaryArgStackSize = 0;
278      for (int i = 0; i < code.Length; i++) {
279        Instruction instr = code[i];
280        if (instr.opCode == OpCodes.Variable) {
281          var variableTreeNode = instr.dynamicNode as VariableTreeNode;
282          instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
283          code[i] = instr;
284        } else if (instr.opCode == OpCodes.LagVariable) {
285          var laggedVariableTreeNode = instr.dynamicNode as LaggedVariableTreeNode;
286          instr.iArg0 = dataset.GetReadOnlyDoubleValues(laggedVariableTreeNode.VariableName);
287          code[i] = instr;
288        } else if (instr.opCode == OpCodes.VariableCondition) {
289          var variableConditionTreeNode = instr.dynamicNode as VariableConditionTreeNode;
290          instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableConditionTreeNode.VariableName);
291        } else if (instr.opCode == OpCodes.Call) {
292          necessaryArgStackSize += instr.nArguments + 1;
293        }
294      }
295      var state = new InterpreterState(code, necessaryArgStackSize);
296
297      int nComponents = tree.Root.GetSubtree(0).SubtreeCount;
298      // produce a n-step forecast for each target variable for all rows
299      var cachedPrognosedValues = new Dictionary<string, double[]>();
300      foreach (var targetVariable in targetVariables)
301        cachedPrognosedValues[targetVariable] = new double[horizon];
302      foreach (var rowEnum in rows) {
303        int row = rowEnum;
304        foreach (var horizonRow in Enumerable.Range(row, horizon)) {
305          int localRow = horizonRow; // create a local variable for the ref parameter
306          for (int c = 0; c < nComponents; c++) {
307            var prog = Evaluate(dataset, ref localRow, row - 1, state, cachedPrognosedValues);
308            yield return prog;
309            cachedPrognosedValues[targetVariables[c]][horizonRow - row] = prog;
310          }
311
312          state.Reset();
313        }
314      }
315    }
316
317    private double Evaluate(Dataset dataset, ref int row, int lastObservedRow, InterpreterState state, Dictionary<string, double[]> cachedPrognosedValues) {
318      Instruction currentInstr = state.NextInstruction();
319      switch (currentInstr.opCode) {
320        case OpCodes.Add: {
321            double s = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
322            for (int i = 1; i < currentInstr.nArguments; i++) {
323              s += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
324            }
325            return s;
326          }
327        case OpCodes.Sub: {
328            double s = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
329            for (int i = 1; i < currentInstr.nArguments; i++) {
330              s -= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
331            }
332            if (currentInstr.nArguments == 1) s = -s;
333            return s;
334          }
335        case OpCodes.Mul: {
336            double p = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
337            for (int i = 1; i < currentInstr.nArguments; i++) {
338              p *= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
339            }
340            return p;
341          }
342        case OpCodes.Div: {
343            double p = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
344            for (int i = 1; i < currentInstr.nArguments; i++) {
345              p /= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
346            }
347            if (currentInstr.nArguments == 1) p = 1.0 / p;
348            return p;
349          }
350        case OpCodes.Average: {
351            double sum = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
352            for (int i = 1; i < currentInstr.nArguments; i++) {
353              sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
354            }
355            return sum / currentInstr.nArguments;
356          }
357        case OpCodes.Cos: {
358            return Math.Cos(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
359          }
360        case OpCodes.Sin: {
361            return Math.Sin(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
362          }
363        case OpCodes.Tan: {
364            return Math.Tan(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
365          }
366        case OpCodes.Square: {
367            return Math.Pow(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues), 2);
368          }
369        case OpCodes.Power: {
370            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
371            double y = Math.Round(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
372            return Math.Pow(x, y);
373          }
374        case OpCodes.SquareRoot: {
375            return Math.Sqrt(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
376          }
377        case OpCodes.Root: {
378            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
379            double y = Math.Round(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
380            return Math.Pow(x, 1 / y);
381          }
382        case OpCodes.Exp: {
383            return Math.Exp(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
384          }
385        case OpCodes.Log: {
386            return Math.Log(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
387          }
388        case OpCodes.Gamma: {
389            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
390            if (double.IsNaN(x)) return double.NaN;
391            else return alglib.gammafunction(x);
392          }
393        case OpCodes.Psi: {
394            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
395            if (double.IsNaN(x)) return double.NaN;
396            else if (x.IsAlmost(0.0)) return double.NaN;
397            else if ((Math.Floor(x) - x).IsAlmost(0)) return double.NaN;
398            return alglib.psi(x);
399          }
400        case OpCodes.Dawson: {
401            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
402            if (double.IsNaN(x)) return double.NaN;
403            return alglib.dawsonintegral(x);
404          }
405        case OpCodes.ExponentialIntegralEi: {
406            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
407            if (double.IsNaN(x)) return double.NaN;
408            return alglib.exponentialintegralei(x);
409          }
410        case OpCodes.SineIntegral: {
411            double si, ci;
412            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
413            if (double.IsNaN(x)) return double.NaN;
414            else {
415              alglib.sinecosineintegrals(x, out si, out ci);
416              return si;
417            }
418          }
419        case OpCodes.CosineIntegral: {
420            double si, ci;
421            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
422            if (double.IsNaN(x)) return double.NaN;
423            else {
424              alglib.sinecosineintegrals(x, out si, out ci);
425              return ci;
426            }
427          }
428        case OpCodes.HyperbolicSineIntegral: {
429            double shi, chi;
430            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
431            if (double.IsNaN(x)) return double.NaN;
432            else {
433              alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
434              return shi;
435            }
436          }
437        case OpCodes.HyperbolicCosineIntegral: {
438            double shi, chi;
439            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
440            if (double.IsNaN(x)) return double.NaN;
441            else {
442              alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
443              return chi;
444            }
445          }
446        case OpCodes.FresnelCosineIntegral: {
447            double c = 0, s = 0;
448            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
449            if (double.IsNaN(x)) return double.NaN;
450            else {
451              alglib.fresnelintegral(x, ref c, ref s);
452              return c;
453            }
454          }
455        case OpCodes.FresnelSineIntegral: {
456            double c = 0, s = 0;
457            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
458            if (double.IsNaN(x)) return double.NaN;
459            else {
460              alglib.fresnelintegral(x, ref c, ref s);
461              return s;
462            }
463          }
464        case OpCodes.AiryA: {
465            double ai, aip, bi, bip;
466            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
467            if (double.IsNaN(x)) return double.NaN;
468            else {
469              alglib.airy(x, out ai, out aip, out bi, out bip);
470              return ai;
471            }
472          }
473        case OpCodes.AiryB: {
474            double ai, aip, bi, bip;
475            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
476            if (double.IsNaN(x)) return double.NaN;
477            else {
478              alglib.airy(x, out ai, out aip, out bi, out bip);
479              return bi;
480            }
481          }
482        case OpCodes.Norm: {
483            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
484            if (double.IsNaN(x)) return double.NaN;
485            else return alglib.normaldistribution(x);
486          }
487        case OpCodes.Erf: {
488            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
489            if (double.IsNaN(x)) return double.NaN;
490            else return alglib.errorfunction(x);
491          }
492        case OpCodes.Bessel: {
493            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
494            if (double.IsNaN(x)) return double.NaN;
495            else return alglib.besseli0(x);
496          }
497        case OpCodes.IfThenElse: {
498            double condition = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
499            double result;
500            if (condition > 0.0) {
501              result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); SkipInstructions(state);
502            } else {
503              SkipInstructions(state); result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
504            }
505            return result;
506          }
507        case OpCodes.AND: {
508            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
509            for (int i = 1; i < currentInstr.nArguments; i++) {
510              if (result > 0.0) result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
511              else {
512                SkipInstructions(state);
513              }
514            }
515            return result > 0.0 ? 1.0 : -1.0;
516          }
517        case OpCodes.OR: {
518            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
519            for (int i = 1; i < currentInstr.nArguments; i++) {
520              if (result <= 0.0) result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
521              else {
522                SkipInstructions(state);
523              }
524            }
525            return result > 0.0 ? 1.0 : -1.0;
526          }
527        case OpCodes.NOT: {
528            return Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues) > 0.0 ? -1.0 : 1.0;
529          }
530        case OpCodes.GT: {
531            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
532            double y = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
533            if (x > y) return 1.0;
534            else return -1.0;
535          }
536        case OpCodes.LT: {
537            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
538            double y = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
539            if (x < y) return 1.0;
540            else return -1.0;
541          }
542        case OpCodes.TimeLag: {
543            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
544            row += timeLagTreeNode.Lag;
545            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
546            row -= timeLagTreeNode.Lag;
547            return result;
548          }
549        case OpCodes.Integral: {
550            int savedPc = state.ProgramCounter;
551            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
552            double sum = 0.0;
553            for (int i = 0; i < Math.Abs(timeLagTreeNode.Lag); i++) {
554              row += Math.Sign(timeLagTreeNode.Lag);
555              sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
556              state.ProgramCounter = savedPc;
557            }
558            row -= timeLagTreeNode.Lag;
559            sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
560            return sum;
561          }
562
563        //mkommend: derivate calculation taken from:
564        //http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/
565        //one sided smooth differentiatior, N = 4
566        // y' = 1/8h (f_i + 2f_i-1, -2 f_i-3 - f_i-4)
567        case OpCodes.Derivative: {
568            int savedPc = state.ProgramCounter;
569            double f_0 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row--;
570            state.ProgramCounter = savedPc;
571            double f_1 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row -= 2;
572            state.ProgramCounter = savedPc;
573            double f_3 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row--;
574            state.ProgramCounter = savedPc;
575            double f_4 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
576            row += 4;
577
578            return (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
579          }
580        case OpCodes.Call: {
581            // evaluate sub-trees
582            double[] argValues = new double[currentInstr.nArguments];
583            for (int i = 0; i < currentInstr.nArguments; i++) {
584              argValues[i] = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
585            }
586            // push on argument values on stack
587            state.CreateStackFrame(argValues);
588
589            // save the pc
590            int savedPc = state.ProgramCounter;
591            // set pc to start of function 
592            state.ProgramCounter = (ushort)currentInstr.iArg0;
593            // evaluate the function
594            double v = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
595
596            // delete the stack frame
597            state.RemoveStackFrame();
598
599            // restore the pc => evaluation will continue at point after my subtrees 
600            state.ProgramCounter = savedPc;
601            return v;
602          }
603        case OpCodes.Arg: {
604            return state.GetStackFrameValue((ushort)currentInstr.iArg0);
605          }
606        case OpCodes.Variable: {
607            if (row < 0 || row >= dataset.Rows)
608              return double.NaN;
609            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
610            if (row <= lastObservedRow || !cachedPrognosedValues.ContainsKey(variableTreeNode.VariableName)) return ((IList<double>)currentInstr.iArg0)[row] * variableTreeNode.Weight;
611            else return cachedPrognosedValues[variableTreeNode.VariableName][row - lastObservedRow - 1] * variableTreeNode.Weight;
612          }
613        case OpCodes.LagVariable: {
614            var laggedVariableTreeNode = (LaggedVariableTreeNode)currentInstr.dynamicNode;
615            int actualRow = row + laggedVariableTreeNode.Lag;
616            if (actualRow < 0 || actualRow >= dataset.Rows)
617              return double.NaN;
618            if (actualRow <= lastObservedRow || !cachedPrognosedValues.ContainsKey(laggedVariableTreeNode.VariableName)) return ((IList<double>)currentInstr.iArg0)[actualRow] * laggedVariableTreeNode.Weight;
619            else return cachedPrognosedValues[laggedVariableTreeNode.VariableName][actualRow - lastObservedRow - 1] * laggedVariableTreeNode.Weight;
620          }
621        case OpCodes.Constant: {
622            var constTreeNode = currentInstr.dynamicNode as ConstantTreeNode;
623            return constTreeNode.Value;
624          }
625
626        //mkommend: this symbol uses the logistic function f(x) = 1 / (1 + e^(-alpha * x) )
627        //to determine the relative amounts of the true and false branch see http://en.wikipedia.org/wiki/Logistic_function
628        case OpCodes.VariableCondition: {
629            if (row < 0 || row >= dataset.Rows)
630              return double.NaN;
631            var variableConditionTreeNode = (VariableConditionTreeNode)currentInstr.dynamicNode;
632            double variableValue;
633            if (row <= lastObservedRow || !cachedPrognosedValues.ContainsKey(variableConditionTreeNode.VariableName))
634              variableValue = ((IList<double>)currentInstr.iArg0)[row];
635            else
636              variableValue = cachedPrognosedValues[variableConditionTreeNode.VariableName][row - lastObservedRow - 1];
637
638            double x = variableValue - variableConditionTreeNode.Threshold;
639            double p = 1 / (1 + Math.Exp(-variableConditionTreeNode.Slope * x));
640
641            double trueBranch = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
642            double falseBranch = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
643
644            return trueBranch * p + falseBranch * (1 - p);
645          }
646        default: throw new NotSupportedException();
647      }
648    }
649
650    private byte MapSymbolToOpCode(ISymbolicExpressionTreeNode treeNode) {
651      if (symbolToOpcode.ContainsKey(treeNode.Symbol.GetType()))
652        return symbolToOpcode[treeNode.Symbol.GetType()];
653      else
654        throw new NotSupportedException("Symbol: " + treeNode.Symbol);
655    }
656
657    // skips a whole branch
658    private void SkipInstructions(InterpreterState state) {
659      int i = 1;
660      while (i > 0) {
661        i += state.NextInstruction().nArguments;
662        i--;
663      }
664    }
665  }
666}
Note: See TracBrowser for help on using the repository browser.