[16330] | 1 | #region License Information
|
---|
[17631] | 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 | */
|
---|
[17631] | 21 |
|
---|
[16330] | 22 | #endregion
|
---|
| 23 |
|
---|
| 24 | using System;
|
---|
[16303] | 25 | using System.Collections.Generic;
|
---|
[17637] | 26 | using System.Collections.ObjectModel;
|
---|
[16303] | 27 | using System.Linq;
|
---|
[17579] | 28 | using HEAL.Attic;
|
---|
[16303] | 29 | using HeuristicLab.Common;
|
---|
| 30 | using HeuristicLab.Core;
|
---|
| 31 | using HeuristicLab.Data;
|
---|
| 32 | using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
|
---|
[16367] | 33 | using HeuristicLab.Parameters;
|
---|
[16303] | 34 |
|
---|
[16377] | 35 | namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
|
---|
[16565] | 36 | [StorableType("DE6C1E1E-D7C1-4070-847E-63B68562B10C")]
|
---|
[17631] | 37 | [Item("IntervalInterpreter", "Interpreter for calculation of intervals of symbolic models.")]
|
---|
[16328] | 38 | public sealed class IntervalInterpreter : ParameterizedNamedItem, IStatefulItem {
|
---|
[16303] | 39 | private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
|
---|
[17631] | 40 | private const string MinSplittingWidthParameterName = "MinSplittingWidth";
|
---|
| 41 | private const string MaxSplittingDepthParameterName = "MaxSplittingDepth";
|
---|
| 42 | private const string UseIntervalSplittingParameterName = "UseIntervalSplitting";
|
---|
[16330] | 43 |
|
---|
[17631] | 44 | public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter =>
|
---|
[17756] | 45 | (IFixedValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName];
|
---|
[16303] | 46 |
|
---|
[17631] | 47 | public IFixedValueParameter<IntValue> MinSplittingWithParameter =>
|
---|
[17756] | 48 | (IFixedValueParameter<IntValue>)Parameters[MinSplittingWidthParameterName];
|
---|
[17631] | 49 |
|
---|
| 50 | public IFixedValueParameter<IntValue> MaxSplittingDepthParameter =>
|
---|
[17756] | 51 | (IFixedValueParameter<IntValue>)Parameters[MaxSplittingDepthParameterName];
|
---|
[17631] | 52 |
|
---|
| 53 | public IFixedValueParameter<BoolValue> UseIntervalSplittingParameter =>
|
---|
[17756] | 54 | (IFixedValueParameter<BoolValue>)Parameters[UseIntervalSplittingParameterName];
|
---|
[17631] | 55 |
|
---|
| 56 | public int MinSplittingWidth {
|
---|
| 57 | get => MinSplittingWithParameter.Value.Value;
|
---|
| 58 | set => MinSplittingWithParameter.Value.Value = value;
|
---|
| 59 | }
|
---|
| 60 |
|
---|
| 61 | public int MaxSplittingDepth {
|
---|
| 62 | get => MaxSplittingDepthParameter.Value.Value;
|
---|
| 63 | set => MaxSplittingDepthParameter.Value.Value = value;
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | public bool UseIntervalSplitting {
|
---|
| 67 | get => UseIntervalSplittingParameter.Value.Value;
|
---|
| 68 | set => UseIntervalSplittingParameter.Value.Value = value;
|
---|
| 69 | }
|
---|
| 70 |
|
---|
[16303] | 71 | public int EvaluatedSolutions {
|
---|
[17628] | 72 | get => EvaluatedSolutionsParameter.Value.Value;
|
---|
| 73 | set => EvaluatedSolutionsParameter.Value.Value = value;
|
---|
[16303] | 74 | }
|
---|
| 75 |
|
---|
[16367] | 76 | [StorableConstructor]
|
---|
[16565] | 77 | private IntervalInterpreter(StorableConstructorFlag _) : base(_) { }
|
---|
[17631] | 78 |
|
---|
[16328] | 79 | private IntervalInterpreter(IntervalInterpreter original, Cloner cloner)
|
---|
[17631] | 80 | : base(original, cloner) { }
|
---|
[17742] | 81 |
|
---|
[16323] | 82 | public IntervalInterpreter()
|
---|
[17631] | 83 | : base("IntervalInterpreter", "Interpreter for calculation of intervals of symbolic models.") {
|
---|
| 84 | Parameters.Add(new FixedValueParameter<IntValue>(EvaluatedSolutionsParameterName,
|
---|
| 85 | "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
|
---|
[17742] | 86 | Parameters.Add(new FixedValueParameter<IntValue>(MinSplittingWidthParameterName,
|
---|
| 87 | "Minimum interval width until splitting is stopped", new IntValue(0)));
|
---|
| 88 | Parameters.Add(new FixedValueParameter<IntValue>(MaxSplittingDepthParameterName,
|
---|
| 89 | "Maximum recursion depth of the splitting", new IntValue(5)));
|
---|
[17631] | 90 | Parameters.Add(new FixedValueParameter<BoolValue>(UseIntervalSplittingParameterName, "", new BoolValue(false)));
|
---|
[16367] | 91 | }
|
---|
[17742] | 92 |
|
---|
[16303] | 93 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
[16323] | 94 | return new IntervalInterpreter(this, cloner);
|
---|
[16303] | 95 | }
|
---|
| 96 |
|
---|
[16330] | 97 | private readonly object syncRoot = new object();
|
---|
| 98 |
|
---|
[16328] | 99 | #region IStatefulItem Members
|
---|
[17631] | 100 |
|
---|
[16328] | 101 | public void InitializeState() {
|
---|
| 102 | EvaluatedSolutions = 0;
|
---|
| 103 | }
|
---|
[17631] | 104 |
|
---|
[16328] | 105 | public void ClearState() { }
|
---|
[17631] | 106 |
|
---|
[16328] | 107 | #endregion
|
---|
| 108 |
|
---|
[17631] | 109 | public Interval GetSymbolicExpressionTreeInterval(
|
---|
| 110 | ISymbolicExpressionTree tree, IDataset dataset,
|
---|
[17756] | 111 | IEnumerable<int> rows = null) {
|
---|
[16403] | 112 | var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
|
---|
[17637] | 113 | return GetSymbolicExpressionTreeInterval(tree, variableRanges);
|
---|
[16330] | 114 | }
|
---|
| 115 |
|
---|
[17631] | 116 | public Interval GetSymbolicExpressionTreeIntervals(
|
---|
| 117 | ISymbolicExpressionTree tree, IDataset dataset,
|
---|
| 118 | out IDictionary<ISymbolicExpressionTreeNode, Interval>
|
---|
[17756] | 119 | nodeIntervals, IEnumerable<int> rows = null) {
|
---|
[16403] | 120 | var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
|
---|
[17637] | 121 | return GetSymbolicExpressionTreeIntervals(tree, variableRanges, out nodeIntervals);
|
---|
[16328] | 122 | }
|
---|
| 123 |
|
---|
[17631] | 124 | public Interval GetSymbolicExpressionTreeInterval(
|
---|
| 125 | ISymbolicExpressionTree tree,
|
---|
[17756] | 126 | IReadOnlyDictionary<string, Interval> variableRanges) {
|
---|
[16364] | 127 | lock (syncRoot) {
|
---|
[16330] | 128 | EvaluatedSolutions++;
|
---|
| 129 | }
|
---|
[16328] | 130 |
|
---|
[17631] | 131 | Interval outputInterval;
|
---|
| 132 |
|
---|
[17637] | 133 | if (UseIntervalSplitting) {
|
---|
[17631] | 134 | outputInterval = GetSymbolicExpressionTreeIntervals(tree, variableRanges,
|
---|
[17756] | 135 | out var _);
|
---|
[17631] | 136 | } else {
|
---|
| 137 | var instructionCount = 0;
|
---|
| 138 | var instructions = PrepareInterpreterState(tree, variableRanges);
|
---|
| 139 | outputInterval = Evaluate(instructions, ref instructionCount);
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | return outputInterval.LowerBound <= outputInterval.UpperBound
|
---|
| 143 | ? outputInterval
|
---|
| 144 | : new Interval(outputInterval.UpperBound, outputInterval.LowerBound);
|
---|
[16328] | 145 | }
|
---|
| 146 |
|
---|
[16364] | 147 |
|
---|
[17631] | 148 | public Interval GetSymbolicExpressionTreeIntervals(
|
---|
| 149 | ISymbolicExpressionTree tree,
|
---|
[17637] | 150 | IReadOnlyDictionary<string, Interval> variableRanges,
|
---|
[17631] | 151 | out IDictionary<ISymbolicExpressionTreeNode, Interval>
|
---|
[17756] | 152 | nodeIntervals) {
|
---|
[16330] | 153 | lock (syncRoot) {
|
---|
| 154 | EvaluatedSolutions++;
|
---|
| 155 | }
|
---|
[17631] | 156 |
|
---|
[16404] | 157 | var intervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
|
---|
| 158 | var instructions = PrepareInterpreterState(tree, variableRanges);
|
---|
[16328] | 159 |
|
---|
[17631] | 160 | Interval outputInterval;
|
---|
[17637] | 161 | if (UseIntervalSplitting) {
|
---|
[17760] | 162 | //var variables = tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(x => x.VariableName).Distinct()
|
---|
| 163 | // .ToList();
|
---|
| 164 | var containsDependencyProblem = ContainsVariableMultipleTimes(tree, out var variables);
|
---|
[17631] | 165 |
|
---|
[17742] | 166 | if (containsDependencyProblem) {
|
---|
[17631] | 167 | var currIndex = 0;
|
---|
| 168 | var currDepth = 0;
|
---|
[17742] | 169 | IDictionary<string, Interval> writeableVariableRanges =
|
---|
| 170 | variableRanges.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
---|
| 171 | //outputInterval = EvaluateRecursive(instructions, intervals, writeableVariableRanges, variables, MinSplittingWidth, MaxSplittingDepth,
|
---|
| 172 | // ref currIndex, ref currDepth, tree);
|
---|
[17760] | 173 | outputInterval = EvaluateWithSplitting(instructions, intervals, writeableVariableRanges, variables);
|
---|
[17631] | 174 | } else {
|
---|
| 175 | var instructionCount = 0;
|
---|
| 176 | outputInterval = Evaluate(instructions, ref instructionCount, intervals);
|
---|
| 177 | }
|
---|
| 178 | } else {
|
---|
| 179 | var instructionCount = 0;
|
---|
| 180 | outputInterval = Evaluate(instructions, ref instructionCount, intervals);
|
---|
| 181 | }
|
---|
| 182 |
|
---|
[16629] | 183 | nodeIntervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
|
---|
[16740] | 184 | foreach (var kvp in intervals) {
|
---|
[16629] | 185 | var interval = kvp.Value;
|
---|
| 186 | if (interval.IsInfiniteOrUndefined || interval.LowerBound <= interval.UpperBound)
|
---|
| 187 | nodeIntervals.Add(kvp.Key, interval);
|
---|
| 188 | else
|
---|
| 189 | nodeIntervals.Add(kvp.Key, new Interval(interval.UpperBound, interval.LowerBound));
|
---|
| 190 | }
|
---|
[16404] | 191 |
|
---|
[16629] | 192 | // because of numerical errors the bounds might be incorrect
|
---|
| 193 | if (outputInterval.IsInfiniteOrUndefined || outputInterval.LowerBound <= outputInterval.UpperBound)
|
---|
| 194 | return outputInterval;
|
---|
[17631] | 195 |
|
---|
| 196 | return new Interval(outputInterval.UpperBound, outputInterval.LowerBound);
|
---|
[16328] | 197 | }
|
---|
| 198 |
|
---|
[16364] | 199 |
|
---|
[17631] | 200 | private static Instruction[] PrepareInterpreterState(
|
---|
| 201 | ISymbolicExpressionTree tree,
|
---|
[17637] | 202 | IReadOnlyDictionary<string, Interval> variableRanges) {
|
---|
[16404] | 203 | if (variableRanges == null)
|
---|
| 204 | throw new ArgumentNullException("No variablew ranges are present!", nameof(variableRanges));
|
---|
[16328] | 205 |
|
---|
[16404] | 206 | //Check if all variables used in the tree are present in the dataset
|
---|
[17742] | 207 | foreach (var variable in tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(n => n.VariableName)
|
---|
| 208 | .Distinct())
|
---|
[17631] | 209 | if (!variableRanges.ContainsKey(variable))
|
---|
| 210 | throw new InvalidOperationException($"No ranges for variable {variable} is present");
|
---|
[16330] | 211 |
|
---|
[17631] | 212 | var code = SymbolicExpressionTreeCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
|
---|
| 213 | foreach (var instr in code.Where(i => i.opCode == OpCodes.Variable)) {
|
---|
[17756] | 214 | var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
|
---|
[16404] | 215 | instr.data = variableRanges[variableTreeNode.VariableName];
|
---|
[16303] | 216 | }
|
---|
[17631] | 217 |
|
---|
[16330] | 218 | return code;
|
---|
[16303] | 219 | }
|
---|
| 220 |
|
---|
[17742] | 221 | public static Interval EvaluateWithSplitting(Instruction[] instructions,
|
---|
| 222 | IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
|
---|
[17760] | 223 | IDictionary<string, Interval> variableIntervals, List<string> multipleOccurenceVariables) {
|
---|
[17756] | 224 | var savedIntervals = variableIntervals.ToDictionary(entry => entry.Key, entry => entry.Value);
|
---|
[17760] | 225 | var min = FindBound(instructions, nodeIntervals, variableIntervals, multipleOccurenceVariables, minimization: true);
|
---|
| 226 | var max = FindBound(instructions, nodeIntervals, savedIntervals, multipleOccurenceVariables, minimization: false);
|
---|
[17637] | 227 |
|
---|
[17756] | 228 | return new Interval(min, max);
|
---|
[17742] | 229 | }
|
---|
| 230 |
|
---|
[17756] | 231 |
|
---|
| 232 | private static double FindBound(Instruction[] instructions,
|
---|
| 233 | IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
|
---|
[17760] | 234 | IDictionary<string, Interval> variableIntervals, List<string> multipleOccurenceVariables, bool minimization = true) {
|
---|
[17756] | 235 | SortedSet<BoxBound> prioQ = new SortedSet<BoxBound>();
|
---|
| 236 |
|
---|
[17742] | 237 | var ic = 0;
|
---|
| 238 | //Calculate full box
|
---|
| 239 | IReadOnlyDictionary<string, Interval> readonlyRanges = variableIntervals.ToDictionary(k => k.Key, k => k.Value);
|
---|
[17756] | 240 | var interval = Evaluate(instructions, ref ic, nodeIntervals, readonlyRanges);
|
---|
| 241 | // the order of keys in a dictionary is guaranteed to be the same order as values in a dictionary
|
---|
| 242 | // https://docs.microsoft.com/en-us/dotnet/api/system.collections.idictionary.keys?view=netcore-3.1#remarks
|
---|
[17760] | 243 | //var box = variableIntervals.Values;
|
---|
| 244 | //Box only contains intervals from multiple occurence variables
|
---|
| 245 | var box = multipleOccurenceVariables.Select(k => variableIntervals[k]);
|
---|
[17757] | 246 | if (minimization) {
|
---|
[17756] | 247 | prioQ.Add(new BoxBound(box, interval.LowerBound));
|
---|
| 248 | } else {
|
---|
| 249 | prioQ.Add(new BoxBound(box, -interval.UpperBound));
|
---|
| 250 | }
|
---|
[17742] | 251 |
|
---|
[17756] | 252 | // TODO a fixed limit for depth?!
|
---|
[17742] | 253 | for (var depth = 0; depth < 200; ++depth) {
|
---|
[17756] | 254 | var currentBound = prioQ.Min;
|
---|
| 255 | prioQ.Remove(currentBound);
|
---|
[17742] | 256 |
|
---|
[17756] | 257 | var newBoxes = Split(currentBound.box);
|
---|
[17742] | 258 |
|
---|
[17756] | 259 | foreach (var newBox in newBoxes) {
|
---|
[17760] | 260 | //var intervalEnum = newBox.GetEnumerator();
|
---|
| 261 | //var keyEnum = readonlyRanges.Keys.GetEnumerator();
|
---|
| 262 | //while (intervalEnum.MoveNext() & keyEnum.MoveNext()) {
|
---|
| 263 | // variableIntervals[keyEnum.Current] = intervalEnum.Current;
|
---|
| 264 | //}
|
---|
| 265 | //Set the splitted variables
|
---|
[17756] | 266 | var intervalEnum = newBox.GetEnumerator();
|
---|
[17760] | 267 | foreach (var key in multipleOccurenceVariables) {
|
---|
| 268 | intervalEnum.MoveNext();
|
---|
| 269 | variableIntervals[key] = intervalEnum.Current;
|
---|
[17742] | 270 | }
|
---|
| 271 |
|
---|
| 272 | ic = 0;
|
---|
| 273 | var res = Evaluate(instructions, ref ic, nodeIntervals,
|
---|
| 274 | new ReadOnlyDictionary<string, Interval>(variableIntervals));
|
---|
[17756] | 275 | if (minimization) {
|
---|
[17760] | 276 | prioQ.Add(new BoxBound(newBox, res.LowerBound));
|
---|
[17756] | 277 | } else {
|
---|
[17760] | 278 | prioQ.Add(new BoxBound(newBox, -res.UpperBound));
|
---|
[17756] | 279 | }
|
---|
[17742] | 280 | }
|
---|
| 281 | }
|
---|
| 282 |
|
---|
[17757] | 283 | return minimization ?
|
---|
| 284 | prioQ.First().bound :
|
---|
[17756] | 285 | -prioQ.First().bound;
|
---|
[17742] | 286 | }
|
---|
| 287 |
|
---|
| 288 | private static IEnumerable<IEnumerable<Interval>> Split(List<Interval> box) {
|
---|
[17756] | 289 | var boxes = box.Select(region => region.Split()).Select(split => new List<Interval> { split.Item1, split.Item2 })
|
---|
[17742] | 290 | .ToList();
|
---|
| 291 |
|
---|
| 292 | return boxes.CartesianProduct();
|
---|
| 293 | }
|
---|
| 294 |
|
---|
[17756] | 295 | // a multi-dimensional box with an associated bound
|
---|
[17757] | 296 | // boxbounds are ordered first by bound (smaller first), then by size of box (larger first) then by distance of bottom left corner to origin
|
---|
[17756] | 297 | private class BoxBound : IComparable<BoxBound> {
|
---|
| 298 | public List<Interval> box;
|
---|
| 299 | public double bound;
|
---|
| 300 | public BoxBound(IEnumerable<Interval> box, double bound) {
|
---|
| 301 | this.box = new List<Interval>(box);
|
---|
| 302 | this.bound = bound;
|
---|
[17742] | 303 | }
|
---|
[17756] | 304 | public int CompareTo(BoxBound other) {
|
---|
| 305 | if (bound != other.bound) return bound.CompareTo(other.bound);
|
---|
[17757] | 306 |
|
---|
[17756] | 307 | var thisSize = box.Aggregate(1.0, (current, dimExtent) => current * dimExtent.Width);
|
---|
| 308 | var otherSize = other.box.Aggregate(1.0, (current, dimExtent) => current * dimExtent.Width);
|
---|
[17757] | 309 | if (thisSize != otherSize) return -thisSize.CompareTo(otherSize);
|
---|
[17742] | 310 |
|
---|
[17757] | 311 | var thisDist = box.Sum(dimExtent => dimExtent.LowerBound * dimExtent.LowerBound);
|
---|
| 312 | var otherDist = other.box.Sum(dimExtent => dimExtent.LowerBound * dimExtent.LowerBound);
|
---|
| 313 | if (thisDist != otherDist) return thisDist.CompareTo(otherDist);
|
---|
| 314 |
|
---|
| 315 | // which is smaller first along the dimensions?
|
---|
| 316 | for (int i = 0; i < box.Count; i++) {
|
---|
| 317 | if (box[i].LowerBound != other.box[i].LowerBound) return box[i].LowerBound.CompareTo(other.box[i].LowerBound);
|
---|
[17742] | 318 | }
|
---|
[17757] | 319 |
|
---|
[17756] | 320 | return 0;
|
---|
[17742] | 321 | }
|
---|
[17758] | 322 | }
|
---|
[17742] | 323 |
|
---|
[17758] | 324 | public static Interval EvaluateRecursive(
|
---|
| 325 | Instruction[] instructions,
|
---|
| 326 | IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
|
---|
| 327 | IDictionary<string, Interval> variableIntervals, IList<string> variables,
|
---|
| 328 | double minWidth, int maxDepth, ref int currIndex, ref int currDepth,
|
---|
| 329 | ISymbolicExpressionTree tree) {
|
---|
| 330 | Interval evaluate() {
|
---|
| 331 | var ic = 0;
|
---|
| 332 | IReadOnlyDictionary<string, Interval> readonlyRanges =
|
---|
| 333 | new ReadOnlyDictionary<string, Interval>(variableIntervals);
|
---|
| 334 | return Evaluate(instructions, ref ic, nodeIntervals, readonlyRanges);
|
---|
| 335 | }
|
---|
[17631] | 336 |
|
---|
[17758] | 337 | Interval recurse(ref int idx, ref int depth) {
|
---|
| 338 | return EvaluateRecursive(instructions, nodeIntervals, variableIntervals, variables, minWidth, maxDepth, ref idx,
|
---|
| 339 | ref depth, tree);
|
---|
| 340 | }
|
---|
[17631] | 341 |
|
---|
| 342 |
|
---|
[17758] | 343 | var v = variables[currIndex];
|
---|
| 344 | var x = variableIntervals[v];
|
---|
| 345 | if (x.Width < minWidth || currDepth == maxDepth || !MultipleTimes(tree, v)) {
|
---|
| 346 | if (currIndex + 1 < variables.Count) {
|
---|
| 347 | currDepth = 0;
|
---|
| 348 | currIndex++;
|
---|
| 349 | var z = recurse(ref currIndex, ref currDepth);
|
---|
| 350 | currIndex--;
|
---|
| 351 | return z;
|
---|
[17631] | 352 | }
|
---|
| 353 |
|
---|
[17758] | 354 | return evaluate();
|
---|
[17631] | 355 | }
|
---|
| 356 |
|
---|
[17758] | 357 | var t = x.Split();
|
---|
| 358 | var xa = t.Item1;
|
---|
| 359 | var xb = t.Item2;
|
---|
| 360 | var d = currDepth;
|
---|
| 361 | currDepth = d + 1;
|
---|
| 362 | variableIntervals[v] = xa;
|
---|
| 363 | var ya = recurse(ref currIndex, ref currDepth);
|
---|
| 364 | currDepth = d + 1;
|
---|
| 365 | variableIntervals[v] = xb;
|
---|
| 366 | var yb = recurse(ref currIndex, ref currDepth);
|
---|
| 367 | variableIntervals[v] = x; // restore interval
|
---|
| 368 | return ya | yb;
|
---|
| 369 | }
|
---|
[17631] | 370 |
|
---|
[17758] | 371 | public static Interval Evaluate(
|
---|
| 372 | Instruction[] instructions, ref int instructionCounter,
|
---|
| 373 | IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals = null,
|
---|
| 374 | IReadOnlyDictionary<string, Interval> variableIntervals = null) {
|
---|
| 375 | var currentInstr = instructions[instructionCounter];
|
---|
| 376 | //Use ref parameter, because the tree will be iterated through recursively from the left-side branch to the right side
|
---|
| 377 | //Update instructionCounter, whenever Evaluate is called
|
---|
| 378 | instructionCounter++;
|
---|
| 379 | Interval result = null;
|
---|
[16331] | 380 |
|
---|
[17758] | 381 | switch (currentInstr.opCode) {
|
---|
| 382 | //Variables, Constants, ...
|
---|
| 383 | case OpCodes.Variable: {
|
---|
| 384 | var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
|
---|
| 385 | var weightInterval = new Interval(variableTreeNode.Weight, variableTreeNode.Weight);
|
---|
| 386 | //var variableInterval = (Interval)currentInstr.data;
|
---|
[16383] | 387 |
|
---|
[17758] | 388 | Interval variableInterval;
|
---|
| 389 | if (variableIntervals != null && variableIntervals.ContainsKey(variableTreeNode.VariableName))
|
---|
| 390 | variableInterval = variableIntervals[variableTreeNode.VariableName];
|
---|
| 391 | else
|
---|
| 392 | variableInterval = (Interval)currentInstr.data;
|
---|
[17631] | 393 |
|
---|
[17758] | 394 | result = Interval.Multiply(variableInterval, weightInterval);
|
---|
| 395 | break;
|
---|
| 396 | }
|
---|
| 397 | case OpCodes.Constant: {
|
---|
| 398 | var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
|
---|
| 399 | result = new Interval(constTreeNode.Value, constTreeNode.Value);
|
---|
| 400 | break;
|
---|
| 401 | }
|
---|
| 402 | //Elementary arithmetic rules
|
---|
| 403 | case OpCodes.Add: {
|
---|
| 404 | //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 405 | result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 406 | for (var i = 1; i < currentInstr.nArguments; i++) {
|
---|
| 407 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 408 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 409 | result = Interval.Add(result, argumentInterval);
|
---|
[17756] | 410 | }
|
---|
| 411 |
|
---|
[17758] | 412 | break;
|
---|
| 413 | }
|
---|
| 414 | case OpCodes.Sub: {
|
---|
| 415 | //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 416 | result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 417 | if (currentInstr.nArguments == 1)
|
---|
| 418 | result = Interval.Multiply(new Interval(-1, -1), result);
|
---|
[16404] | 419 |
|
---|
[17758] | 420 | for (var i = 1; i < currentInstr.nArguments; i++) {
|
---|
[17756] | 421 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 422 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
[17758] | 423 | result = Interval.Subtract(result, argumentInterval);
|
---|
[17756] | 424 | }
|
---|
[17758] | 425 |
|
---|
| 426 | break;
|
---|
| 427 | }
|
---|
| 428 | case OpCodes.Mul: {
|
---|
| 429 | //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 430 | result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 431 | for (var i = 1; i < currentInstr.nArguments; i++) {
|
---|
[17756] | 432 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 433 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
[17758] | 434 | result = Interval.Multiply(result, argumentInterval);
|
---|
[17756] | 435 | }
|
---|
[17758] | 436 |
|
---|
| 437 | break;
|
---|
| 438 | }
|
---|
| 439 | case OpCodes.Div: {
|
---|
| 440 | //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 441 | result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 442 | if (currentInstr.nArguments == 1)
|
---|
| 443 | result = Interval.Divide(new Interval(1, 1), result);
|
---|
| 444 |
|
---|
| 445 | for (var i = 1; i < currentInstr.nArguments; i++) {
|
---|
[17756] | 446 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 447 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
[17758] | 448 | result = Interval.Divide(result, argumentInterval);
|
---|
[17756] | 449 | }
|
---|
[17758] | 450 |
|
---|
| 451 | break;
|
---|
| 452 | }
|
---|
| 453 | //Trigonometric functions
|
---|
| 454 | case OpCodes.Sin: {
|
---|
| 455 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 456 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 457 | result = Interval.Sine(argumentInterval);
|
---|
| 458 | break;
|
---|
| 459 | }
|
---|
| 460 | case OpCodes.Cos: {
|
---|
| 461 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 462 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 463 | result = Interval.Cosine(argumentInterval);
|
---|
| 464 | break;
|
---|
| 465 | }
|
---|
| 466 | case OpCodes.Tan: {
|
---|
| 467 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 468 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 469 | result = Interval.Tangens(argumentInterval);
|
---|
| 470 | break;
|
---|
| 471 | }
|
---|
| 472 | case OpCodes.Tanh: {
|
---|
| 473 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 474 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 475 | result = Interval.HyperbolicTangent(argumentInterval);
|
---|
| 476 | break;
|
---|
| 477 | }
|
---|
| 478 | //Exponential functions
|
---|
| 479 | case OpCodes.Log: {
|
---|
| 480 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 481 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 482 | result = Interval.Logarithm(argumentInterval);
|
---|
| 483 | break;
|
---|
| 484 | }
|
---|
| 485 | case OpCodes.Exp: {
|
---|
| 486 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 487 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 488 | result = Interval.Exponential(argumentInterval);
|
---|
| 489 | break;
|
---|
| 490 | }
|
---|
| 491 | case OpCodes.Square: {
|
---|
| 492 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 493 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 494 | result = Interval.Square(argumentInterval);
|
---|
| 495 | break;
|
---|
| 496 | }
|
---|
| 497 | case OpCodes.SquareRoot: {
|
---|
| 498 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 499 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 500 | result = Interval.SquareRoot(argumentInterval);
|
---|
| 501 | break;
|
---|
| 502 | }
|
---|
| 503 | case OpCodes.Cube: {
|
---|
| 504 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 505 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 506 | result = Interval.Cube(argumentInterval);
|
---|
| 507 | break;
|
---|
| 508 | }
|
---|
| 509 | case OpCodes.CubeRoot: {
|
---|
| 510 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 511 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 512 | result = Interval.CubicRoot(argumentInterval);
|
---|
| 513 | break;
|
---|
| 514 | }
|
---|
| 515 | case OpCodes.Absolute: {
|
---|
| 516 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 517 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 518 | result = Interval.Absolute(argumentInterval);
|
---|
| 519 | break;
|
---|
| 520 | }
|
---|
| 521 | case OpCodes.AnalyticQuotient: {
|
---|
| 522 | //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 523 | result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
| 524 | for (var i = 1; i < currentInstr.nArguments; i++) {
|
---|
[17756] | 525 | //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
|
---|
| 526 | var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
|
---|
[17758] | 527 | result = Interval.AnalyticalQuotient(result, argumentInterval);
|
---|
[17756] | 528 | }
|
---|
[17579] | 529 |
|
---|
[17758] | 530 | break;
|
---|
| 531 | }
|
---|
| 532 | default:
|
---|
| 533 | throw new NotSupportedException($"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
|
---|
| 534 | }
|
---|
[16331] | 535 |
|
---|
[17758] | 536 | if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
|
---|
| 537 | nodeIntervals.Add(currentInstr.dynamicNode, result);
|
---|
[16383] | 538 |
|
---|
[17758] | 539 | return result;
|
---|
| 540 | }
|
---|
[16330] | 541 |
|
---|
[17758] | 542 | private static bool MultipleTimes(ISymbolicExpressionTree tree, string variable) {
|
---|
| 543 | var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
|
---|
| 544 | var group = varlist.Select(x => x.Key == variable).Count();
|
---|
[17631] | 545 |
|
---|
[17758] | 546 | return group > 1;
|
---|
| 547 | }
|
---|
[17631] | 548 |
|
---|
[17760] | 549 | private static bool ContainsVariableMultipleTimes(ISymbolicExpressionTree tree, out List<String> variables) {
|
---|
| 550 | variables = new List<string>();
|
---|
[17758] | 551 | var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
|
---|
[17760] | 552 | foreach (var group in varlist) {
|
---|
| 553 | if (group.Count() > 1) {
|
---|
| 554 | variables.Add(group.Key);
|
---|
| 555 | }
|
---|
| 556 | }
|
---|
| 557 |
|
---|
[17758] | 558 | return varlist.Any(group => group.Count() > 1);
|
---|
| 559 | }
|
---|
[17631] | 560 |
|
---|
| 561 |
|
---|
[17758] | 562 | public static bool IsCompatible(ISymbolicExpressionTree tree) {
|
---|
| 563 | var containsUnknownSymbols = (
|
---|
| 564 | from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
|
---|
| 565 | where
|
---|
| 566 | !(n.Symbol is Variable) &&
|
---|
| 567 | !(n.Symbol is Constant) &&
|
---|
| 568 | !(n.Symbol is StartSymbol) &&
|
---|
| 569 | !(n.Symbol is Addition) &&
|
---|
| 570 | !(n.Symbol is Subtraction) &&
|
---|
| 571 | !(n.Symbol is Multiplication) &&
|
---|
| 572 | !(n.Symbol is Division) &&
|
---|
| 573 | !(n.Symbol is Sine) &&
|
---|
| 574 | !(n.Symbol is Cosine) &&
|
---|
| 575 | !(n.Symbol is Tangent) &&
|
---|
| 576 | !(n.Symbol is HyperbolicTangent) &&
|
---|
| 577 | !(n.Symbol is Logarithm) &&
|
---|
| 578 | !(n.Symbol is Exponential) &&
|
---|
| 579 | !(n.Symbol is Square) &&
|
---|
| 580 | !(n.Symbol is SquareRoot) &&
|
---|
| 581 | !(n.Symbol is Cube) &&
|
---|
| 582 | !(n.Symbol is CubeRoot) &&
|
---|
| 583 | !(n.Symbol is Absolute) &&
|
---|
| 584 | !(n.Symbol is AnalyticQuotient)
|
---|
| 585 | select n).Any();
|
---|
| 586 | return !containsUnknownSymbols;
|
---|
[16330] | 587 | }
|
---|
[17758] | 588 | }
|
---|
| 589 | } |
---|