Changeset 13313


Ignore:
Timestamp:
11/20/15 00:50:47 (4 years ago)
Author:
bburlacu
Message:

#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.

Location:
trunk/sources
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionCompiledTreeInterpreter.cs

    r13288 r13313  
    133133
    134134    public static Func<int, IList<double>[], double> CompileTree(ISymbolicExpressionTree tree, IDataset dataset) {
     135      var lambda = CreateDelegate(tree, dataset);
     136      return lambda.Compile();
     137    }
     138
     139    public static Expression<Func<int, IList<double>[], double>> CreateDelegate(ISymbolicExpressionTree tree, IDataset dataset) {
    135140      var row = Expression.Parameter(typeof(int));
    136141      var columns = Expression.Parameter(typeof(IList<double>[]));
     
    138143      var expr = MakeExpr(tree, variableIndices, row, columns);
    139144      var lambda = Expression.Lambda<Func<int, IList<double>[], double>>(expr, row, columns);
    140       return lambda.Compile();
     145      return lambda;
    141146    }
    142147
     
    243248            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
    244249            var isNaN = Expression.Call(IsNaN, arg);
    245             var gamma = Expression.Call(Gamma, arg);
    246250
    247251            var result = Expression.Variable(typeof(double));
     
    251255                isNaN,
    252256                Expression.Assign(result, Expression.Constant(double.NaN)),
    253                 Expression.Assign(result, gamma)
     257                Expression.Assign(result, Expression.Call(Gamma, arg))
    254258                ),
    255259              result
     
    260264            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
    261265            var isNaN = Expression.Call(IsNaN, arg);
    262             var psi = Expression.Call(Psi, arg);
    263266
    264267            var result = Expression.Variable(typeof(double));
     
    274277                    Expression.Call(IsAlmost, Expression.Subtract(floor, arg), Expression.Constant(0.0))),
    275278                  Expression.Assign(result, Expression.Constant(double.NaN)),
    276                   Expression.Assign(result, psi))
     279                  Expression.Assign(result, Expression.Call(Psi, arg)))
    277280                ),
    278281              result);
     
    283286            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
    284287            var isNaN = Expression.Call(IsNaN, arg);
    285             var exprDawsonIntegral = Expression.Call(DawsonIntegral, arg);
    286             var result = Expression.Variable(typeof(double));
    287 
     288            var result = Expression.Variable(typeof(double));
    288289            var expr = Expression.Block(
    289290              new[] { result },
    290291              Expression.IfThenElse(isNaN,
    291292                Expression.Assign(result, Expression.Constant(double.NaN)),
    292                 Expression.Assign(result, exprDawsonIntegral)),
     293                Expression.Assign(result, Expression.Call(DawsonIntegral, arg))),
    293294              result
    294295              );
     
    299300            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
    300301            var isNaN = Expression.Call(IsNaN, arg);
    301             var expIntegrapEi = Expression.Call(ExponentialIntegralEi, arg);
    302302            var result = Expression.Variable(typeof(double));
    303303            var expr = Expression.Block(
     
    305305              Expression.IfThenElse(isNaN,
    306306                Expression.Assign(result, Expression.Constant(double.NaN)),
    307                 Expression.Assign(result, expIntegrapEi)),
     307                Expression.Assign(result, Expression.Call(ExponentialIntegralEi, arg))),
    308308              result
    309309              );
     
    316316            var si = Expression.Variable(typeof(double));
    317317            var ci = Expression.Variable(typeof(double));
    318             var sinCosIntegrals = Expression.Call(SineCosineIntegrals, arg, si, ci);
    319318            var block = Expression.Block(
    320319              new[] { si, ci },
    321               sinCosIntegrals,
     320              Expression.Call(SineCosineIntegrals, arg, si, ci),
    322321              si
    323322              );
     
    337336            var si = Expression.Variable(typeof(double));
    338337            var ci = Expression.Variable(typeof(double));
    339             var sinCosIntegrals = Expression.Call(SineCosineIntegrals, arg, si, ci);
    340338            var block = Expression.Block(
    341339              new[] { si, ci },
    342               sinCosIntegrals,
     340              Expression.Call(SineCosineIntegrals, arg, si, ci),
    343341              ci
    344342              );
     
    398396            var s = Expression.Variable(typeof(double));
    399397            var c = Expression.Variable(typeof(double));
    400             var fresnel = Expression.Call(FresnelIntegral, arg, c, s);
    401             var block = Expression.Block(new[] { s, c }, fresnel, s);
     398            var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), s);
    402399            var result = Expression.Variable(typeof(double));
    403400            var expr = Expression.Block(new[] { result },
     
    415412            var s = Expression.Variable(typeof(double));
    416413            var c = Expression.Variable(typeof(double));
    417             var fresnel = Expression.Call(FresnelIntegral, arg, c, s);
    418             var block = Expression.Block(new[] { s, c }, fresnel, c);
     414            var block = Expression.Block(new[] { s, c }, Expression.Call(FresnelIntegral, arg, c, s), c);
    419415            var result = Expression.Variable(typeof(double));
    420416            var expr = Expression.Block(new[] { result },
     
    434430            var bi = Expression.Variable(typeof(double));
    435431            var bip = Expression.Variable(typeof(double));
    436             var airy = Expression.Call(Airy, arg, ai, aip, bi, bip);
    437             var block = Expression.Block(new[] { ai, aip, bi, bip }, airy, ai);
     432            var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), ai);
    438433            var result = Expression.Variable(typeof(double));
    439434            var expr = Expression.Block(new[] { result },
     
    453448            var bi = Expression.Variable(typeof(double));
    454449            var bip = Expression.Variable(typeof(double));
    455             var airy = Expression.Call(Airy, arg, ai, aip, bi, bip);
    456             var block = Expression.Block(new[] { ai, aip, bi, bip }, airy, bi);
     450            var block = Expression.Block(new[] { ai, aip, bi, bip }, Expression.Call(Airy, arg, ai, aip, bi, bip), bi);
    457451            var result = Expression.Variable(typeof(double));
    458452            var expr = Expression.Block(new[] { result },
     
    467461        case OpCodes.Norm: {
    468462            var arg = MakeExpr(node.GetSubtree(0), variableIndices, row, columns);
    469             var isNaN = Expression.Call(IsNaN, arg);
    470463            var result = Expression.Variable(typeof(double));
    471464            return Expression.Block(
    472465              new[] { result },
    473466              Expression.IfThenElse(
    474                 isNaN,
    475                 Expression.Assign(result, Expression.Constant(double.NaN)),
     467                Expression.Call(IsNaN, arg),
     468                Expression.Assign(result, arg),
    476469                Expression.Assign(result, Expression.Call(NormalDistribution, arg))),
    477470              result);
  • trunk/sources/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/SymbolicDataAnalysisExpressionTreeInterpreterTest.cs

    r13256 r13313  
    2222using System;
    2323using System.Collections.Generic;
     24using System.Diagnostics;
    2425using System.Globalization;
    2526using System.Linq;
     
    220221    [TestProperty("Time", "long")]
    221222    public void TestInterpreterEvaluationResults() {
    222 
    223223      var twister = new MersenneTwister();
    224224      int seed = twister.Next(0, int.MaxValue);
    225225      twister.Seed((uint)seed);
    226 
    227226      const int numRows = 100;
    228227      var dataset = Util.CreateRandomDataset(twister, numRows, Columns);
    229228
    230229      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();
    231237      var randomTrees = Util.CreateRandomTrees(twister, dataset, grammar, N, 1, 10, 0, 0);
    232238      foreach (ISymbolicExpressionTree tree in randomTrees) {
     
    234240      }
    235241
    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();
    244242      for (int i = 0; i < randomTrees.Length; ++i) {
    245243        var tree = randomTrees[i];
     
    251249            if (double.IsNaN(sum) && double.IsNaN(s)) continue;
    252250
    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);
     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            }
    256337          }
    257338        }
     
    432513
    433514      // 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));
     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));
    438519
    439520      // 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);
     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);
    445526
    446527      // derivative
    447528      // (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);
     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);
    456537
    457538      // 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);
     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);
    462543
    463544      {
Note: See TracChangeset for help on using the changeset viewer.