source: branches/2994-AutoDiffForIntervals/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis-3.4/IntervalCalculationComparison.cs @ 17303

Last change on this file since 17303 was 17303, checked in by gkronber, 3 years ago

#2994 continued refactoring and extended unit tests. Interval calculation still fails for some edge cases (mainly for undefined behaviour). VectorEvaluator and VectorAutoDiffEvaluator produce the same results as the LinearInterpreter. TODO: check gradient calculation

File size: 6.3 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
6using HeuristicLab.Problems.DataAnalysis;
7using HeuristicLab.Problems.DataAnalysis.Symbolic;
8using HeuristicLab.Random;
9using Microsoft.VisualStudio.TestTools.UnitTesting;
10
11namespace HeuristicLab.Tests {
12  [TestClass]
13  public class IntervalCalculationComparison {
14    [TestMethod]
15    [TestCategory("Problems.DataAnalysis")]
16    [TestProperty("Time", "long")]
17    public void TestIntervalCalculationForRandomExpressions() {
18      var grammar = new TypeCoherentExpressionGrammar();
19      grammar.ConfigureAsDefaultRegressionGrammar();
20      // activate supported symbols
21      grammar.Symbols.First(s => s is Square).Enabled = true;
22      grammar.Symbols.First(s => s is SquareRoot).Enabled = true;
23      grammar.Symbols.First(s => s is Cube).Enabled = true;
24      grammar.Symbols.First(s => s is CubeRoot).Enabled = true;
25      grammar.Symbols.First(s => s is Sine).Enabled = true;
26      grammar.Symbols.First(s => s is Cosine).Enabled = true;
27      grammar.Symbols.First(s => s is Exponential).Enabled = true;
28      grammar.Symbols.First(s => s is Logarithm).Enabled = true;
29      grammar.Symbols.First(s => s is Absolute).Enabled = false; // XXX not yet supported by old interval calculator
30      grammar.Symbols.First(s => s is AnalyticQuotient).Enabled = false; // not yet supported by old interval calculator
31
32      var varSy = (Variable)grammar.Symbols.First(s => s is Variable);
33      varSy.AllVariableNames = new string[] { "x", "y" };
34      varSy.VariableNames = varSy.AllVariableNames;
35      varSy.WeightMu = 1.0;
36      varSy.WeightSigma = 1.0;
37      var rand = new FastRandom(1234);
38      var eval1 = new IntervalEvaluator();
39      var eval2 = new IntervalInterpreter();
40
41      IDictionary<string, Interval> posIntervals = new Dictionary<string, Interval>() {
42        { "x", new Interval(1, 2) },
43        { "y", new Interval(0, 1) }
44      };
45      IDictionary<string, Interval> negIntervals = new Dictionary<string, Interval>() {
46        { "x", new Interval(-2, -1) },
47        { "y", new Interval(-1, 0) }
48      };
49      IDictionary<string, Interval> fullIntervals = new Dictionary<string, Interval>() {
50        { "x", new Interval(-2, 2) },
51        { "y", new Interval(-1, 1) }
52      };
53      IDictionary<string, Interval> specialIntervals = new Dictionary<string, Interval>() {
54        { "x", new Interval(1, double.PositiveInfinity) },
55        { "y", new Interval(double.NegativeInfinity, double.PositiveInfinity) }
56      };
57
58      var formatter = new InfixExpressionFormatter();
59      var sb = new StringBuilder();
60      foreach (var interval in new[] { posIntervals, negIntervals, fullIntervals, specialIntervals }) {
61        int N = 10000;
62        int i = 0;
63        while (i < N) {
64          var t = ProbabilisticTreeCreator.Create(rand, grammar, maxTreeLength: 5, maxTreeDepth: 5);
65          var r1 = eval1.Evaluate(t, interval);
66          var r2 = eval2.GetSymbolicExpressionTreeInterval(t, interval);
67          // Console.WriteLine(formatter.Format(t));
68
69          // all NaN is ok (but don't count NaN expressions)
70          if (double.IsNaN(r1.LowerBound) && double.IsNaN(r2.LowerBound) && double.IsNaN(r1.UpperBound) && double.IsNaN(r2.UpperBound)) continue;
71          if (r1.LowerBound == r2.LowerBound && r1.UpperBound == r2.UpperBound) {
72            /* exactly the same value (incl. Inf / -Inf) => ok */
73          } else if ((Math.Abs(r1.LowerBound - r2.LowerBound) < Math.Max(1e-10, Math.Abs(r1.LowerBound * 1e-4))) &&
74                     (Math.Abs(r1.UpperBound - r2.UpperBound) < Math.Max(1e-10, Math.Abs(r1.UpperBound * 1e-4)))) {
75            /* approximately the same value => OK */
76          } else {
77            sb.AppendLine($"{r1} <> {r2} for {formatter.Format(t)} x={interval["x"]} y={interval["y"]}");
78          }
79          i++;
80        }
81      }
82      if (sb.Length > 0) {
83        Console.WriteLine(sb.ToString());
84        Assert.Fail("There were different interval calculation results");
85      }
86    }
87
88    [TestMethod]
89    [TestCategory("Problems.DataAnalysis")]
90    [TestProperty("Time", "short")]
91    public void TestExampleIntervalExpressions() {
92      var parser = new InfixExpressionParser();
93      var eval1 = new IntervalEvaluator();
94      var eval2 = new IntervalInterpreter();
95      IDictionary<string, Interval> interval = new Dictionary<string, Interval>() {
96        { "x", new Interval(1, 2) },
97        { "y", new Interval(0, 1) },
98        { "z", new Interval(double.NegativeInfinity, double.PositiveInfinity) },
99      };
100
101      var exprs = new string[] {
102        "CUBE((0.642971622547268*'x')) * (-16.5400720573962)",
103        "sqr(y / y)", // one interpreter produces [NaN, inf], the other [NaN, 0]
104        "cuberoot(-x)", // the old interpreter calculates cuberoot incorrectly
105        "sqr(log(-x))", // Interval: [NaN, NaN] <> Interval (old): [NaN, 0]
106        "log(1.8*'y' - 1.4*'y')", // Interval: [NaN, 0,587786664902119] <> Interval (old): [0,587786664902119, NaN]
107        "log(z)", // Interval: [NaN, ∞] <> Interval (old): [∞, NaN]
108        "sqr(sqrt(-1))" // Interval: [NaN, NaN] <> Interval (old): [NaN, 0]
109      };
110
111      var formatter = new InfixExpressionFormatter();
112      var sb = new StringBuilder();
113      foreach (var expr in exprs) {
114        var t = parser.Parse(expr);
115
116        var r1 = eval1.Evaluate(t, interval);
117        var r2 = eval2.GetSymbolicExpressionTreeInterval(t, interval);
118        // Console.WriteLine(formatter.Format(t));
119
120        // all NaN is ok
121        if (double.IsNaN(r1.LowerBound) && double.IsNaN(r2.LowerBound) && double.IsNaN(r1.UpperBound) && double.IsNaN(r2.UpperBound)) continue;
122        if (r1.LowerBound == r2.LowerBound && r1.UpperBound == r2.UpperBound) continue;  // Inf, -Inf and exactly the same value are ok
123
124        if ((Math.Abs(r1.LowerBound - r2.LowerBound) < Math.Abs(r1.LowerBound * 1e-4)) &&
125            (Math.Abs(r1.UpperBound - r2.UpperBound) < Math.Abs(r1.UpperBound * 1e-4))) { /* OK */ } else {
126          sb.AppendLine($"{r1} <> {r2} for {formatter.Format(t)} x={interval["x"]} y={interval["y"]}");
127        }
128      }
129      if (sb.Length > 0) {
130        Console.WriteLine(sb.ToString());
131        Assert.Fail("There were different interval calculation results");
132      }
133    }
134  }
135}
Note: See TracBrowser for help on using the repository browser.