Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Sliding Window GP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeLinearInterpreter.cs @ 10217

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

#1837: Merged trunk changes and fixed sliding window visualization.

File size: 18.7 KB
RevLine 
[5571]1#region License Information
2/* HeuristicLab
[9734]3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[5571]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;
[9739]24using System.Linq;
[5571]25using HeuristicLab.Common;
26using HeuristicLab.Core;
[6740]27using HeuristicLab.Data;
[5571]28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
[6740]29using HeuristicLab.Parameters;
[5571]30using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
31
32namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
33  [StorableClass]
[9815]34  [Item("SymbolicDataAnalysisExpressionTreeLinearInterpreter", "Fast linear (non-recursive) interpreter for symbolic expression trees. Does not support ADFs.")]
[9758]35  public sealed class SymbolicDataAnalysisExpressionTreeLinearInterpreter : ParameterizedNamedItem, ISymbolicDataAnalysisExpressionTreeInterpreter {
[5749]36    private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic";
[7615]37    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
[5571]38
[9776]39    private SymbolicDataAnalysisExpressionTreeInterpreter interpreter;
40
[9732]41    public override bool CanChangeName {
42      get { return false; }
43    }
[5571]44
[9732]45    public override bool CanChangeDescription {
46      get { return false; }
47    }
48
[5749]49    #region parameter properties
50    public IValueParameter<BoolValue> CheckExpressionsWithIntervalArithmeticParameter {
51      get { return (IValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
52    }
[7615]53
54    public IValueParameter<IntValue> EvaluatedSolutionsParameter {
55      get { return (IValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
56    }
[5749]57    #endregion
58
59    #region properties
60    public BoolValue CheckExpressionsWithIntervalArithmetic {
61      get { return CheckExpressionsWithIntervalArithmeticParameter.Value; }
62      set { CheckExpressionsWithIntervalArithmeticParameter.Value = value; }
63    }
[7615]64    public IntValue EvaluatedSolutions {
65      get { return EvaluatedSolutionsParameter.Value; }
66      set { EvaluatedSolutionsParameter.Value = value; }
67    }
[5749]68    #endregion
69
[5571]70    [StorableConstructor]
[9758]71    private SymbolicDataAnalysisExpressionTreeLinearInterpreter(bool deserializing)
[9732]72      : base(deserializing) {
73    }
74
[9828]75    private SymbolicDataAnalysisExpressionTreeLinearInterpreter(SymbolicDataAnalysisExpressionTreeLinearInterpreter original, Cloner cloner)
[9732]76      : base(original, cloner) {
[9828]77      interpreter = cloner.Clone(original.interpreter);
[9732]78    }
79
[5571]80    public override IDeepCloneable Clone(Cloner cloner) {
[9734]81      return new SymbolicDataAnalysisExpressionTreeLinearInterpreter(this, cloner);
[5571]82    }
83
[9734]84    public SymbolicDataAnalysisExpressionTreeLinearInterpreter()
[9758]85      : base("SymbolicDataAnalysisExpressionTreeLinearInterpreter", "Linear (non-recursive) interpreter for symbolic expression trees (does not support ADFs).") {
[9734]86      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)));
87      Parameters.Add(new ValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
[9776]88      interpreter = new SymbolicDataAnalysisExpressionTreeInterpreter();
[5571]89    }
90
[7615]91    [StorableHook(HookType.AfterDeserialization)]
92    private void AfterDeserialization() {
[9776]93      if (interpreter == null) interpreter = new SymbolicDataAnalysisExpressionTreeInterpreter();
[7615]94    }
95
96    #region IStatefulItem
97    public void InitializeState() {
98      EvaluatedSolutions.Value = 0;
99    }
100
[9828]101    public void ClearState() { }
[7615]102    #endregion
103
[9734]104    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, IEnumerable<int> rows) {
[8436]105      if (CheckExpressionsWithIntervalArithmetic.Value)
[9734]106        throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
[7120]107
[9004]108      lock (EvaluatedSolutions) {
109        EvaluatedSolutions.Value++; // increment the evaluated solutions counter
110      }
[8436]111
[9739]112      var code = SymbolicExpressionTreeLinearCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
[9758]113      PrepareInstructions(code, dataset);
[9818]114      return rows.Select(row => Evaluate(dataset, row, code));
[9739]115    }
[9732]116
[9818]117    private double Evaluate(Dataset dataset, int row, LinearInstruction[] code) {
[9732]118      for (int i = code.Length - 1; i >= 0; --i) {
[9776]119        if (code[i].skip) continue;
[9758]120        #region opcode switch
[9732]121        var instr = code[i];
122        switch (instr.opCode) {
[9738]123          case OpCodes.Variable: {
124              if (row < 0 || row >= dataset.Rows) instr.value = double.NaN;
125              var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
[9826]126              instr.value = ((IList<double>)instr.data)[row] * variableTreeNode.Weight;
[9738]127            }
128            break;
129          case OpCodes.LagVariable: {
130              var laggedVariableTreeNode = (LaggedVariableTreeNode)instr.dynamicNode;
131              int actualRow = row + laggedVariableTreeNode.Lag;
[9788]132              if (actualRow < 0 || actualRow >= dataset.Rows)
133                instr.value = double.NaN;
134              else
[9826]135                instr.value = ((IList<double>)instr.data)[actualRow] * laggedVariableTreeNode.Weight;
[9738]136            }
137            break;
138          case OpCodes.VariableCondition: {
139              if (row < 0 || row >= dataset.Rows) instr.value = double.NaN;
140              var variableConditionTreeNode = (VariableConditionTreeNode)instr.dynamicNode;
[9826]141              double variableValue = ((IList<double>)instr.data)[row];
[9738]142              double x = variableValue - variableConditionTreeNode.Threshold;
143              double p = 1 / (1 + Math.Exp(-variableConditionTreeNode.Slope * x));
144
145              double trueBranch = code[instr.childIndex].value;
146              double falseBranch = code[instr.childIndex + 1].value;
147
148              instr.value = trueBranch * p + falseBranch * (1 - p);
149            }
150            break;
[9271]151          case OpCodes.Add: {
[9732]152              double s = code[instr.childIndex].value;
153              for (int j = 1; j != instr.nArguments; ++j) {
154                s += code[instr.childIndex + j].value;
[9271]155              }
[9732]156              instr.value = s;
[5571]157            }
[9271]158            break;
159          case OpCodes.Sub: {
[9732]160              double s = code[instr.childIndex].value;
161              for (int j = 1; j != instr.nArguments; ++j) {
162                s -= code[instr.childIndex + j].value;
[9271]163              }
[9732]164              if (instr.nArguments == 1) s = -s;
165              instr.value = s;
[5571]166            }
[9271]167            break;
168          case OpCodes.Mul: {
[9732]169              double p = code[instr.childIndex].value;
170              for (int j = 1; j != instr.nArguments; ++j) {
171                p *= code[instr.childIndex + j].value;
[9271]172              }
[9732]173              instr.value = p;
[5571]174            }
[9271]175            break;
176          case OpCodes.Div: {
[9732]177              double p = code[instr.childIndex].value;
178              for (int j = 1; j != instr.nArguments; ++j) {
179                p /= code[instr.childIndex + j].value;
[9271]180              }
[9732]181              if (instr.nArguments == 1) p = 1.0 / p;
182              instr.value = p;
[5571]183            }
[9271]184            break;
185          case OpCodes.Average: {
[9732]186              double s = code[instr.childIndex].value;
187              for (int j = 1; j != instr.nArguments; ++j) {
188                s += code[instr.childIndex + j].value;
[9271]189              }
[9732]190              instr.value = s / instr.nArguments;
[5571]191            }
[9271]192            break;
193          case OpCodes.Cos: {
[9732]194              instr.value = Math.Cos(code[instr.childIndex].value);
[7842]195            }
[9271]196            break;
197          case OpCodes.Sin: {
[9732]198              instr.value = Math.Sin(code[instr.childIndex].value);
[7842]199            }
[9732]200            break;
[9271]201          case OpCodes.Tan: {
[9732]202              instr.value = Math.Tan(code[instr.childIndex].value);
[7842]203            }
[9271]204            break;
[9758]205          case OpCodes.Square: {
206              instr.value = Math.Pow(code[instr.childIndex].value, 2);
207            }
208            break;
209          case OpCodes.Power: {
210              double x = code[instr.childIndex].value;
211              double y = Math.Round(code[instr.childIndex + 1].value);
212              instr.value = Math.Pow(x, y);
213            }
214            break;
215          case OpCodes.SquareRoot: {
216              instr.value = Math.Sqrt(code[instr.childIndex].value);
217            }
218            break;
[9271]219          case OpCodes.Root: {
[9732]220              double x = code[instr.childIndex].value;
221              double y = code[instr.childIndex + 1].value;
222              instr.value = Math.Pow(x, 1 / y);
[7842]223            }
[9271]224            break;
225          case OpCodes.Exp: {
[9732]226              instr.value = Math.Exp(code[instr.childIndex].value);
[7842]227            }
[9271]228            break;
229          case OpCodes.Log: {
[9732]230              instr.value = Math.Log(code[instr.childIndex].value);
[5571]231            }
[9271]232            break;
233          case OpCodes.Gamma: {
[9732]234              var x = code[instr.childIndex].value;
235              instr.value = double.IsNaN(x) ? double.NaN : alglib.gammafunction(x);
[9271]236            }
237            break;
238          case OpCodes.Psi: {
[9732]239              var x = code[instr.childIndex].value;
240              if (double.IsNaN(x)) instr.value = double.NaN;
241              else if (x <= 0 && (Math.Floor(x) - x).IsAlmost(0)) instr.value = double.NaN;
242              else instr.value = alglib.psi(x);
[9271]243            }
244            break;
245          case OpCodes.Dawson: {
[9732]246              var x = code[instr.childIndex].value;
247              instr.value = double.IsNaN(x) ? double.NaN : alglib.dawsonintegral(x);
[9271]248            }
249            break;
250          case OpCodes.ExponentialIntegralEi: {
[9732]251              var x = code[instr.childIndex].value;
252              instr.value = double.IsNaN(x) ? double.NaN : alglib.exponentialintegralei(x);
[9271]253            }
254            break;
255          case OpCodes.SineIntegral: {
256              double si, ci;
[9732]257              var x = code[instr.childIndex].value;
258              if (double.IsNaN(x)) instr.value = double.NaN;
[5571]259              else {
[9271]260                alglib.sinecosineintegrals(x, out si, out ci);
[9732]261                instr.value = si;
[5571]262              }
263            }
[9271]264            break;
265          case OpCodes.CosineIntegral: {
266              double si, ci;
[9732]267              var x = code[instr.childIndex].value;
268              if (double.IsNaN(x)) instr.value = double.NaN;
[5571]269              else {
[9271]270                alglib.sinecosineintegrals(x, out si, out ci);
[9776]271                instr.value = ci;
[5571]272              }
273            }
[9271]274            break;
275          case OpCodes.HyperbolicSineIntegral: {
276              double shi, chi;
[9732]277              var x = code[instr.childIndex].value;
278              if (double.IsNaN(x)) instr.value = double.NaN;
[9271]279              else {
280                alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
[9732]281                instr.value = shi;
[9271]282              }
[5571]283            }
[9271]284            break;
285          case OpCodes.HyperbolicCosineIntegral: {
286              double shi, chi;
[9732]287              var x = code[instr.childIndex].value;
288              if (double.IsNaN(x)) instr.value = double.NaN;
[9271]289              else {
290                alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
[9732]291                instr.value = chi;
[9271]292              }
293            }
294            break;
295          case OpCodes.FresnelCosineIntegral: {
296              double c = 0, s = 0;
[9732]297              var x = code[instr.childIndex].value;
298              if (double.IsNaN(x)) instr.value = double.NaN;
[9271]299              else {
300                alglib.fresnelintegral(x, ref c, ref s);
[9732]301                instr.value = c;
[9271]302              }
303            }
304            break;
305          case OpCodes.FresnelSineIntegral: {
306              double c = 0, s = 0;
[9732]307              var x = code[instr.childIndex].value;
308              if (double.IsNaN(x)) instr.value = double.NaN;
[9271]309              else {
310                alglib.fresnelintegral(x, ref c, ref s);
[9732]311                instr.value = s;
[9271]312              }
313            }
314            break;
315          case OpCodes.AiryA: {
316              double ai, aip, bi, bip;
[9732]317              var x = code[instr.childIndex].value;
318              if (double.IsNaN(x)) instr.value = double.NaN;
[9271]319              else {
320                alglib.airy(x, out ai, out aip, out bi, out bip);
[9732]321                instr.value = ai;
[9271]322              }
323            }
324            break;
325          case OpCodes.AiryB: {
326              double ai, aip, bi, bip;
[9732]327              var x = code[instr.childIndex].value;
328              if (double.IsNaN(x)) instr.value = double.NaN;
[9271]329              else {
330                alglib.airy(x, out ai, out aip, out bi, out bip);
[9732]331                instr.value = bi;
[9271]332              }
333            }
334            break;
335          case OpCodes.Norm: {
[9732]336              var x = code[instr.childIndex].value;
337              if (double.IsNaN(x)) instr.value = double.NaN;
338              else instr.value = alglib.normaldistribution(x);
[9271]339            }
340            break;
341          case OpCodes.Erf: {
[9732]342              var x = code[instr.childIndex].value;
343              if (double.IsNaN(x)) instr.value = double.NaN;
344              else instr.value = alglib.errorfunction(x);
[9271]345            }
346            break;
347          case OpCodes.Bessel: {
[9732]348              var x = code[instr.childIndex].value;
349              if (double.IsNaN(x)) instr.value = double.NaN;
350              else instr.value = alglib.besseli0(x);
[9271]351            }
352            break;
353          case OpCodes.IfThenElse: {
[9732]354              double condition = code[instr.childIndex].value;
355              double result;
356              if (condition > 0.0) {
357                result = code[instr.childIndex + 1].value;
358              } else {
359                result = code[instr.childIndex + 2].value;
360              }
361              instr.value = result;
[9271]362            }
363            break;
364          case OpCodes.AND: {
[9732]365              double result = code[instr.childIndex].value;
366              for (int j = 1; j < instr.nArguments; j++) {
367                if (result > 0.0) result = code[instr.childIndex + j].value;
368                else break;
[9271]369              }
[9732]370              instr.value = result > 0.0 ? 1.0 : -1.0;
[9271]371            }
372            break;
373          case OpCodes.OR: {
[9732]374              double result = code[instr.childIndex].value;
375              for (int j = 1; j < instr.nArguments; j++) {
376                if (result <= 0.0) result = code[instr.childIndex + j].value;
377                else break;
[9271]378              }
[9732]379              instr.value = result > 0.0 ? 1.0 : -1.0;
[9271]380            }
381            break;
382          case OpCodes.NOT: {
[9732]383              instr.value = code[instr.childIndex].value > 0.0 ? -1.0 : 1.0;
[9271]384            }
385            break;
386          case OpCodes.GT: {
[9732]387              double x = code[instr.childIndex].value;
388              double y = code[instr.childIndex + 1].value;
389              instr.value = x > y ? 1.0 : -1.0;
[9271]390            }
391            break;
392          case OpCodes.LT: {
[9732]393              double x = code[instr.childIndex].value;
394              double y = code[instr.childIndex + 1].value;
395              instr.value = x < y ? 1.0 : -1.0;
[9271]396            }
397            break;
[9776]398          case OpCodes.TimeLag:
399          case OpCodes.Integral:
400          case OpCodes.Derivative: {
[9826]401              var state = (InterpreterState)instr.data;
[9793]402              state.Reset();
403              instr.value = interpreter.Evaluate(dataset, ref row, state);
[9776]404            }
405            break;
[9271]406          default:
[9826]407            var errorText = string.Format("The {0} symbol is not supported by the linear interpreter. To support this symbol, please use the SymbolicDataAnalysisExpressionTreeInterpreter.", instr.dynamicNode.Symbol.Name);
[9758]408            throw new NotSupportedException(errorText);
[9271]409        }
[9739]410        #endregion
[5571]411      }
[9739]412      return code[0].value;
[5571]413    }
[9815]414
415    private static LinearInstruction[] GetPrefixSequence(LinearInstruction[] code, int startIndex) {
416      var list = new List<LinearInstruction>();
417      int i = startIndex;
418      while (i != code.Length) {
419        var instr = code[i];
420        list.Add(instr);
421        i = instr.nArguments > 0 ? instr.childIndex : i + 1;
422      }
423      return list.ToArray();
424    }
425
426    private static void PrepareInstructions(LinearInstruction[] code, Dataset dataset) {
427      for (int i = 0; i != code.Length; ++i) {
428        var instr = code[i];
429        #region opcode switch
430        switch (instr.opCode) {
431          case OpCodes.Constant: {
432              var constTreeNode = (ConstantTreeNode)instr.dynamicNode;
433              instr.value = constTreeNode.Value;
434              instr.skip = true; // the value is already set so this instruction should be skipped in the evaluation phase
435            }
436            break;
437          case OpCodes.Variable: {
438              var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
[9826]439              instr.data = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
[9815]440            }
441            break;
442          case OpCodes.LagVariable: {
443              var laggedVariableTreeNode = (LaggedVariableTreeNode)instr.dynamicNode;
[9826]444              instr.data = dataset.GetReadOnlyDoubleValues(laggedVariableTreeNode.VariableName);
[9815]445            }
446            break;
447          case OpCodes.VariableCondition: {
448              var variableConditionTreeNode = (VariableConditionTreeNode)instr.dynamicNode;
[9826]449              instr.data = dataset.GetReadOnlyDoubleValues(variableConditionTreeNode.VariableName);
[9815]450            }
451            break;
452          case OpCodes.TimeLag:
453          case OpCodes.Integral:
454          case OpCodes.Derivative: {
455              var seq = GetPrefixSequence(code, i);
456              var interpreterState = new InterpreterState(seq, 0);
[9826]457              instr.data = interpreterState;
[9815]458              for (int j = 1; j != seq.Length; ++j)
459                seq[j].skip = true;
460            }
461            break;
462        }
463        #endregion
464      }
465    }
[5571]466  }
467}
Note: See TracBrowser for help on using the repository browser.