Changeset 17758


Ignore:
Timestamp:
09/24/20 11:16:05 (16 months ago)
Author:
gkronber
Message:

#3073: corrected bug in previous commit

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IntervalInterpreter.cs

    r17757 r17758  
    312312        return 0;
    313313      }
    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);
     314    }
     315
     316    public static Interval EvaluateRecursive(
     317      Instruction[] instructions,
     318      IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals,
     319      IDictionary<string, Interval> variableIntervals, IList<string> variables,
     320      double minWidth, int maxDepth, ref int currIndex, ref int currDepth,
     321      ISymbolicExpressionTree tree) {
     322      Interval evaluate() {
     323        var ic = 0;
     324        IReadOnlyDictionary<string, Interval> readonlyRanges =
     325          new ReadOnlyDictionary<string, Interval>(variableIntervals);
     326        return Evaluate(instructions, ref ic, nodeIntervals, readonlyRanges);
     327      }
     328
     329      Interval recurse(ref int idx, ref int depth) {
     330        return EvaluateRecursive(instructions, nodeIntervals, variableIntervals, variables, minWidth, maxDepth, ref idx,
     331          ref depth, tree);
     332      }
     333
     334
     335      var v = variables[currIndex];
     336      var x = variableIntervals[v];
     337      if (x.Width < minWidth || currDepth == maxDepth || !MultipleTimes(tree, v)) {
     338        if (currIndex + 1 < variables.Count) {
     339          currDepth = 0;
     340          currIndex++;
     341          var z = recurse(ref currIndex, ref currDepth);
     342          currIndex--;
     343          return z;
    326344        }
    327345
    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: {
     346        return evaluate();
     347      }
     348
     349      var t = x.Split();
     350      var xa = t.Item1;
     351      var xb = t.Item2;
     352      var d = currDepth;
     353      currDepth = d + 1;
     354      variableIntervals[v] = xa;
     355      var ya = recurse(ref currIndex, ref currDepth);
     356      currDepth = d + 1;
     357      variableIntervals[v] = xb;
     358      var yb = recurse(ref currIndex, ref currDepth);
     359      variableIntervals[v] = x; // restore interval
     360      return ya | yb;
     361    }
     362
     363    public static Interval Evaluate(
     364      Instruction[] instructions, ref int instructionCounter,
     365      IDictionary<ISymbolicExpressionTreeNode, Interval> nodeIntervals = null,
     366      IReadOnlyDictionary<string, Interval> variableIntervals = null) {
     367      var currentInstr = instructions[instructionCounter];
     368      //Use ref parameter, because the tree will be iterated through recursively from the left-side branch to the right side
     369      //Update instructionCounter, whenever Evaluate is called
     370      instructionCounter++;
     371      Interval result = null;
     372
     373      switch (currentInstr.opCode) {
     374        //Variables, Constants, ...
     375        case OpCodes.Variable: {
     376            var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
     377            var weightInterval = new Interval(variableTreeNode.Weight, variableTreeNode.Weight);
     378            //var variableInterval = (Interval)currentInstr.data;
     379
     380            Interval variableInterval;
     381            if (variableIntervals != null && variableIntervals.ContainsKey(variableTreeNode.VariableName))
     382              variableInterval = variableIntervals[variableTreeNode.VariableName];
     383            else
     384              variableInterval = (Interval)currentInstr.data;
     385
     386            result = Interval.Multiply(variableInterval, weightInterval);
     387            break;
     388          }
     389        case OpCodes.Constant: {
     390            var constTreeNode = (ConstantTreeNode)currentInstr.dynamicNode;
     391            result = new Interval(constTreeNode.Value, constTreeNode.Value);
     392            break;
     393          }
     394        //Elementary arithmetic rules
     395        case OpCodes.Add: {
     396            //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     397            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     398            for (var i = 1; i < currentInstr.nArguments; i++) {
    446399              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
    447400              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
    448               result = Interval.Sine(argumentInterval);
    449               break;
     401              result = Interval.Add(result, argumentInterval);
    450402            }
    451           case OpCodes.Cos: {
     403
     404            break;
     405          }
     406        case OpCodes.Sub: {
     407            //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     408            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     409            if (currentInstr.nArguments == 1)
     410              result = Interval.Multiply(new Interval(-1, -1), result);
     411
     412            for (var i = 1; i < currentInstr.nArguments; i++) {
    452413              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
    453414              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
    454               result = Interval.Cosine(argumentInterval);
    455               break;
     415              result = Interval.Subtract(result, argumentInterval);
    456416            }
    457           case OpCodes.Tan: {
     417
     418            break;
     419          }
     420        case OpCodes.Mul: {
     421            //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     422            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     423            for (var i = 1; i < currentInstr.nArguments; i++) {
     424              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     425              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     426              result = Interval.Multiply(result, argumentInterval);
     427            }
     428
     429            break;
     430          }
     431        case OpCodes.Div: {
     432            //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     433            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     434            if (currentInstr.nArguments == 1)
     435              result = Interval.Divide(new Interval(1, 1), result);
     436
     437            for (var i = 1; i < currentInstr.nArguments; i++) {
    458438              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
    459439              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
    460               result = Interval.Tangens(argumentInterval);
    461               break;
     440              result = Interval.Divide(result, argumentInterval);
    462441            }
    463           case OpCodes.Tanh: {
     442
     443            break;
     444          }
     445        //Trigonometric functions
     446        case OpCodes.Sin: {
     447            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     448            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     449            result = Interval.Sine(argumentInterval);
     450            break;
     451          }
     452        case OpCodes.Cos: {
     453            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     454            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     455            result = Interval.Cosine(argumentInterval);
     456            break;
     457          }
     458        case OpCodes.Tan: {
     459            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     460            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     461            result = Interval.Tangens(argumentInterval);
     462            break;
     463          }
     464        case OpCodes.Tanh: {
     465            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     466            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     467            result = Interval.HyperbolicTangent(argumentInterval);
     468            break;
     469          }
     470        //Exponential functions
     471        case OpCodes.Log: {
     472            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     473            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     474            result = Interval.Logarithm(argumentInterval);
     475            break;
     476          }
     477        case OpCodes.Exp: {
     478            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     479            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     480            result = Interval.Exponential(argumentInterval);
     481            break;
     482          }
     483        case OpCodes.Square: {
     484            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     485            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     486            result = Interval.Square(argumentInterval);
     487            break;
     488          }
     489        case OpCodes.SquareRoot: {
     490            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     491            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     492            result = Interval.SquareRoot(argumentInterval);
     493            break;
     494          }
     495        case OpCodes.Cube: {
     496            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     497            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     498            result = Interval.Cube(argumentInterval);
     499            break;
     500          }
     501        case OpCodes.CubeRoot: {
     502            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     503            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     504            result = Interval.CubicRoot(argumentInterval);
     505            break;
     506          }
     507        case OpCodes.Absolute: {
     508            //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     509            var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     510            result = Interval.Absolute(argumentInterval);
     511            break;
     512          }
     513        case OpCodes.AnalyticQuotient: {
     514            //result = Evaluate(instructions, ref instructionCounter, nodeIntervals);
     515            result = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
     516            for (var i = 1; i < currentInstr.nArguments; i++) {
    464517              //var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals);
    465518              var argumentInterval = Evaluate(instructions, ref instructionCounter, nodeIntervals, variableIntervals);
    466               result = Interval.HyperbolicTangent(argumentInterval);
    467               break;
     519              result = Interval.AnalyticalQuotient(result, argumentInterval);
    468520            }
    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       }
     521
     522            break;
     523          }
     524        default:
     525          throw new NotSupportedException($"The tree contains the unknown symbol {currentInstr.dynamicNode.Symbol}");
     526      }
     527
     528      if (!(nodeIntervals == null || nodeIntervals.ContainsKey(currentInstr.dynamicNode)))
     529        nodeIntervals.Add(currentInstr.dynamicNode, result);
     530
     531      return result;
     532    }
     533
     534    private static bool MultipleTimes(ISymbolicExpressionTree tree, string variable) {
     535      var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
     536      var group = varlist.Select(x => x.Key == variable).Count();
     537
     538      return group > 1;
     539    }
     540
     541    private static bool ContainsVariableMultipleTimes(ISymbolicExpressionTree tree) {
     542      var varlist = tree.IterateNodesPrefix().OfType<VariableTreeNode>().GroupBy(x => x.VariableName);
     543      return varlist.Any(group => group.Count() > 1);
     544    }
     545
     546
     547    public static bool IsCompatible(ISymbolicExpressionTree tree) {
     548      var containsUnknownSymbols = (
     549        from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
     550        where
     551          !(n.Symbol is Variable) &&
     552          !(n.Symbol is Constant) &&
     553          !(n.Symbol is StartSymbol) &&
     554          !(n.Symbol is Addition) &&
     555          !(n.Symbol is Subtraction) &&
     556          !(n.Symbol is Multiplication) &&
     557          !(n.Symbol is Division) &&
     558          !(n.Symbol is Sine) &&
     559          !(n.Symbol is Cosine) &&
     560          !(n.Symbol is Tangent) &&
     561          !(n.Symbol is HyperbolicTangent) &&
     562          !(n.Symbol is Logarithm) &&
     563          !(n.Symbol is Exponential) &&
     564          !(n.Symbol is Square) &&
     565          !(n.Symbol is SquareRoot) &&
     566          !(n.Symbol is Cube) &&
     567          !(n.Symbol is CubeRoot) &&
     568          !(n.Symbol is Absolute) &&
     569          !(n.Symbol is AnalyticQuotient)
     570        select n).Any();
     571      return !containsUnknownSymbols;
    572572    }
    573573  }
     574}
Note: See TracChangeset for help on using the changeset viewer.