Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisMultiObjectiveTrainingBestSolutionAnalyzer.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisMultiObjectiveTrainingBestSolutionAnalyzer.cs	(revision 13186)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisMultiObjectiveTrainingBestSolutionAnalyzer.cs	(revision 13241)
@@ -20,5 +20,4 @@
 #endregion
 
-using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -42,4 +41,5 @@
     private const string TrainingBestSolutionQualitiesParameterName = "Best training solution qualities";
     private const string UpdateAlwaysParameterName = "Always update best solutions";
+    private const string TrainingBestSolutionParameterName = "Best training solution";
 
     #region parameter properties
@@ -55,14 +55,15 @@
     #endregion
     #region properties
-    public ItemList<T> TrainingBestSolutions {
+    private ItemList<T> TrainingBestSolutions {
       get { return TrainingBestSolutionsParameter.ActualValue; }
       set { TrainingBestSolutionsParameter.ActualValue = value; }
     }
-    public ItemList<DoubleArray> TrainingBestSolutionQualities {
+    private ItemList<DoubleArray> TrainingBestSolutionQualities {
       get { return TrainingBestSolutionQualitiesParameter.ActualValue; }
       set { TrainingBestSolutionQualitiesParameter.ActualValue = value; }
     }
-    public BoolValue UpdateAlways {
-      get { return UpdateAlwaysParameter.Value; }
+    public bool UpdateAlways {
+      get { return UpdateAlwaysParameter.Value.Value; }
+      set { UpdateAlwaysParameter.Value.Value = value; }
     }
     #endregion
@@ -97,7 +98,11 @@
       }
 
+      if (!results.ContainsKey(TrainingBestSolutionParameterName)) {
+        results.Add(new Result(TrainingBestSolutionParameterName, "", typeof(ISymbolicDataAnalysisSolution)));
+      }
+
       //if the pareto front of best solutions shall be updated regardless of the quality, the list initialized empty to discard old solutions
-      IList<double[]> trainingBestQualities;
-      if (UpdateAlways.Value) {
+      List<double[]> trainingBestQualities;
+      if (UpdateAlways) {
         trainingBestQualities = new List<double[]>();
       } else {
@@ -105,42 +110,46 @@
       }
 
-      #region find best trees
-      IList<int> nonDominatedIndexes = new List<int>();
-      ISymbolicExpressionTree[] tree = SymbolicExpressionTree.ToArray();
+      ISymbolicExpressionTree[] trees = SymbolicExpressionTree.ToArray();
       List<double[]> qualities = Qualities.Select(x => x.ToArray()).ToList();
       bool[] maximization = Maximization.ToArray();
-      List<double[]> newNonDominatedQualities = new List<double[]>();
-      for (int i = 0; i < tree.Length; i++) {
-        if (IsNonDominated(qualities[i], trainingBestQualities, maximization) &&
-          IsNonDominated(qualities[i], qualities, maximization)) {
-          if (!newNonDominatedQualities.Contains(qualities[i], new DoubleArrayComparer())) {
-            newNonDominatedQualities.Add(qualities[i]);
-            nonDominatedIndexes.Add(i);
+
+      var nonDominatedInvididuals = new[] { new { Tree = default(ISymbolicExpressionTree), Qualities = default(double[]) } }.ToList();
+      nonDominatedInvididuals.Clear();
+
+      // build list of new non-dominated solutions
+      for (int i = 0; i < trees.Length; i++) {
+        if (IsNonDominated(qualities[i], nonDominatedInvididuals.Select(ind => ind.Qualities), maximization) &&
+            IsNonDominated(qualities[i], trainingBestQualities, maximization)) {
+          for (int j = nonDominatedInvididuals.Count - 1; j >= 0; j--) {
+            if (IsBetterOrEqual(qualities[i], nonDominatedInvididuals[j].Qualities, maximization)) {
+              nonDominatedInvididuals.RemoveAt(j);
+            }
           }
+          nonDominatedInvididuals.Add(new { Tree = trees[i], Qualities = qualities[i] });
         }
       }
-      #endregion
+
+      var nonDominatedSolutions = nonDominatedInvididuals.Select(x => new { Solution = CreateSolution(x.Tree, x.Qualities), Qualities = x.Qualities }).ToList();
+      nonDominatedSolutions.ForEach(s => s.Solution.Name = string.Join(",", s.Qualities.Select(q => q.ToString())));
+
       #region update Pareto-optimal solution archive
-      if (nonDominatedIndexes.Count > 0) {
-        ItemList<DoubleArray> nonDominatedQualities = new ItemList<DoubleArray>();
-        ItemList<T> nonDominatedSolutions = new ItemList<T>();
-        // add all new non-dominated solutions to the archive
-        foreach (var index in nonDominatedIndexes) {
-          T solution = CreateSolution(tree[index], qualities[index]);
-          nonDominatedSolutions.Add(solution);
-          nonDominatedQualities.Add(new DoubleArray(qualities[index]));
-        }
-        // add old non-dominated solutions only if they are not dominated by one of the new solutions
+      if (nonDominatedSolutions.Count > 0) {
+        //add old non-dominated solutions only if they are not dominated by one of the new solutions
         for (int i = 0; i < trainingBestQualities.Count; i++) {
-          if (IsNonDominated(trainingBestQualities[i], newNonDominatedQualities, maximization)) {
-            if (!newNonDominatedQualities.Contains(trainingBestQualities[i], new DoubleArrayComparer())) {
-              nonDominatedSolutions.Add(TrainingBestSolutions[i]);
-              nonDominatedQualities.Add(TrainingBestSolutionQualities[i]);
-            }
+          if (IsNonDominated(trainingBestQualities[i], nonDominatedSolutions.Select(x => x.Qualities), maximization)) {
+            nonDominatedSolutions.Add(new { Solution = TrainingBestSolutions[i], Qualities = TrainingBestSolutionQualities[i].ToArray() });
           }
         }
 
-        results[TrainingBestSolutionsParameter.Name].Value = nonDominatedSolutions;
-        results[TrainingBestSolutionQualitiesParameter.Name].Value = nonDominatedQualities;
+        //assumes the the first objective is always the accuracy
+        var sortedNonDominatedSolutions = maximization[0]
+          ? nonDominatedSolutions.OrderByDescending(x => x.Qualities[0])
+          : nonDominatedSolutions.OrderBy(x => x.Qualities[0]);
+        var trainingBestSolution = sortedNonDominatedSolutions.Select(s => s.Solution).First();
+        results[TrainingBestSolutionParameterName].Value = trainingBestSolution;
+        TrainingBestSolutions = new ItemList<T>(sortedNonDominatedSolutions.Select(x => x.Solution));
+        results[TrainingBestSolutionsParameter.Name].Value = TrainingBestSolutions;
+        TrainingBestSolutionQualities = new ItemList<DoubleArray>(sortedNonDominatedSolutions.Select(x => new DoubleArray(x.Qualities)));
+        results[TrainingBestSolutionQualitiesParameter.Name].Value = TrainingBestSolutionQualities;
       }
       #endregion
@@ -148,36 +157,25 @@
     }
 
-    private class DoubleArrayComparer : IEqualityComparer<double[]> {
-      public bool Equals(double[] x, double[] y) {
-        if (y.Length != x.Length) throw new ArgumentException();
-        for (int i = 0; i < x.Length; i++) {
-          if (!x[i].IsAlmost(y[i])) return false;
-        }
-        return true;
-      }
-
-      public int GetHashCode(double[] obj) {
-        int c = obj.Length;
-        for (int i = 0; i < obj.Length; i++)
-          c ^= obj[i].GetHashCode();
-        return c;
-      }
-    }
-
     protected abstract T CreateSolution(ISymbolicExpressionTree bestTree, double[] bestQuality);
 
-    private bool IsNonDominated(double[] point, IList<double[]> points, bool[] maximization) {
+    private bool IsNonDominated(double[] point, IEnumerable<double[]> points, bool[] maximization) {
       foreach (var refPoint in points) {
-        bool refPointDominatesPoint = true;
-        for (int i = 0; i < point.Length; i++) {
-          refPointDominatesPoint &= IsBetterOrEqual(refPoint[i], point[i], maximization[i]);
-        }
+        bool refPointDominatesPoint = IsBetterOrEqual(refPoint, point, maximization);
         if (refPointDominatesPoint) return false;
       }
       return true;
     }
+
+    private bool IsBetterOrEqual(double[] lhs, double[] rhs, bool[] maximization) {
+      for (int i = 0; i < lhs.Length; i++) {
+        var result = IsBetterOrEqual(lhs[i], rhs[i], maximization[i]);
+        if (!result) return false;
+      }
+      return true;
+    }
+
     private bool IsBetterOrEqual(double lhs, double rhs, bool maximization) {
-      if (maximization) return lhs > rhs;
-      else return lhs < rhs;
+      if (maximization) return lhs >= rhs;
+      else return lhs <= rhs;
     }
   }
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 13186)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 13241)
@@ -138,4 +138,6 @@
     <Compile Include="Importer\Token.cs" />
     <Compile Include="Interfaces\IModelBacktransformator.cs" />
+    <Compile Include="SymbolicDataAnalysisExpressionTreeSimplificationOperator.cs" />
+    <Compile Include="SymbolicDataAnalysisModelComplexityCalculator.cs" />
     <Compile Include="SymbolicExpressionTreeBacktransformator.cs" />
     <Compile Include="SymbolicDataAnalysisExpressionPruningOperator.cs" />
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplificationOperator.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplificationOperator.cs	(revision 13241)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplificationOperator.cs	(revision 13241)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 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.Operators;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [Item("SymbolicExpressionTreeSimplificationOperator", "Simplfies symbolic expression trees encoding a mathematical formula.")]
+  [StorableClass]
+  public class SymbolicDataAnalysisExpressionTreeSimplificationOperator : SingleSuccessorOperator, ISymbolicExpressionTreeOperator {
+    private const string SymbolicExpressionTreeParameterName = "SymbolicExpressionTree";
+
+    public ILookupParameter<ISymbolicExpressionTree> SymbolicExpressionTreeParameter {
+      get { return (ILookupParameter<ISymbolicExpressionTree>)Parameters[SymbolicExpressionTreeParameterName]; }
+    }
+
+    private readonly SymbolicDataAnalysisExpressionTreeSimplifier simplifier = new SymbolicDataAnalysisExpressionTreeSimplifier();
+
+    [StorableConstructor]
+    protected SymbolicDataAnalysisExpressionTreeSimplificationOperator(bool deserializing) : base(deserializing) { }
+    protected SymbolicDataAnalysisExpressionTreeSimplificationOperator(SymbolicDataAnalysisExpressionTreeSimplificationOperator original, Cloner cloner)
+      : base(original, cloner) { }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SymbolicDataAnalysisExpressionTreeSimplificationOperator(this, cloner);
+    }
+
+    public SymbolicDataAnalysisExpressionTreeSimplificationOperator() {
+      Parameters.Add(new LookupParameter<ISymbolicExpressionTree>(SymbolicExpressionTreeParameterName, "The symbolic expression tree to simplify."));
+    }
+
+    public override IOperation Apply() {
+      var tree = SymbolicExpressionTreeParameter.ActualValue;
+      var simplifiedTree = simplifier.Simplify(tree);
+      simplifiedTree = simplifier.Simplify(simplifiedTree);
+      SymbolicExpressionTreeParameter.ActualValue = simplifiedTree;
+
+      return base.Apply();
+    }
+  }
+}
Index: trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModelComplexityCalculator.cs
===================================================================
--- trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModelComplexityCalculator.cs	(revision 13241)
+++ trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModelComplexityCalculator.cs	(revision 13241)
@@ -0,0 +1,134 @@
+﻿#region License Information
+
+/* HeuristicLab
+ * Copyright (C) 2002-2015 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 {
+  public static class SymbolicDataAnalysisModelComplexityCalculator {
+    public static double CalculateComplexity(ISymbolicExpressionTree tree) {
+      return CalculateComplexity(tree.Root);
+    }
+    public static double CalculateComplexity(ISymbolicExpressionTreeNode treeNode) {
+      var node = treeNode;
+      if (node.Symbol is ProgramRootSymbol) node = node.GetSubtree(0);
+      if (node.Symbol is StartSymbol) node = node.GetSubtree(0);
+
+      switch (OpCodes.MapSymbolToOpCode(node)) {
+        case OpCodes.Constant: {
+            return 1;
+          }
+        case OpCodes.Variable: {
+            return 2;
+          }
+        case OpCodes.Add: {
+            double complexity = 0;
+            for (int i = 0; i < node.SubtreeCount; i++) {
+              complexity += CalculateComplexity(node.GetSubtree(i));
+            }
+            return complexity;
+          }
+        case OpCodes.Sub: {
+            double complexity = 0;
+            for (int i = 0; i < node.SubtreeCount; i++) {
+              complexity += CalculateComplexity(node.GetSubtree(i));
+            }
+            return complexity;
+          }
+        case OpCodes.Mul: {
+            double complexity = 1;
+            for (int i = 0; i < node.SubtreeCount; i++) {
+              var nodeComplexity = CalculateComplexity(node.GetSubtree(i));
+              complexity *= nodeComplexity + 1;
+            }
+            return complexity;
+          }
+        case OpCodes.Div: {
+            double complexity = 1;
+            for (int i = 0; i < node.SubtreeCount; i++) {
+              var nodeComplexity = CalculateComplexity(node.GetSubtree(i));
+              complexity *= nodeComplexity + 1;
+            }
+            return complexity;
+          }
+        case OpCodes.Sin: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            return Math.Pow(2.0, complexity);
+          }
+        case OpCodes.Cos: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            return Math.Pow(2.0, complexity);
+          }
+        case OpCodes.Tan: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            return Math.Pow(2.0, complexity);
+          }
+        case OpCodes.Exp: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            return Math.Pow(2.0, complexity);
+          }
+        case OpCodes.Log: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            return Math.Pow(2.0, complexity);
+          }
+        case OpCodes.Square: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            return complexity * complexity;
+          }
+        case OpCodes.SquareRoot: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            return complexity * complexity * complexity;
+          }
+        case OpCodes.Power: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            var exponentNode = node.GetSubtree(1) as ConstantTreeNode;
+            if (exponentNode != null) {
+              double exponent = exponentNode.Value;
+              if (exponent < 0) exponent = Math.Abs(exponent);
+              if (exponent < 1) exponent = 1 / exponent;
+              return Math.Pow(complexity, Math.Round(exponent));
+            }
+
+            double exponentComplexity = CalculateComplexity(node.GetSubtree(1));
+            return Math.Pow(complexity, 2 * exponentComplexity);
+          }
+        case OpCodes.Root: {
+            double complexity = CalculateComplexity(node.GetSubtree(0));
+            var rootNode = node.GetSubtree(1) as ConstantTreeNode;
+            if (rootNode != null) {
+              double root = rootNode.Value;
+              if (root < 0) root = Math.Abs(root);
+              if (root < 1) root = 1 / root;
+              return Math.Pow(complexity, Math.Round(root));
+            }
+
+            double rootComplexity = CalculateComplexity(node.GetSubtree(1));
+            return Math.Pow(complexity, 2 * rootComplexity);
+          }
+
+        default:
+          throw new NotSupportedException();
+      }
+    }
+  }
+}
