Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3087_Ceres_Integration/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IntervalInterpreter.cs

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

#3087: merged r17784:18004 from trunk to branch to prepare for trunk reintegration (fixed a conflict in CrossValidation.cs)

File size: 14.8 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.Constant: {
189            var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
190            result = new Interval(constTreeNode.Value, constTreeNode.Value);
191            break;
192          }
193        case OpCodes.Add: {
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);
197              result = Interval.Add(result, argumentInterval);
198            }
199
200            break;
201          }
202        case OpCodes.Sub: {
203            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
204            if (currentInstr.nArguments == 1)
205              result = Interval.Multiply(new Interval(-1, -1), result);
206
207            for (var i = 1; i < currentInstr.nArguments; i++) {
208              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
209              result = Interval.Subtract(result, argumentInterval);
210            }
211
212            break;
213          }
214        case OpCodes.Mul: {
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);
218              result = Interval.Multiply(result, argumentInterval);
219            }
220
221            break;
222          }
223        case OpCodes.Div: {
224            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
225            if (currentInstr.nArguments == 1)
226              result = Interval.Divide(new Interval(1, 1), result);
227
228            for (var i = 1; i < currentInstr.nArguments; i++) {
229              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
230              result = Interval.Divide(result, argumentInterval);
231            }
232
233            break;
234          }
235        case OpCodes.Sin: {
236            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
237            result = Interval.Sine(argumentInterval);
238            break;
239          }
240        case OpCodes.Cos: {
241            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
242            result = Interval.Cosine(argumentInterval);
243            break;
244          }
245        case OpCodes.Tan: {
246            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
247            result = Interval.Tangens(argumentInterval);
248            break;
249          }
250        case OpCodes.Tanh: {
251            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
252            result = Interval.HyperbolicTangent(argumentInterval);
253            break;
254          }
255        case OpCodes.Log: {
256            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
257            result = Interval.Logarithm(argumentInterval);
258            break;
259          }
260        case OpCodes.Exp: {
261            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
262            result = Interval.Exponential(argumentInterval);
263            break;
264          }
265        case OpCodes.Square: {
266            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
267            result = Interval.Square(argumentInterval);
268            break;
269          }
270        case OpCodes.SquareRoot: {
271            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
272            result = Interval.SquareRoot(argumentInterval);
273            break;
274          }
275        case OpCodes.Cube: {
276            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
277            result = Interval.Cube(argumentInterval);
278            break;
279          }
280        case OpCodes.CubeRoot: {
281            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
282            result = Interval.CubicRoot(argumentInterval);
283            break;
284          }
285        case OpCodes.Power: {
286            var a = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
287            var b = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
288            // support only integer powers
289            if (b.LowerBound == b.UpperBound && Math.Truncate(b.LowerBound) == b.LowerBound) {
290              result = Interval.Power(a, (int)b.LowerBound);
291            } else {
292              throw new NotSupportedException("Interval is only supported for integer powers");
293            }
294            break;
295          }
296
297        case OpCodes.Absolute: {
298            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
299            result = Interval.Absolute(argumentInterval);
300            break;
301          }
302        case OpCodes.AnalyticQuotient: {
303            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
304            for (var i = 1; i < currentInstr.nArguments; i++) {
305              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
306              result = Interval.AnalyticQuotient(result, argumentInterval);
307            }
308
309            break;
310          }
311        default:
312          throw new NotSupportedException($"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
313      }
314
315      if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
316        nodeIntervals.Add(currentInstr.dynamicNode, result);
317
318      return result;
319    }
320
321
322    public static bool IsCompatible(ISymbolicExpressionTree tree) {
323      foreach (var n in tree.Root.GetSubtree(0).IterateNodesPrefix()) {
324        if (
325          !(n.Symbol is Variable) &&
326          !(n.Symbol is Constant) &&
327          !(n.Symbol is StartSymbol) &&
328          !(n.Symbol is Addition) &&
329          !(n.Symbol is Subtraction) &&
330          !(n.Symbol is Multiplication) &&
331          !(n.Symbol is Division) &&
332          !(n.Symbol is Sine) &&
333          !(n.Symbol is Cosine) &&
334          !(n.Symbol is Tangent) &&
335          !(n.Symbol is HyperbolicTangent) &&
336          !(n.Symbol is Logarithm) &&
337          !(n.Symbol is Exponential) &&
338          !(n.Symbol is Square) &&
339          !(n.Symbol is SquareRoot) &&
340          !(n.Symbol is Cube) &&
341          !(n.Symbol is CubeRoot) &&
342          !(n.Symbol is Power) &&
343          !(n.Symbol is Absolute) &&
344          !(n.Symbol is AnalyticQuotient)) return false;
345
346        else if (n.Symbol is Power) {
347          // only integer exponents are supported
348          var exp = n.GetSubtree(1) as ConstantTreeNode;
349          if (exp == null || exp.Value != Math.Truncate(exp.Value)) return false;
350        }
351      }
352      return true;
353    }
354  }
355}
Note: See TracBrowser for help on using the repository browser.