Index: /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/HeuristicLab.Algorithms.DataAnalysis.Views-3.4.csproj
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/HeuristicLab.Algorithms.DataAnalysis.Views-3.4.csproj	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/HeuristicLab.Algorithms.DataAnalysis.Views-3.4.csproj	(revision 15131)
@@ -125,4 +125,10 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="OneFactorClassificationModelView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="OneFactorClassificationModelView.Designer.cs">
+      <DependentUpon>OneFactorClassificationModelView.cs</DependentUpon>
+    </Compile>
     <Compile Include="RandomForestClassificationSolutionView.cs">
       <SubType>UserControl</SubType>
Index: /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/OneFactorClassificationModelView.Designer.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/OneFactorClassificationModelView.Designer.cs	(revision 15131)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/OneFactorClassificationModelView.Designer.cs	(revision 15131)
@@ -0,0 +1,126 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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
+
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  partial class OneFactorClassificationModelView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.dataGridView = new System.Windows.Forms.DataGridView();
+      this.variableValueColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+      this.classcolumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+      this.variableLabel = new System.Windows.Forms.Label();
+      this.DefaultClassValueLabel = new System.Windows.Forms.Label();
+      ((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // dataGridView
+      // 
+      this.dataGridView.AllowUserToAddRows = false;
+      this.dataGridView.AllowUserToDeleteRows = false;
+      this.dataGridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+            | System.Windows.Forms.AnchorStyles.Left)
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+      this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.variableValueColumn,
+            this.classcolumn});
+      this.dataGridView.Location = new System.Drawing.Point(3, 32);
+      this.dataGridView.Name = "dataGridView";
+      this.dataGridView.ReadOnly = true;
+      this.dataGridView.Size = new System.Drawing.Size(389, 230);
+      this.dataGridView.TabIndex = 0;
+      // 
+      // intervalstart
+      // 
+      this.variableValueColumn.HeaderText = "Factor Value";
+      this.variableValueColumn.Name = "Factor value";
+      this.variableValueColumn.ReadOnly = true;
+      // 
+      // classcolumn
+      // 
+      this.classcolumn.HeaderText = "Class";
+      this.classcolumn.Name = "classcolumn";
+      this.classcolumn.ReadOnly = true;
+      // 
+      // variableLabel
+      // 
+      this.variableLabel.AutoSize = true;
+      this.variableLabel.Location = new System.Drawing.Point(3, 10);
+      this.variableLabel.Name = "variableLabel";
+      this.variableLabel.Size = new System.Drawing.Size(48, 13);
+      this.variableLabel.TabIndex = 1;
+      this.variableLabel.Text = "Variable:";
+      // 
+      // DefaultClassValueLabel
+      // 
+      this.DefaultClassValueLabel.Anchor = System.Windows.Forms.AnchorStyles.Top;
+      this.DefaultClassValueLabel.AutoSize = true;
+      this.DefaultClassValueLabel.Location = new System.Drawing.Point(165, 10);
+      this.DefaultClassValueLabel.Name = "DefaultClassValueLabel";
+      this.DefaultClassValueLabel.Size = new System.Drawing.Size(118, 13);
+      this.DefaultClassValueLabel.TabIndex = 2;
+      this.DefaultClassValueLabel.Text = "Default class label:";
+      // 
+      // OneFactorClassificationModelView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.DefaultClassValueLabel);
+      this.Controls.Add(this.variableLabel);
+      this.Controls.Add(this.dataGridView);
+      this.Name = "OneFactorClassificationModelView";
+      this.Size = new System.Drawing.Size(395, 265);
+      ((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.DataGridView dataGridView;
+    private System.Windows.Forms.Label variableLabel;
+    private System.Windows.Forms.DataGridViewTextBoxColumn variableValueColumn;
+    private System.Windows.Forms.DataGridViewTextBoxColumn classcolumn;
+    private System.Windows.Forms.Label DefaultClassValueLabel;
+  }
+}
Index: /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/OneFactorClassificationModelView.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/OneFactorClassificationModelView.cs	(revision 15131)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/OneFactorClassificationModelView.cs	(revision 15131)
@@ -0,0 +1,70 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Windows.Forms;
+using HeuristicLab.MainForm;
+using HeuristicLab.MainForm.WindowsForms;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.Views {
+  [View("OneFactor Classification Model")]
+  [Content(typeof(OneFactorClassificationModel), IsDefaultView = true)]
+  public partial class OneFactorClassificationModelView : AsynchronousContentView {
+    public OneFactorClassificationModelView() {
+      InitializeComponent();
+    }
+
+    public new OneFactorClassificationModel Content {
+      get { return (OneFactorClassificationModel)base.Content; }
+      set { base.Content = value; }
+    }
+
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      UpdateDataGridView();
+    }
+
+    private void UpdateDataGridView() {
+      if (InvokeRequired) {
+        Invoke((Action)UpdateDataGridView);
+      } else {
+        if (Content == null) {
+          variableLabel.Text = "Variable: ";
+          DefaultClassValueLabel.Text = "Default class label: ";
+          dataGridView.Rows.Clear();
+        } else {
+          variableLabel.Text = "Variable: " + Content.Variable;
+          DefaultClassValueLabel.Text = "Default class label: " + Content.DefaultClass;
+
+          dataGridView.RowCount = Content.Classes.Length;
+
+          for (int row = 0; row < Content.Classes.Length; row++) {
+            dataGridView[0, row].Value = Content.VariableValues[row];
+            dataGridView[1, row].Value = Content.Classes[row];
+          }
+
+          dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.ColumnHeader);
+          dataGridView.AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders);
+        }
+      }
+    }
+  }
+}
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneFactorClassificationModel.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneFactorClassificationModel.cs	(revision 15131)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneFactorClassificationModel.cs	(revision 15131)
@@ -0,0 +1,103 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.Algorithms.DataAnalysis {
+  [StorableClass]
+  [Item("OneFactor Classification Model", "A model that uses only one categorical feature (factor) to determine the class.")]
+  public sealed class OneFactorClassificationModel : ClassificationModel {
+    public override IEnumerable<string> VariablesUsedForPrediction {
+      get { return new[] { Variable }; }
+    }
+
+    [Storable]
+    private string variable;
+    public string Variable {
+      get { return variable; }
+    }
+
+    [Storable]
+    private string[] variableValues;
+    public string[] VariableValues {
+      get { return variableValues; }
+    }
+
+    [Storable]
+    private double[] classes;
+    public double[] Classes {
+      get { return classes; }
+    }
+
+    [Storable]
+    private double defaultClass;
+    public double DefaultClass {
+      get { return defaultClass; }
+    }
+
+    [StorableConstructor]
+    private OneFactorClassificationModel(bool deserializing) : base(deserializing) { }
+    private OneFactorClassificationModel(OneFactorClassificationModel original, Cloner cloner)
+      : base(original, cloner) {
+      this.variable = (string)original.variable;
+      this.variableValues = (string[])original.variableValues.Clone();
+      this.classes = (double[])original.classes.Clone();
+      this.defaultClass = original.defaultClass;
+    }
+    public override IDeepCloneable Clone(Cloner cloner) { return new OneFactorClassificationModel(this, cloner); }
+
+    public OneFactorClassificationModel(string targetVariable, string variable, string[] variableValues, double[] classes, double defaultClass = double.NaN)
+      : base(targetVariable) {
+      if (variableValues.Length != classes.Length) {
+        throw new ArgumentException("Number of variable values and classes has to be equal.");
+      }
+      this.name = ItemName;
+      this.description = ItemDescription;
+      this.variable = variable;
+      this.variableValues = variableValues;
+      this.classes = classes;
+      this.defaultClass = double.IsNaN(defaultClass) ? classes.First() : defaultClass;
+      Array.Sort(variableValues, classes);
+    }
+
+    public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
+      return dataset.GetStringValues(Variable, rows)
+        .Select(GetPredictedValueForInput);
+    }
+
+    private double GetPredictedValueForInput(string val) {
+      var matchingIdx = Array.BinarySearch(variableValues, val);
+      if (matchingIdx >= 0) return classes[matchingIdx];
+      else return DefaultClass;
+    }
+
+    public override IClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
+      return new OneFactorClassificationSolution(this, new ClassificationProblemData(problemData));
+    }
+
+  }
+}
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneFactorClassificationSolution.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneFactorClassificationSolution.cs	(revision 15131)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneFactorClassificationSolution.cs	(revision 15131)
@@ -0,0 +1,48 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.Algorithms.DataAnalysis {
+  [StorableClass]
+  [Item(Name = "OneR Classification Solution", Description = "Represents a OneR classification solution which uses only a single factor for class prediction.")]
+  public sealed class OneFactorClassificationSolution : ClassificationSolution {
+    public new OneFactorClassificationModel Model {
+      get { return (OneFactorClassificationModel)base.Model; }
+      set { base.Model = value; }
+    }
+
+    [StorableConstructor]
+    private OneFactorClassificationSolution(bool deserializing) : base(deserializing) { }
+    private OneFactorClassificationSolution(OneFactorClassificationSolution original, Cloner cloner) : base(original, cloner) { }
+    public OneFactorClassificationSolution(OneFactorClassificationModel model, IClassificationProblemData problemData)
+      : base(model, problemData) {
+      RecalculateResults();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new OneFactorClassificationSolution(this, cloner);
+    }
+  }
+}
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneR.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneR.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneR.cs	(revision 15131)
@@ -20,4 +20,5 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -65,4 +66,27 @@
 
     public static IClassificationSolution CreateOneRSolution(IClassificationProblemData problemData, int minBucketSize = 6) {
+      var classValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
+      var model1 = FindBestDoubleVariableModel(problemData, minBucketSize);
+      var model2 = FindBestFactorModel(problemData);
+
+      if (model1 == null && model2 == null) throw new InvalidProgramException("Could not create OneR solution");
+      else if (model1 == null) return new OneFactorClassificationSolution(model2, (IClassificationProblemData)problemData.Clone());
+      else if (model2 == null) return new OneRClassificationSolution(model1, (IClassificationProblemData)problemData.Clone());
+      else {
+        var model1EstimatedValues = model1.GetEstimatedClassValues(problemData.Dataset, problemData.TrainingIndices);
+        var model1NumCorrect = classValues.Zip(model1EstimatedValues, (a, b) => a.IsAlmost(b)).Count(e => e);
+
+        var model2EstimatedValues = model2.GetEstimatedClassValues(problemData.Dataset, problemData.TrainingIndices);
+        var model2NumCorrect = classValues.Zip(model2EstimatedValues, (a, b) => a.IsAlmost(b)).Count(e => e);
+
+        if (model1NumCorrect > model2NumCorrect) {
+          return new OneRClassificationSolution(model1, (IClassificationProblemData)problemData.Clone());
+        } else {
+          return new OneFactorClassificationSolution(model2, (IClassificationProblemData)problemData.Clone());
+        }
+      }
+    }
+
+    private static OneRClassificationModel FindBestDoubleVariableModel(IClassificationProblemData problemData, int minBucketSize = 6) {
       var bestClassified = 0;
       List<Split> bestSplits = null;
@@ -71,9 +95,17 @@
       var classValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
 
-      foreach (var variable in problemData.AllowedInputVariables) {
+      var allowedInputVariables = problemData.AllowedInputVariables.Where(problemData.Dataset.VariableHasType<double>);
+
+      if (!allowedInputVariables.Any()) return null;
+
+      foreach (var variable in allowedInputVariables) {
         var inputValues = problemData.Dataset.GetDoubleValues(variable, problemData.TrainingIndices);
         var samples = inputValues.Zip(classValues, (i, v) => new Sample(i, v)).OrderBy(s => s.inputValue);
 
-        var missingValuesDistribution = samples.Where(s => double.IsNaN(s.inputValue)).GroupBy(s => s.classValue).ToDictionary(s => s.Key, s => s.Count()).MaxItems(s => s.Value).FirstOrDefault();
+        var missingValuesDistribution = samples
+          .Where(s => double.IsNaN(s.inputValue)).GroupBy(s => s.classValue)
+          .ToDictionary(s => s.Key, s => s.Count())
+          .MaxItems(s => s.Value)
+          .FirstOrDefault();
 
         //calculate class distributions for all distinct inputValues
@@ -120,5 +152,5 @@
           while (sample.inputValue >= splits[splitIndex].thresholdValue)
             splitIndex++;
-          correctClassified += sample.classValue == splits[splitIndex].classValue ? 1 : 0;
+          correctClassified += sample.classValue.IsAlmost(splits[splitIndex].classValue) ? 1 : 0;
         }
         correctClassified += missingValuesDistribution.Value;
@@ -134,5 +166,5 @@
       //remove neighboring splits with the same class value
       for (int i = 0; i < bestSplits.Count - 1; i++) {
-        if (bestSplits[i].classValue == bestSplits[i + 1].classValue) {
+        if (bestSplits[i].classValue.IsAlmost(bestSplits[i + 1].classValue)) {
           bestSplits.Remove(bestSplits[i]);
           i--;
@@ -140,8 +172,44 @@
       }
 
-      var model = new OneRClassificationModel(problemData.TargetVariable, bestVariable, bestSplits.Select(s => s.thresholdValue).ToArray(), bestSplits.Select(s => s.classValue).ToArray(), bestMissingValuesClass);
-      var solution = new OneRClassificationSolution(model, (IClassificationProblemData)problemData.Clone());
-
-      return solution;
+      var model = new OneRClassificationModel(problemData.TargetVariable, bestVariable,
+        bestSplits.Select(s => s.thresholdValue).ToArray(),
+        bestSplits.Select(s => s.classValue).ToArray(), bestMissingValuesClass);
+
+      return model;
+    }
+    private static OneFactorClassificationModel FindBestFactorModel(IClassificationProblemData problemData) {
+      var classValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
+      var defaultClass = FindMostFrequentClassValue(classValues);
+      // only select string variables
+      var allowedInputVariables = problemData.AllowedInputVariables.Where(problemData.Dataset.VariableHasType<string>);
+
+      if (!allowedInputVariables.Any()) return null;
+
+      OneFactorClassificationModel bestModel = null;
+      var bestModelNumCorrect = 0;
+
+      foreach (var variable in allowedInputVariables) {
+        var variableValues = problemData.Dataset.GetStringValues(variable, problemData.TrainingIndices);
+        var groupedClassValues = variableValues
+          .Zip(classValues, (v, c) => new KeyValuePair<string, double>(v, c))
+          .GroupBy(kvp => kvp.Key)
+          .ToDictionary(g => g.Key, g => FindMostFrequentClassValue(g.Select(kvp => kvp.Value)));
+
+        var model = new OneFactorClassificationModel(problemData.TargetVariable, variable,
+          groupedClassValues.Select(kvp => kvp.Key).ToArray(), groupedClassValues.Select(kvp => kvp.Value).ToArray(), defaultClass);
+
+        var modelEstimatedValues = model.GetEstimatedClassValues(problemData.Dataset, problemData.TrainingIndices);
+        var modelNumCorrect = classValues.Zip(modelEstimatedValues, (a, b) => a.IsAlmost(b)).Count(e => e);
+        if (modelNumCorrect > bestModelNumCorrect) {
+          bestModelNumCorrect = modelNumCorrect;
+          bestModel = model;
+        }
+      }
+
+      return bestModel;
+    }
+
+    private static double FindMostFrequentClassValue(IEnumerable<double> classValues) {
+      return classValues.GroupBy(c => c).OrderByDescending(g => g.Count()).Select(g => g.Key).First();
     }
 
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneRClassificationModel.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneRClassificationModel.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneRClassificationModel.cs	(revision 15131)
@@ -31,5 +31,5 @@
   [StorableClass]
   [Item("OneR Classification Model", "A model that uses intervals for one variable to determine the class.")]
-  public class OneRClassificationModel : ClassificationModel {
+  public sealed class OneRClassificationModel : ClassificationModel {
     public override IEnumerable<string> VariablesUsedForPrediction {
       get { return new[] { Variable }; }
@@ -37,5 +37,5 @@
 
     [Storable]
-    protected string variable;
+    private string variable;
     public string Variable {
       get { return variable; }
@@ -43,5 +43,5 @@
 
     [Storable]
-    protected double[] splits;
+    private double[] splits;
     public double[] Splits {
       get { return splits; }
@@ -49,5 +49,5 @@
 
     [Storable]
-    protected double[] classes;
+    private double[] classes;
     public double[] Classes {
       get { return classes; }
@@ -55,5 +55,5 @@
 
     [Storable]
-    protected double missingValuesClass;
+    private double missingValuesClass;
     public double MissingValuesClass {
       get { return missingValuesClass; }
@@ -61,10 +61,11 @@
 
     [StorableConstructor]
-    protected OneRClassificationModel(bool deserializing) : base(deserializing) { }
-    protected OneRClassificationModel(OneRClassificationModel original, Cloner cloner)
+    private OneRClassificationModel(bool deserializing) : base(deserializing) { }
+    private OneRClassificationModel(OneRClassificationModel original, Cloner cloner)
       : base(original, cloner) {
       this.variable = (string)original.variable;
       this.splits = (double[])original.splits.Clone();
       this.classes = (double[])original.classes.Clone();
+      this.missingValuesClass = original.missingValuesClass;
     }
     public override IDeepCloneable Clone(Cloner cloner) { return new OneRClassificationModel(this, cloner); }
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneRClassificationSolution.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneRClassificationSolution.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/BaselineClassifiers/OneRClassificationSolution.cs	(revision 15131)
@@ -28,5 +28,5 @@
   [StorableClass]
   [Item(Name = "OneR Classification Solution", Description = "Represents a OneR classification solution which uses only a single feature with potentially multiple thresholds for class prediction.")]
-  public class OneRClassificationSolution : ClassificationSolution {
+  public sealed class OneRClassificationSolution : ClassificationSolution {
     public new OneRClassificationModel Model {
       get { return (OneRClassificationModel)base.Model; }
@@ -35,6 +35,6 @@
 
     [StorableConstructor]
-    protected OneRClassificationSolution(bool deserializing) : base(deserializing) { }
-    protected OneRClassificationSolution(OneRClassificationSolution original, Cloner cloner) : base(original, cloner) { }
+    private OneRClassificationSolution(bool deserializing) : base(deserializing) { }
+    private OneRClassificationSolution(OneRClassificationSolution original, Cloner cloner) : base(original, cloner) { }
     public OneRClassificationSolution(OneRClassificationModel model, IClassificationProblemData problemData)
       : base(model, problemData) {
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/GaussianProcess/GaussianProcessClassificationModelCreator.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/GaussianProcess/GaussianProcessClassificationModelCreator.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/GaussianProcess/GaussianProcessClassificationModelCreator.cs	(revision 15131)
@@ -67,5 +67,7 @@
         HyperparameterGradientsParameter.ActualValue = new RealVector(model.HyperparameterGradients);
         return base.Apply();
-      } catch (ArgumentException) { } catch (alglib.alglibexception) { }
+      } catch (ArgumentException) {
+      } catch (alglib.alglibexception) {
+      }
       NegativeLogLikelihoodParameter.ActualValue = new DoubleValue(1E300);
       HyperparameterGradientsParameter.ActualValue = new RealVector(Hyperparameter.Count());
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithmStatic.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithmStatic.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithmStatic.cs	(revision 15131)
@@ -148,4 +148,10 @@
     // for custom stepping & termination
     public static IGbmState CreateGbmState(IRegressionProblemData problemData, ILossFunction lossFunction, uint randSeed, int maxSize = 3, double r = 0.66, double m = 0.5, double nu = 0.01) {
+      // check input variables. Only double variables are allowed.
+      var invalidInputs =
+        problemData.AllowedInputVariables.Where(name => !problemData.Dataset.VariableHasType<double>(name));
+      if (invalidInputs.Any())
+        throw new NotSupportedException("Gradient tree boosting only supports real-valued variables. Unsupported inputs: " + string.Join(", ", invalidInputs));
+
       return new GbmState(problemData, lossFunction, randSeed, maxSize, r, m, nu);
     }
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 15131)
@@ -122,4 +122,6 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="BaselineClassifiers\OneFactorClassificationModel.cs" />
+    <Compile Include="BaselineClassifiers\OneFactorClassificationSolution.cs" />
     <Compile Include="BaselineClassifiers\OneR.cs" />
     <Compile Include="BaselineClassifiers\OneRClassificationModel.cs" />
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/AlglibUtil.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/AlglibUtil.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/AlglibUtil.cs	(revision 15131)
@@ -20,4 +20,5 @@
 #endregion
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -27,8 +28,12 @@
   public static class AlglibUtil {
     public static double[,] PrepareInputMatrix(IDataset dataset, IEnumerable<string> variables, IEnumerable<int> rows) {
-      List<string> variablesList = variables.ToList();
+      // check input variables. Only double variables are allowed.
+      var invalidInputs =
+        variables.Where(name => !dataset.VariableHasType<double>(name));
+      if (invalidInputs.Any())
+        throw new NotSupportedException("Unsupported inputs: " + string.Join(", ", invalidInputs));
+
       List<int> rowsList = rows.ToList();
-
-      double[,] matrix = new double[rowsList.Count, variablesList.Count];
+      double[,] matrix = new double[rowsList.Count, variables.Count()];
 
       int col = 0;
@@ -45,5 +50,12 @@
       return matrix;
     }
+
     public static double[,] PrepareAndScaleInputMatrix(IDataset dataset, IEnumerable<string> variables, IEnumerable<int> rows, Scaling scaling) {
+      // check input variables. Only double variables are allowed.
+      var invalidInputs =
+        variables.Where(name => !dataset.VariableHasType<double>(name));
+      if (invalidInputs.Any())
+        throw new NotSupportedException("Unsupported inputs: " + string.Join(", ", invalidInputs));
+
       List<string> variablesList = variables.ToList();
       List<int> rowsList = rows.ToList();
@@ -64,4 +76,57 @@
       return matrix;
     }
+
+    /// <summary>
+    /// Prepares a binary data matrix from a number of factors and specified factor values
+    /// </summary>
+    /// <param name="dataset">A dataset that contains the variable values</param>
+    /// <param name="factorVariables">An enumerable of categorical variables (factors). For each variable an enumerable of values must be specified.</param>
+    /// <param name="rows">An enumerable of row indices for the dataset</param>
+    /// <returns></returns>
+    /// <remarks>Factor variables (categorical variables) are split up into multiple binary variables one for each specified value.</remarks>
+    public static double[,] PrepareInputMatrix(
+      IDataset dataset,
+      IEnumerable<KeyValuePair<string, IEnumerable<string>>> factorVariables,
+      IEnumerable<int> rows) {
+      // check input variables. Only string variables are allowed.
+      var invalidInputs =
+        factorVariables.Select(kvp => kvp.Key).Where(name => !dataset.VariableHasType<string>(name));
+      if (invalidInputs.Any())
+        throw new NotSupportedException("Unsupported inputs: " + string.Join(", ", invalidInputs));
+
+      int numBinaryColumns = factorVariables.Sum(kvp => kvp.Value.Count());
+
+      List<int> rowsList = rows.ToList();
+      double[,] matrix = new double[rowsList.Count, numBinaryColumns];
+
+      int col = 0;
+      foreach (var kvp in factorVariables) {
+        var varName = kvp.Key;
+        var cats = kvp.Value;
+        if (!cats.Any()) continue;
+        foreach (var cat in cats) {
+          var values = dataset.GetStringValues(varName, rows);
+          int row = 0;
+          foreach (var value in values) {
+            matrix[row, col] = value == cat ? 1 : 0;
+            row++;
+          }
+          col++;
+        }
+      }
+      return matrix;
+    }
+
+    public static IEnumerable<KeyValuePair<string, IEnumerable<string>>> GetFactorVariableValues(IDataset ds, IEnumerable<string> factorVariables, IEnumerable<int> rows) {
+      return from factor in factorVariables
+             let distinctValues = ds.GetStringValues(factor, rows).Distinct().ToArray()
+             // 1 distinct value => skip (constant)
+             // 2 distinct values => only take one of the two values
+             // >=3 distinct values => create a binary value for each value
+             let reducedValues = distinctValues.Length <= 2
+               ? distinctValues.Take(distinctValues.Length - 1)
+               : distinctValues
+             select new KeyValuePair<string, IEnumerable<string>>(factor, reducedValues);
+    }
   }
 }
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearDiscriminantAnalysis.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearDiscriminantAnalysis.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearDiscriminantAnalysis.cs	(revision 15131)
@@ -37,5 +37,5 @@
   /// Linear discriminant analysis classification algorithm.
   /// </summary>
-  [Item("Linear Discriminant Analysis", "Linear discriminant analysis classification algorithm (wrapper for ALGLIB).")]
+  [Item("Linear Discriminant Analysis (LDA)", "Linear discriminant analysis classification algorithm (wrapper for ALGLIB).")]
   [Creatable(CreatableAttribute.Categories.DataAnalysisClassification, Priority = 100)]
   [StorableClass]
@@ -71,5 +71,13 @@
       IEnumerable<int> rows = problemData.TrainingIndices;
       int nClasses = problemData.ClassNames.Count();
-      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables.Concat(new string[] { targetVariable }), rows);
+      var doubleVariableNames = allowedInputVariables.Where(dataset.VariableHasType<double>).ToArray();
+      var factorVariableNames = allowedInputVariables.Where(dataset.VariableHasType<string>).ToArray();
+      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(dataset, doubleVariableNames.Concat(new string[] { targetVariable }), rows);
+
+      var factorVariables = AlglibUtil.GetFactorVariableValues(dataset, factorVariableNames, rows);
+      double[,] factorMatrix = AlglibUtil.PrepareInputMatrix(dataset, factorVariables, rows);
+
+      inputMatrix = factorMatrix.HorzCat(inputMatrix);
+
       if (inputMatrix.Cast<double>().Any(x => double.IsNaN(x) || double.IsInfinity(x)))
         throw new NotSupportedException("Linear discriminant analysis does not support NaN or infinity values in the input dataset.");
@@ -83,5 +91,5 @@
       int info;
       double[] w;
-      alglib.fisherlda(inputMatrix, inputMatrix.GetLength(0), allowedInputVariables.Count(), nClasses, out info, out w);
+      alglib.fisherlda(inputMatrix, inputMatrix.GetLength(0), inputMatrix.GetLength(1) - 1, nClasses, out info, out w);
       if (info < 1) throw new ArgumentException("Error in calculation of linear discriminant analysis solution");
 
@@ -93,5 +101,17 @@
 
       int col = 0;
-      foreach (string column in allowedInputVariables) {
+      foreach (var kvp in factorVariables) {
+        var varName = kvp.Key;
+        foreach (var cat in kvp.Value) {
+          BinaryFactorVariableTreeNode vNode =
+            (BinaryFactorVariableTreeNode)new HeuristicLab.Problems.DataAnalysis.Symbolic.BinaryFactorVariable().CreateTreeNode();
+          vNode.VariableName = varName;
+          vNode.VariableValue = cat;
+          vNode.Weight = w[col];
+          addition.AddSubtree(vNode);
+          col++;
+        }
+      }
+      foreach (string column in doubleVariableNames) {
         VariableTreeNode vNode = (VariableTreeNode)new HeuristicLab.Problems.DataAnalysis.Symbolic.Variable().CreateTreeNode();
         vNode.VariableName = column;
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearRegression.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearRegression.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/LinearRegression.cs	(revision 15131)
@@ -74,5 +74,11 @@
       IEnumerable<string> allowedInputVariables = problemData.AllowedInputVariables;
       IEnumerable<int> rows = problemData.TrainingIndices;
-      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables.Concat(new string[] { targetVariable }), rows);
+      var doubleVariables = allowedInputVariables.Where(dataset.VariableHasType<double>);
+      var factorVariableNames = allowedInputVariables.Where(dataset.VariableHasType<string>);
+      var factorVariables = AlglibUtil.GetFactorVariableValues(dataset, factorVariableNames, rows);
+      double[,] binaryMatrix = AlglibUtil.PrepareInputMatrix(dataset, factorVariables, rows);
+      double[,] doubleVarMatrix = AlglibUtil.PrepareInputMatrix(dataset, doubleVariables.Concat(new string[] { targetVariable }), rows);
+      var inputMatrix = binaryMatrix.HorzCat(doubleVarMatrix);
+
       if (inputMatrix.Cast<double>().Any(x => double.IsNaN(x) || double.IsInfinity(x)))
         throw new NotSupportedException("Linear regression does not support NaN or infinity values in the input dataset.");
@@ -99,5 +105,17 @@
 
       int col = 0;
-      foreach (string column in allowedInputVariables) {
+      foreach (var kvp in factorVariables) {
+        var varName = kvp.Key;
+        foreach (var cat in kvp.Value) {
+          BinaryFactorVariableTreeNode vNode =
+            (BinaryFactorVariableTreeNode)new HeuristicLab.Problems.DataAnalysis.Symbolic.BinaryFactorVariable().CreateTreeNode();
+          vNode.VariableName = varName;
+          vNode.VariableValue = cat;
+          vNode.Weight = coefficients[col];
+          addition.AddSubtree(vNode);
+          col++;
+        }
+      }
+      foreach (string column in doubleVariables) {
         VariableTreeNode vNode = (VariableTreeNode)new HeuristicLab.Problems.DataAnalysis.Symbolic.Variable().CreateTreeNode();
         vNode.VariableName = column;
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitClassification.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitClassification.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitClassification.cs	(revision 15131)
@@ -69,7 +69,13 @@
       var dataset = problemData.Dataset;
       string targetVariable = problemData.TargetVariable;
-      IEnumerable<string> allowedInputVariables = problemData.AllowedInputVariables;
+      var doubleVariableNames = problemData.AllowedInputVariables.Where(dataset.VariableHasType<double>);
+      var factorVariableNames = problemData.AllowedInputVariables.Where(dataset.VariableHasType<string>);
       IEnumerable<int> rows = problemData.TrainingIndices;
-      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables.Concat(new string[] { targetVariable }), rows);
+      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(dataset, doubleVariableNames.Concat(new string[] { targetVariable }), rows);
+
+      var factorVariableValues = AlglibUtil.GetFactorVariableValues(dataset, factorVariableNames, rows);
+      var factorMatrix = AlglibUtil.PrepareInputMatrix(dataset, factorVariableValues, rows);
+      inputMatrix = factorMatrix.HorzCat(inputMatrix);
+
       if (inputMatrix.Cast<double>().Any(x => double.IsNaN(x) || double.IsInfinity(x)))
         throw new NotSupportedException("Multinomial logit classification does not support NaN or infinity values in the input dataset.");
@@ -96,5 +102,5 @@
       relClassError = alglib.mnlrelclserror(lm, inputMatrix, nRows);
 
-      MultinomialLogitClassificationSolution solution = new MultinomialLogitClassificationSolution(new MultinomialLogitModel(lm, targetVariable, allowedInputVariables, classValues), (IClassificationProblemData)problemData.Clone());
+      MultinomialLogitClassificationSolution solution = new MultinomialLogitClassificationSolution(new MultinomialLogitModel(lm, targetVariable, doubleVariableNames, factorVariableValues, classValues), (IClassificationProblemData)problemData.Clone());
       return solution;
     }
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitClassificationSolution.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitClassificationSolution.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitClassificationSolution.cs	(revision 15131)
@@ -43,5 +43,5 @@
       : base(original, cloner) {
     }
-    public MultinomialLogitClassificationSolution( MultinomialLogitModel logitModel,IClassificationProblemData problemData)
+    public MultinomialLogitClassificationSolution(MultinomialLogitModel logitModel, IClassificationProblemData problemData)
       : base(logitModel, problemData) {
     }
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitModel.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitModel.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/Linear/MultinomialLogitModel.cs	(revision 15131)
@@ -56,4 +56,7 @@
     [Storable]
     private double[] classValues;
+    [Storable]
+    private List<KeyValuePair<string, IEnumerable<string>>> factorVariables;
+
     [StorableConstructor]
     private MultinomialLogitModel(bool deserializing)
@@ -68,12 +71,22 @@
       allowedInputVariables = (string[])original.allowedInputVariables.Clone();
       classValues = (double[])original.classValues.Clone();
+      this.factorVariables = original.factorVariables.Select(kvp => new KeyValuePair<string, IEnumerable<string>>(kvp.Key, new List<string>(kvp.Value))).ToList();
     }
-    public MultinomialLogitModel(alglib.logitmodel logitModel, string targetVariable, IEnumerable<string> allowedInputVariables, double[] classValues)
+    public MultinomialLogitModel(alglib.logitmodel logitModel, string targetVariable, IEnumerable<string> doubleInputVariables, IEnumerable<KeyValuePair<string, IEnumerable<string>>> factorVariables, double[] classValues)
       : base(targetVariable) {
       this.name = ItemName;
       this.description = ItemDescription;
       this.logitModel = logitModel;
-      this.allowedInputVariables = allowedInputVariables.ToArray();
+      this.allowedInputVariables = doubleInputVariables.ToArray();
+      this.factorVariables = factorVariables.Select(kvp => new KeyValuePair<string, IEnumerable<string>>(kvp.Key, new List<string>(kvp.Value))).ToList();
       this.classValues = (double[])classValues.Clone();
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      // BackwardsCompatibility3.3
+      #region Backwards compatible code, remove with 3.4
+      factorVariables = new List<KeyValuePair<string, IEnumerable<string>>>();
+      #endregion
     }
 
@@ -83,5 +96,9 @@
 
     public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
+
       double[,] inputData = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables, rows);
+      double[,] factorData = AlglibUtil.PrepareInputMatrix(dataset, factorVariables, rows);
+
+      inputData = factorData.HorzCat(inputData);
 
       int n = inputData.GetLength(0);
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NearestNeighbour/NearestNeighbourModel.cs	(revision 15131)
@@ -144,5 +144,5 @@
       if (inputMatrix.Cast<double>().Any(x => double.IsNaN(x) || double.IsInfinity(x)))
         throw new NotSupportedException(
-          "Nearest neighbour classification does not support NaN or infinity values in the input dataset.");
+          "Nearest neighbour model does not support NaN or infinity values in the input dataset.");
 
       this.kdTree = new alglib.nearestneighbor.kdtree();
Index: /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NonlinearRegression/NonlinearRegression.cs
===================================================================
--- /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NonlinearRegression/NonlinearRegression.cs	(revision 15130)
+++ /stable/HeuristicLab.Algorithms.DataAnalysis/3.4/NonlinearRegression/NonlinearRegression.cs	(revision 15131)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
@@ -208,4 +209,43 @@
       var parser = new InfixExpressionParser();
       var tree = parser.Parse(modelStructure);
+      // parser handles double and string variables equally by creating a VariableTreeNode
+      // post-process to replace VariableTreeNodes by FactorVariableTreeNodes for all string variables
+      var factorSymbol = new FactorVariable();
+      factorSymbol.VariableNames =
+        problemData.AllowedInputVariables.Where(name => problemData.Dataset.VariableHasType<string>(name));
+      factorSymbol.AllVariableNames = factorSymbol.VariableNames;
+      factorSymbol.VariableValues =
+        factorSymbol.VariableNames.Select(name =>
+        new KeyValuePair<string, Dictionary<string, int>>(name,
+        problemData.Dataset.GetReadOnlyStringValues(name).Distinct()
+        .Select((n, i) => Tuple.Create(n, i))
+        .ToDictionary(tup => tup.Item1, tup => tup.Item2)));
+
+      foreach (var parent in tree.IterateNodesPrefix().ToArray()) {
+        for (int i = 0; i < parent.SubtreeCount; i++) {
+          var varChild = parent.GetSubtree(i) as VariableTreeNode;
+          var factorVarChild = parent.GetSubtree(i) as FactorVariableTreeNode;
+          if (varChild != null && factorSymbol.VariableNames.Contains(varChild.VariableName)) {
+            parent.RemoveSubtree(i);
+            var factorTreeNode = (FactorVariableTreeNode)factorSymbol.CreateTreeNode();
+            factorTreeNode.VariableName = varChild.VariableName;
+            factorTreeNode.Weights =
+              factorTreeNode.Symbol.GetVariableValues(factorTreeNode.VariableName).Select(_ => 1.0).ToArray();
+            // weight = 1.0 for each value
+            parent.InsertSubtree(i, factorTreeNode);
+          } else if (factorVarChild != null && factorSymbol.VariableNames.Contains(factorVarChild.VariableName)) {
+            if (factorSymbol.GetVariableValues(factorVarChild.VariableName).Count() != factorVarChild.Weights.Length)
+              throw new ArgumentException(
+                string.Format("Factor variable {0} needs exactly {1} weights",
+                factorVarChild.VariableName,
+                factorSymbol.GetVariableValues(factorVarChild.VariableName).Count()));
+            parent.RemoveSubtree(i);
+            var factorTreeNode = (FactorVariableTreeNode)factorSymbol.CreateTreeNode();
+            factorTreeNode.VariableName = factorVarChild.VariableName;
+            factorTreeNode.Weights = factorVarChild.Weights;
+            parent.InsertSubtree(i, factorTreeNode);
+          }
+        }
+      }
 
       if (!SymbolicRegressionConstantOptimizationEvaluator.CanOptimizeConstants(tree)) throw new ArgumentException("The optimizer does not support the specified model structure.");
Index: /stable/HeuristicLab.Common/3.3/MatrixExtensions.cs
===================================================================
--- /stable/HeuristicLab.Common/3.3/MatrixExtensions.cs	(revision 15130)
+++ /stable/HeuristicLab.Common/3.3/MatrixExtensions.cs	(revision 15131)
@@ -20,4 +20,6 @@
 #endregion
 
+using System.Diagnostics.Contracts;
+
 namespace HeuristicLab.Common {
   public static class MatrixExtensions {
@@ -35,4 +37,63 @@
       return result;
     }
+
+    /// <summary>
+    /// Concatenates matrices vertically.
+    /// A      B
+    /// 1 2    9 8
+    /// 3 4    7 6
+    /// 
+    /// VertCat(A, B)
+    /// 1 2 
+    /// 3 4
+    /// 9 8
+    /// 7 6
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="a"></param>
+    /// <param name="b"></param>
+    /// <returns>A new matrix with the number of rows = a.GetLength(0) + b.GetLength(0)</returns>
+    public static T[,] VertCat<T>(this T[,] a, T[,] b) {
+      Contract.Assert(a.GetLength(1) == b.GetLength(1));
+      var aLen = a.GetLength(0);
+      var bLen = b.GetLength(0);
+      var result = new T[aLen + bLen, a.GetLength(1)];
+      for (int i = 0; i < aLen; i++)
+        for (int j = 0; j < a.GetLength(1); j++)
+          result[i, j] = a[i, j];
+      for (int i = 0; i < bLen; i++)
+        for (int j = 0; j < b.GetLength(1); j++)
+          result[i + aLen, j] = b[i, j];
+
+      return result;
+    }
+
+    /// <summary>
+    /// Concatenates matrices horizontally.
+    /// A      B
+    /// 1 2    9 8
+    /// 3 4    7 6
+    /// 
+    /// HorzCat(A, B)
+    /// 1 2 9 8
+    /// 3 4 7 6
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    /// <param name="a"></param>
+    /// <param name="b"></param>
+    /// <returns>A new matrix with the number of columns = a.GetLength(1) + b.GetLength(1)</returns>
+    public static T[,] HorzCat<T>(this T[,] a, T[,] b) {
+      Contract.Assert(a.GetLength(0) == b.GetLength(0));
+      var aLen = a.GetLength(1);
+      var bLen = b.GetLength(1);
+      var result = new T[a.GetLength(0), aLen + bLen];
+      for (int i = 0; i < a.GetLength(0); i++)
+        for (int j = 0; j < aLen; j++)
+          result[i, j] = a[i, j];
+      for (int i = 0; i < a.GetLength(0); i++)
+        for (int j = 0; j < bLen; j++)
+          result[i, j + aLen] = b[i, j];
+      return result;
+    }
   }
 }
Index: /stable/HeuristicLab.Core.Views/3.3/TypeSelector.cs
===================================================================
--- /stable/HeuristicLab.Core.Views/3.3/TypeSelector.cs	(revision 15130)
+++ /stable/HeuristicLab.Core.Views/3.3/TypeSelector.cs	(revision 15131)
@@ -235,5 +235,6 @@
         }
         return node.Nodes.Count == 0;
-      } if (node.Tag is Type) { // Type node
+      }
+      if (node.Tag is Type) { // Type node
         var text = node.Text;
         if (searchTokens.Any(searchToken => !text.ToLower().Contains(searchToken))) {
Index: /stable/HeuristicLab.DataPreprocessing.Views/3.4/HeuristicLab.DataPreprocessing.Views-3.4.csproj
===================================================================
--- /stable/HeuristicLab.DataPreprocessing.Views/3.4/HeuristicLab.DataPreprocessing.Views-3.4.csproj	(revision 15130)
+++ /stable/HeuristicLab.DataPreprocessing.Views/3.4/HeuristicLab.DataPreprocessing.Views-3.4.csproj	(revision 15131)
@@ -98,4 +98,114 @@
   <ItemGroup>
     <Compile Include="DataPreprocessingMenuItem.cs" />
+    <Compile Include="PreprocessingCheckedVariablesView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PreprocessingCheckedVariablesView.Designer.cs">
+      <DependentUpon>PreprocessingCheckedVariablesView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="PreprocessingFeatureCorrelationView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PreprocessingFeatureCorrelationView.Designer.cs">
+      <DependentUpon>PreprocessingFeatureCorrelationView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="CheckedFilterCollectionView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="CheckedFilterCollectionView.Designer.cs">
+      <DependentUpon>CheckedFilterCollectionView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="CheckedTransformationListView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="CheckedTransformationListView.Designer.cs">
+      <DependentUpon>CheckedTransformationListView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="ComparisonFilterView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="ComparisonFilterView.Designer.cs">
+      <DependentUpon>ComparisonFilterView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="RenameColumnsDialog.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="RenameColumnsDialog.Designer.cs">
+      <DependentUpon>RenameColumnsDialog.cs</DependentUpon>
+    </Compile>
+    <Compile Include="ScatterPlotSingleView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="ScatterPlotSingleView.Designer.cs">
+      <DependentUpon>ScatterPlotSingleView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="DataCompletenessView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="DataCompletenessView.Designer.cs">
+      <DependentUpon>DataCompletenessView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="DataGridContentView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="DataGridContentView.Designer.cs">
+      <DependentUpon>DataGridContentView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="DataPreprocessingView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="DataPreprocessingView.designer.cs">
+      <DependentUpon>DataPreprocessingView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="DataPreprocessorStarter.cs" />
+    <Compile Include="PreprocessingDataTableView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PreprocessingDataTableView.Designer.cs">
+      <DependentUpon>PreprocessingDataTableView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="FilterView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="FilterView.Designer.cs">
+      <DependentUpon>FilterView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="ScatterPlotMultiView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="ScatterPlotMultiView.Designer.cs">
+      <DependentUpon>ScatterPlotMultiView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="PreprocessingScatterPlotView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PreprocessingScatterPlotView.Designer.cs">
+      <DependentUpon>PreprocessingScatterPlotView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="SearchAndReplaceDialog.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="SearchAndReplaceDialog.Designer.cs">
+      <DependentUpon>SearchAndReplaceDialog.cs</DependentUpon>
+    </Compile>
+    <Compile Include="HistogramView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="HistogramView.Designer.cs">
+      <DependentUpon>HistogramView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="LineChartView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="LineChartView.Designer.cs">
+      <DependentUpon>LineChartView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="ManipulationView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="ManipulationView.Designer.cs">
+      <DependentUpon>ManipulationView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Plugin.cs" />
     <Compile Include="PreprocessingChartView.cs">
       <SubType>UserControl</SubType>
@@ -103,114 +213,4 @@
     <Compile Include="PreprocessingChartView.Designer.cs">
       <DependentUpon>PreprocessingChartView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="PreprocessingFeatureCorrelationView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="PreprocessingFeatureCorrelationView.Designer.cs">
-      <DependentUpon>PreprocessingFeatureCorrelationView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="CheckedFilterCollectionView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="CheckedFilterCollectionView.Designer.cs">
-      <DependentUpon>CheckedFilterCollectionView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="CheckedTransformationListView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="CheckedTransformationListView.Designer.cs">
-      <DependentUpon>CheckedTransformationListView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="ComparisonFilterView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="ComparisonFilterView.Designer.cs">
-      <DependentUpon>ComparisonFilterView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="RenameColumnsDialog.cs">
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="RenameColumnsDialog.Designer.cs">
-      <DependentUpon>RenameColumnsDialog.cs</DependentUpon>
-    </Compile>
-    <Compile Include="ScatterPlotSingleView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="ScatterPlotSingleView.Designer.cs">
-      <DependentUpon>ScatterPlotSingleView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="DataCompletenessView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="DataCompletenessView.Designer.cs">
-      <DependentUpon>DataCompletenessView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="DataGridContentView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="DataGridContentView.Designer.cs">
-      <DependentUpon>DataGridContentView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="DataPreprocessingView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="DataPreprocessingView.designer.cs">
-      <DependentUpon>DataPreprocessingView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="DataPreprocessorStarter.cs" />
-    <Compile Include="PreprocessingDataTableView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="PreprocessingDataTableView.Designer.cs">
-      <DependentUpon>PreprocessingDataTableView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="FilterView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="FilterView.Designer.cs">
-      <DependentUpon>FilterView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="ScatterPlotMultiView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="ScatterPlotMultiView.Designer.cs">
-      <DependentUpon>ScatterPlotMultiView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="PreprocessingScatterPlotView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="PreprocessingScatterPlotView.Designer.cs">
-      <DependentUpon>PreprocessingScatterPlotView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="SearchAndReplaceDialog.cs">
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="SearchAndReplaceDialog.Designer.cs">
-      <DependentUpon>SearchAndReplaceDialog.cs</DependentUpon>
-    </Compile>
-    <Compile Include="HistogramView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="HistogramView.Designer.cs">
-      <DependentUpon>HistogramView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="LineChartView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="LineChartView.Designer.cs">
-      <DependentUpon>LineChartView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="ManipulationView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="ManipulationView.Designer.cs">
-      <DependentUpon>ManipulationView.cs</DependentUpon>
-    </Compile>
-    <Compile Include="Plugin.cs" />
-    <Compile Include="PreprocessingCheckedVariablesView.cs">
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="PreprocessingCheckedVariablesView.Designer.cs">
-      <DependentUpon>PreprocessingCheckedVariablesView.cs</DependentUpon>
     </Compile>
     <Compile Include="PreprocessingCheckedItemListView.cs">
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views-3.4.csproj
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views-3.4.csproj	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views-3.4.csproj	(revision 15131)
@@ -119,4 +119,10 @@
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="SolutionComparisonView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="SolutionComparisonView.Designer.cs">
+      <DependentUpon>SolutionComparisonView.cs</DependentUpon>
+    </Compile>
     <Compile Include="InteractiveSymbolicClassificationSolutionSimplifierView.cs">
       <SubType>UserControl</SubType>
@@ -163,4 +169,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Algorithms.DataAnalysis\3.4\HeuristicLab.Algorithms.DataAnalysis-3.4.csproj">
+      <Project>{2E782078-FA81-4B70-B56F-74CE38DAC6C8}</Project>
+      <Name>HeuristicLab.Algorithms.DataAnalysis-3.4</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Collections\3.3\HeuristicLab.Collections-3.3.csproj">
       <Project>{958B43BC-CC5C-4FA2-8628-2B3B01D890B6}</Project>
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/InteractiveSymbolicClassificationSolutionSimplifierViewBase.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/InteractiveSymbolicClassificationSolutionSimplifierViewBase.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/InteractiveSymbolicClassificationSolutionSimplifierViewBase.cs	(revision 15131)
@@ -59,15 +59,4 @@
       return model;
     }
-    protected override Dictionary<ISymbolicExpressionTreeNode, double> CalculateReplacementValues(ISymbolicExpressionTree tree) {
-      return tree.Root.GetSubtree(0).GetSubtree(0).IterateNodesPrefix().ToDictionary(
-        n => n,
-        n => calculator.CalculateReplacementValue(Content.Model, n, Content.ProblemData, Content.ProblemData.TrainingIndices)
-        );
-    }
-
-    protected override Dictionary<ISymbolicExpressionTreeNode, double> CalculateImpactValues(ISymbolicExpressionTree tree) {
-      var values = CalculateImpactAndReplacementValues(tree);
-      return values.ToDictionary(x => x.Key, x => x.Value.Item1);
-    }
 
     protected override Dictionary<ISymbolicExpressionTreeNode, Tuple<double, double>> CalculateImpactAndReplacementValues(ISymbolicExpressionTree tree) {
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/SolutionComparisonView.Designer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/SolutionComparisonView.Designer.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/SolutionComparisonView.Designer.cs	(revision 15131)
@@ -0,0 +1,60 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views {
+  partial class SolutionComparisonView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.SuspendLayout();
+      // 
+      // ClassificationSolutionComparisonView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Name = "SymbolicClassificationSolutionComparisonView";
+      this.Size = new System.Drawing.Size(418, 257);
+      this.ResumeLayout(false);
+    }
+
+    #endregion
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/SolutionComparisonView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/SolutionComparisonView.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views/3.4/SolutionComparisonView.cs	(revision 15131)
@@ -0,0 +1,112 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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;
+using System.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Algorithms.DataAnalysis;
+using HeuristicLab.MainForm;
+using HeuristicLab.Problems.DataAnalysis.Views.Classification;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Classification.Views {
+  [View("Solution Comparison")]
+  [Content(typeof(ISymbolicClassificationSolution))]
+  public partial class SolutionComparisonView : ClassificationSolutionComparisonView {
+
+    public SolutionComparisonView() {
+      InitializeComponent();
+    }
+
+    public new ISymbolicClassificationSolution Content {
+      get { return (ISymbolicClassificationSolution)base.Content; }
+      set { base.Content = value; }
+    }
+
+    protected override IEnumerable<IClassificationSolution> GenerateClassificationSolutions() {
+      var solutionsBase = base.GenerateClassificationSolutions();
+      var solutions = new List<IClassificationSolution>();
+
+      var symbolicSolution = Content;
+
+      // does not support lagged variables
+      if (symbolicSolution.Model.SymbolicExpressionTree.IterateNodesPrefix().OfType<LaggedVariableTreeNode>().Any()) return solutionsBase;
+
+      var problemData = (IClassificationProblemData)symbolicSolution.ProblemData.Clone();
+      if (!problemData.TrainingIndices.Any()) return null; // don't create an comparison models if the problem does not have a training set (e.g. loaded into an existing model)
+
+      var usedVariables = Content.Model.SymbolicExpressionTree.IterateNodesPostfix()
+      .OfType<IVariableTreeNode>()
+      .Select(node => node.VariableName).ToArray();
+
+      var usedDoubleVariables = usedVariables
+        .Where(name => problemData.Dataset.VariableHasType<double>(name))
+      .Distinct();
+
+      var usedFactorVariables = usedVariables
+        .Where(name => problemData.Dataset.VariableHasType<string>(name))
+        .Distinct();
+
+      // gkronber: for binary factors we actually produce a binary variable in the new dataset
+      // but only if the variable is not used as a full factor anyway (LR creates binary columns anyway)
+      var usedBinaryFactors =
+        Content.Model.SymbolicExpressionTree.IterateNodesPostfix().OfType<BinaryFactorVariableTreeNode>()
+        .Where(node => !usedFactorVariables.Contains(node.VariableName))
+        .Select(node => Tuple.Create(node.VariableValue, node.VariableValue));
+
+      // create a new problem and dataset
+      var variableNames =
+        usedDoubleVariables
+        .Concat(usedFactorVariables)
+        .Concat(usedBinaryFactors.Select(t => t.Item1 + "=" + t.Item2))
+        .Concat(new string[] { problemData.TargetVariable })
+        .ToArray();
+      var variableValues =
+        usedDoubleVariables.Select(name => (IList)problemData.Dataset.GetDoubleValues(name).ToList())
+        .Concat(usedFactorVariables.Select(name => problemData.Dataset.GetStringValues(name).ToList()))
+        .Concat(
+          // create binary variable
+          usedBinaryFactors.Select(t => problemData.Dataset.GetReadOnlyStringValues(t.Item1).Select(val => val == t.Item2 ? 1.0 : 0.0).ToList())
+        )
+        .Concat(new[] { problemData.Dataset.GetDoubleValues(problemData.TargetVariable).ToList() });
+
+      var newDs = new Dataset(variableNames, variableValues);
+      var newProblemData = new ClassificationProblemData(newDs, variableNames.Take(variableNames.Length - 1), variableNames.Last());
+      newProblemData.TrainingPartition.Start = problemData.TrainingPartition.Start;
+      newProblemData.TrainingPartition.End = problemData.TrainingPartition.End;
+      newProblemData.TestPartition.Start = problemData.TestPartition.Start;
+      newProblemData.TestPartition.End = problemData.TestPartition.End;
+
+      try {
+        var oneR = OneR.CreateOneRSolution(newProblemData);
+        oneR.Name = "OneR Classification Solution (subset)";
+        solutions.Add(oneR);
+      } catch (NotSupportedException) { } catch (ArgumentException) { }
+      try {
+        var lda = LinearDiscriminantAnalysis.CreateLinearDiscriminantAnalysisSolution(newProblemData);
+        lda.Name = "Linear Discriminant Analysis Solution (subset)";
+        solutions.Add(lda);
+      } catch (NotSupportedException) { } catch (ArgumentException) { }
+
+      return solutionsBase.Concat(solutions);
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification/3.4/SymbolicClassificationSolutionImpactValuesCalculator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification/3.4/SymbolicClassificationSolutionImpactValuesCalculator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Classification/3.4/SymbolicClassificationSolutionImpactValuesCalculator.cs	(revision 15131)
@@ -40,20 +40,8 @@
     protected SymbolicClassificationSolutionImpactValuesCalculator(bool deserializing) : base(deserializing) { }
 
-    public override double CalculateReplacementValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows) {
-      var classificationModel = (ISymbolicClassificationModel)model;
-      var classificationProblemData = (IClassificationProblemData)problemData;
-
-      return CalculateReplacementValue(node, classificationModel.SymbolicExpressionTree, classificationModel.Interpreter, classificationProblemData.Dataset, rows);
-    }
-
-    public override double CalculateImpactValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows, double qualityForImpactsCalculation = double.NaN) {
-      double impactValue, replacementValue;
-      double newQualityForImpactsCalculation;
-      CalculateImpactAndReplacementValues(model, node, problemData, rows, out impactValue, out replacementValue, out newQualityForImpactsCalculation, qualityForImpactsCalculation);
-      return impactValue;
-    }
-
-    public override void CalculateImpactAndReplacementValues(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node,
-      IDataAnalysisProblemData problemData, IEnumerable<int> rows, out double impactValue, out double replacementValue, out double newQualityForImpactsCalculation,
+    public override void CalculateImpactAndReplacementValues(ISymbolicDataAnalysisModel model,
+      ISymbolicExpressionTreeNode node,
+      IDataAnalysisProblemData problemData, IEnumerable<int> rows, out double impactValue, out double replacementValue,
+      out double newQualityForImpactsCalculation,
       double qualityForImpactsCalculation = Double.NaN) {
       var classificationModel = (ISymbolicClassificationModel)model;
@@ -63,6 +51,4 @@
         qualityForImpactsCalculation = CalculateQualityForImpacts(classificationModel, classificationProblemData, rows);
 
-      replacementValue = CalculateReplacementValue(classificationModel, node, classificationProblemData, rows);
-      var constantNode = new ConstantTreeNode(new Constant()) { Value = replacementValue };
 
       var cloner = new Cloner();
@@ -72,15 +58,31 @@
       var tempModelParentNode = tempModelNode.Parent;
       int i = tempModelParentNode.IndexOfSubtree(tempModelNode);
-      tempModelParentNode.RemoveSubtree(i);
-      tempModelParentNode.InsertSubtree(i, constantNode);
+      double bestReplacementValue = 0.0;
+      double bestImpactValue = double.PositiveInfinity;
+      newQualityForImpactsCalculation = qualityForImpactsCalculation; // initialize
+      // try the potentially reasonable replacement values and use the best one
+      foreach (var repValue in CalculateReplacementValues(node, classificationModel.SymbolicExpressionTree, classificationModel.Interpreter, classificationProblemData.Dataset, classificationProblemData.TrainingIndices)) {
+        tempModelParentNode.RemoveSubtree(i);
 
-      OnlineCalculatorError errorState;
-      var dataset = classificationProblemData.Dataset;
-      var targetClassValues = dataset.GetDoubleValues(classificationProblemData.TargetVariable, rows);
-      var estimatedClassValues = tempModel.GetEstimatedClassValues(dataset, rows);
-      newQualityForImpactsCalculation = OnlineAccuracyCalculator.Calculate(targetClassValues, estimatedClassValues, out errorState);
-      if (errorState != OnlineCalculatorError.None) newQualityForImpactsCalculation = 0.0;
+        var constantNode = new ConstantTreeNode(new Constant()) { Value = repValue };
+        tempModelParentNode.InsertSubtree(i, constantNode);
 
-      impactValue = qualityForImpactsCalculation - newQualityForImpactsCalculation;
+        var dataset = classificationProblemData.Dataset;
+        var targetClassValues = dataset.GetDoubleValues(classificationProblemData.TargetVariable, rows);
+        var estimatedClassValues = tempModel.GetEstimatedClassValues(dataset, rows);
+        OnlineCalculatorError errorState;
+        newQualityForImpactsCalculation = OnlineAccuracyCalculator.Calculate(targetClassValues, estimatedClassValues,
+          out errorState);
+        if (errorState != OnlineCalculatorError.None) newQualityForImpactsCalculation = 0.0;
+
+        impactValue = qualityForImpactsCalculation - newQualityForImpactsCalculation;
+
+        if (impactValue < bestImpactValue) {
+          bestImpactValue = impactValue;
+          bestReplacementValue = repValue;
+        }
+      }
+      replacementValue = bestReplacementValue;
+      impactValue = bestImpactValue;
     }
 
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.Views/3.4/InteractiveSymbolicRegressionSolutionSimplifierView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.Views/3.4/InteractiveSymbolicRegressionSolutionSimplifierView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.Views/3.4/InteractiveSymbolicRegressionSolutionSimplifierView.cs	(revision 15131)
@@ -48,15 +48,4 @@
     }
 
-    protected override Dictionary<ISymbolicExpressionTreeNode, double> CalculateReplacementValues(ISymbolicExpressionTree tree) {
-      return tree.Root.GetSubtree(0).GetSubtree(0).IterateNodesPrefix().ToDictionary(
-        n => n,
-        n => calculator.CalculateReplacementValue(Content.Model, n, Content.ProblemData, Content.ProblemData.TrainingIndices)
-        );
-    }
-
-    protected override Dictionary<ISymbolicExpressionTreeNode, double> CalculateImpactValues(ISymbolicExpressionTree tree) {
-      var values = CalculateImpactAndReplacementValues(tree);
-      return values.ToDictionary(x => x.Key, x => x.Value.Item1);
-    }
 
     protected override Dictionary<ISymbolicExpressionTreeNode, Tuple<double, double>> CalculateImpactAndReplacementValues(ISymbolicExpressionTree tree) {
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.Views/3.4/SymbolicRegressionSolutionErrorCharacteristicsCurveView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.Views/3.4/SymbolicRegressionSolutionErrorCharacteristicsCurveView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression.Views/3.4/SymbolicRegressionSolutionErrorCharacteristicsCurveView.cs	(revision 15131)
@@ -21,5 +21,7 @@
 
 using System;
+using System.Collections;
 using System.Collections.Generic;
+using System.Diagnostics.Contracts;
 using System.Linq;
 using HeuristicLab.Algorithms.DataAnalysis;
@@ -46,19 +48,47 @@
       if (!problemData.TrainingIndices.Any()) return null; // don't create an LR model if the problem does not have a training set (e.g. loaded into an existing model)
 
-      //clear checked inputVariables
-      foreach (var inputVariable in problemData.InputVariables.CheckedItems) {
-        problemData.InputVariables.SetItemCheckedState(inputVariable.Value, false);
-      }
+      var usedVariables = Content.Model.SymbolicExpressionTree.IterateNodesPostfix()
+        .OfType<IVariableTreeNode>()
+        .Select(node => node.VariableName).ToArray();
 
-      //check inputVariables used in the symbolic regression model
-      var usedVariables =
-        Content.Model.SymbolicExpressionTree.IterateNodesPostfix().OfType<VariableTreeNode>().Select(
-          node => node.VariableName).Distinct();
-      foreach (var variable in usedVariables) {
-        problemData.InputVariables.SetItemCheckedState(
-          problemData.InputVariables.First(x => x.Value == variable), true);
-      }
+      var usedDoubleVariables = usedVariables
+        .Where(name => problemData.Dataset.VariableHasType<double>(name))
+      .Distinct();
 
-      var solution = LinearRegression.CreateLinearRegressionSolution(problemData, out rmse, out cvRmsError);
+      var usedFactorVariables = usedVariables
+        .Where(name => problemData.Dataset.VariableHasType<string>(name))
+        .Distinct();
+
+      // gkronber: for binary factors we actually produce a binary variable in the new dataset
+      // but only if the variable is not used as a full factor anyway (LR creates binary columns anyway)
+      var usedBinaryFactors =
+        Content.Model.SymbolicExpressionTree.IterateNodesPostfix().OfType<BinaryFactorVariableTreeNode>()
+        .Where(node => !usedFactorVariables.Contains(node.VariableName))
+        .Select(node => Tuple.Create(node.VariableValue, node.VariableValue));
+
+      // create a new problem and dataset
+      var variableNames =
+        usedDoubleVariables
+        .Concat(usedFactorVariables)
+        .Concat(usedBinaryFactors.Select(t => t.Item1 + "=" + t.Item2))
+        .Concat(new string[] { problemData.TargetVariable })
+        .ToArray();
+      var variableValues =
+        usedDoubleVariables.Select(name => (IList)problemData.Dataset.GetDoubleValues(name).ToList())
+        .Concat(usedFactorVariables.Select(name => problemData.Dataset.GetStringValues(name).ToList()))
+        .Concat(
+          // create binary variable
+          usedBinaryFactors.Select(t => problemData.Dataset.GetReadOnlyStringValues(t.Item1).Select(val => val == t.Item2 ? 1.0 : 0.0).ToList())
+        )
+        .Concat(new[] { problemData.Dataset.GetDoubleValues(problemData.TargetVariable).ToList() });
+
+      var newDs = new Dataset(variableNames, variableValues);
+      var newProblemData = new RegressionProblemData(newDs, variableNames.Take(variableNames.Length - 1), variableNames.Last());
+      newProblemData.TrainingPartition.Start = problemData.TrainingPartition.Start;
+      newProblemData.TrainingPartition.End = problemData.TrainingPartition.End;
+      newProblemData.TestPartition.Start = problemData.TestPartition.Start;
+      newProblemData.TestPartition.End = problemData.TestPartition.End;
+
+      var solution = LinearRegression.CreateLinearRegressionSolution(newProblemData, out rmse, out cvRmsError);
       solution.Name = "Baseline (linear subset)";
       return solution;
@@ -68,4 +98,8 @@
     protected override IEnumerable<IRegressionSolution> CreateBaselineSolutions() {
       foreach (var sol in base.CreateBaselineSolutions()) yield return sol;
+
+      // does not support lagged variables
+      if (Content.Model.SymbolicExpressionTree.IterateNodesPrefix().OfType<LaggedVariableTreeNode>().Any()) yield break;
+
       yield return CreateLinearRegressionSolution();
     }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/PearsonRSquaredNumberOfVariablesEvaluator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/PearsonRSquaredNumberOfVariablesEvaluator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/PearsonRSquaredNumberOfVariablesEvaluator.cs	(revision 15131)
@@ -66,5 +66,5 @@
       if (decimalPlaces >= 0)
         r2 = Math.Round(r2, decimalPlaces);
-      return new double[2] { r2, solution.IterateNodesPostfix().OfType<VariableTreeNode>().Count() }; // count the number of variables
+      return new double[2] { r2, solution.IterateNodesPostfix().OfType<IVariableTreeNode>().Count() }; // count the number of variables
     }
 
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs	(revision 15131)
@@ -176,23 +176,35 @@
 
 
-    public static double OptimizeConstants(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree tree, IRegressionProblemData problemData, IEnumerable<int> rows, bool applyLinearScaling, int maxIterations, bool updateVariableWeights = true, double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue, bool updateConstantsInTree = true) {
-
-      List<AutoDiff.Variable> variables = new List<AutoDiff.Variable>();
-      List<AutoDiff.Variable> parameters = new List<AutoDiff.Variable>();
-      List<string> variableNames = new List<string>();
-      List<int> lags = new List<int>();
+
+    public static double OptimizeConstants(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter,
+      ISymbolicExpressionTree tree, IRegressionProblemData problemData, IEnumerable<int> rows, bool applyLinearScaling,
+      int maxIterations, bool updateVariableWeights = true,
+      double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue,
+      bool updateConstantsInTree = true) {
+
+      // numeric constants in the tree become variables for constant opt
+      // variables in the tree become parameters (fixed values) for constant opt
+      // for each parameter (variable in the original tree) we store the 
+      // variable name, variable value (for factor vars) and lag as a DataForVariable object.
+      // A dictionary is used to find parameters
+      var variables = new List<AutoDiff.Variable>();
+      var parameters = new Dictionary<DataForVariable, AutoDiff.Variable>();
 
       AutoDiff.Term func;
-      if (!TryTransformToAutoDiff(tree.Root.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out func))
+      if (!TryTransformToAutoDiff(tree.Root.GetSubtree(0), variables, parameters, updateVariableWeights, out func))
         throw new NotSupportedException("Could not optimize constants of symbolic expression tree due to not supported symbols used in the tree.");
-      if (variableNames.Count == 0) return 0.0;
-
-      AutoDiff.IParametricCompiledTerm compiledFunc = func.Compile(variables.ToArray(), parameters.ToArray());
-
-      List<SymbolicExpressionTreeTerminalNode> terminalNodes = null;
+      if (parameters.Count == 0) return 0.0; // gkronber: constant expressions always have a R² of 0.0 
+
+      var parameterEntries = parameters.ToArray(); // order of entries must be the same for x
+      AutoDiff.IParametricCompiledTerm compiledFunc = func.Compile(variables.ToArray(), parameterEntries.Select(kvp => kvp.Value).ToArray());
+
+      List<SymbolicExpressionTreeTerminalNode> terminalNodes = null; // gkronber only used for extraction of initial constants
       if (updateVariableWeights)
         terminalNodes = tree.Root.IterateNodesPrefix().OfType<SymbolicExpressionTreeTerminalNode>().ToList();
       else
-        terminalNodes = new List<SymbolicExpressionTreeTerminalNode>(tree.Root.IterateNodesPrefix().OfType<ConstantTreeNode>());
+        terminalNodes = new List<SymbolicExpressionTreeTerminalNode>
+          (tree.Root.IterateNodesPrefix()
+          .OfType<SymbolicExpressionTreeTerminalNode>()
+          .Where(node => node is ConstantTreeNode || node is FactorVariableTreeNode));
 
       //extract inital constants
@@ -205,8 +217,16 @@
           ConstantTreeNode constantTreeNode = node as ConstantTreeNode;
           VariableTreeNode variableTreeNode = node as VariableTreeNode;
+          BinaryFactorVariableTreeNode binFactorVarTreeNode = node as BinaryFactorVariableTreeNode;
+          FactorVariableTreeNode factorVarTreeNode = node as FactorVariableTreeNode;
           if (constantTreeNode != null)
             c[i++] = constantTreeNode.Value;
           else if (updateVariableWeights && variableTreeNode != null)
             c[i++] = variableTreeNode.Weight;
+          else if (updateVariableWeights && binFactorVarTreeNode != null)
+            c[i++] = binFactorVarTreeNode.Weight;
+          else if (factorVarTreeNode != null) {
+            // gkronber: a factorVariableTreeNode holds a category-specific constant therefore we can consider factors to be the same as constants
+            foreach (var w in factorVarTreeNode.Weights) c[i++] = w;
+          }
         }
       }
@@ -216,13 +236,20 @@
       alglib.lsfitstate state;
       alglib.lsfitreport rep;
-      int info;
+      int retVal;
 
       IDataset ds = problemData.Dataset;
-      double[,] x = new double[rows.Count(), variableNames.Count];
+      double[,] x = new double[rows.Count(), parameters.Count];
       int row = 0;
       foreach (var r in rows) {
-        for (int col = 0; col < variableNames.Count; col++) {
-          int lag = lags[col];
-          x[row, col] = ds.GetDoubleValue(variableNames[col], r + lag);
+        int col = 0;
+        foreach (var kvp in parameterEntries) {
+          var info = kvp.Key;
+          int lag = info.lag;
+          if (ds.VariableHasType<double>(info.variableName)) {
+            x[row, col] = ds.GetDoubleValue(info.variableName, r + lag);
+          } else if (ds.VariableHasType<string>(info.variableName)) {
+            x[row, col] = ds.GetStringValue(info.variableName, r) == info.variableValue ? 1 : 0;
+          } else throw new InvalidProgramException("found a variable of unknown type");
+          col++;
         }
         row++;
@@ -241,5 +268,5 @@
         //alglib.lsfitsetgradientcheck(state, 0.001);
         alglib.lsfitfit(state, function_cx_1_func, function_cx_1_grad, null, null);
-        alglib.lsfitresults(state, out info, out c, out rep);
+        alglib.lsfitresults(state, out retVal, out c, out rep);
       } catch (ArithmeticException) {
         return originalQuality;
@@ -248,6 +275,6 @@
       }
 
-      //info == -7  => constant optimization failed due to wrong gradient
-      if (info != -7) UpdateConstants(tree, c.Skip(2).ToArray(), updateVariableWeights);
+      //retVal == -7  => constant optimization failed due to wrong gradient
+      if (retVal != -7) UpdateConstants(tree, c.Skip(2).ToArray(), updateVariableWeights);
       var quality = SymbolicRegressionSingleObjectivePearsonRSquaredEvaluator.Calculate(interpreter, tree, lowerEstimationLimit, upperEstimationLimit, problemData, rows, applyLinearScaling);
 
@@ -265,8 +292,16 @@
         ConstantTreeNode constantTreeNode = node as ConstantTreeNode;
         VariableTreeNode variableTreeNode = node as VariableTreeNode;
+        BinaryFactorVariableTreeNode binFactorVarTreeNode = node as BinaryFactorVariableTreeNode;
+        FactorVariableTreeNode factorVarTreeNode = node as FactorVariableTreeNode;
         if (constantTreeNode != null)
           constantTreeNode.Value = constants[i++];
         else if (updateVariableWeights && variableTreeNode != null)
           variableTreeNode.Weight = constants[i++];
+        else if (updateVariableWeights && binFactorVarTreeNode != null)
+          binFactorVarTreeNode.Weight = constants[i++];
+        else if (factorVarTreeNode != null) {
+          for (int j = 0; j < factorVarTreeNode.Weights.Length; j++)
+            factorVarTreeNode.Weights[j] = constants[i++];
+        }
       }
     }
@@ -286,5 +321,7 @@
     }
 
-    private static bool TryTransformToAutoDiff(ISymbolicExpressionTreeNode node, List<AutoDiff.Variable> variables, List<AutoDiff.Variable> parameters, List<string> variableNames, List<int> lags, bool updateVariableWeights, out AutoDiff.Term term) {
+    private static bool TryTransformToAutoDiff(ISymbolicExpressionTreeNode node,
+      List<AutoDiff.Variable> variables, Dictionary<DataForVariable, AutoDiff.Variable> parameters,
+      bool updateVariableWeights, out AutoDiff.Term term) {
       if (node.Symbol is Constant) {
         var var = new AutoDiff.Variable();
@@ -293,10 +330,10 @@
         return true;
       }
-      if (node.Symbol is Variable) {
-        var varNode = node as VariableTreeNode;
-        var par = new AutoDiff.Variable();
-        parameters.Add(par);
-        variableNames.Add(varNode.VariableName);
-        lags.Add(0);
+      if (node.Symbol is Variable || node.Symbol is BinaryFactorVariable) {
+        var varNode = node as VariableTreeNodeBase;
+        var factorVarNode = node as BinaryFactorVariableTreeNode;
+        // factor variable values are only 0 or 1 and set in x accordingly
+        var varValue = factorVarNode != null ? factorVarNode.VariableValue : string.Empty;
+        var par = FindOrCreateParameter(parameters, varNode.VariableName, varValue);
 
         if (updateVariableWeights) {
@@ -309,10 +346,21 @@
         return true;
       }
+      if (node.Symbol is FactorVariable) {
+        var factorVarNode = node as FactorVariableTreeNode;
+        var products = new List<Term>();
+        foreach (var variableValue in factorVarNode.Symbol.GetVariableValues(factorVarNode.VariableName)) {
+          var par = FindOrCreateParameter(parameters, factorVarNode.VariableName, variableValue);
+
+          var wVar = new AutoDiff.Variable();
+          variables.Add(wVar);
+
+          products.Add(AutoDiff.TermBuilder.Product(wVar, par));
+        }
+        term = AutoDiff.TermBuilder.Sum(products);
+        return true;
+      }
       if (node.Symbol is LaggedVariable) {
         var varNode = node as LaggedVariableTreeNode;
-        var par = new AutoDiff.Variable();
-        parameters.Add(par);
-        variableNames.Add(varNode.VariableName);
-        lags.Add(varNode.Lag);
+        var par = FindOrCreateParameter(parameters, varNode.VariableName, string.Empty, varNode.Lag);
 
         if (updateVariableWeights) {
@@ -329,5 +377,5 @@
         foreach (var subTree in node.Subtrees) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(subTree, variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+          if (!TryTransformToAutoDiff(subTree, variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
@@ -342,5 +390,5 @@
         for (int i = 0; i < node.SubtreeCount; i++) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(node.GetSubtree(i), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+          if (!TryTransformToAutoDiff(node.GetSubtree(i), variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
@@ -357,5 +405,5 @@
         foreach (var subTree in node.Subtrees) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(subTree, variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+          if (!TryTransformToAutoDiff(subTree, variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
@@ -372,5 +420,5 @@
         foreach (var subTree in node.Subtrees) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(subTree, variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+          if (!TryTransformToAutoDiff(subTree, variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
@@ -384,5 +432,5 @@
       if (node.Symbol is Logarithm) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -394,5 +442,5 @@
       if (node.Symbol is Exponential) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -404,5 +452,5 @@
       if (node.Symbol is Square) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -414,5 +462,5 @@
       if (node.Symbol is SquareRoot) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -424,5 +472,5 @@
       if (node.Symbol is Sine) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -434,5 +482,5 @@
       if (node.Symbol is Cosine) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -444,5 +492,5 @@
       if (node.Symbol is Tangent) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -454,5 +502,5 @@
       if (node.Symbol is Erf) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -464,5 +512,5 @@
       if (node.Symbol is Norm) {
         AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out t)) {
+        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -478,5 +526,5 @@
         variables.Add(alpha);
         AutoDiff.Term branchTerm;
-        if (TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, updateVariableWeights, out branchTerm)) {
+        if (TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out branchTerm)) {
           term = branchTerm * alpha + beta;
           return true;
@@ -488,4 +536,19 @@
       term = null;
       return false;
+    }
+
+    // for each factor variable value we need a parameter which represents a binary indicator for that variable & value combination
+    // each binary indicator is only necessary once. So we only create a parameter if this combination is not yet available
+    private static Term FindOrCreateParameter(Dictionary<DataForVariable, AutoDiff.Variable> parameters,
+      string varName, string varValue = "", int lag = 0) {
+      var data = new DataForVariable(varName, varValue, lag);
+
+      AutoDiff.Variable par = null;
+      if (!parameters.TryGetValue(data, out par)) {
+        // not found -> create new parameter and entries in names and values lists
+        par = new AutoDiff.Variable();
+        parameters.Add(data, par);
+      }
+      return par;
     }
 
@@ -495,4 +558,6 @@
         where
          !(n.Symbol is Variable) &&
+         !(n.Symbol is BinaryFactorVariable) &&
+         !(n.Symbol is FactorVariable) &&
          !(n.Symbol is LaggedVariable) &&
          !(n.Symbol is Constant) &&
@@ -515,4 +580,31 @@
       return !containsUnknownSymbol;
     }
+
+
+    #region helper class
+    private class DataForVariable {
+      public readonly string variableName;
+      public readonly string variableValue; // for factor vars
+      public readonly int lag;
+
+      public DataForVariable(string varName, string varValue, int lag) {
+        this.variableName = varName;
+        this.variableValue = varValue;
+        this.lag = lag;
+      }
+
+      public override bool Equals(object obj) {
+        var other = obj as DataForVariable;
+        if (other == null) return false;
+        return other.variableName.Equals(this.variableName) &&
+               other.variableValue.Equals(this.variableValue) &&
+               other.lag == this.lag;
+      }
+
+      public override int GetHashCode() {
+        return variableName.GetHashCode() ^ variableValue.GetHashCode() ^ lag;
+      }
+    }
+    #endregion
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SymbolicRegressionSolutionImpactValuesCalculator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SymbolicRegressionSolutionImpactValuesCalculator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SymbolicRegressionSolutionImpactValuesCalculator.cs	(revision 15131)
@@ -41,20 +41,8 @@
     [StorableConstructor]
     protected SymbolicRegressionSolutionImpactValuesCalculator(bool deserializing) : base(deserializing) { }
-    public override double CalculateReplacementValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows) {
-      var regressionModel = (ISymbolicRegressionModel)model;
-      var regressionProblemData = (IRegressionProblemData)problemData;
-
-      return CalculateReplacementValue(node, regressionModel.SymbolicExpressionTree, regressionModel.Interpreter, regressionProblemData.Dataset, rows);
-    }
-
-    public override double CalculateImpactValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows, double qualityForImpactsCalculation = double.NaN) {
-      double impactValue, replacementValue, newQualityForImpactsCalculation;
-      CalculateImpactAndReplacementValues(model, node, problemData, rows, out impactValue, out replacementValue, out newQualityForImpactsCalculation, qualityForImpactsCalculation);
-      return impactValue;
-    }
 
     public override void CalculateImpactAndReplacementValues(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node,
       IDataAnalysisProblemData problemData, IEnumerable<int> rows, out double impactValue, out double replacementValue, out double newQualityForImpactsCalculation,
-      double qualityForImpactsCalculation = Double.NaN) {
+      double qualityForImpactsCalculation = double.NaN) {
       var regressionModel = (ISymbolicRegressionModel)model;
       var regressionProblemData = (IRegressionProblemData)problemData;
@@ -63,10 +51,6 @@
       var targetValues = dataset.GetDoubleValues(regressionProblemData.TargetVariable, rows);
 
-      OnlineCalculatorError errorState;
       if (double.IsNaN(qualityForImpactsCalculation))
         qualityForImpactsCalculation = CalculateQualityForImpacts(regressionModel, regressionProblemData, rows);
-
-      replacementValue = CalculateReplacementValue(regressionModel, node, regressionProblemData, rows);
-      var constantNode = new ConstantTreeNode(new Constant()) { Value = replacementValue };
 
       var cloner = new Cloner();
@@ -76,13 +60,31 @@
       var tempModelParentNode = tempModelNode.Parent;
       int i = tempModelParentNode.IndexOfSubtree(tempModelNode);
-      tempModelParentNode.RemoveSubtree(i);
-      tempModelParentNode.InsertSubtree(i, constantNode);
 
-      var estimatedValues = tempModel.GetEstimatedValues(dataset, rows);
-      double r = OnlinePearsonsRCalculator.Calculate(targetValues, estimatedValues, out errorState);
-      if (errorState != OnlineCalculatorError.None) r = 0.0;
-      newQualityForImpactsCalculation = r * r;
+      double bestReplacementValue = 0.0;
+      double bestImpactValue = double.PositiveInfinity;
+      newQualityForImpactsCalculation = qualityForImpactsCalculation; // initialize
+      // try the potentially reasonable replacement values and use the best one
+      foreach (var repValue in CalculateReplacementValues(node, regressionModel.SymbolicExpressionTree, regressionModel.Interpreter, regressionProblemData.Dataset, regressionProblemData.TrainingIndices)) {
 
-      impactValue = qualityForImpactsCalculation - newQualityForImpactsCalculation;
+        tempModelParentNode.RemoveSubtree(i);
+
+        var constantNode = new ConstantTreeNode(new Constant()) { Value = repValue };
+
+        tempModelParentNode.InsertSubtree(i, constantNode);
+
+        var estimatedValues = tempModel.GetEstimatedValues(dataset, rows);
+        OnlineCalculatorError errorState;
+        double r = OnlinePearsonsRCalculator.Calculate(targetValues, estimatedValues, out errorState);
+        if (errorState != OnlineCalculatorError.None) r = 0.0;
+        newQualityForImpactsCalculation = r * r;
+
+        impactValue = qualityForImpactsCalculation - newQualityForImpactsCalculation;
+        if (impactValue < bestImpactValue) {
+          bestImpactValue = impactValue;
+          bestReplacementValue = repValue;
+        }
+      }
+      replacementValue = bestReplacementValue;
+      impactValue = bestImpactValue;
     }
 
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.TimeSeriesPrognosis.Views/3.4/InteractiveSymbolicTimeSeriesPrognosisSolutionSimplifierView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.TimeSeriesPrognosis.Views/3.4/InteractiveSymbolicTimeSeriesPrognosisSolutionSimplifierView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.TimeSeriesPrognosis.Views/3.4/InteractiveSymbolicTimeSeriesPrognosisSolutionSimplifierView.cs	(revision 15131)
@@ -88,18 +88,4 @@
     }
 
-    protected override Dictionary<ISymbolicExpressionTreeNode, double> CalculateReplacementValues(ISymbolicExpressionTree tree) {
-      var replacementValues = new Dictionary<ISymbolicExpressionTreeNode, double>();
-      foreach (var componentBranch in tree.Root.GetSubtree(0).Subtrees)
-        foreach (ISymbolicExpressionTreeNode node in componentBranch.IterateNodesPrefix()) {
-          replacementValues[node] = CalculateReplacementValue(node, tree);
-        }
-      return replacementValues;
-    }
-
-    protected override Dictionary<ISymbolicExpressionTreeNode, double> CalculateImpactValues(ISymbolicExpressionTree tree) {
-      var impactAndReplacementValues = CalculateImpactAndReplacementValues(tree);
-      return impactAndReplacementValues.ToDictionary(x => x.Key, x => x.Value.Item1); // item1 of the tuple is the impact value
-    }
-
     private double CalculateReplacementValue(ISymbolicExpressionTreeNode node, ISymbolicExpressionTree sourceTree) {
       // remove old ADFs
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicDataAnalysisSolutionSimplifierView.cs	(revision 15131)
@@ -183,6 +183,4 @@
     }
 
-    protected abstract Dictionary<ISymbolicExpressionTreeNode, double> CalculateReplacementValues(ISymbolicExpressionTree tree);
-    protected abstract Dictionary<ISymbolicExpressionTreeNode, double> CalculateImpactValues(ISymbolicExpressionTree tree);
     protected abstract Dictionary<ISymbolicExpressionTreeNode, Tuple<double, double>> CalculateImpactAndReplacementValues(ISymbolicExpressionTree tree);
     protected abstract void UpdateModel(ISymbolicExpressionTree tree);
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/LaggedVariableView.Designer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/LaggedVariableView.Designer.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/LaggedVariableView.Designer.cs	(revision 15131)
@@ -63,4 +63,5 @@
       // weightInitializationMuTextBox
       // 
+      this.weightInitializationMuTextBox.Size = new System.Drawing.Size(293, 20);
       this.toolTip.SetToolTip(this.weightInitializationMuTextBox, "The mu (mean) parameter of the normal distribution from which to sample the initi" +
         "al weights.");
@@ -69,5 +70,5 @@
       // 
       this.initializationGroupBox.Location = new System.Drawing.Point(6, 7);
-      this.initializationGroupBox.Size = new System.Drawing.Size(379, 73);
+      this.initializationGroupBox.Size = new System.Drawing.Size(391, 73);
       // 
       // weightSigmaLabel
@@ -77,4 +78,5 @@
       // weightInitializationSigmaTextBox
       // 
+      this.weightInitializationSigmaTextBox.Size = new System.Drawing.Size(293, 20);
       this.toolTip.SetToolTip(this.weightInitializationSigmaTextBox, "The sigma parameter for the normal distribution from which to sample the initial " +
         "weights.");
@@ -83,8 +85,9 @@
       // 
       this.mutationGroupBox.Location = new System.Drawing.Point(6, 86);
-      this.mutationGroupBox.Size = new System.Drawing.Size(379, 73);
+      this.mutationGroupBox.Size = new System.Drawing.Size(391, 95);
       // 
       // multiplicativeWeightChangeLabel
       // 
+      this.multiplicativeWeightChangeLabel.TabIndex = 4;
       this.toolTip.SetToolTip(this.multiplicativeWeightChangeLabel, "The sigma parameter for the normal distribution to use to sample a multiplicative" +
         " change in weight.");
@@ -92,4 +95,5 @@
       // multiplicativeWeightChangeSigmaTextBox
       // 
+      this.multiplicativeWeightChangeSigmaTextBox.TabIndex = 5;
       this.toolTip.SetToolTip(this.multiplicativeWeightChangeSigmaTextBox, "The sigma (std.dev.) parameter for the normal distribution to sample a multiplica" +
         "tive change in weight.");
@@ -97,4 +101,5 @@
       // additiveWeightChangeLabel
       // 
+      this.additiveWeightChangeLabel.TabIndex = 2;
       this.toolTip.SetToolTip(this.additiveWeightChangeLabel, "The sigma (std.dev.) parameter for the normal distribution to sample an additive " +
         "change in weight.");
@@ -102,4 +107,5 @@
       // additiveWeightChangeSigmaTextBox
       // 
+      this.additiveWeightChangeSigmaTextBox.TabIndex = 3;
       this.toolTip.SetToolTip(this.additiveWeightChangeSigmaTextBox, "The sigma (std.dev.) parameter for the normal distribution to sample an additive " +
         "change in weight.");
@@ -111,14 +117,27 @@
       // parametersTabPage
       // 
-      this.parametersTabPage.Size = new System.Drawing.Size(400, 149);
+      this.parametersTabPage.Size = new System.Drawing.Size(412, 208);
       // 
       // tabControl
       // 
       this.tabControl.Location = new System.Drawing.Point(0, 179);
-      this.tabControl.Size = new System.Drawing.Size(408, 175);
-      this.tabControl.TabIndex = 9;
+      this.tabControl.Size = new System.Drawing.Size(420, 234);
+      this.tabControl.TabIndex = 13;
+      // 
+      // varChangeProbTextBox
+      // 
+      this.varChangeProbTextBox.TabIndex = 1;
+      this.toolTip.SetToolTip(this.varChangeProbTextBox, "The sigma (std.dev.) parameter for the normal distribution to sample an additive " +
+        "change in weight.");
+      // 
+      // varChangeProbLabel
+      // 
+      this.varChangeProbLabel.TabIndex = 0;
+      this.toolTip.SetToolTip(this.varChangeProbLabel, "The probability of changing the referenced variable in [0..1]. Variable reference" +
+        "s are sampled uniformly.");
       // 
       // initialFrequencyLabel
       // 
+      this.initialFrequencyLabel.TabIndex = 2;
       this.toolTip.SetToolTip(this.initialFrequencyLabel, "Relative frequency of the symbol in randomly created trees");
       // 
@@ -126,14 +145,30 @@
       // 
       this.errorProvider.SetIconAlignment(this.initialFrequencyTextBox, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
+      this.initialFrequencyTextBox.Size = new System.Drawing.Size(323, 20);
+      this.initialFrequencyTextBox.TabIndex = 3;
+      // 
+      // enabledCheckBox
+      // 
+      this.enabledCheckBox.TabIndex = 8;
+      // 
+      // minimumArityLabel
+      // 
+      this.minimumArityLabel.TabIndex = 4;
+      this.toolTip.SetToolTip(this.minimumArityLabel, "The minimum arity of the symbol");
+      // 
+      // maximumArityLabel
+      // 
+      this.toolTip.SetToolTip(this.maximumArityLabel, "The maximum arity of the symbol");
       // 
       // minimumArityTextBox
       // 
       this.errorProvider.SetIconAlignment(this.minimumArityTextBox, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
-      this.minimumArityTextBox.Size = new System.Drawing.Size(315, 20);
+      this.minimumArityTextBox.Size = new System.Drawing.Size(327, 20);
+      this.minimumArityTextBox.TabIndex = 5;
       // 
       // maximumArityTextBox
       // 
       this.errorProvider.SetIconAlignment(this.maximumArityTextBox, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
-      this.maximumArityTextBox.Size = new System.Drawing.Size(315, 20);
+      this.maximumArityTextBox.Size = new System.Drawing.Size(327, 20);
       // 
       // nameTextBox
@@ -141,4 +176,9 @@
       this.errorProvider.SetIconAlignment(this.nameTextBox, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
       this.errorProvider.SetIconPadding(this.nameTextBox, 2);
+      this.nameTextBox.Size = new System.Drawing.Size(302, 20);
+      // 
+      // infoLabel
+      // 
+      this.infoLabel.Location = new System.Drawing.Point(401, 3);
       // 
       // minTimeOffsetLabel
@@ -148,5 +188,5 @@
       this.minTimeOffsetLabel.Name = "minTimeOffsetLabel";
       this.minTimeOffsetLabel.Size = new System.Drawing.Size(81, 13);
-      this.minTimeOffsetLabel.TabIndex = 5;
+      this.minTimeOffsetLabel.TabIndex = 9;
       this.minTimeOffsetLabel.Text = "Min. time offset:";
       // 
@@ -157,30 +197,29 @@
       this.maxTimeOffsetLabel.Name = "maxTimeOffsetLabel";
       this.maxTimeOffsetLabel.Size = new System.Drawing.Size(84, 13);
-      this.maxTimeOffsetLabel.TabIndex = 7;
+      this.maxTimeOffsetLabel.TabIndex = 11;
       this.maxTimeOffsetLabel.Text = "Max. time offset:";
       // 
       // minTimeOffsetTextBox
       // 
-      this.minTimeOffsetTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+      this.minTimeOffsetTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.minTimeOffsetTextBox.Location = new System.Drawing.Point(93, 127);
       this.minTimeOffsetTextBox.Name = "minTimeOffsetTextBox";
-      this.minTimeOffsetTextBox.Size = new System.Drawing.Size(315, 20);
-      this.minTimeOffsetTextBox.TabIndex = 6;
+      this.minTimeOffsetTextBox.Size = new System.Drawing.Size(327, 20);
+      this.minTimeOffsetTextBox.TabIndex = 10;
       this.minTimeOffsetTextBox.TextChanged += new System.EventHandler(this.minTimeOffsetTextBox_TextChanged);
       // 
       // maxTimeOffsetTextBox
       // 
-      this.maxTimeOffsetTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+      this.maxTimeOffsetTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
       this.maxTimeOffsetTextBox.Location = new System.Drawing.Point(93, 153);
       this.maxTimeOffsetTextBox.Name = "maxTimeOffsetTextBox";
-      this.maxTimeOffsetTextBox.Size = new System.Drawing.Size(315, 20);
-      this.maxTimeOffsetTextBox.TabIndex = 8;
+      this.maxTimeOffsetTextBox.Size = new System.Drawing.Size(327, 20);
+      this.maxTimeOffsetTextBox.TabIndex = 12;
       this.maxTimeOffsetTextBox.TextChanged += new System.EventHandler(this.maxTimeOffsetTextBox_TextChanged);
       // 
       // LaggedVariableView
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.Controls.Add(this.minTimeOffsetTextBox);
@@ -189,5 +228,6 @@
       this.Controls.Add(this.maxTimeOffsetTextBox);
       this.Name = "LaggedVariableView";
-      this.Size = new System.Drawing.Size(408, 352);
+      this.Size = new System.Drawing.Size(420, 411);
+      this.Controls.SetChildIndex(this.enabledCheckBox, 0);
       this.Controls.SetChildIndex(this.maxTimeOffsetTextBox, 0);
       this.Controls.SetChildIndex(this.minTimeOffsetLabel, 0);
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/VariableView.Designer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/VariableView.Designer.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/VariableView.Designer.cs	(revision 15131)
@@ -51,4 +51,6 @@
       this.weightInitializationSigmaTextBox = new System.Windows.Forms.TextBox();
       this.mutationGroupBox = new System.Windows.Forms.GroupBox();
+      this.varChangeProbTextBox = new System.Windows.Forms.TextBox();
+      this.varChangeProbLabel = new System.Windows.Forms.Label();
       this.multiplicativeWeightChangeLabel = new System.Windows.Forms.Label();
       this.multiplicativeWeightChangeSigmaTextBox = new System.Windows.Forms.TextBox();
@@ -67,4 +69,5 @@
       // initialFrequencyLabel
       // 
+      this.initialFrequencyLabel.TabIndex = 2;
       this.toolTip.SetToolTip(this.initialFrequencyLabel, "Relative frequency of the symbol in randomly created trees");
       // 
@@ -73,7 +76,13 @@
       this.errorProvider.SetIconAlignment(this.initialFrequencyTextBox, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
       this.initialFrequencyTextBox.Size = new System.Drawing.Size(311, 20);
+      this.initialFrequencyTextBox.TabIndex = 3;
+      // 
+      // enabledCheckBox
+      // 
+      this.enabledCheckBox.TabIndex = 8;
       // 
       // minimumArityLabel
       // 
+      this.minimumArityLabel.TabIndex = 4;
       this.toolTip.SetToolTip(this.minimumArityLabel, "The minimum arity of the symbol");
       // 
@@ -86,4 +95,5 @@
       this.errorProvider.SetIconAlignment(this.minimumArityTextBox, System.Windows.Forms.ErrorIconAlignment.MiddleLeft);
       this.minimumArityTextBox.Size = new System.Drawing.Size(311, 20);
+      this.minimumArityTextBox.TabIndex = 5;
       // 
       // maximumArityTextBox
@@ -165,4 +175,6 @@
       this.mutationGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+      this.mutationGroupBox.Controls.Add(this.varChangeProbTextBox);
+      this.mutationGroupBox.Controls.Add(this.varChangeProbLabel);
       this.mutationGroupBox.Controls.Add(this.multiplicativeWeightChangeLabel);
       this.mutationGroupBox.Controls.Add(this.multiplicativeWeightChangeSigmaTextBox);
@@ -171,16 +183,39 @@
       this.mutationGroupBox.Location = new System.Drawing.Point(6, 85);
       this.mutationGroupBox.Name = "mutationGroupBox";
-      this.mutationGroupBox.Size = new System.Drawing.Size(391, 73);
+      this.mutationGroupBox.Size = new System.Drawing.Size(391, 97);
       this.mutationGroupBox.TabIndex = 1;
       this.mutationGroupBox.TabStop = false;
       this.mutationGroupBox.Text = "Mutation";
       // 
+      // varChangeProbTextBox
+      // 
+      this.varChangeProbTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.varChangeProbTextBox.Location = new System.Drawing.Point(201, 13);
+      this.varChangeProbTextBox.Name = "varChangeProbTextBox";
+      this.varChangeProbTextBox.Size = new System.Drawing.Size(184, 20);
+      this.varChangeProbTextBox.TabIndex = 1;
+      this.toolTip.SetToolTip(this.varChangeProbTextBox, "The sigma (std.dev.) parameter for the normal distribution to sample an additive " +
+        "change in weight.");
+      this.varChangeProbTextBox.TextChanged += new System.EventHandler(this.VarProbTextBox_TextChanged);
+      // 
+      // varChangeProbLabel
+      // 
+      this.varChangeProbLabel.AutoSize = true;
+      this.varChangeProbLabel.Location = new System.Drawing.Point(6, 16);
+      this.varChangeProbLabel.Name = "varChangeProbLabel";
+      this.varChangeProbLabel.Size = new System.Drawing.Size(134, 13);
+      this.varChangeProbLabel.TabIndex = 0;
+      this.varChangeProbLabel.Text = "Variable change probability";
+      this.toolTip.SetToolTip(this.varChangeProbLabel, "The probability of changing the referenced variable in [0..1]. Variable reference" +
+        "s are sampled uniformly.");
+      // 
       // multiplicativeWeightChangeLabel
       // 
       this.multiplicativeWeightChangeLabel.AutoSize = true;
-      this.multiplicativeWeightChangeLabel.Location = new System.Drawing.Point(6, 48);
+      this.multiplicativeWeightChangeLabel.Location = new System.Drawing.Point(6, 68);
       this.multiplicativeWeightChangeLabel.Name = "multiplicativeWeightChangeLabel";
       this.multiplicativeWeightChangeLabel.Size = new System.Drawing.Size(180, 13);
-      this.multiplicativeWeightChangeLabel.TabIndex = 2;
+      this.multiplicativeWeightChangeLabel.TabIndex = 4;
       this.multiplicativeWeightChangeLabel.Text = "Multiplicative weight change (sigma):";
       this.toolTip.SetToolTip(this.multiplicativeWeightChangeLabel, "The sigma parameter for the normal distribution to use to sample a multiplicative" +
@@ -191,8 +226,8 @@
       this.multiplicativeWeightChangeSigmaTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-      this.multiplicativeWeightChangeSigmaTextBox.Location = new System.Drawing.Point(201, 45);
+      this.multiplicativeWeightChangeSigmaTextBox.Location = new System.Drawing.Point(201, 65);
       this.multiplicativeWeightChangeSigmaTextBox.Name = "multiplicativeWeightChangeSigmaTextBox";
       this.multiplicativeWeightChangeSigmaTextBox.Size = new System.Drawing.Size(184, 20);
-      this.multiplicativeWeightChangeSigmaTextBox.TabIndex = 3;
+      this.multiplicativeWeightChangeSigmaTextBox.TabIndex = 5;
       this.toolTip.SetToolTip(this.multiplicativeWeightChangeSigmaTextBox, "The sigma (std.dev.) parameter for the normal distribution to sample a multiplica" +
         "tive change in weight.");
@@ -202,8 +237,8 @@
       // 
       this.additiveWeightChangeLabel.AutoSize = true;
-      this.additiveWeightChangeLabel.Location = new System.Drawing.Point(6, 22);
+      this.additiveWeightChangeLabel.Location = new System.Drawing.Point(6, 42);
       this.additiveWeightChangeLabel.Name = "additiveWeightChangeLabel";
       this.additiveWeightChangeLabel.Size = new System.Drawing.Size(157, 13);
-      this.additiveWeightChangeLabel.TabIndex = 0;
+      this.additiveWeightChangeLabel.TabIndex = 2;
       this.additiveWeightChangeLabel.Text = "Additive weight change (sigma):";
       this.toolTip.SetToolTip(this.additiveWeightChangeLabel, "The sigma (std.dev.) parameter for the normal distribution to sample an additive " +
@@ -214,8 +249,8 @@
       this.additiveWeightChangeSigmaTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-      this.additiveWeightChangeSigmaTextBox.Location = new System.Drawing.Point(201, 19);
+      this.additiveWeightChangeSigmaTextBox.Location = new System.Drawing.Point(201, 39);
       this.additiveWeightChangeSigmaTextBox.Name = "additiveWeightChangeSigmaTextBox";
       this.additiveWeightChangeSigmaTextBox.Size = new System.Drawing.Size(184, 20);
-      this.additiveWeightChangeSigmaTextBox.TabIndex = 1;
+      this.additiveWeightChangeSigmaTextBox.TabIndex = 3;
       this.toolTip.SetToolTip(this.additiveWeightChangeSigmaTextBox, "The sigma (std.dev.) parameter for the normal distribution to sample an additive " +
         "change in weight.");
@@ -232,6 +267,6 @@
       this.tabControl.Name = "tabControl";
       this.tabControl.SelectedIndex = 0;
-      this.tabControl.Size = new System.Drawing.Size(408, 190);
-      this.tabControl.TabIndex = 5;
+      this.tabControl.Size = new System.Drawing.Size(408, 216);
+      this.tabControl.TabIndex = 9;
       // 
       // variableNamesTabPage
@@ -240,5 +275,5 @@
       this.variableNamesTabPage.Name = "variableNamesTabPage";
       this.variableNamesTabPage.Padding = new System.Windows.Forms.Padding(3);
-      this.variableNamesTabPage.Size = new System.Drawing.Size(400, 164);
+      this.variableNamesTabPage.Size = new System.Drawing.Size(400, 190);
       this.variableNamesTabPage.TabIndex = 0;
       this.variableNamesTabPage.Text = "Variable Names";
@@ -252,5 +287,5 @@
       this.parametersTabPage.Name = "parametersTabPage";
       this.parametersTabPage.Padding = new System.Windows.Forms.Padding(3);
-      this.parametersTabPage.Size = new System.Drawing.Size(400, 164);
+      this.parametersTabPage.Size = new System.Drawing.Size(400, 190);
       this.parametersTabPage.TabIndex = 1;
       this.parametersTabPage.Text = "Parameters";
@@ -259,9 +294,9 @@
       // VariableView
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
       this.Controls.Add(this.tabControl);
       this.Name = "VariableView";
-      this.Size = new System.Drawing.Size(408, 317);
+      this.Size = new System.Drawing.Size(408, 346);
+      this.Controls.SetChildIndex(this.enabledCheckBox, 0);
       this.Controls.SetChildIndex(this.maximumArityLabel, 0);
       this.Controls.SetChildIndex(this.maximumArityTextBox, 0);
@@ -301,4 +336,6 @@
     protected System.Windows.Forms.TabPage parametersTabPage;
     protected System.Windows.Forms.TabControl tabControl;
+    protected System.Windows.Forms.TextBox varChangeProbTextBox;
+    protected System.Windows.Forms.Label varChangeProbLabel;
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/VariableView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/VariableView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/Symbols/VariableView.cs	(revision 15131)
@@ -29,15 +29,14 @@
 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
 using HeuristicLab.MainForm;
-using HeuristicLab.MainForm.WindowsForms;
 
 
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views {
   [View("Variable View")]
-  [Content(typeof(Variable), true)]
+  [Content(typeof(VariableBase), true)]
   public partial class VariableView : SymbolView {
     private CheckedItemCollectionView<StringValue> variableNamesView;
 
-    public new Variable Content {
-      get { return (Variable)base.Content; }
+    public new VariableBase Content {
+      get { return (VariableBase)base.Content; }
       set { base.Content = value; }
     }
@@ -101,4 +100,6 @@
       multiplicativeWeightChangeSigmaTextBox.Enabled = Content != null;
       multiplicativeWeightChangeSigmaTextBox.ReadOnly = ReadOnly;
+      varChangeProbTextBox.Enabled = Content != null;
+      varChangeProbTextBox.ReadOnly = ReadOnly;
     }
 
@@ -175,4 +176,14 @@
       }
     }
+
+    private void VarProbTextBox_TextChanged(object sender, EventArgs e) {
+      double prob;
+      if (double.TryParse(varChangeProbTextBox.Text, out prob) && prob >= 0.0 && prob <= 1.0) {
+        Content.VariableChangeProbability = prob;
+        errorProvider.SetError(varChangeProbTextBox, string.Empty);
+      } else {
+        errorProvider.SetError(varChangeProbTextBox, "Invalid value");
+      }
+    }
     #endregion
 
@@ -184,4 +195,5 @@
         additiveWeightChangeSigmaTextBox.Text = string.Empty;
         multiplicativeWeightChangeSigmaTextBox.Text = string.Empty;
+        varChangeProbTextBox.Text = string.Empty;
         // temporarily deregister to prevent circular calling of events
         DeregisterVariableNamesViewContentEvents();
@@ -201,8 +213,10 @@
         additiveWeightChangeSigmaTextBox.Text = Content.WeightManipulatorSigma.ToString();
         multiplicativeWeightChangeSigmaTextBox.Text = Content.MultiplicativeWeightManipulatorSigma.ToString();
+        varChangeProbTextBox.Text = Content.VariableChangeProbability.ToString();
       }
       SetEnabledStateOfControls();
     }
     #endregion
+
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/TreeEditDialogs/SymbolicExpressionTreeNodeInsertDialog.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/TreeEditDialogs/SymbolicExpressionTreeNodeInsertDialog.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/TreeEditDialogs/SymbolicExpressionTreeNodeInsertDialog.cs	(revision 15131)
@@ -65,6 +65,6 @@
         constantValueLabel.Visible = true;
         constantValueTextBox.Visible = true;
-      } else if (symbol is Variable) {
-        var variableSymbol = (Variable)symbol;
+      } else if (symbol is VariableBase) {
+        var variableSymbol = (VariableBase)symbol;
         if (variableSymbol.VariableNames.Any()) {
           foreach (var name in variableSymbol.VariableNames)
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisAlleleFrequencyAnalyzer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisAlleleFrequencyAnalyzer.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisAlleleFrequencyAnalyzer.cs	(revision 15131)
@@ -89,6 +89,12 @@
       var varTreeNode = tree as VariableTreeNode;
       var constTreeNode = tree as ConstantTreeNode;
+      var factorVarTreeNode = tree as FactorVariableTreeNode;
+      var binFactorVarTreeNode = tree as BinaryFactorVariableTreeNode;
       if (varTreeNode != null) {
         builder.Append("(var " + varTreeNode.VariableName);
+      } else if (factorVarTreeNode != null) {
+        builder.Append("(factor " + factorVarTreeNode.VariableName);
+      } else if (binFactorVarTreeNode != null) {
+        builder.Append("(factor " + binFactorVarTreeNode.VariableName + "=" + binFactorVarTreeNode.VariableValue);
       } else if (constTreeNode != null) {
         builder.Append("(const");
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisVariableFrequencyAnalyzer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisVariableFrequencyAnalyzer.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisVariableFrequencyAnalyzer.cs	(revision 15131)
@@ -22,4 +22,5 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using HeuristicLab.Analysis;
@@ -41,4 +42,5 @@
     private const string VariableFrequenciesParameterName = "VariableFrequencies";
     private const string AggregateLaggedVariablesParameterName = "AggregateLaggedVariables";
+    private const string AggregateFactorVariablesParameterName = "AggregateFactorVariables";
     private const string VariableImpactsParameterName = "VariableImpacts";
 
@@ -52,4 +54,7 @@
     public IValueLookupParameter<BoolValue> AggregateLaggedVariablesParameter {
       get { return (IValueLookupParameter<BoolValue>)Parameters[AggregateLaggedVariablesParameterName]; }
+    }
+    public IValueLookupParameter<BoolValue> AggregateFactorVariablesParameter {
+      get { return (IValueLookupParameter<BoolValue>)Parameters[AggregateFactorVariablesParameterName]; }
     }
     #endregion
@@ -59,4 +64,8 @@
       set { AggregateLaggedVariablesParameter.Value = value; }
     }
+    public BoolValue AggregateFactorVariables {
+      get { return AggregateFactorVariablesParameter.ActualValue; }
+      set { AggregateFactorVariablesParameter.Value = value; }
+    }
     #endregion
     [StorableConstructor]
@@ -70,4 +79,15 @@
       Parameters.Add(new LookupParameter<DoubleMatrix>(VariableImpactsParameterName, "The relative variable relevance calculated as the average relative variable frequency over the whole run."));
       Parameters.Add(new ValueLookupParameter<BoolValue>(AggregateLaggedVariablesParameterName, "Switch that determines whether all references to a variable should be aggregated regardless of time-offsets. Turn off to analyze all variable references with different time offsets separately.", new BoolValue(true)));
+      Parameters.Add(new ValueLookupParameter<BoolValue>(AggregateFactorVariablesParameterName, "Switch that determines whether all references to factor variables should be aggregated regardless of the value. Turn off to analyze all factor variable references with different values separately.", new BoolValue(true)));
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      // BackwardsCompatibility3.3
+      #region Backwards compatible code, remove with 3.4
+      if (!Parameters.ContainsKey(AggregateFactorVariablesParameterName)) {
+        Parameters.Add(new ValueLookupParameter<BoolValue>(AggregateFactorVariablesParameterName, "Switch that determines whether all references to factor variables should be aggregated regardless of the value. Turn off to analyze all factor variable references with different values separately.", new BoolValue(true)));
+      }
+      #endregion
     }
 
@@ -93,5 +113,5 @@
       int numberOfValues = datatable.Rows.Select(r => r.Values.Count).DefaultIfEmpty().First();
 
-      foreach (var pair in SymbolicDataAnalysisVariableFrequencyAnalyzer.CalculateVariableFrequencies(expressions, AggregateLaggedVariables.Value)) {
+      foreach (var pair in CalculateVariableFrequencies(expressions, AggregateLaggedVariables.Value, AggregateFactorVariables.Value)) {
         if (!datatable.Rows.ContainsKey(pair.Key)) {
           // initialize a new row for the variable and pad with zeros
@@ -128,8 +148,9 @@
     }
 
-    public static IEnumerable<KeyValuePair<string, double>> CalculateVariableFrequencies(IEnumerable<ISymbolicExpressionTree> trees, bool aggregateLaggedVariables = true) {
+    public static IEnumerable<KeyValuePair<string, double>> CalculateVariableFrequencies(IEnumerable<ISymbolicExpressionTree> trees,
+      bool aggregateLaggedVariables = true, bool aggregateFactorVariables = true) {
 
       var variableFrequencies = trees
-        .SelectMany(t => GetVariableReferences(t, aggregateLaggedVariables))
+        .SelectMany(t => GetVariableReferences(t, aggregateLaggedVariables, aggregateFactorVariables))
         .GroupBy(pair => pair.Key, pair => pair.Value)
         .ToDictionary(g => g.Key, g => (double)g.Sum());
@@ -141,49 +162,61 @@
     }
 
-    private static IEnumerable<KeyValuePair<string, int>> GetVariableReferences(ISymbolicExpressionTree tree, bool aggregateLaggedVariables = true) {
+    private static IEnumerable<KeyValuePair<string, int>> GetVariableReferences(ISymbolicExpressionTree tree,
+      bool aggregateLaggedVariables = true, bool aggregateFactorVariables = true) {
       Dictionary<string, int> references = new Dictionary<string, int>();
       if (aggregateLaggedVariables) {
         tree.Root.ForEachNodePrefix(node => {
-          if (node.Symbol is Variable) {
-            var varNode = node as VariableTreeNode;
-            IncReferenceCount(references, varNode.VariableName);
-          } else if (node.Symbol is VariableCondition) {
-            var varCondNode = node as VariableConditionTreeNode;
-            IncReferenceCount(references, varCondNode.VariableName);
+          if (node is IVariableTreeNode) {
+            var factorNode = node as BinaryFactorVariableTreeNode;
+            if (factorNode != null && !aggregateFactorVariables) {
+              IncReferenceCount(references, factorNode.VariableName + "=" + factorNode.VariableValue);
+            } else {
+              var varNode = node as IVariableTreeNode;
+              IncReferenceCount(references, varNode.VariableName);
+            }
           }
         });
       } else {
-        GetVariableReferences(references, tree.Root, 0);
+        GetVariableReferences(references, tree.Root, 0, aggregateFactorVariables);
       }
       return references;
     }
 
-    private static void GetVariableReferences(Dictionary<string, int> references, ISymbolicExpressionTreeNode node, int currentLag) {
-      if (node.Symbol is LaggedVariable) {
-        var laggedVarNode = node as LaggedVariableTreeNode;
-        IncReferenceCount(references, laggedVarNode.VariableName, currentLag + laggedVarNode.Lag);
-      } else if (node.Symbol is Variable) {
-        var varNode = node as VariableTreeNode;
-        IncReferenceCount(references, varNode.VariableName, currentLag);
-      } else if (node.Symbol is VariableCondition) {
-        var varCondNode = node as VariableConditionTreeNode;
-        IncReferenceCount(references, varCondNode.VariableName, currentLag);
-        GetVariableReferences(references, node.GetSubtree(0), currentLag);
-        GetVariableReferences(references, node.GetSubtree(1), currentLag);
+    private static void GetVariableReferences(Dictionary<string, int> references, ISymbolicExpressionTreeNode node, int currentLag, bool aggregateFactorVariables) {
+      if (node is IVariableTreeNode) {
+        var laggedVarTreeNode = node as LaggedVariableTreeNode;
+        var binFactorVariableTreeNode = node as BinaryFactorVariableTreeNode;
+        var varConditionTreeNode = node as VariableConditionTreeNode;
+        if (laggedVarTreeNode != null) {
+          IncReferenceCount(references, laggedVarTreeNode.VariableName, currentLag + laggedVarTreeNode.Lag);
+        } else if (binFactorVariableTreeNode != null) {
+          if (aggregateFactorVariables) {
+            IncReferenceCount(references, binFactorVariableTreeNode.VariableName, currentLag);
+          } else {
+            IncReferenceCount(references, binFactorVariableTreeNode.VariableName + "=" + binFactorVariableTreeNode.VariableValue, currentLag);
+          }
+        } else if (varConditionTreeNode != null) {
+          IncReferenceCount(references, varConditionTreeNode.VariableName, currentLag);
+          GetVariableReferences(references, node.GetSubtree(0), currentLag, aggregateFactorVariables);
+          GetVariableReferences(references, node.GetSubtree(1), currentLag, aggregateFactorVariables);
+        } else {
+          var varNode = node as IVariableTreeNode;
+          IncReferenceCount(references, varNode.VariableName, currentLag);
+        }
       } else if (node.Symbol is Integral) {
         var laggedNode = node as LaggedTreeNode;
         for (int l = laggedNode.Lag; l <= 0; l++) {
-          GetVariableReferences(references, node.GetSubtree(0), currentLag + l);
+          GetVariableReferences(references, node.GetSubtree(0), currentLag + l, aggregateFactorVariables);
         }
       } else if (node.Symbol is Derivative) {
         for (int l = -4; l <= 0; l++) {
-          GetVariableReferences(references, node.GetSubtree(0), currentLag + l);
+          GetVariableReferences(references, node.GetSubtree(0), currentLag + l, aggregateFactorVariables);
         }
       } else if (node.Symbol is TimeLag) {
         var laggedNode = node as LaggedTreeNode;
-        GetVariableReferences(references, node.GetSubtree(0), currentLag + laggedNode.Lag);
+        GetVariableReferences(references, node.GetSubtree(0), currentLag + laggedNode.Lag, aggregateFactorVariables);
       } else {
         foreach (var subtree in node.Subtrees) {
-          GetVariableReferences(references, subtree, currentLag);
+          GetVariableReferences(references, subtree, currentLag, aggregateFactorVariables);
         }
       }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/InfixExpressionFormatter.cs	(revision 15131)
@@ -141,4 +141,36 @@
             strBuilder.Append(")");
           }
+        } else if (node.Symbol is FactorVariable) {
+          var factorNode = node as FactorVariableTreeNode;
+          if (factorNode.VariableName.Contains("'")) {
+            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
+          } else {
+            strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
+          }
+          strBuilder.AppendFormat("[{0}]",
+            string.Join(", ", factorNode.Weights.Select(w => w.ToString(CultureInfo.InvariantCulture))));
+        } else if (node.Symbol is BinaryFactorVariable) {
+          var factorNode = node as BinaryFactorVariableTreeNode;
+          if (!factorNode.Weight.IsAlmost(1.0)) {
+            strBuilder.Append("(");
+            strBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", factorNode.Weight);
+            strBuilder.Append("*");
+          }
+          if (factorNode.VariableName.Contains("'")) {
+            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableName);
+          } else {
+            strBuilder.AppendFormat("'{0}'", factorNode.VariableName);
+          }
+          strBuilder.Append(" = ");
+          if (factorNode.VariableValue.Contains("'")) {
+            strBuilder.AppendFormat("\"{0}\"", factorNode.VariableValue);
+          } else {
+            strBuilder.AppendFormat("'{0}'", factorNode.VariableValue);
+          }
+
+          if (!factorNode.Weight.IsAlmost(1.0)) {
+            strBuilder.Append(")");
+          }
+
         } else if (node.Symbol is Constant) {
           var constNode = node as ConstantTreeNode;
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionCSharpFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionCSharpFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionCSharpFormatter.cs	(revision 15131)
@@ -25,4 +25,5 @@
 using System.Linq;
 using System.Text;
+using System.Text.RegularExpressions;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -53,4 +54,42 @@
       GenerateFooter(strBuilder);
       return strBuilder.ToString();
+    }
+
+    private string VariableName2Identifier(string name) {     
+      /*
+       * identifier-start-character:
+       *    letter-character
+       *    _ (the underscore character U+005F)
+       *  identifier-part-characters:
+       *    identifier-part-character
+       *    identifier-part-characters   identifier-part-character
+       *  identifier-part-character:
+       *    letter-character
+       *    decimal-digit-character
+       *    connecting-character
+       *    combining-character
+       *    formatting-character
+       *  letter-character:
+       *    A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl 
+       *    A unicode-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl
+       *  combining-character:
+       *    A Unicode character of classes Mn or Mc 
+       *    A unicode-escape-sequence representing a character of classes Mn or Mc
+       *  decimal-digit-character:
+       *    A Unicode character of the class Nd 
+       *    A unicode-escape-sequence representing a character of the class Nd
+       *  connecting-character:
+       *    A Unicode character of the class Pc 
+       *    A unicode-escape-sequence representing a character of the class Pc
+       *  formatting-character:
+       *    A Unicode character of the class Cf 
+       *    A unicode-escape-sequence representing a character of the class Cf
+       */
+
+      var invalidIdentifierStarts = new Regex(@"[^_\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}]");
+      var invalidIdentifierParts = new Regex(@"[^\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\p{Cf}]");
+      return "@" +
+        (invalidIdentifierStarts.IsMatch(name.Substring(0, 1)) ? "_" : "") + // prepend '_' if necessary
+        invalidIdentifierParts.Replace(name, "_");
     }
 
@@ -106,12 +145,27 @@
         if (node is VariableTreeNode) {
           var varNode = node as VariableTreeNode;
-          strBuilder.AppendFormat("{0} * {1}", varNode.VariableName, varNode.Weight.ToString("g17", CultureInfo.InvariantCulture));
+          strBuilder.AppendFormat("{0} * {1}", VariableName2Identifier(varNode.VariableName), varNode.Weight.ToString("g17", CultureInfo.InvariantCulture));
         } else if (node is ConstantTreeNode) {
           var constNode = node as ConstantTreeNode;
           strBuilder.Append(constNode.Value.ToString("g17", CultureInfo.InvariantCulture));
+        } else if (node.Symbol is FactorVariable) {
+          var factorNode = node as FactorVariableTreeNode;
+          FormatFactor(factorNode, strBuilder);
+        } else if (node.Symbol is BinaryFactorVariable) {
+          var binFactorNode = node as BinaryFactorVariableTreeNode;
+          FormatBinaryFactor(binFactorNode, strBuilder);
         } else {
           throw new NotSupportedException("Formatting of symbol: " + node.Symbol + " not supported for C# symbolic expression tree formatter.");
         }
       }
+    }
+
+    private void FormatFactor(FactorVariableTreeNode node, StringBuilder strBuilder) {
+      strBuilder.AppendFormat("EvaluateFactor({0}, new [] {{ {1} }}, new [] {{ {2} }})", VariableName2Identifier(node.VariableName),
+        string.Join(",", node.Symbol.GetVariableValues(node.VariableName).Select(name => "\"" + name + "\"")), string.Join(",", node.Weights.Select(v => v.ToString(CultureInfo.InvariantCulture))));
+    }
+
+    private void FormatBinaryFactor(BinaryFactorVariableTreeNode node, StringBuilder strBuilder) {
+      strBuilder.AppendFormat(CultureInfo.InvariantCulture, "EvaluateBinaryFactor({0}, \"{1}\", {2})", VariableName2Identifier(node.VariableName), node.VariableValue, node.Weight);
     }
 
@@ -182,13 +236,27 @@
       GenerateAverageSource(strBuilder);
       GenerateIfThenElseSource(strBuilder);
+      GenerateFactorSource(strBuilder);
+      GenerateBinaryFactorSource(strBuilder);
       strBuilder.Append(Environment.NewLine + "public static double Evaluate (");
 
-      HashSet<string> varNames = new HashSet<string>();
-      foreach (var node in symbolicExpressionTree.IterateNodesPostfix().Where(x => x is VariableTreeNode)) {
-        varNames.Add(((VariableTreeNode)node).VariableName);
-      }
-
-      var orderedNames = varNames.OrderBy(n => n, new NaturalStringComparer()).Select(n => "double " + n);
+      // here we don't have access to problemData to determine the type for each variable (double/string) therefore we must distinguish based on the symbol type
+      HashSet<string> doubleVarNames = new HashSet<string>();
+      foreach (var node in symbolicExpressionTree.IterateNodesPostfix().Where(x => x is VariableTreeNode || x is VariableConditionTreeNode)) {
+        doubleVarNames.Add(((IVariableTreeNode)node).VariableName);
+      }
+
+      HashSet<string> stringVarNames = new HashSet<string>();
+      foreach (var node in symbolicExpressionTree.IterateNodesPostfix().Where(x => x is BinaryFactorVariableTreeNode || x is FactorVariableTreeNode)) {
+        stringVarNames.Add(((IVariableTreeNode)node).VariableName);
+      }
+
+      var orderedNames = stringVarNames.OrderBy(n => n, new NaturalStringComparer()).Select(n => "string " + VariableName2Identifier(n) + " /* " + n + " */");
       strBuilder.Append(string.Join(", ", orderedNames));
+
+      if (stringVarNames.Any() && doubleVarNames.Any())
+        strBuilder.AppendLine(",");
+      orderedNames = doubleVarNames.OrderBy(n => n, new NaturalStringComparer()).Select(n => "double " + VariableName2Identifier(n) + " /* " + n + " */");
+      strBuilder.Append(string.Join(", ", orderedNames));
+
 
       strBuilder.AppendLine(") {");
@@ -198,4 +266,5 @@
     private void GenerateFooter(StringBuilder strBuilder) {
       strBuilder.AppendLine(";");
+
       strBuilder.AppendLine("return result;");
       strBuilder.AppendLine("}");
@@ -215,4 +284,19 @@
       strBuilder.AppendLine("}");
     }
+
+    private void GenerateFactorSource(StringBuilder strBuilder) {
+      strBuilder.AppendLine("private static double EvaluateFactor(string factorValue, string[] factorValues, double[] constants) {");
+      strBuilder.AppendLine("   for(int i=0;i<factorValues.Length;i++) " +
+                            "      if(factorValues[i] == factorValue) return constants[i];" +
+                            "   throw new ArgumentException();");
+      strBuilder.AppendLine("}");
+    }
+
+    private void GenerateBinaryFactorSource(StringBuilder strBuilder) {
+      strBuilder.AppendLine("private static double EvaluateBinaryFactor(string factorValue, string targetValue, double weight) {");
+      strBuilder.AppendLine("  return factorValue == targetValue ? weight : 0.0;");
+      strBuilder.AppendLine("}");
+    }
+
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionExcelFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionExcelFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionExcelFormatter.cs	(revision 15131)
@@ -51,5 +51,5 @@
       while (dividend > 0) {
         int modulo = (dividend - 1) % 26;
-        columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
+        columnName = Convert.ToChar(65 + modulo) + columnName;
         dividend = (int)((dividend - modulo) / 26);
       }
@@ -60,10 +60,10 @@
     private readonly Dictionary<string, string> variableNameMapping = new Dictionary<string, string>();
     private int currentVariableIndex = 0;
-    private string GetColumnToVariableName(string variabelName) {
-      if (!variableNameMapping.ContainsKey(variabelName)) {
+    private string GetColumnToVariableName(string varName) {
+      if (!variableNameMapping.ContainsKey(varName)) {
         currentVariableIndex++;
-        variableNameMapping.Add(variabelName, GetExcelColumnName(currentVariableIndex));
-      }
-      return string.Format("${0}1", variableNameMapping[variabelName]);
+        variableNameMapping.Add(varName, GetExcelColumnName(currentVariableIndex));
+      }
+      return string.Format("${0}1", variableNameMapping[varName]);
     }
     public string Format(ISymbolicExpressionTree symbolicExpressionTree) {
@@ -71,12 +71,20 @@
     }
 
+
     public string Format(ISymbolicExpressionTree symbolicExpressionTree, IDataset dataset) {
+      if (dataset != null)
+        return FormatWithMapping(symbolicExpressionTree, CalculateVariableMapping(symbolicExpressionTree, dataset));
+      else return FormatWithMapping(symbolicExpressionTree, new Dictionary<string, string>());
+    }
+
+    public string FormatWithMapping(ISymbolicExpressionTree symbolicExpressionTree, Dictionary<string,string> variableNameMapping)
+    {
+      foreach(var kvp in variableNameMapping) this.variableNameMapping.Add(kvp.Key,kvp.Value);
       var stringBuilder = new StringBuilder();
-      if (dataset != null) CalculateVariableMapping(symbolicExpressionTree, dataset);
-
+      
       stringBuilder.Append("=");
       stringBuilder.Append(FormatRecursively(symbolicExpressionTree.Root));
 
-      foreach (var variable in variableNameMapping) {
+      foreach (var variable in this.variableNameMapping) {
         stringBuilder.AppendLine();
         stringBuilder.Append(variable.Key + " = " + variable.Value);
@@ -85,14 +93,14 @@
     }
 
-    private void CalculateVariableMapping(ISymbolicExpressionTree tree, IDataset dataset) {
-      int columnIndex = 0;
+    private Dictionary<string,string> CalculateVariableMapping(ISymbolicExpressionTree tree, IDataset dataset) {
+      var mapping = new Dictionary<string,string>();
       int inputIndex = 0;
-      var usedVariables = tree.IterateNodesPrefix().OfType<VariableTreeNode>().Select(v => v.VariableName).Distinct();
+      var usedVariables = tree.IterateNodesPrefix().OfType<IVariableTreeNode>().Select(v => v.VariableName).Distinct().ToArray();
       foreach (var variable in dataset.VariableNames) {
-        columnIndex++;
         if (!usedVariables.Contains(variable)) continue;
         inputIndex++;
-        variableNameMapping[variable] = GetExcelColumnName(inputIndex);
-      }
+        mapping[variable] = GetExcelColumnName(inputIndex);
+      }
+      return mapping;
     }
 
@@ -190,5 +198,25 @@
         stringBuilder.Append(variableTreeNode.Weight.ToString(CultureInfo.InvariantCulture));
         stringBuilder.Append("*");
-        stringBuilder.Append(GetColumnToVariableName(variableTreeNode.VariableName));// + LagToString(currentLag));
+        stringBuilder.Append(GetColumnToVariableName(variableTreeNode.VariableName));
+      } else if (symbol is BinaryFactorVariable) {
+        var binFactorNode = node as BinaryFactorVariableTreeNode;
+        stringBuilder.AppendFormat("IF({0}=\"{1}\", {2}, 0)",
+          GetColumnToVariableName(binFactorNode.VariableName),
+          binFactorNode.VariableValue,
+          binFactorNode.Weight.ToString(CultureInfo.InvariantCulture)
+          );
+      } else if (symbol is FactorVariable) {
+        var factorNode = node as FactorVariableTreeNode;
+        var values = factorNode.Symbol.GetVariableValues(factorNode.VariableName).ToArray();
+        var w = factorNode.Weights;
+        // create nested if
+        for (int i = 0; i < values.Length; i++) {
+          stringBuilder.AppendFormat("IF({0}=\"{1}\", {2}, ",
+            GetColumnToVariableName(factorNode.VariableName),
+            values[i],
+            w[i].ToString(CultureInfo.InvariantCulture));
+        }
+        stringBuilder.Append("\"\""); // return empty string on unknown value
+        stringBuilder.Append(')', values.Length); // add closing parenthesis
       } else if (symbol is Power) {
         stringBuilder.Append("POWER(");
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs	(revision 15131)
@@ -33,5 +33,6 @@
   [StorableClass]
   public sealed class SymbolicDataAnalysisExpressionLatexFormatter : NamedItem, ISymbolicExpressionTreeStringFormatter {
-    private readonly List<double> constants;
+    private readonly List<KeyValuePair<string, double>> constants;
+    private int constIndex;
     private int targetCount;
     private int currentLag;
@@ -43,5 +44,8 @@
     private SymbolicDataAnalysisExpressionLatexFormatter(SymbolicDataAnalysisExpressionLatexFormatter original, Cloner cloner)
       : base(original, cloner) {
-      constants = new List<double>(original.constants);
+      constants = new List<KeyValuePair<string, double>>(original.constants);
+      constIndex = original.constIndex;
+      currentLag = original.currentLag;
+      targetCount = original.targetCount;
     }
     public SymbolicDataAnalysisExpressionLatexFormatter()
@@ -49,5 +53,5 @@
       Name = ItemName;
       Description = ItemDescription;
-      constants = new List<double>();
+      constants = new List<KeyValuePair<string, double>>();
     }
 
@@ -63,4 +67,5 @@
         StringBuilder strBuilder = new StringBuilder();
         constants.Clear();
+        constIndex = 0;
         this.targetVariable = targetVariable;
         containsTimeSeriesSymbol = symbolicExpressionTree.IterateNodesBreadth().Any(n => IsTimeSeriesSymbol(n.Symbol));
@@ -84,5 +89,5 @@
       }
       int i = 1;
-      foreach (SymbolicExpressionTreeNode subTree in node.Subtrees.Skip(1)) {
+      foreach (var subTree in node.Subtrees.Skip(1)) {
         FormatSep(node, strBuilder, i);
         // format the whole subtree
@@ -175,16 +180,41 @@
         strBuilder.Append(@" \operatorname{if}  \left( ");
       } else if (node.Symbol is Constant) {
+        var constName = "c_{" + constIndex + "}";
+        constIndex++;
         var constNode = node as ConstantTreeNode;
         if (constNode.Value.IsAlmost(1.0)) {
           strBuilder.Append("1 ");
         } else {
-          strBuilder.Append("c_{" + constants.Count + "} ");
-          constants.Add(constNode.Value);
-        }
+          strBuilder.Append(constName);
+          constants.Add(new KeyValuePair<string, double>(constName, constNode.Value));
+        }
+
+      } else if (node.Symbol is FactorVariable) {
+        var factorNode = node as FactorVariableTreeNode;
+        var constName = "c_{" + constIndex + "}";
+        strBuilder.Append(constName + " ");
+        foreach (var e in factorNode.Symbol.GetVariableValues(factorNode.VariableName)
+          .Zip(factorNode.Weights, Tuple.Create)) {
+          constants.Add(new KeyValuePair<string, double>("c_{" + constIndex + ", " + EscapeLatexString(factorNode.VariableName) + "=" + EscapeLatexString(e.Item1) + "}", e.Item2));
+        }
+        constIndex++;
+      } else if (node.Symbol is BinaryFactorVariable) {
+        var binFactorNode = node as BinaryFactorVariableTreeNode;
+        if (!binFactorNode.Weight.IsAlmost((1.0))) {
+          var constName = "c_{" + constIndex + "}";
+          strBuilder.Append(constName + "  \\cdot");
+          constants.Add(new KeyValuePair<string, double>(constName, binFactorNode.Weight));
+          constIndex++;
+        }
+        strBuilder.Append("(" + EscapeLatexString(binFactorNode.VariableName));
+        strBuilder.Append(LagToString(currentLag));
+        strBuilder.Append(" = " + EscapeLatexString(binFactorNode.VariableValue) + " )");
       } else if (node.Symbol is LaggedVariable) {
         var laggedVarNode = node as LaggedVariableTreeNode;
         if (!laggedVarNode.Weight.IsAlmost(1.0)) {
-          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
-          constants.Add(laggedVarNode.Weight);
+          var constName = "c_{" + constIndex + "}";
+          strBuilder.Append(constName + "  \\cdot");
+          constants.Add(new KeyValuePair<string, double>(constName, laggedVarNode.Weight));
+          constIndex++;
         }
         strBuilder.Append(EscapeLatexString(laggedVarNode.VariableName));
@@ -194,6 +224,8 @@
         var varNode = node as VariableTreeNode;
         if (!varNode.Weight.IsAlmost((1.0))) {
-          strBuilder.Append("c_{" + constants.Count + "} \\cdot ");
-          constants.Add(varNode.Weight);
+          var constName = "c_{" + constIndex + "}";
+          strBuilder.Append(constName + "  \\cdot");
+          constants.Add(new KeyValuePair<string, double>(constName, varNode.Weight));
+          constIndex++;
         }
         strBuilder.Append(EscapeLatexString(varNode.VariableName));
@@ -229,8 +261,12 @@
       } else if (node.Symbol is VariableCondition) {
         var conditionTreeNode = node as VariableConditionTreeNode;
-        string p = @"1 /  1 + \exp  - c_{" + constants.Count + "} ";
-        constants.Add(conditionTreeNode.Slope);
-        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"}   ";
-        constants.Add(conditionTreeNode.Threshold);
+        var constName = "c_{" + constants.Count + "}";
+        string p = @"1 /  1 + \exp  - " + constName + " ";
+        constants.Add(new KeyValuePair<string, double>(constName, conditionTreeNode.Slope));
+        constIndex++;
+        var const2Name = "c_{" + constants.Count + @"}";
+        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - " + const2Name + "   ";
+        constants.Add(new KeyValuePair<string, double>(const2Name, conditionTreeNode.Threshold));
+        constIndex++;
         strBuilder.Append(@" \left( " + p + @"\cdot ");
       } else {
@@ -323,8 +359,12 @@
       } else if (node.Symbol is VariableCondition) {
         var conditionTreeNode = node as VariableConditionTreeNode;
-        string p = @"1 / \left( 1 + \exp \left( - c_{" + constants.Count + "} ";
-        constants.Add(conditionTreeNode.Slope);
-        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - c_{" + constants.Count + @"} \right) \right) \right)   ";
-        constants.Add(conditionTreeNode.Threshold);
+        var const1Name = "c_{" + constants.Count + "}";
+        string p = @"1 / \left( 1 + \exp \left( - " + const1Name + " ";
+        constants.Add(new KeyValuePair<string, double>(const1Name, conditionTreeNode.Slope));
+        constIndex++;
+        var const2Name = "c_{" + constants.Count + "}";
+        p += @" \cdot " + EscapeLatexString(conditionTreeNode.VariableName) + LagToString(currentLag) + " - " + const2Name + " \right) \right) \right)   ";
+        constants.Add(new KeyValuePair<string, double>(const2Name, conditionTreeNode.Threshold));
+        constIndex++;
         strBuilder.Append(@" +  \left( 1 - " + p + @" \right) \cdot ");
       } else {
@@ -404,4 +444,6 @@
       } else if (node.Symbol is LaggedVariable) {
       } else if (node.Symbol is Variable) {
+      } else if (node.Symbol is FactorVariable) {
+      } else if (node.Symbol is BinaryFactorVariable) {
       } else if (node.Symbol is ProgramRootSymbol) {
         strBuilder
@@ -411,13 +453,11 @@
         // output all constant values
         if (constants.Count > 0) {
-          int i = 0;
           foreach (var constant in constants) {
             // replace "." with ".&" to align decimal points
-            var constStr = string.Format(System.Globalization.NumberFormatInfo.InvariantInfo, "{0:G5}", constant);
+            var constStr = string.Format(System.Globalization.NumberFormatInfo.InvariantInfo, "{0:G5}", constant.Value);
             if (!constStr.Contains(".")) constStr = constStr + ".0";
             constStr = constStr.Replace(".", "&.");  // fix problem in rendering of aligned expressions
-            strBuilder.Append("c_{" + i + "}& = & " + constStr);
+            strBuilder.Append(constant.Key + "& = & " + constStr);
             strBuilder.Append(@"\\");
-            i++;
           }
         }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionMATLABFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionMATLABFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionMATLABFormatter.cs	(revision 15131)
@@ -21,4 +21,5 @@
 
 using System.Globalization;
+using System.Linq;
 using System.Text;
 using HeuristicLab.Common;
@@ -77,4 +78,26 @@
       stringBuilder.AppendLine("  y = (f0 + 2*f1 - 2*f3 - f4) / 8;");
       stringBuilder.AppendLine("end");
+
+      var factorVariableNames =
+        symbolicExpressionTree.IterateNodesPostfix()
+          .OfType<FactorVariableTreeNode>()
+          .Select(n => n.VariableName)
+          .Distinct();
+
+      foreach (var factorVarName in factorVariableNames) {
+        var factorSymb = symbolicExpressionTree.IterateNodesPostfix()
+          .OfType<FactorVariableTreeNode>()
+          .First(n => n.VariableName == factorVarName)
+          .Symbol;
+        stringBuilder.AppendFormat("function y = switch_{0}(val, v)", factorVarName).AppendLine();
+        var values = factorSymb.GetVariableValues(factorVarName).ToArray();
+        stringBuilder.AppendLine("switch val");
+        for (int i = 0; i < values.Length; i++) {
+          stringBuilder.AppendFormat(CultureInfo.InvariantCulture, "  case \"{0}\" y = v({1})", values[i], i).AppendLine();
+        }
+        stringBuilder.AppendLine("end");
+        stringBuilder.AppendLine();
+      }
+
       return stringBuilder.ToString();
     }
@@ -296,4 +319,14 @@
         stringBuilder.Append("*");
         stringBuilder.Append(variableTreeNode.VariableName + LagToString(currentLag));
+      } else if (symbol is HeuristicLab.Problems.DataAnalysis.Symbolic.FactorVariable) {
+        var factorNode = node as FactorVariableTreeNode;
+        var weights = string.Join(" ", factorNode.Weights.Select(w => w.ToString("G17", CultureInfo.InvariantCulture)));
+        stringBuilder.AppendFormat("switch_{0}(\"{1}\",[{2}])",
+          factorNode.VariableName, factorNode.VariableName, weights)
+          .AppendLine();
+      } else if (symbol is HeuristicLab.Problems.DataAnalysis.Symbolic.BinaryFactorVariable) {
+        var factorNode = node as BinaryFactorVariableTreeNode;
+        stringBuilder.AppendFormat(CultureInfo.InvariantCulture,
+          "((strcmp({0},\"{1}\")==1) * {2:G17})", factorNode.VariableName, factorNode.VariableValue, factorNode.Weight);
       } else if (symbol is Power) {
         stringBuilder.Append("(");
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionMathematicaFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionMathematicaFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionMathematicaFormatter.cs	(revision 15131)
@@ -110,10 +110,25 @@
         }
       } else {
-        if (node is VariableTreeNode) {
+        // terminals
+        if (node.Symbol is Variable) {
           var varNode = node as VariableTreeNode;
           strBuilder.AppendFormat("Times[{0}, {1}]", varNode.VariableName, varNode.Weight.ToString("G17", CultureInfo.InvariantCulture));
-        } else if (node is ConstantTreeNode) {
+        } else if (node.Symbol is Constant) {
           var constNode = node as ConstantTreeNode;
           strBuilder.Append(constNode.Value.ToString("G17", CultureInfo.InvariantCulture));
+        } else if (node.Symbol is FactorVariable) {
+          var factorNode = node as FactorVariableTreeNode;
+          strBuilder.AppendFormat("Switch[{0},", factorNode.VariableName);
+          var varValues = factorNode.Symbol.GetVariableValues(factorNode.VariableName).ToArray();
+          var weights = varValues.Select(factorNode.GetValue).ToArray();
+
+          var weightStr = string.Join(", ",
+            varValues.Zip(weights, (s, d) => string.Format(CultureInfo.InvariantCulture, "\"{0}\", {1:G17}", s, d)));
+          strBuilder.Append(weightStr);
+          strBuilder.Append("]");
+        } else if (node.Symbol is BinaryFactorVariable) {
+          var factorNode = node as BinaryFactorVariableTreeNode;
+          strBuilder.AppendFormat(CultureInfo.InvariantCulture, "If[{0}==\"{1}\",{2:G17},0.0]",
+            factorNode.VariableName, factorNode.VariableValue, factorNode.Weight);
         } else {
           throw new NotSupportedException("Formatting of symbol: " + node.Symbol + " is not supported.");
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionSmalltalkFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionSmalltalkFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionSmalltalkFormatter.cs	(revision 15131)
@@ -115,5 +115,5 @@
         stringBuilder.Append("]");
       } else if (symbol is LaggedVariable) {
-        stringBuilder.Append("lagged variable not implemented");
+        stringBuilder.Append("lagged variables are not supported");
       } else if (symbol is LessThan) {
         stringBuilder.Append("(");
@@ -165,4 +165,6 @@
         stringBuilder.Append("*");
         stringBuilder.Append(variableTreeNode.VariableName);
+      } else if (symbol is BinaryFactorVariable || symbol is FactorVariable) {
+        stringBuilder.Append("factor variables are not supported");
       } else {
         stringBuilder.Append("(");
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/ArithmeticExpressionGrammar.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/ArithmeticExpressionGrammar.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/ArithmeticExpressionGrammar.cs	(revision 15131)
@@ -53,6 +53,8 @@
       constant.MaxValue = 20;
       var variableSymbol = new HeuristicLab.Problems.DataAnalysis.Symbolic.Variable();
+      var binFactorVariableSymbol = new BinaryFactorVariable();
+      var factorVariableSymbol = new FactorVariable();
 
-      var allSymbols = new List<Symbol>() { add, sub, mul, div, constant, variableSymbol };
+      var allSymbols = new List<Symbol>() { add, sub, mul, div, constant, variableSymbol, binFactorVariableSymbol, factorVariableSymbol};
       var functionSymbols = new List<Symbol>() { add, sub, mul, div };
 
@@ -65,4 +67,6 @@
       SetSubtreeCount(constant, 0, 0);
       SetSubtreeCount(variableSymbol, 0, 0);
+      SetSubtreeCount(binFactorVariableSymbol, 0, 0);
+      SetSubtreeCount(factorVariableSymbol, 0, 0);
 
       // allow each symbol as child of the start symbol
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/FullFunctionalExpressionGrammar.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/FullFunctionalExpressionGrammar.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/FullFunctionalExpressionGrammar.cs	(revision 15131)
@@ -115,4 +115,6 @@
       constant.MaxValue = 20;
       var variableSymbol = new HeuristicLab.Problems.DataAnalysis.Symbolic.Variable();
+      var binFactorVariable = new BinaryFactorVariable();
+      var factorVariable = new FactorVariable();
       var laggedVariable = new LaggedVariable();
       laggedVariable.InitialFrequency = 0.0;
@@ -123,5 +125,5 @@
       var allSymbols = new List<Symbol>() { add, sub, mul, div, mean, sin, cos, tan, log, square, pow, sqrt, root, exp,
         airyA, airyB, bessel, cosineIntegral, dawson, erf, expIntegralEi, fresnelCosineIntegral, fresnelSineIntegral, gamma, hypCosineIntegral, hypSineIntegral, norm, psi, sineIntegral,
-        @if, gt, lt, and, or, not,xor, timeLag, integral, derivative, constant, variableSymbol, laggedVariable,autoregressiveVariable, variableCondition };
+        @if, gt, lt, and, or, not,xor, timeLag, integral, derivative, constant, variableSymbol, binFactorVariable, factorVariable, laggedVariable,autoregressiveVariable, variableCondition };
       var unaryFunctionSymbols = new List<Symbol>() { square, sqrt, sin, cos, tan, log, exp, not, timeLag, integral, derivative,
         airyA, airyB, bessel, cosineIntegral, dawson, erf, expIntegralEi, fresnelCosineIntegral, fresnelSineIntegral, gamma, hypCosineIntegral, hypSineIntegral, norm, psi, sineIntegral
@@ -130,5 +132,5 @@
       var binaryFunctionSymbols = new List<Symbol>() { pow, root, gt, lt, variableCondition };
       var ternarySymbols = new List<Symbol>() { add, sub, mul, div, mean, and, or, xor };
-      var terminalSymbols = new List<Symbol>() { variableSymbol, constant, laggedVariable, autoregressiveVariable };
+      var terminalSymbols = new List<Symbol>() { variableSymbol, binFactorVariable, factorVariable, constant, laggedVariable, autoregressiveVariable };
 
       foreach (var symb in allSymbols)
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/TypeCoherentExpressionGrammar.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/TypeCoherentExpressionGrammar.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Grammars/TypeCoherentExpressionGrammar.cs	(revision 15131)
@@ -104,4 +104,6 @@
       constant.MaxValue = 20;
       var variableSymbol = new Variable();
+      var binFactorVariable = new BinaryFactorVariable();
+      var factorVariable = new FactorVariable();
       var laggedVariable = new LaggedVariable();
       var autoregressiveVariable = new AutoregressiveTargetVariable();
@@ -112,7 +114,7 @@
       var trigonometricSymbols = new GroupSymbol(TrigonometricFunctionsName, new List<ISymbol>() { sin, cos, tan });
       var exponentialAndLogarithmicSymbols = new GroupSymbol(ExponentialFunctionsName, new List<ISymbol> { exp, log });
-      var specialFunctions = new GroupSymbol(SpecialFunctionsName, new List<ISymbol> { airyA, airyB, bessel, cosineIntegral, dawson, erf, expIntegralEi, 
+      var specialFunctions = new GroupSymbol(SpecialFunctionsName, new List<ISymbol> { airyA, airyB, bessel, cosineIntegral, dawson, erf, expIntegralEi,
         fresnelCosineIntegral,fresnelSineIntegral,gamma,hypCosineIntegral,hypSineIntegral,norm, psi, sineIntegral});
-      var terminalSymbols = new GroupSymbol(TerminalsName, new List<ISymbol> { constant, variableSymbol });
+      var terminalSymbols = new GroupSymbol(TerminalsName, new List<ISymbol> { constant, variableSymbol, binFactorVariable, factorVariable });
       var realValuedSymbols = new GroupSymbol(RealValuedSymbolsName, new List<ISymbol>() { arithmeticSymbols, trigonometricSymbols, exponentialAndLogarithmicSymbols, specialFunctions, terminalSymbols });
 
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 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj	(revision 15131)
@@ -140,4 +140,6 @@
     <Compile Include="Importer\Token.cs" />
     <Compile Include="Interfaces\IModelBacktransformator.cs" />
+    <Compile Include="Interfaces\IVariableTreeNode.cs" />
+    <Compile Include="Interfaces\IVariableSymbol.cs" />
     <Compile Include="Interpreter\SymbolicDataAnalysisExpressionCompiledTreeInterpreter.cs" />
     <Compile Include="SymbolicDataAnalysisExpressionTreeSimplificationOperator.cs" />
@@ -198,4 +200,10 @@
     <Compile Include="Symbols\AiryB.cs" />
     <Compile Include="Symbols\Bessel.cs" />
+    <Compile Include="Symbols\BinaryFactorVariable.cs" />
+    <Compile Include="Symbols\BinaryFactorVariableTreeNode.cs" />
+    <Compile Include="Symbols\FactorVariableTreeNode.cs" />
+    <Compile Include="Symbols\FactorVariable.cs" />
+    <Compile Include="Symbols\VariableBase.cs" />
+    <Compile Include="Symbols\VariableTreeNodeBase.cs" />
     <Compile Include="Symbols\Xor.cs" />
     <Compile Include="Symbols\Erf.cs" />
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/InfixExpressionParser.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/InfixExpressionParser.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/InfixExpressionParser.cs	(revision 15131)
@@ -33,10 +33,26 @@
   /// Parses mathematical expressions in infix form. E.g. x1 * (3.0 * x2 + x3)
   /// Identifier format (functions or variables): '_' | letter { '_' | letter | digit }
-  /// Variables names can be set under quotes "" or '' because variable names might contain spaces. 
+  /// Variables names and variable values can be set under quotes "" or '' because variable names might contain spaces. 
+  ///   Variable = ident | " ident " | ' ident ' 
   /// It is also possible to use functions e.g. log("x1") or real-valued constants e.g. 3.1415 . 
   /// Variable names are case sensitive. Function names are not case sensitive.
+  /// 
+  /// 
+  /// S             = Expr EOF
+  /// Expr          = ['-' | '+'] Term { '+' Term | '-' Term }
+  /// Term          = Fact { '*' Fact | '/' Fact }
+  /// Fact          = '(' Expr ')' 
+  ///                 | 'LAG' '(' varId ',' ['+' | '-' ] number ')' 
+  ///                 | funcId '(' ArgList ')' 
+  ///                 | VarExpr | number
+  /// ArgList       = Expr { ',' Expr }
+  /// VarExpr       = varId OptFactorPart
+  /// OptFactorPart = [ ('=' varVal | '[' ['+' | '-' ] number {',' ['+' | '-' ] number } ']' ) ]
+  /// varId         =  ident | ' ident ' | " ident "
+  /// varVal        =  ident | ' ident ' | " ident "
+  /// ident         =  '_' | letter { '_' | letter | digit }
   /// </summary>
   public sealed class InfixExpressionParser {
-    private enum TokenType { Operator, Identifier, Number, LeftPar, RightPar, Comma, End, NA };
+    private enum TokenType { Operator, Identifier, Number, LeftPar, RightPar, LeftBracket, RightBracket, Comma, Eq, End, NA };
     private class Token {
       internal double doubleVal;
@@ -65,4 +81,6 @@
     private Constant constant = new Constant();
     private Variable variable = new Variable();
+    private BinaryFactorVariable binaryFactorVar = new BinaryFactorVariable();
+    private FactorVariable factorVar = new FactorVariable();
 
     private ProgramRootSymbol programRootSymbol = new ProgramRootSymbol();
@@ -150,4 +168,5 @@
             && str[pos] != '/'
             && str[pos] != ')'
+            && str[pos] != ']'
             && str[pos] != ',') {
             sb.Append(str[pos]);
@@ -214,4 +233,13 @@
           pos++;
           yield return new Token { TokenType = TokenType.RightPar, strVal = ")" };
+        } else if (str[pos] == '[') {
+          pos++;
+          yield return new Token { TokenType = TokenType.LeftBracket, strVal = "[" };
+        } else if (str[pos] == ']') {
+          pos++;
+          yield return new Token { TokenType = TokenType.RightBracket, strVal = "]" };
+        } else if (str[pos] == '=') {
+          pos++;
+          yield return new Token { TokenType = TokenType.Eq, strVal = "=" };
         } else if (str[pos] == ',') {
           pos++;
@@ -222,10 +250,5 @@
       }
     }
-
-    // S       = Expr EOF
-    // Expr    = ['-' | '+'] Term { '+' Term | '-' Term }
-    // Term    = Fact { '*' Fact | '/' Fact }
-    // Fact    = '(' Expr ')' | funcId '(' ArgList ')' | varId | number
-    // ArgList = Expr { ',' Expr }
+    /// S             = Expr EOF
     private ISymbolicExpressionTreeNode ParseS(Queue<Token> tokens) {
       var expr = ParseExpr(tokens);
@@ -237,4 +260,6 @@
       return expr;
     }
+
+    /// Expr          = ['-' | '+'] Term { '+' Term | '-' Term }
     private ISymbolicExpressionTreeNode ParseExpr(Queue<Token> tokens) {
       var next = tokens.Peek();
@@ -300,5 +325,5 @@
     }
 
-    // Term = Fact { '*' Fact | '/' Fact }
+    /// Term          = Fact { '*' Fact | '/' Fact }
     private ISymbolicExpressionTreeNode ParseTerm(Queue<Token> tokens) {
       var factors = new List<ISymbolicExpressionTreeNode>();
@@ -335,5 +360,14 @@
     }
 
-    // Fact = '(' Expr ')' | 'LAG' '(' varId ',' ['+' | '-'] number ')' | funcId '(' ArgList ')' | varId | number
+    /// Fact          = '(' Expr ')' 
+    ///                 | 'LAG' '(' varId ',' ['+' | '-' ] number ')' 
+    ///                 | funcId '(' ArgList ')' 
+    ///                 | VarExpr | number
+    /// ArgList       = Expr { ',' Expr }
+    /// VarExpr       = varId OptFactorPart
+    /// OptFactorPart = [ ('=' varVal | '[' ['+' | '-' ] number {',' ['+' | '-' ] number } ']' ) ]
+    /// varId         =  ident | ' ident ' | " ident "
+    /// varVal        =  ident | ' ident ' | " ident "
+    /// ident         =  '_' | letter { '_' | letter | digit }
     private ISymbolicExpressionTreeNode ParseFact(Queue<Token> tokens) {
       var next = tokens.Peek();
@@ -348,5 +382,5 @@
         var idTok = tokens.Dequeue();
         if (tokens.Peek().TokenType == TokenType.LeftPar) {
-          // function identifier
+          // function identifier or LAG
           var funcId = idTok.strVal.ToUpperInvariant();
 
@@ -394,8 +428,56 @@
         } else {
           // variable
-          var varNode = (VariableTreeNode)variable.CreateTreeNode();
-          varNode.Weight = 1.0;
-          varNode.VariableName = idTok.strVal;
-          return varNode;
+          if (tokens.Peek().TokenType == TokenType.Eq) {
+            // binary factor
+            tokens.Dequeue(); // skip Eq
+            var valTok = tokens.Dequeue();
+            if (valTok.TokenType != TokenType.Identifier) throw new ArgumentException("expected identifier");
+            var binFactorNode = (BinaryFactorVariableTreeNode)binaryFactorVar.CreateTreeNode();
+            binFactorNode.Weight = 1.0;
+            binFactorNode.VariableName = idTok.strVal;
+            binFactorNode.VariableValue = valTok.strVal;
+            return binFactorNode;
+          } else if (tokens.Peek().TokenType == TokenType.LeftBracket) {
+            // factor variable
+            var factorVariableNode = (FactorVariableTreeNode)factorVar.CreateTreeNode();
+            factorVariableNode.VariableName = idTok.strVal;
+
+            tokens.Dequeue(); // skip [
+            var weights = new List<double>();
+            // at least one weight is necessary
+            var sign = 1.0;
+            if (tokens.Peek().TokenType == TokenType.Operator) {
+              var opToken = tokens.Dequeue();
+              if (opToken.strVal == "+") sign = 1.0;
+              else if (opToken.strVal == "-") sign = -1.0;
+              else throw new ArgumentException();
+            }
+            if (tokens.Peek().TokenType != TokenType.Number) throw new ArgumentException("number expected");
+            var weightTok = tokens.Dequeue();
+            weights.Add(sign * weightTok.doubleVal);
+            while (tokens.Peek().TokenType == TokenType.Comma) {
+              // skip comma
+              tokens.Dequeue();
+              if (tokens.Peek().TokenType == TokenType.Operator) {
+                var opToken = tokens.Dequeue();
+                if (opToken.strVal == "+") sign = 1.0;
+                else if (opToken.strVal == "-") sign = -1.0;
+                else throw new ArgumentException();
+              }
+              weightTok = tokens.Dequeue();
+              if (weightTok.TokenType != TokenType.Number) throw new ArgumentException("number expected");
+              weights.Add(sign * weightTok.doubleVal);
+            }
+            var rightBracketToken = tokens.Dequeue();
+            if (rightBracketToken.TokenType != TokenType.RightBracket) throw new ArgumentException("closing bracket ] expected");
+            factorVariableNode.Weights = weights.ToArray();
+            return factorVariableNode;
+          } else {
+            // variable
+            var varNode = (VariableTreeNode)variable.CreateTreeNode();
+            varNode.Weight = 1.0;
+            varNode.VariableName = idTok.strVal;
+            return varNode;
+          }
         }
       } else if (next.TokenType == TokenType.Number) {
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/SymbolicExpressionImporter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/SymbolicExpressionImporter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Importer/SymbolicExpressionImporter.cs	(revision 15131)
@@ -35,5 +35,5 @@
     private const string INVOKESTART = "CALL";
     private const string TIMELAGSTART = "LAG";
-    private Dictionary<string, Symbol> knownSymbols = new Dictionary<string, Symbol>() 
+    private Dictionary<string, Symbol> knownSymbols = new Dictionary<string, Symbol>()
       {
         {"+", new Addition()},
@@ -45,4 +45,6 @@
         {"POW", new Power()},
         {"ROOT", new Root()},
+        {"SQR", new Square()},
+        {"SQRT", new SquareRoot()},
         {"SIN",new Sine()},
         {"COS", new Cosine()},
@@ -74,4 +76,6 @@
         {"PROG", new ProgramRootSymbol()},
         {"MAIN", new StartSymbol()},
+        {"FACTOR", new FactorVariable() },
+        {"BINFACTOR", new BinaryFactorVariable()}
       };
 
@@ -82,4 +86,6 @@
     TimeLag timeLag = new TimeLag();
     Integral integral = new Integral();
+    FactorVariable factorVar = new FactorVariable();
+    BinaryFactorVariable binFactorVar = new BinaryFactorVariable();
 
     ProgramRootSymbol programRootSymbol = new ProgramRootSymbol();
@@ -136,4 +142,8 @@
             tree.AddSubtree(ParseSexp(tokens));
           }
+        } else if (tokens.Peek().StringValue.StartsWith("FACTOR")) {
+          tree = ParseFactor(tokens);
+        } else if (tokens.Peek().StringValue.StartsWith("BINFACTOR")) {
+          tree = ParseBinaryFactor(tokens);
         } else {
           Token curToken = tokens.Dequeue();
@@ -201,4 +211,48 @@
     }
 
+    private ISymbolicExpressionTreeNode ParseFactor(Queue<Token> tokens) {
+      Token tok = tokens.Dequeue();
+      Debug.Assert(tok.StringValue == "FACTOR");
+      FactorVariableTreeNode t = (FactorVariableTreeNode)(new FactorVariable()).CreateTreeNode(); // create a new symbol each time on purpose
+      var varNameTok = tokens.Dequeue();
+      Debug.Assert(tok.Symbol == TokenSymbol.SYMB);
+      t.VariableName = varNameTok.StringValue;
+
+      var weights = new List<double>();
+      while (tokens.Peek().Symbol == TokenSymbol.NUMBER) {
+        weights.Add(tokens.Dequeue().DoubleValue);
+      }
+
+      t.Weights = weights.ToArray();
+
+      // create a set of (virtual) values to match the number of weights
+      t.Symbol.VariableNames = new string[] { t.VariableName };
+      t.Symbol.VariableValues = new[]
+      { new KeyValuePair<string, Dictionary<string,int>>(
+        t.VariableName,
+        weights.Select((_, i) => Tuple.Create(_,i)).ToDictionary(tup=>"X" + tup.Item2, tup=>tup.Item2)) };
+      return t;
+    }
+
+    private ISymbolicExpressionTreeNode ParseBinaryFactor(Queue<Token> tokens) {
+      Token tok = tokens.Dequeue();
+      Debug.Assert(tok.StringValue == "BINFACTOR");
+      var t = (BinaryFactorVariableTreeNode)binFactorVar.CreateTreeNode();
+      var varNameTok = tokens.Dequeue();
+      Debug.Assert(varNameTok.Symbol == TokenSymbol.SYMB);
+      t.VariableName = varNameTok.StringValue;
+
+      var varValTok = tokens.Dequeue();
+      Debug.Assert(varValTok.Symbol == TokenSymbol.SYMB);
+      t.VariableValue = varValTok.StringValue;
+
+      var weightTok = tokens.Dequeue();
+      Debug.Assert(weightTok.Symbol == TokenSymbol.NUMBER);
+      t.Weight = weightTok.DoubleValue;
+
+      return t;
+    }
+
+
     private ISymbolicExpressionTreeNode ParseLaggedVariable(Queue<Token> tokens) {
       Token varTok = tokens.Dequeue();
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/ISymbolicDataAnalysisImpactValuesCalculator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/ISymbolicDataAnalysisImpactValuesCalculator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/ISymbolicDataAnalysisImpactValuesCalculator.cs	(revision 15131)
@@ -5,6 +5,4 @@
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
   public interface ISymbolicDataAnalysisSolutionImpactValuesCalculator : IItem {
-    double CalculateReplacementValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows);
-    double CalculateImpactValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows, double qualityForImpactsCalculation = double.NaN);
     void CalculateImpactAndReplacementValues(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData,
       IEnumerable<int> rows, out double impactValue, out double replacementValue, out double newQualityForImpactsCalculation, double qualityForImpactsCalculation = double.NaN);
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IVariableSymbol.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IVariableSymbol.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IVariableSymbol.cs	(revision 15131)
@@ -0,0 +1,30 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Encodings.SymbolicExpressionTreeEncoding;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+
+  /// <summary>
+  /// Any symbol that references a variable (includes terminal as well as non-terminal symbols)
+  /// </summary>
+  public interface IVariableSymbol : ISymbol {
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IVariableTreeNode.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IVariableTreeNode.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IVariableTreeNode.cs	(revision 15131)
@@ -0,0 +1,30 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Encodings.SymbolicExpressionTreeEncoding;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  /// <summary>
+  /// Any tree node that references a variable 
+  /// </summary>
+  public interface IVariableTreeNode : ISymbolicExpressionTreeNode {
+    string VariableName { get; }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/OpCodes.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/OpCodes.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/OpCodes.cs	(revision 15131)
@@ -83,4 +83,7 @@
     public const byte Erf = 43;
     public const byte Bessel = 44;
+    public const byte FactorVariable = 46;
+    public const byte BinaryFactorVariable = 47;
+
 
     private static Dictionary<Type, byte> symbolToOpcode = new Dictionary<Type, byte>() {
@@ -130,5 +133,7 @@
       { typeof(Norm), OpCodes.Norm},
       { typeof(Erf), OpCodes.Erf},
-      { typeof(Bessel), OpCodes.Bessel}   
+      { typeof(Bessel), OpCodes.Bessel},
+      { typeof(FactorVariable), OpCodes.FactorVariable },
+      { typeof(BinaryFactorVariable), OpCodes.BinaryFactorVariable }
     };
 
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeILEmittingInterpreter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeILEmittingInterpreter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeILEmittingInterpreter.cs	(revision 15131)
@@ -66,4 +66,5 @@
     private static MethodInfo erf = thisType.GetMethod("Erf", new Type[] { typeof(double) });
     private static MethodInfo bessel = thisType.GetMethod("Bessel", new Type[] { typeof(double) });
+    private static MethodInfo string_eq = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
     #endregion
 
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeInterpreter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeInterpreter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeInterpreter.cs	(revision 15131)
@@ -22,4 +22,5 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -143,4 +144,10 @@
           var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
           instr.data = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
+        } else if (instr.opCode == OpCodes.FactorVariable) {
+          var factorTreeNode = instr.dynamicNode as FactorVariableTreeNode;
+          instr.data = dataset.GetReadOnlyStringValues(factorTreeNode.VariableName);
+        } else if (instr.opCode == OpCodes.BinaryFactorVariable) {
+          var factorTreeNode = instr.dynamicNode as BinaryFactorVariableTreeNode;
+          instr.data = dataset.GetReadOnlyStringValues(factorTreeNode.VariableName);
         } else if (instr.opCode == OpCodes.LagVariable) {
           var laggedVariableTreeNode = (LaggedVariableTreeNode)instr.dynamicNode;
@@ -455,4 +462,14 @@
             return ((IList<double>)currentInstr.data)[row] * variableTreeNode.Weight;
           }
+        case OpCodes.BinaryFactorVariable: {
+            if (row < 0 || row >= dataset.Rows) return double.NaN;
+            var factorVarTreeNode = currentInstr.dynamicNode as BinaryFactorVariableTreeNode;
+            return ((IList<string>)currentInstr.data)[row] == factorVarTreeNode.VariableValue ? factorVarTreeNode.Weight : 0;
+          }
+        case OpCodes.FactorVariable: {
+            if (row < 0 || row >= dataset.Rows) return double.NaN;
+            var factorVarTreeNode = currentInstr.dynamicNode as FactorVariableTreeNode;
+            return factorVarTreeNode.GetValue(((IList<string>)currentInstr.data)[row]);
+          }
         case OpCodes.LagVariable: {
             var laggedVariableTreeNode = (LaggedVariableTreeNode)currentInstr.dynamicNode;
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeLinearInterpreter.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeLinearInterpreter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/SymbolicDataAnalysisExpressionTreeLinearInterpreter.cs	(revision 15131)
@@ -149,4 +149,16 @@
             var variableTreeNode = (VariableTreeNode)instr.dynamicNode;
             instr.value = ((IList<double>)instr.data)[row] * variableTreeNode.Weight;
+          }
+        } else if (instr.opCode == OpCodes.BinaryFactorVariable) {
+          if (row < 0 || row >= dataset.Rows) instr.value = double.NaN;
+          else {
+            var factorTreeNode = instr.dynamicNode as BinaryFactorVariableTreeNode;
+            instr.value = ((IList<string>)instr.data)[row] == factorTreeNode.VariableValue ? factorTreeNode.Weight : 0;
+          }
+        } else if (instr.opCode == OpCodes.FactorVariable) {
+          if (row < 0 || row >= dataset.Rows) instr.value = double.NaN;
+          else {
+            var factorTreeNode = instr.dynamicNode as FactorVariableTreeNode;
+            instr.value = factorTreeNode.GetValue(((IList<string>)instr.data)[row]);
           }
         } else if (instr.opCode == OpCodes.LagVariable) {
@@ -403,4 +415,14 @@
             }
             break;
+          case OpCodes.BinaryFactorVariable: {
+              var factorVariableTreeNode = instr.dynamicNode as BinaryFactorVariableTreeNode;
+              instr.data = dataset.GetReadOnlyStringValues(factorVariableTreeNode.VariableName);
+            }
+            break;
+          case OpCodes.FactorVariable: {
+              var factorVariableTreeNode = instr.dynamicNode as FactorVariableTreeNode;
+              instr.data = dataset.GetReadOnlyStringValues(factorVariableTreeNode.VariableName);
+            }
+            break;
           case OpCodes.LagVariable: {
               var laggedVariableTreeNode = (LaggedVariableTreeNode)instr.dynamicNode;
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplifier.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplifier.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplifier.cs	(revision 15131)
@@ -1,3 +1,4 @@
 ﻿#region License Information
+
 /* HeuristicLab
  * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
@@ -18,10 +19,13 @@
  * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
  */
+
 #endregion
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using HeuristicLab.Common;
+using HeuristicLab.Core;
 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
 
@@ -32,5 +36,4 @@
   public class SymbolicDataAnalysisExpressionTreeSimplifier {
     private Addition addSymbol = new Addition();
-    private Subtraction subSymbol = new Subtraction();
     private Multiplication mulSymbol = new Multiplication();
     private Division divSymbol = new Division();
@@ -62,9 +65,16 @@
       ISymbolicExpressionTreeNode rootNode = (new ProgramRootSymbol()).CreateTreeNode();
       rootNode.AddSubtree(GetSimplifiedTree(macroExpandedTree));
+
+#if DEBUG
+      // check that each node is only referenced once
+      var nodes = rootNode.IterateNodesPrefix().ToArray();
+      foreach(var n in nodes) if(nodes.Count(ni => ni == n) > 1) throw new InvalidOperationException();
+#endif
       return new SymbolicExpressionTree(rootNode);
     }
 
     // the argumentTrees list contains already expanded trees used as arguments for invocations
-    private ISymbolicExpressionTreeNode MacroExpand(ISymbolicExpressionTreeNode root, ISymbolicExpressionTreeNode node, IList<ISymbolicExpressionTreeNode> argumentTrees) {
+    private ISymbolicExpressionTreeNode MacroExpand(ISymbolicExpressionTreeNode root, ISymbolicExpressionTreeNode node,
+      IList<ISymbolicExpressionTreeNode> argumentTrees) {
       List<ISymbolicExpressionTreeNode> subtrees = new List<ISymbolicExpressionTreeNode>(node.Subtrees);
       while (node.SubtreeCount > 0) node.RemoveSubtree(0);
@@ -98,6 +108,6 @@
     }
 
-
     #region symbol predicates
+
     // arithmetic
     private bool IsDivision(ISymbolicExpressionTreeNode node) {
@@ -120,50 +130,65 @@
       return node.Symbol is Average;
     }
+
     // exponential
     private bool IsLog(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Logarithm;
     }
+
     private bool IsExp(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Exponential;
     }
+
     private bool IsRoot(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Root;
     }
+
     private bool IsSquare(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Square;
     }
+
     private bool IsSquareRoot(ISymbolicExpressionTreeNode node) {
       return node.Symbol is SquareRoot;
     }
+
     private bool IsPower(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Power;
     }
+
     // trigonometric
     private bool IsSine(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Sine;
     }
+
     private bool IsCosine(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Cosine;
     }
+
     private bool IsTangent(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Tangent;
     }
+
     // boolean
     private bool IsIfThenElse(ISymbolicExpressionTreeNode node) {
       return node.Symbol is IfThenElse;
     }
+
     private bool IsAnd(ISymbolicExpressionTreeNode node) {
       return node.Symbol is And;
     }
+
     private bool IsOr(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Or;
     }
+
     private bool IsNot(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Not;
     }
+
     // comparison
     private bool IsGreaterThan(ISymbolicExpressionTreeNode node) {
       return node.Symbol is GreaterThan;
     }
+
     private bool IsLessThan(ISymbolicExpressionTreeNode node) {
       return node.Symbol is LessThan;
@@ -183,4 +208,16 @@
     }
 
+    private bool IsVariableBase(ISymbolicExpressionTreeNode node) {
+      return node is VariableTreeNodeBase;
+    }
+
+    private bool IsFactor(ISymbolicExpressionTreeNode node) {
+      return node is FactorVariableTreeNode;
+    }
+
+    private bool IsBinFactor(ISymbolicExpressionTreeNode node) {
+      return node is BinaryFactorVariableTreeNode;
+    }
+
     private bool IsConstant(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Constant;
@@ -191,4 +228,5 @@
       return node.Symbol is TimeLag;
     }
+
     private bool IsIntegral(ISymbolicExpressionTreeNode node) {
       return node.Symbol is Integral;
@@ -203,5 +241,5 @@
     /// <returns></returns>
     public ISymbolicExpressionTreeNode GetSimplifiedTree(ISymbolicExpressionTreeNode original) {
-      if (IsConstant(original) || IsVariable(original)) {
+      if (IsConstant(original) || IsVariableBase(original)) {
         return (ISymbolicExpressionTreeNode)original.Clone();
       } else if (IsAddition(original)) {
@@ -254,6 +292,6 @@
     }
 
-
     #region specific simplification routines
+
     private ISymbolicExpressionTreeNode SimplifyAny(ISymbolicExpressionTreeNode original) {
       // can't simplify this function but simplify all subtrees 
@@ -303,5 +341,6 @@
         var remaining = original.Subtrees.Skip(2);
         return
-          MakeProduct(GetSimplifiedTree(first), Invert(remaining.Aggregate(GetSimplifiedTree(second), (a, b) => MakeProduct(a, GetSimplifiedTree(b)))));
+          MakeProduct(GetSimplifiedTree(first),
+            Invert(remaining.Aggregate(GetSimplifiedTree(second), (a, b) => MakeProduct(a, GetSimplifiedTree(b)))));
       }
     }
@@ -344,4 +383,5 @@
       return MakeNot(GetSimplifiedTree(original.GetSubtree(0)));
     }
+
     private ISymbolicExpressionTreeNode SimplifyOr(ISymbolicExpressionTreeNode original) {
       return original.Subtrees
@@ -349,4 +389,5 @@
         .Aggregate(MakeOr);
     }
+
     private ISymbolicExpressionTreeNode SimplifyAnd(ISymbolicExpressionTreeNode original) {
       return original.Subtrees
@@ -354,28 +395,38 @@
         .Aggregate(MakeAnd);
     }
+
     private ISymbolicExpressionTreeNode SimplifyLessThan(ISymbolicExpressionTreeNode original) {
       return MakeLessThan(GetSimplifiedTree(original.GetSubtree(0)), GetSimplifiedTree(original.GetSubtree(1)));
     }
+
     private ISymbolicExpressionTreeNode SimplifyGreaterThan(ISymbolicExpressionTreeNode original) {
       return MakeGreaterThan(GetSimplifiedTree(original.GetSubtree(0)), GetSimplifiedTree(original.GetSubtree(1)));
     }
+
     private ISymbolicExpressionTreeNode SimplifyIfThenElse(ISymbolicExpressionTreeNode original) {
-      return MakeIfThenElse(GetSimplifiedTree(original.GetSubtree(0)), GetSimplifiedTree(original.GetSubtree(1)), GetSimplifiedTree(original.GetSubtree(2)));
-    }
+      return MakeIfThenElse(GetSimplifiedTree(original.GetSubtree(0)), GetSimplifiedTree(original.GetSubtree(1)),
+        GetSimplifiedTree(original.GetSubtree(2)));
+    }
+
     private ISymbolicExpressionTreeNode SimplifyTangent(ISymbolicExpressionTreeNode original) {
       return MakeTangent(GetSimplifiedTree(original.GetSubtree(0)));
     }
+
     private ISymbolicExpressionTreeNode SimplifyCosine(ISymbolicExpressionTreeNode original) {
       return MakeCosine(GetSimplifiedTree(original.GetSubtree(0)));
     }
+
     private ISymbolicExpressionTreeNode SimplifySine(ISymbolicExpressionTreeNode original) {
       return MakeSine(GetSimplifiedTree(original.GetSubtree(0)));
     }
+
     private ISymbolicExpressionTreeNode SimplifyExp(ISymbolicExpressionTreeNode original) {
       return MakeExp(GetSimplifiedTree(original.GetSubtree(0)));
     }
+
     private ISymbolicExpressionTreeNode SimplifySquare(ISymbolicExpressionTreeNode original) {
       return MakeSquare(GetSimplifiedTree(original.GetSubtree(0)));
     }
+
     private ISymbolicExpressionTreeNode SimplifySquareRoot(ISymbolicExpressionTreeNode original) {
       return MakeSquareRoot(GetSimplifiedTree(original.GetSubtree(0)));
@@ -385,4 +436,5 @@
       return MakeLog(GetSimplifiedTree(original.GetSubtree(0)));
     }
+
     private ISymbolicExpressionTreeNode SimplifyRoot(ISymbolicExpressionTreeNode original) {
       return MakeRoot(GetSimplifiedTree(original.GetSubtree(0)), GetSimplifiedTree(original.GetSubtree(1)));
@@ -392,4 +444,5 @@
       return MakePower(GetSimplifiedTree(original.GetSubtree(0)), GetSimplifiedTree(original.GetSubtree(1)));
     }
+
     private ISymbolicExpressionTreeNode SimplifyTimeLag(ISymbolicExpressionTreeNode original) {
       var laggedTreeNode = original as ILaggedTreeNode;
@@ -401,4 +454,5 @@
       }
     }
+
     private ISymbolicExpressionTreeNode SimplifyIntegral(ISymbolicExpressionTreeNode original) {
       var laggedTreeNode = original as ILaggedTreeNode;
@@ -414,4 +468,5 @@
 
     #region low level tree restructuring
+
     private ISymbolicExpressionTreeNode MakeTimeLag(ISymbolicExpressionTreeNode subtree, int lag) {
       if (lag == 0) return subtree;
@@ -444,5 +499,6 @@
       } else if (!IsBoolean(t)) {
         var gtNode = gtSymbol.CreateTreeNode();
-        gtNode.AddSubtree(t); gtNode.AddSubtree(MakeConstant(0.0));
+        gtNode.AddSubtree(t);
+        gtNode.AddSubtree(MakeConstant(0.0));
         var notNode = notSymbol.CreateTreeNode();
         notNode.AddSubtree(gtNode);
@@ -484,4 +540,5 @@
       }
     }
+
     private ISymbolicExpressionTreeNode MakeAnd(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
       if (IsConstant(a) && IsConstant(b)) {
@@ -513,5 +570,7 @@
       }
     }
-    private ISymbolicExpressionTreeNode MakeLessThan(ISymbolicExpressionTreeNode leftSide, ISymbolicExpressionTreeNode rightSide) {
+
+    private ISymbolicExpressionTreeNode MakeLessThan(ISymbolicExpressionTreeNode leftSide,
+      ISymbolicExpressionTreeNode rightSide) {
       if (IsConstant(leftSide) && IsConstant(rightSide)) {
         var lsConst = leftSide as ConstantTreeNode;
@@ -526,5 +585,7 @@
       }
     }
-    private ISymbolicExpressionTreeNode MakeGreaterThan(ISymbolicExpressionTreeNode leftSide, ISymbolicExpressionTreeNode rightSide) {
+
+    private ISymbolicExpressionTreeNode MakeGreaterThan(ISymbolicExpressionTreeNode leftSide,
+      ISymbolicExpressionTreeNode rightSide) {
       if (IsConstant(leftSide) && IsConstant(rightSide)) {
         var lsConst = leftSide as ConstantTreeNode;
@@ -539,5 +600,7 @@
       }
     }
-    private ISymbolicExpressionTreeNode MakeIfThenElse(ISymbolicExpressionTreeNode condition, ISymbolicExpressionTreeNode trueBranch, ISymbolicExpressionTreeNode falseBranch) {
+
+    private ISymbolicExpressionTreeNode MakeIfThenElse(ISymbolicExpressionTreeNode condition,
+      ISymbolicExpressionTreeNode trueBranch, ISymbolicExpressionTreeNode falseBranch) {
       if (IsConstant(condition)) {
         var constT = condition as ConstantTreeNode;
@@ -550,5 +613,6 @@
         } else {
           var gtNode = gtSymbol.CreateTreeNode();
-          gtNode.AddSubtree(condition); gtNode.AddSubtree(MakeConstant(0.0));
+          gtNode.AddSubtree(condition);
+          gtNode.AddSubtree(MakeConstant(0.0));
           ifNode.AddSubtree(gtNode);
         }
@@ -563,4 +627,10 @@
         var constT = node as ConstantTreeNode;
         return MakeConstant(Math.Sin(constT.Value));
+      } else if (IsFactor(node)) {
+        var factor = node as FactorVariableTreeNode;
+        return MakeFactor(factor.Symbol, factor.VariableName, factor.Weights.Select(Math.Sin));
+      } else if (IsBinFactor(node)) {
+        var binFactor = node as BinaryFactorVariableTreeNode;
+        return MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, Math.Sin(binFactor.Weight));
       } else {
         var sineNode = sineSymbol.CreateTreeNode();
@@ -569,8 +639,15 @@
       }
     }
+
     private ISymbolicExpressionTreeNode MakeTangent(ISymbolicExpressionTreeNode node) {
       if (IsConstant(node)) {
         var constT = node as ConstantTreeNode;
         return MakeConstant(Math.Tan(constT.Value));
+      } else if (IsFactor(node)) {
+        var factor = node as FactorVariableTreeNode;
+        return MakeFactor(factor.Symbol, factor.VariableName, factor.Weights.Select(Math.Tan));
+      } else if (IsBinFactor(node)) {
+        var binFactor = node as BinaryFactorVariableTreeNode;
+        return MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, Math.Tan(binFactor.Weight));
       } else {
         var tanNode = tanSymbol.CreateTreeNode();
@@ -579,8 +656,17 @@
       }
     }
+
     private ISymbolicExpressionTreeNode MakeCosine(ISymbolicExpressionTreeNode node) {
       if (IsConstant(node)) {
         var constT = node as ConstantTreeNode;
         return MakeConstant(Math.Cos(constT.Value));
+      } else if (IsFactor(node)) {
+        var factor = node as FactorVariableTreeNode;
+        return MakeFactor(factor.Symbol, factor.VariableName, factor.Weights.Select(Math.Cos));
+      } else if (IsBinFactor(node)) {
+        var binFactor = node as BinaryFactorVariableTreeNode;
+        // cos(0) = 1 see similar case for Exp(binfactor)
+        return MakeSum(MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, Math.Cos(binFactor.Weight) - 1),
+          MakeConstant(1.0));
       } else {
         var cosNode = cosineSymbol.CreateTreeNode();
@@ -589,8 +675,17 @@
       }
     }
+
     private ISymbolicExpressionTreeNode MakeExp(ISymbolicExpressionTreeNode node) {
       if (IsConstant(node)) {
         var constT = node as ConstantTreeNode;
         return MakeConstant(Math.Exp(constT.Value));
+      } else if (IsFactor(node)) {
+        var factNode = node as FactorVariableTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => Math.Exp(w)));
+      } else if (IsBinFactor(node)) {
+        // exp( binfactor w val=a) = if(val=a) exp(w) else exp(0) = binfactor( (exp(w) - 1) val a) + 1
+        var binFactor = node as BinaryFactorVariableTreeNode;
+        return
+          MakeSum(MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, Math.Exp(binFactor.Weight) - 1), MakeConstant(1.0));
       } else if (IsLog(node)) {
         return node.GetSubtree(0);
@@ -605,4 +700,21 @@
       }
     }
+    private ISymbolicExpressionTreeNode MakeLog(ISymbolicExpressionTreeNode node) {
+      if (IsConstant(node)) {
+        var constT = node as ConstantTreeNode;
+        return MakeConstant(Math.Log(constT.Value));
+      } else if (IsFactor(node)) {
+        var factNode = node as FactorVariableTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => Math.Log(w)));
+      } else if (IsExp(node)) {
+        return node.GetSubtree(0);
+      } else if (IsSquareRoot(node)) {
+        return MakeFraction(MakeLog(node.GetSubtree(0)), MakeConstant(2.0));
+      } else {
+        var logNode = logSymbol.CreateTreeNode();
+        logNode.AddSubtree(node);
+        return logNode;
+      }
+    }
 
     private ISymbolicExpressionTreeNode MakeSquare(ISymbolicExpressionTreeNode node) {
@@ -610,4 +722,10 @@
         var constT = node as ConstantTreeNode;
         return MakeConstant(constT.Value * constT.Value);
+      } else if (IsFactor(node)) {
+        var factNode = node as FactorVariableTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => w * w));
+      } else if (IsBinFactor(node)) {
+        var binFactor = node as BinaryFactorVariableTreeNode;
+        return MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, binFactor.Weight * binFactor.Weight);
       } else if (IsSquareRoot(node)) {
         return node.GetSubtree(0);
@@ -618,8 +736,15 @@
       }
     }
+
     private ISymbolicExpressionTreeNode MakeSquareRoot(ISymbolicExpressionTreeNode node) {
       if (IsConstant(node)) {
         var constT = node as ConstantTreeNode;
         return MakeConstant(Math.Sqrt(constT.Value));
+      } else if (IsFactor(node)) {
+        var factNode = node as FactorVariableTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => Math.Sqrt(w)));
+      } else if (IsBinFactor(node)) {
+        var binFactor = node as BinaryFactorVariableTreeNode;
+        return MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, Math.Sqrt(binFactor.Weight));
       } else if (IsSquare(node)) {
         return node.GetSubtree(0);
@@ -631,18 +756,4 @@
     }
 
-    private ISymbolicExpressionTreeNode MakeLog(ISymbolicExpressionTreeNode node) {
-      if (IsConstant(node)) {
-        var constT = node as ConstantTreeNode;
-        return MakeConstant(Math.Log(constT.Value));
-      } else if (IsExp(node)) {
-        return node.GetSubtree(0);
-      } else if (IsSquareRoot(node)) {
-        return MakeFraction(MakeLog(node.GetSubtree(0)), MakeConstant(2.0));
-      } else {
-        var logNode = logSymbol.CreateTreeNode();
-        logNode.AddSubtree(node);
-        return logNode;
-      }
-    }
     private ISymbolicExpressionTreeNode MakeRoot(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
       if (IsConstant(a) && IsConstant(b)) {
@@ -650,4 +761,25 @@
         var constB = b as ConstantTreeNode;
         return MakeConstant(Math.Pow(constA.Value, 1.0 / Math.Round(constB.Value)));
+      } else if (IsFactor(a) && IsConstant(b)) {
+        var factNode = a as FactorVariableTreeNode;
+        var constNode = b as ConstantTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName,
+          factNode.Weights.Select(w => Math.Pow(w, 1.0 / Math.Round(constNode.Value))));
+      } else if (IsBinFactor(a) && IsConstant(b)) {
+        var binFactor = a as BinaryFactorVariableTreeNode;
+        var constNode = b as ConstantTreeNode;
+        return MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, Math.Pow(binFactor.Weight, 1.0 / Math.Round(constNode.Value)));
+      } else if (IsConstant(a) && IsFactor(b)) {
+        var constNode = a as ConstantTreeNode;
+        var factNode = b as FactorVariableTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => Math.Pow(constNode.Value, 1.0 / Math.Round(w))));
+      } else if (IsConstant(a) && IsBinFactor(b)) {
+        var constNode = a as ConstantTreeNode;
+        var factNode = b as BinaryFactorVariableTreeNode;
+        return MakeBinFactor(factNode.Symbol, factNode.VariableName, factNode.VariableValue, Math.Pow(constNode.Value, 1.0 / Math.Round(factNode.Weight)));
+      } else if (IsFactor(a) && IsFactor(b) && AreSameTypeAndVariable(a, b)) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as FactorVariableTreeNode;
+        return MakeFactor(node0.Symbol, node0.VariableName, node0.Weights.Zip(node1.Weights, (u, v) => Math.Pow(u, 1.0 / Math.Round(v))));
       } else if (IsConstant(b)) {
         var constB = b as ConstantTreeNode;
@@ -677,4 +809,6 @@
       }
     }
+
+
     private ISymbolicExpressionTreeNode MakePower(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
       if (IsConstant(a) && IsConstant(b)) {
@@ -682,4 +816,24 @@
         var constB = b as ConstantTreeNode;
         return MakeConstant(Math.Pow(constA.Value, Math.Round(constB.Value)));
+      } else if (IsFactor(a) && IsConstant(b)) {
+        var factNode = a as FactorVariableTreeNode;
+        var constNode = b as ConstantTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => Math.Pow(w, Math.Round(constNode.Value))));
+      } else if (IsBinFactor(a) && IsConstant(b)) {
+        var binFactor = a as BinaryFactorVariableTreeNode;
+        var constNode = b as ConstantTreeNode;
+        return MakeBinFactor(binFactor.Symbol, binFactor.VariableName, binFactor.VariableValue, Math.Pow(binFactor.Weight, Math.Round(constNode.Value)));
+      } else if (IsConstant(a) && IsFactor(b)) {
+        var constNode = a as ConstantTreeNode;
+        var factNode = b as FactorVariableTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => Math.Pow(constNode.Value, Math.Round(w))));
+      } else if (IsConstant(a) && IsBinFactor(b)) {
+        var constNode = a as ConstantTreeNode;
+        var factNode = b as BinaryFactorVariableTreeNode;
+        return MakeBinFactor(factNode.Symbol, factNode.VariableName, factNode.VariableValue, Math.Pow(constNode.Value, Math.Round(factNode.Weight)));
+      } else if (IsFactor(a) && IsFactor(b) && AreSameTypeAndVariable(a, b)) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as FactorVariableTreeNode;
+        return MakeFactor(node0.Symbol, node0.VariableName, node0.Weights.Zip(node1.Weights, (u, v) => Math.Pow(u, Math.Round(v))));
       } else if (IsConstant(b)) {
         var constB = b as ConstantTreeNode;
@@ -716,13 +870,39 @@
         // fold constants
         return MakeConstant(((ConstantTreeNode)a).Value / ((ConstantTreeNode)b).Value);
-      } if (IsConstant(a) && !((ConstantTreeNode)a).Value.IsAlmost(1.0)) {
+      } else if ((IsConstant(a) && !((ConstantTreeNode)a).Value.IsAlmost(1.0))) {
         return MakeFraction(MakeConstant(1.0), MakeProduct(b, Invert(a)));
-      } else if (IsVariable(a) && IsConstant(b)) {
+      } else if (IsVariableBase(a) && IsConstant(b)) {
         // merge constant values into variable weights
         var constB = ((ConstantTreeNode)b).Value;
-        ((VariableTreeNode)a).Weight /= constB;
+        ((VariableTreeNodeBase)a).Weight /= constB;
         return a;
-      } else if (IsVariable(a) && IsVariable(b) && AreSameVariable(a, b)) {
-        // cancel variables
+      } else if (IsFactor(a) && IsConstant(b)) {
+        var factNode = a as FactorVariableTreeNode;
+        var constNode = b as ConstantTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select(w => w / constNode.Value));
+      } else if (IsBinFactor(a) && IsConstant(b)) {
+        var factNode = a as BinaryFactorVariableTreeNode;
+        var constNode = b as ConstantTreeNode;
+        return MakeBinFactor(factNode.Symbol, factNode.VariableName, factNode.VariableValue, factNode.Weight / constNode.Value);
+      } else if (IsFactor(a) && IsFactor(b) && AreSameTypeAndVariable(a, b)) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as FactorVariableTreeNode;
+        return MakeFactor(node0.Symbol, node0.VariableName, node0.Weights.Zip(node1.Weights, (u, v) => u / v));
+      } else if (IsFactor(a) && IsBinFactor(b) && ((IVariableTreeNode)a).VariableName == ((IVariableTreeNode)b).VariableName) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as BinaryFactorVariableTreeNode;
+        var varValues = node0.Symbol.GetVariableValues(node0.VariableName).ToArray();
+        var wi = Array.IndexOf(varValues, node1.VariableValue);
+        if (wi < 0) throw new ArgumentException();
+        var newWeighs = new double[varValues.Length];
+        node0.Weights.CopyTo(newWeighs, 0);
+        for (int i = 0; i < newWeighs.Length; i++)
+          if (wi == i) newWeighs[i] /= node1.Weight;
+          else newWeighs[i] /= 0.0;
+        return MakeFactor(node0.Symbol, node0.VariableName, newWeighs);
+      } else if (IsFactor(a)) {
+        return MakeFraction(MakeConstant(1.0), MakeProduct(b, Invert(a)));
+      } else if (IsVariableBase(a) && IsVariableBase(b) && AreSameTypeAndVariable(a, b) && !IsBinFactor(b)) {
+        // cancel variables (not allowed for bin factors because of division by zero)
         var aVar = a as VariableTreeNode;
         var bVar = b as VariableTreeNode;
@@ -731,6 +911,6 @@
         return a.Subtrees
           .Select(x => GetSimplifiedTree(x))
-         .Select(x => MakeFraction(x, b))
-         .Aggregate((c, d) => MakeSum(c, d));
+          .Select(x => MakeFraction(x, GetSimplifiedTree(b)))
+          .Aggregate((c, d) => MakeSum(c, d));
       } else if (IsMultiplication(a) && IsConstant(b)) {
         return MakeProduct(a, Invert(b));
@@ -767,4 +947,25 @@
         // x + 0 => x
         return a;
+      } else if (IsFactor(a) && IsConstant(b)) {
+        var factNode = a as FactorVariableTreeNode;
+        var constNode = b as ConstantTreeNode;
+        return MakeFactor(factNode.Symbol, factNode.VariableName, factNode.Weights.Select((w) => w + constNode.Value));
+      } else if (IsFactor(a) && IsFactor(b) && AreSameTypeAndVariable(a, b)) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as FactorVariableTreeNode;
+        return MakeFactor(node0.Symbol, node0.VariableName, node0.Weights.Zip(node1.Weights, (u, v) => u + v));
+      } else if (IsBinFactor(a) && IsFactor(b)) {
+        return MakeSum(b, a);
+      } else if (IsFactor(a) && IsBinFactor(b) &&
+        ((IVariableTreeNode)a).VariableName == ((IVariableTreeNode)b).VariableName) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as BinaryFactorVariableTreeNode;
+        var varValues = node0.Symbol.GetVariableValues(node0.VariableName).ToArray();
+        var wi = Array.IndexOf(varValues, node1.VariableValue);
+        if (wi < 0) throw new ArgumentException();
+        var newWeighs = new double[varValues.Length];
+        node0.Weights.CopyTo(newWeighs, 0);
+        newWeighs[wi] += node1.Weight;
+        return MakeFactor(node0.Symbol, node0.VariableName, newWeighs);
       } else if (IsAddition(a) && IsAddition(b)) {
         // merge additions
@@ -829,22 +1030,59 @@
 
     // makes sure variable symbols in sums are combined
-    // possible improvement: combine sums of products where the products only reference the same variable
     private void MergeVariablesInSum(ISymbolicExpressionTreeNode sum) {
       var subtrees = new List<ISymbolicExpressionTreeNode>(sum.Subtrees);
       while (sum.Subtrees.Any()) sum.RemoveSubtree(0);
-      var groupedVarNodes = from node in subtrees.OfType<VariableTreeNode>()
-                            let lag = (node is LaggedVariableTreeNode) ? ((LaggedVariableTreeNode)node).Lag : 0
-                            group node by node.VariableName + lag into g
+      var groupedVarNodes = from node in subtrees.OfType<IVariableTreeNode>()
+                            where node.SubtreeCount == 0
+                            group node by GroupId(node) into g
                             select g;
-      var unchangedSubtrees = subtrees.Where(t => !(t is VariableTreeNode));
+      var constant = (from node in subtrees.OfType<ConstantTreeNode>()
+                      select node.Value).DefaultIfEmpty(0.0).Sum();
+      var unchangedSubtrees = subtrees.Where(t => t.SubtreeCount > 0 || !(t is IVariableTreeNode) && !(t is ConstantTreeNode));
 
       foreach (var variableNodeGroup in groupedVarNodes) {
-        var weightSum = variableNodeGroup.Select(t => t.Weight).Sum();
-        var representative = variableNodeGroup.First();
-        representative.Weight = weightSum;
-        sum.AddSubtree(representative);
+        var firstNode = variableNodeGroup.First();
+        if (firstNode is VariableTreeNodeBase) {
+          var representative = firstNode as VariableTreeNodeBase;
+          var weightSum = variableNodeGroup.Cast<VariableTreeNodeBase>().Select(t => t.Weight).Sum();
+          representative.Weight = weightSum;
+          sum.AddSubtree(representative);
+        } else if (firstNode is FactorVariableTreeNode) {
+          var representative = firstNode as FactorVariableTreeNode;
+          foreach (var node in variableNodeGroup.Skip(1).Cast<FactorVariableTreeNode>()) {
+            for (int j = 0; j < representative.Weights.Length; j++) {
+              representative.Weights[j] += node.Weights[j];
+            }
+          }
+          for (int j = 0; j < representative.Weights.Length; j++) {
+            representative.Weights[j] += constant;
+          }
+          sum.AddSubtree(representative);
+        }
       }
       foreach (var unchangedSubtree in unchangedSubtrees)
         sum.AddSubtree(unchangedSubtree);
+      if (!constant.IsAlmost(0.0)) {
+        sum.AddSubtree(MakeConstant(constant));
+      }
+    }
+
+    // nodes referencing variables can be grouped if they have
+    private string GroupId(IVariableTreeNode node) {
+      var binaryFactorNode = node as BinaryFactorVariableTreeNode;
+      var factorNode = node as FactorVariableTreeNode;
+      var variableNode = node as VariableTreeNode;
+      var laggedVarNode = node as LaggedVariableTreeNode;
+      if (variableNode != null) {
+        return "var " + variableNode.VariableName;
+      } else if (binaryFactorNode != null) {
+        return "binfactor " + binaryFactorNode.VariableName + " " + binaryFactorNode.VariableValue;
+      } else if (factorNode != null) {
+        return "factor " + factorNode.VariableName;
+      } else if (laggedVarNode != null) {
+        return "lagged " + laggedVarNode.VariableName + " " + laggedVarNode.Lag;
+      } else {
+        throw new NotSupportedException();
+      }
     }
 
@@ -853,19 +1091,46 @@
       if (IsConstant(a) && IsConstant(b)) {
         // fold constants
-        ((ConstantTreeNode)a).Value *= ((ConstantTreeNode)b).Value;
-        return a;
+        return MakeConstant(((ConstantTreeNode)a).Value * ((ConstantTreeNode)b).Value);
       } else if (IsConstant(a)) {
         // a * $ => $ * a
         return MakeProduct(b, a);
+      } else if (IsFactor(a) && IsFactor(b) && AreSameTypeAndVariable(a, b)) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as FactorVariableTreeNode;
+        return MakeFactor(node0.Symbol, node0.VariableName, node0.Weights.Zip(node1.Weights, (u, v) => u * v));
+      } else if (IsBinFactor(a) && IsBinFactor(b) && AreSameTypeAndVariable(a, b)) {
+        var node0 = a as BinaryFactorVariableTreeNode;
+        var node1 = b as BinaryFactorVariableTreeNode;
+        return MakeBinFactor(node0.Symbol, node0.VariableName, node0.VariableValue, node0.Weight * node1.Weight);
+      } else if (IsFactor(a) && IsConstant(b)) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as ConstantTreeNode;
+        return MakeFactor(node0.Symbol, node0.VariableName, node0.Weights.Select(w => w * node1.Value));
+      } else if (IsBinFactor(a) && IsConstant(b)) {
+        var node0 = a as BinaryFactorVariableTreeNode;
+        var node1 = b as ConstantTreeNode;
+        return MakeBinFactor(node0.Symbol, node0.VariableName, node0.VariableValue, node0.Weight * node1.Value);
+      } else if (IsBinFactor(a) && IsFactor(b)) {
+        return MakeProduct(b, a);
+      } else if (IsFactor(a) && IsBinFactor(b) &&
+        ((IVariableTreeNode)a).VariableName == ((IVariableTreeNode)b).VariableName) {
+        var node0 = a as FactorVariableTreeNode;
+        var node1 = b as BinaryFactorVariableTreeNode;
+        var varValues = node0.Symbol.GetVariableValues(node0.VariableName).ToArray();
+        var wi = Array.IndexOf(varValues, node1.VariableValue);
+        if (wi < 0) throw new ArgumentException();
+        return MakeBinFactor(node1.Symbol, node1.VariableName, node1.VariableValue, node1.Weight * node0.Weights[wi]);
       } else if (IsConstant(b) && ((ConstantTreeNode)b).Value.IsAlmost(1.0)) {
         // $ * 1.0 => $
         return a;
-      } else if (IsConstant(b) && IsVariable(a)) {
+      } else if (IsConstant(b) && IsVariableBase(a)) {
         // multiply constants into variables weights
-        ((VariableTreeNode)a).Weight *= ((ConstantTreeNode)b).Value;
+        ((VariableTreeNodeBase)a).Weight *= ((ConstantTreeNode)b).Value;
         return a;
-      } else if (IsConstant(b) && IsAddition(a)) {
+      } else if (IsConstant(b) && IsAddition(a) ||
+          IsFactor(b) && IsAddition(a) ||
+          IsBinFactor(b) && IsAddition(a)) {
         // multiply constants into additions
-        return a.Subtrees.Select(x => MakeProduct(x, b)).Aggregate((c, d) => MakeSum(c, d));
+        return a.Subtrees.Select(x => MakeProduct(GetSimplifiedTree(x), GetSimplifiedTree(b))).Aggregate((c, d) => MakeSum(c, d));
       } else if (IsDivision(a) && IsDivision(b)) {
         // (a1 / a2) * (b1 / b2) => (a1 * b1) / (a2 * b2)
@@ -888,5 +1153,5 @@
       } else if (IsMultiplication(a)) {
         // a is already an multiplication => append b
-        a.AddSubtree(b);
+        a.AddSubtree(GetSimplifiedTree(b));
         MergeVariablesAndConstantsInProduct(a);
         return a;
@@ -899,8 +1164,9 @@
       }
     }
+
     #endregion
 
-
     #region helper functions
+
     private bool ContainsVariableCondition(ISymbolicExpressionTreeNode node) {
       if (node.Symbol is VariableCondition) return true;
@@ -932,17 +1198,6 @@
     }
 
-    private bool AreSameVariable(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
-      var aLaggedVar = a as LaggedVariableTreeNode;
-      var bLaggedVar = b as LaggedVariableTreeNode;
-      if (aLaggedVar != null && bLaggedVar != null) {
-        return aLaggedVar.VariableName == bLaggedVar.VariableName &&
-          aLaggedVar.Lag == bLaggedVar.Lag;
-      }
-      var aVar = a as VariableTreeNode;
-      var bVar = b as VariableTreeNode;
-      if (aVar != null && bVar != null) {
-        return aVar.VariableName == bVar.VariableName;
-      }
-      return false;
+    private bool AreSameTypeAndVariable(ISymbolicExpressionTreeNode a, ISymbolicExpressionTreeNode b) {
+      return GroupId((IVariableTreeNode)a) == GroupId((IVariableTreeNode)b);
     }
 
@@ -951,31 +1206,46 @@
       var subtrees = new List<ISymbolicExpressionTreeNode>(prod.Subtrees);
       while (prod.Subtrees.Any()) prod.RemoveSubtree(0);
-      var groupedVarNodes = from node in subtrees.OfType<VariableTreeNode>()
-                            let lag = (node is LaggedVariableTreeNode) ? ((LaggedVariableTreeNode)node).Lag : 0
-                            group node by node.VariableName + lag into g
+      var groupedVarNodes = from node in subtrees.OfType<IVariableTreeNode>()
+                            where node.SubtreeCount == 0
+                            group node by GroupId(node) into g
                             orderby g.Count()
                             select g;
-      var constantProduct = (from node in subtrees.OfType<VariableTreeNode>()
+      var constantProduct = (from node in subtrees.OfType<VariableTreeNodeBase>()
                              select node.Weight)
-                            .Concat(from node in subtrees.OfType<ConstantTreeNode>()
-                                    select node.Value)
-                            .DefaultIfEmpty(1.0)
-                            .Aggregate((c1, c2) => c1 * c2);
+        .Concat(from node in subtrees.OfType<ConstantTreeNode>()
+                select node.Value)
+        .DefaultIfEmpty(1.0)
+        .Aggregate((c1, c2) => c1 * c2);
 
       var unchangedSubtrees = from tree in subtrees
-                              where !(tree is VariableTreeNode)
-                              where !(tree is ConstantTreeNode)
+                              where tree.SubtreeCount > 0 || !(tree is IVariableTreeNode) && !(tree is ConstantTreeNode)
                               select tree;
 
       foreach (var variableNodeGroup in groupedVarNodes) {
-        var representative = variableNodeGroup.First();
-        representative.Weight = 1.0;
-        if (variableNodeGroup.Count() > 1) {
-          var poly = mulSymbol.CreateTreeNode();
-          for (int p = 0; p < variableNodeGroup.Count(); p++) {
-            poly.AddSubtree((ISymbolicExpressionTreeNode)representative.Clone());
+        var firstNode = variableNodeGroup.First();
+        if (firstNode is VariableTreeNodeBase) {
+          var representative = (VariableTreeNodeBase)firstNode;
+          representative.Weight = 1.0;
+          if (variableNodeGroup.Count() > 1) {
+            var poly = mulSymbol.CreateTreeNode();
+            for (int p = 0; p < variableNodeGroup.Count(); p++) {
+              poly.AddSubtree((ISymbolicExpressionTreeNode)representative.Clone());
+            }
+            prod.AddSubtree(poly);
+          } else {
+            prod.AddSubtree(representative);
           }
-          prod.AddSubtree(poly);
-        } else {
+        } else if (firstNode is FactorVariableTreeNode) {
+          var representative = (FactorVariableTreeNode)firstNode;
+          foreach (var node in variableNodeGroup.Skip(1).Cast<FactorVariableTreeNode>()) {
+            for (int j = 0; j < representative.Weights.Length; j++) {
+              representative.Weights[j] *= node.Weights[j];
+            }
+          }
+          for (int j = 0; j < representative.Weights.Length; j++) {
+            representative.Weights[j] *= constantProduct;
+          }
+          constantProduct = 1.0;
+          // if the product already contains a factor it is not necessary to multiply a constant below
           prod.AddSubtree(representative);
         }
@@ -993,5 +1263,5 @@
     /// <summary>
     /// x => x * -1
-    /// Doesn't create new trees and manipulates x
+    /// Is only used in cases where it is not necessary to create new tree nodes. Manipulates x directly.
     /// </summary>
     /// <param name="x"></param>
@@ -1000,7 +1270,13 @@
       if (IsConstant(x)) {
         ((ConstantTreeNode)x).Value *= -1;
-      } else if (IsVariable(x)) {
-        var variableTree = (VariableTreeNode)x;
+      } else if (IsVariableBase(x)) {
+        var variableTree = (VariableTreeNodeBase)x;
         variableTree.Weight *= -1.0;
+      } else if (IsFactor(x)) {
+        var factorNode = (FactorVariableTreeNode)x;
+        for (int i = 0; i < factorNode.Weights.Length; i++) factorNode.Weights[i] *= -1;
+      } else if (IsBinFactor(x)) {
+        var factorNode = (BinaryFactorVariableTreeNode)x;
+        factorNode.Weight *= -1;
       } else if (IsAddition(x)) {
         // (x0 + x1 + .. + xn) * -1 => (-x0 + -x1 + .. + -xn)        
@@ -1024,5 +1300,5 @@
     /// <summary>
     /// x => 1/x
-    /// Doesn't create new trees and manipulates x
+    /// Must create new tree nodes
     /// </summary>
     /// <param name="x"></param>
@@ -1031,4 +1307,7 @@
       if (IsConstant(x)) {
         return MakeConstant(1.0 / ((ConstantTreeNode)x).Value);
+      } else if (IsFactor(x)) {
+        var factorNode = (FactorVariableTreeNode)x;
+        return MakeFactor(factorNode.Symbol, factorNode.VariableName, factorNode.Weights.Select(w => 1.0 / w));
       } else if (IsDivision(x)) {
         return MakeFraction(x.GetSubtree(1), x.GetSubtree(0));
@@ -1045,10 +1324,19 @@
     }
 
-    private ISymbolicExpressionTreeNode MakeVariable(double weight, string name) {
-      var tree = (VariableTreeNode)varSymbol.CreateTreeNode();
+    private ISymbolicExpressionTreeNode MakeFactor(FactorVariable sy, string variableName, IEnumerable<double> weights) {
+      var tree = (FactorVariableTreeNode)sy.CreateTreeNode();
+      tree.VariableName = variableName;
+      tree.Weights = weights.ToArray();
+      return tree;
+    }
+    private ISymbolicExpressionTreeNode MakeBinFactor(BinaryFactorVariable sy, string variableName, string variableValue, double weight) {
+      var tree = (BinaryFactorVariableTreeNode)sy.CreateTreeNode();
+      tree.VariableName = variableName;
+      tree.VariableValue = variableValue;
       tree.Weight = weight;
-      tree.VariableName = name;
       return tree;
     }
+
+
     #endregion
   }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModel.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModel.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModel.cs	(revision 15131)
@@ -63,11 +63,9 @@
         var variables =
           SymbolicExpressionTree.IterateNodesPrefix()
-            .OfType<VariableTreeNode>()
+            .OfType<IVariableTreeNode>()
             .Select(x => x.VariableName)
             .Distinct();
-        var variableConditions = SymbolicExpressionTree.IterateNodesPrefix()
-          .OfType<VariableConditionTreeNode>().Select(x => x.VariableName).Distinct();
 
-        return variables.Union(variableConditions).OrderBy(x => x);
+        return variables.OrderBy(x => x);
       }
     }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModelComplexityCalculator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModelComplexityCalculator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModelComplexityCalculator.cs	(revision 15131)
@@ -39,8 +39,10 @@
             return 1;
           }
-        case OpCodes.Variable: {
+        case OpCodes.Variable:
+        case OpCodes.BinaryFactorVariable:
+        case OpCodes.FactorVariable: {
             return 2;
           }
-        case OpCodes.Add: 
+        case OpCodes.Add:
         case OpCodes.Sub: {
             double complexity = 0;
@@ -50,5 +52,5 @@
             return complexity;
           }
-        case OpCodes.Mul: 
+        case OpCodes.Mul:
         case OpCodes.Div: {
             double complexity = 1;
@@ -60,7 +62,7 @@
           }
         case OpCodes.Sin:
-        case OpCodes.Cos: 
+        case OpCodes.Cos:
         case OpCodes.Tan:
-        case OpCodes.Exp: 
+        case OpCodes.Exp:
         case OpCodes.Log: {
             double complexity = CalculateComplexity(node.GetSubtree(0));
@@ -75,5 +77,5 @@
             return complexity * complexity * complexity;
           }
-        case OpCodes.Power:          
+        case OpCodes.Power:
         case OpCodes.Root: {
             double complexity = CalculateComplexity(node.GetSubtree(0));
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisProblem.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisProblem.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisProblem.cs	(revision 15131)
@@ -208,16 +208,32 @@
 
     protected virtual void UpdateGrammar() {
-      SymbolicExpressionTreeGrammar.MaximumFunctionArguments = MaximumFunctionArguments.Value;
-      SymbolicExpressionTreeGrammar.MaximumFunctionDefinitions = MaximumFunctionDefinitions.Value;
-      foreach (var varSymbol in SymbolicExpressionTreeGrammar.Symbols.OfType<HeuristicLab.Problems.DataAnalysis.Symbolic.Variable>()) {
+      var problemData = ProblemData;
+      var ds = problemData.Dataset;
+      var grammar = SymbolicExpressionTreeGrammar;
+      grammar.MaximumFunctionArguments = MaximumFunctionArguments.Value;
+      grammar.MaximumFunctionDefinitions = MaximumFunctionDefinitions.Value;
+      foreach (var varSymbol in grammar.Symbols.OfType<HeuristicLab.Problems.DataAnalysis.Symbolic.VariableBase>()) {
         if (!varSymbol.Fixed) {
-          varSymbol.AllVariableNames = ProblemData.InputVariables.Select(x => x.Value);
-          varSymbol.VariableNames = ProblemData.AllowedInputVariables;
+          varSymbol.AllVariableNames = problemData.InputVariables.Select(x => x.Value).Where(x => ds.VariableHasType<double>(x));
+          varSymbol.VariableNames = problemData.AllowedInputVariables.Where(x => ds.VariableHasType<double>(x));
         }
       }
-      foreach (var varSymbol in SymbolicExpressionTreeGrammar.Symbols.OfType<HeuristicLab.Problems.DataAnalysis.Symbolic.VariableCondition>()) {
-        if (!varSymbol.Fixed) {
-          varSymbol.AllVariableNames = ProblemData.InputVariables.Select(x => x.Value);
-          varSymbol.VariableNames = ProblemData.AllowedInputVariables;
+      foreach (var factorSymbol in grammar.Symbols.OfType<BinaryFactorVariable>()) {
+        if (!factorSymbol.Fixed) {
+          factorSymbol.AllVariableNames = problemData.InputVariables.Select(x => x.Value).Where(x => ds.VariableHasType<string>(x));
+          factorSymbol.VariableNames = problemData.AllowedInputVariables.Where(x => ds.VariableHasType<string>(x));
+          factorSymbol.VariableValues = factorSymbol.VariableNames
+            .ToDictionary(varName => varName, varName => ds.GetStringValues(varName).Distinct().ToList());
+        }
+      }
+      foreach (var factorSymbol in grammar.Symbols.OfType<FactorVariable>()) {
+        if (!factorSymbol.Fixed) {
+          factorSymbol.AllVariableNames = problemData.InputVariables.Select(x => x.Value).Where(x => ds.VariableHasType<string>(x));
+          factorSymbol.VariableNames = problemData.AllowedInputVariables.Where(x => ds.VariableHasType<string>(x));
+          factorSymbol.VariableValues = factorSymbol.VariableNames
+            .ToDictionary(varName => varName,
+            varName => ds.GetStringValues(varName).Distinct()
+            .Select((n, i) => Tuple.Create(n, i))
+            .ToDictionary(tup => tup.Item1, tup => tup.Item2));
         }
       }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisSingleObjectiveProblem.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisSingleObjectiveProblem.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisSingleObjectiveProblem.cs	(revision 15131)
@@ -73,5 +73,5 @@
     }
 
-    public SymbolicDataAnalysisSingleObjectiveProblem(T problemData, U evaluator, V solutionCreator)
+    protected SymbolicDataAnalysisSingleObjectiveProblem(T problemData, U evaluator, V solutionCreator)
       : base(problemData, evaluator, solutionCreator) {
       Parameters.Add(new FixedValueParameter<BoolValue>(MaximizationParameterName, "Set to false if the problem should be minimized."));
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisSolutionImpactValuesCalculator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisSolutionImpactValuesCalculator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisSolutionImpactValuesCalculator.cs	(revision 15131)
@@ -21,4 +21,5 @@
 
 using System.Collections.Generic;
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -36,25 +37,35 @@
     [StorableConstructor]
     protected SymbolicDataAnalysisSolutionImpactValuesCalculator(bool deserializing) : base(deserializing) { }
-    public abstract double CalculateReplacementValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows);
-    public abstract double CalculateImpactValue(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows, double qualityForImpactsCalculation = double.NaN);
     public abstract void CalculateImpactAndReplacementValues(ISymbolicDataAnalysisModel model, ISymbolicExpressionTreeNode node, IDataAnalysisProblemData problemData, IEnumerable<int> rows, out double impactValue, out double replacementValue, out double newQualityForImpactsCalculation, double qualityForImpactsCalculation = double.NaN);
 
-    protected static double CalculateReplacementValue(ISymbolicExpressionTreeNode node, ISymbolicExpressionTree sourceTree, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter,
+    protected IEnumerable<double> CalculateReplacementValues(ISymbolicExpressionTreeNode node, ISymbolicExpressionTree sourceTree, ISymbolicDataAnalysisExpressionTreeInterpreter interpreter,
       IDataset dataset, IEnumerable<int> rows) {
       //optimization: constant nodes return always the same value
       ConstantTreeNode constantNode = node as ConstantTreeNode;
-      if (constantNode != null) return constantNode.Value;
+      BinaryFactorVariableTreeNode binaryFactorNode = node as BinaryFactorVariableTreeNode;
+      FactorVariableTreeNode factorNode = node as FactorVariableTreeNode;
+      if (constantNode != null) {
+        yield return constantNode.Value;
+      } else if (binaryFactorNode != null) {
+        // valid replacements are either all off or all on
+        yield return 0;
+        yield return 1;
+      } else if (factorNode != null) {
+        foreach (var w in factorNode.Weights) yield return w;
+        yield return 0.0;
+      } else {
+        var rootSymbol = new ProgramRootSymbol().CreateTreeNode();
+        var startSymbol = new StartSymbol().CreateTreeNode();
+        rootSymbol.AddSubtree(startSymbol);
+        startSymbol.AddSubtree((ISymbolicExpressionTreeNode)node.Clone());
 
-      var rootSymbol = new ProgramRootSymbol().CreateTreeNode();
-      var startSymbol = new StartSymbol().CreateTreeNode();
-      rootSymbol.AddSubtree(startSymbol);
-      startSymbol.AddSubtree((ISymbolicExpressionTreeNode)node.Clone());
-
-      var tempTree = new SymbolicExpressionTree(rootSymbol);
-      // clone ADFs of source tree
-      for (int i = 1; i < sourceTree.Root.SubtreeCount; i++) {
-        tempTree.Root.AddSubtree((ISymbolicExpressionTreeNode)sourceTree.Root.GetSubtree(i).Clone());
+        var tempTree = new SymbolicExpressionTree(rootSymbol);
+        // clone ADFs of source tree
+        for (int i = 1; i < sourceTree.Root.SubtreeCount; i++) {
+          tempTree.Root.AddSubtree((ISymbolicExpressionTreeNode)sourceTree.Root.GetSubtree(i).Clone());
+        }
+        yield return interpreter.GetSymbolicExpressionTreeValues(tempTree, dataset, rows).Median();
+        yield return interpreter.GetSymbolicExpressionTreeValues(tempTree, dataset, rows).Average(); // TODO perf
       }
-      return interpreter.GetSymbolicExpressionTreeValues(tempTree, dataset, rows).Median();
     }
   }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/BinaryFactorVariable.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/BinaryFactorVariable.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/BinaryFactorVariable.cs	(revision 15131)
@@ -0,0 +1,76 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  [Item("BinaryFactorVariable", "Represents a categorical variable (comparable to factors as in R) and it's value.")]
+  public class BinaryFactorVariable : VariableBase {
+
+    private readonly Dictionary<string, List<string>> variableValues;
+
+    [Storable]
+    public IEnumerable<KeyValuePair<string, List<string>>> VariableValues {
+      get { return variableValues; }
+      set {
+        if (value == null) throw new ArgumentNullException();
+        variableValues.Clear();
+        foreach (var kvp in value) {
+          variableValues.Add(kvp.Key, new List<string>(kvp.Value));
+        }
+      }
+    }
+
+    [StorableConstructor]
+    protected BinaryFactorVariable(bool deserializing)
+      : base(deserializing) {
+      variableValues = new Dictionary<string, List<string>>();
+    }
+    protected BinaryFactorVariable(BinaryFactorVariable original, Cloner cloner)
+      : base(original, cloner) {
+      variableValues =
+        original.variableValues.ToDictionary(kvp => kvp.Key, kvp => new List<string>(kvp.Value));
+    }
+    public BinaryFactorVariable() : this("BinaryFactorVariable", "Represents a categorical variable (comparable to factors as in R) and it's value.") { }
+    public BinaryFactorVariable(string name, string description)
+      : base(name, description) {
+      variableValues = new Dictionary<string, List<string>>();
+    }
+
+    public override ISymbolicExpressionTreeNode CreateTreeNode() {
+      return new BinaryFactorVariableTreeNode(this);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BinaryFactorVariable(this, cloner);
+    }
+
+    public IEnumerable<string> GetVariableValues(string variableName) {
+      return variableValues[variableName];
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/BinaryFactorVariableTreeNode.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/BinaryFactorVariableTreeNode.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/BinaryFactorVariableTreeNode.cs	(revision 15131)
@@ -0,0 +1,84 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  public sealed class BinaryFactorVariableTreeNode : VariableTreeNodeBase {
+    public new BinaryFactorVariable Symbol {
+      get { return (BinaryFactorVariable)base.Symbol; }
+    }
+
+    [Storable]
+    private string variableValue;
+    public string VariableValue {
+      get { return variableValue; }
+      set { variableValue = value; }
+    }
+
+    [StorableConstructor]
+    private BinaryFactorVariableTreeNode(bool deserializing) : base(deserializing) { }
+    private BinaryFactorVariableTreeNode(BinaryFactorVariableTreeNode original, Cloner cloner)
+      : base(original, cloner) {
+      variableValue = original.variableValue;
+    }
+    public BinaryFactorVariableTreeNode(BinaryFactorVariable variableSymbol) : base(variableSymbol) { }
+
+    public override bool HasLocalParameters {
+      get { return true; }
+    }
+
+    public override void ResetLocalParameters(IRandom random) {
+      base.ResetLocalParameters(random);
+      variableValue = Symbol.GetVariableValues(VariableName).SampleRandom(random);
+    }
+
+    public override void ShakeLocalParameters(IRandom random, double shakingFactor) {
+      // 50% additive & 50% multiplicative (override of functionality of base class because of a BUG)
+      if (random.NextDouble() < 0.5) {
+        double x = NormalDistributedRandom.NextDouble(random, Symbol.WeightManipulatorMu, Symbol.WeightManipulatorSigma);
+        Weight = Weight + x * shakingFactor;
+      } else {
+        double x = NormalDistributedRandom.NextDouble(random, 1.0, Symbol.MultiplicativeWeightManipulatorSigma);
+        Weight = Weight * x;
+      }
+      if (random.NextDouble() < Symbol.VariableChangeProbability) {
+        var oldName = VariableName;
+        VariableName = Symbol.VariableNames.SampleRandom(random);
+        // reinitialize weights if variable has changed (similar to FactorVariableTreeNode)
+        if (oldName != VariableName)
+          Weight = NormalDistributedRandom.NextDouble(random, Symbol.WeightMu, Symbol.WeightSigma);
+      }
+      variableValue = Symbol.GetVariableValues(VariableName).SampleRandom(random);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BinaryFactorVariableTreeNode(this, cloner);
+    }
+
+    public override string ToString() {
+      return base.ToString() + " = " + variableValue;
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/FactorVariable.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/FactorVariable.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/FactorVariable.cs	(revision 15131)
@@ -0,0 +1,78 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  [Item("FactorVariable", "Represents a categorical variable (comparable to factors as in R).")]
+  public class FactorVariable : VariableBase {
+    private readonly Dictionary<string, Dictionary<string, int>> variableValues; // for each variable value also store a zero-based index
+    [Storable]
+    public IEnumerable<KeyValuePair<string, Dictionary<string, int>>> VariableValues {
+      get { return variableValues; }
+      set {
+        if(value == null) throw new ArgumentNullException();
+        variableValues.Clear();
+        foreach(var kvp in value) {
+          variableValues.Add(kvp.Key, new Dictionary<string, int>(kvp.Value));
+        }
+      }
+    }
+
+    [StorableConstructor]
+    protected FactorVariable(bool deserializing)
+      : base(deserializing) {
+      variableValues = new Dictionary<string, Dictionary<string, int>>();
+    }
+    protected FactorVariable(FactorVariable original, Cloner cloner)
+      : base(original, cloner) {
+      variableValues =
+        original.variableValues.ToDictionary(kvp => kvp.Key, kvp => new Dictionary<string, int>(kvp.Value));
+    }
+    public FactorVariable() : this("FactorVariable", "Represents a categorical variable (comparable to factors as in R).") { }
+    public FactorVariable(string name, string description)
+      : base(name, description) {
+      variableValues = new Dictionary<string, Dictionary<string,int>>();
+    }
+
+    public override ISymbolicExpressionTreeNode CreateTreeNode() {
+      return new FactorVariableTreeNode(this);
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new FactorVariable(this, cloner);
+    }
+
+    public IEnumerable<string> GetVariableValues(string variableName) {
+      return variableValues[variableName].Keys;
+    }
+
+    public int GetIndexForValue(string variableName, string variableValue) {
+      return variableValues[variableName][variableValue];
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/FactorVariableTreeNode.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/FactorVariableTreeNode.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/FactorVariableTreeNode.cs	(revision 15131)
@@ -0,0 +1,116 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Random;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  public sealed class FactorVariableTreeNode : SymbolicExpressionTreeTerminalNode, IVariableTreeNode {
+    public new FactorVariable Symbol {
+      get { return (FactorVariable)base.Symbol; }
+    }
+    [Storable]
+    private double[] weights;
+    public double[] Weights {
+      get { return weights; }
+      set { weights = value; }
+    }
+    [Storable]
+    private string variableName;
+    public string VariableName {
+      get { return variableName; }
+      set { variableName = value; }
+    }
+
+    [StorableConstructor]
+    private FactorVariableTreeNode(bool deserializing) : base(deserializing) { }
+    private FactorVariableTreeNode(FactorVariableTreeNode original, Cloner cloner)
+      : base(original, cloner) {
+      variableName = original.variableName;
+      if (original.weights != null) {
+        this.weights = new double[original.Weights.Length];
+        Array.Copy(original.Weights, weights, weights.Length);
+      }
+    }
+
+    public FactorVariableTreeNode(FactorVariable variableSymbol)
+      : base(variableSymbol) {
+    }
+
+    public override bool HasLocalParameters {
+      get { return true; }
+    }
+
+    public override void ResetLocalParameters(IRandom random) {
+      base.ResetLocalParameters(random);
+      variableName = Symbol.VariableNames.SampleRandom(random);
+      weights =
+        Symbol.GetVariableValues(variableName)
+        .Select(_ => NormalDistributedRandom.NextDouble(random, 0, 1)).ToArray();
+    }
+
+    public override void ShakeLocalParameters(IRandom random, double shakingFactor) {
+      // mutate only one randomly selected weight
+      var idx = random.Next(weights.Length);
+      // 50% additive & 50% multiplicative
+      if (random.NextDouble() < 0.5) {
+        double x = NormalDistributedRandom.NextDouble(random, Symbol.WeightManipulatorMu,
+          Symbol.WeightManipulatorSigma);
+        weights[idx] = weights[idx] + x * shakingFactor;
+      } else {
+        double x = NormalDistributedRandom.NextDouble(random, 1.0, Symbol.MultiplicativeWeightManipulatorSigma);
+        weights[idx] = weights[idx] * x;
+      }
+      if (random.NextDouble() < Symbol.VariableChangeProbability) {
+        VariableName = Symbol.VariableNames.SampleRandom(random);
+        if (weights.Length != Symbol.GetVariableValues(VariableName).Count()) {
+          // if the length of the weight array does not match => re-initialize weights
+          weights =
+            Symbol.GetVariableValues(variableName)
+              .Select(_ => NormalDistributedRandom.NextDouble(random, 0, 1))
+              .ToArray();
+        }
+      }
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new FactorVariableTreeNode(this, cloner);
+    }
+
+    public double GetValue(string cat) {
+      return weights[Symbol.GetIndexForValue(VariableName, cat)];
+    }
+
+    public override string ToString() {
+      var weightStr = string.Join("; ",
+        Symbol.GetVariableValues(VariableName).Select(value => value + ": " + GetValue(value).ToString("E4")));
+      return VariableName + " (factor) "
+        + "[" + weightStr + "]";
+    }
+  }
+}
+
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/LaggedVariable.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/LaggedVariable.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/LaggedVariable.cs	(revision 15131)
@@ -27,5 +27,5 @@
   [StorableClass]
   [Item("LaggedVariable", "Represents a variable value with a time offset.")]
-  public class LaggedVariable : Variable {
+  public class LaggedVariable : VariableBase {
     [Storable]
     private int minLag;
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/LaggedVariableTreeNode.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/LaggedVariableTreeNode.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/LaggedVariableTreeNode.cs	(revision 15131)
@@ -26,5 +26,5 @@
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
   [StorableClass]
-  public sealed class LaggedVariableTreeNode : VariableTreeNode, ILaggedTreeNode {
+  public sealed class LaggedVariableTreeNode : VariableTreeNodeBase, ILaggedTreeNode {
     public new LaggedVariable Symbol {
       get { return (LaggedVariable)base.Symbol; }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/Variable.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/Variable.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/Variable.cs	(revision 15131)
@@ -29,143 +29,15 @@
   [StorableClass]
   [Item("Variable", "Represents a variable value.")]
-  public class Variable : Symbol {
-    #region Properties
-    [Storable]
-    private double weightMu;
-    public double WeightMu {
-      get { return weightMu; }
-      set {
-        if (value != weightMu) {
-          weightMu = value;
-          OnChanged(EventArgs.Empty);
-        }
-      }
-    }
-    [Storable]
-    private double weightSigma;
-    public double WeightSigma {
-      get { return weightSigma; }
-      set {
-        if (weightSigma < 0.0) throw new ArgumentException("Negative sigma is not allowed.");
-        if (value != weightSigma) {
-          weightSigma = value;
-          OnChanged(EventArgs.Empty);
-        }
-      }
-    }
-    [Storable]
-    private double weightManipulatorMu;
-    public double WeightManipulatorMu {
-      get { return weightManipulatorMu; }
-      set {
-        if (value != weightManipulatorMu) {
-          weightManipulatorMu = value;
-          OnChanged(EventArgs.Empty);
-        }
-      }
-    }
-    [Storable]
-    private double weightManipulatorSigma;
-    public double WeightManipulatorSigma {
-      get { return weightManipulatorSigma; }
-      set {
-        if (weightManipulatorSigma < 0.0) throw new ArgumentException("Negative sigma is not allowed.");
-        if (value != weightManipulatorSigma) {
-          weightManipulatorSigma = value;
-          OnChanged(EventArgs.Empty);
-        }
-      }
-    }
-    [Storable(DefaultValue = 0.0)]
-    private double multiplicativeWeightManipulatorSigma;
-    public double MultiplicativeWeightManipulatorSigma {
-      get { return multiplicativeWeightManipulatorSigma; }
-      set {
-        if (multiplicativeWeightManipulatorSigma < 0.0) throw new ArgumentException("Negative sigma is not allowed.");
-        if (value != multiplicativeWeightManipulatorSigma) {
-          multiplicativeWeightManipulatorSigma = value;
-          OnChanged(EventArgs.Empty);
-        }
-      }
-    }
-    private List<string> variableNames;
-    [Storable]
-    public IEnumerable<string> VariableNames {
-      get { return variableNames; }
-      set {
-        if (value == null) throw new ArgumentNullException();
-        variableNames.Clear();
-        variableNames.AddRange(value);
-        OnChanged(EventArgs.Empty);
-      }
-    }
-
-    private List<string> allVariableNames;
-    [Storable]
-    public IEnumerable<string> AllVariableNames {
-      get { return allVariableNames; }
-      set {
-        if (value == null) throw new ArgumentNullException();
-        allVariableNames.Clear();
-        allVariableNames.AddRange(value);
-      }
-    }
-
-    public override bool Enabled {
-      get {
-        if (variableNames.Count == 0) return false;
-        return base.Enabled;
-      }
-      set {
-        if (variableNames.Count == 0) base.Enabled = false;
-        else base.Enabled = value;
-      }
-    }
-
-    private const int minimumArity = 0;
-    private const int maximumArity = 0;
-
-    public override int MinimumArity {
-      get { return minimumArity; }
-    }
-    public override int MaximumArity {
-      get { return maximumArity; }
-    }
-    #endregion
-
-    [StorableHook(HookType.AfterDeserialization)]
-    private void AfterDeserialization() {
-      if (allVariableNames == null || (allVariableNames.Count == 0 && variableNames.Count > 0)) {
-        allVariableNames = variableNames;
-      }
-    }
+  public class Variable : VariableBase {
 
     [StorableConstructor]
     protected Variable(bool deserializing)
       : base(deserializing) {
-      variableNames = new List<string>();
-      allVariableNames = new List<string>();
     }
     protected Variable(Variable original, Cloner cloner)
       : base(original, cloner) {
-      weightMu = original.weightMu;
-      weightSigma = original.weightSigma;
-      variableNames = new List<string>(original.variableNames);
-      allVariableNames = new List<string>(original.allVariableNames);
-      weightManipulatorMu = original.weightManipulatorMu;
-      weightManipulatorSigma = original.weightManipulatorSigma;
-      multiplicativeWeightManipulatorSigma = original.multiplicativeWeightManipulatorSigma;
     }
-    public Variable() : this("Variable", "Represents a variable value.") { }
-    public Variable(string name, string description)
-      : base(name, description) {
-      weightMu = 1.0;
-      weightSigma = 1.0;
-      weightManipulatorMu = 0.0;
-      weightManipulatorSigma = 0.05;
-      multiplicativeWeightManipulatorSigma = 0.03;
-      variableNames = new List<string>();
-      allVariableNames = new List<string>();
-    }
+    public Variable() : base("Variable", "Represents a variable value.") { }
+    public Variable(string name, string description) : base(name, description) { }
 
     public override ISymbolicExpressionTreeNode CreateTreeNode() {
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableBase.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableBase.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableBase.cs	(revision 15131)
@@ -0,0 +1,182 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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 HeuristicLab.Common;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  public abstract class VariableBase : Symbol, IVariableSymbol {
+    #region Properties
+    [Storable]
+    private double weightMu;
+    public double WeightMu {
+      get { return weightMu; }
+      set {
+        if(value != weightMu) {
+          weightMu = value;
+          OnChanged(EventArgs.Empty);
+        }
+      }
+    }
+    [Storable]
+    private double weightSigma;
+    public double WeightSigma {
+      get { return weightSigma; }
+      set {
+        if(weightSigma < 0.0) throw new ArgumentException("Negative sigma is not allowed.");
+        if(value != weightSigma) {
+          weightSigma = value;
+          OnChanged(EventArgs.Empty);
+        }
+      }
+    }
+    [Storable]
+    private double weightManipulatorMu;
+    public double WeightManipulatorMu {
+      get { return weightManipulatorMu; }
+      set {
+        if(value != weightManipulatorMu) {
+          weightManipulatorMu = value;
+          OnChanged(EventArgs.Empty);
+        }
+      }
+    }
+    [Storable]
+    private double weightManipulatorSigma;
+    public double WeightManipulatorSigma {
+      get { return weightManipulatorSigma; }
+      set {
+        if(weightManipulatorSigma < 0.0) throw new ArgumentException("Negative sigma is not allowed.");
+        if(value != weightManipulatorSigma) {
+          weightManipulatorSigma = value;
+          OnChanged(EventArgs.Empty);
+        }
+      }
+    }
+    [Storable(DefaultValue = 0.0)]
+    private double multiplicativeWeightManipulatorSigma;
+    public double MultiplicativeWeightManipulatorSigma {
+      get { return multiplicativeWeightManipulatorSigma; }
+      set {
+        if(multiplicativeWeightManipulatorSigma < 0.0) throw new ArgumentException("Negative sigma is not allowed.");
+        if(value != multiplicativeWeightManipulatorSigma) {
+          multiplicativeWeightManipulatorSigma = value;
+          OnChanged(EventArgs.Empty);
+        }
+      }
+    }
+
+    [Storable(DefaultValue = 1.0)]
+    private double variableChangeProbability;
+
+    public double VariableChangeProbability {
+      get { return variableChangeProbability; }
+      set {
+        if(value < 0 || value > 1.0) throw new ArgumentException("Variable change probability must lie in the interval [0..1]");
+        variableChangeProbability = value;
+      }
+    }
+
+    private List<string> variableNames;
+    [Storable]
+    public IEnumerable<string> VariableNames {
+      get { return variableNames; }
+      set {
+        if(value == null) throw new ArgumentNullException();
+        variableNames.Clear();
+        variableNames.AddRange(value);
+        OnChanged(EventArgs.Empty);
+      }
+    }
+
+    private List<string> allVariableNames;
+    [Storable]
+    public IEnumerable<string> AllVariableNames {
+      get { return allVariableNames; }
+      set {
+        if(value == null) throw new ArgumentNullException();
+        allVariableNames.Clear();
+        allVariableNames.AddRange(value);
+      }
+    }
+
+    public override bool Enabled {
+      get {
+        if(variableNames.Count == 0) return false;
+        return base.Enabled;
+      }
+      set {
+        if(variableNames.Count == 0) base.Enabled = false;
+        else base.Enabled = value;
+      }
+    }
+
+    private const int minimumArity = 0;
+    private const int maximumArity = 0;
+
+    public override int MinimumArity {
+      get { return minimumArity; }
+    }
+    public override int MaximumArity {
+      get { return maximumArity; }
+    }
+    #endregion
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if(allVariableNames == null || (allVariableNames.Count == 0 && variableNames.Count > 0)) {
+        allVariableNames = variableNames;
+      }
+    }
+
+    [StorableConstructor]
+    protected VariableBase(bool deserializing)
+      : base(deserializing) {
+      variableNames = new List<string>();
+      allVariableNames = new List<string>();
+    }
+    protected VariableBase(VariableBase original, Cloner cloner)
+      : base(original, cloner) {
+      weightMu = original.weightMu;
+      weightSigma = original.weightSigma;
+      variableNames = new List<string>(original.variableNames);
+      allVariableNames = new List<string>(original.allVariableNames);
+      weightManipulatorMu = original.weightManipulatorMu;
+      weightManipulatorSigma = original.weightManipulatorSigma;
+      multiplicativeWeightManipulatorSigma = original.multiplicativeWeightManipulatorSigma;
+      variableChangeProbability = original.variableChangeProbability;
+      }
+    protected VariableBase(string name, string description)
+      : base(name, description) {
+      weightMu = 1.0;
+      weightSigma = 1.0;
+      weightManipulatorMu = 0.0;
+      weightManipulatorSigma = 0.05;
+      multiplicativeWeightManipulatorSigma = 0.03;
+      variableChangeProbability = 0.2;
+      variableNames = new List<string>();
+      allVariableNames = new List<string>();
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableCondition.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableCondition.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableCondition.cs	(revision 15131)
@@ -30,5 +30,5 @@
   [StorableClass]
   [Item("Variable Condition", "Represents a condition that tests a given variable against a specified threshold.")]
-  public sealed class VariableCondition : Symbol {
+  public sealed class VariableCondition : Symbol, IVariableSymbol {
     #region properties
     [Storable]
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableConditionTreeNode.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableConditionTreeNode.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableConditionTreeNode.cs	(revision 15131)
@@ -29,5 +29,5 @@
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
   [StorableClass]
-  public sealed class VariableConditionTreeNode : SymbolicExpressionTreeNode {
+  public sealed class VariableConditionTreeNode : SymbolicExpressionTreeNode, IVariableTreeNode {
     #region properties
     public new VariableCondition Symbol {
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableTreeNode.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableTreeNode.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableTreeNode.cs	(revision 15131)
@@ -21,73 +21,22 @@
 
 using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using HeuristicLab.Random;
 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
   [StorableClass]
-  public class VariableTreeNode : SymbolicExpressionTreeTerminalNode {
+  public sealed class VariableTreeNode : VariableTreeNodeBase {
     public new Variable Symbol {
       get { return (Variable)base.Symbol; }
     }
-    [Storable]
-    private double weight;
-    public double Weight {
-      get { return weight; }
-      set { weight = value; }
+    [StorableConstructor]
+    private VariableTreeNode(bool deserializing) : base(deserializing) { }
+    private VariableTreeNode(VariableTreeNode original, Cloner cloner)
+      : base(original, cloner) {
     }
-    [Storable]
-    private string variableName;
-    public string VariableName {
-      get { return variableName; }
-      set { variableName = value; }
-    }
-
-    [StorableConstructor]
-    protected VariableTreeNode(bool deserializing) : base(deserializing) { }
-    protected VariableTreeNode(VariableTreeNode original, Cloner cloner)
-      : base(original, cloner) {
-      weight = original.weight;
-      variableName = original.variableName;
-    }
-    protected VariableTreeNode() { }
+    private VariableTreeNode() { }
     public VariableTreeNode(Variable variableSymbol) : base(variableSymbol) { }
-
-    public override bool HasLocalParameters {
-      get { return true; }
-    }
-
-    public override void ResetLocalParameters(IRandom random) {
-      base.ResetLocalParameters(random);
-      weight = NormalDistributedRandom.NextDouble(random, Symbol.WeightMu, Symbol.WeightSigma);
-
-#pragma warning disable 612, 618
-      variableName = Symbol.VariableNames.SelectRandom(random);
-#pragma warning restore 612, 618
-    }
-
-    public override void ShakeLocalParameters(IRandom random, double shakingFactor) {
-      base.ShakeLocalParameters(random, shakingFactor);
-      // 50% additive & 50% multiplicative
-      if (random.NextDouble() < 0) {
-        double x = NormalDistributedRandom.NextDouble(random, Symbol.WeightManipulatorMu, Symbol.WeightManipulatorSigma);
-        weight = weight + x * shakingFactor;
-      } else {
-        double x = NormalDistributedRandom.NextDouble(random, 1.0, Symbol.MultiplicativeWeightManipulatorSigma);
-        weight = weight * x;
-      }
-#pragma warning disable 612, 618
-      variableName = Symbol.VariableNames.SelectRandom(random);
-#pragma warning restore 612, 618
-    }
 
     public override IDeepCloneable Clone(Cloner cloner) {
       return new VariableTreeNode(this, cloner);
     }
-
-    public override string ToString() {
-      if (weight.IsAlmost(1.0)) return variableName;
-      else return weight.ToString("E4") + " " + variableName;
-    }
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableTreeNodeBase.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableTreeNodeBase.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Symbols/VariableTreeNodeBase.cs	(revision 15131)
@@ -0,0 +1,97 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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;
+using HeuristicLab.Random;
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+  [StorableClass]
+  public abstract class VariableTreeNodeBase : SymbolicExpressionTreeTerminalNode, IVariableTreeNode {
+    public new VariableBase Symbol {
+      get { return (VariableBase)base.Symbol; }
+    }
+    [Storable]
+    private double weight;
+    public double Weight {
+      get { return weight; }
+      set { weight = value; }
+    }
+    [Storable]
+    private string variableName;
+    public string VariableName {
+      get { return variableName; }
+      set { variableName = value; }
+    }
+
+    [StorableConstructor]
+    protected VariableTreeNodeBase(bool deserializing) : base(deserializing) { }
+    protected VariableTreeNodeBase(VariableTreeNodeBase original, Cloner cloner)
+      : base(original, cloner) {
+      weight = original.weight;
+      variableName = original.variableName;
+    }
+    protected VariableTreeNodeBase() { }
+    protected VariableTreeNodeBase(VariableBase variableSymbol) : base(variableSymbol) { }
+
+    public override bool HasLocalParameters {
+      get { return true; }
+    }
+
+    public override void ResetLocalParameters(IRandom random) {
+      base.ResetLocalParameters(random);
+      weight = NormalDistributedRandom.NextDouble(random, Symbol.WeightMu, Symbol.WeightSigma);
+
+#pragma warning disable 612, 618
+      variableName = Symbol.VariableNames.SelectRandom(random);
+#pragma warning restore 612, 618
+    }
+
+    public override void ShakeLocalParameters(IRandom random, double shakingFactor) {
+      base.ShakeLocalParameters(random, shakingFactor);
+
+      // 50% additive & 50% multiplicative (TODO: BUG in if statement below -> fix in HL 4.0!)
+      if (random.NextDouble() < 0) {
+        double x = NormalDistributedRandom.NextDouble(random, Symbol.WeightManipulatorMu, Symbol.WeightManipulatorSigma);
+        weight = weight + x * shakingFactor;
+      } else {
+        double x = NormalDistributedRandom.NextDouble(random, 1.0, Symbol.MultiplicativeWeightManipulatorSigma);
+        weight = weight * x;
+      }
+
+      if (Symbol.VariableChangeProbability >= 1.0 || random.NextDouble() < Symbol.VariableChangeProbability) {
+        var oldName = variableName;
+#pragma warning disable 612, 618
+        variableName = Symbol.VariableNames.SelectRandom(random);
+#pragma warning restore 612, 618
+        if (oldName != variableName) {
+          weight = NormalDistributedRandom.NextDouble(random, Symbol.WeightMu, Symbol.WeightSigma);
+        }
+      }
+    }
+
+    public override string ToString() {
+      if (weight.IsAlmost(1.0)) return variableName;
+      else return weight.ToString("E4") + " " + variableName;
+    }
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/ClassificationSolutionComparisonView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/ClassificationSolutionComparisonView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/ClassificationSolutionComparisonView.cs	(revision 15131)
@@ -80,7 +80,6 @@
 
           IClassificationProblemData problemData = Content.ProblemData;
-          var dataset = problemData.Dataset;
           solutions = new List<IClassificationSolution>() { Content };
-          solutions.AddRange(GenerateClassificationSolutions(problemData));
+          solutions.AddRange(GenerateClassificationSolutions().OrderBy(s=>s.Name));
 
           dataGridView.ColumnCount = 4;
@@ -105,9 +104,9 @@
             var trainingIndizes = problemData.TrainingIndices;
             var originalTrainingValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, trainingIndizes);
-            var estimatedTrainingValues = solution.Model.GetEstimatedClassValues(dataset, trainingIndizes);
+            var estimatedTrainingValues = solution.EstimatedTrainingClassValues;
 
             var testIndices = problemData.TestIndices;
             var originalTestValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, testIndices);
-            var estimatedTestValues = solution.Model.GetEstimatedClassValues(dataset, testIndices);
+            var estimatedTestValues = solution.EstimatedTestClassValues;
 
             OnlineCalculatorError errorState;
@@ -126,15 +125,18 @@
     }
 
-    private IEnumerable<IClassificationSolution> GenerateClassificationSolutions(IClassificationProblemData problemData) {
+    protected virtual IEnumerable<IClassificationSolution> GenerateClassificationSolutions() {
+      var problemData = Content.ProblemData;
       var newSolutions = new List<IClassificationSolution>();
       var zeroR = ZeroR.CreateZeroRSolution(problemData);
       zeroR.Name = "ZeroR Classification Solution";
       newSolutions.Add(zeroR);
-      var oneR = OneR.CreateOneRSolution(problemData);
-      oneR.Name = "OneR Classification Solution";
-      newSolutions.Add(oneR);
+      try {
+        var oneR = OneR.CreateOneRSolution(problemData);
+        oneR.Name = "OneR Classification Solution (all variables)";
+        newSolutions.Add(oneR);
+      } catch (NotSupportedException) { } catch (ArgumentException) { }
       try {
         var lda = LinearDiscriminantAnalysis.CreateLinearDiscriminantAnalysisSolution(problemData);
-        lda.Name = "Linear Discriminant Analysis Solution";
+        lda.Name = "Linear Discriminant Analysis Solution (all variables)";
         newSolutions.Add(lda);
       } catch (NotSupportedException) { } catch (ArgumentException) { }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/DensityChart.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/DensityChart.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/DensityChart.cs	(revision 15131)
@@ -30,10 +30,20 @@
     }
 
+    public void UpdateChart(IList<string> data, double minimumHeight = 0.1) {
+      if (data == null || !data.Any())
+        return;
+      UpdateChartWithBuckets(CalculateBuckets(data));
+    }
+
+
     public void UpdateChart(IList<double> data, double min, double max, int numBuckets, double minimumHeight = 0.1) {
       if (data == null || numBuckets < 0 || min > max || max < min)
         return;
 
-      var buckets = CalculateBuckets(data, numBuckets, min, max);
+      UpdateChartWithBuckets(CalculateBuckets(data, numBuckets, min, max));
+    }
 
+
+    private void UpdateChartWithBuckets(double[] buckets) {
       // set minimum height of all non-zero buckets on 10% of maximum
       double minHeight = buckets.Max() * 0.1;
@@ -69,4 +79,7 @@
       return buckets;
     }
+    private double[] CalculateBuckets(IList<string> data) {
+      return data.GroupBy(val => val).OrderBy(g => g.Key).Select(g => (double)g.Count()).Concat(new double[] { 0.0 }).ToArray();
+    }
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/FactorGradientChart.Designer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/FactorGradientChart.Designer.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/FactorGradientChart.Designer.cs	(revision 15131)
@@ -0,0 +1,159 @@
+﻿namespace HeuristicLab.Problems.DataAnalysis.Views {
+  partial class FactorGradientChart {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      System.Windows.Forms.DataVisualization.Charting.VerticalLineAnnotation verticalLineAnnotation1 = new System.Windows.Forms.DataVisualization.Charting.VerticalLineAnnotation();
+      System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
+      System.Windows.Forms.DataVisualization.Charting.StripLine stripLine1 = new System.Windows.Forms.DataVisualization.Charting.StripLine();
+      System.Windows.Forms.DataVisualization.Charting.StripLine stripLine2 = new System.Windows.Forms.DataVisualization.Charting.StripLine();
+      System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
+      System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
+      System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series();
+      System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series();
+      System.Windows.Forms.DataVisualization.Charting.Series series4 = new System.Windows.Forms.DataVisualization.Charting.Series();
+      System.Windows.Forms.DataVisualization.Charting.Title title1 = new System.Windows.Forms.DataVisualization.Charting.Title();
+      this.calculationPendingLabel = new System.Windows.Forms.Label();
+      this.calculationPendingTimer = new System.Windows.Forms.Timer(this.components);
+      this.chart = new HeuristicLab.Visualization.ChartControlsExtensions.EnhancedChart();
+      this.toolTip = new System.Windows.Forms.ToolTip(this.components);
+      ((System.ComponentModel.ISupportInitialize)(this.chart)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // calculationPendingLabel
+      // 
+      this.calculationPendingLabel.BackColor = System.Drawing.Color.White;
+      this.calculationPendingLabel.Image = HeuristicLab.Common.Resources.VSImageLibrary.Timer;
+      this.calculationPendingLabel.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
+      this.calculationPendingLabel.Location = new System.Drawing.Point(3, 3);
+      this.calculationPendingLabel.Margin = new System.Windows.Forms.Padding(0);
+      this.calculationPendingLabel.Name = "calculationPendingLabel";
+      this.calculationPendingLabel.Size = new System.Drawing.Size(17, 17);
+      this.calculationPendingLabel.TabIndex = 1;
+      this.calculationPendingLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+      this.calculationPendingLabel.Visible = false;
+      // 
+      // calculationPendingTimer
+      // 
+      this.calculationPendingTimer.Tick += new System.EventHandler(this.calculationPendingTimer_Tick);
+      // 
+      // chart
+      // 
+      this.chart.AllowDrop = true;
+      verticalLineAnnotation1.AllowMoving = true;
+      verticalLineAnnotation1.AxisXName = "ChartArea\\rX";
+      verticalLineAnnotation1.ClipToChartArea = "ChartArea";
+      verticalLineAnnotation1.IsInfinitive = true;
+      verticalLineAnnotation1.LineColor = System.Drawing.Color.Red;
+      verticalLineAnnotation1.LineDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Dash;
+      verticalLineAnnotation1.Name = "VerticalLineAnnotation";
+      verticalLineAnnotation1.YAxisName = "ChartArea\\rY";
+      this.chart.Annotations.Add(verticalLineAnnotation1);
+      chartArea1.AxisX.IsMarginVisible = false;
+      stripLine1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(223)))), ((int)(((byte)(58)))), ((int)(((byte)(2)))));
+      stripLine2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(223)))), ((int)(((byte)(58)))), ((int)(((byte)(2)))));
+      chartArea1.AxisX.StripLines.Add(stripLine1);
+      chartArea1.AxisX.StripLines.Add(stripLine2);
+      chartArea1.Name = "ChartArea";
+      chartArea1.Position.Auto = false;
+      chartArea1.Position.Height = 90F;
+      chartArea1.Position.Width = 100F;
+      chartArea1.Position.Y = 10F;
+      this.chart.ChartAreas.Add(chartArea1);
+      this.chart.Dock = System.Windows.Forms.DockStyle.Fill;
+      legend1.Alignment = System.Drawing.StringAlignment.Center;
+      legend1.Docking = System.Windows.Forms.DataVisualization.Charting.Docking.Top;
+      legend1.LegendItemOrder = System.Windows.Forms.DataVisualization.Charting.LegendItemOrder.ReversedSeriesOrder;
+      legend1.LegendStyle = System.Windows.Forms.DataVisualization.Charting.LegendStyle.Row;
+      legend1.Name = "Default";
+      this.chart.Legends.Add(legend1);
+      this.chart.Location = new System.Drawing.Point(0, 0);
+      this.chart.Name = "chart";
+      series1.BorderColor = System.Drawing.Color.Red;
+      series1.BorderDashStyle = System.Windows.Forms.DataVisualization.Charting.ChartDashStyle.Dash;
+      series1.ChartArea = "ChartArea";
+      series1.IsVisibleInLegend = false;
+      series1.Legend = "Default";
+      series1.Name = "Series1";
+      series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String;
+      series1.YValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Double;
+      series2.ChartArea = "ChartArea";
+      series2.IsVisibleInLegend = false;
+      series2.Legend = "Default";
+      series2.Name = "Series2";
+      series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String;
+      series3.ChartArea = "ChartArea";
+      series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.BoxPlot;
+      series3.Color = System.Drawing.Color.Black;
+      series3.Legend = "Default";
+      series3.Name = "Series3";
+      series3.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String;
+      series3.YValuesPerPoint = 6;
+      series4.ChartArea = "ChartArea";
+      series4.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.BoxPlot;
+      series4.Legend = "Default";
+      series4.Name = "Series4";
+      series4.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String;
+      series4.YValuesPerPoint = 6;
+      this.chart.Series.Add(series1);
+      this.chart.Series.Add(series2);
+      this.chart.Series.Add(series3);
+      this.chart.Series.Add(series4);
+      this.chart.Size = new System.Drawing.Size(453, 308);
+      this.chart.TabIndex = 0;
+      title1.Alignment = System.Drawing.ContentAlignment.TopCenter;
+      title1.DockedToChartArea = "ChartArea";
+      title1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+      title1.IsDockedInsideChartArea = false;
+      title1.Name = "Title";
+      title1.Text = "[Title]";
+      this.chart.Titles.Add(title1);
+      this.chart.SelectionRangeChanged += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.CursorEventArgs>(this.chart_SelectionRangeChanged);
+      this.chart.PostPaint += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.ChartPaintEventArgs>(this.chart_PostPaint);
+      this.chart.DragDrop += new System.Windows.Forms.DragEventHandler(this.chart_DragDrop);
+      this.chart.DragEnter += new System.Windows.Forms.DragEventHandler(this.chart_DragEnter);
+      this.chart.MouseClick += new System.Windows.Forms.MouseEventHandler(this.chart_MouseClick);
+      this.chart.Resize += new System.EventHandler(this.chart_Resize);
+      // 
+      // FactorGradientChart
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.calculationPendingLabel);
+      this.Controls.Add(this.chart);
+      this.Name = "FactorGradientChart";
+      this.Size = new System.Drawing.Size(453, 308);
+      ((System.ComponentModel.ISupportInitialize)(this.chart)).EndInit();
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private HeuristicLab.Visualization.ChartControlsExtensions.EnhancedChart chart;
+    private System.Windows.Forms.Label calculationPendingLabel;
+    private System.Windows.Forms.Timer calculationPendingTimer;
+    private System.Windows.Forms.ToolTip toolTip;
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/FactorGradientChart.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/FactorGradientChart.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/FactorGradientChart.cs	(revision 15131)
@@ -0,0 +1,565 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+using HeuristicLab.Common;
+using HeuristicLab.MainForm.WindowsForms;
+using HeuristicLab.Visualization.ChartControlsExtensions;
+
+namespace HeuristicLab.Problems.DataAnalysis.Views {
+  public partial class FactorGradientChart : UserControl, IGradientChart {
+    private ModifiableDataset sharedFixedVariables; // used for syncronising variable values between charts
+    private ModifiableDataset internalDataset; // holds the x values for each point drawn
+
+    private CancellationTokenSource cancelCurrentRecalculateSource;
+
+    private readonly List<IRegressionSolution> solutions;
+    private readonly Dictionary<IRegressionSolution, Series> seriesCache;
+    private readonly Dictionary<IRegressionSolution, Series> ciSeriesCache;
+
+    #region Properties
+    public string XAxisTitle {
+      get { return chart.ChartAreas[0].AxisX.Title; }
+      set { chart.ChartAreas[0].AxisX.Title = value; }
+    }
+
+    public string YAxisTitle {
+      get { return chart.ChartAreas[0].AxisY.Title; }
+      set { chart.ChartAreas[0].AxisY.Title = value; }
+    }
+
+    public bool ShowLegend {
+      get { return chart.Legends[0].Enabled; }
+      set { chart.Legends[0].Enabled = value; }
+    }
+    public bool ShowCursor {
+      get { return chart.Annotations[0].Visible; }
+      set {
+        chart.Annotations[0].Visible = value;
+        if (!value) chart.Titles[0].Text = string.Empty;
+      }
+    }
+
+    private int yAxisTicks = 5;
+    public int YAxisTicks {
+      get { return yAxisTicks; }
+      set {
+        if (value != yAxisTicks) {
+          yAxisTicks = value;
+          SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
+          RecalculateInternalDataset();
+        }
+      }
+    }
+    private double? fixedYAxisMin;
+    public double? FixedYAxisMin {
+      get { return fixedYAxisMin; }
+      set {
+        if ((value.HasValue && fixedYAxisMin.HasValue && !value.Value.IsAlmost(fixedYAxisMin.Value)) || (value.HasValue != fixedYAxisMin.HasValue)) {
+          fixedYAxisMin = value;
+          SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
+        }
+      }
+    }
+    private double? fixedYAxisMax;
+    public double? FixedYAxisMax {
+      get { return fixedYAxisMax; }
+      set {
+        if ((value.HasValue && fixedYAxisMax.HasValue && !value.Value.IsAlmost(fixedYAxisMax.Value)) || (value.HasValue != fixedYAxisMax.HasValue)) {
+          fixedYAxisMax = value;
+          SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
+        }
+      }
+    }
+
+    private string freeVariable;
+    public string FreeVariable {
+      get { return freeVariable; }
+      set {
+        if (value == freeVariable) return;
+        if (solutions.Any(s => !s.ProblemData.Dataset.StringVariables.Contains(value))) {
+          throw new ArgumentException("Variable does not exist in the ProblemData of the Solutions.");
+        }
+        freeVariable = value;
+        RecalculateInternalDataset();
+      }
+    }
+
+    private double yMin;
+    public double YMin {
+      get { return yMin; }
+    }
+    private double yMax;
+    public double YMax {
+      get { return yMax; }
+    }
+
+    public bool IsZoomed {
+      get { return chart.ChartAreas[0].AxisX.ScaleView.IsZoomed; }
+    }
+
+    internal ElementPosition InnerPlotPosition {
+      get { return chart.ChartAreas[0].InnerPlotPosition; }
+    }
+    #endregion
+
+    private List<string> variableValues;
+
+    public event EventHandler ChartPostPaint;
+
+    public FactorGradientChart() {
+      InitializeComponent();
+
+      solutions = new List<IRegressionSolution>();
+      seriesCache = new Dictionary<IRegressionSolution, Series>();
+      ciSeriesCache = new Dictionary<IRegressionSolution, Series>();
+
+      // Configure axis
+      chart.CustomizeAllChartAreas();
+      chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
+      chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
+      chart.ChartAreas[0].CursorX.Interval = 0;
+
+      chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
+      chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
+      chart.ChartAreas[0].CursorY.Interval = 0;
+
+      Disposed += GradientChart_Disposed;
+    }
+
+    private void GradientChart_Disposed(object sender, EventArgs e) {
+      if (cancelCurrentRecalculateSource != null)
+        cancelCurrentRecalculateSource.Cancel();
+    }
+
+    public void Configure(IEnumerable<IRegressionSolution> solutions, ModifiableDataset sharedFixedVariables, string freeVariable, IList<string> variableValues, bool initializeAxisRanges = true) {
+      if (!SolutionsCompatible(solutions))
+        throw new ArgumentException("Solutions are not compatible with the problem data.");
+      this.freeVariable = freeVariable;
+      this.variableValues = new List<string>(variableValues);
+
+      this.solutions.Clear();
+      this.solutions.AddRange(solutions);
+
+      // add an event such that whenever a value is changed in the shared dataset, 
+      // this change is reflected in the internal dataset (where the value becomes a whole column)
+      if (this.sharedFixedVariables != null)
+        this.sharedFixedVariables.ItemChanged -= sharedFixedVariables_ItemChanged;
+      this.sharedFixedVariables = sharedFixedVariables;
+      this.sharedFixedVariables.ItemChanged += sharedFixedVariables_ItemChanged;
+
+      RecalculateInternalDataset();
+
+      chart.Series.Clear();
+      seriesCache.Clear();
+      ciSeriesCache.Clear();
+      foreach (var solution in this.solutions) {
+        var series = CreateSeries(solution);
+        seriesCache.Add(solution, series.Item1);
+        if (series.Item2 != null)
+          ciSeriesCache.Add(solution, series.Item2);
+      }
+
+      InitSeriesData();
+      OrderAndColorSeries();
+
+    }
+
+    public async Task RecalculateAsync(bool updateOnFinish = true, bool resetYAxis = true) {
+      if (IsDisposed
+        || sharedFixedVariables == null || !solutions.Any() || string.IsNullOrEmpty(freeVariable)
+        || !variableValues.Any())
+        return;
+
+      calculationPendingTimer.Start();
+
+      // cancel previous recalculate call
+      if (cancelCurrentRecalculateSource != null)
+        cancelCurrentRecalculateSource.Cancel();
+      cancelCurrentRecalculateSource = new CancellationTokenSource();
+      var cancellationToken = cancelCurrentRecalculateSource.Token;
+
+      // Update series
+      try {
+        var limits = await UpdateAllSeriesDataAsync(cancellationToken);
+
+        yMin = limits.Lower;
+        yMax = limits.Upper;
+        // Set y-axis
+        if (resetYAxis)
+          SetupAxis(chart.ChartAreas[0].AxisY, yMin, yMax, YAxisTicks, FixedYAxisMin, FixedYAxisMax);
+
+        calculationPendingTimer.Stop();
+        calculationPendingLabel.Visible = false;
+        if (updateOnFinish)
+          Update();
+      } catch (OperationCanceledException) { } catch (AggregateException ae) {
+        if (!ae.InnerExceptions.Any(e => e is OperationCanceledException))
+          throw;
+      }
+    }
+
+    public void UpdateTitlePosition() {
+      var title = chart.Titles[0];
+      var plotArea = InnerPlotPosition;
+
+      title.Visible = plotArea.Width != 0;
+
+      title.Position.X = plotArea.X + (plotArea.Width / 2);
+    }
+
+    private void SetupAxis(Axis axis, double minValue, double maxValue, int ticks, double? fixedAxisMin, double? fixedAxisMax) {
+      if (minValue < maxValue) {
+        double axisMin, axisMax, axisInterval;
+        ChartUtil.CalculateAxisInterval(minValue, maxValue, ticks, out axisMin, out axisMax, out axisInterval);
+        axis.Minimum = fixedAxisMin ?? axisMin;
+        axis.Maximum = fixedAxisMax ?? axisMax;
+        axis.Interval = (axis.Maximum - axis.Minimum) / ticks;
+      }
+
+      try {
+        chart.ChartAreas[0].RecalculateAxesScale();
+      } catch (InvalidOperationException) {
+        // Can occur if eg. axis min == axis max
+      }
+    }
+
+
+    private void RecalculateInternalDataset() {
+      if (sharedFixedVariables == null)
+        return;
+
+      var factorValues = new List<string>(variableValues);
+
+      var variables = sharedFixedVariables.VariableNames.ToList();
+      var values = new List<IList>();
+      foreach (var varName in variables) {
+        if (varName == FreeVariable) {
+          values.Add(factorValues);
+        } else if (sharedFixedVariables.VariableHasType<double>(varName)) {
+          values.Add(Enumerable.Repeat(sharedFixedVariables.GetDoubleValue(varName, 0), factorValues.Count).ToList());
+        } else if (sharedFixedVariables.VariableHasType<string>(varName)) {
+          values.Add(Enumerable.Repeat(sharedFixedVariables.GetStringValue(varName, 0), factorValues.Count).ToList());
+        }
+      }
+
+      internalDataset = new ModifiableDataset(variables, values);
+    }
+
+    private Tuple<Series, Series> CreateSeries(IRegressionSolution solution) {
+      var series = new Series {
+        ChartType = SeriesChartType.Column,
+        Name = solution.ProblemData.TargetVariable + " " + solutions.IndexOf(solution),
+        XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String
+      };
+      series.LegendText = series.Name;
+
+      Series confidenceIntervalSeries = null;
+      confidenceIntervalSeries = new Series {
+        ChartType = SeriesChartType.BoxPlot,
+        XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.String,
+        Color = Color.Black,
+        YValuesPerPoint = 5,
+        Name = "95% Conf. Interval " + series.Name,
+        IsVisibleInLegend = false
+      };
+      return Tuple.Create(series, confidenceIntervalSeries);
+    }
+
+    private void OrderAndColorSeries() {
+      chart.SuspendRepaint();
+
+      chart.Series.Clear();
+      // Add mean series for applying palette colors
+      foreach (var solution in solutions) {
+        chart.Series.Add(seriesCache[solution]);
+      }
+
+      chart.Palette = ChartColorPalette.BrightPastel;
+      chart.ApplyPaletteColors();
+      chart.Palette = ChartColorPalette.None;
+
+      // Add confidence interval series after its coresponding series for correct z index
+      foreach (var solution in solutions) {
+        Series ciSeries;
+        if (ciSeriesCache.TryGetValue(solution, out ciSeries)) {
+          int idx = chart.Series.IndexOf(seriesCache[solution]);
+          chart.Series.Insert(idx + 1, ciSeries);
+        }
+      }
+
+      chart.ResumeRepaint(true);
+    }
+
+    private async Task<DoubleLimit> UpdateAllSeriesDataAsync(CancellationToken cancellationToken) {
+      var updateTasks = solutions.Select(solution => UpdateSeriesDataAsync(solution, cancellationToken));
+
+      double min = double.MaxValue, max = double.MinValue;
+      foreach (var update in updateTasks) {
+        var limit = await update;
+        if (limit.Lower < min) min = limit.Lower;
+        if (limit.Upper > max) max = limit.Upper;
+      }
+
+      return new DoubleLimit(min, max);
+    }
+
+    private Task<DoubleLimit> UpdateSeriesDataAsync(IRegressionSolution solution, CancellationToken cancellationToken) {
+      return Task.Run(() => {
+        var yvalues = solution.Model.GetEstimatedValues(internalDataset, Enumerable.Range(0, internalDataset.Rows)).ToList();
+
+        double min = double.MaxValue, max = double.MinValue;
+
+        var series = seriesCache[solution];
+        for (int i = 0; i < variableValues.Count; i++) {
+          series.Points[i].SetValueXY(variableValues[i], yvalues[i]);
+          if (yvalues[i] < min) min = yvalues[i];
+          if (yvalues[i] > max) max = yvalues[i];
+        }
+        chart.Invalidate();
+
+        cancellationToken.ThrowIfCancellationRequested();
+
+        var confidenceBoundSolution = solution as IConfidenceRegressionSolution;
+        if (confidenceBoundSolution != null) {
+          var confidenceIntervalSeries = ciSeriesCache[solution];
+          var variances = confidenceBoundSolution.Model.GetEstimatedVariances(internalDataset, Enumerable.Range(0, internalDataset.Rows)).ToList();
+          for (int i = 0; i < variableValues.Count; i++) {
+            var lower = yvalues[i] - 1.96 * Math.Sqrt(variances[i]);
+            var upper = yvalues[i] + 1.96 * Math.Sqrt(variances[i]);
+            confidenceIntervalSeries.Points[i].SetValueXY(variableValues[i], lower, upper, yvalues[i], yvalues[i], yvalues[i]);
+            if (lower < min) min = lower;
+            if (upper > max) max = upper;
+          }
+          chart.Invalidate();
+        }
+
+        cancellationToken.ThrowIfCancellationRequested();
+        return new DoubleLimit(min, max);
+      }, cancellationToken);
+    }
+
+    private void InitSeriesData() {
+      if (internalDataset == null)
+        return;
+
+      foreach (var solution in solutions)
+        InitSeriesData(solution, variableValues);
+    }
+
+    private void InitSeriesData(IRegressionSolution solution, IList<string> values) {
+
+      var series = seriesCache[solution];
+      series.Points.SuspendUpdates();
+      series.Points.Clear();
+      for (int i = 0; i < values.Count; i++) {
+        series.Points.AddXY(values[i], 0.0);
+        series.Points.Last().ToolTip = values[i];
+      }
+
+      UpdateAllSeriesStyles(variableValues.IndexOf(sharedFixedVariables.GetStringValue(FreeVariable, 0)));
+      series.Points.ResumeUpdates();
+
+      Series confidenceIntervalSeries;
+      if (ciSeriesCache.TryGetValue(solution, out confidenceIntervalSeries)) {
+        confidenceIntervalSeries.Points.SuspendUpdates();
+        confidenceIntervalSeries.Points.Clear();
+        for (int i = 0; i < values.Count; i++)
+          confidenceIntervalSeries.Points.AddXY(values[i], 0.0, 0.0, 0.0, 0.0, 0.0);
+        confidenceIntervalSeries.Points.ResumeUpdates();
+      }
+    }
+
+    public async Task AddSolutionAsync(IRegressionSolution solution) {
+      if (!SolutionsCompatible(solutions.Concat(new[] { solution })))
+        throw new ArgumentException("The solution is not compatible with the problem data.");
+      if (solutions.Contains(solution))
+        return;
+
+      solutions.Add(solution);
+
+      var series = CreateSeries(solution);
+      seriesCache.Add(solution, series.Item1);
+      if (series.Item2 != null)
+        ciSeriesCache.Add(solution, series.Item2);
+
+      InitSeriesData(solution, variableValues);
+      OrderAndColorSeries();
+
+      await RecalculateAsync();
+      var args = new EventArgs<IRegressionSolution>(solution);
+      OnSolutionAdded(this, args);
+    }
+
+    public async Task RemoveSolutionAsync(IRegressionSolution solution) {
+      throw new NotSupportedException();
+    }
+
+    private static bool SolutionsCompatible(IEnumerable<IRegressionSolution> solutions) {
+      var refSolution = solutions.First();
+      var refSolVars = refSolution.ProblemData.Dataset.VariableNames;
+      foreach (var solution in solutions.Skip(1)) {
+        var variables1 = solution.ProblemData.Dataset.VariableNames;
+        if (!variables1.All(refSolVars.Contains))
+          return false;
+
+        foreach (var factorVar in variables1.Where(solution.ProblemData.Dataset.VariableHasType<string>)) {
+          var distinctVals = refSolution.ProblemData.Dataset.GetStringValues(factorVar).Distinct();
+          if (solution.ProblemData.Dataset.GetStringValues(factorVar).Any(val => !distinctVals.Contains(val))) return false;
+        }
+      }
+      return true;
+    }
+
+    #region Events
+    public event EventHandler<EventArgs<IRegressionSolution>> SolutionAdded;
+    public void OnSolutionAdded(object sender, EventArgs<IRegressionSolution> args) {
+      var added = SolutionAdded;
+      if (added == null) return;
+      added(sender, args);
+    }
+
+    public event EventHandler<EventArgs<IRegressionSolution>> SolutionRemoved;
+    public void OnSolutionRemoved(object sender, EventArgs<IRegressionSolution> args) {
+      var removed = SolutionRemoved;
+      if (removed == null) return;
+      removed(sender, args);
+    }
+
+    public event EventHandler VariableValueChanged;
+    public void OnVariableValueChanged(object sender, EventArgs args) {
+      var changed = VariableValueChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+
+    public event EventHandler ZoomChanged;
+    public void OnZoomChanged(object sender, EventArgs args) {
+      var changed = ZoomChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+
+    private void sharedFixedVariables_ItemChanged(object o, EventArgs<int, int> e) {
+      if (o != sharedFixedVariables) return;
+      var variables = sharedFixedVariables.VariableNames.ToList();
+      var rowIndex = e.Value;
+      var columnIndex = e.Value2;
+
+      var variableName = variables[columnIndex];
+      if (variableName == FreeVariable) return;
+      if (internalDataset.VariableHasType<double>(variableName)) {
+        var v = sharedFixedVariables.GetDoubleValue(variableName, rowIndex);
+        var values = new List<double>(Enumerable.Repeat(v, internalDataset.Rows));
+        internalDataset.ReplaceVariable(variableName, values);
+      } else if (internalDataset.VariableHasType<string>(variableName)) {
+        var v = sharedFixedVariables.GetStringValue(variableName, rowIndex);
+        var values = new List<String>(Enumerable.Repeat(v, internalDataset.Rows));
+        internalDataset.ReplaceVariable(variableName, values);
+      } else {
+        // unsupported type 
+        throw new NotSupportedException();
+      }
+    }
+
+    private async void chart_DragDrop(object sender, DragEventArgs e) {
+      var data = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
+      if (data != null) {
+        var solution = data as IRegressionSolution;
+        if (!solutions.Contains(solution))
+          await AddSolutionAsync(solution);
+      }
+    }
+    private void chart_DragEnter(object sender, DragEventArgs e) {
+      if (!e.Data.GetDataPresent(HeuristicLab.Common.Constants.DragDropDataFormat)) return;
+      e.Effect = DragDropEffects.None;
+
+      var data = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
+      var regressionSolution = data as IRegressionSolution;
+      if (regressionSolution != null) {
+        e.Effect = DragDropEffects.Copy;
+      }
+    }
+
+    private void calculationPendingTimer_Tick(object sender, EventArgs e) {
+      calculationPendingLabel.Visible = true;
+      Update();
+    }
+
+    private void chart_SelectionRangeChanged(object sender, CursorEventArgs e) {
+      OnZoomChanged(this, EventArgs.Empty);
+    }
+
+    private void chart_Resize(object sender, EventArgs e) {
+      UpdateTitlePosition();
+    }
+
+    private void chart_PostPaint(object sender, ChartPaintEventArgs e) {
+      if (ChartPostPaint != null)
+        ChartPostPaint(this, EventArgs.Empty);
+    }
+    #endregion
+
+    private void chart_MouseClick(object sender, MouseEventArgs e) {
+      var hitTestResult = chart.HitTest(e.X, e.Y, ChartElementType.DataPoint);
+      if (hitTestResult != null && hitTestResult.ChartElementType == ChartElementType.DataPoint) {
+        var series = hitTestResult.Series;
+        var dataPoint = (DataPoint)hitTestResult.Object;
+        var idx = series.Points.IndexOf(dataPoint);
+        UpdateSelectedValue(variableValues[idx]);
+
+        UpdateAllSeriesStyles(idx);
+      }
+    }
+
+    private void UpdateAllSeriesStyles(int selectedValueIndex) {
+      if (ShowCursor) {
+        chart.Titles[0].Text = FreeVariable + " : " + variableValues[selectedValueIndex];
+        chart.Update();
+      }
+      foreach (var s in seriesCache.Values) {
+        if (s.ChartType == SeriesChartType.Column)
+          for (int i = 0; i < s.Points.Count; i++) {
+            if (i != selectedValueIndex) {
+              s.Points[i].BorderDashStyle = ChartDashStyle.NotSet;
+            } else {
+              s.Points[i].BorderDashStyle = ChartDashStyle.Dash;
+              s.Points[i].BorderColor = Color.Red;
+            }
+          }
+      }
+    }
+
+    private void UpdateSelectedValue(string variableValue) {
+      sharedFixedVariables.SetVariableValue(variableValue, FreeVariable, 0);
+      OnVariableValueChanged(this, EventArgs.Empty);
+    }
+  }
+}
+
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/GradientChart.Designer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/GradientChart.Designer.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/GradientChart.Designer.cs	(revision 15131)
@@ -25,5 +25,4 @@
     private void InitializeComponent() {
       this.components = new System.ComponentModel.Container();
-      System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GradientChart));
       System.Windows.Forms.DataVisualization.Charting.VerticalLineAnnotation verticalLineAnnotation1 = new System.Windows.Forms.DataVisualization.Charting.VerticalLineAnnotation();
       System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/GradientChart.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/GradientChart.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Controls/GradientChart.cs	(revision 15131)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Drawing;
@@ -34,5 +35,5 @@
 
 namespace HeuristicLab.Problems.DataAnalysis.Views {
-  public partial class GradientChart : UserControl {
+  public partial class GradientChart : UserControl, IGradientChart {
     private ModifiableDataset sharedFixedVariables; // used for syncronising variable values between charts
     private ModifiableDataset internalDataset; // holds the x values for each point drawn
@@ -319,7 +320,5 @@
         if (updateOnFinish)
           Update();
-      }
-      catch (OperationCanceledException) { }
-      catch (AggregateException ae) {
+      } catch (OperationCanceledException) { } catch (AggregateException ae) {
         if (!ae.InnerExceptions.Any(e => e is OperationCanceledException))
           throw;
@@ -347,6 +346,5 @@
       try {
         chart.ChartAreas[0].RecalculateAxesScale();
-      }
-      catch (InvalidOperationException) {
+      } catch (InvalidOperationException) {
         // Can occur if eg. axis min == axis max
       }
@@ -381,11 +379,20 @@
         xvalues.Add(xmin + i * step);
 
-      var variables = sharedFixedVariables.DoubleVariables.ToList();
-      internalDataset = new ModifiableDataset(variables,
-        variables.Select(x => x == FreeVariable
-          ? xvalues
-          : Enumerable.Repeat(sharedFixedVariables.GetDoubleValue(x, 0), xvalues.Count).ToList()
-        )
-      );
+      if (sharedFixedVariables == null)
+        return;
+
+      var variables = sharedFixedVariables.VariableNames.ToList();
+      var values = new List<IList>();
+      foreach (var varName in variables) {
+        if (varName == FreeVariable) {
+          values.Add(xvalues);
+        } else if (sharedFixedVariables.VariableHasType<double>(varName)) {
+          values.Add(Enumerable.Repeat(sharedFixedVariables.GetDoubleValue(varName, 0), xvalues.Count).ToList());
+        } else if (sharedFixedVariables.VariableHasType<string>(varName)) {
+          values.Add(Enumerable.Repeat(sharedFixedVariables.GetStringValue(varName, 0), xvalues.Count).ToList());
+        }
+      }
+
+      internalDataset = new ModifiableDataset(variables, values);
     }
 
@@ -552,12 +559,14 @@
 
     private static bool SolutionsCompatible(IEnumerable<IRegressionSolution> solutions) {
-      foreach (var solution1 in solutions) {
-        var variables1 = solution1.ProblemData.Dataset.DoubleVariables;
-        foreach (var solution2 in solutions) {
-          if (solution1 == solution2)
-            continue;
-          var variables2 = solution2.ProblemData.Dataset.DoubleVariables;
-          if (!variables1.All(variables2.Contains))
-            return false;
+      var refSolution = solutions.First();
+      var refSolVars = refSolution.ProblemData.Dataset.VariableNames;
+      foreach (var solution in solutions.Skip(1)) {
+        var variables1 = solution.ProblemData.Dataset.VariableNames;
+        if (!variables1.All(refSolVars.Contains))
+          return false;
+
+        foreach (var factorVar in variables1.Where(solution.ProblemData.Dataset.VariableHasType<string>)) {
+          var distinctVals = refSolution.ProblemData.Dataset.GetStringValues(factorVar).Distinct();
+          if (solution.ProblemData.Dataset.GetStringValues(factorVar).Any(val => !distinctVals.Contains(val))) return false;
         }
       }
@@ -608,5 +617,5 @@
     private void sharedFixedVariables_ItemChanged(object o, EventArgs<int, int> e) {
       if (o != sharedFixedVariables) return;
-      var variables = sharedFixedVariables.DoubleVariables.ToList();
+      var variables = sharedFixedVariables.VariableNames.ToList();
       var rowIndex = e.Value;
       var columnIndex = e.Value2;
@@ -614,7 +623,16 @@
       var variableName = variables[columnIndex];
       if (variableName == FreeVariable) return;
-      var v = sharedFixedVariables.GetDoubleValue(variableName, rowIndex);
-      var values = new List<double>(Enumerable.Repeat(v, DrawingSteps));
-      internalDataset.ReplaceVariable(variableName, values);
+      if (internalDataset.VariableHasType<double>(variableName)) {
+        var v = sharedFixedVariables.GetDoubleValue(variableName, rowIndex);
+        var values = new List<double>(Enumerable.Repeat(v, internalDataset.Rows));
+        internalDataset.ReplaceVariable(variableName, values);
+      } else if (internalDataset.VariableHasType<string>(variableName)) {
+        var v = sharedFixedVariables.GetStringValue(variableName, rowIndex);
+        var values = new List<String>(Enumerable.Repeat(v, internalDataset.Rows));
+        internalDataset.ReplaceVariable(variableName, values);
+      } else {
+        // unsupported type 
+        throw new NotSupportedException();
+      }
     }
 
@@ -635,5 +653,5 @@
       UpdateCursor();
     }
-    void UpdateCursor() {
+    private void UpdateCursor() {
       var x = VerticalLineAnnotation.X;
       sharedFixedVariables.SetVariableValue(x, FreeVariable, 0);
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/HeuristicLab.Problems.DataAnalysis.Views-3.4.csproj
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/HeuristicLab.Problems.DataAnalysis.Views-3.4.csproj	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/HeuristicLab.Problems.DataAnalysis.Views-3.4.csproj	(revision 15131)
@@ -132,4 +132,10 @@
       <DependentUpon>DensityTrackbar.cs</DependentUpon>
     </Compile>
+    <Compile Include="Controls\FactorGradientChart.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Controls\FactorGradientChart.Designer.cs">
+      <DependentUpon>FactorGradientChart.cs</DependentUpon>
+    </Compile>
     <Compile Include="FeatureCorrelation\TimeframeFeatureCorrelationCalculator.cs" />
     <Compile Include="FeatureCorrelation\AbstractFeatureCorrelationCalculator.cs" />
@@ -207,4 +213,5 @@
       <DependentUpon>GradientChartConfigurationDialog.cs</DependentUpon>
     </Compile>
+    <Compile Include="Interfaces\IGradientChart.cs" />
     <Compile Include="Interfaces\IDataPreprocessorStarter.cs" />
     <Compile Include="MenuItems\ChangeDataOfOptimizersMenuItem.cs" />
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Interfaces/IGradientChart.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Interfaces/IGradientChart.cs	(revision 15131)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Interfaces/IGradientChart.cs	(revision 15131)
@@ -0,0 +1,37 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 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.Threading.Tasks;
+
+namespace HeuristicLab.Problems.DataAnalysis.Views {
+  public interface IGradientChart {
+    Task RecalculateAsync(bool updateOnFinish, bool resetYAxis);
+    double YMin { get;  }
+    double YMax { get; }
+    double? FixedYAxisMin { get; set; }
+    double? FixedYAxisMax { get; set; }
+    string YAxisTitle { get; set; }
+    Task AddSolutionAsync(IRegressionSolution solution);
+    Task RemoveSolutionAsync(IRegressionSolution solution);
+    bool IsZoomed { get; }
+    void Refresh();
+  }
+}
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionTargetResponseGradientView.cs	(revision 15131)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Drawing;
@@ -35,5 +36,5 @@
   [Content(typeof(IRegressionSolution))]
   public partial class RegressionSolutionTargetResponseGradientView : DataAnalysisSolutionEvaluationView {
-    private readonly Dictionary<string, GradientChart> gradientCharts;
+    private readonly Dictionary<string, IGradientChart> gradientCharts;
     private readonly Dictionary<string, DensityChart> densityCharts;
     private readonly Dictionary<string, Panel> groupingPanels;
@@ -49,5 +50,5 @@
       }
     }
-    private IEnumerable<GradientChart> VisibleGradientCharts {
+    private IEnumerable<IGradientChart> VisibleGradientCharts {
       get { return VisibleVariables.Select(v => gradientCharts[v]); }
     }
@@ -61,5 +62,5 @@
     public RegressionSolutionTargetResponseGradientView() {
       InitializeComponent();
-      gradientCharts = new Dictionary<string, GradientChart>();
+      gradientCharts = new Dictionary<string, IGradientChart>();
       densityCharts = new Dictionary<string, DensityChart>();
       groupingPanels = new Dictionary<string, Panel>();
@@ -120,9 +121,18 @@
 
 
-      var variableValues = allowedInputVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
+      var doubleVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<double>);
+      var doubleVariableValues = (IEnumerable<IList>)doubleVariables.Select(x => new List<double> { problemData.Dataset.GetDoubleValues(x, problemData.TrainingIndices).Median() });
+
+      var factorVariables = allowedInputVariables.Where(problemData.Dataset.VariableHasType<string>);
+      var factorVariableValues = (IEnumerable<IList>)factorVariables.Select(x => new List<string> {
+        problemData.Dataset.GetStringValues(x, problemData.TrainingIndices)
+        .GroupBy(val => val).OrderByDescending(g => g.Count()).First().Key // most frequent value
+      });
+
       if (sharedFixedVariables != null)
         sharedFixedVariables.ItemChanged -= SharedFixedVariables_ItemChanged;
-      sharedFixedVariables = new ModifiableDataset(allowedInputVariables, variableValues);
-      // ItemChanged eventhandler is registered later, after creating the gradient charts
+
+      sharedFixedVariables = new ModifiableDataset(doubleVariables.Concat(factorVariables), doubleVariableValues.Concat(factorVariableValues));
+
 
       // create controls
@@ -130,5 +140,5 @@
       densityCharts.Clear();
       groupingPanels.Clear();
-      foreach (var variableName in allowedInputVariables) {
+      foreach (var variableName in doubleVariables) {
         var gradientChart = CreateGradientChart(variableName, sharedFixedVariables);
         gradientCharts.Add(variableName, gradientChart);
@@ -158,5 +168,5 @@
         // Initially, the inner plot areas are not initialized for hidden charts (scollpanel, ...)
         // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
-        gradientChart.ChartPostPaint += OnGradientChartOnChartPostPaint;
+        gradientChart.ChartPostPaint += OnGradientChartPostPaint;
 
         var panel = new Panel() {
@@ -170,5 +180,43 @@
         groupingPanels.Add(variableName, panel);
       }
-
+      foreach (var variableName in factorVariables) {
+        var gradientChart = CreateFactorGradientChart(variableName, sharedFixedVariables);
+        gradientCharts.Add(variableName, gradientChart);
+
+        var densityChart = new DensityChart() {
+          Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right,
+          Margin = Padding.Empty,
+          Height = 12,
+          Visible = false,
+          Top = (int)(gradientChart.Height * 0.1),
+        };
+        densityCharts.Add(variableName, densityChart);
+        gradientChart.ZoomChanged += (o, e) => {
+          var gradient = (FactorGradientChart)o;
+          var density = densityCharts[gradient.FreeVariable];
+          density.Visible = densityComboBox.SelectedIndex != 0 && !gradient.IsZoomed;
+          if (density.Visible)
+            UpdateDensityChart(density, gradient.FreeVariable);
+        };
+        gradientChart.SizeChanged += (o, e) => {
+          var gradient = (FactorGradientChart)o;
+          var density = densityCharts[gradient.FreeVariable];
+          density.Top = (int)(gradient.Height * 0.1);
+        };
+
+        // Initially, the inner plot areas are not initialized for hidden charts (scollpanel, ...)
+        // This event handler listens for the paint event once (where everything is already initialized) to do some manual layouting.
+        gradientChart.ChartPostPaint += OnFactorGradientChartPostPaint;
+
+        var panel = new Panel() {
+          Dock = DockStyle.Fill,
+          Margin = Padding.Empty,
+          BackColor = Color.White
+        };
+
+        panel.Controls.Add(densityChart);
+        panel.Controls.Add(gradientChart);
+        groupingPanels.Add(variableName, panel);
+      }
       // update variable list
       variableListView.ItemChecked -= variableListView_ItemChecked;
@@ -196,5 +244,6 @@
     }
 
-    private void OnGradientChartOnChartPostPaint(object o, EventArgs e) {
+
+    private void OnGradientChartPostPaint(object o, EventArgs e) {
       var gradient = (GradientChart)o;
       var density = densityCharts[gradient.FreeVariable];
@@ -209,5 +258,21 @@
       // removed after succesful layouting due to performance reasons
       if (gcPlotPosition.Width != 0)
-        gradient.ChartPostPaint -= OnGradientChartOnChartPostPaint;
+        gradient.ChartPostPaint -= OnGradientChartPostPaint;
+    }
+
+    private void OnFactorGradientChartPostPaint(object o, EventArgs e) {
+      var gradient = (FactorGradientChart)o;
+      var density = densityCharts[gradient.FreeVariable];
+
+      density.Width = gradient.Width;
+
+      var gcPlotPosition = gradient.InnerPlotPosition;
+      density.Left = (int)(gcPlotPosition.X / 100.0 * gradient.Width);
+      density.Width = (int)(gcPlotPosition.Width / 100.0 * gradient.Width);
+      gradient.UpdateTitlePosition();
+
+      // removed after succesful layouting due to performance reasons
+      if (gcPlotPosition.Width != 0)
+        gradient.ChartPostPaint -= OnFactorGradientChartPostPaint;
     }
 
@@ -215,5 +280,5 @@
       foreach (var variable in VisibleVariables) {
         var gradientChart = gradientCharts[variable];
-        await gradientChart.RecalculateAsync();
+        await gradientChart.RecalculateAsync(false, false);
       }
       gradientChartTableLayout.SuspendLayout();
@@ -224,6 +289,8 @@
       gradientChartTableLayout.Refresh();
       foreach (var variable in VisibleVariables) {
-        var densityChart = densityCharts[variable];
-        UpdateDensityChart(densityChart, variable);
+        DensityChart densityChart;
+        if (densityCharts.TryGetValue(variable, out densityChart)) {
+          UpdateDensityChart(densityChart, variable);
+        }
       }
     }
@@ -238,7 +305,9 @@
       };
       gradientChart.VariableValueChanged += async (o, e) => {
-        var recalculations = VisibleGradientCharts.Except(new[] { (GradientChart)o }).Select(async chart => {
-          await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
-        }).ToList();
+        var recalculations = VisibleGradientCharts
+          .Except(new[] { (IGradientChart)o })
+          .Select(async chart => {
+            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
+          }).ToList();
         await Task.WhenAll(recalculations);
 
@@ -251,5 +320,29 @@
       return gradientChart;
     }
-
+    private FactorGradientChart CreateFactorGradientChart(string variableName, ModifiableDataset sharedFixedVariables) {
+      var gradientChart = new FactorGradientChart {
+        Dock = DockStyle.Fill,
+        Margin = Padding.Empty,
+        ShowLegend = false,
+        ShowCursor = true,
+        YAxisTicks = 5,
+      };
+      gradientChart.VariableValueChanged += async (o, e) => {
+        var recalculations = VisibleGradientCharts
+          .Except(new[] { (FactorGradientChart)o })
+          .Select(async chart => {
+            await chart.RecalculateAsync(updateOnFinish: false, resetYAxis: false);
+          }).ToList();
+        await Task.WhenAll(recalculations);
+
+        if (recalculations.All(t => t.IsCompleted))
+          SetupYAxis();
+      };
+      var variableValues = Content.ProblemData.Dataset.GetStringValues(variableName).Distinct().OrderBy(n => n).ToList();
+      gradientChart.Configure(new[] { Content }, sharedFixedVariables, variableName, variableValues);
+      gradientChart.SolutionAdded += gradientChart_SolutionAdded;
+      gradientChart.SolutionRemoved += gradientChart_SolutionRemoved;
+      return gradientChart;
+    }
     private void SetupYAxis() {
       double axisMin, axisMax;
@@ -345,5 +438,5 @@
       if (item.Checked) {
         tl.Controls.Add(chartsPanel);
-        await gradientChart.RecalculateAsync();
+        await gradientChart.RecalculateAsync(false, false);
       } else {
         tl.Controls.Remove(chartsPanel);
@@ -409,21 +502,39 @@
         indices = GetDensityIndices(densityComboBox.SelectedIndex).ToList();
       }
-      var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
-      var gradientChart = gradientCharts[variable];
-      var min = gradientChart.FixedXAxisMin;
-      var max = gradientChart.FixedXAxisMax;
-      var buckets = gradientChart.DrawingSteps;
-      if (min.HasValue && max.HasValue) {
-        densityChart.UpdateChart(data, min.Value, max.Value, buckets);
-        densityChart.Width = gradientChart.Width;
-
-        var gcPlotPosition = gradientChart.InnerPlotPosition;
-        densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
-        densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
-
-        densityChart.Visible = true;
-      }
-
-      gradientChart.UpdateTitlePosition();
+      if (Content.ProblemData.Dataset.VariableHasType<double>(variable)) {
+        var data = Content.ProblemData.Dataset.GetDoubleValues(variable, indices).ToList();
+        var gradientChart = gradientCharts[variable] as GradientChart;
+        if (gradientChart != null) {
+          var min = gradientChart.FixedXAxisMin;
+          var max = gradientChart.FixedXAxisMax;
+          var buckets = gradientChart.DrawingSteps;
+          if (min.HasValue && max.HasValue) {
+            densityChart.UpdateChart(data, min.Value, max.Value, buckets);
+            densityChart.Width = gradientChart.Width;
+
+            var gcPlotPosition = gradientChart.InnerPlotPosition;
+            densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
+            densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
+
+            densityChart.Visible = true;
+          }
+          gradientChart.UpdateTitlePosition();
+        }
+      } else if (Content.ProblemData.Dataset.VariableHasType<string>(variable)) {
+        var data = Content.ProblemData.Dataset.GetStringValues(variable).ToList();
+        var gradientChart = gradientCharts[variable] as FactorGradientChart;
+        if (gradientChart != null) {
+          densityChart.UpdateChart(data);
+          densityChart.Width = gradientChart.Width;
+
+          var gcPlotPosition = gradientChart.InnerPlotPosition;
+          densityChart.Left = (int)(gcPlotPosition.X / 100.0 * gradientChart.Width);
+          densityChart.Width = (int)(gcPlotPosition.Width / 100.0 * gradientChart.Width);
+
+          densityChart.Visible = true;
+
+          gradientChart.UpdateTitlePosition();
+        }
+      }
     }
 
@@ -448,5 +559,5 @@
         var densityChart = densityCharts[variable];
         // recalculate and refresh
-        await gradientChart.RecalculateAsync();
+        await gradientChart.RecalculateAsync(false, false);
         gradientChart.Refresh();
         UpdateDensityChart(densityChart, variable);
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.Designer.cs	(revision 15131)
@@ -47,6 +47,8 @@
       this.dataPartitionComboBox = new System.Windows.Forms.ComboBox();
       this.dataPartitionLabel = new System.Windows.Forms.Label();
-      this.replacementLabel = new System.Windows.Forms.Label();
+      this.numericVarReplacementLabel = new System.Windows.Forms.Label();
       this.replacementComboBox = new System.Windows.Forms.ComboBox();
+      this.factorVarReplacementLabel = new System.Windows.Forms.Label();
+      this.factorVarReplComboBox = new System.Windows.Forms.ComboBox();
       this.SuspendLayout();
       // 
@@ -58,9 +60,9 @@
       this.variableImactsArrayView.Caption = "StringConvertibleArray View";
       this.variableImactsArrayView.Content = null;
-      this.variableImactsArrayView.Location = new System.Drawing.Point(3, 59);
+      this.variableImactsArrayView.Location = new System.Drawing.Point(3, 84);
       this.variableImactsArrayView.Name = "variableImactsArrayView";
       this.variableImactsArrayView.ReadOnly = true;
-      this.variableImactsArrayView.Size = new System.Drawing.Size(304, 223);
-      this.variableImactsArrayView.TabIndex = 4;
+      this.variableImactsArrayView.Size = new System.Drawing.Size(363, 278);
+      this.variableImactsArrayView.TabIndex = 2;
       // 
       // dataPartitionComboBox
@@ -71,5 +73,5 @@
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.DataPartitionEnum.Test,
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.DataPartitionEnum.All});
-      this.dataPartitionComboBox.Location = new System.Drawing.Point(82, 3);
+      this.dataPartitionComboBox.Location = new System.Drawing.Point(197, 3);
       this.dataPartitionComboBox.Name = "dataPartitionComboBox";
       this.dataPartitionComboBox.Size = new System.Drawing.Size(121, 21);
@@ -86,12 +88,12 @@
       this.dataPartitionLabel.Text = "Data partition:";
       // 
-      // replacementLabel
+      // numericVarReplacementLabel
       // 
-      this.replacementLabel.AutoSize = true;
-      this.replacementLabel.Location = new System.Drawing.Point(3, 35);
-      this.replacementLabel.Name = "replacementLabel";
-      this.replacementLabel.Size = new System.Drawing.Size(73, 13);
-      this.replacementLabel.TabIndex = 2;
-      this.replacementLabel.Text = "Replacement:";
+      this.numericVarReplacementLabel.AutoSize = true;
+      this.numericVarReplacementLabel.Location = new System.Drawing.Point(3, 33);
+      this.numericVarReplacementLabel.Name = "numericVarReplacementLabel";
+      this.numericVarReplacementLabel.Size = new System.Drawing.Size(173, 13);
+      this.numericVarReplacementLabel.TabIndex = 2;
+      this.numericVarReplacementLabel.Text = "Replacement for numeric variables:";
       // 
       // replacementComboBox
@@ -103,5 +105,5 @@
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum.Noise,
             HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum.Shuffle});
-      this.replacementComboBox.Location = new System.Drawing.Point(82, 32);
+      this.replacementComboBox.Location = new System.Drawing.Point(197, 30);
       this.replacementComboBox.Name = "replacementComboBox";
       this.replacementComboBox.Size = new System.Drawing.Size(121, 21);
@@ -109,15 +111,39 @@
       this.replacementComboBox.SelectedIndexChanged += new System.EventHandler(this.replacementComboBox_SelectedIndexChanged);
       // 
+      // factorVarReplacementLabel
+      // 
+      this.factorVarReplacementLabel.AutoSize = true;
+      this.factorVarReplacementLabel.Location = new System.Drawing.Point(3, 60);
+      this.factorVarReplacementLabel.Name = "factorVarReplacementLabel";
+      this.factorVarReplacementLabel.Size = new System.Drawing.Size(188, 13);
+      this.factorVarReplacementLabel.TabIndex = 0;
+      this.factorVarReplacementLabel.Text = "Replacement for categorical variables:";
+      // 
+      // factorVarReplComboBox
+      // 
+      this.factorVarReplComboBox.FormattingEnabled = true;
+      this.factorVarReplComboBox.Items.AddRange(new object[] {
+            HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum.Best,
+            HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum.Mode,
+            HeuristicLab.Problems.DataAnalysis.RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum.Shuffle});
+      this.factorVarReplComboBox.Location = new System.Drawing.Point(197, 57);
+      this.factorVarReplComboBox.Name = "factorVarReplComboBox";
+      this.factorVarReplComboBox.Size = new System.Drawing.Size(121, 21);
+      this.factorVarReplComboBox.TabIndex = 1;
+      this.factorVarReplComboBox.SelectedIndexChanged += new System.EventHandler(this.replacementComboBox_SelectedIndexChanged);
+      // 
       // RegressionSolutionVariableImpactsView
       // 
       this.AllowDrop = true;
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
+      this.Controls.Add(this.factorVarReplComboBox);
+      this.Controls.Add(this.factorVarReplacementLabel);
       this.Controls.Add(this.replacementComboBox);
-      this.Controls.Add(this.replacementLabel);
+      this.Controls.Add(this.numericVarReplacementLabel);
       this.Controls.Add(this.dataPartitionLabel);
       this.Controls.Add(this.dataPartitionComboBox);
       this.Controls.Add(this.variableImactsArrayView);
       this.Name = "RegressionSolutionVariableImpactsView";
-      this.Size = new System.Drawing.Size(310, 285);
+      this.Size = new System.Drawing.Size(369, 365);
       this.ResumeLayout(false);
       this.PerformLayout();
@@ -130,6 +156,8 @@
     private System.Windows.Forms.ComboBox dataPartitionComboBox;
     private System.Windows.Forms.Label dataPartitionLabel;
-    private System.Windows.Forms.Label replacementLabel;
+    private System.Windows.Forms.Label numericVarReplacementLabel;
     private System.Windows.Forms.ComboBox replacementComboBox;
+    private System.Windows.Forms.Label factorVarReplacementLabel;
+    private System.Windows.Forms.ComboBox factorVarReplComboBox;
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis.Views/3.4/Regression/RegressionSolutionVariableImpactsView.cs	(revision 15131)
@@ -43,4 +43,5 @@
       this.dataPartitionComboBox.SelectedIndex = 0;
       this.replacementComboBox.SelectedIndex = 0;
+      this.factorVarReplComboBox.SelectedIndex = 0;
     }
 
@@ -76,9 +77,15 @@
 
     private void UpdateVariableImpacts() {
-      if (Content == null || replacementComboBox.SelectedIndex < 0 || dataPartitionComboBox.SelectedIndex < 0) return;
+      if (Content == null || replacementComboBox.SelectedIndex < 0
+        || factorVarReplComboBox.SelectedIndex < 0
+        || dataPartitionComboBox.SelectedIndex < 0) return;
       var mainForm = (MainForm.WindowsForms.MainForm)MainFormManager.MainForm;
       variableImactsArrayView.Caption = Content.Name + " Variable Impacts";
       var replMethod =
-         (RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum)replacementComboBox.Items[replacementComboBox.SelectedIndex];
+         (RegressionSolutionVariableImpactsCalculator.ReplacementMethodEnum)
+           replacementComboBox.Items[replacementComboBox.SelectedIndex];
+      var factorReplMethod =
+        (RegressionSolutionVariableImpactsCalculator.FactorReplacementMethodEnum)
+          factorVarReplComboBox.Items[factorVarReplComboBox.SelectedIndex];
       var dataPartition =
         (RegressionSolutionVariableImpactsCalculator.DataPartitionEnum)dataPartitionComboBox.SelectedItem;
@@ -88,5 +95,5 @@
           mainForm.AddOperationProgressToView(this, "Calculating variable impacts for " + Content.Name);
 
-          var impacts = RegressionSolutionVariableImpactsCalculator.CalculateImpacts(Content, dataPartition, replMethod);
+          var impacts = RegressionSolutionVariableImpactsCalculator.CalculateImpacts(Content, dataPartition, replMethod, factorReplMethod);
           var impactArray = new DoubleArray(impacts.Select(i => i.Item2).ToArray());
           impactArray.ElementNames = impacts.Select(i => i.Item1);
Index: /stable/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis/3.4/Dataset.cs	(revision 15131)
@@ -168,4 +168,9 @@
       get { return variableValues.Where(p => p.Value is List<double>).Select(p => p.Key); }
     }
+
+    public IEnumerable<string> StringVariables {
+      get { return variableValues.Where(p => p.Value is List<string>).Select(p => p.Key); }
+    }
+
     public IEnumerable<double> GetDoubleValues(string variableName) {
       return GetValues<double>(variableName);
@@ -189,4 +194,18 @@
       return GetValues<double>(variableName, rows);
     }
+
+    public string GetStringValue(string variableName, int row) {
+      var values = GetValues<string>(variableName);
+      return values[row];
+    }
+
+    public IEnumerable<string> GetStringValues(string variableName, IEnumerable<int> rows) {
+      return GetValues<string>(variableName, rows);
+    }
+    public ReadOnlyCollection<string> GetReadOnlyStringValues(string variableName) {
+      var values = GetValues<string>(variableName);
+      return values.AsReadOnly();
+    }
+
     private IEnumerable<T> GetValues<T>(string variableName, IEnumerable<int> rows) {
       var values = GetValues<T>(variableName);
Index: /stable/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/DataAnalysisProblemData.cs	(revision 15131)
@@ -131,10 +131,11 @@
     protected DataAnalysisProblemData(IDataset dataset, IEnumerable<string> allowedInputVariables, IEnumerable<ITransformation> transformations = null) {
       if (dataset == null) throw new ArgumentNullException("The dataset must not be null.");
-      if (allowedInputVariables == null) throw new ArgumentNullException("The allowedInputVariables must not be null.");
-
-      if (allowedInputVariables.Except(dataset.DoubleVariables).Any())
-        throw new ArgumentException("All allowed input variables must be present in the dataset and of type double.");
-
-      var inputVariables = new CheckedItemList<StringValue>(dataset.DoubleVariables.Select(x => new StringValue(x)));
+      if (allowedInputVariables == null) throw new ArgumentNullException("The allowed input variables must not be null.");
+
+      if (allowedInputVariables.Except(dataset.DoubleVariables).Except(dataset.StringVariables).Any())
+        throw new ArgumentException("All allowed input variables must be present in the dataset and of type double or string.");
+
+      var variables = dataset.VariableNames.Where(variable => dataset.VariableHasType<double>(variable) || dataset.VariableHasType<string>(variable));
+      var inputVariables = new CheckedItemList<StringValue>(variables.Select(x => new StringValue(x)));
       foreach (StringValue x in inputVariables)
         inputVariables.SetItemCheckedState(x, allowedInputVariables.Contains(x.Value));
Index: /stable/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Regression/RegressionSolutionVariableImpactsCalculator.cs	(revision 15131)
@@ -42,5 +42,9 @@
       Noise
     }
-
+    public enum FactorReplacementMethodEnum {
+      Best,
+      Mode,
+      Shuffle
+    }
     public enum DataPartitionEnum {
       Training,
@@ -88,7 +92,9 @@
     }
 
-    public static IEnumerable<Tuple<string, double>> CalculateImpacts(IRegressionSolution solution,
+    public static IEnumerable<Tuple<string, double>> CalculateImpacts(
+      IRegressionSolution solution,
       DataPartitionEnum data = DataPartitionEnum.Training,
-      ReplacementMethodEnum replacement = ReplacementMethodEnum.Median) {
+      ReplacementMethodEnum replacementMethod = ReplacementMethodEnum.Median,
+      FactorReplacementMethodEnum factorReplacementMethod = FactorReplacementMethodEnum.Best) {
 
       var problemData = solution.ProblemData;
@@ -128,6 +134,7 @@
       var allowedInputVariables = dataset.VariableNames.Where(v => inputvariables.Contains(v)).ToList();
 
-      foreach (var inputVariable in allowedInputVariables) {
-        var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows, replacement);
+      // calculate impacts for double variables
+      foreach (var inputVariable in allowedInputVariables.Where(problemData.Dataset.VariableHasType<double>)) {
+        var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows, replacementMethod);
         var newR2 = OnlinePearsonsRCalculator.Calculate(targetValues, newEstimates, out error);
         if (error != OnlineCalculatorError.None) throw new InvalidOperationException("Error during R² calculation with replaced inputs.");
@@ -137,4 +144,37 @@
         impacts[inputVariable] = impact;
       }
+
+      // calculate impacts for string variables
+      foreach (var inputVariable in allowedInputVariables.Where(problemData.Dataset.VariableHasType<string>)) {
+        if (factorReplacementMethod == FactorReplacementMethodEnum.Best) {
+          // try replacing with all possible values and find the best replacement value
+          var smallestImpact = double.PositiveInfinity;
+          foreach (var repl in problemData.Dataset.GetStringValues(inputVariable, rows).Distinct()) {
+            var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows,
+              Enumerable.Repeat(repl, dataset.Rows));
+            var newR2 = OnlinePearsonsRCalculator.Calculate(targetValues, newEstimates, out error);
+            if (error != OnlineCalculatorError.None)
+              throw new InvalidOperationException("Error during R² calculation with replaced inputs.");
+
+            newR2 = newR2 * newR2;
+            var impact = originalR2 - newR2;
+            if (impact < smallestImpact) smallestImpact = impact;
+          }
+          impacts[inputVariable] = smallestImpact;
+        } else {
+          // for replacement methods shuffle and mode
+          // calculate impacts for factor variables
+
+          var newEstimates = EvaluateModelWithReplacedVariable(solution.Model, inputVariable, modifiableDataset, rows,
+            factorReplacementMethod);
+          var newR2 = OnlinePearsonsRCalculator.Calculate(targetValues, newEstimates, out error);
+          if (error != OnlineCalculatorError.None)
+            throw new InvalidOperationException("Error during R² calculation with replaced inputs.");
+
+          newR2 = newR2 * newR2;
+          var impact = originalR2 - newR2;
+          impacts[inputVariable] = impact;
+        }
+      } // foreach
       return impacts.OrderByDescending(i => i.Value).Select(i => Tuple.Create(i.Key, i.Value));
     }
@@ -184,5 +224,47 @@
       }
 
-      dataset.ReplaceVariable(variable, replacementValues);
+      return EvaluateModelWithReplacedVariable(model, variable, dataset, rows, replacementValues);
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(
+      IRegressionModel model, string variable, ModifiableDataset dataset,
+      IEnumerable<int> rows,
+      FactorReplacementMethodEnum replacement = FactorReplacementMethodEnum.Shuffle) {
+      var originalValues = dataset.GetReadOnlyStringValues(variable).ToList();
+      List<string> replacementValues;
+      IRandom rand;
+
+      switch (replacement) {
+        case FactorReplacementMethodEnum.Mode:
+          var mostCommonValue = rows.Select(r => originalValues[r])
+            .GroupBy(v => v)
+            .OrderByDescending(g => g.Count())
+            .First().Key;
+          replacementValues = Enumerable.Repeat(mostCommonValue, dataset.Rows).ToList();
+          break;
+        case FactorReplacementMethodEnum.Shuffle:
+          // new var has same empirical distribution but the relation to y is broken
+          rand = new FastRandom(31415);
+          // prepare a complete column for the dataset
+          replacementValues = Enumerable.Repeat(string.Empty, dataset.Rows).ToList();
+          // shuffle only the selected rows
+          var shuffledValues = rows.Select(r => originalValues[r]).Shuffle(rand).ToList();
+          int i = 0;
+          // update column values 
+          foreach (var r in rows) {
+            replacementValues[r] = shuffledValues[i++];
+          }
+          break;
+        default:
+          throw new ArgumentException(string.Format("FactorReplacementMethod {0} cannot be handled.", replacement));
+      }
+
+      return EvaluateModelWithReplacedVariable(model, variable, dataset, rows, replacementValues);
+    }
+
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IRegressionModel model, string variable,
+      ModifiableDataset dataset, IEnumerable<int> rows, IEnumerable<double> replacementValues) {
+      var originalValues = dataset.GetReadOnlyDoubleValues(variable).ToList();
+      dataset.ReplaceVariable(variable, replacementValues.ToList());
       //mkommend: ToList is used on purpose to avoid lazy evaluation that could result in wrong estimates due to variable replacements
       var estimates = model.GetEstimatedValues(dataset, rows).ToList();
@@ -191,4 +273,14 @@
       return estimates;
     }
+    private static IEnumerable<double> EvaluateModelWithReplacedVariable(IRegressionModel model, string variable,
+      ModifiableDataset dataset, IEnumerable<int> rows, IEnumerable<string> replacementValues) {
+      var originalValues = dataset.GetReadOnlyStringValues(variable).ToList();
+      dataset.ReplaceVariable(variable, replacementValues.ToList());
+      //mkommend: ToList is used on purpose to avoid lazy evaluation that could result in wrong estimates due to variable replacements
+      var estimates = model.GetEstimatedValues(dataset, rows).ToList();
+      dataset.ReplaceVariable(variable, originalValues);
+
+      return estimates;
+    }
   }
 }
Index: /stable/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataset.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataset.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis/3.4/Interfaces/IDataset.cs	(revision 15131)
@@ -30,4 +30,7 @@
     IEnumerable<string> VariableNames { get; }
     IEnumerable<string> DoubleVariables { get; }
+    IEnumerable<string> StringVariables { get; }
+
+    bool VariableHasType<T>(string variableName);
 
     double GetDoubleValue(string variableName, int row);
@@ -36,5 +39,9 @@
     ReadOnlyCollection<double> GetReadOnlyDoubleValues(string variableName);
 
+    string GetStringValue(string variableName, int row);
     IEnumerable<string> GetStringValues(string variableName);
+    IEnumerable<string> GetStringValues(string variableName, IEnumerable<int> rows);
+    ReadOnlyCollection<string> GetReadOnlyStringValues(string VariableName);
+
     IEnumerable<DateTime> GetDateTimeValues(string variableName);
   }
Index: /stable/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs
===================================================================
--- /stable/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.DataAnalysis/3.4/ModifiableDataset.cs	(revision 15131)
@@ -77,5 +77,5 @@
     public void ReplaceVariable(string variableName, IList values) {
       if (!variableValues.ContainsKey(variableName))
-        throw new ArgumentException(string.Format("Variable {0} is not present in the dataset."), variableName);
+        throw new ArgumentException(string.Format("Variable {0} is not present in the dataset.", variableName));
       if (values.Count != variableValues[variableName].Count)
         throw new ArgumentException("The number of values must coincide with the number of dataset rows.");
Index: /stable/HeuristicLab.Problems.ExternalEvaluation.GP/3.5/ExternalEvaluationSymbolicExpressionTreeStringFormatter.cs
===================================================================
--- /stable/HeuristicLab.Problems.ExternalEvaluation.GP/3.5/ExternalEvaluationSymbolicExpressionTreeStringFormatter.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.ExternalEvaluation.GP/3.5/ExternalEvaluationSymbolicExpressionTreeStringFormatter.cs	(revision 15131)
@@ -58,5 +58,5 @@
       StringBuilder strBuilder = new StringBuilder();
       if (Indent) strBuilder.Append(' ', indentLength);
-      if (node.Subtrees.Count() > 0) { // internal node
+      if (node.Subtrees.Any()) { // internal node
         strBuilder.Append("(");
         if (node.Symbol is Addition) {
@@ -103,8 +103,8 @@
         strBuilder.Append(")");
       } else {
-        if (node is VariableTreeNode) {
+        if (node.Symbol is HeuristicLab.Problems.DataAnalysis.Symbolic.Variable) {
           var varNode = node as VariableTreeNode;
           strBuilder.AppendFormat("(* {0} {1})", varNode.VariableName, varNode.Weight.ToString("g17", CultureInfo.InvariantCulture));
-        } else if (node is ConstantTreeNode) {
+        } else if (node.Symbol is Constant) {
           var constNode = node as ConstantTreeNode;
           strBuilder.Append(constNode.Value.ToString("g17", CultureInfo.InvariantCulture));
Index: /stable/HeuristicLab.Problems.GrammaticalEvolution/3.4/SymbolicRegression/GESymbolicDataAnalysisProblem.cs
===================================================================
--- /stable/HeuristicLab.Problems.GrammaticalEvolution/3.4/SymbolicRegression/GESymbolicDataAnalysisProblem.cs	(revision 15130)
+++ /stable/HeuristicLab.Problems.GrammaticalEvolution/3.4/SymbolicRegression/GESymbolicDataAnalysisProblem.cs	(revision 15131)
@@ -177,4 +177,5 @@
       ApplyLinearScalingParameter.Hidden = true;
 
+      if (problemData.AllowedInputVariables.Any(name => !problemData.Dataset.VariableHasType<double>(name))) throw new NotSupportedException("Categorical variables are not supported");
       SymbolicExpressionTreeGrammar = new GESymbolicExpressionGrammar(problemData.AllowedInputVariables, problemData.AllowedInputVariables.Count() * 3);
       SymbolicExpressionTreeInterpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
Index: /stable/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/InfixExpressionParserTest.cs
===================================================================
--- /stable/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/InfixExpressionParserTest.cs	(revision 15130)
+++ /stable/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/InfixExpressionParserTest.cs	(revision 15131)
@@ -37,5 +37,5 @@
       Console.WriteLine(formatter.Format(parser.Parse("3*3")));
       Console.WriteLine(formatter.Format(parser.Parse("3 * 4")));
-      Console.WriteLine(formatter.Format(parser.Parse("123E-03")  ));
+      Console.WriteLine(formatter.Format(parser.Parse("123E-03")));
       Console.WriteLine(formatter.Format(parser.Parse("123e-03")));
       Console.WriteLine(formatter.Format(parser.Parse("123e+03")));
@@ -106,4 +106,19 @@
       Console.WriteLine(formatter.Format(parser.Parse("x * LAG('x', +1)")));
 
+      Console.WriteLine(formatter.Format(parser.Parse("x [1.0] * y")));
+      Console.WriteLine(formatter.Format(parser.Parse("x [1.0, 2.0] * y [1.0, 2.0]")));
+      Console.WriteLine(formatter.Format(parser.Parse("x[1] * y")));
+      Console.WriteLine(formatter.Format(parser.Parse("x[1, 2] * y [1, 2]")));
+
+      Console.WriteLine(formatter.Format(parser.Parse("x [+1.0] * y")));
+      Console.WriteLine(formatter.Format(parser.Parse("x [-1.0] * y")));
+      Console.WriteLine(formatter.Format(parser.Parse("x [-1.0, -2.0] * y [+1.0, +2.0]")));
+
+      Console.WriteLine(formatter.Format(parser.Parse("x='bla' * y")));
+      Console.WriteLine(formatter.Format(parser.Parse("x = 'bla'")));
+      Console.WriteLine(formatter.Format(parser.Parse("x = \"bla\"")));
+      Console.WriteLine(formatter.Format(parser.Parse("1.0 * x = bla")));
+      Console.WriteLine(formatter.Format(parser.Parse("-1.0 * x = bla")));
+      Console.WriteLine(formatter.Format(parser.Parse("+1.0 * \"x\" = bla + y = \"bla2\"")));
     }
   }
Index: /stable/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/SymbolicDataAnalysisExpressionTreeSimplifierTest.cs
===================================================================
--- /stable/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/SymbolicDataAnalysisExpressionTreeSimplifierTest.cs	(revision 15130)
+++ /stable/HeuristicLab.Tests/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4/SymbolicDataAnalysisExpressionTreeSimplifierTest.cs	(revision 15131)
@@ -19,9 +19,7 @@
  */
 #endregion
-
 using System;
-using System.Collections.Generic;
+using System.Globalization;
 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
-using HeuristicLab.Problems.DataAnalysis.Symbolic;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
@@ -39,316 +37,209 @@
       SymbolicExpressionTreeStringFormatter formatter = new SymbolicExpressionTreeStringFormatter();
       #region single argument arithmetics
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(+ 1.0)"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(+ (variable 2.0 a))"));
-        var expectedTree = importer.Import("(variable 2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(- 1.0)"));
-        var expectedTree = importer.Import("-1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(- (variable 2.0 a))"));
-        var expectedTree = importer.Import("(variable -2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(* 2.0)"));
-        var expectedTree = importer.Import("2.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(* (variable 2.0 a))"));
-        var expectedTree = importer.Import("(variable 2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(/ 2.0)"));
-        var expectedTree = importer.Import("0.5");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(/ (variable 2.0 a))"));
-        var expectedTree = importer.Import("(/ 1.0 (variable 2.0 a))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+
+      AssertEqualAfterSimplification("(+ 1.0)", "1.0");
+      AssertEqualAfterSimplification("(- 1.0)", "-1.0");
+      AssertEqualAfterSimplification("(- (variable 2.0 a))", "(variable -2.0 a)");
+      AssertEqualAfterSimplification("(* 2.0)", "2.0");
+      AssertEqualAfterSimplification("(* (variable 2.0 a))", "(variable 2.0 a)");
+      AssertEqualAfterSimplification("(/ 2.0)", "0.5");
+      AssertEqualAfterSimplification("(/ (variable 2.0 a))", "(/ 1.0 (variable 2.0 a))");
+      #endregion
+
       #region aggregation of constants into factors
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(* 2.0 (variable 2.0 a))"));
-        var expectedTree = importer.Import("(variable 4.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(/ (variable 2.0 a) 2.0)"));
-        var expectedTree = importer.Import("(variable 1.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(/ (variable 2.0 a) (* 2.0 2.0))"));
-        var expectedTree = importer.Import("(variable 0.5 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+      AssertEqualAfterSimplification("(* 2.0 (variable 2.0 a))", "(variable 4.0 a)");
+      AssertEqualAfterSimplification("(/ (variable 2.0 a) 2.0)", "(variable 1.0 a)");
+      AssertEqualAfterSimplification("(/ (variable 2.0 a) (* 2.0 2.0))", "(variable 0.5 a)");
+      #endregion
+
       #region constant and variable folding
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(+ 1.0 2.0)"));
-        var expectedTree = importer.Import("3.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(+ (variable 2.0 a) (variable 2.0 a))"));
-        var expectedTree = importer.Import("(variable 4.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(- (variable 2.0 a) (variable 1.0 a))"));
-        var expectedTree = importer.Import("(variable 1.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(* (variable 2.0 a) (variable 2.0 a))"));
-        var expectedTree = importer.Import("(* (* (variable 1.0 a) (variable 1.0 a)) 4.0)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        var actualTree = simplifier.Simplify(importer.Import("(/ (variable 1.0 a) (variable 2.0 a))"));
-        var expectedTree = importer.Import("0.5");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+      AssertEqualAfterSimplification("(+ 1.0 2.0)", "3.0");
+      AssertEqualAfterSimplification("(+ (variable 2.0 a) (variable 2.0 a))", "(variable 4.0 a)");
+      AssertEqualAfterSimplification("(- (variable 2.0 a) (variable 1.0 a))", "(variable 1.0 a)");
+      AssertEqualAfterSimplification("(* (variable 2.0 a) (variable 2.0 a))", "(* (* (variable 1.0 a) (variable 1.0 a)) 4.0)");
+      AssertEqualAfterSimplification("(/ (variable 1.0 a) (variable 2.0 a))", "0.5");
+      #endregion
+
       #region logarithm rules
-      {
-        // cancellation
-        var actualTree = simplifier.Simplify(importer.Import("(log (exp (variable 2.0 a)))"));
-        var expectedTree = importer.Import("(variable 2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // must not transform logs in this way as we do not know wether both variables are positive
-        var actualTree = simplifier.Simplify(importer.Import("(log (* (variable 1.0 a) (variable 1.0 b)))"));
-        var expectedTree = importer.Import("(log (* (variable 1.0 a) (variable 1.0 b)))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // must not transform logs in this way as we do not know wether both variables are positive
-        var actualTree = simplifier.Simplify(importer.Import("(log (/ (variable 1.0 a) (variable 1.0 b)))"));
-        var expectedTree = importer.Import("(log (/ (variable 1.0 a) (variable 1.0 b)))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+
+      // cancellation
+      AssertEqualAfterSimplification("(log (exp (variable 2.0 a)))", "(variable 2.0 a)");
+      // must not transform logs in this way as we do not know wether both variables are positive
+      AssertEqualAfterSimplification("(log (* (variable 1.0 a) (variable 1.0 b)))", "(log (* (variable 1.0 a) (variable 1.0 b)))");
+      // must not transform logs in this way as we do not know wether both variables are positive
+      AssertEqualAfterSimplification("(log (/ (variable 1.0 a) (variable 1.0 b)))", "(log (/ (variable 1.0 a) (variable 1.0 b)))");
+      #endregion
+
       #region exponentiation rules
-      {
-        // cancellation
-        var actualTree = simplifier.Simplify(importer.Import("(exp (log (variable 2.0 a)))"));
-        var expectedTree = importer.Import("(variable 2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // exp transformation
-        var actualTree = simplifier.Simplify(importer.Import("(exp (+ (variable 2.0 a) (variable 3.0 b)))"));
-        var expectedTree = importer.Import("(* (exp (variable 2.0 a)) (exp (variable 3.0 b)))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // exp transformation
-        var actualTree = simplifier.Simplify(importer.Import("(exp (- (variable 2.0 a) (variable 3.0 b)))"));
-        var expectedTree = importer.Import("(* (exp (variable 2.0 a)) (exp (variable -3.0 b)))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // exp transformation
-        var actualTree = simplifier.Simplify(importer.Import("(exp (- (variable 2.0 a) (* (variable 3.0 b) (variable 4.0 c))))"));
-        var expectedTree = importer.Import("(* (exp (variable 2.0 a)) (exp (* (variable 1.0 b) (variable 1.0 c) -12.0)))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // exp transformation
-        var actualTree = simplifier.Simplify(importer.Import("(exp (- (variable 2.0 a) (* (variable 3.0 b) (cos (variable 4.0 c)))))"));
-        var expectedTree = importer.Import("(* (exp (variable 2.0 a)) (exp (* (variable 1.0 b) (cos (variable 4.0 c)) -3.0)))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+      // cancellation
+      AssertEqualAfterSimplification("(exp (log (variable 2.0 a)))", "(variable 2.0 a)");
+      // exp transformation
+      AssertEqualAfterSimplification("(exp (+ (variable 2.0 a) (variable 3.0 b)))", "(* (exp (variable 2.0 a)) (exp (variable 3.0 b)))");
+      // exp transformation
+      AssertEqualAfterSimplification("(exp (- (variable 2.0 a) (variable 3.0 b)))", "(* (exp (variable 2.0 a)) (exp (variable -3.0 b)))");
+      // exp transformation
+      AssertEqualAfterSimplification("(exp (- (variable 2.0 a) (* (variable 3.0 b) (variable 4.0 c))))", "(* (exp (variable 2.0 a)) (exp (* (variable 1.0 b) (variable 1.0 c) -12.0)))");
+      // exp transformation
+      AssertEqualAfterSimplification("(exp (- (variable 2.0 a) (* (variable 3.0 b) (cos (variable 4.0 c)))))", "(* (exp (variable 2.0 a)) (exp (* (variable 1.0 b) (cos (variable 4.0 c)) -3.0)))");
+      #endregion
+
       #region power rules
-      {
-        // cancellation
-        var actualTree = simplifier.Simplify(importer.Import("(pow (variable 2.0 a) 0.0)"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // fixed point
-        var actualTree = simplifier.Simplify(importer.Import("(pow (variable 2.0 a) 1.0)"));
-        var expectedTree = importer.Import("(variable 2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // inversion fixed point
-        var actualTree = simplifier.Simplify(importer.Import("(pow (variable 2.0 a) -1.0)"));
-        var expectedTree = importer.Import("(/ 1.0 (variable 2.0 a))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // inversion 
-        var actualTree = simplifier.Simplify(importer.Import("(pow (variable 2.0 a) -2.0)"));
-        var expectedTree = importer.Import("(/ 1.0 (pow (variable 2.0 a) 2.0))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // constant folding
-        var actualTree = simplifier.Simplify(importer.Import("(pow 3.0 2.0)"));
-        var expectedTree = importer.Import("9.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+
+      // cancellation
+      AssertEqualAfterSimplification("(pow (variable 2.0 a) 0.0)", "1.0");
+      // fixed point
+      AssertEqualAfterSimplification("(pow (variable 2.0 a) 1.0)", "(variable 2.0 a)");
+      // inversion fixed point
+      AssertEqualAfterSimplification("(pow (variable 2.0 a) -1.0)", "(/ 1.0 (variable 2.0 a))");
+      // inversion 
+      AssertEqualAfterSimplification("(pow (variable 2.0 a) -2.0)", "(/ 1.0 (pow (variable 2.0 a) 2.0))");
+      // constant folding
+      AssertEqualAfterSimplification("(pow 3.0 2.0)", "9.0");
+      #endregion
+
       #region root rules
-      {
-        // cancellation
-        var actualTree = simplifier.Simplify(importer.Import("(root (variable 2.0 a) 0.0)"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // fixed point
-        var actualTree = simplifier.Simplify(importer.Import("(root (variable 2.0 a) 1.0)"));
-        var expectedTree = importer.Import("(variable 2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // inversion fixed point
-        var actualTree = simplifier.Simplify(importer.Import("(root (variable 2.0 a) -1.0)"));
-        var expectedTree = importer.Import("(/ 1.0 (variable 2.0 a))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // inversion
-        var actualTree = simplifier.Simplify(importer.Import("(root (variable 2.0 a) -2.0)"));
-        var expectedTree = importer.Import("(/ 1.0 (root (variable 2.0 a) 2.0))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // constant folding
-        var actualTree = simplifier.Simplify(importer.Import("(root 9.0 2.0)"));
-        var expectedTree = importer.Import("3.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+      // cancellation
+      AssertEqualAfterSimplification("(root (variable 2.0 a) 0.0)", "1.0");
+      // fixed point
+      AssertEqualAfterSimplification("(root (variable 2.0 a) 1.0)", "(variable 2.0 a)");
+      // inversion fixed point
+      AssertEqualAfterSimplification("(root (variable 2.0 a) -1.0)", "(/ 1.0 (variable 2.0 a))");
+      // inversion
+      AssertEqualAfterSimplification("(root (variable 2.0 a) -2.0)", "(/ 1.0 (root (variable 2.0 a) 2.0))");
+      // constant folding
+      AssertEqualAfterSimplification("(root 9.0 2.0)", "3.0");
+      #endregion
+
       #region boolean operations
-      {
-        // always true and
-        var actualTree = simplifier.Simplify(importer.Import("(and 1.0 2.0)"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // always false and
-        var actualTree = simplifier.Simplify(importer.Import("(and 1.0 -2.0)"));
-        var expectedTree = importer.Import("-1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // always true or
-        var actualTree = simplifier.Simplify(importer.Import("(or -1.0 2.0)"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // always false or
-        var actualTree = simplifier.Simplify(importer.Import("(or -1.0 -2.0)"));
-        var expectedTree = importer.Import("-1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // constant not 
-        var actualTree = simplifier.Simplify(importer.Import("(not -2.0)"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // constant not 
-        var actualTree = simplifier.Simplify(importer.Import("(not 2.0)"));
-        var expectedTree = importer.Import("-1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // constant not 
-        var actualTree = simplifier.Simplify(importer.Import("(not 0.0)"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // nested nots
-        var actualTree = simplifier.Simplify(importer.Import("(not (not 1.0))"));
-        var expectedTree = importer.Import("1.0");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // not of non-Boolean argument
-        var actualTree = simplifier.Simplify(importer.Import("(not (variable 1.0 a))"));
-        var expectedTree = importer.Import("(not (> (variable 1.0 a) 0.0))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // not Boolean argument
-        var actualTree = simplifier.Simplify(importer.Import("(not (and (> (variable 1.0 a) 0.0) (> (variable 1.0 a) 0.0)))"));
-        var expectedTree = importer.Import("(not (and (> (variable 1.0 a) 0.0) (> (variable 1.0 a) 0.0)))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      #endregion
+      // always true and
+      AssertEqualAfterSimplification("(and 1.0 2.0)", "1.0");
+      // always false and
+      AssertEqualAfterSimplification("(and 1.0 -2.0)", "-1.0");
+      // always true or
+      AssertEqualAfterSimplification("(or -1.0 2.0)", "1.0");
+      // always false or
+      AssertEqualAfterSimplification("(or -1.0 -2.0)", "-1.0");
+      // constant not 
+      AssertEqualAfterSimplification("(not -2.0)", "1.0");
+      // constant not 
+      AssertEqualAfterSimplification("(not 2.0)", "-1.0");
+      // constant not 
+      AssertEqualAfterSimplification("(not 0.0)", "1.0");
+      // nested nots
+      AssertEqualAfterSimplification("(not (not 1.0))", "1.0");
+      // not of non-Boolean argument
+      AssertEqualAfterSimplification("(not (variable 1.0 a))", "(not (> (variable 1.0 a) 0.0))");
+      // not Boolean argument
+      AssertEqualAfterSimplification("(not (and (> (variable 1.0 a) 0.0) (> (variable 1.0 a) 0.0)))", "(not (and (> (variable 1.0 a) 0.0) (> (variable 1.0 a) 0.0)))");
+      #endregion
+
       #region conditionals
-      {
-        // always false
-        var actualTree = simplifier.Simplify(importer.Import("(if -1.0 (variable 2.0 a) (variable 3.0 a))"));
-        var expectedTree = importer.Import("(variable 3.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // always true
-        var actualTree = simplifier.Simplify(importer.Import("(if 1.0 (variable 2.0 a) (variable 3.0 a))"));
-        var expectedTree = importer.Import("(variable 2.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // always false (0.0)
-        var actualTree = simplifier.Simplify(importer.Import("(if 0.0 (variable 2.0 a) (variable 3.0 a))"));
-        var expectedTree = importer.Import("(variable 3.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // complex constant condition (always false)
-        var actualTree = simplifier.Simplify(importer.Import("(if (* 1.0 -2.0) (variable 2.0 a) (variable 3.0 a))"));
-        var expectedTree = importer.Import("(variable 3.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // complex constant condition (always false)
-        var actualTree = simplifier.Simplify(importer.Import("(if (/ (variable 1.0 a) (variable -2.0 a)) (variable 2.0 a) (variable 3.0 a))"));
-        var expectedTree = importer.Import("(variable 3.0 a)");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
-      {
-        // insertion of relational operator
-        var actualTree = simplifier.Simplify(importer.Import("(if (variable 1.0 a) (variable 2.0 a) (variable 3.0 a))"));
-        var expectedTree = importer.Import("(if (> (variable 1.0 a) 0.0) (variable 2.0 a) (variable 3.0 a))");
-        Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
-      }
+      // always false
+      AssertEqualAfterSimplification("(if -1.0 (variable 2.0 a) (variable 3.0 a))", "(variable 3.0 a)");
+      // always true
+      AssertEqualAfterSimplification("(if 1.0 (variable 2.0 a) (variable 3.0 a))", "(variable 2.0 a)");
+      // always false (0.0)
+      AssertEqualAfterSimplification("(if 0.0 (variable 2.0 a) (variable 3.0 a))", "(variable 3.0 a)");
+      // complex constant condition (always false)
+      AssertEqualAfterSimplification("(if (* 1.0 -2.0) (variable 2.0 a) (variable 3.0 a))", "(variable 3.0 a)");
+      // complex constant condition (always false)
+      AssertEqualAfterSimplification("(if (/ (variable 1.0 a) (variable -2.0 a)) (variable 2.0 a) (variable 3.0 a))", "(variable 3.0 a)");
+      // insertion of relational operator
+      AssertEqualAfterSimplification("(if (variable 1.0 a) (variable 2.0 a) (variable 3.0 a))", "(if (> (variable 1.0 a) 0.0) (variable 2.0 a) (variable 3.0 a))");
+      #endregion
+
+      #region factor variables
+      AssertEqualAfterSimplification("(factor a 1.0)", "(factor a 1.0)");
+      // factor folding
+      AssertEqualAfterSimplification("(+ (factor a 1.0 1.0) (factor a 2.0 3.0))", "(factor a 3.0 4.0)");
+      AssertEqualAfterSimplification("(- (factor a 1.0 1.0) (factor a 2.0 3.0))", "(factor a -1.0 -2.0)");
+      AssertEqualAfterSimplification("(* (factor a 2.0 2.0) (factor a 2.0 3.0))", "(factor a 4.0 6.0)");
+      AssertEqualAfterSimplification("(/ (factor a 2.0 5.0))", "(factor a 0.5 0.2)");
+      AssertEqualAfterSimplification("(/ (factor a 4.0 6.0) (factor a 2.0 3.0))", "(factor a 2.0 2.0)");
+      AssertEqualAfterSimplification("(+ 3.0 (factor a 4.0 6.0))", "(factor a 7.0 9.0)");
+      AssertEqualAfterSimplification("(+ (factor a 4.0 6.0) 3.0)", "(factor a 7.0 9.0)");
+      AssertEqualAfterSimplification("(- 3.0 (factor a 4.0 6.0))", "(factor a -1.0 -3.0)");
+      AssertEqualAfterSimplification("(- (factor a 4.0 6.0) 3.0)", "(factor a 1.0 3.0)");
+      AssertEqualAfterSimplification("(* 2.0 (factor a 4.0 6.0))", "(factor a 8.0 12.0)");
+      AssertEqualAfterSimplification("(* (factor a 4.0 6.0) 2.0)", "(factor a 8.0 12.0)");
+      AssertEqualAfterSimplification("(* (factor a 4.0 6.0) (variable 2.0 a))", "(* (factor a 8.0 12.0) (variable 1.0 a))"); // not possible (a is used as factor and double variable) interpreter will fail
+      AssertEqualAfterSimplification(
+        "(log (factor a 10.0 100.0))",
+        string.Format(CultureInfo.InvariantCulture, "(factor a {0} {1})", Math.Log(10.0), Math.Log(100.0)));
+      AssertEqualAfterSimplification(
+        "(exp (factor a 2.0 3.0))",
+        string.Format(CultureInfo.InvariantCulture, "(factor a {0} {1})", Math.Exp(2.0), Math.Exp(3.0)));
+      AssertEqualAfterSimplification("(sqrt (factor a 9.0 16.0))", "(factor a 3.0 4.0))");
+      AssertEqualAfterSimplification("(sqr (factor a 2.0 3.0))", "(factor a 4.0 9.0))");
+      AssertEqualAfterSimplification("(root (factor a 8.0 27.0) 3)", "(factor a 2.0 3.0))");
+      AssertEqualAfterSimplification("(pow (factor a 2.0 3.0) 3)", "(factor a 8.0 27.0))");
+
+      AssertEqualAfterSimplification("(sin (factor a 1.0 2.0) )",
+        string.Format(CultureInfo.InvariantCulture, "(factor a {0} {1}))", Math.Sin(1.0), Math.Sin(2.0)));
+      AssertEqualAfterSimplification("(cos (factor a 1.0 2.0) )",
+        string.Format(CultureInfo.InvariantCulture, "(factor a {0} {1}))", Math.Cos(1.0), Math.Cos(2.0)));
+      AssertEqualAfterSimplification("(tan (factor a 1.0 2.0) )",
+        string.Format(CultureInfo.InvariantCulture, "(factor a {0} {1}))", Math.Tan(1.0), Math.Tan(2.0)));
+
+
+      AssertEqualAfterSimplification("(binfactor a val 1.0)", "(binfactor a val 1.0)");
+      // binfactor folding
+      AssertEqualAfterSimplification("(+ (binfactor a val 1.0) (binfactor a val 2.0))", "(binfactor a val 3.0)");
+      AssertEqualAfterSimplification("(+ (binfactor a val0 1.0) (binfactor a val1 2.0))", "(+ (binfactor a val0 1.0) (binfactor a val1 2.0))"); // cannot be simplified (different vals)
+      AssertEqualAfterSimplification("(+ (binfactor a val 1.0) (binfactor b val 2.0))", "(+ (binfactor a val 1.0) (binfactor b val 2.0))"); // cannot be simplified (different vars)
+      AssertEqualAfterSimplification("(- (binfactor a val 1.0) (binfactor a val 2.0))", "(binfactor a val -1.0)");
+      AssertEqualAfterSimplification("(* (binfactor a val 2.0) (binfactor a val 3.0))", "(binfactor a val 6.0)");
+      AssertEqualAfterSimplification("(/ (binfactor a val 6.0) (binfactor a val 3.0))", "(/ (binfactor a val 6.0) (binfactor a val 3.0))"); // not allowed! 0/0 for other values than 'val'
+      AssertEqualAfterSimplification("(/ (binfactor a val 4.0))", "(/ 1.0 (binfactor a val 4.0))"); // not allowed!
+
+      AssertEqualAfterSimplification("(+ 3.0 (binfactor a val 4.0 ))", "(+ (binfactor a val 4.0 ) 3.0))"); // not allowed
+      AssertEqualAfterSimplification("(- 3.0 (binfactor a val 4.0 ))", "(+ (binfactor a val -4.0 ) 3.0)"); 
+      AssertEqualAfterSimplification("(+ (binfactor a val 4.0 ) 3.0)", "(+ (binfactor a val 4.0 ) 3.0)");  // not allowed
+      AssertEqualAfterSimplification("(- (binfactor a val 4.0 ) 3.0)", "(+ (binfactor a val 4.0 ) -3.0)"); 
+      AssertEqualAfterSimplification("(* 2.0 (binfactor a val 4.0))", "(binfactor a val 8.0 )");
+      AssertEqualAfterSimplification("(* (binfactor a val 4.0) 2.0)", "(binfactor a val 8.0 )");
+      AssertEqualAfterSimplification("(* (binfactor a val 4.0) (variable 2.0 a))", "(* (binfactor a val 1.0) (variable 1.0 a) 8.0)");   // not possible (a is used as factor and double variable) interpreter will fail
+      AssertEqualAfterSimplification("(log (binfactor a val 10.0))", "(log (binfactor a val 10.0))"); // not allowed (log(0))
+
+      // exp( binfactor w val=a) = if(val=a) exp(w) else exp(0) = binfactor( (exp(w) - 1) val a) + 1
+      AssertEqualAfterSimplification("(exp (binfactor a val 3.0))", 
+        string.Format(CultureInfo.InvariantCulture, "(+ (binfactor a val {0}) 1.0)", Math.Exp(3.0) - 1)
+        ); 
+      AssertEqualAfterSimplification("(sqrt (binfactor a val 16.0))", "(binfactor a val 4.0))"); // sqrt(0) = 0
+      AssertEqualAfterSimplification("(sqr (binfactor a val 3.0))", "(binfactor a val 9.0))"); // 0*0 = 0
+      AssertEqualAfterSimplification("(root (binfactor a val 27.0) 3)", "(binfactor a val 3.0))");
+      AssertEqualAfterSimplification("(pow (binfactor a val 3.0) 3)", "(binfactor a val 27.0))");
+
+      AssertEqualAfterSimplification("(sin (binfactor a val 2.0) )",
+        string.Format(CultureInfo.InvariantCulture, "(binfactor a val {0}))", Math.Sin(2.0))); // sin(0) = 0
+      AssertEqualAfterSimplification("(cos (binfactor a val 2.0) )", 
+        string.Format(CultureInfo.InvariantCulture, "(+ (binfactor a val {0}) 1.0)", Math.Cos(2.0) - 1)); // cos(0) = 1
+      AssertEqualAfterSimplification("(tan (binfactor a val 2.0) )",
+        string.Format(CultureInfo.InvariantCulture, "(binfactor a val {0}))", Math.Tan(2.0))); // tan(0) = 0
+
+      // combination of factor and binfactor
+      AssertEqualAfterSimplification("(+ (binfactor a x0 2.0) (factor a 2.0 3.0))", "(factor a 4.0 3.0)");
+      AssertEqualAfterSimplification("(+ (factor a 2.0 3.0) (binfactor a x0 2.0))", "(factor a 4.0 3.0)");
+      AssertEqualAfterSimplification("(* (binfactor a x1 2.0) (factor a 2.0 3.0))", "(binfactor a x1 6.0)"); // all other values have weight zero in binfactor
+      AssertEqualAfterSimplification("(* (factor a 2.0 3.0) (binfactor a x1 2.0))", "(binfactor a x1 6.0)"); // all other values have weight zero in binfactor
+      AssertEqualAfterSimplification("(/ (binfactor a x0 2.0) (factor a 2.0 3.0))", "(binfactor a x0 1.0)");
+      AssertEqualAfterSimplification("(/ (factor a 2.0 3.0) (binfactor a x0 2.0))",
+        string.Format(CultureInfo.InvariantCulture, "(factor a 1.0 {0})", 3.0 / 0.0));
+      AssertEqualAfterSimplification("(- (binfactor a x0 2.0) (factor a 2.0 3.0))", "(factor a 0.0 -3.0)");
+      AssertEqualAfterSimplification("(- (factor a 2.0 3.0) (binfactor a x0 2.0))", "(factor a 0.0 3.0)");
       #endregion
     }
 
-    private void AssertEqualEnumerations(IEnumerable<double> expected, IEnumerable<double> actual) {
-      var expectedEnumerator = expected.GetEnumerator();
-      var actualEnumerator = actual.GetEnumerator();
-      while (expectedEnumerator.MoveNext() & actualEnumerator.MoveNext()) {
-        Assert.AreEqual(expectedEnumerator.Current, actualEnumerator.Current, Math.Abs(1E-6 * expectedEnumerator.Current));
-      }
-      if (expectedEnumerator.MoveNext() | actualEnumerator.MoveNext())
-        Assert.Fail("Number of elements in enumerations do not match");
+
+    private void AssertEqualAfterSimplification(string original, string expected) {
+      var simplifier = new SymbolicDataAnalysisExpressionTreeSimplifier();
+      var formatter = new SymbolicExpressionTreeStringFormatter();
+      var importer = new SymbolicExpressionImporter();
+      var actualTree = simplifier.Simplify(importer.Import(original));
+      var expectedTree = importer.Import(expected);
+      Assert.AreEqual(formatter.Format(expectedTree), formatter.Format(actualTree));
+
     }
   }
 }
+
