Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IntervalInterpreter.cs @ 17911

Last change on this file since 17911 was 17911, checked in by gkronber, 3 years ago

#3073 code improvements after reintegration of branch

File size: 14.0 KB
RevLine 
[16330]1#region License Information
[17902]2
[16330]3/* HeuristicLab
[17180]4 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[16330]5 *
6 * This file is part of HeuristicLab.
7 *
8 * HeuristicLab is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * HeuristicLab is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
20 */
[17902]21
[16330]22#endregion
23
24using System;
[16303]25using System.Collections.Generic;
26using System.Linq;
[17579]27using HEAL.Attic;
[16303]28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
[16367]32using HeuristicLab.Parameters;
[16303]33
[16377]34namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
[16565]35  [StorableType("DE6C1E1E-D7C1-4070-847E-63B68562B10C")]
[17902]36  [Item("IntervalInterpreter", "Interpreter for calculation of intervals of symbolic models.")]
[16328]37  public sealed class IntervalInterpreter : ParameterizedNamedItem, IStatefulItem {
[16303]38    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
[17902]39    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter =>
40      (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName];
[16330]41
[16303]42    public int EvaluatedSolutions {
[17902]43      get => EvaluatedSolutionsParameter.Value.Value;
44      set => EvaluatedSolutionsParameter.Value.Value = value;
[16303]45    }
46
[16367]47    [StorableConstructor]
[16565]48    private IntervalInterpreter(StorableConstructorFlag _) : base(_) { }
[17902]49
[16328]50    private IntervalInterpreter(IntervalInterpreter original, Cloner cloner)
[17902]51      : base(original, cloner) { }
[16303]52
[16323]53    public IntervalInterpreter()
[17902]54      : base("IntervalInterpreter", "Interpreter for calculation of intervals of symbolic models.") {
55      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName,
56        "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
[16367]57    }
[16303]58
59    public override IDeepCloneable Clone(Cloner cloner) {
[16323]60      return new IntervalInterpreter(this, cloner);
[16303]61    }
62
[16330]63    private readonly object syncRoot = new object();
64
[16328]65    #region IStatefulItem Members
[17902]66
[16328]67    public void InitializeState() {
68      EvaluatedSolutions = 0;
69    }
[17902]70
[16328]71    public void ClearState() { }
[17902]72
[16328]73    #endregion
74
[17902]75    public Interval GetSymbolicExpressionTreeInterval(
76      ISymbolicExpressionTree tree, IDataset dataset,
77      IEnumerable<int> rows = null) {
[16403]78      var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
[16740]79      return GetSymbolicExpressionTreeInterval(tree, variableRanges);
[16330]80    }
81
[17902]82    public Interval GetSymbolicExpressionTreeIntervals(
83      ISymbolicExpressionTree tree, IDataset dataset,
84      out IDictionary<ISymbolicExpressionTreeNode, Interval>
85        nodeIntervals, IEnumerable<int> rows = null) {
[16403]86      var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
[16740]87      return GetSymbolicExpressionTreeIntervals(tree, variableRanges, out nodeIntervals);
[16328]88    }
89
[17902]90    public Interval GetSymbolicExpressionTreeInterval(
91      ISymbolicExpressionTree tree,
92      IReadOnlyDictionary<string, Interval> variableRanges) {
[16364]93      lock (syncRoot) {
[16330]94        EvaluatedSolutions++;
95      }
[17902]96
97      Interval outputInterval;
98
99      var instructionCount = 0;
[16404]100      var instructions = PrepareInterpreterState(tree, variableRanges);
[17902]101      outputInterval = Evaluate(instructions, ref instructionCount);
[16328]102
[17902]103      return outputInterval.LowerBound <= outputInterval.UpperBound
104        ? outputInterval
105        : new Interval(outputInterval.UpperBound, outputInterval.LowerBound);
[16328]106    }
107
[16364]108
[17902]109    public Interval GetSymbolicExpressionTreeIntervals(
110      ISymbolicExpressionTree tree,
111      IReadOnlyDictionary<string, Interval> variableRanges,
112      out IDictionary<ISymbolicExpressionTreeNode, Interval>
113        nodeIntervals) {
[16330]114      lock (syncRoot) {
115        EvaluatedSolutions++;
116      }
[17902]117
[16404]118      var intervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
119      var instructions = PrepareInterpreterState(tree, variableRanges);
[16328]120
[17902]121      Interval outputInterval;
122      var instructionCount = 0;
123      outputInterval = Evaluate(instructions, ref instructionCount, intervals);
124
[16629]125      nodeIntervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
[16740]126      foreach (var kvp in intervals) {
[16629]127        var interval = kvp.Value;
128        if (interval.IsInfiniteOrUndefined || interval.LowerBound <= interval.UpperBound)
129          nodeIntervals.Add(kvp.Key, interval);
130        else
131          nodeIntervals.Add(kvp.Key, new Interval(interval.UpperBound, interval.LowerBound));
132      }
[16404]133
[16629]134      // because of numerical errors the bounds might be incorrect
135      if (outputInterval.IsInfiniteOrUndefined || outputInterval.LowerBound <= outputInterval.UpperBound)
136        return outputInterval;
[17902]137
138      return new Interval(outputInterval.UpperBound, outputInterval.LowerBound);
[16328]139    }
140
[16364]141
[17902]142    private static Instruction[] PrepareInterpreterState(
143      ISymbolicExpressionTree tree,
144      IReadOnlyDictionary<string, Interval> variableRanges) {
[16404]145      if (variableRanges == null)
146        throw new ArgumentNullException("No variablew ranges are present!", nameof(variableRanges));
[16328]147
[17911]148      // Check if all variables used in the tree are present in the dataset
[17902]149      foreach (var variable in tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(n => n.VariableName)
150                                   .Distinct())
151        if (!variableRanges.ContainsKey(variable))
152          throw new InvalidOperationException($"No ranges for variable {variable} is present");
[16330]153
[17902]154      var code = SymbolicExpressionTreeCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
155      foreach (var instr in code.Where(i => i.opCode == OpCodes.Variable)) {
[16364]156        var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
[16404]157        instr.data = variableRanges[variableTreeNode.VariableName];
[16303]158      }
[17902]159
[16330]160      return code;
[16303]161    }
162
[17902]163    // Use ref parameter, because the tree will be iterated through recursively from the left-side branch to the right side
164    // Update instructionCounter, whenever Evaluate is called
165    public static Interval Evaluate(
166    Instruction[] instructions, ref int instructionCounter,
167      IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals = null,
168      IReadOnlyDictionary<string, Interval> variableIntervals = null) {
169      var currentInstr = instructions[instructionCounter];
170
[16404]171      instructionCounter++;
[17902]172      Interval result;
[16331]173
[16303]174      switch (currentInstr.opCode) {
[16383]175        case OpCodes.Variable: {
176            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
[16404]177            var weightInterval = new Interval(variableTreeNode.Weight, variableTreeNode.Weight);
[16383]178
[17902]179            Interval variableInterval;
180            if (variableIntervals != null && variableIntervals.ContainsKey(variableTreeNode.VariableName))
181              variableInterval = variableIntervals[variableTreeNode.VariableName];
182            else
183              variableInterval = (Interval)currentInstr.data;
184
[16404]185            result = Interval.Multiply(variableInterval, weightInterval);
[16403]186            break;
[16383]187          }
188        case OpCodes.Constant: {
189            var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
[16403]190            result = new Interval(constTreeNode.Value, constTreeNode.Value);
191            break;
[16383]192          }
[16303]193        case OpCodes.Add: {
[17902]194            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
195            for (var i = 1; i < currentInstr.nArguments; i++) {
196              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]197              result = Interval.Add(result, argumentInterval);
[16303]198            }
[17902]199
[16331]200            break;
[16303]201          }
202        case OpCodes.Sub: {
[17902]203            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]204            if (currentInstr.nArguments == 1)
205              result = Interval.Multiply(new Interval(-1, -1), result);
[16404]206
[17902]207            for (var i = 1; i < currentInstr.nArguments; i++) {
208              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]209              result = Interval.Subtract(result, argumentInterval);
[16303]210            }
[17902]211
[16331]212            break;
[16303]213          }
214        case OpCodes.Mul: {
[17902]215            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
216            for (var i = 1; i < currentInstr.nArguments; i++) {
217              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]218              result = Interval.Multiply(result, argumentInterval);
[16303]219            }
[17902]220
[16331]221            break;
[16303]222          }
223        case OpCodes.Div: {
[17902]224            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16404]225            if (currentInstr.nArguments == 1)
226              result = Interval.Divide(new Interval(1, 1), result);
[16383]227
[17902]228            for (var i = 1; i < currentInstr.nArguments; i++) {
229              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]230              result = Interval.Divide(result, argumentInterval);
[16303]231            }
[17902]232
[16331]233            break;
[16303]234          }
235        case OpCodes.Sin: {
[17902]236            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]237            result = Interval.Sine(argumentInterval);
[16331]238            break;
[16303]239          }
240        case OpCodes.Cos: {
[17902]241            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]242            result = Interval.Cosine(argumentInterval);
[16331]243            break;
[16303]244          }
245        case OpCodes.Tan: {
[17902]246            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]247            result = Interval.Tangens(argumentInterval);
[16331]248            break;
[16303]249          }
[16758]250        case OpCodes.Tanh: {
[17902]251            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16758]252            result = Interval.HyperbolicTangent(argumentInterval);
253            break;
254          }
[16303]255        case OpCodes.Log: {
[17902]256            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]257            result = Interval.Logarithm(argumentInterval);
[16331]258            break;
[16303]259          }
260        case OpCodes.Exp: {
[17902]261            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]262            result = Interval.Exponential(argumentInterval);
[16331]263            break;
[16303]264          }
[16323]265        case OpCodes.Square: {
[17902]266            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[16383]267            result = Interval.Square(argumentInterval);
[16331]268            break;
[16323]269          }
[17579]270        case OpCodes.SquareRoot: {
[17902]271            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[17579]272            result = Interval.SquareRoot(argumentInterval);
273            break;
274          }
275        case OpCodes.Cube: {
[17902]276            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[17579]277            result = Interval.Cube(argumentInterval);
278            break;
279          }
280        case OpCodes.CubeRoot: {
[17902]281            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[17579]282            result = Interval.CubicRoot(argumentInterval);
283            break;
284          }
285        case OpCodes.Absolute: {
[17902]286            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[17579]287            result = Interval.Absolute(argumentInterval);
288            break;
289          }
290        case OpCodes.AnalyticQuotient: {
[17902]291            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[17579]292            for (var i = 1; i < currentInstr.nArguments; i++) {
[17902]293              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
[17911]294              result = Interval.AnalyticQuotient(result, argumentInterval);
[16303]295            }
[17579]296
[16331]297            break;
[16303]298          }
299        default:
[16383]300          throw new NotSupportedException($"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
[16303]301      }
[16331]302
[17902]303      if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
[16404]304        nodeIntervals.Add(currentInstr.dynamicNode, result);
[16383]305
[16374]306      return result;
[16303]307    }
[16330]308
[17902]309
[16330]310    public static bool IsCompatible(ISymbolicExpressionTree tree) {
[17902]311      var containsUnknownSymbols = (
[16330]312        from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
313        where
[17902]314          !(n.Symbol is Variable) &&
[17579]315          !(n.Symbol is Constant) &&
[16330]316          !(n.Symbol is StartSymbol) &&
317          !(n.Symbol is Addition) &&
318          !(n.Symbol is Subtraction) &&
319          !(n.Symbol is Multiplication) &&
320          !(n.Symbol is Division) &&
321          !(n.Symbol is Sine) &&
322          !(n.Symbol is Cosine) &&
323          !(n.Symbol is Tangent) &&
[17902]324          !(n.Symbol is HyperbolicTangent) &&
[16330]325          !(n.Symbol is Logarithm) &&
326          !(n.Symbol is Exponential) &&
327          !(n.Symbol is Square) &&
328          !(n.Symbol is SquareRoot) &&
[17579]329          !(n.Symbol is Cube) &&
330          !(n.Symbol is CubeRoot) &&
331          !(n.Symbol is Absolute) &&
332          !(n.Symbol is AnalyticQuotient)
[16330]333        select n).Any();
[17902]334      return !containsUnknownSymbols;
[16330]335    }
[16303]336  }
[17902]337}
Note: See TracBrowser for help on using the repository browser.