Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.BottomUpTreeDistance.sln
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.BottomUpTreeDistance.sln	(revision 11909)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.BottomUpTreeDistance.sln	(revision 11910)
@@ -36,8 +36,6 @@
 		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|Any CPU.Build.0 = Release|Any CPU
-		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|x64.ActiveCfg = Release|x64
-		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|x64.Build.0 = Release|x64
-		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|x86.ActiveCfg = Release|x86
-		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|x86.Build.0 = Release|x86
+		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|x64.ActiveCfg = Release|Any CPU
+		{07C05C8F-0769-4607-96C6-D90A219F4C85}.Release|x86.ActiveCfg = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
Index: anches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisInternalDiversityAnalyzer.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisInternalDiversityAnalyzer.cs	(revision 11909)
+++ 	(revision )
@@ -1,135 +1,0 @@
-﻿#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Linq;
-using HeuristicLab.Analysis;
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Data;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
-using HeuristicLab.Operators;
-using HeuristicLab.Optimization;
-using HeuristicLab.Parameters;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-
-namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Analyzers {
-  [StorableClass]
-  [Item("SymbolicDataAnalysisInternalDiversityAnalyzer", "An analyzer that determines the average diversity of symbolic expression trees.")]
-  public class SymbolicDataAnalysisInternalDiversityAnalyzer : SingleSuccessorOperator, ISymbolicDataAnalysisAnalyzer {
-    private const string SymbolicExpressionTreeParameterName = "SymbolicExpressionTree";
-    private const string QualityParameterName = "Quality";
-    private const string PercentageOfBestSolutionsParameterName = "PercentageOfBestSolutions";
-    private const string ResultCollectionParameterName = "Results";
-    private readonly BottomUpSimilarityCalculator busCalculator;
-
-    public SymbolicDataAnalysisInternalDiversityAnalyzer() {
-      busCalculator = new BottomUpSimilarityCalculator();
-
-      Parameters.Add(new ScopeTreeLookupParameter<ISymbolicExpressionTree>(SymbolicExpressionTreeParameterName));
-      Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>(QualityParameterName));
-      Parameters.Add(new LookupParameter<ResultCollection>(ResultCollectionParameterName));
-      Parameters.Add(new FixedValueParameter<PercentValue>(PercentageOfBestSolutionsParameterName));
-    }
-
-    protected SymbolicDataAnalysisInternalDiversityAnalyzer(SymbolicDataAnalysisInternalDiversityAnalyzer original, Cloner cloner)
-      : base(original, cloner) {
-    }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new SymbolicDataAnalysisInternalDiversityAnalyzer(this, cloner);
-    }
-
-    #region parameter properties
-    public IScopeTreeLookupParameter<ISymbolicExpressionTree> SymbolicExpressionTreeParameter {
-      get { return (IScopeTreeLookupParameter<ISymbolicExpressionTree>)Parameters[SymbolicExpressionTreeParameterName]; }
-    }
-
-    public IScopeTreeLookupParameter<DoubleValue> QualityParameter {
-      get { return (IScopeTreeLookupParameter<DoubleValue>)Parameters[QualityParameterName]; }
-    }
-
-    public ILookupParameter<ResultCollection> ResultCollectionParameter {
-      get { return (ILookupParameter<ResultCollection>)Parameters[ResultCollectionParameterName]; }
-    }
-
-    public IFixedValueParameter<PercentValue> PercentageOfBestSolutionsParameter {
-      get { return (IFixedValueParameter<PercentValue>)Parameters[PercentageOfBestSolutionsParameterName]; }
-    }
-    #endregion
-
-    public double PercentageOfBestSolutions {
-      get { return PercentageOfBestSolutionsParameter.Value.Value; }
-      set { PercentageOfBestSolutionsParameter.Value.Value = value; }
-    }
-
-    public bool EnabledByDefault { get { return true; } }
-
-    public override IOperation Apply() {
-
-      var results = ResultCollectionParameter.ActualValue;
-
-      DataTable table;
-      if (!results.ContainsKey("Avg. Internal Diversity")) {
-        table = new DataTable("Internal Diversity") { VisualProperties = { YAxisTitle = "Internal Diversity" } };
-        var row = new DataRow("Avg. Internal Diversity") { VisualProperties = { StartIndexZero = true } };
-        table.Rows.Add(row);
-        results.Add(new Result("Avg. Internal Diversity", table));
-      } else {
-        table = (DataTable)results["Avg. Internal Diversity"].Value;
-      }
-
-      var trees = SymbolicExpressionTreeParameter.ActualValue.ToArray();
-      var qualities = QualityParameter.ActualValue.ToArray();
-
-      var pairs = trees.Zip(qualities, (t, q) => new { Tree = t, Quality = q }).OrderByDescending(x => x.Quality);
-      int n = (int)Math.Floor(trees.Length * PercentageOfBestSolutions);
-      double avgInternalDiversity = pairs.Take(n).Average(x => CalculateInternalDiversity(x.Tree));
-
-      table.Rows["Avg. Internal Diversity"].Values.Add(avgInternalDiversity);
-      return base.Apply();
-    }
-
-    private double CalculateInternalDiversity(ISymbolicExpressionTree tree) {
-      var branchPoint = tree.IterateNodesPrefix().FirstOrDefault(x => x.SubtreeCount == 2);
-      if (branchPoint == null)
-        return 1;
-
-      var s1 = branchPoint.GetSubtree(0);
-      var s2 = branchPoint.GetSubtree(1);
-
-      // set parents to null because otherwise the bottom-up calculator will throw an exception
-      var p1 = s1.Parent;
-      s1.Parent = null;
-      var p2 = s2.Parent;
-      s2.Parent = null;
-
-      var m = busCalculator.ComputeBottomUpMapping(s1, s2);
-      var diversity = 1 - 2.0 * m.Count / (s1.GetLength() + s2.GetLength());
-
-      // restore parents
-      s1.Parent = p1;
-      s2.Parent = p2;
-
-      return diversity;
-    }
-  }
-}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 11909)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 11910)
@@ -203,5 +203,4 @@
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="Analyzers\SymbolicDataAnalysisInternalDiversityAnalyzer.cs" />
     <Compile Include="Analyzers\SymbolicDataAnalysisSingleObjectivePruningAnalyzer.cs" />
     <Compile Include="Analyzers\SymbolicDataAnalysisSingleObjectiveValidationParetoBestSolutionAnalyzer.cs" />
@@ -221,12 +220,11 @@
     <Compile Include="Importer\Token.cs" />
     <Compile Include="Interfaces\IModelBacktransformator.cs" />
-    <Compile Include="Matching\SymbolicExpressionTreeCanonicalSorter.cs" />
-    <Compile Include="Matching\SymbolicExpressionTreeEqualityComparer.cs" />
-    <Compile Include="Matching\SymbolicExpressionTreeMatching.cs" />
-    <Compile Include="Matching\SymbolicExpressionTreeMaxCommonSequenceCalculator.cs" />
-    <Compile Include="Matching\SymbolicExpressionTreeNodeComparer.cs" />
-    <Compile Include="Matching\SymbolicExpressionTreeNodeSimilarityComparer.cs" />
-    <Compile Include="SimilarityCalculators\BottomUpSimilarityCalculator.cs" />
-    <Compile Include="SimilarityCalculators\MaxCommonSubtreeSimilarityCalculator.cs" />
+    <Compile Include="TreeMatching\SymbolicExpressionTreeCanonicalSorter.cs" />
+    <Compile Include="TreeMatching\SymbolicExpressionTreeEqualityComparer.cs" />
+    <Compile Include="TreeMatching\SymbolicExpressionTreeMatching.cs" />
+    <Compile Include="TreeMatching\SymbolicExpressionTreeNodeComparer.cs" />
+    <Compile Include="TreeMatching\SymbolicExpressionTreeNodeEqualityComparer.cs" />
+    <Compile Include="TreeMatching\SymbolicExpressionTreeBottomUpSimilarityCalculator.cs" />
+    <Compile Include="TreeMatching\SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator.cs" />
     <Compile Include="SymbolicExpressionTreeBacktransformator.cs" />
     <Compile Include="SymbolicDataAnalysisExpressionPruningOperator.cs" />
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisProblem.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisProblem.cs	(revision 11909)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisProblem.cs	(revision 11910)
@@ -358,5 +358,5 @@
       }
       foreach (var op in operators.OfType<SingleObjectivePopulationDiversityAnalyzer>()) {
-        op.SimilarityCalculator = new BottomUpSimilarityCalculator();
+        op.SimilarityCalculator = new SymbolicExpressionTreeBottomUpSimilarityCalculator();
       }
     }
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeBottomUpSimilarityCalculator.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeBottomUpSimilarityCalculator.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeBottomUpSimilarityCalculator.cs	(revision 11910)
@@ -0,0 +1,233 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Optimization.Operators;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  [Item("SymbolicExpressionTreeBottomUpSimilarityCalculator", "A similarity calculator which uses the tree bottom-up distance as a similarity metric.")]
+  public class SymbolicExpressionTreeBottomUpSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
+    private readonly HashSet<string> commutativeSymbols = new HashSet<string> { "Addition", "Multiplication", "Average", "And", "Or", "Xor" };
+    public bool MatchVariableWeights { get; set; }
+    public bool MatchConstantValues { get; set; }
+
+    public SymbolicExpressionTreeBottomUpSimilarityCalculator() { }
+
+    protected SymbolicExpressionTreeBottomUpSimilarityCalculator(SymbolicExpressionTreeBottomUpSimilarityCalculator original, Cloner cloner)
+      : base(original, cloner) {
+      MatchVariableWeights = original.MatchVariableWeights;
+      MatchConstantValues = original.MatchConstantValues;
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SymbolicExpressionTreeBottomUpSimilarityCalculator(this, cloner);
+    }
+
+    public double CalculateSimilarity(ISymbolicExpressionTree t1, ISymbolicExpressionTree t2) {
+      if (t1 == t2)
+        return 1;
+
+      var map = ComputeBottomUpMapping(t1.Root, t2.Root);
+      return 2.0 * map.Count / (t1.Length + t2.Length);
+    }
+
+    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
+      var t1 = leftSolution.Variables[SolutionVariableName].Value as ISymbolicExpressionTree;
+      var t2 = rightSolution.Variables[SolutionVariableName].Value as ISymbolicExpressionTree;
+
+      if (t1 == null || t2 == null)
+        throw new ArgumentException("Cannot calculate similarity when one of the arguments is null.");
+
+      var similarity = CalculateSimilarity(t1, t2);
+      if (similarity > 1.0)
+        throw new Exception("Similarity value cannot be greater than 1");
+
+      return similarity;
+    }
+
+    public Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> ComputeBottomUpMapping(ISymbolicExpressionTreeNode n1, ISymbolicExpressionTreeNode n2) {
+      var comparer = new SymbolicExpressionTreeNodeComparer(); // use a node comparer because it's faster than calling node.ToString() (strings are expensive) and comparing strings
+      var compactedGraph = Compact(n1, n2);
+
+      var forwardMap = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>(); // nodes of t1 => nodes of t2
+      var reverseMap = new Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>(); // nodes of t2 => nodes of t1
+
+      // visit nodes in order of decreasing height to ensure correct mapping
+      var nodes1 = n1.IterateNodesPrefix().ToList();
+      var nodes2 = n2.IterateNodesPrefix().ToList();
+      for (int i = 0; i < nodes1.Count; ++i) {
+        var v = nodes1[i];
+        if (forwardMap.ContainsKey(v))
+          continue;
+        var kv = compactedGraph[v];
+        ISymbolicExpressionTreeNode w = null;
+        for (int j = 0; j < nodes2.Count; ++j) {
+          var t = nodes2[j];
+          if (reverseMap.ContainsKey(t) || compactedGraph[t] != kv)
+            continue;
+          w = t;
+          break;
+        }
+        if (w == null) continue;
+
+        // at this point we know that v and w are isomorphic, however, the mapping cannot be done directly (as in the paper) because the trees are unordered (subtree order might differ)
+        // the solution is to sort subtrees by label using IterateBreadthOrdered (this will work because the subtrees are isomorphic!) and simultaneously iterate over the two subtrees
+        var vv = IterateBreadthOrdered(v, comparer).ToList();
+        var ww = IterateBreadthOrdered(w, comparer).ToList();
+        int len = Math.Min(vv.Count, ww.Count);
+        for (int j = 0; j < len; ++j) {
+          var s = vv[j];
+          var t = ww[j];
+          Debug.Assert(!reverseMap.ContainsKey(t));
+
+          forwardMap[s] = t;
+          reverseMap[t] = s;
+        }
+      }
+
+      return forwardMap;
+    }
+
+    /// <summary>
+    /// Creates a compact representation of the two trees as a directed acyclic graph
+    /// </summary>
+    /// <param name="n1">The root of the first tree</param>
+    /// <param name="n2">The root of the second tree</param>
+    /// <returns>The compacted DAG representing the two trees</returns>
+    private Dictionary<ISymbolicExpressionTreeNode, GraphNode> Compact(ISymbolicExpressionTreeNode n1, ISymbolicExpressionTreeNode n2) {
+      var nodeMap = new Dictionary<ISymbolicExpressionTreeNode, GraphNode>(); // K
+      var labelMap = new Dictionary<string, GraphNode>(); // L
+      var childrenCount = new Dictionary<ISymbolicExpressionTreeNode, int>(); // Children
+
+      var nodes = n1.IterateNodesPostfix().Concat(n2.IterateNodesPostfix()); // the disjoint union F
+      var list = new List<GraphNode>();
+      var queue = new Queue<ISymbolicExpressionTreeNode>();
+
+      foreach (var n in nodes) {
+        if (n.SubtreeCount == 0) {
+          var label = Label(n);
+          if (!labelMap.ContainsKey(label)) {
+            var z = new GraphNode { SymbolicExpressionTreeNode = n, Label = label };
+            labelMap[z.Label] = z;
+          }
+          nodeMap[n] = labelMap[label];
+          queue.Enqueue(n);
+        } else {
+          childrenCount[n] = n.SubtreeCount;
+        }
+      }
+      while (queue.Any()) {
+        var n = queue.Dequeue();
+        if (n.SubtreeCount > 0) {
+          bool found = false;
+          var label = n.Symbol.Name;
+          var depth = n.GetDepth();
+
+          bool sort = n.SubtreeCount > 1 && commutativeSymbols.Contains(label);
+          var nSubtrees = n.Subtrees.Select(x => nodeMap[x]).ToList();
+          if (sort) nSubtrees.Sort((a, b) => string.CompareOrdinal(a.Label, b.Label));
+
+          for (int i = list.Count - 1; i >= 0; --i) {
+            var w = list[i];
+            if (!(n.SubtreeCount == w.SubtreeCount && label == w.Label && depth == w.Depth))
+              continue;
+
+            // sort V and W when the symbol is commutative because we are dealing with unordered trees
+            var m = w.SymbolicExpressionTreeNode;
+            var mSubtrees = m.Subtrees.Select(x => nodeMap[x]).ToList();
+            if (sort) mSubtrees.Sort((a, b) => string.CompareOrdinal(a.Label, b.Label));
+
+            found = nSubtrees.SequenceEqual(mSubtrees);
+            if (found) {
+              nodeMap[n] = w;
+              break;
+            }
+          }
+
+          if (!found) {
+            var w = new GraphNode { SymbolicExpressionTreeNode = n, Label = label, Depth = depth };
+            list.Add(w);
+            nodeMap[n] = w;
+          }
+        }
+
+        if (n == n1 || n == n2)
+          continue;
+
+        var p = n.Parent;
+        if (p == null)
+          continue;
+
+        childrenCount[p]--;
+
+        if (childrenCount[p] == 0)
+          queue.Enqueue(p);
+      }
+
+      return nodeMap;
+    }
+
+    private IEnumerable<ISymbolicExpressionTreeNode> IterateBreadthOrdered(ISymbolicExpressionTreeNode node, ISymbolicExpressionTreeNodeComparer comparer) {
+      var list = new List<ISymbolicExpressionTreeNode> { node };
+      int i = 0;
+      while (i < list.Count) {
+        var n = list[i];
+        if (n.SubtreeCount > 0) {
+          var subtrees = commutativeSymbols.Contains(node.Symbol.Name) ? n.Subtrees.OrderBy(x => x, comparer) : n.Subtrees;
+          list.AddRange(subtrees);
+        }
+        i++;
+      }
+      return list;
+    }
+
+    private string Label(ISymbolicExpressionTreeNode node) {
+      if (node.SubtreeCount > 0)
+        return node.Symbol.Name;
+
+      var constant = node as ConstantTreeNode;
+      if (constant != null)
+        return MatchConstantValues ? constant.Value.ToString(CultureInfo.InvariantCulture) : constant.Symbol.Name;
+      var variable = node as VariableTreeNode;
+      if (variable != null) {
+        return MatchVariableWeights ? variable.Weight + variable.VariableName : variable.VariableName;
+      }
+
+      return node.ToString();
+    }
+
+    private class GraphNode {
+      public ISymbolicExpressionTreeNode SymbolicExpressionTreeNode;
+      public string Label;
+      public int Depth;
+      public int SubtreeCount { get { return SymbolicExpressionTreeNode.SubtreeCount; } }
+    }
+  }
+}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeCanonicalSorter.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeCanonicalSorter.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeCanonicalSorter.cs	(revision 11910)
@@ -0,0 +1,30 @@
+﻿
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public class SymbolicExpressionTreeCanonicalSorter {
+    private readonly HashSet<Type> nonSymmetricSymbols = new HashSet<Type> { typeof(Subtraction), typeof(Division) };
+
+    public void SortSubtrees(ISymbolicExpressionTree tree) {
+      SortSubtrees(tree.Root);
+    }
+
+    public void SortSubtrees(ISymbolicExpressionTreeNode node) {
+      if (node.SubtreeCount == 0) return;
+      var subtrees = node.Subtrees as List<ISymbolicExpressionTreeNode> ?? node.Subtrees.ToList();
+      if (IsSymmetric(node.Symbol)) {
+        var comparer = new SymbolicExpressionTreeNodeComparer();
+        subtrees.Sort(comparer);
+      }
+      foreach (var s in subtrees)
+        SortSubtrees(s);
+    }
+
+    private bool IsSymmetric(ISymbol s) {
+      return !nonSymmetricSymbols.Contains(s.GetType());
+    }
+  }
+}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeEqualityComparer.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeEqualityComparer.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeEqualityComparer.cs	(revision 11910)
@@ -0,0 +1,18 @@
+﻿using System;
+using System.Collections.Generic;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public class SymbolicExpressionTreeEqualityComparer : IEqualityComparer<ISymbolicExpressionTree> {
+    public SymbolicExpressionTreeNodeEqualityComparer SimilarityComparer { get; set; }
+
+    public bool Equals(ISymbolicExpressionTree a, ISymbolicExpressionTree b) {
+      if (SimilarityComparer == null) throw new Exception("SimilarityComparer needs to be initialized first.");
+      return a.Length == b.Length && SymbolicExpressionTreeMatching.Match(a.Root, b.Root, SimilarityComparer) == Math.Min(a.Length, b.Length);
+    }
+
+    public int GetHashCode(ISymbolicExpressionTree tree) {
+      return (tree.Length << 8) % 12345;
+    }
+  }
+}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeMatching.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeMatching.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeMatching.cs	(revision 11910)
@@ -0,0 +1,125 @@
+﻿#region License Information
+
+/* HeuristicLab
+ * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+//using HeuristicLab.EvolutionTracking;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class SymbolicExpressionTreeMatching {
+    public static bool ContainsSubtree(this ISymbolicExpressionTreeNode root, ISymbolicExpressionTreeNode subtree, SymbolicExpressionTreeNodeEqualityComparer comparer) {
+      return FindMatches(root, subtree, comparer).Any();
+    }
+    public static IEnumerable<ISymbolicExpressionTreeNode> FindMatches(ISymbolicExpressionTree tree, ISymbolicExpressionTreeNode subtree, SymbolicExpressionTreeNodeEqualityComparer comparer) {
+      return FindMatches(tree.Root, subtree, comparer);
+    }
+
+    public static IEnumerable<ISymbolicExpressionTreeNode> FindMatches(ISymbolicExpressionTreeNode root, ISymbolicExpressionTreeNode subtree, SymbolicExpressionTreeNodeEqualityComparer comp) {
+      var fragmentLength = subtree.GetLength();
+      // below, we use ">=" for Match(n, subtree, comp) >= fragmentLength because in case of relaxed conditions, 
+      // we can have multiple matches of the same node
+
+      return root.IterateNodesBreadth().Where(n => n.GetLength() >= fragmentLength && Match(n, subtree, comp) == fragmentLength);
+    }
+
+    ///<summary>
+    /// Finds the longest common subsequence in quadratic time and linear space
+    /// Variant of:
+    /// D. S. Hirschberg. A linear space algorithm for or computing maximal common subsequences. 1975.
+    /// http://dl.acm.org/citation.cfm?id=360861
+    /// </summary>
+    /// <returns>Number of pairs that were matched</returns>
+    public static int Match(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b, ISymbolicExpressionTreeNodeSimilarityComparer comp) {
+      if (!comp.Equals(a, b)) return 0;
+      int m = a.SubtreeCount;
+      int n = b.SubtreeCount;
+      if (m == 0 || n == 0) return 1;
+      var matrix = new int[m + 1, n + 1];
+      for (int i = 1; i <= m; ++i) {
+        var ai = a.GetSubtree(i - 1);
+        for (int j = 1; j <= n; ++j) {
+          var bj = b.GetSubtree(j - 1);
+          int match = Match(ai, bj, comp);
+          matrix[i, j] = Math.Max(Math.Max(matrix[i, j - 1], matrix[i - 1, j]), matrix[i - 1, j - 1] + match);
+        }
+      }
+      return matrix[m, n] + 1;
+    }
+
+    /// <summary>
+    /// Calculates the difference between two symbolic expression trees.
+    /// </summary>
+    /// <param name="tree">The first symbolic expression tree</param>
+    /// <param name="other">The second symbolic expression tree</param>
+    /// <returns>Returns the root of the subtree (from T1) by which T1 differs from T2, or null if no difference is found.</returns>
+    public static ISymbolicExpressionTreeNode Difference(this ISymbolicExpressionTree tree, ISymbolicExpressionTree other) {
+      return Difference(tree.Root, other.Root);
+    }
+
+    public static ISymbolicExpressionTreeNode Difference(this ISymbolicExpressionTreeNode node, ISymbolicExpressionTreeNode other) {
+      var a = node.IterateNodesPrefix().ToList();
+      var b = other.IterateNodesPrefix().ToList();
+      var list = new List<ISymbolicExpressionTreeNode>();
+      for (int i = 0, j = 0; i < a.Count && j < b.Count; ++i, ++j) {
+        var s1 = a[i].ToString();
+        var s2 = b[j].ToString();
+        if (s1 == s2) continue;
+        list.Add(a[i]);
+        // skip subtrees since the parents are already different
+        i += a[i].SubtreeCount;
+        j += b[j].SubtreeCount;
+      }
+      ISymbolicExpressionTreeNode result = list.Count > 0 ? LowestCommonAncestor(node, list) : null;
+      return result;
+    }
+
+    private static ISymbolicExpressionTreeNode LowestCommonAncestor(ISymbolicExpressionTreeNode root, List<ISymbolicExpressionTreeNode> nodes) {
+      if (nodes.Count == 0)
+        throw new ArgumentException("The nodes list should contain at least one element.");
+
+      if (nodes.Count == 1)
+        return nodes[0];
+
+      int minLevel = nodes.Min(x => root.GetBranchLevel(x));
+
+      // bring the nodes in the nodes to the same level (relative to the root)
+      for (int i = 0; i < nodes.Count; ++i) {
+        var node = nodes[i];
+        var level = root.GetBranchLevel(node);
+        for (int j = minLevel; j < level; ++j)
+          node = node.Parent;
+        nodes[i] = node;
+      }
+
+      // while not all the elements in the nodes are equal, go one level up
+      while (nodes.Any(x => x != nodes[0])) {
+        for (int i = 0; i < nodes.Count; ++i)
+          nodes[i] = nodes[i].Parent;
+      }
+
+      return nodes[0];
+    }
+  }
+}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator.cs	(revision 11910)
@@ -0,0 +1,105 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Optimization.Operators;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  [Item("SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator", "A similarity calculator based on the size of the maximum common subtree between two trees")]
+  public class SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator : SingleObjectiveSolutionSimilarityCalculator {
+    [Storable]
+    private readonly SymbolicExpressionTreeNodeEqualityComparer comparer;
+    public bool MatchVariableWeights {
+      get { return comparer.MatchVariableWeights; }
+      set { comparer.MatchVariableWeights = value; }
+    }
+
+    public bool MatchConstantValues {
+      get { return comparer.MatchConstantValues; }
+      set { comparer.MatchConstantValues = value; }
+    }
+
+    [StorableConstructor]
+    protected SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator(bool deserializing) : base(deserializing) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator(this, cloner);
+    }
+
+    protected SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator(SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator original, Cloner cloner)
+      : base(original, cloner) {
+      comparer = cloner.Clone(original.comparer);
+    }
+
+    public SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator() {
+      comparer = new SymbolicExpressionTreeNodeEqualityComparer {
+        MatchConstantValues = true,
+        MatchVariableNames = true,
+        MatchVariableWeights = true
+      };
+    }
+
+    public SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator(bool matchVariableWeights, bool matchConstantValues) {
+      comparer = new SymbolicExpressionTreeNodeEqualityComparer {
+        MatchConstantValues = matchConstantValues,
+        MatchVariableNames = true,
+        MatchVariableWeights = matchVariableWeights
+      };
+    }
+
+    public double CalculateSimilarity(ISymbolicExpressionTree t1, ISymbolicExpressionTree t2) {
+      return MaxCommonSubtreeSimilarity(t1, t2, comparer);
+    }
+
+    public override double CalculateSolutionSimilarity(IScope leftSolution, IScope rightSolution) {
+      var t1 = leftSolution.Variables[SolutionVariableName].Value as ISymbolicExpressionTree;
+      var t2 = rightSolution.Variables[SolutionVariableName].Value as ISymbolicExpressionTree;
+
+      if (t1 == null || t2 == null)
+        throw new ArgumentException("Cannot calculate similarity when one of the arguments is null.");
+
+      return MaxCommonSubtreeSimilarity(t1, t2, comparer);
+    }
+
+    public static double MaxCommonSubtreeSimilarity(ISymbolicExpressionTree a, ISymbolicExpressionTree b, ISymbolicExpressionTreeNodeSimilarityComparer comparer) {
+      int max = 0;
+      var rootA = a.Root.GetSubtree(0).GetSubtree(0);
+      var rootB = b.Root.GetSubtree(0).GetSubtree(0);
+      foreach (var aa in rootA.IterateNodesBreadth()) {
+        int lenA = aa.GetLength();
+        if (lenA <= max) continue;
+        foreach (var bb in rootB.IterateNodesBreadth()) {
+          int lenB = bb.GetLength();
+          if (lenB <= max) continue;
+          int matches = SymbolicExpressionTreeMatching.Match(aa, bb, comparer);
+          if (max < matches)
+            max = matches;
+        }
+      }
+      return 2.0 * max / (rootA.GetLength() + rootB.GetLength());
+    }
+  }
+}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeNodeComparer.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeNodeComparer.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeNodeComparer.cs	(revision 11910)
@@ -0,0 +1,72 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  // this comparer considers that a < b if the type of a is "greater" than the type of b, for example:
+  // - A function node is "greater" than a terminal node
+  // - A variable terminal is "greater" than a constant terminal
+  // - used for bringing subtrees to a "canonical" form when the operation allows reordering of arguments
+  public class SymbolicExpressionTreeNodeComparer : ISymbolicExpressionTreeNodeComparer {
+    public static int CompareNodes(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      var ta = a as SymbolicExpressionTreeTerminalNode;
+      var tb = b as SymbolicExpressionTreeTerminalNode;
+
+      if (ta == null)
+        return tb == null ? String.CompareOrdinal(a.Symbol.Name, b.Symbol.Name) : -1;
+
+      if (tb == null)
+        return 1;
+
+      // at this point we know a and b are both terminals
+      var va = a as VariableTreeNode;
+      var vb = b as VariableTreeNode;
+
+      if (va != null)
+        return vb == null ? -1 : CompareVariables(va, vb);
+
+      if (vb != null)
+        return 1;
+
+      // at this point we know a and b are not variables
+      var ca = a as ConstantTreeNode;
+      var cb = b as ConstantTreeNode;
+
+      if (ca != null && cb != null)
+        return ca.Value.CompareTo(cb.Value);
+
+      // for other unknown terminal types, compare strings
+      return string.CompareOrdinal(a.ToString(), b.ToString());
+    }
+
+    private static int CompareVariables(VariableTreeNode a, VariableTreeNode b) {
+      int result = string.CompareOrdinal(a.VariableName, b.VariableName);
+      return result == 0 ? a.Weight.CompareTo(b.Weight) : result;
+    }
+
+    public int Compare(ISymbolicExpressionTreeNode x, ISymbolicExpressionTreeNode y) {
+      return CompareNodes(x, y);
+    }
+  }
+
+}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeNodeEqualityComparer.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeNodeEqualityComparer.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/TreeMatching/SymbolicExpressionTreeNodeEqualityComparer.cs	(revision 11910)
@@ -0,0 +1,97 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [Item("SymbolicExpressionTreeNodeEqualityComparer", "An operator that checks node equality based on different similarity measures.")]
+  [StorableClass]
+  public class SymbolicExpressionTreeNodeEqualityComparer : Item, ISymbolicExpressionTreeNodeSimilarityComparer {
+    [StorableConstructor]
+    protected SymbolicExpressionTreeNodeEqualityComparer(bool deserializing) : base(deserializing) { }
+    protected SymbolicExpressionTreeNodeEqualityComparer(SymbolicExpressionTreeNodeEqualityComparer original, Cloner cloner)
+      : base(original, cloner) {
+      matchConstantValues = original.matchConstantValues;
+      matchVariableNames = original.matchVariableNames;
+      matchVariableWeights = original.matchVariableWeights;
+    }
+    public override IDeepCloneable Clone(Cloner cloner) { return new SymbolicExpressionTreeNodeEqualityComparer(this, cloner); }
+
+    // more flexible matching criteria 
+    [Storable]
+    private bool matchConstantValues;
+    public bool MatchConstantValues {
+      get { return matchConstantValues; }
+      set { matchConstantValues = value; }
+    }
+
+    [Storable]
+    private bool matchVariableNames;
+    public bool MatchVariableNames {
+      get { return matchVariableNames; }
+      set { matchVariableNames = value; }
+    }
+
+    [Storable]
+    private bool matchVariableWeights;
+    public bool MatchVariableWeights {
+      get { return matchVariableWeights; }
+      set { matchVariableWeights = value; }
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+    }
+
+    public SymbolicExpressionTreeNodeEqualityComparer() {
+      matchConstantValues = true;
+      matchVariableNames = true;
+      matchVariableWeights = true;
+    }
+
+    public int GetHashCode(ISymbolicExpressionTreeNode n) {
+      return n.ToString().ToLower().GetHashCode();
+    }
+
+    public bool Equals(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      if (!(a is SymbolicExpressionTreeTerminalNode))
+        // if a and b are non terminal nodes, check equality of symbol names
+        return !(b is SymbolicExpressionTreeTerminalNode) && a.Symbol.Name.Equals(b.Symbol.Name);
+      var va = a as VariableTreeNode;
+      if (va != null) {
+        var vb = b as VariableTreeNode;
+        if (vb == null) return false;
+
+        return (!MatchVariableNames || va.VariableName.Equals(vb.VariableName)) && (!MatchVariableWeights || va.Weight.Equals(vb.Weight));
+      }
+      var ca = a as ConstantTreeNode;
+      if (ca != null) {
+        var cb = b as ConstantTreeNode;
+        if (cb == null) return false;
+        return (!MatchConstantValues || ca.Value.Equals(cb.Value));
+      }
+      return false;
+    }
+  }
+}
Index: anches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/BottomUpTreeSimilarityCalculatorTest.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/BottomUpTreeSimilarityCalculatorTest.cs	(revision 11909)
+++ 	(revision )
@@ -1,176 +1,0 @@
-﻿using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Drawing;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
-using HeuristicLab.Random;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Tests {
-  [TestClass]
-  public class BottomUpSimilarityCalculatorTest {
-    private readonly BottomUpSimilarityCalculator busCalculator;
-    private readonly SymbolicExpressionImporter importer;
-
-    private const int N = 150;
-    private const int Rows = 1;
-    private const int Columns = 10;
-
-    public BottomUpSimilarityCalculatorTest() {
-      busCalculator = new BottomUpSimilarityCalculator { MatchConstantValues = true, MatchVariableWeights = true };
-      importer = new SymbolicExpressionImporter();
-    }
-
-    [TestMethod]
-    [TestCategory("Problems.DataAnalysis.Symbolic")]
-    [TestProperty("Time", "short")]
-    public void TestBottomUpMatching() {
-      TestMatchedNodes("(+ 1 2)", "(+ 2 1)", 5);
-      TestMatchedNodes("(- 2 1)", "(- 1 2)", 2);
-      TestMatchedNodes("(* (variable 1 X1) (variable 1 X2))", "(* (+ (variable 1 X1) 1) (+ (variable 1 X2) 1))", 2);
-
-      TestMatchedNodes("(* (variable 1 X1) (variable 1 X2))", "(* (+ (variable 1 X1) 1) (variable 1 X2))", 2);
-
-      TestMatchedNodes("(+ (variable 1 a) (variable 1 b))", "(+ (variable 1 a) (variable 1 a))", 1);
-      TestMatchedNodes("(+ (+ (variable 1 a) (variable 1 b)) (variable 1 b))", "(+ (* (+ (variable 1 a) (variable 1 b)) (variable 1 b)) (+ (+ (variable 1 a) (variable 1 b)) (variable 1 b)))", 5);
-
-      TestMatchedNodes(
-        "(* (+ 2.84 (exp (+ (log (/ (variable 2.0539 X5) (variable -9.2452e-1 X6))) (/ (variable 2.0539 X5) (variable -9.2452e-1 X6))))) 2.9081)",
-        "(* (- (variable 9.581e-1 X6) (+ (- (variable 5.1491e-1 X5) 1.614e+1) (+ (/ (variable 2.0539 X5) (variable -9.2452e-1 X6)) (log (/ (variable 2.0539 X5) (variable -9.2452e-1 X6)))))) 2.9081)",
-        9);
-
-      TestMatchedNodes("(+ (exp 2.1033) (/ -4.3072 (variable 2.4691 X7)))", "(/ 1 (+ (/ -4.3072 (variable 2.4691 X7)) (exp 2.1033)))", 6);
-      TestMatchedNodes("(+ (exp 2.1033) (/ -4.3072 (variable 2.4691 X7)))", "(/ 1 (+ (/ (variable 2.4691 X7) -4.3072) (exp 2.1033)))", 4);
-
-      const string expr1 = "(* (- 1.2175e+1 (+ (/ (exp -1.4134e+1) (exp 9.2013)) (exp (log (exp (/ (exp (- (* -4.2461 (variable 2.2634 X5)) (- -9.6267e-1 3.3243))) (- (/ (/ (variable 1.0883 X1) (variable 6.9620e-1 X2)) (log 1.3011e+1)) (variable -4.3098e-1 X7)))))))) (log 1.3011e+1))";
-      const string expr2 = "(* (- 1.2175e+1 (+ (/ (/ (+ (variable 3.0140 X9) (variable 1.3430 X8)) -1.0864e+1) (exp 9.2013)) (exp (log (exp (/ (exp (- (* -4.2461 (variable 2.2634 X5)) (- -9.6267e-1 3.3243))) (- (/ (/ (variable 1.0883 X1) (variable 6.9620e-1 X2)) (log 1.3011e+1)) (variable -4.3098e-1 X7)))))))) (exp (variable 4.0899e-1 X7)))";
-
-      TestMatchedNodes(expr1, expr2, 23);
-
-    }
-
-    [TestMethod]
-    [TestCategory("Problems.DataAnalysis.Symbolic")]
-    [TestProperty("Time", "short")]
-    private void TestMatchedNodes(string expr1, string expr2, int expected) {
-      var t1 = importer.Import(expr1);
-      var t2 = importer.Import(expr2);
-
-      var mapping = busCalculator.ComputeBottomUpMapping(t1.Root, t2.Root);
-      var c = mapping.Count;
-
-      if (c != expected) {
-        throw new Exception("Match count " + c + " is different than expected value " + expected);
-      }
-    }
-
-    [TestMethod]
-    [TestCategory("Problems.DataAnalysis.Symbolic")]
-    [TestProperty("Time", "long")]
-    public void TestBottomUpSimilarityCalculatorPerformance() {
-      var grammar = new TypeCoherentExpressionGrammar();
-      grammar.ConfigureAsDefaultRegressionGrammar();
-      var twister = new MersenneTwister(31415);
-      var ds = Util.CreateRandomDataset(twister, Rows, Columns);
-      var trees = Util.CreateRandomTrees(twister, ds, grammar, N, 1, 100, 0, 0);
-
-      double s = 0;
-      var sw = new Stopwatch();
-
-      sw.Start();
-      for (int i = 0; i < trees.Length - 1; ++i) {
-        for (int j = i + 1; j < trees.Length; ++j) {
-          s += busCalculator.CalculateSimilarity(trees[i], trees[j]);
-        }
-      }
-
-      sw.Stop();
-      Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds / 1000.0 + ", Avg. similarity: " + s / (N * (N - 1) / 2));
-      Console.WriteLine(N * (N + 1) / (2 * sw.ElapsedMilliseconds / 1000.0) + " similarity calculations per second.");
-    }
-
-    private static string FormatMapping(ISymbolicExpressionTree t1, ISymbolicExpressionTree t2, Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> map) {
-      var symbolNameMap = new Dictionary<string, string>
-    {
-      {"ProgramRootSymbol", "Prog"},
-      {"StartSymbol","RPB"},
-      {"Multiplication", "$\\times$"},
-      {"Division", "$\\div$"},
-      {"Addition", "$+$"},
-      {"Subtraction", "$-$"},
-      {"Exponential", "$\\exp$"},
-      {"Logarithm", "$\\log$"}
-    };
-
-      var sb = new StringBuilder();
-      var nodeIds = new Dictionary<ISymbolicExpressionTreeNode, string>();
-      int offset = 0;
-      var layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>(x => x.Subtrees);
-      var nodeCoordinates = layoutEngine.CalculateLayout(t1.Root).ToDictionary(n => n.Content, n => new PointF(n.X, n.Y));
-
-      double ws = 0.5;
-      double hs = 0.5;
-
-      var nl = Environment.NewLine;
-      sb.Append("\\documentclass[class=minimal,border=0pt]{standalone}" + nl +
-                 "\\usepackage{tikz}" + nl +
-                 "\\begin{document}" + nl +
-                 "\\begin{tikzpicture}" + nl +
-                 "\\def\\ws{1}" + nl +
-                 "\\def\\hs{0.7}" + nl +
-                 "\\def\\offs{" + offset + "}" + nl);
-
-      foreach (var node in t1.IterateNodesBreadth()) {
-        var id = Guid.NewGuid().ToString();
-        nodeIds[node] = id;
-        var coord = nodeCoordinates[node];
-        var nodeName = symbolNameMap.ContainsKey(node.Symbol.Name) ? symbolNameMap[node.Symbol.Name] : node.ToString();
-        sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\node ({0}) at (\\ws*{1} + \\offs,\\hs*{2}) {{{3}}};", nodeIds[node], ws * coord.X, -hs * coord.Y, EscapeLatexString(nodeName)));
-      }
-
-      foreach (ISymbolicExpressionTreeNode t in t1.IterateNodesBreadth()) {
-        var n = t;
-        foreach (var s in t.Subtrees) {
-          sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\draw ({0}) -- ({1});", nodeIds[n], nodeIds[s]));
-        }
-      }
-
-      nodeCoordinates = layoutEngine.CalculateLayout(t2.Root).ToDictionary(n => n.Content, n => new PointF(n.X, n.Y));
-
-      offset = 20;
-      sb.Append("\\def\\offs{" + offset + "}" + nl);
-      foreach (var node in t2.IterateNodesBreadth()) {
-        var id = Guid.NewGuid().ToString();
-        nodeIds[node] = id;
-        var coord = nodeCoordinates[node];
-        var nodeName = symbolNameMap.ContainsKey(node.Symbol.Name) ? symbolNameMap[node.Symbol.Name] : node.ToString();
-        sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\node ({0}) at (\\ws*{1} + \\offs,\\hs*{2}) {{{3}}};", nodeIds[node], ws * coord.X, -hs * coord.Y, EscapeLatexString(nodeName)));
-      }
-
-      foreach (ISymbolicExpressionTreeNode t in t2.IterateNodesBreadth()) {
-        var n = t;
-        foreach (var s in t.Subtrees) {
-          sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\draw ({0}) -- ({1});", nodeIds[n], nodeIds[s]));
-        }
-      }
-
-      foreach (var p in map) {
-        var id1 = nodeIds[p.Key];
-        var id2 = nodeIds[p.Value];
-
-        sb.Append(string.Format(CultureInfo.InvariantCulture, "\\path[draw,->,color=gray] ({0}) edge[bend left,dashed] ({1});" + Environment.NewLine, id1, id2));
-      }
-      sb.Append("\\end{tikzpicture}" + nl +
-                "\\end{document}" + nl);
-      return sb.ToString();
-    }
-
-    private static string EscapeLatexString(string s) {
-      return s.Replace("\\", "\\\\").Replace("{", "\\{").Replace("}", "\\}").Replace("_", "\\_");
-    }
-  }
-}
Index: anches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/MaxCommonSubtreeSimilarityCalculatorTest.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/MaxCommonSubtreeSimilarityCalculatorTest.cs	(revision 11909)
+++ 	(revision )
@@ -1,71 +1,0 @@
-﻿using System;
-using System.Diagnostics;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
-using HeuristicLab.Problems.DataAnalysis.Symbolic;
-using HeuristicLab.Problems.DataAnalysis.Symbolic.Tests;
-using HeuristicLab.Random;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace HeuristicLab.Tests {
-  /// <summary>
-  /// Summary description for MaxCommonSubtreeSimilarityCalculatorTest
-  /// </summary>
-  [TestClass]
-  public class MaxCommonSubtreeSimilarityCalculatorTest {
-    private readonly ISymbolicExpressionTreeNodeSimilarityComparer comparer;
-
-    private const int N = 100;
-    private const int Rows = 1;
-    private const int Columns = 10;
-
-    public MaxCommonSubtreeSimilarityCalculatorTest() {
-      comparer = new SymbolicExpressionTreeNodeSimilarityComparer();
-    }
-
-    #region Additional test attributes
-    //
-    // You can use the following additional attributes as you write your tests:
-    //
-    // Use ClassInitialize to run code before running the first test in the class
-    // [ClassInitialize()]
-    // public static void MyClassInitialize(TestContext testContext) { }
-    //
-    // Use ClassCleanup to run code after all tests in a class have run
-    // [ClassCleanup()]
-    // public static void MyClassCleanup() { }
-    //
-    // Use TestInitialize to run code before running each test 
-    // [TestInitialize()]
-    // public void MyTestInitialize() { }
-    //
-    // Use TestCleanup to run code after each test has run
-    // [TestCleanup()]
-    // public void MyTestCleanup() { }
-    //
-    #endregion
-
-    [TestMethod]
-    [TestCategory("Problems.DataAnalysis.Symbolic")]
-    [TestProperty("Time", "long")]
-    public void TestMaxCommonSubtreeSimilarityCalculatorPerformance() {
-      var grammar = new TypeCoherentExpressionGrammar();
-      grammar.ConfigureAsDefaultRegressionGrammar();
-      var twister = new MersenneTwister(31415);
-      var ds = Util.CreateRandomDataset(twister, Rows, Columns);
-      var trees = Util.CreateRandomTrees(twister, ds, grammar, N, 1, 100, 0, 0);
-
-      double s = 0;
-      var sw = new Stopwatch();
-
-      sw.Start();
-      for (int i = 0; i < trees.Length - 1; ++i) {
-        for (int j = i + 1; j < trees.Length; ++j) {
-          s += MaxCommonSubtreeSimilarityCalculator.MaxCommonSubtreeSimilarity(trees[i], trees[j], comparer);
-        }
-      }
-      sw.Stop();
-      Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds / 1000.0 + ", Avg. similarity: " + s / (N * (N - 1) / 2));
-      Console.WriteLine(N * (N + 1) / (2 * sw.ElapsedMilliseconds / 1000.0) + " similarity calculations per second.");
-    }
-  }
-}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/SymbolicExpressionTreeBottomUpSimilarityCalculatorTest.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/SymbolicExpressionTreeBottomUpSimilarityCalculatorTest.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/SymbolicExpressionTreeBottomUpSimilarityCalculatorTest.cs	(revision 11910)
@@ -0,0 +1,176 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
+using HeuristicLab.Random;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Tests {
+  [TestClass]
+  public class BottomUpSimilarityCalculatorTest {
+    private readonly SymbolicExpressionTreeBottomUpSimilarityCalculator busCalculator;
+    private readonly SymbolicExpressionImporter importer;
+
+    private const int N = 150;
+    private const int Rows = 1;
+    private const int Columns = 10;
+
+    public BottomUpSimilarityCalculatorTest() {
+      busCalculator = new SymbolicExpressionTreeBottomUpSimilarityCalculator { MatchConstantValues = true, MatchVariableWeights = true };
+      importer = new SymbolicExpressionImporter();
+    }
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis.Symbolic")]
+    [TestProperty("Time", "short")]
+    public void TestBottomUpMatching() {
+      TestMatchedNodes("(+ 1 2)", "(+ 2 1)", 5);
+      TestMatchedNodes("(- 2 1)", "(- 1 2)", 2);
+      TestMatchedNodes("(* (variable 1 X1) (variable 1 X2))", "(* (+ (variable 1 X1) 1) (+ (variable 1 X2) 1))", 2);
+
+      TestMatchedNodes("(* (variable 1 X1) (variable 1 X2))", "(* (+ (variable 1 X1) 1) (variable 1 X2))", 2);
+
+      TestMatchedNodes("(+ (variable 1 a) (variable 1 b))", "(+ (variable 1 a) (variable 1 a))", 1);
+      TestMatchedNodes("(+ (+ (variable 1 a) (variable 1 b)) (variable 1 b))", "(+ (* (+ (variable 1 a) (variable 1 b)) (variable 1 b)) (+ (+ (variable 1 a) (variable 1 b)) (variable 1 b)))", 5);
+
+      TestMatchedNodes(
+        "(* (+ 2.84 (exp (+ (log (/ (variable 2.0539 X5) (variable -9.2452e-1 X6))) (/ (variable 2.0539 X5) (variable -9.2452e-1 X6))))) 2.9081)",
+        "(* (- (variable 9.581e-1 X6) (+ (- (variable 5.1491e-1 X5) 1.614e+1) (+ (/ (variable 2.0539 X5) (variable -9.2452e-1 X6)) (log (/ (variable 2.0539 X5) (variable -9.2452e-1 X6)))))) 2.9081)",
+        9);
+
+      TestMatchedNodes("(+ (exp 2.1033) (/ -4.3072 (variable 2.4691 X7)))", "(/ 1 (+ (/ -4.3072 (variable 2.4691 X7)) (exp 2.1033)))", 6);
+      TestMatchedNodes("(+ (exp 2.1033) (/ -4.3072 (variable 2.4691 X7)))", "(/ 1 (+ (/ (variable 2.4691 X7) -4.3072) (exp 2.1033)))", 4);
+
+      const string expr1 = "(* (- 1.2175e+1 (+ (/ (exp -1.4134e+1) (exp 9.2013)) (exp (log (exp (/ (exp (- (* -4.2461 (variable 2.2634 X5)) (- -9.6267e-1 3.3243))) (- (/ (/ (variable 1.0883 X1) (variable 6.9620e-1 X2)) (log 1.3011e+1)) (variable -4.3098e-1 X7)))))))) (log 1.3011e+1))";
+      const string expr2 = "(* (- 1.2175e+1 (+ (/ (/ (+ (variable 3.0140 X9) (variable 1.3430 X8)) -1.0864e+1) (exp 9.2013)) (exp (log (exp (/ (exp (- (* -4.2461 (variable 2.2634 X5)) (- -9.6267e-1 3.3243))) (- (/ (/ (variable 1.0883 X1) (variable 6.9620e-1 X2)) (log 1.3011e+1)) (variable -4.3098e-1 X7)))))))) (exp (variable 4.0899e-1 X7)))";
+
+      TestMatchedNodes(expr1, expr2, 23);
+
+    }
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis.Symbolic")]
+    [TestProperty("Time", "short")]
+    private void TestMatchedNodes(string expr1, string expr2, int expected) {
+      var t1 = importer.Import(expr1);
+      var t2 = importer.Import(expr2);
+
+      var mapping = busCalculator.ComputeBottomUpMapping(t1.Root, t2.Root);
+      var c = mapping.Count;
+
+      if (c != expected) {
+        throw new Exception("Match count " + c + " is different than expected value " + expected);
+      }
+    }
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis.Symbolic")]
+    [TestProperty("Time", "long")]
+    public void TestBottomUpSimilarityCalculatorPerformance() {
+      var grammar = new TypeCoherentExpressionGrammar();
+      grammar.ConfigureAsDefaultRegressionGrammar();
+      var twister = new MersenneTwister(31415);
+      var ds = Util.CreateRandomDataset(twister, Rows, Columns);
+      var trees = Util.CreateRandomTrees(twister, ds, grammar, N, 1, 100, 0, 0);
+
+      double s = 0;
+      var sw = new Stopwatch();
+
+      sw.Start();
+      for (int i = 0; i < trees.Length - 1; ++i) {
+        for (int j = i + 1; j < trees.Length; ++j) {
+          s += busCalculator.CalculateSimilarity(trees[i], trees[j]);
+        }
+      }
+
+      sw.Stop();
+      Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds / 1000.0 + ", Avg. similarity: " + s / (N * (N - 1) / 2));
+      Console.WriteLine(N * (N + 1) / (2 * sw.ElapsedMilliseconds / 1000.0) + " similarity calculations per second.");
+    }
+
+    private static string FormatMapping(ISymbolicExpressionTree t1, ISymbolicExpressionTree t2, Dictionary<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode> map) {
+      var symbolNameMap = new Dictionary<string, string>
+    {
+      {"ProgramRootSymbol", "Prog"},
+      {"StartSymbol","RPB"},
+      {"Multiplication", "$\\times$"},
+      {"Division", "$\\div$"},
+      {"Addition", "$+$"},
+      {"Subtraction", "$-$"},
+      {"Exponential", "$\\exp$"},
+      {"Logarithm", "$\\log$"}
+    };
+
+      var sb = new StringBuilder();
+      var nodeIds = new Dictionary<ISymbolicExpressionTreeNode, string>();
+      int offset = 0;
+      var layoutEngine = new ReingoldTilfordLayoutEngine<ISymbolicExpressionTreeNode>(x => x.Subtrees);
+      var nodeCoordinates = layoutEngine.CalculateLayout(t1.Root).ToDictionary(n => n.Content, n => new PointF(n.X, n.Y));
+
+      double ws = 0.5;
+      double hs = 0.5;
+
+      var nl = Environment.NewLine;
+      sb.Append("\\documentclass[class=minimal,border=0pt]{standalone}" + nl +
+                 "\\usepackage{tikz}" + nl +
+                 "\\begin{document}" + nl +
+                 "\\begin{tikzpicture}" + nl +
+                 "\\def\\ws{1}" + nl +
+                 "\\def\\hs{0.7}" + nl +
+                 "\\def\\offs{" + offset + "}" + nl);
+
+      foreach (var node in t1.IterateNodesBreadth()) {
+        var id = Guid.NewGuid().ToString();
+        nodeIds[node] = id;
+        var coord = nodeCoordinates[node];
+        var nodeName = symbolNameMap.ContainsKey(node.Symbol.Name) ? symbolNameMap[node.Symbol.Name] : node.ToString();
+        sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\node ({0}) at (\\ws*{1} + \\offs,\\hs*{2}) {{{3}}};", nodeIds[node], ws * coord.X, -hs * coord.Y, EscapeLatexString(nodeName)));
+      }
+
+      foreach (ISymbolicExpressionTreeNode t in t1.IterateNodesBreadth()) {
+        var n = t;
+        foreach (var s in t.Subtrees) {
+          sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\draw ({0}) -- ({1});", nodeIds[n], nodeIds[s]));
+        }
+      }
+
+      nodeCoordinates = layoutEngine.CalculateLayout(t2.Root).ToDictionary(n => n.Content, n => new PointF(n.X, n.Y));
+
+      offset = 20;
+      sb.Append("\\def\\offs{" + offset + "}" + nl);
+      foreach (var node in t2.IterateNodesBreadth()) {
+        var id = Guid.NewGuid().ToString();
+        nodeIds[node] = id;
+        var coord = nodeCoordinates[node];
+        var nodeName = symbolNameMap.ContainsKey(node.Symbol.Name) ? symbolNameMap[node.Symbol.Name] : node.ToString();
+        sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\node ({0}) at (\\ws*{1} + \\offs,\\hs*{2}) {{{3}}};", nodeIds[node], ws * coord.X, -hs * coord.Y, EscapeLatexString(nodeName)));
+      }
+
+      foreach (ISymbolicExpressionTreeNode t in t2.IterateNodesBreadth()) {
+        var n = t;
+        foreach (var s in t.Subtrees) {
+          sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "\\draw ({0}) -- ({1});", nodeIds[n], nodeIds[s]));
+        }
+      }
+
+      foreach (var p in map) {
+        var id1 = nodeIds[p.Key];
+        var id2 = nodeIds[p.Value];
+
+        sb.Append(string.Format(CultureInfo.InvariantCulture, "\\path[draw,->,color=gray] ({0}) edge[bend left,dashed] ({1});" + Environment.NewLine, id1, id2));
+      }
+      sb.Append("\\end{tikzpicture}" + nl +
+                "\\end{document}" + nl);
+      return sb.ToString();
+    }
+
+    private static string EscapeLatexString(string s) {
+      return s.Replace("\\", "\\\\").Replace("{", "\\{").Replace("}", "\\}").Replace("_", "\\_");
+    }
+  }
+}
Index: /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculatorTest.cs
===================================================================
--- /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculatorTest.cs	(revision 11910)
+++ /branches/HeuristicLab.BottomUpTreeDistance/HeuristicLab.Tests/SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculatorTest.cs	(revision 11910)
@@ -0,0 +1,71 @@
+﻿using System;
+using System.Diagnostics;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Problems.DataAnalysis.Symbolic;
+using HeuristicLab.Problems.DataAnalysis.Symbolic.Tests;
+using HeuristicLab.Random;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace HeuristicLab.Tests {
+  /// <summary>
+  /// Summary description for MaxCommonSubtreeSimilarityCalculatorTest
+  /// </summary>
+  [TestClass]
+  public class SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculatorTest {
+    private readonly ISymbolicExpressionTreeNodeSimilarityComparer comparer;
+
+    private const int N = 100;
+    private const int Rows = 1;
+    private const int Columns = 10;
+
+    public SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculatorTest() {
+      comparer = new SymbolicExpressionTreeNodeEqualityComparer();
+    }
+
+    #region Additional test attributes
+    //
+    // You can use the following additional attributes as you write your tests:
+    //
+    // Use ClassInitialize to run code before running the first test in the class
+    // [ClassInitialize()]
+    // public static void MyClassInitialize(TestContext testContext) { }
+    //
+    // Use ClassCleanup to run code after all tests in a class have run
+    // [ClassCleanup()]
+    // public static void MyClassCleanup() { }
+    //
+    // Use TestInitialize to run code before running each test 
+    // [TestInitialize()]
+    // public void MyTestInitialize() { }
+    //
+    // Use TestCleanup to run code after each test has run
+    // [TestCleanup()]
+    // public void MyTestCleanup() { }
+    //
+    #endregion
+
+    [TestMethod]
+    [TestCategory("Problems.DataAnalysis.Symbolic")]
+    [TestProperty("Time", "long")]
+    public void TestMaxCommonSubtreeSimilarityCalculatorPerformance() {
+      var grammar = new TypeCoherentExpressionGrammar();
+      grammar.ConfigureAsDefaultRegressionGrammar();
+      var twister = new MersenneTwister(31415);
+      var ds = Util.CreateRandomDataset(twister, Rows, Columns);
+      var trees = Util.CreateRandomTrees(twister, ds, grammar, N, 1, 100, 0, 0);
+
+      double s = 0;
+      var sw = new Stopwatch();
+
+      sw.Start();
+      for (int i = 0; i < trees.Length - 1; ++i) {
+        for (int j = i + 1; j < trees.Length; ++j) {
+          s += SymbolicExpressionTreeMaxCommonSubtreeSimilarityCalculator.MaxCommonSubtreeSimilarity(trees[i], trees[j], comparer);
+        }
+      }
+      sw.Stop();
+      Console.WriteLine("Elapsed time: " + sw.ElapsedMilliseconds / 1000.0 + ", Avg. similarity: " + s / (N * (N - 1) / 2));
+      Console.WriteLine(N * (N + 1) / (2 * sw.ElapsedMilliseconds / 1000.0) + " similarity calculations per second.");
+    }
+  }
+}
