Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.DataAnalysis.Symbolic.LinearInterpreter/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeLinearInterpreter.cs @ 9290

Last change on this file since 9290 was 9290, checked in by bburlacu, 11 years ago

#2021: Removed double field from Instruction.cs, added separated double array for computing the values.

File size: 19.2 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("SymbolicDataAnalysisExpressionTreeLinearInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.")]
34  public class SymbolicDataAnalysisExpressionTreeLinearInterpreter : 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 SymbolicDataAnalysisExpressionTreeLinearInterpreter(bool deserializing) : base(deserializing) { }
65    protected SymbolicDataAnalysisExpressionTreeLinearInterpreter(SymbolicDataAnalysisExpressionTreeLinearInterpreter original, Cloner cloner) : base(original, cloner) { }
66    public override IDeepCloneable Clone(Cloner cloner) {
67      return new SymbolicDataAnalysisExpressionTreeLinearInterpreter(this, cloner);
68    }
69
70    public SymbolicDataAnalysisExpressionTreeLinearInterpreter()
71      : base("SymbolicDataAnalysisExpressionTreeLinearInterpreter", "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 SymbolicDataAnalysisExpressionTreeLinearInterpreter(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      lock (EvaluatedSolutions) {
102        EvaluatedSolutions.Value++; // increment the evaluated solutions counter
103      }
104
105      Instruction[] code = SymbolicExpressionTreeCompiler.CompilePostfix(tree, OpCodes.MapSymbolToOpCode);
106      foreach (Instruction instr in code) {
107        switch (instr.opCode) {
108          case OpCodes.Variable: {
109              var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
110              instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
111            }
112            break;
113          case OpCodes.LagVariable: {
114              var laggedVariableTreeNode = (LaggedVariableTreeNode)instr.dynamicNode;
115              instr.iArg0 = dataset.GetReadOnlyDoubleValues(laggedVariableTreeNode.VariableName);
116            }
117            break;
118          case OpCodes.VariableCondition: {
119              var variableConditionTreeNode = (VariableConditionTreeNode)instr.dynamicNode;
120              instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableConditionTreeNode.VariableName);
121            }
122            break;
123        }
124      }
125
126      double[] values = new double[code.Length];
127
128      foreach (var rowEnum in rows) {
129        int row = rowEnum;
130        yield return Evaluate(dataset, ref row, code, values);
131      }
132    }
133
134    protected virtual double Evaluate(Dataset dataset, ref int row, Instruction[] code, double[] values) {
135      int count = 0;
136      for (int j = 0; j != code.Length; ++j) {
137        Instruction currentInstr = code[j];
138
139        #region switch
140        switch (currentInstr.opCode) {
141          case OpCodes.Add: {
142              double s = values[j - currentInstr.nArguments];
143              for (int i = j - currentInstr.nArguments + 1; i < j; i++) {
144                s += values[i];
145              }
146              values[j] = s;
147            }
148            break;
149          case OpCodes.Sub: {
150              double s = values[j - currentInstr.nArguments];
151              for (int i = j - currentInstr.nArguments + 1; i < j; i++) {
152                s -= values[i];
153              }
154              if (currentInstr.nArguments == 1) s = -s;
155              values[j] = s;
156            }
157            break;
158          case OpCodes.Mul: {
159              double p = values[j - currentInstr.nArguments];
160              for (int i = j - currentInstr.nArguments + 1; i < j; i++) {
161                p *= values[i];
162              }
163              values[j] = p;
164            }
165            break;
166          case OpCodes.Div: {
167              double p = values[j - currentInstr.nArguments];
168              for (int i = j - currentInstr.nArguments + 1; i < j; i++) {
169                p /= values[i];
170              }
171              if (currentInstr.nArguments == 1) p = 1.0 / p;
172              values[j] = p;
173            }
174            break;
175          case OpCodes.Average: {
176              double sum = values[j - currentInstr.nArguments];
177              for (int i = j - currentInstr.nArguments + 1; i < j; i++) {
178                sum += values[i];
179              }
180              values[j] = sum / currentInstr.nArguments;
181            }
182            break;
183          case OpCodes.Cos: {
184              values[j] = Math.Cos(values[j - currentInstr.nArguments]);
185            }
186            break;
187          case OpCodes.Sin: {
188              values[j] = Math.Sin(values[j - currentInstr.nArguments]);
189              break;
190            }
191          case OpCodes.Tan: {
192              values[j] = Math.Tan(values[j - currentInstr.nArguments]);
193            }
194            break;
195          case OpCodes.Square: {
196              values[j] = Math.Pow(values[j - currentInstr.nArguments], 2);
197            }
198            break;
199          case OpCodes.Power: {
200              double x = values[j - currentInstr.nArguments];
201              double y = Math.Round(values[j - currentInstr.nArguments + 1]);
202              values[j] = Math.Pow(x, y);
203            }
204            break;
205          case OpCodes.SquareRoot: {
206              values[j] = Math.Sqrt(values[j - currentInstr.nArguments]);
207            }
208            break;
209          case OpCodes.Root: {
210              double x = values[j - currentInstr.nArguments];
211              double y = Math.Round(values[j - currentInstr.nArguments + 1]);
212              values[j] = Math.Pow(x, 1 / y);
213            }
214            break;
215          case OpCodes.Exp: {
216              values[j] = Math.Exp(values[j - currentInstr.nArguments]);
217            }
218            break;
219          case OpCodes.Log: {
220              values[j] = Math.Log(values[j - currentInstr.nArguments]);
221            }
222            break;
223          case OpCodes.Gamma: {
224              values[j] = double.IsNaN(values[j - currentInstr.nArguments]) ? double.NaN : alglib.gammafunction(values[j - currentInstr.nArguments]);
225            }
226            break;
227          case OpCodes.Psi: {
228              var x = values[j - currentInstr.nArguments];
229              if (double.IsNaN(x)) values[j] = double.NaN;
230              else if (x <= 0 && (Math.Floor(x) - x).IsAlmost(0)) values[j] = double.NaN;
231              else values[j] = alglib.psi(x);
232            }
233            break;
234          case OpCodes.Dawson: {
235              var x = values[j - currentInstr.nArguments];
236              values[j] = double.IsNaN(x) ? double.NaN : alglib.dawsonintegral(x);
237            }
238            break;
239          case OpCodes.ExponentialIntegralEi: {
240              var x = values[j - currentInstr.nArguments];
241              values[j] = double.IsNaN(x) ? double.NaN : alglib.exponentialintegralei(x);
242            }
243            break;
244          case OpCodes.SineIntegral: {
245              double si, ci;
246              var x = values[j - currentInstr.nArguments];
247              if (double.IsNaN(x)) values[j] = double.NaN;
248              else {
249                alglib.sinecosineintegrals(x, out si, out ci);
250                values[j] = si;
251              }
252            }
253            break;
254          case OpCodes.CosineIntegral: {
255              double si, ci;
256              var x = values[j - currentInstr.nArguments];
257              if (double.IsNaN(x)) values[j] = double.NaN;
258              else {
259                alglib.sinecosineintegrals(x, out si, out ci);
260                values[j] = ci;
261              }
262            }
263            break;
264          case OpCodes.HyperbolicSineIntegral: {
265              double shi, chi;
266              var x = values[j - currentInstr.nArguments];
267              if (double.IsNaN(x)) values[j] = double.NaN;
268              else {
269                alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
270                values[j] = shi;
271              }
272            }
273            break;
274          case OpCodes.HyperbolicCosineIntegral: {
275              double shi, chi;
276              var x = values[j - currentInstr.nArguments];
277              if (double.IsNaN(x)) values[j] = double.NaN;
278              else {
279                alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
280                values[j] = chi;
281              }
282            }
283            break;
284          case OpCodes.FresnelCosineIntegral: {
285              double c = 0, s = 0;
286              var x = values[j - currentInstr.nArguments];
287              if (double.IsNaN(x)) values[j] = double.NaN;
288              else {
289                alglib.fresnelintegral(x, ref c, ref s);
290                values[j] = c;
291              }
292            }
293            break;
294          case OpCodes.FresnelSineIntegral: {
295              double c = 0, s = 0;
296              var x = values[j - currentInstr.nArguments];
297              if (double.IsNaN(x)) values[j] = double.NaN;
298              else {
299                alglib.fresnelintegral(x, ref c, ref s);
300                values[j] = s;
301              }
302            }
303            break;
304          case OpCodes.AiryA: {
305              double ai, aip, bi, bip;
306              var x = values[j - currentInstr.nArguments];
307              if (double.IsNaN(x)) values[j] = double.NaN;
308              else {
309                alglib.airy(x, out ai, out aip, out bi, out bip);
310                values[j] = ai;
311              }
312            }
313            break;
314          case OpCodes.AiryB: {
315              double ai, aip, bi, bip;
316              var x = values[j - currentInstr.nArguments];
317              if (double.IsNaN(x)) values[j] = double.NaN;
318              else {
319                alglib.airy(x, out ai, out aip, out bi, out bip);
320                values[j] = bi;
321              }
322            }
323            break;
324          case OpCodes.Norm: {
325              var x = values[j - currentInstr.nArguments];
326              values[j] = double.IsNaN(x) ? double.NaN : alglib.normaldistribution(x);
327            }
328            break;
329          case OpCodes.Erf: {
330              var x = values[j - currentInstr.nArguments];
331              values[j] = double.IsNaN(x) ? double.NaN : alglib.errorfunction(x);
332            }
333            break;
334          case OpCodes.Bessel: {
335              var x = values[j - currentInstr.nArguments];
336              values[j] = double.IsNaN(x) ? double.NaN : alglib.besseli0(x);
337            }
338            break;
339          case OpCodes.IfThenElse: {
340              double condition = values[j - currentInstr.nArguments];
341              double result = condition > 0.0 ? values[j - currentInstr.nArguments] : values[j - currentInstr.nArguments + 1];
342              values[j] = result;
343            }
344            break;
345          case OpCodes.AND: {
346              double result = values[j - currentInstr.nArguments];
347              for (int i = j - currentInstr.nArguments + 1; i < j; i++) {
348                if (result > 0.0) result = values[i];
349              }
350              values[j] = result > 0.0 ? 1.0 : -1.0;
351            }
352            break;
353          case OpCodes.OR: {
354              double result = values[j - currentInstr.nArguments];
355              for (int i = 1; i < currentInstr.nArguments; i++) {
356                if (result <= 0.0) result = values[i]; ;
357              }
358              values[j] = result > 0.0 ? 1.0 : -1.0;
359            }
360            break;
361          case OpCodes.NOT: {
362              values[j] = values[j - currentInstr.nArguments] > 0.0 ? -1.0 : 1.0;
363            }
364            break;
365          case OpCodes.GT: {
366              double x = values[j - currentInstr.nArguments];
367              double y = values[j - currentInstr.nArguments + 1];
368              values[j] = x > y ? 1.0 : -1.0;
369            }
370            break;
371          case OpCodes.LT: {
372              double x = values[j - currentInstr.nArguments];
373              double y = values[j - currentInstr.nArguments + 1];
374              values[j] = x < y ? 1.0 : -1.0;
375            }
376            break;
377          case OpCodes.TimeLag: {
378              throw new NotSupportedException();
379            }
380          case OpCodes.Integral: {
381              throw new NotSupportedException();
382            }
383
384          //mkommend: derivate calculation taken from:
385          //http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/
386          //one sided smooth differentiatior, N = 4
387          // y' = 1/8h (f_i + 2f_i-1, -2 f_i-3 - f_i-4)
388          case OpCodes.Derivative: {
389              throw new NotSupportedException();
390            }
391          case OpCodes.Call: {
392              throw new NotSupportedException();
393            }
394          case OpCodes.Arg: {
395              throw new NotSupportedException();
396            }
397          case OpCodes.Variable: {
398              if (row < 0 || row >= dataset.Rows) values[j] = double.NaN;
399              else {
400                var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
401                values[j] = ((IList<double>)currentInstr.iArg0)[row] * variableTreeNode.Weight;
402              }
403            }
404            break;
405          case OpCodes.LagVariable: {
406              var laggedVariableTreeNode = (LaggedVariableTreeNode)currentInstr.dynamicNode;
407              int actualRow = row + laggedVariableTreeNode.Lag;
408              if (actualRow < 0 || actualRow >= dataset.Rows) values[j] = double.NaN;
409              else {
410                values[j] = ((IList<double>)currentInstr.iArg0)[actualRow] * laggedVariableTreeNode.Weight;
411              }
412            }
413            break;
414          case OpCodes.Constant: {
415              var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
416              values[j] = constTreeNode.Value;
417            }
418            break;
419
420          //mkommend: this symbol uses the logistic function f(x) = 1 / (1 + e^(-alpha * x) )
421          //to determine the relative amounts of the true and false branch see http://en.wikipedia.org/wiki/Logistic_function
422          case OpCodes.VariableCondition: {
423              if (row < 0 || row >= dataset.Rows) values[j] = double.NaN;
424              else {
425                var variableConditionTreeNode = (VariableConditionTreeNode)currentInstr.dynamicNode;
426                double variableValue = ((IList<double>)currentInstr.iArg0)[row];
427                double x = variableValue - variableConditionTreeNode.Threshold;
428                double p = 1 / (1 + Math.Exp(-variableConditionTreeNode.Slope * x));
429
430                double trueBranch = values[j - currentInstr.nArguments];
431                double falseBranch = values[j - currentInstr.nArguments + 1];
432
433                values[j] = trueBranch * p + falseBranch * (1 - p);
434              }
435            }
436            break;
437          default:
438            throw new NotSupportedException();
439        }
440        #endregion
441
442        // book-keeping in order to keep all the values correct
443        int narg = currentInstr.nArguments;
444
445        // we count the values before the next function is encountered
446        if (narg == 0) { ++count; continue; }
447
448        if (count > narg) {
449          for (int i = 1; i <= count - narg; ++i)
450            values[j - i] = values[j - i - narg];
451        }
452
453        count -= (narg - 1); // because after being evaluated the current instruction becomes a 'value'
454      }
455
456      return values[values.Length - 1];
457    }
458  }
459}
Note: See TracBrowser for help on using the repository browser.