source: branches/2994-AutoDiffForIntervals/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/SymbolicDataAnalysisExpressionTreeInterpreterTest.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: 33.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Globalization;
25using System.Linq;
26using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
27using HeuristicLab.Random;
28using Microsoft.VisualStudio.TestTools.UnitTesting;
29namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Tests {
30
31
32  [TestClass]
33  public class SymbolicDataAnalysisExpressionTreeInterpreterTest {
34    private const int N = 1000;
35    private const int Rows = 1000;
36    private const int Columns = 50;
37
38    private static Dataset ds = new Dataset(new string[] { "Y", "A", "B" }, new double[,] {
39        { 1.0, 1.0, 1.0 },
40        { 2.0, 2.0, 2.0 },
41        { 3.0, 1.0, 2.0 },
42        { 4.0, 1.0, 1.0 },
43        { 5.0, 2.0, 2.0 },
44        { 6.0, 1.0, 2.0 },
45        { 7.0, 1.0, 1.0 },
46        { 8.0, 2.0, 2.0 },
47        { 9.0, 1.0, 2.0 },
48        { 10.0, 1.0, 1.0 },
49        { 11.0, 2.0, 2.0 },
50        { 12.0, 1.0, 2.0 }
51      });
52
53    [TestMethod]
54    [TestCategory("Problems.DataAnalysis.Symbolic")]
55    [TestProperty("Time", "long")]
56    public void StandardInterpreterTestTypeCoherentGrammarPerformance() {
57      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeInterpreter(), 12.5e6);
58    }
59    [TestMethod]
60    [TestCategory("Problems.DataAnalysis.Symbolic")]
61    [TestProperty("Time", "long")]
62    public void StandardInterpreterTestFullGrammarPerformance() {
63      TestFullGrammarPerformance(new SymbolicDataAnalysisExpressionTreeInterpreter(), 12.5e6);
64    }
65    [TestMethod]
66    [TestCategory("Problems.DataAnalysis.Symbolic")]
67    [TestProperty("Time", "long")]
68    public void StandardInterpreterTestArithmeticGrammarPerformance() {
69      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeInterpreter(), 12.5e6);
70    }
71
72    [TestMethod]
73    [TestCategory("Problems.DataAnalysis.Symbolic")]
74    [TestProperty("Time", "long")]
75    public void CompiledInterpreterTestTypeCoherentGrammarPerformance() {
76      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionCompiledTreeInterpreter(), 12.5e6);
77    }
78    [TestMethod]
79    [TestCategory("Problems.DataAnalysis.Symbolic")]
80    [TestProperty("Time", "long")]
81    public void CompiledInterpreterTestFullGrammarPerformance() {
82      TestFullGrammarPerformance(new SymbolicDataAnalysisExpressionCompiledTreeInterpreter(), 12.5e6);
83    }
84    [TestMethod]
85    [TestCategory("Problems.DataAnalysis.Symbolic")]
86    [TestProperty("Time", "long")]
87    public void CompiledInterpreterTestArithmeticGrammarPerformance() {
88      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionCompiledTreeInterpreter(), 12.5e6);
89    }
90
91    [TestMethod]
92    [TestCategory("Problems.DataAnalysis.Symbolic")]
93    [TestProperty("Time", "long")]
94    public void ILEmittingInterpreterTestTypeCoherentGrammarPerformance() {
95      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter(), 7.5e6);
96    }
97    [TestMethod]
98    [TestCategory("Problems.DataAnalysis.Symbolic")]
99    [TestProperty("Time", "long")]
100    public void ILEmittingInterpreterTestArithmeticGrammarPerformance() {
101      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter(), 7.5e6);
102    }
103
104    [TestMethod]
105    [TestCategory("Problems.DataAnalysis.Symbolic")]
106    [TestProperty("Time", "long")]
107    public void LinearInterpreterTestTypeCoherentGrammarPerformance() {
108      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
109    }
110    [TestMethod]
111    [TestCategory("Problems.DataAnalysis.Symbolic")]
112    [TestProperty("Time", "long")]
113    public void LinearInterpreterTestFullGrammarPerformance() {
114      TestFullGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
115    }
116    [TestMethod]
117    [TestCategory("Problems.DataAnalysis.Symbolic")]
118    [TestProperty("Time", "long")]
119    public void LinearInterpreterTestArithmeticGrammarPerformance() {
120      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
121    }
122
123    [TestMethod]
124    [TestCategory("Problems.DataAnalysis.Symbolic")]
125    [TestProperty("Time", "long")]
126    public void BatchInterpreterTestTypeCoherentGrammarPerformance() {
127      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeBatchInterpreter(), 12.5e6);
128    }
129    [TestMethod]
130    [TestCategory("Problems.DataAnalysis.Symbolic")]
131    [TestProperty("Time", "long")]
132    public void BatchInterpreterTestArithmeticGrammarPerformance() {
133      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeBatchInterpreter(), 12.5e6);
134    }
135
136    private void TestTypeCoherentGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
137      var twister = new MersenneTwister(31415);
138      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
139
140      var grammar = new TypeCoherentExpressionGrammar();
141      grammar.ConfigureAsDefaultRegressionGrammar();
142
143      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
144      foreach (ISymbolicExpressionTree tree in randomTrees) {
145        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
146      }
147      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
148      //mkommend: commented due to performance issues on the builder
149      // Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
150    }
151
152    private void TestFullGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
153      var twister = new MersenneTwister(31415);
154      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
155
156      var grammar = new FullFunctionalExpressionGrammar();
157      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
158      foreach (ISymbolicExpressionTree tree in randomTrees) {
159        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
160      }
161      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
162      //mkommend: commented due to performance issues on the builder
163      //Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
164    }
165
166    private void TestArithmeticGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
167      var twister = new MersenneTwister(31415);
168      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
169
170      var grammar = new ArithmeticExpressionGrammar();
171      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
172      foreach (SymbolicExpressionTree tree in randomTrees) {
173        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
174      }
175
176      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
177      //mkommend: commented due to performance issues on the builder
178      //Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
179    }
180
181
182    /// <summary>
183    ///A test for Evaluate
184    ///</summary>
185    [TestMethod]
186    [TestCategory("Problems.DataAnalysis.Symbolic")]
187    [TestProperty("Time", "short")]
188    public void StandardInterpreterTestEvaluation() {
189      var interpreter = new SymbolicDataAnalysisExpressionTreeInterpreter();
190      EvaluateTerminals(interpreter, ds);
191      EvaluateOperations(interpreter, ds);
192      EvaluateLaggedOperations(interpreter, ds);
193      EvaluateSpecialFunctions(interpreter, ds);
194      EvaluateAdf(interpreter, ds);
195    }
196
197    /// <summary>
198    ///A test for Evaluate
199    ///</summary>
200    [TestMethod]
201    [TestCategory("Problems.DataAnalysis.Symbolic")]
202    [TestProperty("Time", "short")]
203    public void ILEmittingInterpreterTestEvaluation() {
204      var interpreter = new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter();
205      EvaluateTerminals(interpreter, ds);
206      EvaluateOperations(interpreter, ds);
207      EvaluateLaggedOperations(interpreter, ds);
208      EvaluateSpecialFunctions(interpreter, ds);
209    }
210
211    [TestMethod]
212    [TestCategory("Problems.DataAnalysis.Symbolic")]
213    [TestProperty("Time", "short")]
214    public void CompiledInterpreterTestEvaluation() {
215      var interpreter = new SymbolicDataAnalysisExpressionCompiledTreeInterpreter();
216      EvaluateTerminals(interpreter, ds);
217      EvaluateOperations(interpreter, ds);
218      EvaluateSpecialFunctions(interpreter, ds);
219    }
220
221    [TestMethod]
222    [TestCategory("Problems.DataAnalysis.Symbolic")]
223    [TestProperty("Time", "short")]
224    public void LinearInterpreterTestEvaluation() {
225      var interpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
226      //ADFs are not supported by the linear interpreter
227      EvaluateTerminals(interpreter, ds);
228      EvaluateOperations(interpreter, ds);
229      EvaluateLaggedOperations(interpreter, ds);
230      EvaluateSpecialFunctions(interpreter, ds);
231    }
232
233    [TestMethod]
234    [TestCategory("Problems.DataAnalysis.Symbolic")]
235    [TestProperty("Time", "long")]
236    public void TestInterpretersEstimatedValuesConsistency() {
237      var twister = new MersenneTwister();
238      int seed = twister.Next(0, int.MaxValue);
239      twister.Seed((uint)seed);
240      const int numRows = 100;
241      var dataset = Util.CreateRandomDataset(twister, numRows, Columns);
242
243      var grammar = new TypeCoherentExpressionGrammar();
244
245      var interpreters = new ISymbolicDataAnalysisExpressionTreeInterpreter[] {
246        new SymbolicDataAnalysisExpressionTreeLinearInterpreter(),
247        new SymbolicDataAnalysisExpressionTreeInterpreter(),
248      };
249
250      var rows = Enumerable.Range(0, numRows).ToList();
251      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 10, 0, 0);
252      foreach (ISymbolicExpressionTree tree in randomTrees) {
253        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
254      }
255
256      for (int i = 0; i < randomTrees.Length; ++i) {
257        var tree = randomTrees[i];
258        var valuesMatrix = interpreters.Select(x => x.GetSymbolicExpressionTreeValues(tree, dataset, rows)).ToList();
259        for (int m = 0; m < interpreters.Length - 1; ++m) {
260          var sum = valuesMatrix[m].Sum();
261          for (int n = m + 1; n < interpreters.Length; ++n) {
262            var s = valuesMatrix[n].Sum();
263            if (double.IsNaN(sum) && double.IsNaN(s)) continue;
264
265            string errorMessage = string.Format("Interpreters {0} and {1} do not agree on tree {2} (seed = {3}).", interpreters[m].Name, interpreters[n].Name, i, seed);
266            Assert.AreEqual(sum, s, 1e-12, errorMessage);
267          }
268        }
269      }
270    }
271
272    [TestMethod]
273    [TestCategory("Problems.DataAnalysis.Symbolic")]
274    [TestProperty("Time", "long")]
275    public void TestVectorInterpretersEstimatedValuesConsistency() {
276      var twister = new MersenneTwister();
277      twister.Seed(31415);
278      const int numRows = 100;
279      var dataset = Util.CreateRandomDataset(twister, numRows, Columns);
280
281      var grammar = new TypeCoherentExpressionGrammar();
282      grammar.ConfigureAsDefaultRegressionGrammar();
283      grammar.Symbols.First(s => s is Square).Enabled = true;
284      grammar.Symbols.First(s => s is SquareRoot).Enabled = true;
285      grammar.Symbols.First(s => s is Cube).Enabled = true;
286      grammar.Symbols.First(s => s is CubeRoot).Enabled = true;
287      grammar.Symbols.First(s => s is Exponential).Enabled = true;
288      grammar.Symbols.First(s => s is Logarithm).Enabled = true;
289      grammar.Symbols.First(s => s is Sine).Enabled = true;
290      grammar.Symbols.First(s => s is Cosine).Enabled = true;
291      grammar.Symbols.First(s => s is Absolute).Enabled = true;
292      grammar.Symbols.First(s => s is AnalyticQuotient).Enabled = true;
293
294      var refInterpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
295      var newInterpreter = new VectorEvaluator();
296      var newAutoDiffInterpreter = new VectorAutoDiffEvaluator();
297
298      var rows = Enumerable.Range(0, numRows).ToList();
299      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 10, 0, 0);
300      foreach (ISymbolicExpressionTree tree in randomTrees) {
301        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
302      }
303
304      for (int i = 0; i < randomTrees.Length; ++i) {
305        var tree = randomTrees[i];
306        var refValues = refInterpreter.GetSymbolicExpressionTreeValues(tree, dataset, rows).ToArray();
307        var newValues = newInterpreter.Evaluate(tree, dataset, rows.ToArray()).ToArray();
308        var newAutoDiffValues = new double[numRows];
309        newAutoDiffInterpreter.Evaluate(tree, dataset, rows.ToArray(), new ISymbolicExpressionTreeNode[0], newAutoDiffValues, null);
310
311        for (int j = 0; j < rows.Count; j++) {
312          if (double.IsNaN(refValues[j]) && double.IsNaN(newValues[j]) && double.IsNaN(newAutoDiffValues[j])) continue;
313          string errorMessage = string.Format("Interpreters do not agree on tree {0} {1}.", i, (new InfixExpressionFormatter()).Format(tree));
314
315          var relDelta = Math.Abs(refValues[j]) * 1e-5;
316          Assert.AreEqual(refValues[j], newValues[j], relDelta, errorMessage);
317          Assert.AreEqual(newValues[j], newAutoDiffValues[j], relDelta, errorMessage);
318        }
319      }
320    }
321
322    [TestMethod]
323    [TestCategory("Problems.DataAnalysis.Symbolic")]
324    [TestProperty("Time", "long")]
325    public void TestCompiledInterpreterEstimatedValuesConsistency() {
326      const double delta = 1e-12;
327
328      var twister = new MersenneTwister();
329      int seed = twister.Next(0, int.MaxValue);
330      twister.Seed((uint)seed);
331
332      Console.WriteLine(seed);
333
334      const int numRows = 100;
335      var dataset = Util.CreateRandomDataset(twister, numRows, Columns);
336
337      var grammar = new TypeCoherentExpressionGrammar();
338      grammar.ConfigureAsDefaultRegressionGrammar();
339      grammar.Symbols.First(x => x.Name == "Power Functions").Enabled = true;
340
341      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 10, 0, 0);
342      foreach (ISymbolicExpressionTree tree in randomTrees) {
343        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
344      }
345
346      var interpreters = new ISymbolicDataAnalysisExpressionTreeInterpreter[] {
347        new SymbolicDataAnalysisExpressionCompiledTreeInterpreter(),
348        new SymbolicDataAnalysisExpressionTreeInterpreter(),
349        new SymbolicDataAnalysisExpressionTreeLinearInterpreter(),
350      };
351      var rows = Enumerable.Range(0, numRows).ToList();
352      var formatter = new SymbolicExpressionTreeHierarchicalFormatter();
353
354      for (int i = 0; i < randomTrees.Length; ++i) {
355        var tree = randomTrees[i];
356        var valuesMatrix = interpreters.Select(x => x.GetSymbolicExpressionTreeValues(tree, dataset, rows).ToList()).ToList();
357        for (int m = 0; m < interpreters.Length - 1; ++m) {
358          for (int n = m + 1; n < interpreters.Length; ++n) {
359            for (int row = 0; row < numRows; ++row) {
360              var v1 = valuesMatrix[m][row];
361              var v2 = valuesMatrix[n][row];
362              if (double.IsNaN(v1) && double.IsNaN(v2)) continue;
363              if (Math.Abs(v1 - v2) > delta) {
364                Console.WriteLine(formatter.Format(tree));
365                foreach (var node in tree.Root.GetSubtree(0).GetSubtree(0).IterateNodesPrefix().ToList()) {
366                  var rootNode = (SymbolicExpressionTreeTopLevelNode)grammar.ProgramRootSymbol.CreateTreeNode();
367                  if (rootNode.HasLocalParameters) rootNode.ResetLocalParameters(twister);
368                  rootNode.SetGrammar(grammar.CreateExpressionTreeGrammar());
369
370                  var startNode = (SymbolicExpressionTreeTopLevelNode)grammar.StartSymbol.CreateTreeNode();
371                  if (startNode.HasLocalParameters) startNode.ResetLocalParameters(twister);
372                  startNode.SetGrammar(grammar.CreateExpressionTreeGrammar());
373
374                  rootNode.AddSubtree(startNode);
375                  var t = new SymbolicExpressionTree(rootNode);
376                  var start = t.Root.GetSubtree(0);
377                  var p = node.Parent;
378                  start.AddSubtree(node);
379                  Console.WriteLine(node);
380
381                  var y1 = interpreters[m].GetSymbolicExpressionTreeValues(t, dataset, new[] { row }).First();
382                  var y2 = interpreters[n].GetSymbolicExpressionTreeValues(t, dataset, new[] { row }).First();
383
384                  if (double.IsNaN(y1) && double.IsNaN(y2)) continue;
385                  string prefix = Math.Abs(y1 - y2) > delta ? "++" : "==";
386                  Console.WriteLine("\t{0} Row {1}: {2} {3}, Deviation = {4}", prefix, row, y1, y2, Math.Abs(y1 - y2));
387                  node.Parent = p;
388                }
389              }
390              string errorMessage = string.Format("Interpreters {0} and {1} do not agree on tree {2} and row {3} (seed = {4}).", interpreters[m].Name, interpreters[n].Name, i, row, seed);
391              Assert.AreEqual(v1, v2, delta, errorMessage);
392            }
393          }
394        }
395      }
396    }
397
398    private void EvaluateTerminals(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
399      // constants
400      Evaluate(interpreter, ds, "(+ 1.5 3.5)", 0, 5.0);
401
402      // variables
403      Evaluate(interpreter, ds, "(variable 2.0 a)", 0, 2.0);
404      Evaluate(interpreter, ds, "(variable 2.0 a)", 1, 4.0);
405    }
406
407    private void EvaluateAdf(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
408
409      // ADF     
410      Evaluate(interpreter, ds, @"(PROG
411                                    (MAIN
412                                      (CALL ADF0))
413                                    (defun ADF0 1.0))", 1, 1.0);
414      Evaluate(interpreter, ds, @"(PROG
415                                    (MAIN
416                                      (* (CALL ADF0) (CALL ADF0)))
417                                    (defun ADF0 2.0))", 1, 4.0);
418      Evaluate(interpreter, ds, @"(PROG
419                                    (MAIN
420                                      (CALL ADF0 2.0 3.0))
421                                    (defun ADF0
422                                      (+ (ARG 0) (ARG 1))))", 1, 5.0);
423      Evaluate(interpreter, ds, @"(PROG
424                                    (MAIN (CALL ADF1 2.0 3.0))
425                                    (defun ADF0
426                                      (- (ARG 1) (ARG 0)))
427                                    (defun ADF1
428                                      (+ (CALL ADF0 (ARG 1) (ARG 0))
429                                         (CALL ADF0 (ARG 0) (ARG 1)))))", 1, 0.0);
430      Evaluate(interpreter, ds, @"(PROG
431                                    (MAIN (CALL ADF1 (variable 2.0 a) 3.0))
432                                    (defun ADF0
433                                      (- (ARG 1) (ARG 0)))
434                                    (defun ADF1                                                                             
435                                      (CALL ADF0 (ARG 1) (ARG 0))))", 1, 1.0);
436      Evaluate(interpreter, ds,
437               @"(PROG
438                                    (MAIN (CALL ADF1 (variable 2.0 a) 3.0))
439                                    (defun ADF0
440                                      (- (ARG 1) (ARG 0)))
441                                    (defun ADF1                                                                             
442                                      (+ (CALL ADF0 (ARG 1) (ARG 0))
443                                         (CALL ADF0 (ARG 0) (ARG 1)))))", 1, 0.0);
444    }
445
446    private void EvaluateSpecialFunctions(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
447      // special functions
448      Action<double> checkAiry = (x) => {
449        double ai, aip, bi, bip;
450        alglib.airy(x, out ai, out aip, out bi, out bip);
451        Evaluate(interpreter, ds, "(airya " + x + ")", 0, ai);
452        Evaluate(interpreter, ds, "(airyb " + x + ")", 0, bi);
453      };
454
455      Action<double> checkBessel = (x) => {
456        Evaluate(interpreter, ds, "(bessel " + x + ")", 0, alglib.besseli0(x));
457      };
458
459      Action<double> checkSinCosIntegrals = (x) => {
460        double si, ci;
461        alglib.sinecosineintegrals(x, out si, out ci);
462        Evaluate(interpreter, ds, "(cosint " + x + ")", 0, ci);
463        Evaluate(interpreter, ds, "(sinint " + x + ")", 0, si);
464      };
465      Action<double> checkHypSinCosIntegrals = (x) => {
466        double shi, chi;
467        alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
468        Evaluate(interpreter, ds, "(hypcosint " + x + ")", 0, chi);
469        Evaluate(interpreter, ds, "(hypsinint " + x + ")", 0, shi);
470      };
471      Action<double> checkFresnelSinCosIntegrals = (x) => {
472        double c = 0, s = 0;
473        alglib.fresnelintegral(x, ref c, ref s);
474        Evaluate(interpreter, ds, "(fresnelcosint " + x + ")", 0, c);
475        Evaluate(interpreter, ds, "(fresnelsinint " + x + ")", 0, s);
476      };
477      Action<double> checkNormErf = (x) => {
478        Evaluate(interpreter, ds, "(norm " + x + ")", 0, alglib.normaldistribution(x));
479        Evaluate(interpreter, ds, "(erf " + x + ")", 0, alglib.errorfunction(x));
480      };
481
482      Action<double> checkGamma = (x) => {
483        Evaluate(interpreter, ds, "(gamma " + x + ")", 0, alglib.gammafunction(x));
484      };
485      Action<double> checkPsi = (x) => {
486        try {
487          Evaluate(interpreter, ds, "(psi " + x + ")", 0, alglib.psi(x));
488        } catch (alglib.alglibexception) { // ignore cases where alglib throws an exception
489        }
490      };
491      Action<double> checkDawson = (x) => {
492        Evaluate(interpreter, ds, "(dawson " + x + ")", 0, alglib.dawsonintegral(x));
493      };
494      Action<double> checkExpInt = (x) => {
495        Evaluate(interpreter, ds, "(expint " + x + ")", 0, alglib.exponentialintegralei(x));
496      };
497
498      foreach (var e in new[] { -2.0, -1.0, 0.0, 1.0, 2.0 }) {
499        checkAiry(e);
500        checkBessel(e);
501        checkSinCosIntegrals(e);
502        checkGamma(e);
503        checkExpInt(e);
504        checkDawson(e);
505        checkPsi(e);
506        checkNormErf(e);
507        checkFresnelSinCosIntegrals(e);
508        checkHypSinCosIntegrals(e);
509      }
510    }
511
512    private void EvaluateLaggedOperations(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
513      // lag
514      Evaluate(interpreter, ds, "(lagVariable 1.0 a -1) ", 1, ds.GetDoubleValue("A", 0));
515      Evaluate(interpreter, ds, "(lagVariable 1.0 a -1) ", 2, ds.GetDoubleValue("A", 1));
516      Evaluate(interpreter, ds, "(lagVariable 1.0 a 0) ", 2, ds.GetDoubleValue("A", 2));
517      Evaluate(interpreter, ds, "(lagVariable 1.0 a 1) ", 0, ds.GetDoubleValue("A", 1));
518
519      // integral
520      Evaluate(interpreter, ds, "(integral -1.0 (variable 1.0 a)) ", 1, ds.GetDoubleValue("A", 0) + ds.GetDoubleValue("A", 1));
521      Evaluate(interpreter, ds, "(integral -1.0 (lagVariable 1.0 a 1)) ", 1, ds.GetDoubleValue("A", 1) + ds.GetDoubleValue("A", 2));
522      Evaluate(interpreter, ds, "(integral -2.0 (variable 1.0 a)) ", 2, ds.GetDoubleValue("A", 0) + ds.GetDoubleValue("A", 1) + ds.GetDoubleValue("A", 2));
523      Evaluate(interpreter, ds, "(integral -1.0 (* (variable 1.0 a) (variable 1.0 b)))", 1, ds.GetDoubleValue("A", 0) * ds.GetDoubleValue("B", 0) + ds.GetDoubleValue("A", 1) * ds.GetDoubleValue("B", 1));
524      Evaluate(interpreter, ds, "(integral -2.0 3.0)", 1, 9.0);
525
526      // derivative
527      // (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
528      Evaluate(interpreter, ds, "(diff (variable 1.0 a)) ", 5, (ds.GetDoubleValue("A", 5) + 2 * ds.GetDoubleValue("A", 4) - 2 * ds.GetDoubleValue("A", 2) - ds.GetDoubleValue("A", 1)) / 8.0);
529      Evaluate(interpreter, ds, "(diff (variable 1.0 b)) ", 5, (ds.GetDoubleValue("B", 5) + 2 * ds.GetDoubleValue("B", 4) - 2 * ds.GetDoubleValue("B", 2) - ds.GetDoubleValue("B", 1)) / 8.0);
530      Evaluate(interpreter, ds, "(diff (* (variable 1.0 a) (variable 1.0 b)))", 5, +
531        (ds.GetDoubleValue("A", 5) * ds.GetDoubleValue("B", 5) +
532        2 * ds.GetDoubleValue("A", 4) * ds.GetDoubleValue("B", 4) -
533        2 * ds.GetDoubleValue("A", 2) * ds.GetDoubleValue("B", 2) -
534        ds.GetDoubleValue("A", 1) * ds.GetDoubleValue("B", 1)) / 8.0);
535      Evaluate(interpreter, ds, "(diff -2.0 3.0)", 5, 0.0);
536
537      // timelag
538      Evaluate(interpreter, ds, "(lag -1.0 (lagVariable 1.0 a 2)) ", 1, ds.GetDoubleValue("A", 2));
539      Evaluate(interpreter, ds, "(lag -2.0 (lagVariable 1.0 a 2)) ", 2, ds.GetDoubleValue("A", 2));
540      Evaluate(interpreter, ds, "(lag -1.0 (* (lagVariable 1.0 a 1) (lagVariable 1.0 b 2)))", 1, ds.GetDoubleValue("A", 1) * ds.GetDoubleValue("B", 2));
541      Evaluate(interpreter, ds, "(lag -2.0 3.0)", 1, 3.0);
542    }
543
544    private void EvaluateOperations(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
545      // addition
546      Evaluate(interpreter, ds, "(+ (variable 2.0 a ))", 1, 4.0);
547      Evaluate(interpreter, ds, "(+ (variable 2.0 a ) (variable 3.0 b ))", 0, 5.0);
548      Evaluate(interpreter, ds, "(+ (variable 2.0 a ) (variable 3.0 b ))", 1, 10.0);
549      Evaluate(interpreter, ds, "(+ (variable 2.0 a) (variable 3.0 b ))", 2, 8.0);
550      Evaluate(interpreter, ds, "(+ 8.0 2.0 2.0)", 0, 12.0);
551
552      // subtraction
553      Evaluate(interpreter, ds, "(- (variable 2.0 a ))", 1, -4.0);
554      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b))", 0, -1.0);
555      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b ))", 1, -2.0);
556      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b ))", 2, -4.0);
557      Evaluate(interpreter, ds, "(- 8.0 2.0 2.0)", 0, 4.0);
558
559      // multiplication
560      Evaluate(interpreter, ds, "(* (variable 2.0 a ))", 0, 2.0);
561      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 0, 6.0);
562      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 1, 24.0);
563      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 2, 12.0);
564      Evaluate(interpreter, ds, "(* 8.0 2.0 2.0)", 0, 32.0);
565
566      // division
567      Evaluate(interpreter, ds, "(/ (variable 2.0 a ))", 1, 1.0 / 4.0);
568      Evaluate(interpreter, ds, "(/ (variable 2.0 a ) 2.0)", 0, 1.0);
569      Evaluate(interpreter, ds, "(/ (variable 2.0 a ) 2.0)", 1, 2.0);
570      Evaluate(interpreter, ds, "(/ (variable 3.0 b ) 2.0)", 2, 3.0);
571      Evaluate(interpreter, ds, "(/ 8.0 2.0 2.0)", 0, 2.0);
572
573      // gt
574      Evaluate(interpreter, ds, "(> (variable 2.0 a) 2.0)", 0, -1.0);
575      Evaluate(interpreter, ds, "(> 2.0 (variable 2.0 a))", 0, -1.0);
576      Evaluate(interpreter, ds, "(> (variable 2.0 a) 1.9)", 0, 1.0);
577      Evaluate(interpreter, ds, "(> 1.9 (variable 2.0 a))", 0, -1.0);
578      Evaluate(interpreter, ds, "(> (log -1.0) (log -1.0))", 0, -1.0); // (> nan nan) should be false
579
580      // lt
581      Evaluate(interpreter, ds, "(< (variable 2.0 a) 2.0)", 0, -1.0);
582      Evaluate(interpreter, ds, "(< 2.0 (variable 2.0 a))", 0, -1.0);
583      Evaluate(interpreter, ds, "(< (variable 2.0 a) 1.9)", 0, -1.0);
584      Evaluate(interpreter, ds, "(< 1.9 (variable 2.0 a))", 0, 1.0);
585      Evaluate(interpreter, ds, "(< (log -1.0) (log -1.0))", 0, -1.0); // (< nan nan) should be false
586
587      // If
588      Evaluate(interpreter, ds, "(if -10.0 2.0 3.0)", 0, 3.0);
589      Evaluate(interpreter, ds, "(if -1.0 2.0 3.0)", 0, 3.0);
590      Evaluate(interpreter, ds, "(if 0.0 2.0 3.0)", 0, 3.0);
591      Evaluate(interpreter, ds, "(if 1.0 2.0 3.0)", 0, 2.0);
592      Evaluate(interpreter, ds, "(if 10.0 2.0 3.0)", 0, 2.0);
593      Evaluate(interpreter, ds, "(if (log -1.0) 2.0 3.0)", 0, 3.0); // if(nan) should return the else branch
594
595      // NOT
596      Evaluate(interpreter, ds, "(not -1.0)", 0, 1.0);
597      Evaluate(interpreter, ds, "(not -2.0)", 0, 1.0);
598      Evaluate(interpreter, ds, "(not 1.0)", 0, -1.0);
599      Evaluate(interpreter, ds, "(not 2.0)", 0, -1.0);
600      Evaluate(interpreter, ds, "(not 0.0)", 0, 1.0);
601      Evaluate(interpreter, ds, "(not (log -1.0))", 0, 1.0);
602
603      // AND
604      Evaluate(interpreter, ds, "(and -1.0 -2.0)", 0, -1.0);
605      Evaluate(interpreter, ds, "(and -1.0 2.0)", 0, -1.0);
606      Evaluate(interpreter, ds, "(and 1.0 -2.0)", 0, -1.0);
607      Evaluate(interpreter, ds, "(and 1.0 0.0)", 0, -1.0);
608      Evaluate(interpreter, ds, "(and 0.0 0.0)", 0, -1.0);
609      Evaluate(interpreter, ds, "(and 1.0 2.0)", 0, 1.0);
610      Evaluate(interpreter, ds, "(and 1.0 2.0 3.0)", 0, 1.0);
611      Evaluate(interpreter, ds, "(and 1.0 -2.0 3.0)", 0, -1.0);
612      Evaluate(interpreter, ds, "(and (log -1.0))", 0, -1.0); // (and NaN)
613      Evaluate(interpreter, ds, "(and (log -1.0)  1.0)", 0, -1.0); // (and NaN 1.0)
614
615      // OR
616      Evaluate(interpreter, ds, "(or -1.0 -2.0)", 0, -1.0);
617      Evaluate(interpreter, ds, "(or -1.0 2.0)", 0, 1.0);
618      Evaluate(interpreter, ds, "(or 1.0 -2.0)", 0, 1.0);
619      Evaluate(interpreter, ds, "(or 1.0 2.0)", 0, 1.0);
620      Evaluate(interpreter, ds, "(or 0.0 0.0)", 0, -1.0);
621      Evaluate(interpreter, ds, "(or -1.0 -2.0 -3.0)", 0, -1.0);
622      Evaluate(interpreter, ds, "(or -1.0 -2.0 3.0)", 0, 1.0);
623      Evaluate(interpreter, ds, "(or (log -1.0))", 0, -1.0); // (or NaN)
624      Evaluate(interpreter, ds, "(or (log -1.0)  1.0)", 0, -1.0); // (or NaN 1.0)
625
626      // XOR
627      Evaluate(interpreter, ds, "(xor -1.0 -2.0)", 0, -1.0);
628      Evaluate(interpreter, ds, "(xor -1.0 2.0)", 0, 1.0);
629      Evaluate(interpreter, ds, "(xor 1.0 -2.0)", 0, 1.0);
630      Evaluate(interpreter, ds, "(xor 1.0 2.0)", 0, -1.0);
631      Evaluate(interpreter, ds, "(xor 0.0 0.0)", 0, -1.0);
632      Evaluate(interpreter, ds, "(xor -1.0 -2.0 -3.0)", 0, -1.0);
633      Evaluate(interpreter, ds, "(xor -1.0 -2.0 3.0)", 0, 1.0);
634      Evaluate(interpreter, ds, "(xor -1.0 2.0 3.0)", 0, -1.0);
635      Evaluate(interpreter, ds, "(xor 1.0 2.0 3.0)", 0, 1.0);
636      Evaluate(interpreter, ds, "(xor (log -1.0))", 0, -1.0);
637      Evaluate(interpreter, ds, "(xor (log -1.0)  1.0)", 0, 1.0);
638
639      // sin, cos, tan
640      Evaluate(interpreter, ds, "(sin " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, 0.0);
641      Evaluate(interpreter, ds, "(sin 0.0)", 0, 0.0);
642      Evaluate(interpreter, ds, "(cos " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, -1.0);
643      Evaluate(interpreter, ds, "(cos 0.0)", 0, 1.0);
644      Evaluate(interpreter, ds, "(tan " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, Math.Tan(Math.PI));
645      Evaluate(interpreter, ds, "(tan 0.0)", 0, Math.Tan(Math.PI));
646
647      // exp, log
648      Evaluate(interpreter, ds, "(log (exp 7.0))", 0, Math.Log(Math.Exp(7)));
649      Evaluate(interpreter, ds, "(exp (log 7.0))", 0, Math.Exp(Math.Log(7)));
650      Evaluate(interpreter, ds, "(log -3.0)", 0, Math.Log(-3));
651
652      // power
653      Evaluate(interpreter, ds, "(pow 2.0 3.0)", 0, 8.0);
654      Evaluate(interpreter, ds, "(pow 4.0 0.5)", 0, 1.0); // interpreter should round to the nearest integer value value (.5 is rounded to the even number)
655      Evaluate(interpreter, ds, "(pow 4.0 2.5)", 0, 16.0); // interpreter should round to the nearest integer value value (.5 is rounded to the even number)
656      Evaluate(interpreter, ds, "(pow -2.0 3.0)", 0, -8.0);
657      Evaluate(interpreter, ds, "(pow 2.0 -3.0)", 0, 1.0 / 8.0);
658      Evaluate(interpreter, ds, "(pow -2.0 -3.0)", 0, -1.0 / 8.0);
659
660      // root
661      Evaluate(interpreter, ds, "(root 9.0 2.0)", 0, 3.0);
662      Evaluate(interpreter, ds, "(root 27.0 3.0)", 0, 3.0);
663      Evaluate(interpreter, ds, "(root 2.0 -3.0)", 0, Math.Pow(2.0, -1.0 / 3.0));
664
665      // mean
666      Evaluate(interpreter, ds, "(mean -1.0 1.0 -1.0)", 0, -1.0 / 3.0);
667    }
668
669    private void Evaluate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds, string expr, int index, double expected) {
670      var importer = new SymbolicExpressionImporter();
671      ISymbolicExpressionTree tree = importer.Import(expr);
672
673      double actual = interpreter.GetSymbolicExpressionTreeValues(tree, ds, Enumerable.Range(index, 1)).First();
674
675      Assert.IsFalse(double.IsNaN(actual) && !double.IsNaN(expected));
676      Assert.IsFalse(!double.IsNaN(actual) && double.IsNaN(expected));
677      if (!double.IsNaN(actual) && !double.IsNaN(expected))
678        Assert.AreEqual(expected, actual, 1.0E-12, expr);
679    }
680  }
681}
Note: See TracBrowser for help on using the repository browser.