Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/SymbolicDataAnalysisExpressionTreeInterpreterTest.cs @ 14353

Last change on this file since 14353 was 14353, checked in by bburlacu, 8 years ago

#2685: Add correction step for values miscalculated due to cyclical symbol dependencies in the grammar. Updated unit test.

File size: 28.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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 = 5000;
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 ILEmittingInterpreterTestTypeCoherentGrammarPerformance() {
76      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter(), 7.5e6);
77    }
78    [TestMethod]
79    [TestCategory("Problems.DataAnalysis.Symbolic")]
80    [TestProperty("Time", "long")]
81    public void ILEmittingInterpreterTestArithmeticGrammarPerformance() {
82      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter(), 7.5e6);
83    }
84
85    [TestMethod]
86    [TestCategory("Problems.DataAnalysis.Symbolic")]
87    [TestProperty("Time", "long")]
88    public void LinearInterpreterTestTypeCoherentGrammarPerformance() {
89      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
90    }
91    [TestMethod]
92    [TestCategory("Problems.DataAnalysis.Symbolic")]
93    [TestProperty("Time", "long")]
94    public void LinearInterpreterTestFullGrammarPerformance() {
95      TestFullGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
96    }
97    [TestMethod]
98    [TestCategory("Problems.DataAnalysis.Symbolic")]
99    [TestProperty("Time", "long")]
100    public void LinearInterpreterTestArithmeticGrammarPerformance() {
101      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
102    }
103
104    private void TestTypeCoherentGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
105      var twister = new MersenneTwister(31415);
106      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
107
108      var grammar = new TypeCoherentExpressionGrammar();
109      grammar.ConfigureAsDefaultRegressionGrammar();
110
111      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
112      foreach (ISymbolicExpressionTree tree in randomTrees) {
113        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
114      }
115      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
116      //mkommend: commented due to performance issues on the builder
117      // Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
118    }
119
120    private void TestFullGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
121      var twister = new MersenneTwister(31415);
122      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
123
124      var grammar = new FullFunctionalExpressionGrammar();
125      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
126      foreach (ISymbolicExpressionTree tree in randomTrees) {
127        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
128      }
129      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
130      //mkommend: commented due to performance issues on the builder
131      //Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
132    }
133
134    private void TestArithmeticGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
135      var twister = new MersenneTwister(31415);
136      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
137
138      var grammar = new ArithmeticExpressionGrammar();
139      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
140      foreach (SymbolicExpressionTree tree in randomTrees) {
141        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
142      }
143
144      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
145      //mkommend: commented due to performance issues on the builder
146      //Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
147    }
148
149
150    /// <summary>
151    ///A test for Evaluate
152    ///</summary>
153    [TestMethod]
154    [TestCategory("Problems.DataAnalysis.Symbolic")]
155    [TestProperty("Time", "short")]
156    public void StandardInterpreterTestEvaluation() {
157      var interpreter = new SymbolicDataAnalysisExpressionTreeInterpreter();
158      EvaluateTerminals(interpreter, ds);
159      EvaluateOperations(interpreter, ds);
160      EvaluateLaggedOperations(interpreter, ds);
161      EvaluateSpecialFunctions(interpreter, ds);
162      EvaluateAdf(interpreter, ds);
163    }
164
165    [TestMethod]
166    [TestCategory("Problems.DataAnalysis.Symbolic")]
167    [TestProperty("Time", "short")]
168    public void LinearInterpreterTestEvaluation() {
169      var interpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
170      //ADFs are not supported by the linear interpreter
171      EvaluateTerminals(interpreter, ds);
172      EvaluateOperations(interpreter, ds);
173      EvaluateLaggedOperations(interpreter, ds);
174      EvaluateSpecialFunctions(interpreter, ds);
175    }
176
177    [TestMethod]
178    [TestCategory("Problems.DataAnalysis.Symbolic")]
179    [TestProperty("Time", "long")]
180    public void TestInterpretersEstimatedValuesConsistency() {
181      var twister = new MersenneTwister();
182      int seed = twister.Next(0, int.MaxValue);
183      twister.Seed((uint)seed);
184      const int numRows = 100;
185      var dataset = Util.CreateRandomDataset(twister, numRows, Columns);
186
187      var grammar = new TypeCoherentExpressionGrammar();
188
189      var interpreters = new ISymbolicDataAnalysisExpressionTreeInterpreter[] {
190        new SymbolicDataAnalysisExpressionTreeLinearInterpreter(),
191        new SymbolicDataAnalysisExpressionTreeInterpreter(),
192      };
193
194      var rows = Enumerable.Range(0, numRows).ToList();
195      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 10, 0, 0);
196      foreach (ISymbolicExpressionTree tree in randomTrees) {
197        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
198      }
199
200      for (int i = 0; i < randomTrees.Length; ++i) {
201        var tree = randomTrees[i];
202        var valuesMatrix = interpreters.Select(x => x.GetSymbolicExpressionTreeValues(tree, dataset, rows)).ToList();
203        for (int m = 0; m < interpreters.Length - 1; ++m) {
204          var sum = valuesMatrix[m].Sum();
205          for (int n = m + 1; n < interpreters.Length; ++n) {
206            var s = valuesMatrix[n].Sum();
207            if (double.IsNaN(sum) && double.IsNaN(s)) continue;
208
209            string errorMessage = string.Format("Interpreters {0} and {1} do not agree on tree {2} (seed = {3}).", interpreters[m].Name, interpreters[n].Name, i, seed);
210            Assert.AreEqual(sum, s, 1e-12, errorMessage);
211          }
212        }
213      }
214    }
215
216    [TestMethod]
217    [TestCategory("Problems.DataAnalysis.Symbolic")]
218    [TestProperty("Time", "long")]
219    public void TestCompiledInterpreterEstimatedValuesConsistency() {
220      const double delta = 1e-12;
221
222      var twister = new MersenneTwister();
223      int seed = twister.Next(0, int.MaxValue);
224      twister.Seed((uint)seed);
225
226      Console.WriteLine(seed);
227
228      const int numRows = 100;
229      var dataset = Util.CreateRandomDataset(twister, numRows, Columns);
230
231      var grammar = new TypeCoherentExpressionGrammar();
232      grammar.ConfigureAsDefaultRegressionGrammar();
233      grammar.Symbols.First(x => x.Name == "Power Functions").Enabled = true;
234
235      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 10, 0, 0);
236      foreach (ISymbolicExpressionTree tree in randomTrees) {
237        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
238      }
239
240      var interpreters = new ISymbolicDataAnalysisExpressionTreeInterpreter[] {
241        new SymbolicDataAnalysisExpressionTreeInterpreter(),
242        new SymbolicDataAnalysisExpressionTreeLinearInterpreter(),
243      };
244      var rows = Enumerable.Range(0, numRows).ToList();
245      var formatter = new SymbolicExpressionTreeHierarchicalFormatter();
246
247      for (int i = 0; i < randomTrees.Length; ++i) {
248        var tree = randomTrees[i];
249        var valuesMatrix = interpreters.Select(x => x.GetSymbolicExpressionTreeValues(tree, dataset, rows).ToList()).ToList();
250        for (int m = 0; m < interpreters.Length - 1; ++m) {
251          for (int n = m + 1; n < interpreters.Length; ++n) {
252            for (int row = 0; row < numRows; ++row) {
253              var v1 = valuesMatrix[m][row];
254              var v2 = valuesMatrix[n][row];
255              if (double.IsNaN(v1) && double.IsNaN(v2)) continue;
256              if (Math.Abs(v1 - v2) > delta) {
257                Console.WriteLine(formatter.Format(tree));
258                foreach (var node in tree.Root.GetSubtree(0).GetSubtree(0).IterateNodesPrefix().ToList()) {
259                  var rootNode = (SymbolicExpressionTreeTopLevelNode)grammar.ProgramRootSymbol.CreateTreeNode();
260                  if (rootNode.HasLocalParameters) rootNode.ResetLocalParameters(twister);
261                  rootNode.SetGrammar(grammar.CreateExpressionTreeGrammar());
262
263                  var startNode = (SymbolicExpressionTreeTopLevelNode)grammar.StartSymbol.CreateTreeNode();
264                  if (startNode.HasLocalParameters) startNode.ResetLocalParameters(twister);
265                  startNode.SetGrammar(grammar.CreateExpressionTreeGrammar());
266
267                  rootNode.AddSubtree(startNode);
268                  var t = new SymbolicExpressionTree(rootNode);
269                  var start = t.Root.GetSubtree(0);
270                  var p = node.Parent;
271                  start.AddSubtree(node);
272                  Console.WriteLine(node);
273
274                  var y1 = interpreters[m].GetSymbolicExpressionTreeValues(t, dataset, new[] { row }).First();
275                  var y2 = interpreters[n].GetSymbolicExpressionTreeValues(t, dataset, new[] { row }).First();
276
277                  if (double.IsNaN(y1) && double.IsNaN(y2)) continue;
278                  string prefix = Math.Abs(y1 - y2) > delta ? "++" : "==";
279                  Console.WriteLine("\t{0} Row {1}: {2} {3}, Deviation = {4}", prefix, row, y1, y2, Math.Abs(y1 - y2));
280                  node.Parent = p;
281                }
282              }
283              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);
284              Assert.AreEqual(v1, v2, delta, errorMessage);
285            }
286          }
287        }
288      }
289    }
290
291    private void EvaluateTerminals(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
292      // constants
293      Evaluate(interpreter, ds, "(+ 1.5 3.5)", 0, 5.0);
294
295      // variables
296      Evaluate(interpreter, ds, "(variable 2.0 a)", 0, 2.0);
297      Evaluate(interpreter, ds, "(variable 2.0 a)", 1, 4.0);
298    }
299
300    private void EvaluateAdf(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
301
302      // ADF     
303      Evaluate(interpreter, ds, @"(PROG
304                                    (MAIN
305                                      (CALL ADF0))
306                                    (defun ADF0 1.0))", 1, 1.0);
307      Evaluate(interpreter, ds, @"(PROG
308                                    (MAIN
309                                      (* (CALL ADF0) (CALL ADF0)))
310                                    (defun ADF0 2.0))", 1, 4.0);
311      Evaluate(interpreter, ds, @"(PROG
312                                    (MAIN
313                                      (CALL ADF0 2.0 3.0))
314                                    (defun ADF0
315                                      (+ (ARG 0) (ARG 1))))", 1, 5.0);
316      Evaluate(interpreter, ds, @"(PROG
317                                    (MAIN (CALL ADF1 2.0 3.0))
318                                    (defun ADF0
319                                      (- (ARG 1) (ARG 0)))
320                                    (defun ADF1
321                                      (+ (CALL ADF0 (ARG 1) (ARG 0))
322                                         (CALL ADF0 (ARG 0) (ARG 1)))))", 1, 0.0);
323      Evaluate(interpreter, ds, @"(PROG
324                                    (MAIN (CALL ADF1 (variable 2.0 a) 3.0))
325                                    (defun ADF0
326                                      (- (ARG 1) (ARG 0)))
327                                    (defun ADF1                                                                             
328                                      (CALL ADF0 (ARG 1) (ARG 0))))", 1, 1.0);
329      Evaluate(interpreter, ds,
330               @"(PROG
331                                    (MAIN (CALL ADF1 (variable 2.0 a) 3.0))
332                                    (defun ADF0
333                                      (- (ARG 1) (ARG 0)))
334                                    (defun ADF1                                                                             
335                                      (+ (CALL ADF0 (ARG 1) (ARG 0))
336                                         (CALL ADF0 (ARG 0) (ARG 1)))))", 1, 0.0);
337    }
338
339    private void EvaluateSpecialFunctions(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
340      // special functions
341      Action<double> checkAiry = (x) => {
342        double ai, aip, bi, bip;
343        alglib.airy(x, out ai, out aip, out bi, out bip);
344        Evaluate(interpreter, ds, "(airya " + x + ")", 0, ai);
345        Evaluate(interpreter, ds, "(airyb " + x + ")", 0, bi);
346      };
347
348      Action<double> checkBessel = (x) => {
349        Evaluate(interpreter, ds, "(bessel " + x + ")", 0, alglib.besseli0(x));
350      };
351
352      Action<double> checkSinCosIntegrals = (x) => {
353        double si, ci;
354        alglib.sinecosineintegrals(x, out si, out ci);
355        Evaluate(interpreter, ds, "(cosint " + x + ")", 0, ci);
356        Evaluate(interpreter, ds, "(sinint " + x + ")", 0, si);
357      };
358      Action<double> checkHypSinCosIntegrals = (x) => {
359        double shi, chi;
360        alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
361        Evaluate(interpreter, ds, "(hypcosint " + x + ")", 0, chi);
362        Evaluate(interpreter, ds, "(hypsinint " + x + ")", 0, shi);
363      };
364      Action<double> checkFresnelSinCosIntegrals = (x) => {
365        double c = 0, s = 0;
366        alglib.fresnelintegral(x, ref c, ref s);
367        Evaluate(interpreter, ds, "(fresnelcosint " + x + ")", 0, c);
368        Evaluate(interpreter, ds, "(fresnelsinint " + x + ")", 0, s);
369      };
370      Action<double> checkNormErf = (x) => {
371        Evaluate(interpreter, ds, "(norm " + x + ")", 0, alglib.normaldistribution(x));
372        Evaluate(interpreter, ds, "(erf " + x + ")", 0, alglib.errorfunction(x));
373      };
374
375      Action<double> checkGamma = (x) => {
376        Evaluate(interpreter, ds, "(gamma " + x + ")", 0, alglib.gammafunction(x));
377      };
378      Action<double> checkPsi = (x) => {
379        try {
380          Evaluate(interpreter, ds, "(psi " + x + ")", 0, alglib.psi(x));
381        }
382        catch (alglib.alglibexception) { // ignore cases where alglib throws an exception
383        }
384      };
385      Action<double> checkDawson = (x) => {
386        Evaluate(interpreter, ds, "(dawson " + x + ")", 0, alglib.dawsonintegral(x));
387      };
388      Action<double> checkExpInt = (x) => {
389        Evaluate(interpreter, ds, "(expint " + x + ")", 0, alglib.exponentialintegralei(x));
390      };
391
392      foreach (var e in new[] { -2.0, -1.0, 0.0, 1.0, 2.0 }) {
393        checkAiry(e);
394        checkBessel(e);
395        checkSinCosIntegrals(e);
396        checkGamma(e);
397        checkExpInt(e);
398        checkDawson(e);
399        checkPsi(e);
400        checkNormErf(e);
401        checkFresnelSinCosIntegrals(e);
402        checkHypSinCosIntegrals(e);
403      }
404    }
405
406    private void EvaluateLaggedOperations(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
407      // lag
408      Evaluate(interpreter, ds, "(lagVariable 1.0 a -1) ", 1, ds.GetDoubleValue("A", 0));
409      Evaluate(interpreter, ds, "(lagVariable 1.0 a -1) ", 2, ds.GetDoubleValue("A", 1));
410      Evaluate(interpreter, ds, "(lagVariable 1.0 a 0) ", 2, ds.GetDoubleValue("A", 2));
411      Evaluate(interpreter, ds, "(lagVariable 1.0 a 1) ", 0, ds.GetDoubleValue("A", 1));
412
413      // integral
414      Evaluate(interpreter, ds, "(integral -1.0 (variable 1.0 a)) ", 1, ds.GetDoubleValue("A", 0) + ds.GetDoubleValue("A", 1));
415      Evaluate(interpreter, ds, "(integral -1.0 (lagVariable 1.0 a 1)) ", 1, ds.GetDoubleValue("A", 1) + ds.GetDoubleValue("A", 2));
416      Evaluate(interpreter, ds, "(integral -2.0 (variable 1.0 a)) ", 2, ds.GetDoubleValue("A", 0) + ds.GetDoubleValue("A", 1) + ds.GetDoubleValue("A", 2));
417      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));
418      Evaluate(interpreter, ds, "(integral -2.0 3.0)", 1, 9.0);
419
420      // derivative
421      // (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
422      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);
423      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);
424      Evaluate(interpreter, ds, "(diff (* (variable 1.0 a) (variable 1.0 b)))", 5, +
425        (ds.GetDoubleValue("A", 5) * ds.GetDoubleValue("B", 5) +
426        2 * ds.GetDoubleValue("A", 4) * ds.GetDoubleValue("B", 4) -
427        2 * ds.GetDoubleValue("A", 2) * ds.GetDoubleValue("B", 2) -
428        ds.GetDoubleValue("A", 1) * ds.GetDoubleValue("B", 1)) / 8.0);
429      Evaluate(interpreter, ds, "(diff -2.0 3.0)", 5, 0.0);
430
431      // timelag
432      Evaluate(interpreter, ds, "(lag -1.0 (lagVariable 1.0 a 2)) ", 1, ds.GetDoubleValue("A", 2));
433      Evaluate(interpreter, ds, "(lag -2.0 (lagVariable 1.0 a 2)) ", 2, ds.GetDoubleValue("A", 2));
434      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));
435      Evaluate(interpreter, ds, "(lag -2.0 3.0)", 1, 3.0);
436    }
437
438    private void EvaluateOperations(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds) {
439      // addition
440      Evaluate(interpreter, ds, "(+ (variable 2.0 a ))", 1, 4.0);
441      Evaluate(interpreter, ds, "(+ (variable 2.0 a ) (variable 3.0 b ))", 0, 5.0);
442      Evaluate(interpreter, ds, "(+ (variable 2.0 a ) (variable 3.0 b ))", 1, 10.0);
443      Evaluate(interpreter, ds, "(+ (variable 2.0 a) (variable 3.0 b ))", 2, 8.0);
444      Evaluate(interpreter, ds, "(+ 8.0 2.0 2.0)", 0, 12.0);
445
446      // subtraction
447      Evaluate(interpreter, ds, "(- (variable 2.0 a ))", 1, -4.0);
448      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b))", 0, -1.0);
449      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b ))", 1, -2.0);
450      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b ))", 2, -4.0);
451      Evaluate(interpreter, ds, "(- 8.0 2.0 2.0)", 0, 4.0);
452
453      // multiplication
454      Evaluate(interpreter, ds, "(* (variable 2.0 a ))", 0, 2.0);
455      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 0, 6.0);
456      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 1, 24.0);
457      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 2, 12.0);
458      Evaluate(interpreter, ds, "(* 8.0 2.0 2.0)", 0, 32.0);
459
460      // division
461      Evaluate(interpreter, ds, "(/ (variable 2.0 a ))", 1, 1.0 / 4.0);
462      Evaluate(interpreter, ds, "(/ (variable 2.0 a ) 2.0)", 0, 1.0);
463      Evaluate(interpreter, ds, "(/ (variable 2.0 a ) 2.0)", 1, 2.0);
464      Evaluate(interpreter, ds, "(/ (variable 3.0 b ) 2.0)", 2, 3.0);
465      Evaluate(interpreter, ds, "(/ 8.0 2.0 2.0)", 0, 2.0);
466
467      // gt
468      Evaluate(interpreter, ds, "(> (variable 2.0 a) 2.0)", 0, -1.0);
469      Evaluate(interpreter, ds, "(> 2.0 (variable 2.0 a))", 0, -1.0);
470      Evaluate(interpreter, ds, "(> (variable 2.0 a) 1.9)", 0, 1.0);
471      Evaluate(interpreter, ds, "(> 1.9 (variable 2.0 a))", 0, -1.0);
472      Evaluate(interpreter, ds, "(> (log -1.0) (log -1.0))", 0, -1.0); // (> nan nan) should be false
473
474      // lt
475      Evaluate(interpreter, ds, "(< (variable 2.0 a) 2.0)", 0, -1.0);
476      Evaluate(interpreter, ds, "(< 2.0 (variable 2.0 a))", 0, -1.0);
477      Evaluate(interpreter, ds, "(< (variable 2.0 a) 1.9)", 0, -1.0);
478      Evaluate(interpreter, ds, "(< 1.9 (variable 2.0 a))", 0, 1.0);
479      Evaluate(interpreter, ds, "(< (log -1.0) (log -1.0))", 0, -1.0); // (< nan nan) should be false
480
481      // If
482      Evaluate(interpreter, ds, "(if -10.0 2.0 3.0)", 0, 3.0);
483      Evaluate(interpreter, ds, "(if -1.0 2.0 3.0)", 0, 3.0);
484      Evaluate(interpreter, ds, "(if 0.0 2.0 3.0)", 0, 3.0);
485      Evaluate(interpreter, ds, "(if 1.0 2.0 3.0)", 0, 2.0);
486      Evaluate(interpreter, ds, "(if 10.0 2.0 3.0)", 0, 2.0);
487      Evaluate(interpreter, ds, "(if (log -1.0) 2.0 3.0)", 0, 3.0); // if(nan) should return the else branch
488
489      // NOT
490      Evaluate(interpreter, ds, "(not -1.0)", 0, 1.0);
491      Evaluate(interpreter, ds, "(not -2.0)", 0, 1.0);
492      Evaluate(interpreter, ds, "(not 1.0)", 0, -1.0);
493      Evaluate(interpreter, ds, "(not 2.0)", 0, -1.0);
494      Evaluate(interpreter, ds, "(not 0.0)", 0, 1.0);
495      Evaluate(interpreter, ds, "(not (log -1.0))", 0, 1.0);
496
497      // AND
498      Evaluate(interpreter, ds, "(and -1.0 -2.0)", 0, -1.0);
499      Evaluate(interpreter, ds, "(and -1.0 2.0)", 0, -1.0);
500      Evaluate(interpreter, ds, "(and 1.0 -2.0)", 0, -1.0);
501      Evaluate(interpreter, ds, "(and 1.0 0.0)", 0, -1.0);
502      Evaluate(interpreter, ds, "(and 0.0 0.0)", 0, -1.0);
503      Evaluate(interpreter, ds, "(and 1.0 2.0)", 0, 1.0);
504      Evaluate(interpreter, ds, "(and 1.0 2.0 3.0)", 0, 1.0);
505      Evaluate(interpreter, ds, "(and 1.0 -2.0 3.0)", 0, -1.0);
506      Evaluate(interpreter, ds, "(and (log -1.0))", 0, -1.0); // (and NaN)
507      Evaluate(interpreter, ds, "(and (log -1.0)  1.0)", 0, -1.0); // (and NaN 1.0)
508
509      // OR
510      Evaluate(interpreter, ds, "(or -1.0 -2.0)", 0, -1.0);
511      Evaluate(interpreter, ds, "(or -1.0 2.0)", 0, 1.0);
512      Evaluate(interpreter, ds, "(or 1.0 -2.0)", 0, 1.0);
513      Evaluate(interpreter, ds, "(or 1.0 2.0)", 0, 1.0);
514      Evaluate(interpreter, ds, "(or 0.0 0.0)", 0, -1.0);
515      Evaluate(interpreter, ds, "(or -1.0 -2.0 -3.0)", 0, -1.0);
516      Evaluate(interpreter, ds, "(or -1.0 -2.0 3.0)", 0, 1.0);
517      Evaluate(interpreter, ds, "(or (log -1.0))", 0, -1.0); // (or NaN)
518      Evaluate(interpreter, ds, "(or (log -1.0)  1.0)", 0, -1.0); // (or NaN 1.0)
519
520      // XOR
521      Evaluate(interpreter, ds, "(xor -1.0 -2.0)", 0, -1.0);
522      Evaluate(interpreter, ds, "(xor -1.0 2.0)", 0, 1.0);
523      Evaluate(interpreter, ds, "(xor 1.0 -2.0)", 0, 1.0);
524      Evaluate(interpreter, ds, "(xor 1.0 2.0)", 0, -1.0);
525      Evaluate(interpreter, ds, "(xor 0.0 0.0)", 0, -1.0);
526      Evaluate(interpreter, ds, "(xor -1.0 -2.0 -3.0)", 0, -1.0);
527      Evaluate(interpreter, ds, "(xor -1.0 -2.0 3.0)", 0, 1.0);
528      Evaluate(interpreter, ds, "(xor -1.0 2.0 3.0)", 0, -1.0);
529      Evaluate(interpreter, ds, "(xor 1.0 2.0 3.0)", 0, 1.0);
530      Evaluate(interpreter, ds, "(xor (log -1.0))", 0, -1.0);
531      Evaluate(interpreter, ds, "(xor (log -1.0)  1.0)", 0, 1.0);
532
533      // sin, cos, tan
534      Evaluate(interpreter, ds, "(sin " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, 0.0);
535      Evaluate(interpreter, ds, "(sin 0.0)", 0, 0.0);
536      Evaluate(interpreter, ds, "(cos " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, -1.0);
537      Evaluate(interpreter, ds, "(cos 0.0)", 0, 1.0);
538      Evaluate(interpreter, ds, "(tan " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, Math.Tan(Math.PI));
539      Evaluate(interpreter, ds, "(tan 0.0)", 0, Math.Tan(Math.PI));
540
541      // exp, log
542      Evaluate(interpreter, ds, "(log (exp 7.0))", 0, Math.Log(Math.Exp(7)));
543      Evaluate(interpreter, ds, "(exp (log 7.0))", 0, Math.Exp(Math.Log(7)));
544      Evaluate(interpreter, ds, "(log -3.0)", 0, Math.Log(-3));
545
546      // power
547      Evaluate(interpreter, ds, "(pow 2.0 3.0)", 0, 8.0);
548      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)
549      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)
550      Evaluate(interpreter, ds, "(pow -2.0 3.0)", 0, -8.0);
551      Evaluate(interpreter, ds, "(pow 2.0 -3.0)", 0, 1.0 / 8.0);
552      Evaluate(interpreter, ds, "(pow -2.0 -3.0)", 0, -1.0 / 8.0);
553
554      // root
555      Evaluate(interpreter, ds, "(root 9.0 2.0)", 0, 3.0);
556      Evaluate(interpreter, ds, "(root 27.0 3.0)", 0, 3.0);
557      Evaluate(interpreter, ds, "(root 2.0 -3.0)", 0, Math.Pow(2.0, -1.0 / 3.0));
558
559      // mean
560      Evaluate(interpreter, ds, "(mean -1.0 1.0 -1.0)", 0, -1.0 / 3.0);
561    }
562
563    private void Evaluate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, IDataset ds, string expr, int index, double expected) {
564      var importer = new SymbolicExpressionImporter();
565      ISymbolicExpressionTree tree = importer.Import(expr);
566
567      double actual = interpreter.GetSymbolicExpressionTreeValues(tree, ds, Enumerable.Range(index, 1)).First();
568
569      Assert.IsFalse(double.IsNaN(actual) && !double.IsNaN(expected));
570      Assert.IsFalse(!double.IsNaN(actual) && double.IsNaN(expected));
571      if (!double.IsNaN(actual) && !double.IsNaN(expected))
572        Assert.AreEqual(expected, actual, 1.0E-12, expr);
573    }
574  }
575}
Note: See TracBrowser for help on using the repository browser.