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

Last change on this file since 13256 was 13256, checked in by mkommend, 6 years ago

#2442: Added new unit tests for interpreters.

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