Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2442: Added CreateDelegate method to be able to get the lambda expression corresponding to a symbolic expression tree, performed minor cosmetic enhancements of the SymbolicDataAnalysisExpressionCompiledTreeInterpreter. Updated unit tests, added unit test for testing the correctness of the SymbolicDataAnalysisExpressionCompiledTreeInterpreter which can show exactly which operations cause deviations.

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