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

Last change on this file since 9955 was 9955, checked in by mkommend, 7 years ago

#2108: Added new resource folder for unit tests and removed run configuration.

File size: 25.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 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 System.Linq.Expressions;
28using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
29using HeuristicLab.Random;
30using Microsoft.VisualStudio.TestTools.UnitTesting;
31namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Tests {
32
33
34  [TestClass]
35  public class SymbolicDataAnalysisExpressionTreeInterpreterTest {
36    private const int N = 1000;
37    private const int Rows = 1000;
38    private const int Columns = 50;
39
40    private static Dataset ds = new Dataset(new string[] { "Y", "A", "B" }, new double[,] {
41        { 1.0, 1.0, 1.0 },
42        { 2.0, 2.0, 2.0 },
43        { 3.0, 1.0, 2.0 },
44        { 4.0, 1.0, 1.0 },
45        { 5.0, 2.0, 2.0 },
46        { 6.0, 1.0, 2.0 },
47        { 7.0, 1.0, 1.0 },
48        { 8.0, 2.0, 2.0 },
49        { 9.0, 1.0, 2.0 },
50        { 10.0, 1.0, 1.0 },
51        { 11.0, 2.0, 2.0 },
52        { 12.0, 1.0, 2.0 }
53      });
54
55    [TestMethod]
56    [TestCategory("Problems.DataAnalysis.Symbolic")]
57    [TestProperty("Time", "long")]
58    public void StandardInterpreterTestTypeCoherentGrammarPerformance() {
59      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeInterpreter(), 12.5e6);
60    }
61    [TestMethod]
62    [TestCategory("Problems.DataAnalysis.Symbolic")]
63    [TestProperty("Time", "long")]
64    public void StandardInterpreterTestFullGrammarPerformance() {
65      TestFullGrammarPerformance(new SymbolicDataAnalysisExpressionTreeInterpreter(), 12.5e6);
66    }
67    [TestMethod]
68    [TestCategory("Problems.DataAnalysis.Symbolic")]
69    [TestProperty("Time", "long")]
70    public void StandardInterpreterTestArithmeticGrammarPerformance() {
71      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeInterpreter(), 12.5e6);
72    }
73
74
75    [TestMethod]
76    [TestCategory("Problems.DataAnalysis.Symbolic")]
77    [TestProperty("Time", "long")]
78    public void ILEmittingInterpreterTestTypeCoherentGrammarPerformance() {
79      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter(), 7.5e6);
80    }
81    [TestMethod]
82    [TestCategory("Problems.DataAnalysis.Symbolic")]
83    [TestProperty("Time", "long")]
84    public void ILEmittingInterpreterTestFullGrammarPerformance() {
85      TestFullGrammarPerformance(new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter(), 7.5e6);
86    }
87    [TestMethod]
88    [TestCategory("Problems.DataAnalysis.Symbolic")]
89    [TestProperty("Time", "long")]
90    public void ILEmittingInterpreterTestArithmeticGrammarPerformance() {
91      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter(), 7.5e6);
92    }
93
94
95    [TestMethod]
96    [TestCategory("Problems.DataAnalysis.Symbolic")]
97    [TestProperty("Time", "long")]
98    public void LinearInterpreterTestTypeCoherentGrammarPerformance() {
99      TestTypeCoherentGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
100    }
101    [TestMethod]
102    [TestCategory("Problems.DataAnalysis.Symbolic")]
103    [TestProperty("Time", "long")]
104    public void LinearInterpreterTestFullGrammarPerformance() {
105      TestFullGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
106    }
107    [TestMethod]
108    [TestCategory("Problems.DataAnalysis.Symbolic")]
109    [TestProperty("Time", "long")]
110    public void LinearInterpreterTestArithmeticGrammarPerformance() {
111      TestArithmeticGrammarPerformance(new SymbolicDataAnalysisExpressionTreeLinearInterpreter(), 12.5e6);
112    }
113
114    [TestMethod]
115    [TestCategory("Problems.DataAnalysis.Symbolic")]
116    [TestProperty("Time", "long")]
117    public void ExpressionTreeInterpreterTestArithmeticGrammarPerformance() {
118      TestArithmeticGrammarPerformance(new ExpressionTreeInterpreter(), 12.5e6);
119    }
120
121    [TestMethod]
122    [TestCategory("Problems.DataAnalysis.Symbolic")]
123    [TestProperty("Time", "long")]
124    public void ExpressionTreeInterpreterTestCompilationPerformance() {
125      var twister = new MersenneTwister(31415);
126      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
127      var grammar = new ArithmeticExpressionGrammar();
128      //grammar.Symbols.OfType<Variable>().First().Enabled = false;
129      grammar.MaximumFunctionArguments = 0;
130      grammar.MaximumFunctionDefinitions = 0;
131      grammar.MinimumFunctionArguments = 0;
132      grammar.MinimumFunctionDefinitions = 0;
133
134      var trees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0).ToArray();
135      foreach (SymbolicExpressionTree tree in trees)
136        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
137
138
139      var data = dataset.DoubleVariables.ToDictionary(key => key, v => (IList<double>)dataset.GetReadOnlyDoubleValues(v));
140      var param = Expression.Parameter(typeof(int), "row");
141
142      Stopwatch watch = new Stopwatch();
143      int repetitions = 3;
144
145      long nNodes = trees.Aggregate<ISymbolicExpressionTree, long>(0, (current, t) => current + t.Length);
146
147      for (int rep = 0; rep < repetitions; rep++) {
148        watch.Start();
149        foreach (ISymbolicExpressionTree t in trees) {
150          var exp = ExpressionTreeInterpreter.CreateExpression(t.Root.GetSubtree(0).GetSubtree(0), data, param);
151          while (exp.CanReduce) exp = exp.Reduce();
152          var function = Expression.Lambda<Func<int, double>>(exp, param).Compile();
153          function(0);
154        }
155        watch.Stop();
156      }
157      Console.WriteLine("Random tree compilation performance of expression tree interpreter: " + Environment.NewLine +
158        watch.ElapsedMilliseconds + "ms " + Environment.NewLine +
159        Util.NodesPerSecond(nNodes * repetitions, watch) + " nodes/sec" + Environment.NewLine +
160        Util.NodesPerSecond(trees.Length * repetitions, watch) + " trees/sec"
161        );
162
163    }
164
165
166    private void TestTypeCoherentGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
167      var twister = new MersenneTwister(31415);
168      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
169      var grammar = new TypeCoherentExpressionGrammar();
170      grammar.ConfigureAsDefaultRegressionGrammar();
171      grammar.MaximumFunctionArguments = 0;
172      grammar.MaximumFunctionDefinitions = 0;
173      grammar.MinimumFunctionArguments = 0;
174      grammar.MinimumFunctionDefinitions = 0;
175      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
176      foreach (ISymbolicExpressionTree tree in randomTrees) {
177        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
178      }
179      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
180      //mkommend: commented due to performance issues on the builder
181      // Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
182    }
183
184    private void TestFullGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
185      var twister = new MersenneTwister(31415);
186      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
187      var grammar = new FullFunctionalExpressionGrammar();
188      grammar.MaximumFunctionArguments = 0;
189      grammar.MaximumFunctionDefinitions = 0;
190      grammar.MinimumFunctionArguments = 0;
191      grammar.MinimumFunctionDefinitions = 0;
192      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
193      foreach (ISymbolicExpressionTree tree in randomTrees) {
194        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
195      }
196      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
197      //mkommend: commented due to performance issues on the builder
198      //Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
199    }
200
201    private void TestArithmeticGrammarPerformance(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, double nodesPerSecThreshold) {
202      var twister = new MersenneTwister(31415);
203      var dataset = Util.CreateRandomDataset(twister, Rows, Columns);
204      var grammar = new ArithmeticExpressionGrammar();
205      //grammar.Symbols.OfType<Variable>().First().Enabled = false;
206      grammar.MaximumFunctionArguments = 0;
207      grammar.MaximumFunctionDefinitions = 0;
208      grammar.MinimumFunctionArguments = 0;
209      grammar.MinimumFunctionDefinitions = 0;
210      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 100, 0, 0);
211      foreach (SymbolicExpressionTree tree in randomTrees) {
212        Util.InitTree(tree, twister, new List<string>(dataset.VariableNames));
213      }
214
215      double nodesPerSec = Util.CalculateEvaluatedNodesPerSec(randomTrees, interpreter, dataset, 3);
216      //mkommend: commented due to performance issues on the builder
217      //Assert.IsTrue(nodesPerSec > nodesPerSecThreshold); // evaluated nodes per seconds must be larger than 15mNodes/sec
218    }
219
220
221    /// <summary>
222    ///A test for Evaluate
223    ///</summary>
224    [TestMethod]
225    [TestCategory("Problems.DataAnalysis.Symbolic")]
226    [TestProperty("Time", "short")]
227    public void StandardInterpreterTestEvaluation() {
228      var interpreter = new SymbolicDataAnalysisExpressionTreeInterpreter();
229      EvaluateTerminals(interpreter, ds);
230      EvaluateOperations(interpreter, ds);
231      EvaluateAdf(interpreter, ds);
232    }
233
234    [TestMethod]
235    [TestCategory("Problems.DataAnalysis.Symbolic")]
236    [TestProperty("Time", "short")]
237    public void ILEmittingInterpreterTestEvaluation() {
238      var interpreter = new SymbolicDataAnalysisExpressionTreeILEmittingInterpreter();
239      EvaluateTerminals(interpreter, ds);
240      EvaluateOperations(interpreter, ds);
241    }
242
243    [TestMethod]
244    [TestCategory("Problems.DataAnalysis.Symbolic")]
245    [TestProperty("Time", "short")]
246    public void LinearInterpreterTestEvaluation() {
247      var interpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
248
249      //ADFs are not supported by the linear interpreter
250      EvaluateTerminals(interpreter, ds);
251      EvaluateOperations(interpreter, ds);
252    }
253
254    private void EvaluateTerminals(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, Dataset ds) {
255      // constants
256      Evaluate(interpreter, ds, "(+ 1.5 3.5)", 0, 5.0);
257
258      // variables
259      Evaluate(interpreter, ds, "(variable 2.0 a)", 0, 2.0);
260      Evaluate(interpreter, ds, "(variable 2.0 a)", 1, 4.0);
261    }
262
263    private void EvaluateAdf(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, Dataset ds) {
264
265      // ADF     
266      Evaluate(interpreter, ds, @"(PROG
267                                    (MAIN
268                                      (CALL ADF0))
269                                    (defun ADF0 1.0))", 1, 1.0);
270      Evaluate(interpreter, ds, @"(PROG
271                                    (MAIN
272                                      (* (CALL ADF0) (CALL ADF0)))
273                                    (defun ADF0 2.0))", 1, 4.0);
274      Evaluate(interpreter, ds, @"(PROG
275                                    (MAIN
276                                      (CALL ADF0 2.0 3.0))
277                                    (defun ADF0
278                                      (+ (ARG 0) (ARG 1))))", 1, 5.0);
279      Evaluate(interpreter, ds, @"(PROG
280                                    (MAIN (CALL ADF1 2.0 3.0))
281                                    (defun ADF0
282                                      (- (ARG 1) (ARG 0)))
283                                    (defun ADF1
284                                      (+ (CALL ADF0 (ARG 1) (ARG 0))
285                                         (CALL ADF0 (ARG 0) (ARG 1)))))", 1, 0.0);
286      Evaluate(interpreter, ds, @"(PROG
287                                    (MAIN (CALL ADF1 (variable 2.0 a) 3.0))
288                                    (defun ADF0
289                                      (- (ARG 1) (ARG 0)))
290                                    (defun ADF1                                                                             
291                                      (CALL ADF0 (ARG 1) (ARG 0))))", 1, 1.0);
292      Evaluate(interpreter, ds,
293               @"(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))
299                                         (CALL ADF0 (ARG 0) (ARG 1)))))", 1, 0.0);
300    }
301
302    private void EvaluateOperations(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, Dataset ds) {
303      // addition
304      Evaluate(interpreter, ds, "(+ (variable 2.0 a ))", 1, 4.0);
305      Evaluate(interpreter, ds, "(+ (variable 2.0 a ) (variable 3.0 b ))", 0, 5.0);
306      Evaluate(interpreter, ds, "(+ (variable 2.0 a ) (variable 3.0 b ))", 1, 10.0);
307      Evaluate(interpreter, ds, "(+ (variable 2.0 a) (variable 3.0 b ))", 2, 8.0);
308      Evaluate(interpreter, ds, "(+ 8.0 2.0 2.0)", 0, 12.0);
309
310      // subtraction
311      Evaluate(interpreter, ds, "(- (variable 2.0 a ))", 1, -4.0);
312      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b))", 0, -1.0);
313      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b ))", 1, -2.0);
314      Evaluate(interpreter, ds, "(- (variable 2.0 a ) (variable 3.0 b ))", 2, -4.0);
315      Evaluate(interpreter, ds, "(- 8.0 2.0 2.0)", 0, 4.0);
316
317      // multiplication
318      Evaluate(interpreter, ds, "(* (variable 2.0 a ))", 0, 2.0);
319      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 0, 6.0);
320      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 1, 24.0);
321      Evaluate(interpreter, ds, "(* (variable 2.0 a ) (variable 3.0 b ))", 2, 12.0);
322      Evaluate(interpreter, ds, "(* 8.0 2.0 2.0)", 0, 32.0);
323
324      // division
325      Evaluate(interpreter, ds, "(/ (variable 2.0 a ))", 1, 1.0 / 4.0);
326      Evaluate(interpreter, ds, "(/ (variable 2.0 a ) 2.0)", 0, 1.0);
327      Evaluate(interpreter, ds, "(/ (variable 2.0 a ) 2.0)", 1, 2.0);
328      Evaluate(interpreter, ds, "(/ (variable 3.0 b ) 2.0)", 2, 3.0);
329      Evaluate(interpreter, ds, "(/ 8.0 2.0 2.0)", 0, 2.0);
330
331      // gt
332      Evaluate(interpreter, ds, "(> (variable 2.0 a) 2.0)", 0, -1.0);
333      Evaluate(interpreter, ds, "(> 2.0 (variable 2.0 a))", 0, -1.0);
334      Evaluate(interpreter, ds, "(> (variable 2.0 a) 1.9)", 0, 1.0);
335      Evaluate(interpreter, ds, "(> 1.9 (variable 2.0 a))", 0, -1.0);
336      Evaluate(interpreter, ds, "(> (log -1.0) (log -1.0))", 0, -1.0); // (> nan nan) should be false
337
338      // lt
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      // If
346      Evaluate(interpreter, ds, "(if -10.0 2.0 3.0)", 0, 3.0);
347      Evaluate(interpreter, ds, "(if -1.0 2.0 3.0)", 0, 3.0);
348      Evaluate(interpreter, ds, "(if 0.0 2.0 3.0)", 0, 3.0);
349      Evaluate(interpreter, ds, "(if 1.0 2.0 3.0)", 0, 2.0);
350      Evaluate(interpreter, ds, "(if 10.0 2.0 3.0)", 0, 2.0);
351      Evaluate(interpreter, ds, "(if (log -1.0) 2.0 3.0)", 0, 3.0); // if(nan) should return the else branch
352
353      // NOT
354      Evaluate(interpreter, ds, "(not -1.0)", 0, 1.0);
355      Evaluate(interpreter, ds, "(not -2.0)", 0, 1.0);
356      Evaluate(interpreter, ds, "(not 1.0)", 0, -1.0);
357      Evaluate(interpreter, ds, "(not 2.0)", 0, -1.0);
358      Evaluate(interpreter, ds, "(not 0.0)", 0, 1.0);
359      Evaluate(interpreter, ds, "(not (log -1.0))", 0, 1.0);
360
361      // AND
362      Evaluate(interpreter, ds, "(and -1.0 -2.0)", 0, -1.0);
363      Evaluate(interpreter, ds, "(and -1.0 2.0)", 0, -1.0);
364      Evaluate(interpreter, ds, "(and 1.0 -2.0)", 0, -1.0);
365      Evaluate(interpreter, ds, "(and 1.0 0.0)", 0, -1.0);
366      Evaluate(interpreter, ds, "(and 0.0 0.0)", 0, -1.0);
367      Evaluate(interpreter, ds, "(and 1.0 2.0)", 0, 1.0);
368      Evaluate(interpreter, ds, "(and 1.0 2.0 3.0)", 0, 1.0);
369      Evaluate(interpreter, ds, "(and 1.0 -2.0 3.0)", 0, -1.0);
370      Evaluate(interpreter, ds, "(and (log -1.0))", 0, -1.0); // (and NaN)
371      Evaluate(interpreter, ds, "(and (log -1.0)  1.0)", 0, -1.0); // (and NaN 1.0)
372
373
374      // OR
375      Evaluate(interpreter, ds, "(or -1.0 -2.0)", 0, -1.0);
376      Evaluate(interpreter, ds, "(or -1.0 2.0)", 0, 1.0);
377      Evaluate(interpreter, ds, "(or 1.0 -2.0)", 0, 1.0);
378      Evaluate(interpreter, ds, "(or 1.0 2.0)", 0, 1.0);
379      Evaluate(interpreter, ds, "(or 0.0 0.0)", 0, -1.0);
380      Evaluate(interpreter, ds, "(or -1.0 -2.0 -3.0)", 0, -1.0);
381      Evaluate(interpreter, ds, "(or -1.0 -2.0 3.0)", 0, 1.0);
382      Evaluate(interpreter, ds, "(or (log -1.0))", 0, -1.0); // (or NaN)
383      Evaluate(interpreter, ds, "(or (log -1.0)  1.0)", 0, -1.0); // (or NaN 1.0)
384
385      // sin, cos, tan
386      Evaluate(interpreter, ds, "(sin " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, 0.0);
387      Evaluate(interpreter, ds, "(sin 0.0)", 0, 0.0);
388      Evaluate(interpreter, ds, "(cos " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, -1.0);
389      Evaluate(interpreter, ds, "(cos 0.0)", 0, 1.0);
390      Evaluate(interpreter, ds, "(tan " + Math.PI.ToString(NumberFormatInfo.InvariantInfo) + ")", 0, Math.Tan(Math.PI));
391      Evaluate(interpreter, ds, "(tan 0.0)", 0, Math.Tan(Math.PI));
392
393      // exp, log
394      Evaluate(interpreter, ds, "(log (exp 7.0))", 0, Math.Log(Math.Exp(7)));
395      Evaluate(interpreter, ds, "(exp (log 7.0))", 0, Math.Exp(Math.Log(7)));
396      Evaluate(interpreter, ds, "(log -3.0)", 0, Math.Log(-3));
397
398      // power
399      Evaluate(interpreter, ds, "(pow 2.0 3.0)", 0, 8.0);
400      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)
401      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)
402      Evaluate(interpreter, ds, "(pow -2.0 3.0)", 0, -8.0);
403      Evaluate(interpreter, ds, "(pow 2.0 -3.0)", 0, 1.0 / 8.0);
404      Evaluate(interpreter, ds, "(pow -2.0 -3.0)", 0, -1.0 / 8.0);
405
406      // root
407      Evaluate(interpreter, ds, "(root 9.0 2.0)", 0, 3.0);
408      Evaluate(interpreter, ds, "(root 27.0 3.0)", 0, 3.0);
409      Evaluate(interpreter, ds, "(root 2.0 -3.0)", 0, Math.Pow(2.0, -1.0 / 3.0));
410
411      // mean
412      Evaluate(interpreter, ds, "(mean -1.0 1.0 -1.0)", 0, -1.0 / 3.0);
413
414      // lag
415      Evaluate(interpreter, ds, "(lagVariable 1.0 a -1) ", 1, ds.GetDoubleValue("A", 0));
416      Evaluate(interpreter, ds, "(lagVariable 1.0 a -1) ", 2, ds.GetDoubleValue("A", 1));
417      Evaluate(interpreter, ds, "(lagVariable 1.0 a 0) ", 2, ds.GetDoubleValue("A", 2));
418      Evaluate(interpreter, ds, "(lagVariable 1.0 a 1) ", 0, ds.GetDoubleValue("A", 1));
419
420      // integral
421      Evaluate(interpreter, ds, "(integral -1.0 (variable 1.0 a)) ", 1, ds.GetDoubleValue("A", 0) + ds.GetDoubleValue("A", 1));
422      Evaluate(interpreter, ds, "(integral -1.0 (lagVariable 1.0 a 1)) ", 1, ds.GetDoubleValue("A", 1) + ds.GetDoubleValue("A", 2));
423      Evaluate(interpreter, ds, "(integral -2.0 (variable 1.0 a)) ", 2, ds.GetDoubleValue("A", 0) + ds.GetDoubleValue("A", 1) + ds.GetDoubleValue("A", 2));
424      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));
425      Evaluate(interpreter, ds, "(integral -2.0 3.0)", 1, 9.0);
426
427      // derivative
428      // (f_0 + 2 * f_1 - 2 * f_3 - f_4) / 8; // h = 1
429      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);
430      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);
431      Evaluate(interpreter, ds, "(diff (* (variable 1.0 a) (variable 1.0 b)))", 5, +
432        (ds.GetDoubleValue("A", 5) * ds.GetDoubleValue("B", 5) +
433        2 * ds.GetDoubleValue("A", 4) * ds.GetDoubleValue("B", 4) -
434        2 * ds.GetDoubleValue("A", 2) * ds.GetDoubleValue("B", 2) -
435        ds.GetDoubleValue("A", 1) * ds.GetDoubleValue("B", 1)) / 8.0);
436      Evaluate(interpreter, ds, "(diff -2.0 3.0)", 5, 0.0);
437
438      // timelag
439      Evaluate(interpreter, ds, "(lag -1.0 (lagVariable 1.0 a 2)) ", 1, ds.GetDoubleValue("A", 2));
440      Evaluate(interpreter, ds, "(lag -2.0 (lagVariable 1.0 a 2)) ", 2, ds.GetDoubleValue("A", 2));
441      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));
442      Evaluate(interpreter, ds, "(lag -2.0 3.0)", 1, 3.0);
443
444      {
445        // special functions
446        Action<double> checkAiry = (x) => {
447          double ai, aip, bi, bip;
448          alglib.airy(x, out ai, out aip, out bi, out bip);
449          Evaluate(interpreter, ds, "(airya " + x + ")", 0, ai);
450          Evaluate(interpreter, ds, "(airyb " + x + ")", 0, bi);
451        };
452
453        Action<double> checkBessel = (x) => {
454          Evaluate(interpreter, ds, "(bessel " + x + ")", 0, alglib.besseli0(x));
455        };
456
457        Action<double> checkSinCosIntegrals = (x) => {
458          double si, ci;
459          alglib.sinecosineintegrals(x, out si, out ci);
460          Evaluate(interpreter, ds, "(cosint " + x + ")", 0, ci);
461          Evaluate(interpreter, ds, "(sinint " + x + ")", 0, si);
462        };
463        Action<double> checkHypSinCosIntegrals = (x) => {
464          double shi, chi;
465          alglib.hyperbolicsinecosineintegrals(x, out shi, out chi);
466          Evaluate(interpreter, ds, "(hypcosint " + x + ")", 0, chi);
467          Evaluate(interpreter, ds, "(hypsinint " + x + ")", 0, shi);
468        };
469        Action<double> checkFresnelSinCosIntegrals = (x) => {
470          double c = 0, s = 0;
471          alglib.fresnelintegral(x, ref c, ref s);
472          Evaluate(interpreter, ds, "(fresnelcosint " + x + ")", 0, c);
473          Evaluate(interpreter, ds, "(fresnelsinint " + x + ")", 0, s);
474        };
475        Action<double> checkNormErf = (x) => {
476          Evaluate(interpreter, ds, "(norm " + x + ")", 0, alglib.normaldistribution(x));
477          Evaluate(interpreter, ds, "(erf " + x + ")", 0, alglib.errorfunction(x));
478        };
479
480        Action<double> checkGamma = (x) => {
481          Evaluate(interpreter, ds, "(gamma " + x + ")", 0, alglib.gammafunction(x));
482        };
483        Action<double> checkPsi = (x) => {
484          try {
485            Evaluate(interpreter, ds, "(psi " + x + ")", 0, alglib.psi(x));
486          }
487          catch (alglib.alglibexception) { // ignore cases where alglib throws an exception
488          }
489        };
490        Action<double> checkDawson = (x) => {
491          Evaluate(interpreter, ds, "(dawson " + x + ")", 0, alglib.dawsonintegral(x));
492        };
493        Action<double> checkExpInt = (x) => {
494          Evaluate(interpreter, ds, "(expint " + x + ")", 0, alglib.exponentialintegralei(x));
495        };
496
497
498
499        foreach (var e in new[] { -2.0, -1.0, 0.0, 1.0, 2.0 }) {
500          checkAiry(e);
501          checkBessel(e);
502          checkSinCosIntegrals(e);
503          checkGamma(e);
504          checkExpInt(e);
505          checkDawson(e);
506          checkPsi(e);
507          checkNormErf(e);
508          checkFresnelSinCosIntegrals(e);
509          checkHypSinCosIntegrals(e);
510        }
511      }
512    }
513
514    private void Evaluate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, Dataset ds, string expr, int index, double expected) {
515      var importer = new SymbolicExpressionImporter();
516      ISymbolicExpressionTree tree = importer.Import(expr);
517
518      double actual = interpreter.GetSymbolicExpressionTreeValues(tree, ds, Enumerable.Range(index, 1)).First();
519
520      Assert.IsFalse(double.IsNaN(actual) && !double.IsNaN(expected));
521      Assert.IsFalse(!double.IsNaN(actual) && double.IsNaN(expected));
522      Assert.AreEqual(expected, actual, 1.0E-12, expr);
523    }
524  }
525}
Note: See TracBrowser for help on using the repository browser.