Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IntervalArithBoundsEstimator.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.6 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
23using System;
24using System.Collections.Generic;
25using System.Linq;
26using HEAL.Attic;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Data;
30using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
31using HeuristicLab.Parameters;
32
33namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
34  [StorableType("C8539434-6FB0-47D0-9F5A-2CAE5D8B8B4F")]
35  [Item("Interval Arithmetic Bounds Estimator", "Interpreter for calculation of intervals of symbolic models.")]
36  public sealed class IntervalArithBoundsEstimator : ParameterizedNamedItem, IBoundsEstimator {
37    #region Parameters
38
39    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
40
41    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter =>
42      (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName];
43
44    public int EvaluatedSolutions {
45      get => EvaluatedSolutionsParameter.Value.Value;
46      set => EvaluatedSolutionsParameter.Value.Value = value;
47    }
48    #endregion
49
50    #region Constructors
51
52    [StorableConstructor]
53    private IntervalArithBoundsEstimator(StorableConstructorFlag _) : base(_) { }
54
55    protected IntervalArithBoundsEstimator(IntervalArithBoundsEstimator original, Cloner cloner) : base(original, cloner) { }
56
57    public IntervalArithBoundsEstimator() : base("Interval Arithmetic Bounds Estimator",
58      "Estimates the bounds of the model with interval arithmetic") {
59      Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName,
60        "A counter for the total number of solutions the estimator has evaluated.", new IntValue(0)));
61    }
62
63    public override IDeepCloneable Clone(Cloner cloner) {
64      return new IntervalArithBoundsEstimator(this, cloner);
65    }
66
67    #endregion
68
69    #region IStatefulItem Members
70
71    private readonly object syncRoot = new object();
72
73    public void InitializeState() {
74      EvaluatedSolutions = 0;
75    }
76
77    public void ClearState() { }
78
79    #endregion
80
81    #region Evaluation
82
83    private static Instruction[] PrepareInterpreterState(
84      ISymbolicExpressionTree tree,
85      IDictionary<string, Interval> variableRanges) {
86      if (variableRanges == null)
87        throw new ArgumentNullException("No variable ranges are present!", nameof(variableRanges));
88
89      // Check if all variables used in the tree are present in the dataset
90      foreach (var variable in tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(n => n.VariableName)
91                                   .Distinct())
92        if (!variableRanges.ContainsKey(variable))
93          throw new InvalidOperationException($"No ranges for variable {variable} is present");
94
95      var code = SymbolicExpressionTreeCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
96      foreach (var instr in code.Where(i => i.opCode == OpCodes.Variable)) {
97        var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
98        instr.data = variableRanges[variableTreeNode.VariableName];
99      }
100
101      return code;
102    }
103
104    // Use ref parameter, because the tree will be iterated through recursively from the left-side branch to the right side
105    // Update instructionCounter, whenever Evaluate is called
106    public static Interval Evaluate(
107      Instruction[] instructions, ref int instructionCounter,
108      IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals = null,
109      IDictionary<string, Interval> variableIntervals = null) {
110      var currentInstr = instructions[instructionCounter];
111      instructionCounter++;
112      Interval result;
113
114      switch (currentInstr.opCode) {
115        case OpCodes.Variable: {
116            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
117            var weightInterval = new Interval(variableTreeNode.Weight, variableTreeNode.Weight);
118
119            Interval variableInterval;
120            if (variableIntervals != null && variableIntervals.ContainsKey(variableTreeNode.VariableName))
121              variableInterval = variableIntervals[variableTreeNode.VariableName];
122            else
123              variableInterval = (Interval)currentInstr.data;
124
125            result = Interval.Multiply(variableInterval, weightInterval);
126            break;
127          }
128        case OpCodes.Constant: // fall through
129        case OpCodes.Number: {
130            var numericTreeNode = (INumericTreeNode)currentInstr.dynamicNode;
131            result = new Interval(numericTreeNode.Value, numericTreeNode.Value);
132            break;
133          }
134        case OpCodes.Add: {
135            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
136            for (var i = 1; i < currentInstr.nArguments; i++) {
137              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
138              result = Interval.Add(result, argumentInterval);
139            }
140
141            break;
142          }
143        case OpCodes.Sub: {
144            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
145            if (currentInstr.nArguments == 1)
146              result = Interval.Multiply(new Interval(-1, -1), result);
147
148            for (var i = 1; i < currentInstr.nArguments; i++) {
149              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
150              result = Interval.Subtract(result, argumentInterval);
151            }
152
153            break;
154          }
155        case OpCodes.Mul: {
156            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
157            for (var i = 1; i < currentInstr.nArguments; i++) {
158              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
159              result = Interval.Multiply(result, argumentInterval);
160            }
161
162            break;
163          }
164        case OpCodes.Div: {
165            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
166            if (currentInstr.nArguments == 1)
167              result = Interval.Divide(new Interval(1, 1), result);
168
169            for (var i = 1; i < currentInstr.nArguments; i++) {
170              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
171              result = Interval.Divide(result, argumentInterval);
172            }
173
174            break;
175          }
176        case OpCodes.Sin: {
177            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
178            result = Interval.Sine(argumentInterval);
179            break;
180          }
181        case OpCodes.Cos: {
182            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
183            result = Interval.Cosine(argumentInterval);
184            break;
185          }
186        case OpCodes.Tan: {
187            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
188            result = Interval.Tangens(argumentInterval);
189            break;
190          }
191        case OpCodes.Tanh: {
192            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
193            result = Interval.HyperbolicTangent(argumentInterval);
194            break;
195          }
196        case OpCodes.Log: {
197            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
198            result = Interval.Logarithm(argumentInterval);
199            break;
200          }
201        case OpCodes.Exp: {
202            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
203            result = Interval.Exponential(argumentInterval);
204            break;
205          }
206        case OpCodes.Square: {
207            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
208            result = Interval.Square(argumentInterval);
209            break;
210          }
211        case OpCodes.SquareRoot: {
212            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
213            result = Interval.SquareRoot(argumentInterval);
214            break;
215          }
216        case OpCodes.Cube: {
217            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
218            result = Interval.Cube(argumentInterval);
219            break;
220          }
221        case OpCodes.CubeRoot: {
222            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
223            result = Interval.CubicRoot(argumentInterval);
224            break;
225          }
226        case OpCodes.Power: {
227          var a = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
228          var b = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
229          // support only integer powers
230          if (b.LowerBound == b.UpperBound && Math.Truncate(b.LowerBound) == b.LowerBound) {
231            result = Interval.Power(a, (int)b.LowerBound);
232          } else {
233            throw new NotSupportedException("Interval is only supported for integer powers");
234          }
235          break;
236        }
237        case OpCodes.Absolute: {
238            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
239            result = Interval.Absolute(argumentInterval);
240            break;
241          }
242        case OpCodes.AnalyticQuotient: {
243            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
244            for (var i = 1; i < currentInstr.nArguments; i++) {
245              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
246              result = Interval.AnalyticQuotient(result, argumentInterval);
247            }
248
249            break;
250          }
251        default:
252          throw new NotSupportedException(
253            $"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
254      }
255
256      if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
257        nodeIntervals.Add(currentInstr.dynamicNode, result);
258
259      return result;
260    }
261
262    #endregion
263
264    #region Helpers
265
266    private static IDictionary<string, Interval> GetOccurringVariableRanges(
267      ISymbolicExpressionTree tree, IntervalCollection variableRanges) {
268      var variables = tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(v => v.VariableName).Distinct()
269                          .ToList();
270
271      return variables.ToDictionary(x => x, x => variableRanges.GetReadonlyDictionary()[x]);
272    }
273
274    #endregion
275 
276    public Interval GetModelBound(ISymbolicExpressionTree tree, IntervalCollection variableRanges) {
277      lock (syncRoot) {
278        EvaluatedSolutions++;
279      }
280
281      var occuringVariableRanges = GetOccurringVariableRanges(tree, variableRanges);
282      var instructions = PrepareInterpreterState(tree, occuringVariableRanges);
283      Interval resultInterval;
284      var instructionCounter = 0;
285      resultInterval = Evaluate(instructions, ref instructionCounter, variableIntervals: occuringVariableRanges);
286
287      // because of numerical errors the bounds might be incorrect
288      if (resultInterval.IsInfiniteOrUndefined || resultInterval.LowerBound <= resultInterval.UpperBound)
289        return resultInterval;
290
291      return new Interval(resultInterval.UpperBound, resultInterval.LowerBound);
292    }
293
294    public IDictionary<ISymbolicExpressionTreeNode, Interval> GetModelNodeBounds(
295      ISymbolicExpressionTree tree, IntervalCollection variableRanges) {
296      throw new NotImplementedException();
297    }
298
299    public double GetConstraintViolation(
300      ISymbolicExpressionTree tree, IntervalCollection variableRanges, ShapeConstraint constraint) {
301      var occuringVariableRanges = GetOccurringVariableRanges(tree, variableRanges);
302      var instructions = PrepareInterpreterState(tree, occuringVariableRanges);
303      var instructionCounter = 0;
304      var modelBound = Evaluate(instructions, ref instructionCounter, variableIntervals: occuringVariableRanges);
305      if (constraint.Interval.Contains(modelBound)) return 0.0;
306
307
308      var error = 0.0;
309
310      if (!constraint.Interval.Contains(modelBound.LowerBound)) {
311        error += Math.Abs(modelBound.LowerBound - constraint.Interval.LowerBound);
312      }
313
314      if (!constraint.Interval.Contains(modelBound.UpperBound)) {
315        error += Math.Abs(modelBound.UpperBound - constraint.Interval.UpperBound);
316      }
317
318      return error;
319    }
320
321
322    public bool IsCompatible(ISymbolicExpressionTree tree) {
323      var containsUnknownSymbols = (
324        from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
325        where
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)
347        select n).Any();
348      return !containsUnknownSymbols;
349    }
350  }
351}
Note: See TracBrowser for help on using the repository browser.