Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3040_VectorBasedGP/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeVectorInterpreter.cs @ 17463

Last change on this file since 17463 was 17463, checked in by pfleck, 4 years ago

#3040 Added type coherent vector grammar to enforce that the root symbol is a scalar.

File size: 16.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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.Analysis;
25using HeuristicLab.Common;
26using HeuristicLab.Core;
27using HeuristicLab.Data;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
29using HeuristicLab.Parameters;
30using HEAL.Attic;
31using MathNet.Numerics.LinearAlgebra;
32using MathNet.Numerics.Statistics;
33
34using DoubleVector = MathNet.Numerics.LinearAlgebra.Vector<double>;
35
36namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
37  [StorableType("DE68A1D9-5AFC-4DDD-AB62-29F3B8FC28E0")]
38  [Item("SymbolicDataAnalysisExpressionTreeVectorInterpreter", "Interpreter for symbolic expression trees including vector arithmetic.")]
39  public class SymbolicDataAnalysisExpressionTreeVectorInterpreter : ParameterizedNamedItem, ISymbolicDataAnalysisExpressionTreeInterpreter {
40
41    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
42
43    public override bool CanChangeName {
44      get { return false; }
45    }
46
47    public override bool CanChangeDescription {
48      get { return false; }
49    }
50
51    #region parameter properties
52    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter {
53      get { return (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
54    }
55    #endregion
56
57    #region properties
58    public int EvaluatedSolutions {
59      get { return EvaluatedSolutionsParameter.Value.Value; }
60      set { EvaluatedSolutionsParameter.Value.Value = value; }
61    }
62    #endregion
63
64    [StorableConstructor]
65    protected SymbolicDataAnalysisExpressionTreeVectorInterpreter(StorableConstructorFlag _) : base(_) { }
66
67    protected SymbolicDataAnalysisExpressionTreeVectorInterpreter(SymbolicDataAnalysisExpressionTreeVectorInterpreter original, Cloner cloner)
68      : base(original, cloner) { }
69
70    public override IDeepCloneable Clone(Cloner cloner) {
71      return new SymbolicDataAnalysisExpressionTreeVectorInterpreter(this, cloner);
72    }
73
74    public SymbolicDataAnalysisExpressionTreeVectorInterpreter()
75      : base("SymbolicDataAnalysisExpressionTreeVectorInterpreter", "Interpreter for symbolic expression trees including vector arithmetic.") {
76      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
77    }
78
79    protected SymbolicDataAnalysisExpressionTreeVectorInterpreter(string name, string description)
80      : base(name, description) {
81      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
82    }
83
84    [StorableHook(HookType.AfterDeserialization)]
85    private void AfterDeserialization() {
86
87    }
88
89    #region IStatefulItem
90    public void InitializeState() {
91      EvaluatedSolutions = 0;
92    }
93
94    public void ClearState() { }
95    #endregion
96
97    private readonly object syncRoot = new object();
98    public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, IDataset dataset, IEnumerable<int> rows) {
99      lock (syncRoot) {
100        EvaluatedSolutions++; // increment the evaluated solutions counter
101      }
102      var state = PrepareInterpreterState(tree, dataset);
103
104      foreach (var rowEnum in rows) {
105        int row = rowEnum;
106        var result = Evaluate(dataset, ref row, state);
107        if (result.IsScalar)
108          yield return result.Scalar;
109        else
110          yield return double.NaN;
111        //if (!result.IsScalar)
112        //  throw new InvalidOperationException("Result of the tree is not a scalar.");
113        //yield return result.Scalar;
114        state.Reset();
115      }
116    }
117
118    private static InterpreterState PrepareInterpreterState(ISymbolicExpressionTree tree, IDataset dataset) {
119      Instruction[] code = SymbolicExpressionTreeCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
120      int necessaryArgStackSize = 0;
121      foreach (Instruction instr in code) {
122        if (instr.opCode == OpCodes.Variable) {
123          var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
124          if (dataset.VariableHasType<double>(variableTreeNode.VariableName))
125            instr.data = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
126          else if (dataset.VariableHasType<DoubleVector>(variableTreeNode.VariableName))
127            instr.data = dataset.GetReadOnlyDoubleVectorValues(variableTreeNode.VariableName);
128          else throw new NotSupportedException($"Type of variable {variableTreeNode.VariableName} is not supported.");
129        } else if (instr.opCode == OpCodes.FactorVariable) {
130          var factorTreeNode = instr.dynamicNode as FactorVariableTreeNode;
131          instr.data = dataset.GetReadOnlyStringValues(factorTreeNode.VariableName);
132        } else if (instr.opCode == OpCodes.BinaryFactorVariable) {
133          var factorTreeNode = instr.dynamicNode as BinaryFactorVariableTreeNode;
134          instr.data = dataset.GetReadOnlyStringValues(factorTreeNode.VariableName);
135        } else if (instr.opCode == OpCodes.LagVariable) {
136          var laggedVariableTreeNode = (LaggedVariableTreeNode)instr.dynamicNode;
137          instr.data = dataset.GetReadOnlyDoubleValues(laggedVariableTreeNode.VariableName);
138        } else if (instr.opCode == OpCodes.VariableCondition) {
139          var variableConditionTreeNode = (VariableConditionTreeNode)instr.dynamicNode;
140          instr.data = dataset.GetReadOnlyDoubleValues(variableConditionTreeNode.VariableName);
141        } else if (instr.opCode == OpCodes.Call) {
142          necessaryArgStackSize += instr.nArguments + 1;
143        }
144      }
145      return new InterpreterState(code, necessaryArgStackSize);
146    }
147
148
149    public struct EvaluationResult {
150      public double Scalar { get; }
151      public bool IsScalar => !double.IsNaN(Scalar);
152
153      public DoubleVector Vector { get; }
154      public bool IsVector => Vector != null;
155      //public bool IsVector => !(Vector.Count == 1 && double.IsNaN(Vector[0]));
156
157      public bool IsNaN => !IsScalar && !IsVector;
158
159      public EvaluationResult(double scalar) {
160        Scalar = scalar;
161        //Vector = NaNVector;
162        Vector = null;
163      }
164      public EvaluationResult(DoubleVector vector) {
165        if (vector == null) throw new ArgumentNullException(nameof(vector));
166        Vector = vector;
167        Scalar = double.NaN;
168      }
169
170      public override string ToString() {
171        if (IsScalar) return Scalar.ToString();
172        if (IsVector) return Vector.ToVectorString();
173        return "NaN";
174      }
175
176      public static readonly EvaluationResult NaN = new EvaluationResult(double.NaN);
177      //private static readonly DoubleVector NaNVector = DoubleVector.Build.Dense(1, double.NaN);
178    }
179
180    private static EvaluationResult ArithmeticApply(EvaluationResult lhs, EvaluationResult rhs,
181      Func<double, double, double> ssFunc = null,
182      Func<double, DoubleVector, DoubleVector> svFunc = null,
183      Func<DoubleVector, double, DoubleVector> vsFunc = null,
184      Func<DoubleVector, DoubleVector, DoubleVector> vvFunc = null) {
185      if (lhs.IsScalar && rhs.IsScalar && ssFunc != null) return new EvaluationResult(ssFunc(lhs.Scalar, rhs.Scalar));
186      if (lhs.IsScalar && rhs.IsVector && svFunc != null) return new EvaluationResult(svFunc(lhs.Scalar, rhs.Vector));
187      if (lhs.IsVector && rhs.IsScalar && vsFunc != null) return new EvaluationResult(vsFunc(lhs.Vector, rhs.Scalar));
188      if (lhs.IsVector && rhs.IsVector && vvFunc != null) return new EvaluationResult(vvFunc(lhs.Vector, rhs.Vector));
189      return EvaluationResult.NaN;
190      //throw new NotSupportedException($"Unsupported combination of argument types: ({lhs}) / ({rhs})");
191    }
192
193    private static EvaluationResult FunctionApply(EvaluationResult val,
194      Func<double, double> sFunc = null,
195      Func<DoubleVector, DoubleVector> vFunc = null) {
196      if (val.IsScalar && sFunc != null) return new EvaluationResult(sFunc(val.Scalar));
197      if (val.IsVector && vFunc != null) return new EvaluationResult(vFunc(val.Vector));
198      return EvaluationResult.NaN;
199      //throw new NotSupportedException($"Unsupported argument type ({val})");
200    }
201    private static EvaluationResult AggregateApply(EvaluationResult val,
202      Func<double, double> sFunc = null,
203      Func<DoubleVector, double> vFunc = null) {
204      if (val.IsScalar && sFunc != null) return new EvaluationResult(sFunc(val.Scalar));
205      if (val.IsVector && vFunc != null) return new EvaluationResult(vFunc(val.Vector));
206      return EvaluationResult.NaN;
207      //throw new NotSupportedException($"Unsupported argument type ({val})");
208    }
209
210    public virtual EvaluationResult Evaluate(IDataset dataset, ref int row, InterpreterState state) {
211      Instruction currentInstr = state.NextInstruction();
212      switch (currentInstr.opCode) {
213        case OpCodes.Add: {
214            var cur = Evaluate(dataset, ref row, state);
215            for (int i = 1; i < currentInstr.nArguments; i++) {
216              var op = Evaluate(dataset, ref row, state);
217              cur = ArithmeticApply(cur, op,
218                (s1, s2) => s1 + s2,
219                (s1, v2) => s1 + v2,
220                (v1, s2) => v1 + s2,
221                (v1, v2) => v1 + v2);
222            }
223            return cur;
224          }
225        case OpCodes.Sub: {
226            var cur = Evaluate(dataset, ref row, state);
227            for (int i = 1; i < currentInstr.nArguments; i++) {
228              var op = Evaluate(dataset, ref row, state);
229              cur = ArithmeticApply(cur, op,
230                (s1, s2) => s1 - s2,
231                (s1, v2) => s1 - v2,
232                (v1, s2) => v1 - s2,
233                (v1, v2) => v1 - v2);
234            }
235            return cur;
236          }
237        case OpCodes.Mul: {
238            var cur = Evaluate(dataset, ref row, state);
239            for (int i = 1; i < currentInstr.nArguments; i++) {
240              var op = Evaluate(dataset, ref row, state);
241              cur = ArithmeticApply(cur, op,
242                (s1, s2) => s1 * s2,
243                (s1, v2) => s1 * v2,
244                (v1, s2) => v1 * s2,
245                (v1, v2) => v1.PointwiseMultiply(v2));
246            }
247            return cur;
248          }
249        case OpCodes.Div: {
250            var cur = Evaluate(dataset, ref row, state);
251            for (int i = 1; i < currentInstr.nArguments; i++) {
252              var op = Evaluate(dataset, ref row, state);
253              cur = ArithmeticApply(cur, op,
254                (s1, s2) => s1 / s2,
255                (s1, v2) => s1 / v2,
256                (v1, s2) => v1 / s2,
257                (v1, v2) => v1 / v2);
258            }
259            return cur;
260          }
261        case OpCodes.Absolute: {
262            var cur = Evaluate(dataset, ref row, state);
263            return FunctionApply(cur, Math.Abs, DoubleVector.Abs);
264          }
265        case OpCodes.Tanh: {
266            var cur = Evaluate(dataset, ref row, state);
267            return FunctionApply(cur, Math.Tanh, DoubleVector.Tanh);
268          }
269        case OpCodes.Cos: {
270            var cur = Evaluate(dataset, ref row, state);
271            return FunctionApply(cur, Math.Cos, DoubleVector.Cos);
272          }
273        case OpCodes.Sin: {
274            var cur = Evaluate(dataset, ref row, state);
275            return FunctionApply(cur, Math.Sin, DoubleVector.Sin);
276          }
277        case OpCodes.Tan: {
278            var cur = Evaluate(dataset, ref row, state);
279            return FunctionApply(cur, Math.Tan, DoubleVector.Tan);
280          }
281        case OpCodes.Square: {
282            var cur = Evaluate(dataset, ref row, state);
283            return FunctionApply(cur,
284              s => Math.Pow(s, 2),
285              v => v.PointwisePower(2));
286          }
287        case OpCodes.Cube: {
288            var cur = Evaluate(dataset, ref row, state);
289            return FunctionApply(cur,
290              s => Math.Pow(s, 3),
291              v => v.PointwisePower(3));
292          }
293        case OpCodes.Power: {
294            var x = Evaluate(dataset, ref row, state);
295            var y = Evaluate(dataset, ref row, state);
296            return ArithmeticApply(x, y,
297              (s1, s2) => Math.Pow(s1, Math.Round(s2)),
298              (s1, v2) => DoubleVector.Build.Dense(v2.Count, s1).PointwisePower(DoubleVector.Round(v2)),
299              (v1, s2) => v1.PointwisePower(Math.Round(s2)),
300              (v1, v2) => v1.PointwisePower(DoubleVector.Round(v2)));
301          }
302        case OpCodes.SquareRoot: {
303            var cur = Evaluate(dataset, ref row, state);
304            return FunctionApply(cur,
305              s => Math.Sqrt(s),
306              v => DoubleVector.Sqrt(v));
307          }
308        case OpCodes.CubeRoot: {
309            var cur = Evaluate(dataset, ref row, state);
310            return FunctionApply(cur,
311              s => s < 0 ? -Math.Pow(-s, 1.0 / 3.0) : Math.Pow(s, 1.0 / 3.0),
312              v => v.Map(s => s < 0 ? -Math.Pow(-s, 1.0 / 3.0) : Math.Pow(s, 1.0 / 3.0)));
313          }
314        case OpCodes.Root: {
315            var x = Evaluate(dataset, ref row, state);
316            var y = Evaluate(dataset, ref row, state);
317            return ArithmeticApply(x, y,
318              (s1, s2) => Math.Pow(s1, 1.0 / Math.Round(s2)),
319              (s1, v2) => DoubleVector.Build.Dense(v2.Count, s1).PointwisePower(1.0 / DoubleVector.Round(v2)),
320              (v1, s2) => v1.PointwisePower(1.0 / Math.Round(s2)),
321              (v1, v2) => v1.PointwisePower(1.0 / DoubleVector.Round(v2)));
322          }
323        case OpCodes.Exp: {
324            var cur = Evaluate(dataset, ref row, state);
325            return FunctionApply(cur,
326              s => Math.Exp(s),
327              v => DoubleVector.Exp(v));
328          }
329        case OpCodes.Log: {
330            var cur = Evaluate(dataset, ref row, state);
331            return FunctionApply(cur,
332              s => Math.Log(s),
333              v => DoubleVector.Log(v));
334          }
335        case OpCodes.Sum: {
336            var cur = Evaluate(dataset, ref row, state);
337            return AggregateApply(cur,
338              s => s,
339              v => v.Sum());
340          }
341        case OpCodes.Average: {
342            var cur = Evaluate(dataset, ref row, state);
343            return AggregateApply(cur,
344              s => s,
345              v => v.Mean());
346          }
347        case OpCodes.StandardDeviation: {
348            var cur = Evaluate(dataset, ref row, state);
349            return AggregateApply(cur,
350              s => 0,
351              v => v.Count > 1 ? Statistics.StandardDeviation(v) : 0);
352          }
353        case OpCodes.Variable: {
354            if (row < 0 || row >= dataset.Rows) return EvaluationResult.NaN;
355            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
356            if (currentInstr.data is IList<double> doubleList)
357              return new EvaluationResult(doubleList[row] * variableTreeNode.Weight);
358            if (currentInstr.data is IList<DoubleVector> doubleVectorList)
359              return new EvaluationResult(doubleVectorList[row] * variableTreeNode.Weight);
360            throw new NotSupportedException($"Unsupported type of variable: {currentInstr.data.GetType().GetPrettyName()}");
361          }
362        case OpCodes.BinaryFactorVariable: {
363            if (row < 0 || row >= dataset.Rows) return EvaluationResult.NaN;
364            var factorVarTreeNode = currentInstr.dynamicNode as BinaryFactorVariableTreeNode;
365            return new EvaluationResult(((IList<string>)currentInstr.data)[row] == factorVarTreeNode.VariableValue ? factorVarTreeNode.Weight : 0);
366          }
367        case OpCodes.FactorVariable: {
368            if (row < 0 || row >= dataset.Rows) return EvaluationResult.NaN;
369            var factorVarTreeNode = currentInstr.dynamicNode as FactorVariableTreeNode;
370            return new EvaluationResult(factorVarTreeNode.GetValue(((IList<string>)currentInstr.data)[row]));
371          }
372        case OpCodes.Constant: {
373            var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
374            return new EvaluationResult(constTreeNode.Value);
375          }
376
377        default:
378          throw new NotSupportedException($"Unsupported OpCode: {currentInstr.opCode}");
379      }
380    }
381  }
382}
Note: See TracBrowser for help on using the repository browser.