Free cookie consent management tool by TermsFeed Policy Generator

source: branches/DataAnalysis.Symbolic.VariableImpacts/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeInterpreter.cs @ 8901

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

#1081: Corrected evaluators and time series models.

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