Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2021: Reverted r9290 as it is actually slower than the initial implementation.

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