Index: branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/IGrammarEnumerationEvaluator.cs
===================================================================
--- branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/IGrammarEnumerationEvaluator.cs	(revision 16151)
+++ branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/Analysis/IGrammarEnumerationEvaluator.cs	(revision 16157)
@@ -21,4 +21,5 @@
 
 using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
 using HeuristicLab.Problems.DataAnalysis;
 
@@ -26,4 +27,6 @@
   public interface IGrammarEnumerationEvaluator : IItem {
     double Evaluate(IRegressionProblemData problemData, Grammar grammar, SymbolList sentence);
+    double Evaluate(IRegressionProblemData problemData, ISymbolicExpressionTree tree);
+    bool OptimizeConstants { get; set; }
   }
 }
Index: branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs
===================================================================
--- branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs	(revision 16151)
+++ branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/GrammarEnumerationAlgorithm.cs	(revision 16157)
@@ -52,8 +52,7 @@
 
     private readonly string EvaluatorParameterName = "Evaluator";
-
-    private readonly string ErrorWeightParameterName = "Error Weight";
     private readonly string SearchDataStructureParameterName = "Search Data Structure";
     private readonly string MaxComplexityParameterName = "Max. Complexity";
+    private readonly string MaxLengthParameterName = "Max. Length";
     private readonly string GuiUpdateIntervalParameterName = "GUI Update Interval";
     private readonly string GrammarSymbolsParameterName = "Grammar Symbols";
@@ -67,9 +66,9 @@
     public override bool SupportsPause { get { return true; } }
 
-    public IFixedValueParameter<RSquaredEvaluator> EvaluatorParameter {
-      get { return (IFixedValueParameter<RSquaredEvaluator>)Parameters[EvaluatorParameterName]; }
-    }
-
-    public RSquaredEvaluator Evaluator {
+    public IValueParameter<IGrammarEnumerationEvaluator> EvaluatorParameter {
+      get { return (IValueParameter<IGrammarEnumerationEvaluator>)Parameters[EvaluatorParameterName]; }
+    }
+
+    public IGrammarEnumerationEvaluator Evaluator {
       get { return EvaluatorParameter.Value; }
     }
@@ -77,4 +76,8 @@
     protected IFixedValueParameter<IntValue> MaxComplexityParameter {
       get { return (IFixedValueParameter<IntValue>)Parameters[MaxComplexityParameterName]; }
+    }
+
+    protected IFixedValueParameter<IntValue> MaxLengthParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[MaxLengthParameterName]; }
     }
 
@@ -84,11 +87,7 @@
     }
 
-    protected IFixedValueParameter<DoubleValue> ErrorWeightParameter {
-      get { return (IFixedValueParameter<DoubleValue>)Parameters[ErrorWeightParameterName]; }
-    }
-
-    public double ErrorWeight {
-      get { return ErrorWeightParameter.Value.Value; }
-      set { ErrorWeightParameter.Value.Value = value; }
+    public int MaxLength {
+      get { return MaxLengthParameter.Value.Value; }
+      set { MaxLengthParameter.Value.Value = value; }
     }
 
@@ -200,10 +199,10 @@
 
     public GrammarEnumerationAlgorithm() {
-      Parameters.Add(new FixedValueParameter<DoubleValue>(ErrorWeightParameterName, "Defines, how much weight is put on a phrase's r² value when priorizing phrases during search.", new DoubleValue(0.8)));
       Parameters.Add(new FixedValueParameter<IntValue>(MaxComplexityParameterName, "The maximum number of variable symbols in a sentence.", new IntValue(12)));
+      Parameters.Add(new FixedValueParameter<IntValue>(MaxLengthParameterName, "The maximum number of variable symbols in a sentence.", new IntValue(20)));
       Parameters.Add(new FixedValueParameter<IntValue>(GuiUpdateIntervalParameterName, "Number of generated sentences, until GUI is refreshed.", new IntValue(5000)));
       Parameters.Add(new FixedValueParameter<IntValue>(SearchDataStructureSizeParameterName, "The size of the search data structure.", new IntValue((int)1e5)));
       Parameters.Add(new FixedValueParameter<EnumValue<StorageType>>(SearchDataStructureParameterName, new EnumValue<StorageType>(StorageType.SortedSet)));
-      Parameters.Add(new FixedValueParameter<RSquaredEvaluator>(EvaluatorParameterName, new RSquaredEvaluator()));
+      Parameters.Add(new ValueParameter<IGrammarEnumerationEvaluator>(EvaluatorParameterName, new RSquaredEvaluator()));
 
       SearchDataStructureParameter.Value.ValueChanged += (o, e) => Prepare();
@@ -283,8 +282,9 @@
       }
 
-      MaxSentenceLength = Grammar.GetMaxSentenceLength(MaxComplexity);
-      var errorWeight = ErrorWeight;
       var evaluator = EvaluatorParameter.Value;
       var problemData = Problem.ProblemData;
+
+      int maxLength = MaxLength;
+      int maxComplexity = MaxComplexity;
 
       // main search loop
@@ -313,7 +313,10 @@
 
           SymbolList newPhrase = currPhrase.DerivePhrase(nonterminalSymbolIndex, appliedProductions[i]);
+
+          if (newPhrase.Count > maxLength)
+            continue;
+
           int newPhraseComplexity = newPhrase.Complexity;
-
-          if (newPhraseComplexity > MaxComplexity)
+          if (newPhraseComplexity > maxComplexity)
             continue;
 
@@ -327,21 +330,10 @@
             OnSentenceGenerated(fetchedSearchNode.Hash, fetchedSearchNode.SymbolList, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
 
-            // Is the best solution found? (only if RSquaredEvaluator is activated)
-            //if (Results.ContainsKey(RSquaredEvaluator.BestTrainingQualityResultName)) {
-            //  double r2 = ((DoubleValue)Results[RSquaredEvaluator.BestTrainingQualityResultName].Value).Value;
-            //  if (r2.IsAlmost(1.0)) {
-            //    UpdateView(force: true);
-            //    return;
-            //  }
-            //}
-
             if (!DistinctSentencesComplexity.ContainsKey(phraseHash) || DistinctSentencesComplexity[phraseHash] > newPhraseComplexity) {
               if (DistinctSentencesComplexity.ContainsKey(phraseHash)) OverwrittenSentencesCount++; // for analysis only
-
               DistinctSentencesComplexity[phraseHash] = newPhraseComplexity;
               OnDistinctSentenceGenerated(fetchedSearchNode.Hash, fetchedSearchNode.SymbolList, phraseHash, newPhrase, expandedSymbol, appliedProductions[i]);
             }
             UpdateView();
-
           } else if (!OpenPhrases.Contains(phraseHash) && !ArchivedPhrases.Contains(phraseHash)) {
             double r2 = IsCompleteSentence(newPhrase) ? evaluator.Evaluate(problemData, Grammar, newPhrase) : fetchedSearchNode.R2;
@@ -397,17 +389,5 @@
       var tree = Grammar.ParseSymbolicExpressionTree(BestTrainingSentence);
       var model = new SymbolicRegressionModel(Problem.ProblemData.TargetVariable, tree, interpreter);
-
-      var iterations = EvaluatorParameter.Value.ConstantOptimizationIterations;
-      var applyLinearScaling = EvaluatorParameter.Value.ApplyLinearScaling;
-
-      SymbolicRegressionConstantOptimizationEvaluator.OptimizeConstants(
-        interpreter,
-        model.SymbolicExpressionTree,
-        Problem.ProblemData,
-        Problem.ProblemData.TrainingIndices,
-        applyLinearScaling: applyLinearScaling,
-        maxIterations: iterations,
-        updateVariableWeights: false,
-        updateConstantsInTree: true);
+      Evaluator.Evaluate(Problem.ProblemData, tree); // this call will optimize the constants in the tree (if enabled)
 
       model.Scale(Problem.ProblemData);
Index: branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/RSquaredEvaluator.cs
===================================================================
--- branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/RSquaredEvaluator.cs	(revision 16151)
+++ branches/2886_SymRegGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration/GrammarEnumeration/RSquaredEvaluator.cs	(revision 16157)
@@ -61,7 +61,16 @@
     }
 
+    private IFixedValueParameter<IntValue> SeedParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[SeedParameterName]; }
+    }
+
     private int Restarts {
       get { return RestartsParameter.Value.Value; }
       set { RestartsParameter.Value.Value = value; }
+    }
+
+    private int Seed {
+      get { return SeedParameter.Value.Value; }
+      set { SeedParameter.Value.Value = value; }
     }
 
@@ -113,4 +122,5 @@
 
     public double Evaluate(IRegressionProblemData problemData, ISymbolicExpressionTree tree) {
+      random.Seed((uint)Seed); // not the ideal solution for ensuring result consistency
       return Evaluate(problemData, tree, random, OptimizeConstants, ConstantOptimizationIterations, ApplyLinearScaling, Restarts);
     }
Index: branches/2886_SymRegGrammarEnumeration/Test/AlgorithmPerformanceTest.cs
===================================================================
--- branches/2886_SymRegGrammarEnumeration/Test/AlgorithmPerformanceTest.cs	(revision 16157)
+++ branches/2886_SymRegGrammarEnumeration/Test/AlgorithmPerformanceTest.cs	(revision 16157)
@@ -0,0 +1,76 @@
+﻿using System;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
+using HeuristicLab.Problems.Instances.DataAnalysis;
+using HeuristicLab.Random;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Test {
+  [TestClass]
+  public class AlgorithmPerformanceTest {
+    private const int Seed = 1234;
+    private IRandom rand;
+
+    private const double SuccessThreshold = 0.9999999;
+
+    private GrammarEnumerationAlgorithm alg;
+    private RegressionProblem problem;
+
+    [TestInitialize]
+    public void InitTest() {
+      rand = new FastRandom(Seed);
+      alg = new GrammarEnumerationAlgorithm();
+      //var provider = new PennMLRegressionInstanceProvider();
+      //var id = provider.GetDataDescriptors().Single(x => x.Name == "1027_ESL"); // something not randomly generated
+      //problem = new RegressionProblem { ProblemData = provider.LoadData(id) };
+      problem = new RegressionProblem { ProblemData = new KeijzerFunctionEleven(Seed).GenerateRegressionData() };
+      alg.Problem = problem;
+      alg.GuiUpdateInterval = int.MaxValue;
+      alg.SearchDataStructure = StorageType.Stack;
+      alg.SearchDataStructureSize = (int)1e5;
+
+      alg.Stopped += (sender, args) => mutex.Set();
+      alg.Started += (sender, args) => mutex.Reset();
+    }
+
+    private readonly ManualResetEvent mutex = new ManualResetEvent(false);
+
+    [TestMethod]
+    [TestProperty("Performance", "Phrase expansion speed for 2 variables.")]
+    public void AlgorithmPerformance() {
+      int reps = 5;
+      foreach (var st in Enum.GetValues(typeof(StorageType)).Cast<StorageType>()) {
+        alg.SearchDataStructure = st;
+        double avgExpansionSpeed = 0;
+        for (int i = 0; i < reps; ++i) {
+          alg.Prepare();
+          alg.Start();
+          mutex.WaitOne();
+          var expansionSpeed = (IntValue)alg.Results["Expansions per second"].Value;
+          avgExpansionSpeed += expansionSpeed.Value;
+        }
+
+        var distinct = (IntValue)alg.Results["Distinct Sentences"].Value;
+        var sentences = (IntValue)alg.Results["Generated Sentences"].Value;
+        var expansions = (IntValue)alg.Results["Phrase Expansions"].Value;
+        var archived = (IntValue)alg.Results["Generated/Archived Phrases"].Value;
+        var overwritten = (IntValue)alg.Results["Sentences overwritten"].Value;
+        var best = (SymbolicRegressionSolution)alg.Results["Best solution (Training)"].Value;
+        Console.WriteLine(st);
+        Console.WriteLine($"Generated/archived phrases: {archived.Value}");
+        Console.WriteLine($"Expansions per second: {avgExpansionSpeed / reps}");
+        Console.WriteLine($"Generated sentences: {sentences.Value}");
+        Console.WriteLine($"Distinct sentences: {distinct.Value}");
+        Console.WriteLine($"Overwritten sentences: {overwritten.Value}");
+        Console.WriteLine($"Train R2: {best.TrainingRSquared}");
+        Console.WriteLine($"Test R2: {best.TestRSquared}");
+        Console.WriteLine();
+      }
+    }
+  }
+}
Index: branches/2886_SymRegGrammarEnumeration/Test/Test.csproj
===================================================================
--- branches/2886_SymRegGrammarEnumeration/Test/Test.csproj	(revision 16151)
+++ branches/2886_SymRegGrammarEnumeration/Test/Test.csproj	(revision 16157)
@@ -119,4 +119,5 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="TreeHashingTest.cs" />
+    <Compile Include="AlgorithmPerformanceTest.cs" />
   </ItemGroup>
   <ItemGroup>
Index: branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs
===================================================================
--- branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs	(revision 16151)
+++ branches/2886_SymRegGrammarEnumeration/Test/TreeHashingTest.cs	(revision 16157)
@@ -1,3 +1,5 @@
-﻿using System.Linq;
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
 using HeuristicLab.Algorithms.DataAnalysis.SymRegGrammarEnumeration;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -146,42 +148,81 @@
     }
 
-    /* DEPRECATED; SINCE WE DO NOT ALLOW COMPOUND DIVISIONS
-    [TestMethod]
-    [TestCategory("TreeHashing")]
-    public void CompoundInverseCancellationToSingleInverse() {
-      SymbolList s1 = new SymbolList(new Symbol[] { varA, varB, grammar.Addition, grammar.Inv, grammar.Inv, grammar.Inv });
-      SymbolList s2 = new SymbolList(new Symbol[] { varA, varB, grammar.Addition, grammar.Inv });
-
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
-
-      Assert.AreEqual(hash1, hash2);
-    }
-
-    [TestMethod]
-    [TestCategory("TreeHashing")]
-    public void CompoundInverseCancellationToDivisor() {
-      SymbolList s1 = new SymbolList(new Symbol[] { varA, varB, grammar.Addition, grammar.Inv, grammar.Inv });
-      SymbolList s2 = new SymbolList(new Symbol[] { varA, varB, grammar.Addition });
-
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
-
-      Assert.AreEqual(hash1, hash2);
-    }
-
-    [TestMethod]
-    [TestCategory("TreeHashing")]
-    public void UncancelableCompoundInverse() {
-      // 1 / ( 1/b + sin(a*c) )
-      SymbolList s1 = new SymbolList(new Symbol[] { varB, grammar.Inv, varA, varC, grammar.Multiplication, grammar.Sin, grammar.Addition, grammar.Inv });
-      // b + sin(a*c)
-      SymbolList s2 = new SymbolList(new Symbol[] { varB, varA, varC, grammar.Multiplication, grammar.Sin, grammar.Addition });
-
-      int hash1 = grammar.CalcHashCode(s1);
-      int hash2 = grammar.CalcHashCode(s2);
-
-      Assert.AreNotEqual(hash1, hash2);
-    }*/
+    [TestMethod]
+    [TestCategory("TreeHashing")]
+    public void EnumerateGrammarTest() {
+      //const int nvars = 1;
+      //var variables = Enumerable.Range(1, nvars).Select(x => $"x{x}").ToArray();
+      var variables = new[] { "b", "a" };
+      var grammar = new Grammar(variables, Enum.GetValues(typeof(GrammarRule)).Cast<GrammarRule>());
+
+      int hash(SymbolList s) => grammar.Hasher.CalcHashCode(s);
+
+      List<SymbolList> sentences = EnumerateGrammarBreadth(grammar, length: 20, hashPhrases: false).ToList();
+      Console.WriteLine($"Breadth: {sentences.Count};{sentences.Select(hash).Distinct().Count() }");
+
+      sentences = EnumerateGrammarBreadth(grammar, length: 20, hashPhrases: true).ToList();
+      Console.WriteLine($"Breadth (hashed): {sentences.Count};{sentences.Select(hash).Distinct().Count() }");
+
+      sentences = EnumerateGrammarDepth(grammar, length: 20, hashPhrases: false).ToList();
+      Console.WriteLine($"Depth: {sentences.Count};{sentences.Select(hash).Distinct().Count() }");
+
+      sentences = EnumerateGrammarDepth(grammar, length: 20, hashPhrases: true).ToList();
+      Console.WriteLine($"Depth (hashed): {sentences.Count};{sentences.Select(hash).Distinct().Count() }");
+    }
+
+    private static IEnumerable<SymbolList> EnumerateGrammarBreadth(Grammar grammar, int length, bool hashPhrases = true) {
+      var phrases = new Queue<SymbolList>();
+      phrases.Enqueue(new SymbolList(grammar.StartSymbol));
+      var sentences = new List<SymbolList>();
+      var archive = new HashSet<int>();
+
+      while (phrases.Any()) {
+        var phrase = phrases.Dequeue();
+
+        if (phrase.Count > length)
+          continue;
+
+        if (phrase.IsSentence()) {
+          sentences.Add(phrase);
+          continue;
+        }
+
+        if (hashPhrases && !archive.Add(grammar.Hasher.CalcHashCode(phrase))) {
+          continue;
+        }
+
+        var idx = phrase.NextNonterminalIndex();
+        var productions = grammar.Productions[phrase[idx]];
+        var derived = productions.Select(p => phrase.DerivePhrase(idx, p)).Where(p => p.Count <= length);
+        foreach (var d in derived)
+          phrases.Enqueue(d);
+      }
+      return sentences;
+    }
+
+    private static IEnumerable<SymbolList> EnumerateGrammarDepth(Grammar grammar, int length, bool hashPhrases = true) {
+      return Expand(new SymbolList(grammar.StartSymbol), grammar, length, hashPhrases ? new HashSet<int>() : null);
+    }
+
+    private static IEnumerable<SymbolList> Expand(SymbolList phrase, Grammar grammar, int maxLength, HashSet<int> visited) {
+      if (phrase.Count > maxLength) {
+        yield break;
+      }
+
+      if (phrase.IsSentence()) {
+        yield return phrase;
+        yield break;
+      }
+
+      if (visited != null && !visited.Add(grammar.Hasher.CalcHashCode(phrase))) {
+        yield break;
+      }
+
+      var i = phrase.NextNonterminalIndex();
+      var productions = grammar.Productions[phrase[i]];
+
+      foreach (var s in productions.SelectMany(p => Expand(phrase.DerivePhrase(i, p), grammar, maxLength, visited)))
+        yield return s;
+    }
   }
 }
