Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TimeSeries/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeInterpreter.cs @ 7843

Last change on this file since 7843 was 7843, checked in by gkronber, 12 years ago

#1081: fixed compile errors after merging changes from the trunk

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 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 SymbolicDataAnalysisExpressionTreeInterpreter(bool deserializing) : base(deserializing) { }
236    private SymbolicDataAnalysisExpressionTreeInterpreter(SymbolicDataAnalysisExpressionTreeInterpreter original, Cloner cloner) : base(original, cloner) { }
237    public override IDeepCloneable Clone(Cloner cloner) {
238      return new SymbolicDataAnalysisExpressionTreeInterpreter(this, cloner);
239    }
240
241    public 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      var compiler = new SymbolicExpressionTreeCompiler();
277      Instruction[] code = compiler.Compile(tree, MapSymbolToOpCode);
278      int necessaryArgStackSize = 0;
279      for (int i = 0; i < code.Length; i++) {
280        Instruction instr = code[i];
281        if (instr.opCode == OpCodes.Variable) {
282          var variableTreeNode = instr.dynamicNode as VariableTreeNode;
283          instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
284          code[i] = instr;
285        } else if (instr.opCode == OpCodes.LagVariable) {
286          var laggedVariableTreeNode = instr.dynamicNode as LaggedVariableTreeNode;
287          instr.iArg0 = dataset.GetReadOnlyDoubleValues(laggedVariableTreeNode.VariableName);
288          code[i] = instr;
289        } else if (instr.opCode == OpCodes.VariableCondition) {
290          var variableConditionTreeNode = instr.dynamicNode as VariableConditionTreeNode;
291          instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableConditionTreeNode.VariableName);
292        } else if (instr.opCode == OpCodes.Call) {
293          necessaryArgStackSize += instr.nArguments + 1;
294        }
295      }
296      var state = new InterpreterState(code, necessaryArgStackSize);
297
298      int nComponents = tree.Root.GetSubtree(0).SubtreeCount;
299      // produce a n-step forecast for each target variable for all rows
300      var cachedPrognosedValues = new Dictionary<string, double[]>();
301      foreach (var targetVariable in targetVariables)
302        cachedPrognosedValues[targetVariable] = new double[horizon];
303      foreach (var rowEnum in rows) {
304        int row = rowEnum;
305        foreach (var horizonRow in Enumerable.Range(row, horizon)) {
306          int localRow = horizonRow; // create a local variable for the ref parameter
307          for (int c = 0; c < nComponents; c++) {
308            var prog = Evaluate(dataset, ref localRow, row - 1, state, cachedPrognosedValues);
309            yield return prog;
310            cachedPrognosedValues[targetVariables[c]][horizonRow - row] = prog;
311          }
312
313          state.Reset();
314        }
315      }
316    }
317
318    private double Evaluate(Dataset dataset, ref int row, int lastObservedRow, InterpreterState state, Dictionary<string, double[]> cachedPrognosedValues) {
319      Instruction currentInstr = state.NextInstruction();
320      switch (currentInstr.opCode) {
321        case OpCodes.Add: {
322            double s = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
323            for (int i = 1; i < currentInstr.nArguments; i++) {
324              s += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
325            }
326            return s;
327          }
328        case OpCodes.Sub: {
329            double s = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
330            for (int i = 1; i < currentInstr.nArguments; i++) {
331              s -= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
332            }
333            if (currentInstr.nArguments == 1) s = -s;
334            return s;
335          }
336        case OpCodes.Mul: {
337            double p = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
338            for (int i = 1; i < currentInstr.nArguments; i++) {
339              p *= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
340            }
341            return p;
342          }
343        case OpCodes.Div: {
344            double p = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
345            for (int i = 1; i < currentInstr.nArguments; i++) {
346              p /= Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
347            }
348            if (currentInstr.nArguments == 1) p = 1.0 / p;
349            return p;
350          }
351        case OpCodes.Average: {
352            double sum = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
353            for (int i = 1; i < currentInstr.nArguments; i++) {
354              sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
355            }
356            return sum / currentInstr.nArguments;
357          }
358        case OpCodes.Cos: {
359            return Math.Cos(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
360          }
361        case OpCodes.Sin: {
362            return Math.Sin(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
363          }
364        case OpCodes.Tan: {
365            return Math.Tan(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
366          }
367        case OpCodes.Square: {
368            return Math.Pow(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues), 2);
369          }
370        case OpCodes.Power: {
371            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
372            double y = Math.Round(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
373            return Math.Pow(x, y);
374          }
375        case OpCodes.SquareRoot: {
376            return Math.Sqrt(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
377          }
378        case OpCodes.Root: {
379            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
380            double y = Math.Round(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
381            return Math.Pow(x, 1 / y);
382          }
383        case OpCodes.Exp: {
384            return Math.Exp(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
385          }
386        case OpCodes.Log: {
387            return Math.Log(Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues));
388          }
389        case OpCodes.Gamma: {
390            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
391            if (double.IsNaN(x)) return double.NaN;
392            else return alglib.gammafunction(x);
393          }
394        case OpCodes.Psi: {
395            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
396            if (double.IsNaN(x)) return double.NaN;
397            else if (x.IsAlmost(0.0)) return double.NaN;
398            else if ((Math.Floor(x) - x).IsAlmost(0)) return double.NaN;
399            return alglib.psi(x);
400          }
401        case OpCodes.Dawson: {
402            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
403            if (double.IsNaN(x)) return double.NaN;
404            return alglib.dawsonintegral(x);
405          }
406        case OpCodes.ExponentialIntegralEi: {
407            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
408            if (double.IsNaN(x)) return double.NaN;
409            return alglib.exponentialintegralei(x);
410          }
411        case OpCodes.SineIntegral: {
412            double si, ci;
413            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
414            if (double.IsNaN(x)) return double.NaN;
415            else {
416              alglib.sinecosineintegrals(x, out si, out ci);
417              return si;
418            }
419          }
420        case OpCodes.CosineIntegral: {
421            double si, ci;
422            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
423            if (double.IsNaN(x)) return double.NaN;
424            else {
425              alglib.sinecosineintegrals(x, out si, out ci);
426              return ci;
427            }
428          }
429        case OpCodes.HyperbolicSineIntegral: {
430            double shi, chi;
431            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
432            if (double.IsNaN(x)) return double.NaN;
433            else {
434              alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
435              return shi;
436            }
437          }
438        case OpCodes.HyperbolicCosineIntegral: {
439            double shi, chi;
440            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
441            if (double.IsNaN(x)) return double.NaN;
442            else {
443              alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
444              return chi;
445            }
446          }
447        case OpCodes.FresnelCosineIntegral: {
448            double c = 0, s = 0;
449            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
450            if (double.IsNaN(x)) return double.NaN;
451            else {
452              alglib.fresnelintegral(x, ref c, ref s);
453              return c;
454            }
455          }
456        case OpCodes.FresnelSineIntegral: {
457            double c = 0, s = 0;
458            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
459            if (double.IsNaN(x)) return double.NaN;
460            else {
461              alglib.fresnelintegral(x, ref c, ref s);
462              return s;
463            }
464          }
465        case OpCodes.AiryA: {
466            double ai, aip, bi, bip;
467            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
468            if (double.IsNaN(x)) return double.NaN;
469            else {
470              alglib.airy(x, out ai, out aip, out bi, out bip);
471              return ai;
472            }
473          }
474        case OpCodes.AiryB: {
475            double ai, aip, bi, bip;
476            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
477            if (double.IsNaN(x)) return double.NaN;
478            else {
479              alglib.airy(x, out ai, out aip, out bi, out bip);
480              return bi;
481            }
482          }
483        case OpCodes.Norm: {
484            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
485            if (double.IsNaN(x)) return double.NaN;
486            else return alglib.normaldistribution(x);
487          }
488        case OpCodes.Erf: {
489            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
490            if (double.IsNaN(x)) return double.NaN;
491            else return alglib.errorfunction(x);
492          }
493        case OpCodes.Bessel: {
494            var x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
495            if (double.IsNaN(x)) return double.NaN;
496            else return alglib.besseli0(x);
497          }
498        case OpCodes.IfThenElse: {
499            double condition = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
500            double result;
501            if (condition > 0.0) {
502              result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); SkipInstructions(state);
503            } else {
504              SkipInstructions(state); result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
505            }
506            return result;
507          }
508        case OpCodes.AND: {
509            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
510            for (int i = 1; i < currentInstr.nArguments; i++) {
511              if (result > 0.0) result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
512              else {
513                SkipInstructions(state);
514              }
515            }
516            return result > 0.0 ? 1.0 : -1.0;
517          }
518        case OpCodes.OR: {
519            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
520            for (int i = 1; i < currentInstr.nArguments; i++) {
521              if (result <= 0.0) result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
522              else {
523                SkipInstructions(state);
524              }
525            }
526            return result > 0.0 ? 1.0 : -1.0;
527          }
528        case OpCodes.NOT: {
529            return Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues) > 0.0 ? -1.0 : 1.0;
530          }
531        case OpCodes.GT: {
532            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
533            double y = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
534            if (x > y) return 1.0;
535            else return -1.0;
536          }
537        case OpCodes.LT: {
538            double x = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
539            double y = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
540            if (x < y) return 1.0;
541            else return -1.0;
542          }
543        case OpCodes.TimeLag: {
544            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
545            row += timeLagTreeNode.Lag;
546            double result = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
547            row -= timeLagTreeNode.Lag;
548            return result;
549          }
550        case OpCodes.Integral: {
551            int savedPc = state.ProgramCounter;
552            var timeLagTreeNode = (LaggedTreeNode)currentInstr.dynamicNode;
553            double sum = 0.0;
554            for (int i = 0; i < Math.Abs(timeLagTreeNode.Lag); i++) {
555              row += Math.Sign(timeLagTreeNode.Lag);
556              sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
557              state.ProgramCounter = savedPc;
558            }
559            row -= timeLagTreeNode.Lag;
560            sum += Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
561            return sum;
562          }
563
564        //mkommend: derivate calculation taken from:
565        //http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/
566        //one sided smooth differentiatior, N = 4
567        // y' = 1/8h (f_i + 2f_i-1, -2 f_i-3 - f_i-4)
568        case OpCodes.Derivative: {
569            int savedPc = state.ProgramCounter;
570            double f_0 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row--;
571            state.ProgramCounter = savedPc;
572            double f_1 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row -= 2;
573            state.ProgramCounter = savedPc;
574            double f_3 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues); row--;
575            state.ProgramCounter = savedPc;
576            double f_4 = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
577            row += 4;
578
579            return (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
580          }
581        case OpCodes.Call: {
582            // evaluate sub-trees
583            double[] argValues = new double[currentInstr.nArguments];
584            for (int i = 0; i < currentInstr.nArguments; i++) {
585              argValues[i] = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
586            }
587            // push on argument values on stack
588            state.CreateStackFrame(argValues);
589
590            // save the pc
591            int savedPc = state.ProgramCounter;
592            // set pc to start of function 
593            state.ProgramCounter = (ushort)currentInstr.iArg0;
594            // evaluate the function
595            double v = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
596
597            // delete the stack frame
598            state.RemoveStackFrame();
599
600            // restore the pc => evaluation will continue at point after my subtrees 
601            state.ProgramCounter = savedPc;
602            return v;
603          }
604        case OpCodes.Arg: {
605            return state.GetStackFrameValue((ushort)currentInstr.iArg0);
606          }
607        case OpCodes.Variable: {
608            if (row < 0 || row >= dataset.Rows)
609              return double.NaN;
610            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
611            if (row <= lastObservedRow || !cachedPrognosedValues.ContainsKey(variableTreeNode.VariableName)) return ((IList<double>)currentInstr.iArg0)[row] * variableTreeNode.Weight;
612            else return cachedPrognosedValues[variableTreeNode.VariableName][row - lastObservedRow - 1] * variableTreeNode.Weight;
613          }
614        case OpCodes.LagVariable: {
615            var laggedVariableTreeNode = (LaggedVariableTreeNode)currentInstr.dynamicNode;
616            int actualRow = row + laggedVariableTreeNode.Lag;
617            if (actualRow < 0 || actualRow >= dataset.Rows)
618              return double.NaN;
619            if (actualRow <= lastObservedRow || !cachedPrognosedValues.ContainsKey(laggedVariableTreeNode.VariableName)) return ((IList<double>)currentInstr.iArg0)[actualRow] * laggedVariableTreeNode.Weight;
620            else return cachedPrognosedValues[laggedVariableTreeNode.VariableName][actualRow - lastObservedRow - 1] * laggedVariableTreeNode.Weight;
621          }
622        case OpCodes.Constant: {
623            var constTreeNode = currentInstr.dynamicNode as ConstantTreeNode;
624            return constTreeNode.Value;
625          }
626
627        //mkommend: this symbol uses the logistic function f(x) = 1 / (1 + e^(-alpha * x) )
628        //to determine the relative amounts of the true and false branch see http://en.wikipedia.org/wiki/Logistic_function
629        case OpCodes.VariableCondition: {
630            if (row < 0 || row >= dataset.Rows)
631              return double.NaN;
632            var variableConditionTreeNode = (VariableConditionTreeNode)currentInstr.dynamicNode;
633            double variableValue;
634            if (row <= lastObservedRow || !cachedPrognosedValues.ContainsKey(variableConditionTreeNode.VariableName))
635              variableValue = ((IList<double>)currentInstr.iArg0)[row];
636            else
637              variableValue = cachedPrognosedValues[variableConditionTreeNode.VariableName][row - lastObservedRow - 1];
638
639            double x = variableValue - variableConditionTreeNode.Threshold;
640            double p = 1 / (1 + Math.Exp(-variableConditionTreeNode.Slope * x));
641
642            double trueBranch = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
643            double falseBranch = Evaluate(dataset, ref row, lastObservedRow, state, cachedPrognosedValues);
644
645            return trueBranch * p + falseBranch * (1 - p);
646          }
647        default: throw new NotSupportedException();
648      }
649    }
650
651    private byte MapSymbolToOpCode(ISymbolicExpressionTreeNode treeNode) {
652      if (symbolToOpcode.ContainsKey(treeNode.Symbol.GetType()))
653        return symbolToOpcode[treeNode.Symbol.GetType()];
654      else
655        throw new NotSupportedException("Symbol: " + treeNode.Symbol);
656    }
657
658    // skips a whole branch
659    private void SkipInstructions(InterpreterState state) {
660      int i = 1;
661      while (i > 0) {
662        i += state.NextInstruction().nArguments;
663        i--;
664      }
665    }
666  }
667}
Note: See TracBrowser for help on using the repository browser.