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

Last change on this file since 17757 was 17757, checked in by gkronber, 20 months ago

#3073 change sorting order for BoxBound

File size: 25.9 KB
Line 
1#region License Information
2
3/* HeuristicLab
4 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
5 *
6 * This file is part of HeuristicLab.
7 *
8 * HeuristicLab is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * HeuristicLab is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#endregion
23
24using System;
25using System.Collections.Generic;
26using System.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) {
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) {
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) {
127      lock (syncRoot) {
128        EvaluatedSolutions++;
129      }
130
131      Interval outputInterval;
132
133      if (UseIntervalSplitting) {
134        outputInterval = GetSymbolicExpressionTreeIntervals(tree, variableRanges,
135          out var _);
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) {
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);
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      var savedIntervals = variableIntervals.ToDictionary(entry => entry.Key, entry => entry.Value);
225      var min = FindBound(instructions, nodeIntervals, variableIntervals, minimization: true);
226      var max = FindBound(instructions, nodeIntervals, savedIntervals, minimization: false);
227
228      return new Interval(min, max);
229    }
230
231
232    private static double FindBound(Instruction[] instructions,
233                                    IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
234                                    IDictionary<string, Interval> variableIntervals, bool minimization = true) {
235      SortedSet<BoxBound> prioQ = new SortedSet<BoxBound>();
236
237      var ic = 0;
238      //Calculate full box
239      IReadOnlyDictionary<string, Interval> readonlyRanges = variableIntervals.ToDictionary(k => k.Key, k => k.Value);
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
243      var box = variableIntervals.Values;
244      if (minimization) {
245        prioQ.Add(new BoxBound(box, interval.LowerBound));
246      } else {
247        prioQ.Add(new BoxBound(box, -interval.UpperBound));
248      }
249
250      // TODO a fixed limit for depth?!
251      for (var depth = 0; depth < 200; ++depth) {
252        var currentBound = prioQ.Min;
253        prioQ.Remove(currentBound);
254
255        var newBoxes = Split(currentBound.box);
256
257        foreach (var newBox in newBoxes) {
258          var intervalEnum = newBox.GetEnumerator();
259          var keyEnum = readonlyRanges.Keys.GetEnumerator();
260          while (intervalEnum.MoveNext() & keyEnum.MoveNext()) {
261            variableIntervals[keyEnum.Current] = intervalEnum.Current;
262          }
263
264          ic = 0;
265          var res = Evaluate(instructions, ref ic, nodeIntervals,
266            new ReadOnlyDictionary<string, Interval>(variableIntervals));
267          if (minimization) {
268            prioQ.Add(new BoxBound(newBox, interval.LowerBound));
269          } else {
270            prioQ.Add(new BoxBound(newBox, -interval.UpperBound));
271          }
272        }
273      }
274
275      return minimization ?
276        prioQ.First().bound :
277        -prioQ.First().bound;
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    // a multi-dimensional box with an associated bound
288    // boxbounds are ordered first by bound (smaller first), then by size of box (larger first) then by distance of bottom left corner to origin
289    private class BoxBound : IComparable<BoxBound> {
290      public List<Interval> box;
291      public double bound;
292      public BoxBound(IEnumerable<Interval> box, double bound) {
293        this.box = new List<Interval>(box);
294        this.bound = bound;
295      }
296      public int CompareTo(BoxBound other) {
297        if (bound != other.bound) return bound.CompareTo(other.bound);
298
299        var thisSize = box.Aggregate(1.0, (current, dimExtent) => current * dimExtent.Width);
300        var otherSize = other.box.Aggregate(1.0, (current, dimExtent) => current * dimExtent.Width);
301        if (thisSize != otherSize) return -thisSize.CompareTo(otherSize);
302
303        var thisDist = box.Sum(dimExtent => dimExtent.LowerBound * dimExtent.LowerBound);
304        var otherDist = other.box.Sum(dimExtent => dimExtent.LowerBound * dimExtent.LowerBound);
305        if (thisDist != otherDist) return thisDist.CompareTo(otherDist);
306
307        // which is smaller first along the dimensions?
308        for (int i = 0; i < box.Count; i++) {
309          if (box[i].LowerBound != other.box[i].LowerBound) return box[i].LowerBound.CompareTo(other.box[i].LowerBound);
310        }
311
312        return 0;
313      }
314
315      public static Interval EvaluateRecursive(
316        Instruction[] instructions,
317        IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
318        IDictionary<string, Interval> variableIntervals, IList<string> variables,
319        double minWidth, int maxDepth, ref int currIndex, ref int currDepth,
320        ISymbolicExpressionTree tree) {
321        Interval evaluate() {
322          var ic = 0;
323          IReadOnlyDictionary<string, Interval> readonlyRanges =
324            new ReadOnlyDictionary<string, Interval>(variableIntervals);
325          return Evaluate(instructions, ref ic, nodeIntervals, readonlyRanges);
326        }
327
328        Interval recurse(ref int idx, ref int depth) {
329          return EvaluateRecursive(instructions, nodeIntervals, variableIntervals, variables, minWidth, maxDepth, ref idx,
330            ref depth, tree);
331        }
332
333
334        var v = variables[currIndex];
335        var x = variableIntervals[v];
336        if (x.Width < minWidth || currDepth == maxDepth || !MultipleTimes(tree, v)) {
337          if (currIndex + 1 < variables.Count) {
338            currDepth = 0;
339            currIndex++;
340            var z = recurse(ref currIndex, ref currDepth);
341            currIndex--;
342            return z;
343          }
344
345          return evaluate();
346        }
347
348        var t = x.Split();
349        var xa = t.Item1;
350        var xb = t.Item2;
351        var d = currDepth;
352        currDepth = d + 1;
353        variableIntervals[v] = xa;
354        var ya = recurse(ref currIndex, ref currDepth);
355        currDepth = d + 1;
356        variableIntervals[v] = xb;
357        var yb = recurse(ref currIndex, ref currDepth);
358        variableIntervals[v] = x; // restore interval
359        return ya | yb;
360      }
361
362      public static Interval Evaluate(
363        Instruction[] instructions, ref int instructionCounter,
364        IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals = null,
365        IReadOnlyDictionary<string, Interval> variableIntervals = null) {
366        var currentInstr = instructions[instructionCounter];
367        //Use ref parameter, because the tree will be iterated through recursively from the left-side branch to the right side
368        //Update instructionCounter, whenever Evaluate is called
369        instructionCounter++;
370        Interval result = null;
371
372        switch (currentInstr.opCode) {
373          //Variables, Constants, ...
374          case OpCodes.Variable: {
375              var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
376              var weightInterval = new Interval(variableTreeNode.Weight, variableTreeNode.Weight);
377              //var variableInterval = (Interval)currentInstr.data;
378
379              Interval variableInterval;
380              if (variableIntervals != null && variableIntervals.ContainsKey(variableTreeNode.VariableName))
381                variableInterval = variableIntervals[variableTreeNode.VariableName];
382              else
383                variableInterval = (Interval)currentInstr.data;
384
385              result = Interval.Multiply(variableInterval, weightInterval);
386              break;
387            }
388          case OpCodes.Constant: {
389              var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
390              result = new Interval(constTreeNode.Value, constTreeNode.Value);
391              break;
392            }
393          //Elementary arithmetic rules
394          case OpCodes.Add: {
395              //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
396              result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
397              for (var i = 1; i < currentInstr.nArguments; i++) {
398                //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
399                var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
400                result = Interval.Add(result, argumentInterval);
401              }
402
403              break;
404            }
405          case OpCodes.Sub: {
406              //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
407              result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
408              if (currentInstr.nArguments == 1)
409                result = Interval.Multiply(new Interval(-1, -1), result);
410
411              for (var i = 1; i < currentInstr.nArguments; i++) {
412                //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
413                var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
414                result = Interval.Subtract(result, argumentInterval);
415              }
416
417              break;
418            }
419          case OpCodes.Mul: {
420              //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
421              result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
422              for (var i = 1; i < currentInstr.nArguments; i++) {
423                var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
424                //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
425                result = Interval.Multiply(result, argumentInterval);
426              }
427
428              break;
429            }
430          case OpCodes.Div: {
431              //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
432              result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
433              if (currentInstr.nArguments == 1)
434                result = Interval.Divide(new Interval(1, 1), result);
435
436              for (var i = 1; i < currentInstr.nArguments; i++) {
437                //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
438                var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
439                result = Interval.Divide(result, argumentInterval);
440              }
441
442              break;
443            }
444          //Trigonometric functions
445          case OpCodes.Sin: {
446              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
447              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
448              result = Interval.Sine(argumentInterval);
449              break;
450            }
451          case OpCodes.Cos: {
452              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
453              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
454              result = Interval.Cosine(argumentInterval);
455              break;
456            }
457          case OpCodes.Tan: {
458              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
459              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
460              result = Interval.Tangens(argumentInterval);
461              break;
462            }
463          case OpCodes.Tanh: {
464              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
465              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
466              result = Interval.HyperbolicTangent(argumentInterval);
467              break;
468            }
469          //Exponential functions
470          case OpCodes.Log: {
471              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
472              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
473              result = Interval.Logarithm(argumentInterval);
474              break;
475            }
476          case OpCodes.Exp: {
477              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
478              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
479              result = Interval.Exponential(argumentInterval);
480              break;
481            }
482          case OpCodes.Square: {
483              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
484              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
485              result = Interval.Square(argumentInterval);
486              break;
487            }
488          case OpCodes.SquareRoot: {
489              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
490              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
491              result = Interval.SquareRoot(argumentInterval);
492              break;
493            }
494          case OpCodes.Cube: {
495              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
496              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
497              result = Interval.Cube(argumentInterval);
498              break;
499            }
500          case OpCodes.CubeRoot: {
501              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
502              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
503              result = Interval.CubicRoot(argumentInterval);
504              break;
505            }
506          case OpCodes.Absolute: {
507              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
508              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
509              result = Interval.Absolute(argumentInterval);
510              break;
511            }
512          case OpCodes.AnalyticQuotient: {
513              //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
514              result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
515              for (var i = 1; i < currentInstr.nArguments; i++) {
516                //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
517                var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
518                result = Interval.AnalyticalQuotient(result, argumentInterval);
519              }
520
521              break;
522            }
523          default:
524            throw new NotSupportedException($"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
525        }
526
527        if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
528          nodeIntervals.Add(currentInstr.dynamicNode, result);
529
530        return result;
531      }
532
533      private static bool MultipleTimes(ISymbolicExpressionTree tree, string variable) {
534        var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
535        var group = varlist.Select(x => x.Key == variable).Count();
536
537        return group > 1;
538      }
539
540      private static bool ContainsVariableMultipleTimes(ISymbolicExpressionTree tree) {
541        var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
542        return varlist.Any(group => group.Count() > 1);
543      }
544
545
546      public static bool IsCompatible(ISymbolicExpressionTree tree) {
547        var containsUnknownSymbols = (
548          from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
549          where
550            !(n.Symbol is Variable) &&
551            !(n.Symbol is Constant) &&
552            !(n.Symbol is StartSymbol) &&
553            !(n.Symbol is Addition) &&
554            !(n.Symbol is Subtraction) &&
555            !(n.Symbol is Multiplication) &&
556            !(n.Symbol is Division) &&
557            !(n.Symbol is Sine) &&
558            !(n.Symbol is Cosine) &&
559            !(n.Symbol is Tangent) &&
560            !(n.Symbol is HyperbolicTangent) &&
561            !(n.Symbol is Logarithm) &&
562            !(n.Symbol is Exponential) &&
563            !(n.Symbol is Square) &&
564            !(n.Symbol is SquareRoot) &&
565            !(n.Symbol is Cube) &&
566            !(n.Symbol is CubeRoot) &&
567            !(n.Symbol is Absolute) &&
568            !(n.Symbol is AnalyticQuotient)
569          select n).Any();
570        return !containsUnknownSymbols;
571      }
572    }
573  }
Note: See TracBrowser for help on using the repository browser.