Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisBuildingBlockAnalyzer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisBuildingBlockAnalyzer.cs	(revision 17090)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisBuildingBlockAnalyzer.cs	(revision 17090)
@@ -0,0 +1,222 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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 System.Text;
+using HeuristicLab.Analysis;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using static HeuristicLab.Problems.DataAnalysis.Symbolic.SymbolicExpressionHashExtensions;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Analyzers {
+  [Item("SymbolicDataAnalysisBuildingBlockAnalyzer", "An analyzer that uses tree hashing to identify the most common subtrees (building blocks) in the population")]
+  [StorableClass]
+  public sealed class SymbolicDataAnalysisBuildingBlockAnalyzer : SymbolicDataAnalysisAnalyzer {
+    private const string BuildingBlocksResultName = "BuildingBlocks";
+    private const string SolutionUniquenessResultName = "SolutionUniqueness";
+    private const string MinimumSubtreeLengthParameterName = "MinimumSubtreeLength";
+    private const string SimplifyTreesParameterName = "SimplifyTrees";
+
+    private Dictionary<ulong, DataRow> hashToRow = new Dictionary<ulong, DataRow>();
+
+    #region parameters
+    public IValueLookupParameter<IntValue> MinimumSubtreeLengthParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters[MinimumSubtreeLengthParameterName]; }
+    }
+
+    public IValueLookupParameter<BoolValue> SimplifyTreesParameter {
+      get { return (IValueLookupParameter<BoolValue>)Parameters[SimplifyTreesParameterName]; }
+    }
+    #endregion
+
+    #region parameter properties
+    public IntValue MinimumSubtreeLength {
+      get { return MinimumSubtreeLengthParameter.ActualValue; }
+    }
+
+    public BoolValue SimplifyTrees {
+      get { return SimplifyTreesParameter.ActualValue; }
+    }
+    #endregion
+
+    public override void InitializeState() {
+      base.InitializeState();
+
+      hashToRow = new Dictionary<ulong, DataRow>();
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if (!Parameters.ContainsKey(SimplifyTreesParameterName)) {
+        Parameters.Add(new ValueLookupParameter<BoolValue>(SimplifyTreesParameterName, new BoolValue(false)));
+      }
+    }
+
+    public SymbolicDataAnalysisBuildingBlockAnalyzer() {
+      Parameters.Add(new ValueLookupParameter<IntValue>(MinimumSubtreeLengthParameterName, new IntValue(3)));
+      Parameters.Add(new ValueLookupParameter<BoolValue>(SimplifyTreesParameterName, new BoolValue(false)));
+    }
+
+    private SymbolicDataAnalysisBuildingBlockAnalyzer(SymbolicDataAnalysisBuildingBlockAnalyzer original, Cloner cloner) : base(original, cloner) {
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SymbolicDataAnalysisBuildingBlockAnalyzer(this, cloner);
+    }
+
+    [StorableConstructor]
+    private SymbolicDataAnalysisBuildingBlockAnalyzer(bool deserializing) : base(deserializing) { }
+
+    private readonly Func<byte[], ulong> hashFunction = HashUtil.JSHash;
+
+    public override IOperation Apply() {
+      DataTable dt;
+
+      if (!ResultCollection.ContainsKey(BuildingBlocksResultName)) {
+        dt = new DataTable(BuildingBlocksResultName);
+        ResultCollection.Add(new Result(BuildingBlocksResultName, dt));
+      } else {
+        dt = (DataTable)ResultCollection[BuildingBlocksResultName].Value;
+      }
+
+      var minLength = MinimumSubtreeLength.Value - 1; // -1 because the HashNode.Size property returns the size without current node (-1)
+      var simplify = SimplifyTrees.Value;
+
+      var expressions = new Dictionary<ulong, string>();
+      var expressionCounts = new Dictionary<ulong, int>();
+
+      int totalCount = 0; // total number of examined subtrees
+
+      var hashes = new List<ulong>();
+      // count hashes
+      foreach (var tree in SymbolicExpressionTree) {
+        var hashNodes = tree.Root.GetSubtree(0).GetSubtree(0).MakeNodes();
+        var simplified = simplify ? hashNodes.Simplify(hashFunction) : hashNodes.Sort(hashFunction);
+        hashes.Add(simplified.Last().CalculatedHashValue); // maybe calculate aggregate hash instead
+
+        for (int i = 0; i < simplified.Length; i++) {
+          HashNode<ISymbolicExpressionTreeNode> s = simplified[i];
+          if (s.IsLeaf || s.Size < minLength) {
+            continue;
+          }
+          ++totalCount;
+          var hash = s.CalculatedHashValue;
+          if (expressions.ContainsKey(hash)) {
+            expressionCounts[hash]++;
+            continue;
+          }
+
+          var sb = new StringBuilder();
+          for (int j = i - s.Size; j < i; ++j) {
+            sb.Append(GetLabel(simplified[j].Data)).Append(" ");
+          }
+          sb.Append(GetLabel(simplified[i].Data));
+          expressions[hash] = sb.ToString();
+          expressionCounts[hash] = 1;
+        }
+      }
+
+      // fill in values for existing rows
+      foreach (var t in hashToRow) {
+        var hash = t.Key;
+        var row = t.Value;
+
+        expressionCounts.TryGetValue(hash, out int count);
+        row.Values.Add(count);
+      }
+
+      var nValues = dt.Rows.Any() ? dt.Rows.Max(x => x.Values.Count) : 0;
+
+      // check if we have new rows
+      foreach (var t in expressionCounts.OrderByDescending(x => x.Value).Take(10)) {
+        var hash = t.Key;
+        var count = t.Value;
+        var label = expressions[hash];
+
+        if (hashToRow.ContainsKey(hash)) {
+          continue;
+        }
+        var row = new DataRow(label) { VisualProperties = { StartIndexZero = true } };
+        if (nValues > 0) {
+          row.Values.AddRange(Enumerable.Repeat<double>(0, nValues - 1)); // pad with zeroes
+        }
+        row.Values.Add(count);
+        dt.Rows.Add(row);
+        hashToRow[hash] = row;
+      }
+
+      // compute solution uniqueness
+      DataTableHistory dth;
+      var counts = hashes.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());
+      if (!ResultCollection.ContainsKey(SolutionUniquenessResultName)) {
+        dth = new DataTableHistory();
+        ResultCollection.Add(new Result(SolutionUniquenessResultName, dth));
+      } else {
+        dth = (DataTableHistory)ResultCollection[SolutionUniquenessResultName].Value;
+      }
+
+      var ct = new DataTable("Unique Solutions");
+      var ctr = new DataRow { VisualProperties = { StartIndexZero = true, ChartType = DataRowVisualProperties.DataRowChartType.Columns } };
+      ctr.Values.AddRange(hashes.Select(x => (double)counts[x]).OrderByDescending(x => x));
+      ct.Rows.Add(ctr);
+      dth.Add(ct);
+
+      var max = dth.Max(x => x.Rows.First().Values.Max());
+      foreach (var table in dth) {
+        table.VisualProperties.YAxisMinimumAuto = false;
+        table.VisualProperties.YAxisMaximumAuto = false;
+        table.VisualProperties.YAxisMinimumFixedValue = 0;
+        table.VisualProperties.YAxisMaximumFixedValue = max;
+      }
+
+      return base.Apply();
+    }
+
+    private static string GetLabel(ISymbolicExpressionTreeNode node) {
+      if (node is ConstantTreeNode constant) {
+        return "C";
+      }
+      if (node is VariableTreeNode variable) {
+        return variable.VariableName;
+      }
+      if (node.Symbol is Addition) {
+        return "+";
+      }
+      if (node.Symbol is Subtraction) {
+        return "-";
+      }
+      if (node.Symbol is Multiplication) {
+        return "*";
+      }
+      if (node.Symbol is Division) {
+        return "/";
+      }
+      return node.Symbol.ToString();
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Crossovers/SymbolicDataAnalysisExpressionDiversityPreservingCrossover.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Crossovers/SymbolicDataAnalysisExpressionDiversityPreservingCrossover.cs	(revision 17090)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Crossovers/SymbolicDataAnalysisExpressionDiversityPreservingCrossover.cs	(revision 17090)
@@ -0,0 +1,146 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+using static HeuristicLab.Problems.DataAnalysis.Symbolic.SymbolicExpressionHashExtensions;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [Item("DiversityCrossover", "Simple crossover operator prioritizing internal nodes according to the given probability.")]
+  [StorableClass]
+  public sealed class SymbolicDataAnalysisExpressionDiversityPreservingCrossover<T> : SymbolicDataAnalysisExpressionCrossover<T> where T : class, IDataAnalysisProblemData {
+
+    private const string InternalCrossoverPointProbabilityParameterName = "InternalCrossoverPointProbability";
+    private const string WindowingParameterName = "Windowing";
+    private const string ProportionalSamplingParameterName = "ProportionalSampling";
+
+    private static readonly Func<byte[], ulong> hashFunction = HashUtil.JSHash;
+
+    #region Parameter Properties
+    public IValueLookupParameter<PercentValue> InternalCrossoverPointProbabilityParameter {
+      get { return (IValueLookupParameter<PercentValue>)Parameters[InternalCrossoverPointProbabilityParameterName]; }
+    }
+
+    public IValueLookupParameter<BoolValue> WindowingParameter {
+      get { return (IValueLookupParameter<BoolValue>)Parameters[WindowingParameterName]; }
+    }
+
+    public IValueLookupParameter<BoolValue> ProportionalSamplingParameter {
+      get { return (IValueLookupParameter<BoolValue>)Parameters[ProportionalSamplingParameterName]; }
+    }
+    #endregion
+
+    #region Properties
+    public PercentValue InternalCrossoverPointProbability {
+      get { return InternalCrossoverPointProbabilityParameter.ActualValue; }
+    }
+
+    public BoolValue Windowing {
+      get { return WindowingParameter.ActualValue; }
+    }
+
+    public BoolValue ProportionalSampling {
+      get { return ProportionalSamplingParameter.ActualValue; }
+    }
+    #endregion
+
+    public SymbolicDataAnalysisExpressionDiversityPreservingCrossover() {
+      name = "DiversityCrossover";
+      Parameters.Add(new ValueLookupParameter<PercentValue>(InternalCrossoverPointProbabilityParameterName, "The probability to select an internal crossover point (instead of a leaf node).", new PercentValue(0.9)));
+      Parameters.Add(new ValueLookupParameter<BoolValue>(WindowingParameterName, "Use proportional sampling with windowing for cutpoint selection.", new BoolValue(false)));
+      Parameters.Add(new ValueLookupParameter<BoolValue>(ProportionalSamplingParameterName, "Select cutpoints proportionally using probabilities as weights instead of randomly.", new BoolValue(true)));
+    }
+
+    private SymbolicDataAnalysisExpressionDiversityPreservingCrossover(SymbolicDataAnalysisExpressionDiversityPreservingCrossover<T> original, Cloner cloner) : base(original, cloner) {
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SymbolicDataAnalysisExpressionDiversityPreservingCrossover<T>(this, cloner);
+    }
+
+    [StorableConstructor]
+    private SymbolicDataAnalysisExpressionDiversityPreservingCrossover(bool deserializing) : base(deserializing) { }
+
+    private static ISymbolicExpressionTreeNode ActualRoot(ISymbolicExpressionTree tree) {
+      return tree.Root.GetSubtree(0).GetSubtree(0);
+    }
+
+    public static ISymbolicExpressionTree Cross(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1, double internalCrossoverPointProbability, int maxLength, int maxDepth, bool windowing, bool proportionalSampling = false) {
+      var leafCrossoverPointProbability = 1 - internalCrossoverPointProbability;
+
+      var nodes0 = ActualRoot(parent0).MakeNodes().Sort(hashFunction);
+      var nodes1 = ActualRoot(parent1).MakeNodes().Sort(hashFunction);
+
+      IList<HashNode<ISymbolicExpressionTreeNode>> sampled0;
+      IList<HashNode<ISymbolicExpressionTreeNode>> sampled1;
+
+      if (proportionalSampling) {
+        var p = internalCrossoverPointProbability;
+        var weights0 = nodes0.Select(x => x.IsLeaf ? 1 - p : p);
+        sampled0 = nodes0.SampleProportionalWithoutRepetition(random, nodes0.Length, weights0, windowing: windowing).ToArray();
+
+        var weights1 = nodes1.Select(x => x.IsLeaf ? 1 - p : p);
+        sampled1 = nodes1.SampleProportionalWithoutRepetition(random, nodes1.Length, weights1, windowing: windowing).ToArray();
+      } else {
+        sampled0 = ChooseNodes(random, nodes0, internalCrossoverPointProbability).ShuffleInPlace(random);
+        sampled1 = ChooseNodes(random, nodes1, internalCrossoverPointProbability).ShuffleInPlace(random);
+      }
+
+      foreach (var selected in sampled0) {
+        var cutpoint = new CutPoint(selected.Data.Parent, selected.Data);
+
+        var maxAllowedDepth = maxDepth - parent0.Root.GetBranchLevel(selected.Data);
+        var maxAllowedLength = maxLength - (parent0.Length - selected.Data.GetLength());
+
+        foreach (var candidate in sampled1) {
+          if (candidate.CalculatedHashValue == selected.CalculatedHashValue
+            || candidate.Data.GetDepth() > maxAllowedDepth
+            || candidate.Data.GetLength() > maxAllowedLength
+            || !cutpoint.IsMatchingPointType(candidate.Data)) {
+            continue;
+          }
+
+          Swap(cutpoint, candidate.Data);
+          return parent0;
+        }
+      }
+      return parent0;
+    }
+
+    public override ISymbolicExpressionTree Crossover(IRandom random, ISymbolicExpressionTree parent0, ISymbolicExpressionTree parent1) {
+      if (this.ExecutionContext == null) {
+        throw new InvalidOperationException("ExecutionContext not set.");
+      }
+
+      var maxDepth = MaximumSymbolicExpressionTreeDepth.Value;
+      var maxLength = MaximumSymbolicExpressionTreeLength.Value;
+
+      var internalCrossoverPointProbability = InternalCrossoverPointProbability.Value;
+      var windowing = Windowing.Value;
+      var proportionalSampling = ProportionalSampling.Value;
+
+      return Cross(random, parent0, parent1, internalCrossoverPointProbability, maxLength, maxDepth, windowing, proportionalSampling);
+    }
+
+    private static List<HashNode<ISymbolicExpressionTreeNode>> ChooseNodes(IRandom random, IEnumerable<HashNode<ISymbolicExpressionTreeNode>> nodes, double internalCrossoverPointProbability) {
+      var list = new List<HashNode<ISymbolicExpressionTreeNode>>();
+
+      var chooseInternal = random.NextDouble() < internalCrossoverPointProbability;
+
+      if (chooseInternal) {
+        list.AddRange(nodes.Where(x => !x.IsLeaf && x.Data.Parent != null));
+      }
+      if (!chooseInternal || list.Count == 0) {
+        list.AddRange(nodes.Where(x => x.IsLeaf && x.Data.Parent != null));
+      }
+
+      return list;
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs	(revision 17090)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashExtensions.cs	(revision 17090)
@@ -0,0 +1,237 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class SymbolicExpressionHashExtensions {
+    public sealed class HashNode<T> : IComparable<HashNode<T>>, IEquatable<HashNode<T>> where T : class {
+      public T Data;
+      public int Arity;
+      public int Size;
+      public bool IsCommutative;
+
+      public bool Enabled;
+      public ulong HashValue;           // the initial (fixed) hash value for this individual node/data
+      public ulong CalculatedHashValue; // the calculated hash value (taking into account the children hash values)
+
+      public delegate void SimplifyAction(ref HashNode<T>[] nodes, int i);
+      public SimplifyAction Simplify;
+
+      public IComparer<T> Comparer;
+
+      public bool IsLeaf => Arity == 0;
+
+      public HashNode(IComparer<T> comparer) {
+        Comparer = comparer;
+      }
+
+      private HashNode() { }
+
+      public int CompareTo(HashNode<T> other) {
+        var res = Comparer.Compare(Data, other.Data);
+        return res == 0 ? CalculatedHashValue.CompareTo(other.CalculatedHashValue) : res;
+      }
+
+      public override string ToString() {
+        return $"{Data} {Arity} {Size} {CalculatedHashValue} {Enabled}";
+      }
+
+      public bool Equals(HashNode<T> other) {
+        return CalculatedHashValue.Equals(other.CalculatedHashValue);
+      }
+
+      public override bool Equals(object obj) {
+        var other = obj as HashNode<T>;
+        if (other != null)
+          return Equals(other);
+        return base.Equals(obj);
+      }
+
+      public override int GetHashCode() {
+        return (int)CalculatedHashValue;
+      }
+
+      public static bool operator ==(HashNode<T> a, HashNode<T> b) {
+        return a.Equals(b);
+      }
+
+      public static bool operator !=(HashNode<T> a, HashNode<T> b) {
+        return !a.Equals(b);
+      }
+    }
+
+    public static ulong ComputeHash<T>(this HashNode<T>[] nodes, int i, Func<byte[], ulong> hashFunction) where T : class {
+      var node = nodes[i];
+      const int size = sizeof(ulong);
+      var hashes = new ulong[node.Arity + 1];
+      var bytes = new byte[(node.Arity + 1) * size];
+
+      for (int j = i - 1, k = 0; k < node.Arity; ++k, j -= 1 + nodes[j].Size) {
+        hashes[k] = nodes[j].CalculatedHashValue;
+      }
+      hashes[node.Arity] = node.HashValue;
+      Buffer.BlockCopy(hashes, 0, bytes, 0, bytes.Length);
+      return hashFunction(bytes);
+    }
+
+    // set the enabled state for the whole subtree rooted at this node 
+    public static void SetEnabled<T>(this HashNode<T>[] nodes, int i, bool enabled) where T : class {
+      nodes[i].Enabled = enabled;
+      for (int j = i - nodes[i].Size; j < i; ++j)
+        nodes[j].Enabled = enabled;
+    }
+
+    public static HashNode<T>[] Simplify<T>(this HashNode<T>[] nodes, Func<byte[], ulong> hashFunction) where T : class {
+      var reduced = nodes.UpdateNodeSizes().Reduce().Sort(hashFunction);
+
+      for (int i = 0; i < reduced.Length; ++i) {
+        var node = reduced[i];
+        if (node.IsLeaf) {
+          continue;
+        }
+        node.Simplify?.Invoke(ref reduced, i);
+      }
+      // detect if anything was simplified
+      var count = 0;
+      foreach (var node in reduced) {
+        if (!node.Enabled) { ++count; }
+      }
+      if (count == 0) {
+        return reduced;
+      }
+
+      var simplified = new HashNode<T>[reduced.Length - count];
+      int j = 0;
+      foreach (var node in reduced) {
+        if (node.Enabled) {
+          simplified[j++] = node;
+        }
+      }
+      return simplified.UpdateNodeSizes().Reduce().Sort(hashFunction);
+    }
+
+    public static HashNode<T>[] Sort<T>(this HashNode<T>[] nodes, Func<byte[], ulong> hashFunction) where T : class {
+      int sort(int a, int b) => nodes[a].CompareTo(nodes[b]);
+
+      for (int i = 0; i < nodes.Length; ++i) {
+        var node = nodes[i];
+
+        if (node.IsLeaf) {
+          continue;
+        }
+
+        if (node.IsCommutative) { // only sort when the argument order does not matter
+          var arity = node.Arity;
+          var size = node.Size;
+
+          if (arity == size) { // all child nodes are terminals
+            Array.Sort(nodes, i - size, size);
+          } else { // i have some non-terminal children
+            var sorted = new HashNode<T>[size];
+            var indices = new int[node.Arity];
+            for (int j = i - 1, k = 0; k < node.Arity; j -= 1 + nodes[j].Size, ++k) {
+              indices[k] = j;
+            }
+            Array.Sort(indices, sort);
+
+            int idx = 0;
+            foreach (var j in indices) {
+              var child = nodes[j];
+              if (!child.IsLeaf) { // must copy complete subtree
+                Array.Copy(nodes, j - child.Size, sorted, idx, child.Size);
+                idx += child.Size;
+              }
+              sorted[idx++] = nodes[j];
+            }
+            Array.Copy(sorted, 0, nodes, i - size, size);
+          }
+        }
+        node.CalculatedHashValue = nodes.ComputeHash(i, hashFunction);
+      }
+      return nodes;
+    }
+
+    /// <summary>
+    /// Get a function node's child indicest
+    /// </summary>
+    /// <typeparam name="T">The data type encapsulated by a hash node</typeparam>
+    /// <param name="nodes">An array of hash nodes with up-to-date node sizes</param>
+    /// <param name="i">The index in the array of hash nodes of the node whose children we want to iterate</param>
+    /// <returns>An array containing child indices</returns>
+    public static int[] IterateChildren<T>(this HashNode<T>[] nodes, int i) where T : class {
+      var node = nodes[i];
+      var arity = node.Arity;
+      var children = new int[arity];
+      var idx = i - 1;
+      for (int j = 0; j < arity; ++j) {
+        children[j] = idx;
+        idx -= 1 + nodes[idx].Size;
+      }
+      return children;
+    }
+
+    public static HashNode<T>[] UpdateNodeSizes<T>(this HashNode<T>[] nodes) where T : class {
+      for (int i = 0; i < nodes.Length; ++i) {
+        var node = nodes[i];
+        if (node.IsLeaf) {
+          node.Size = 0;
+          continue;
+        }
+        node.Size = node.Arity;
+
+        for (int j = i - 1, k = 0; k < node.Arity; j -= 1 + nodes[j].Size, ++k) {
+          node.Size += nodes[j].Size;
+        }
+      }
+      return nodes;
+    }
+
+    private static HashNode<T>[] Reduce<T>(this HashNode<T>[] nodes) where T : class {
+      int count = 0;
+      for (int i = 0; i < nodes.Length; ++i) {
+        var node = nodes[i];
+        if (node.IsLeaf || !node.IsCommutative) {
+          continue;
+        }
+
+        var arity = node.Arity;
+        for (int j = i - 1, k = 0; k < arity; j -= 1 + nodes[j].Size, ++k) {
+          if (node.HashValue == nodes[j].HashValue) {
+            nodes[j].Enabled = false;
+            node.Arity += nodes[j].Arity - 1;
+            ++count;
+          }
+        }
+      }
+      if (count == 0)
+        return nodes;
+
+      var reduced = new HashNode<T>[nodes.Length - count];
+      var idx = 0;
+      foreach (var node in nodes) {
+        if (node.Enabled) { reduced[idx++] = node; }
+      }
+      return reduced.UpdateNodeSizes();
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs	(revision 17090)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/HashUtil.cs	(revision 17090)
@@ -0,0 +1,92 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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.Security.Cryptography;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class HashUtil {
+    // This class contains some hash functions adapted from http://partow.net/programming/hashfunctions/index.html#AvailableHashFunctions
+
+    // A simple hash function from Robert Sedgwicks Algorithms in C book.I've added some simple optimizations to the algorithm in order to speed up its hashing process. 
+    public static ulong RSHash(byte[] input) {
+      const int b = 378551;
+      ulong a = 63689;
+      ulong hash = 0;
+
+      foreach (var v in input) {
+        hash = (hash * a) + v;
+        a *= b;
+      }
+      return hash;
+    }
+
+    // A bitwise hash function written by Justin Sobel
+    public static ulong JSHash(byte[] input) {
+      ulong hash = 1315423911;
+      for (int i = 0; i < input.Length; ++i)
+        hash ^= (hash << 5) + input[i] + (hash >> 2);
+      return hash;
+    }
+
+    // This hash function comes from Brian Kernighan and Dennis Ritchie's book "The C Programming Language". It is a simple hash function using a strange set of possible seeds which all constitute a pattern of 31....31...31 etc, it seems to be very similar to the DJB hash function. 
+    public static ulong BKDRHash(byte[] input) {
+      ulong seed = 131;
+      ulong hash = 0;
+      foreach (var v in input) {
+        hash = (hash * seed) + v;
+      }
+      return hash;
+    }
+
+    // This is the algorithm of choice which is used in the open source SDBM project. The hash function seems to have a good over-all distribution for many different data sets. It seems to work well in situations where there is a high variance in the MSBs of the elements in a data set. 
+    public static ulong SDBMHash(byte[] input) {
+      ulong hash = 0;
+      foreach (var v in input) {
+        hash = v + (hash << 6) + (hash << 16) - hash;
+      }
+      return hash;
+    }
+
+    // An algorithm produced by Professor Daniel J. Bernstein and shown first to the world on the usenet newsgroup comp.lang.c. It is one of the most efficient hash functions ever published. 
+    public static ulong DJBHash(byte[] input) {
+      ulong hash = 5381;
+      foreach (var v in input) {
+        hash = (hash << 5) + hash + v;
+      }
+      return hash;
+    }
+
+    // An algorithm proposed by Donald E.Knuth in The Art Of Computer Programming Volume 3, under the topic of sorting and search chapter 6.4. 
+    public static ulong DEKHash(byte[] input) {
+      ulong hash = (ulong)input.Length;
+      foreach (var v in input) {
+        hash = (hash << 5) ^ (hash >> 27) ^ v;
+      }
+      return hash;
+    }
+
+    public static ulong CryptoHash(HashAlgorithm ha, byte[] input) {
+      return BitConverter.ToUInt64(ha.ComputeHash(input), 0);
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs	(revision 17090)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Hashing/SymbolicExpressionTreeHash.cs	(revision 17090)
@@ -0,0 +1,388 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2019 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 static HeuristicLab.Problems.DataAnalysis.Symbolic.SymbolicExpressionHashExtensions;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  public static class SymbolicExpressionTreeHash {
+    private static readonly Addition add = new Addition();
+    private static readonly Subtraction sub = new Subtraction();
+    private static readonly Multiplication mul = new Multiplication();
+    private static readonly Division div = new Division();
+    private static readonly Logarithm log = new Logarithm();
+    private static readonly Exponential exp = new Exponential();
+    private static readonly Sine sin = new Sine();
+    private static readonly Cosine cos = new Cosine();
+    private static readonly Constant constant = new Constant();
+
+    private static readonly ISymbolicExpressionTreeNodeComparer comparer = new SymbolicExpressionTreeNodeComparer();
+    private static ISymbolicExpressionTreeNode ActualRoot(this ISymbolicExpressionTree tree) => tree.Root.GetSubtree(0).GetSubtree(0);
+
+    #region tree hashing
+    public static ulong[] Hash(this ISymbolicExpressionTree tree, bool simplify = false, bool strict = false) {
+      return tree.ActualRoot().Hash(simplify, strict);
+    }
+
+    public static ulong[] Hash(this ISymbolicExpressionTreeNode node, bool simplify = false, bool strict = false) {
+      ulong hashFunction(byte[] input) => HashUtil.DJBHash(input);
+
+      var hashNodes = simplify ? node.MakeNodes(strict).Simplify(hashFunction) : node.MakeNodes(strict).Sort(hashFunction);
+      var hashes = new ulong[hashNodes.Length];
+      for (int i = 0; i < hashes.Length; ++i) {
+        hashes[i] = hashNodes[i].CalculatedHashValue;
+      }
+      return hashes;
+    }
+
+    public static ulong ComputeHash(this ISymbolicExpressionTree tree, bool simplify = false, bool strict = false) {
+      return ComputeHash(tree.ActualRoot(), simplify, strict);
+    }
+
+    public static ulong ComputeHash(this ISymbolicExpressionTreeNode treeNode, bool simplify = false, bool strict = false) {
+      return treeNode.Hash(simplify, strict).Last();
+    }
+
+    public static HashNode<ISymbolicExpressionTreeNode> ToHashNode(this ISymbolicExpressionTreeNode node, bool strict = false) {
+      var symbol = node.Symbol;
+      var name = symbol.Name;
+      if (node is ConstantTreeNode constantNode) {
+        name = strict ? constantNode.Value.ToString() : symbol.Name;
+      } else if (node is VariableTreeNode variableNode) {
+        name = strict ? variableNode.Weight.ToString() + variableNode.VariableName : variableNode.VariableName;
+      }
+      var hash = (ulong)name.GetHashCode();
+      var hashNode = new HashNode<ISymbolicExpressionTreeNode>(comparer) {
+        Data = node,
+        Arity = node.SubtreeCount,
+        Size = node.SubtreeCount,
+        IsCommutative = node.Symbol is Addition || node.Symbol is Multiplication,
+        Enabled = true,
+        HashValue = hash,
+        CalculatedHashValue = hash
+      };
+      if (symbol is Addition) {
+        hashNode.Simplify = SimplifyAddition;
+      } else if (symbol is Multiplication) {
+        hashNode.Simplify = SimplifyMultiplication;
+      } else if (symbol is Division) {
+        hashNode.Simplify = SimplifyDivision;
+      } else if (symbol is Logarithm || symbol is Exponential || symbol is Sine || symbol is Cosine) {
+        hashNode.Simplify = SimplifyUnaryNode;
+      } else if (symbol is Subtraction) {
+        hashNode.Simplify = SimplifyBinaryNode;
+      }
+      return hashNode;
+    }
+
+    public static HashNode<ISymbolicExpressionTreeNode>[] MakeNodes(this ISymbolicExpressionTree tree, bool strict = false) {
+      return MakeNodes(tree.ActualRoot(), strict);
+    }
+
+    public static HashNode<ISymbolicExpressionTreeNode>[] MakeNodes(this ISymbolicExpressionTreeNode node, bool strict = false) {
+      return node.IterateNodesPostfix().Select(x => x.ToHashNode(strict)).ToArray().UpdateNodeSizes();
+    }
+    #endregion
+
+    #region tree similarity
+    public static double ComputeSimilarity(ISymbolicExpressionTree t1, ISymbolicExpressionTree t2, bool simplify = false, bool strict = false) {
+      return ComputeSimilarity(t1.ActualRoot(), t2.ActualRoot(), simplify, strict);
+    }
+
+    public static double ComputeSimilarity(ISymbolicExpressionTreeNode t1, ISymbolicExpressionTreeNode t2, bool simplify = false, bool strict = false) {
+      var lh = t1.Hash(simplify, strict);
+      var rh = t2.Hash(simplify, strict);
+
+      Array.Sort(lh);
+      Array.Sort(rh);
+
+      return ComputeSimilarity(lh, rh);
+    }
+
+    // requires lhs and rhs to be sorted
+    public static int IntersectCount(this ulong[] lh, ulong[] rh) {
+      int count = 0;
+      for (int i = 0, j = 0; i < lh.Length && j < rh.Length;) {
+        var h1 = lh[i];
+        var h2 = rh[j];
+        if (h1 == h2) {
+          ++count;
+          ++i;
+          ++j;
+        } else if (h1 < h2) {
+          ++i;
+        } else if (h1 > h2) {
+          ++j;
+        }
+      }
+      return count;
+    }
+
+    public static IEnumerable<ulong> Intersect(this ulong[] lh, ulong[] rh) {
+      for (int i = 0, j = 0; i < lh.Length && j < rh.Length;) {
+        var h1 = lh[i];
+        var h2 = rh[j];
+        if (h1 == h2) {
+          yield return h1;
+          ++i;
+          ++j;
+        } else if (h1 < h2) {
+          ++i;
+        } else if (h1 > h2) {
+          ++j;
+        }
+      }
+    }
+
+    // this will only work if lh and rh are sorted
+    public static double ComputeSimilarity(ulong[] lh, ulong[] rh) {
+      return 2d * IntersectCount(lh, rh) / (lh.Length + rh.Length);
+    }
+
+    public static double ComputeAverageSimilarity(IList<ISymbolicExpressionTree> trees, bool simplify = false, bool strict = false) {
+      var total = trees.Count * (trees.Count - 1) / 2;
+      double avg = 0;
+      var hashes = new ulong[trees.Count][];
+      // build hash arrays
+      for (int i = 0; i < trees.Count; ++i) {
+        var nodes = trees[i].MakeNodes(strict);
+        hashes[i] = (simplify ? nodes.Simplify(HashUtil.DJBHash) : nodes.Sort(HashUtil.DJBHash)).Select(x => x.CalculatedHashValue).ToArray();
+        Array.Sort(hashes[i]);
+      }
+      // compute similarity matrix
+      for (int i = 0; i < trees.Count - 1; ++i) {
+        for (int j = i + 1; j < trees.Count; ++j) {
+          avg += ComputeSimilarity(hashes[i], hashes[j]);
+        }
+      }
+      return avg / total;
+    }
+
+    public static double[,] ComputeSimilarityMatrix(IList<ISymbolicExpressionTree> trees, bool simplify = false, bool strict = false) {
+      var sim = new double[trees.Count, trees.Count];
+      var hashes = new ulong[trees.Count][];
+      // build hash arrays
+      for (int i = 0; i < trees.Count; ++i) {
+        var nodes = trees[i].MakeNodes(strict);
+        hashes[i] = (simplify ? nodes.Simplify(HashUtil.DJBHash) : nodes.Sort(HashUtil.DJBHash)).Select(x => x.CalculatedHashValue).ToArray();
+        Array.Sort(hashes[i]);
+      }
+      // compute similarity matrix
+      for (int i = 0; i < trees.Count - 1; ++i) {
+        for (int j = i + 1; j < trees.Count; ++j) {
+          sim[i, j] = sim[j, i] = ComputeSimilarity(hashes[i], hashes[j]);
+        }
+      }
+      return sim;
+    }
+    #endregion
+
+    #region parse a nodes array back into a tree
+    public static ISymbolicExpressionTree ToTree(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      var root = new ProgramRootSymbol().CreateTreeNode();
+      var start = new StartSymbol().CreateTreeNode();
+      root.AddSubtree(start);
+      start.AddSubtree(nodes.ToSubtree());
+      return new SymbolicExpressionTree(root);
+    }
+
+    public static ISymbolicExpressionTreeNode ToSubtree(this HashNode<ISymbolicExpressionTreeNode>[] nodes) {
+      var treeNodes = nodes.Select(x => x.Data.Symbol.CreateTreeNode()).ToArray();
+
+      for (int i = nodes.Length - 1; i >= 0; --i) {
+        var node = nodes[i];
+
+        if (node.IsLeaf) {
+          if (node.Data is VariableTreeNode variable) {
+            var variableTreeNode = (VariableTreeNode)treeNodes[i];
+            variableTreeNode.VariableName = variable.VariableName;
+            variableTreeNode.Weight = variable.Weight;
+          } else if (node.Data is ConstantTreeNode @const) {
+            var constantTreeNode = (ConstantTreeNode)treeNodes[i];
+            constantTreeNode.Value = @const.Value;
+          }
+          continue;
+        }
+
+        var treeNode = treeNodes[i];
+
+        foreach (var j in nodes.IterateChildren(i)) {
+          treeNode.AddSubtree(treeNodes[j]);
+        }
+      }
+
+      return treeNodes.Last();
+    }
+
+    private static T CreateTreeNode<T>(this ISymbol symbol) where T : class, ISymbolicExpressionTreeNode {
+      return (T)symbol.CreateTreeNode();
+    }
+    #endregion
+
+    #region tree simplification
+    // these simplification methods rely on the assumption that child nodes of the current node have already been simplified
+    // (in other words simplification should be applied in a bottom-up fashion)
+    public static ISymbolicExpressionTree Simplify(ISymbolicExpressionTree tree) {
+      ulong hashFunction(byte[] bytes) => HashUtil.JSHash(bytes);
+      var root = tree.Root.GetSubtree(0).GetSubtree(0);
+      var nodes = root.MakeNodes();
+      var simplified = nodes.Simplify(hashFunction);
+      return simplified.ToTree();
+    }
+
+    public static void SimplifyAddition(ref HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      // simplify additions of terms by eliminating terms with the same symbol and hash
+      var children = nodes.IterateChildren(i);
+
+      // we always assume the child nodes are sorted
+      var curr = children[0];
+      var node = nodes[i];
+
+      foreach (var j in children.Skip(1)) {
+        if (nodes[j] == nodes[curr]) {
+          nodes.SetEnabled(j, false);
+          node.Arity--;
+        } else {
+          curr = j;
+        }
+      }
+      if (node.Arity == 1) { // if the arity is 1 we don't need the addition node at all
+        node.Enabled = false;
+      }
+    }
+
+    // simplify multiplications by reducing constants and div terms
+    public static void SimplifyMultiplication(ref HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var node = nodes[i];
+      var children = nodes.IterateChildren(i);
+
+      for (int j = 0; j < children.Length; ++j) {
+        var c = children[j];
+        var child = nodes[c];
+
+        if (!child.Enabled)
+          continue;
+
+        var symbol = child.Data.Symbol;
+        if (symbol is Constant) {
+          for (int k = j + 1; k < children.Length; ++k) {
+            var d = children[k];
+            if (nodes[d].Data.Symbol is Constant) {
+              nodes[d].Enabled = false;
+              node.Arity--;
+            } else {
+              break;
+            }
+          }
+        } else if (symbol is Division) {
+          var div = nodes[c];
+          var denominator =
+            div.Arity == 1 ?
+            nodes[c - 1] :                    // 1 / x is expressed as div(x) (with a single child)
+            nodes[c - nodes[c - 1].Size - 2]; // assume division always has arity 1 or 2
+
+          foreach (var d in children) {
+            if (nodes[d].Enabled && nodes[d] == denominator) {
+              nodes[c].Enabled = nodes[d].Enabled = denominator.Enabled = false;
+              node.Arity -= 2; // matching child + division node
+              break;
+            }
+          }
+        }
+
+        if (node.Arity == 0) { // if everything is simplified this node becomes constant
+          var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+          constantTreeNode.Value = 1;
+          nodes[i] = constantTreeNode.ToHashNode();
+        } else if (node.Arity == 1) { // when i have only 1 arg left i can skip this node
+          node.Enabled = false;
+        }
+      }
+    }
+
+    public static void SimplifyDivision(ref HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var node = nodes[i];
+      var children = nodes.IterateChildren(i);
+
+      var tmp = nodes;
+
+      if (children.All(x => tmp[x].Data.Symbol is Constant)) {
+        var v = ((ConstantTreeNode)nodes[children.First()].Data).Value;
+        if (node.Arity == 1) {
+          v = 1 / v;
+        } else if (node.Arity > 1) {
+          foreach (var j in children.Skip(1)) {
+            v /= ((ConstantTreeNode)nodes[j].Data).Value;
+          }
+        }
+        var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+        constantTreeNode.Value = v;
+        nodes[i] = constantTreeNode.ToHashNode();
+        return;
+      }
+
+      var nominator = nodes[children[0]];
+      foreach (var j in children.Skip(1)) {
+        var denominator = nodes[j];
+        if (nominator == denominator) {
+          // disable all the children of the division node (nominator and children + denominator and children)
+          nominator.Enabled = denominator.Enabled = false;
+          node.Arity -= 2; // nominator + denominator
+        }
+        if (node.Arity == 0) {
+          var constantTreeNode = constant.CreateTreeNode<ConstantTreeNode>();
+          constantTreeNode.Value = 1; // x / x = 1
+          nodes[i] = constantTreeNode.ToHashNode();
+        }
+      }
+    }
+
+    public static void SimplifyUnaryNode(ref HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      // check if the child of the unary node is a constant, then the whole node can be simplified
+      var parent = nodes[i];
+      var child = nodes[i - 1];
+
+      var parentSymbol = parent.Data.Symbol;
+      var childSymbol = child.Data.Symbol;
+
+      if (childSymbol is Constant) {
+        nodes[i].Enabled = false;
+      } else if ((parentSymbol is Exponential && childSymbol is Logarithm) || (parentSymbol is Logarithm && childSymbol is Exponential)) {
+        child.Enabled = parent.Enabled = false;
+      }
+    }
+
+    public static void SimplifyBinaryNode(ref HashNode<ISymbolicExpressionTreeNode>[] nodes, int i) {
+      var children = nodes.IterateChildren(i);
+      var tmp = nodes;
+      if (children.All(x => tmp[x].Data.Symbol is Constant)) {
+        foreach (var j in children) {
+          nodes[j].Enabled = false;
+        }
+        nodes[i] = constant.CreateTreeNode().ToHashNode();
+      }
+    }
+    #endregion
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 17089)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 17090)
@@ -128,4 +128,5 @@
   <ItemGroup>
     <Compile Include="Analyzers\SymbolicDataAnalysisBottomUpDiversityAnalyzer.cs" />
+    <Compile Include="Analyzers\SymbolicDataAnalysisBuildingBlockAnalyzer.cs" />
     <Compile Include="Analyzers\SymbolicDataAnalysisSingleObjectivePruningAnalyzer.cs" />
     <Compile Include="Analyzers\SymbolicDataAnalysisSingleObjectiveValidationParetoBestSolutionAnalyzer.cs" />
@@ -146,8 +147,12 @@
     <Compile Include="Converters\DerivativeCalculator.cs" />
     <Compile Include="Converters\TreeToAutoDiffTermConverter.cs" />
+    <Compile Include="Crossovers\SymbolicDataAnalysisExpressionDiversityPreservingCrossover.cs" />
     <Compile Include="Formatters\InfixExpressionFormatter.cs" />
     <Compile Include="Formatters\TSQLExpressionFormatter.cs" />
     <Compile Include="Formatters\SymbolicDataAnalysisExpressionMathematicaFormatter.cs" />
     <Compile Include="Formatters\SymbolicDataAnalysisExpressionCSharpFormatter.cs" />
+    <Compile Include="Hashing\HashExtensions.cs" />
+    <Compile Include="Hashing\HashUtil.cs" />
+    <Compile Include="Hashing\SymbolicExpressionTreeHash.cs" />
     <Compile Include="Importer\InfixExpressionParser.cs" />
     <Compile Include="Importer\SymbolicExpressionImporter.cs" />
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModel.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModel.cs	(revision 17089)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModel.cs	(revision 17090)
@@ -118,14 +118,12 @@
       ConstantTreeNode alphaTreeNode = null;
       ConstantTreeNode betaTreeNode = null;
-      // check if model has been scaled previously by analyzing the structure of the tree
+      // check if model has a structure that can be re-used for scaling
       var startNode = SymbolicExpressionTree.Root.GetSubtree(0);
-      if (startNode.GetSubtree(0).Symbol is Addition) {
-        var addNode = startNode.GetSubtree(0);
-        if (addNode.SubtreeCount == 2 && addNode.GetSubtree(0).Symbol is Multiplication && addNode.GetSubtree(1).Symbol is Constant) {
-          alphaTreeNode = addNode.GetSubtree(1) as ConstantTreeNode;
-          var mulNode = addNode.GetSubtree(0);
-          if (mulNode.SubtreeCount == 2 && mulNode.GetSubtree(1).Symbol is Constant) {
-            betaTreeNode = mulNode.GetSubtree(1) as ConstantTreeNode;
-          }
+      var addNode = startNode.GetSubtree(0);
+      if (addNode.Symbol is Addition && addNode.SubtreeCount == 2) {
+        alphaTreeNode = (ConstantTreeNode)addNode.Subtrees.LastOrDefault(n => n is ConstantTreeNode);
+        var mulNode = addNode.Subtrees.FirstOrDefault(n => n.Symbol is Multiplication);
+        if (mulNode != null) {
+          betaTreeNode = (ConstantTreeNode)mulNode.Subtrees.LastOrDefault(n => n is ConstantTreeNode);
         }
       }
