Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2021: Initial implementation of the SymbolicExpressionTreeLinearInterpreter.

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