Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 18132 was 18132, checked in by gkronber, 2 years ago

#3140: merged r18091:18131 from branch to trunk

File size: 14.9 KB
Line 
1#region License Information
2
3/* HeuristicLab
4 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
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 */
21
22#endregion
23
24using System;
25using System.Collections.Generic;
26using System.Linq;
27using HEAL.Attic;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
32using HeuristicLab.Parameters;
33
34namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
35  [StorableType("DE6C1E1E-D7C1-4070-847E-63B68562B10C")]
36  [Item("IntervalInterpreter", "Interpreter for calculation of intervals of symbolic models.")]
37  public sealed class IntervalInterpreter : ParameterizedNamedItem, IStatefulItem {
38    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
39    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter =>
40      (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName];
41
42    public int EvaluatedSolutions {
43      get => EvaluatedSolutionsParameter.Value.Value;
44      set => EvaluatedSolutionsParameter.Value.Value = value;
45    }
46
47    [StorableConstructor]
48    private IntervalInterpreter(StorableConstructorFlag _) : base(_) { }
49
50    private IntervalInterpreter(IntervalInterpreter original, Cloner cloner)
51      : base(original, cloner) { }
52
53    public IntervalInterpreter()
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)));
57    }
58
59    public override IDeepCloneable Clone(Cloner cloner) {
60      return new IntervalInterpreter(this, cloner);
61    }
62
63    private readonly object syncRoot = new object();
64
65    #region IStatefulItem Members
66
67    public void InitializeState() {
68      EvaluatedSolutions = 0;
69    }
70
71    public void ClearState() { }
72
73    #endregion
74
75    public Interval GetSymbolicExpressionTreeInterval(
76      ISymbolicExpressionTree tree, IDataset dataset,
77      IEnumerable<int> rows = null) {
78      var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
79      return GetSymbolicExpressionTreeInterval(tree, variableRanges);
80    }
81
82    public Interval GetSymbolicExpressionTreeIntervals(
83      ISymbolicExpressionTree tree, IDataset dataset,
84      out IDictionary<ISymbolicExpressionTreeNode, Interval>
85        nodeIntervals, IEnumerable<int> rows = null) {
86      var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
87      return GetSymbolicExpressionTreeIntervals(tree, variableRanges, out nodeIntervals);
88    }
89
90    public Interval GetSymbolicExpressionTreeInterval(
91      ISymbolicExpressionTree tree,
92      IReadOnlyDictionary<string, Interval> variableRanges) {
93      lock (syncRoot) {
94        EvaluatedSolutions++;
95      }
96
97      Interval outputInterval;
98
99      var instructionCount = 0;
100      var instructions = PrepareInterpreterState(tree, variableRanges);
101      outputInterval = Evaluate(instructions, ref instructionCount);
102
103      return outputInterval.LowerBound <= outputInterval.UpperBound
104        ? outputInterval
105        : new Interval(outputInterval.UpperBound, outputInterval.LowerBound);
106    }
107
108
109    public Interval GetSymbolicExpressionTreeIntervals(
110      ISymbolicExpressionTree tree,
111      IReadOnlyDictionary<string, Interval> variableRanges,
112      out IDictionary<ISymbolicExpressionTreeNode, Interval>
113        nodeIntervals) {
114      lock (syncRoot) {
115        EvaluatedSolutions++;
116      }
117
118      var intervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
119      var instructions = PrepareInterpreterState(tree, variableRanges);
120
121      Interval outputInterval;
122      var instructionCount = 0;
123      outputInterval = Evaluate(instructions, ref instructionCount, intervals);
124
125      nodeIntervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
126      foreach (var kvp in intervals) {
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      }
133
134      // because of numerical errors the bounds might be incorrect
135      if (outputInterval.IsInfiniteOrUndefined || outputInterval.LowerBound <= outputInterval.UpperBound)
136        return outputInterval;
137
138      return new Interval(outputInterval.UpperBound, outputInterval.LowerBound);
139    }
140
141
142    private static Instruction[] PrepareInterpreterState(
143      ISymbolicExpressionTree tree,
144      IReadOnlyDictionary<string, Interval> variableRanges) {
145      if (variableRanges == null)
146        throw new ArgumentNullException("No variablew ranges are present!", nameof(variableRanges));
147
148      // Check if all variables used in the tree are present in the dataset
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");
153
154      var code = SymbolicExpressionTreeCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
155      foreach (var instr in code.Where(i => i.opCode == OpCodes.Variable)) {
156        var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
157        instr.data = variableRanges[variableTreeNode.VariableName];
158      }
159
160      return code;
161    }
162
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
171      instructionCounter++;
172      Interval result;
173
174      switch (currentInstr.opCode) {
175        case OpCodes.Variable: {
176            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
177            var weightInterval = new Interval(variableTreeNode.Weight, variableTreeNode.Weight);
178
179            Interval variableInterval;
180            if (variableIntervals != null && variableIntervals.ContainsKey(variableTreeNode.VariableName))
181              variableInterval = variableIntervals[variableTreeNode.VariableName];
182            else
183              variableInterval = (Interval)currentInstr.data;
184
185            result = Interval.Multiply(variableInterval, weightInterval);
186            break;
187          }
188        case OpCodes.Number: // fall through
189        case OpCodes.Constant: {
190          var numericTreeNode = (INumericTreeNode)currentInstr.dynamicNode;
191          result = new Interval(numericTreeNode.Value, numericTreeNode.Value);
192          break;
193          }
194        case OpCodes.Add: {
195            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
196            for (var i = 1; i < currentInstr.nArguments; i++) {
197              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
198              result = Interval.Add(result, argumentInterval);
199            }
200
201            break;
202          }
203        case OpCodes.Sub: {
204            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
205            if (currentInstr.nArguments == 1)
206              result = Interval.Multiply(new Interval(-1, -1), result);
207
208            for (var i = 1; i < currentInstr.nArguments; i++) {
209              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
210              result = Interval.Subtract(result, argumentInterval);
211            }
212
213            break;
214          }
215        case OpCodes.Mul: {
216            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
217            for (var i = 1; i < currentInstr.nArguments; i++) {
218              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
219              result = Interval.Multiply(result, argumentInterval);
220            }
221
222            break;
223          }
224        case OpCodes.Div: {
225            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
226            if (currentInstr.nArguments == 1)
227              result = Interval.Divide(new Interval(1, 1), result);
228
229            for (var i = 1; i < currentInstr.nArguments; i++) {
230              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
231              result = Interval.Divide(result, argumentInterval);
232            }
233
234            break;
235          }
236        case OpCodes.Sin: {
237            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
238            result = Interval.Sine(argumentInterval);
239            break;
240          }
241        case OpCodes.Cos: {
242            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
243            result = Interval.Cosine(argumentInterval);
244            break;
245          }
246        case OpCodes.Tan: {
247            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
248            result = Interval.Tangens(argumentInterval);
249            break;
250          }
251        case OpCodes.Tanh: {
252            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
253            result = Interval.HyperbolicTangent(argumentInterval);
254            break;
255          }
256        case OpCodes.Log: {
257            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
258            result = Interval.Logarithm(argumentInterval);
259            break;
260          }
261        case OpCodes.Exp: {
262            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
263            result = Interval.Exponential(argumentInterval);
264            break;
265          }
266        case OpCodes.Square: {
267            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
268            result = Interval.Square(argumentInterval);
269            break;
270          }
271        case OpCodes.SquareRoot: {
272            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
273            result = Interval.SquareRoot(argumentInterval);
274            break;
275          }
276        case OpCodes.Cube: {
277            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
278            result = Interval.Cube(argumentInterval);
279            break;
280          }
281        case OpCodes.CubeRoot: {
282            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
283            result = Interval.CubicRoot(argumentInterval);
284            break;
285          }
286        case OpCodes.Power: {
287            var a = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
288            var b = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
289            // support only integer powers
290            if (b.LowerBound == b.UpperBound && Math.Truncate(b.LowerBound) == b.LowerBound) {
291              result = Interval.Power(a, (int)b.LowerBound);
292            } else {
293              throw new NotSupportedException("Interval is only supported for integer powers");
294            }
295            break;
296          }
297
298        case OpCodes.Absolute: {
299            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
300            result = Interval.Absolute(argumentInterval);
301            break;
302          }
303        case OpCodes.AnalyticQuotient: {
304            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
305            for (var i = 1; i < currentInstr.nArguments; i++) {
306              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
307              result = Interval.AnalyticQuotient(result, argumentInterval);
308            }
309
310            break;
311          }
312        default:
313          throw new NotSupportedException($"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
314      }
315
316      if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
317        nodeIntervals.Add(currentInstr.dynamicNode, result);
318
319      return result;
320    }
321
322
323    public static bool IsCompatible(ISymbolicExpressionTree tree) {
324      foreach (var n in tree.Root.GetSubtree(0).IterateNodesPrefix()) {
325        if (
326          !(n.Symbol is Variable) &&
327          !(n.Symbol is Number) &&
328          !(n.Symbol is Constant) &&
329          !(n.Symbol is StartSymbol) &&
330          !(n.Symbol is Addition) &&
331          !(n.Symbol is Subtraction) &&
332          !(n.Symbol is Multiplication) &&
333          !(n.Symbol is Division) &&
334          !(n.Symbol is Sine) &&
335          !(n.Symbol is Cosine) &&
336          !(n.Symbol is Tangent) &&
337          !(n.Symbol is HyperbolicTangent) &&
338          !(n.Symbol is Logarithm) &&
339          !(n.Symbol is Exponential) &&
340          !(n.Symbol is Square) &&
341          !(n.Symbol is SquareRoot) &&
342          !(n.Symbol is Cube) &&
343          !(n.Symbol is CubeRoot) &&
344          !(n.Symbol is Power) &&
345          !(n.Symbol is Absolute) &&
346          !(n.Symbol is AnalyticQuotient)) return false;
347
348        else if (n.Symbol is Power) {
349          // only integer exponents are supported
350          var exponent = n.GetSubtree(1) as INumericTreeNode;
351          if (exponent == null || exponent.Value != Math.Truncate(exponent.Value)) return false;
352        }
353      }
354      return true;
355    }
356  }
357}
Note: See TracBrowser for help on using the repository browser.