Free cookie consent management tool by TermsFeed Policy Generator

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

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

#3040 Added separate mean symbol instead of reusing the average symbol.

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