Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IntervalInterpreter.cs @ 17742

Last change on this file since 17742 was 17742, checked in by chaider, 4 years ago

#3073 Added smart splitting method to interpreter

File size: 25.4 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.Collections.ObjectModel;
27using System.Linq;
28using HEAL.Attic;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Data;
32using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
33using HeuristicLab.Parameters;
34
35namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
36  [StorableType("DE6C1E1E-D7C1-4070-847E-63B68562B10C")]
37  [Item("IntervalInterpreter", "Interpreter for calculation of intervals of symbolic models.")]
38  public sealed class IntervalInterpreter : ParameterizedNamedItem, IStatefulItem {
39    private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
40    private const string MinSplittingWidthParameterName = "MinSplittingWidth";
41    private const string MaxSplittingDepthParameterName = "MaxSplittingDepth";
42    private const string UseIntervalSplittingParameterName = "UseIntervalSplitting";
43
44    public IFixedValueParameter<IntValue> EvaluatedSolutionsParameter =>
45      (IFixedValueParameter<IntValue>) Parameters[EvaluatedSolutionsParameterName];
46
47    public IFixedValueParameter<IntValue> MinSplittingWithParameter =>
48      (IFixedValueParameter<IntValue>) Parameters[MinSplittingWidthParameterName];
49
50    public IFixedValueParameter<IntValue> MaxSplittingDepthParameter =>
51      (IFixedValueParameter<IntValue>) Parameters[MaxSplittingDepthParameterName];
52
53    public IFixedValueParameter<BoolValue> UseIntervalSplittingParameter =>
54      (IFixedValueParameter<BoolValue>) Parameters[UseIntervalSplittingParameterName];
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
71    public int EvaluatedSolutions {
72      get => EvaluatedSolutionsParameter.Value.Value;
73      set => EvaluatedSolutionsParameter.Value.Value = value;
74    }
75
76    [StorableConstructor]
77    private IntervalInterpreter(StorableConstructorFlag _) : base(_) { }
78
79    private IntervalInterpreter(IntervalInterpreter original, Cloner cloner)
80      : base(original, cloner) { }
81
82    public IntervalInterpreter()
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)));
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)));
90      Parameters.Add(new FixedValueParameter<BoolValue>(UseIntervalSplittingParameterName, "", new BoolValue(false)));
91    }
92
93    public override IDeepCloneable Clone(Cloner cloner) {
94      return new IntervalInterpreter(this, cloner);
95    }
96
97    private readonly object syncRoot = new object();
98
99    #region IStatefulItem Members
100
101    public void InitializeState() {
102      EvaluatedSolutions = 0;
103    }
104
105    public void ClearState() { }
106
107    #endregion
108
109    public Interval GetSymbolicExpressionTreeInterval(
110      ISymbolicExpressionTree tree, IDataset dataset,
111      IEnumerable<int> rows = null, int splitDirection = 0) {
112      var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
113      return GetSymbolicExpressionTreeInterval(tree, variableRanges);
114    }
115
116    public Interval GetSymbolicExpressionTreeIntervals(
117      ISymbolicExpressionTree tree, IDataset dataset,
118      out IDictionary<ISymbolicExpressionTreeNode, Interval>
119        nodeIntervals, IEnumerable<int> rows = null, int splitDirection = 0) {
120      var variableRanges = DatasetUtil.GetVariableRanges(dataset, rows);
121      return GetSymbolicExpressionTreeIntervals(tree, variableRanges, out nodeIntervals);
122    }
123
124    public Interval GetSymbolicExpressionTreeInterval(
125      ISymbolicExpressionTree tree,
126      IReadOnlyDictionary<string, Interval> variableRanges, int splitDirection = 0) {
127      lock (syncRoot) {
128        EvaluatedSolutions++;
129      }
130
131      Interval outputInterval;
132
133      if (UseIntervalSplitting) {
134        outputInterval = GetSymbolicExpressionTreeIntervals(tree, variableRanges,
135          out var nodeIntervals);
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);
145    }
146
147
148    public Interval GetSymbolicExpressionTreeIntervals(
149      ISymbolicExpressionTree tree,
150      IReadOnlyDictionary<string, Interval> variableRanges,
151      out IDictionary<ISymbolicExpressionTreeNode, Interval>
152        nodeIntervals, int splitDirection = 0) {
153      lock (syncRoot) {
154        EvaluatedSolutions++;
155      }
156
157      var intervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
158      var instructions = PrepareInterpreterState(tree, variableRanges);
159
160      Interval outputInterval;
161      if (UseIntervalSplitting) {
162        var variables = tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(x => x.VariableName).Distinct()
163                            .ToList();
164        var containsDependencyProblem = ContainsVariableMultipleTimes(tree);
165
166        if (containsDependencyProblem) {
167          var currIndex = 0;
168          var currDepth = 0;
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);
173          outputInterval = EvaluateWithSplitting(instructions, intervals, writeableVariableRanges, splitDirection);
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
183      nodeIntervals = new Dictionary<ISymbolicExpressionTreeNode, Interval>();
184      foreach (var kvp in intervals) {
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      }
191
192      // because of numerical errors the bounds might be incorrect
193      if (outputInterval.IsInfiniteOrUndefined || outputInterval.LowerBound <= outputInterval.UpperBound)
194        return outputInterval;
195
196      return new Interval(outputInterval.UpperBound, outputInterval.LowerBound);
197    }
198
199
200    private static Instruction[] PrepareInterpreterState(
201      ISymbolicExpressionTree tree,
202      IReadOnlyDictionary<string, Interval> variableRanges) {
203      if (variableRanges == null)
204        throw new ArgumentNullException("No variablew ranges are present!", nameof(variableRanges));
205
206      //Check if all variables used in the tree are present in the dataset
207      foreach (var variable in tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(n => n.VariableName)
208                                   .Distinct())
209        if (!variableRanges.ContainsKey(variable))
210          throw new InvalidOperationException($"No ranges for variable {variable} is present");
211
212      var code = SymbolicExpressionTreeCompiler.Compile(tree, OpCodes.MapSymbolToOpCode);
213      foreach (var instr in code.Where(i => i.opCode == OpCodes.Variable)) {
214        var variableTreeNode = (VariableTreeNode) instr.dynamicNode;
215        instr.data = variableRanges[variableTreeNode.VariableName];
216      }
217
218      return code;
219    }
220
221    public static Interval EvaluateWithSplitting(Instruction[] instructions,
222                                                 IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
223                                                 IDictionary<string, Interval> variableIntervals,
224                                                 int splitDirection = 0) {
225      if (splitDirection == 0) {
226        var savedIntervals = variableIntervals.ToDictionary(entry => entry.Key, entry => entry.Value);
227        var minimization = PerformSplitting(instructions, nodeIntervals, variableIntervals, -1);
228        var maximization = PerformSplitting(instructions, nodeIntervals, savedIntervals, 1);
229
230        return new Interval(minimization.LowerBound, maximization.UpperBound);
231      }
232
233      return PerformSplitting(instructions, nodeIntervals, variableIntervals, splitDirection);
234    }
235
236    private static Interval PerformSplitting(Instruction[] instructions,
237                                             IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
238                                             IDictionary<string, Interval> variableIntervals, int splitDirection) {
239      SortedList<Tuple<double, List<Interval>>, Interval> priorityList = null;
240      priorityList = splitDirection == -1 ? new SortedList<Tuple<double, List<Interval>>, Interval>(new BoxMinimizer()) : new SortedList<Tuple<double, List<Interval>>, Interval>(new BoxMaximizer());
241      var ic = 0;
242      //Calculate full box
243      IReadOnlyDictionary<string, Interval> readonlyRanges = variableIntervals.ToDictionary(k => k.Key, k => k.Value);
244      var fullbox = Evaluate(instructions, ref ic, nodeIntervals, readonlyRanges);
245      var box = variableIntervals.Keys.Select(k => variableIntervals[k]).ToList();
246      priorityList.Add(
247        splitDirection == -1
248          ? new Tuple<double, List<Interval>>(fullbox.LowerBound, box)
249          : new Tuple<double, List<Interval>>(fullbox.UpperBound, box), fullbox);
250
251      for (var depth = 0; depth < 200; ++depth) {
252        var firstBox = priorityList.First();
253        priorityList.RemoveAt(0);
254
255        var boxes = Split(firstBox.Key.Item2);
256
257        foreach (var b in boxes) {
258          var i = 0;
259          foreach (var k in readonlyRanges.Keys) {
260            variableIntervals[k] = b.ToList()[i];
261            ++i;
262          }
263
264          ic = 0;
265          var res = Evaluate(instructions, ref ic, nodeIntervals,
266            new ReadOnlyDictionary<string, Interval>(variableIntervals));
267          priorityList.Add(
268            splitDirection == -1
269              ? new Tuple<double, List<Interval>>(res.LowerBound, b.ToList())
270              : new Tuple<double, List<Interval>>(res.UpperBound, b.ToList()), res);
271        }
272      }
273
274      //Calculate final interval
275      var final = new Interval(0, 0);
276
277      return priorityList.Aggregate(final, (current, b) => current | b.Value);
278    }
279
280    private static IEnumerable<IEnumerable<Interval>> Split(List<Interval> box) {
281      var boxes = box.Select(region => region.Split()).Select(split => new List<Interval> {split.Item1, split.Item2})
282                     .ToList();
283
284      return boxes.CartesianProduct();
285    }
286
287    private class BoxMinimizer : IComparer<Tuple<double, List<Interval>>> {
288      public int Compare(Tuple<double, List<Interval>> x, Tuple<double, List<Interval>> y) {
289        var result = x.Item1.CompareTo(y.Item1);
290
291        if (result == 0) {
292          var sizeBox2 = y.Item2.Aggregate(1.0, (current, region) => current * region.Width);
293          var sizeBox1 = x.Item2.Aggregate(1.0, (current, region) => current * region.Width);
294
295          if (sizeBox2 < sizeBox1) return -1;
296
297          return 1;
298        }
299
300        return result;
301      }
302    }
303
304    private class BoxMaximizer : IComparer<Tuple<double, List<Interval>>> {
305      public int Compare(Tuple<double, List<Interval>> x, Tuple<double, List<Interval>> y) {
306        var result = y.Item1.CompareTo(x.Item1);
307
308        if (result == 0) {
309          var sizeBox2 = y.Item2.Aggregate(1.0, (current, region) => current * region.Width);
310          var sizeBox1 = x.Item2.Aggregate(1.0, (current, region) => current * region.Width);
311
312          if (sizeBox2 > sizeBox1) return -1;
313
314          return 1;
315        }
316
317        return result;
318      }
319    }
320
321    public static Interval EvaluateRecursive(
322      Instruction[] instructions,
323      IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
324      IDictionary<string, Interval> variableIntervals, IList<string> variables,
325      double minWidth, int maxDepth, ref int currIndex, ref int currDepth,
326      ISymbolicExpressionTree tree) {
327      Interval evaluate() {
328        var ic = 0;
329        IReadOnlyDictionary<string, Interval> readonlyRanges =
330          new ReadOnlyDictionary<string, Interval>(variableIntervals);
331        return Evaluate(instructions, ref ic, nodeIntervals, readonlyRanges);
332      }
333
334      Interval recurse(ref int idx, ref int depth) {
335        return EvaluateRecursive(instructions, nodeIntervals, variableIntervals, variables, minWidth, maxDepth, ref idx,
336          ref depth, tree);
337      }
338
339
340      var v = variables[currIndex];
341      var x = variableIntervals[v];
342      if (x.Width < minWidth || currDepth == maxDepth || !MultipleTimes(tree, v)) {
343        if (currIndex + 1 < variables.Count) {
344          currDepth = 0;
345          currIndex++;
346          var z = recurse(ref currIndex, ref currDepth);
347          currIndex--;
348          return z;
349        }
350
351        return evaluate();
352      }
353
354      var t = x.Split();
355      var xa = t.Item1;
356      var xb = t.Item2;
357      var d = currDepth;
358      currDepth = d + 1;
359      variableIntervals[v] = xa;
360      var ya = recurse(ref currIndex, ref currDepth);
361      currDepth = d + 1;
362      variableIntervals[v] = xb;
363      var yb = recurse(ref currIndex, ref currDepth);
364      variableIntervals[v] = x; // restore interval
365      return ya | yb;
366    }
367
368    public static Interval Evaluate(
369      Instruction[] instructions, ref int instructionCounter,
370      IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals = null,
371      IReadOnlyDictionary<string, Interval> variableIntervals = null) {
372      var currentInstr = instructions[instructionCounter];
373      //Use ref parameter, because the tree will be iterated through recursively from the left-side branch to the right side
374      //Update instructionCounter, whenever Evaluate is called
375      instructionCounter++;
376      Interval result = null;
377
378      switch (currentInstr.opCode) {
379        //Variables, Constants, ...
380        case OpCodes.Variable: {
381          var variableTreeNode = (VariableTreeNode) currentInstr.dynamicNode;
382          var weightInterval = new Interval(variableTreeNode.Weight, variableTreeNode.Weight);
383          //var variableInterval = (Interval)currentInstr.data;
384
385          Interval variableInterval;
386          if (variableIntervals != null && variableIntervals.ContainsKey(variableTreeNode.VariableName))
387            variableInterval = variableIntervals[variableTreeNode.VariableName];
388          else
389            variableInterval = (Interval) currentInstr.data;
390
391          result = Interval.Multiply(variableInterval, weightInterval);
392          break;
393        }
394        case OpCodes.Constant: {
395          var constTreeNode = (ConstantTreeNode) currentInstr.dynamicNode;
396          result = new Interval(constTreeNode.Value, constTreeNode.Value);
397          break;
398        }
399        //Elementary arithmetic rules
400        case OpCodes.Add: {
401          //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
402          result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
403          for (var i = 1; i < currentInstr.nArguments; i++) {
404            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
405            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
406            result = Interval.Add(result, argumentInterval);
407          }
408
409          break;
410        }
411        case OpCodes.Sub: {
412          //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
413          result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
414          if (currentInstr.nArguments == 1)
415            result = Interval.Multiply(new Interval(-1, -1), result);
416
417          for (var i = 1; i < currentInstr.nArguments; i++) {
418            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
419            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
420            result = Interval.Subtract(result, argumentInterval);
421          }
422
423          break;
424        }
425        case OpCodes.Mul: {
426          //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
427          result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
428          for (var i = 1; i < currentInstr.nArguments; i++) {
429            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
430            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
431            result = Interval.Multiply(result, argumentInterval);
432          }
433
434          break;
435        }
436        case OpCodes.Div: {
437          //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
438          result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
439          if (currentInstr.nArguments == 1)
440            result = Interval.Divide(new Interval(1, 1), result);
441
442          for (var i = 1; i < currentInstr.nArguments; i++) {
443            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
444            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
445            result = Interval.Divide(result, argumentInterval);
446          }
447
448          break;
449        }
450        //Trigonometric functions
451        case OpCodes.Sin: {
452          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
453          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
454          result = Interval.Sine(argumentInterval);
455          break;
456        }
457        case OpCodes.Cos: {
458          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
459          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
460          result = Interval.Cosine(argumentInterval);
461          break;
462        }
463        case OpCodes.Tan: {
464          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
465          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
466          result = Interval.Tangens(argumentInterval);
467          break;
468        }
469        case OpCodes.Tanh: {
470          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
471          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
472          result = Interval.HyperbolicTangent(argumentInterval);
473          break;
474        }
475        //Exponential functions
476        case OpCodes.Log: {
477          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
478          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
479          result = Interval.Logarithm(argumentInterval);
480          break;
481        }
482        case OpCodes.Exp: {
483          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
484          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
485          result = Interval.Exponential(argumentInterval);
486          break;
487        }
488        case OpCodes.Square: {
489          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
490          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
491          result = Interval.Square(argumentInterval);
492          break;
493        }
494        case OpCodes.SquareRoot: {
495          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
496          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
497          result = Interval.SquareRoot(argumentInterval);
498          break;
499        }
500        case OpCodes.Cube: {
501          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
502          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
503          result = Interval.Cube(argumentInterval);
504          break;
505        }
506        case OpCodes.CubeRoot: {
507          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
508          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
509          result = Interval.CubicRoot(argumentInterval);
510          break;
511        }
512        case OpCodes.Absolute: {
513          //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
514          var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
515          result = Interval.Absolute(argumentInterval);
516          break;
517        }
518        case OpCodes.AnalyticQuotient: {
519          //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
520          result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
521          for (var i = 1; i < currentInstr.nArguments; i++) {
522            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
523            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
524            result = Interval.AnalyticalQuotient(result, argumentInterval);
525          }
526
527          break;
528        }
529        default:
530          throw new NotSupportedException($"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
531      }
532
533      if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
534        nodeIntervals.Add(currentInstr.dynamicNode, result);
535
536      return result;
537    }
538
539    private static bool MultipleTimes(ISymbolicExpressionTree tree, string variable) {
540      var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
541      var group = varlist.Select(x => x.Key == variable).Count();
542
543      return group > 1;
544    }
545
546    private static bool ContainsVariableMultipleTimes(ISymbolicExpressionTree tree) {
547      var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
548      return varlist.Any(group => group.Count() > 1);
549    }
550
551
552    public static bool IsCompatible(ISymbolicExpressionTree tree) {
553      var containsUnknownSymbols = (
554        from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
555        where
556          !(n.Symbol is Variable) &&
557          !(n.Symbol is Constant) &&
558          !(n.Symbol is StartSymbol) &&
559          !(n.Symbol is Addition) &&
560          !(n.Symbol is Subtraction) &&
561          !(n.Symbol is Multiplication) &&
562          !(n.Symbol is Division) &&
563          !(n.Symbol is Sine) &&
564          !(n.Symbol is Cosine) &&
565          !(n.Symbol is Tangent) &&
566          !(n.Symbol is HyperbolicTangent) &&
567          !(n.Symbol is Logarithm) &&
568          !(n.Symbol is Exponential) &&
569          !(n.Symbol is Square) &&
570          !(n.Symbol is SquareRoot) &&
571          !(n.Symbol is Cube) &&
572          !(n.Symbol is CubeRoot) &&
573          !(n.Symbol is Absolute) &&
574          !(n.Symbol is AnalyticQuotient)
575        select n).Any();
576      return !containsUnknownSymbols;
577    }
578  }
579}
Note: See TracBrowser for help on using the repository browser.