Index: branches/1721-RandomForestPersistence/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModel.cs
===================================================================
--- branches/1721-RandomForestPersistence/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModel.cs	(revision 10321)
+++ branches/1721-RandomForestPersistence/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModel.cs	(revision 10322)
@@ -40,5 +40,5 @@
       get {
         // recalculate lazily
-        if (randomForest == null || randomForest.innerobj.trees == null) RecalculateModel();
+        if (randomForest.innerobj.trees == null || randomForest.innerobj.trees.Length == 0) RecalculateModel();
         return randomForest;
       }
@@ -50,13 +50,7 @@
     private int seed;
     [Storable]
-    private Dataset originalTrainingData;
-    [Storable]
-    private int[] trainingRows;
+    private IDataAnalysisProblemData originalTrainingData;
     [Storable]
     private double[] classValues;
-    [Storable]
-    private string[] allowedInputVariables;
-    [Storable]
-    private string targetVariable;
     [Storable]
     private int nTrees;
@@ -75,24 +69,20 @@
     private RandomForestModel(RandomForestModel original, Cloner cloner)
       : base(original, cloner) {
-      // clone the model if necessary
-      if (original.randomForest != null) {
-        randomForest = new alglib.decisionforest();
-        randomForest.innerobj.bufsize = original.randomForest.innerobj.bufsize;
-        randomForest.innerobj.nclasses = original.randomForest.innerobj.nclasses;
-        randomForest.innerobj.ntrees = original.randomForest.innerobj.ntrees;
-        randomForest.innerobj.nvars = original.randomForest.innerobj.nvars;
-        // we assume that the trees array (double[]) is immutable in alglib
-        randomForest.innerobj.trees = original.randomForest.innerobj.trees;
-      }
+      randomForest = new alglib.decisionforest();
+      randomForest.innerobj.bufsize = original.randomForest.innerobj.bufsize;
+      randomForest.innerobj.nclasses = original.randomForest.innerobj.nclasses;
+      randomForest.innerobj.ntrees = original.randomForest.innerobj.ntrees;
+      randomForest.innerobj.nvars = original.randomForest.innerobj.nvars;
+      // we assume that the trees array (double[]) is immutable in alglib
+      randomForest.innerobj.trees = original.randomForest.innerobj.trees;
+      
+      // allowedInputVariables is immutable so we don't need to clone
+      allowedInputVariables = original.allowedInputVariables;
+
       // clone data which is necessary to rebuild the model
       this.seed = original.seed;
-      // Dataset is immutable so we do not need to clone
-      this.originalTrainingData = original.originalTrainingData;
-      this.targetVariable = original.targetVariable;
-      // trainingRows, classvalues and allowedInputVariables are immutable
-      this.trainingRows = original.trainingRows;
-      this.allowedInputVariables = original.allowedInputVariables;
-      this.classValues = original.classValues;  // null for regression problems
-
+      this.originalTrainingData = cloner.Clone(original.originalTrainingData);
+      // classvalues is immutable so we don't need to clone
+      this.classValues = original.classValues;
       this.nTrees = original.nTrees;
       this.r = original.r;
@@ -102,5 +92,5 @@
     // random forest models can only be created through the static factory methods CreateRegressionModel and CreateClassificationModel
     private RandomForestModel(alglib.decisionforest randomForest,
-      int seed, Dataset originalTrainingData, IEnumerable<int> trainingRows, IEnumerable<string> allowedInputVariables, string targetVariable,
+      int seed, IDataAnalysisProblemData originalTrainingData,
       int nTrees, double r, double m, double[] classValues = null)
       : base() {
@@ -111,9 +101,6 @@
       // data which is necessary for recalculation of the model
       this.seed = seed;
-      this.originalTrainingData = originalTrainingData;
-      this.trainingRows = (int[])trainingRows.ToArray().Clone();
-      this.targetVariable = targetVariable;
-      this.allowedInputVariables = (string[])allowedInputVariables.ToArray().Clone();
-      this.classValues = classValues; // null for regression problems
+      this.originalTrainingData = (IDataAnalysisProblemData)originalTrainingData.Clone();
+      this.classValues = classValues;
       this.nTrees = nTrees;
       this.r = r;
@@ -127,12 +114,14 @@
     private void RecalculateModel() {
       double rmsError, oobRmsError, relClassError, oobRelClassError;
-      if (classValues == null) {
-        var model = CreateRegressionModel(originalTrainingData, trainingRows, allowedInputVariables, targetVariable,
+      var regressionProblemData = originalTrainingData as IRegressionProblemData;
+      var classificationProblemData = originalTrainingData as IClassificationProblemData;
+      if (regressionProblemData != null) {
+        var model = CreateRegressionModel(regressionProblemData,
                                               nTrees, r, m, seed, out rmsError, out oobRmsError,
                                               out relClassError, out oobRelClassError);
         randomForest = model.randomForest;
-      } else {
-        var model = CreateClassificationModel(originalTrainingData, trainingRows, allowedInputVariables, targetVariable,
-                                              classValues, nTrees, r, m, seed, out rmsError, out oobRmsError,
+      } else if (classificationProblemData != null) {
+        var model = CreateClassificationModel(classificationProblemData,
+                                              nTrees, r, m, seed, out rmsError, out oobRmsError,
                                               out relClassError, out oobRelClassError);
         randomForest = model.randomForest;
@@ -141,5 +130,5 @@
 
     public IEnumerable<double> GetEstimatedValues(Dataset dataset, IEnumerable<int> rows) {
-      double[,] inputData = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables, rows);
+      double[,] inputData = AlglibUtil.PrepareInputMatrix(dataset, AllowedInputVariables, rows);
       AssertInputMatrix(inputData);
 
@@ -159,5 +148,5 @@
 
     public IEnumerable<double> GetEstimatedClassValues(Dataset dataset, IEnumerable<int> rows) {
-      double[,] inputData = AlglibUtil.PrepareInputMatrix(dataset, allowedInputVariables, rows);
+      double[,] inputData = AlglibUtil.PrepareInputMatrix(dataset, AllowedInputVariables, rows);
       AssertInputMatrix(inputData);
 
@@ -200,20 +189,7 @@
     public static RandomForestModel CreateRegressionModel(IRegressionProblemData problemData, int nTrees, double r, double m, int seed,
       out double rmsError, out double avgRelError, out double outOfBagAvgRelError, out double outOfBagRmsError) {
-      return CreateRegressionModel(problemData.Dataset, problemData.TrainingIndices,
-                                     problemData.AllowedInputVariables, problemData.TargetVariable,
-                                     nTrees, r, m, seed,
-                                     out rmsError, out outOfBagRmsError, out avgRelError, out outOfBagAvgRelError);
-    }
-
-    private static RandomForestModel CreateRegressionModel(
-      // prob param
-        Dataset dataset, IEnumerable<int> trainingRows, IEnumerable<string> allowedInputVariables, string targetVariable,
-      // alg param
-        int nTrees, double r, double m, int seed,
-      // results
-        out double rmsError, out double avgRelError, out double outOfBagAvgRelError, out double outOfBagRmsError) {
-
-      var variables = allowedInputVariables.Concat(new string[] { targetVariable });
-      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(dataset, variables, trainingRows);
+
+      var variables = problemData.AllowedInputVariables.Concat(new string[] { problemData.TargetVariable });
+      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(problemData.Dataset, variables, problemData.TrainingIndices);
 
       alglib.dfreport rep;
@@ -226,5 +202,5 @@
 
       return new RandomForestModel(dForest,
-        seed, dataset, trainingRows, allowedInputVariables, targetVariable,
+        seed, problemData,
         nTrees, r, m);
     }
@@ -232,23 +208,9 @@
     public static RandomForestModel CreateClassificationModel(IClassificationProblemData problemData, int nTrees, double r, double m, int seed,
       out double rmsError, out double outOfBagRmsError, out double relClassificationError, out double outOfBagRelClassificationError) {
-      return CreateClassificationModel(problemData.Dataset, problemData.TrainingIndices,
-                                       problemData.AllowedInputVariables, problemData.TargetVariable,
-                                       problemData.ClassValues.ToArray(),
-                                       nTrees, r, m, seed,
-                                       out rmsError, out outOfBagRmsError, out relClassificationError,
-                                       out outOfBagRelClassificationError);
-    }
-
-    private static RandomForestModel CreateClassificationModel(
-      // prob param
-        Dataset dataset, IEnumerable<int> trainingRows, IEnumerable<string> allowedInputVariables, string targetVariable, double[] classValues,
-      // alg param
-        int nTrees, double r, double m, int seed,
-      // results
-        out double rmsError, out double outOfBagRmsError, out double relClassificationError, out double outOfBagRelClassificationError) {
-
-      var variables = allowedInputVariables.Concat(new string[] { targetVariable });
-      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(dataset, variables, trainingRows);
-
+
+      var variables = problemData.AllowedInputVariables.Concat(new string[] { problemData.TargetVariable });
+      double[,] inputMatrix = AlglibUtil.PrepareInputMatrix(problemData.Dataset, variables, problemData.TrainingIndices);
+
+      var classValues = problemData.ClassValues.ToArray();
       int nClasses = classValues.Length;
 
@@ -274,5 +236,5 @@
 
       return new RandomForestModel(dForest,
-        seed, dataset, trainingRows, allowedInputVariables, targetVariable,
+        seed, problemData,
         nTrees, r, m, classValues);
     }
@@ -284,5 +246,5 @@
       int info = 0;
       alglib.math.rndobject = new System.Random(seed);
-      alglib.decisionforest dForest = new alglib.decisionforest();
+      var dForest = new alglib.decisionforest();
       rep = new alglib.dfreport();
       int nRows = inputMatrix.GetLength(0);
@@ -307,30 +269,64 @@
 
     #region persistence for backwards compatibility
-    [Storable(AllowOneWay = true)]
+    // when the originalTrainingData is null this means the model was loaded from an old file 
+    // therefore, we cannot use the new persistence mechanism because the original data is not available anymore
+    // in such cases we still store the compete model
+    private bool IsCompatibilityLoaded { get { return originalTrainingData == null; } }
+
+    private string[] allowedInputVariables;
+    [Storable(Name = "allowedInputVariables")]
+    private string[] AllowedInputVariables {
+      get {
+        if (IsCompatibilityLoaded) return allowedInputVariables;
+        else return originalTrainingData.AllowedInputVariables.ToArray();
+      }
+      set { allowedInputVariables = value; }
+    }
+    [Storable]
     private int RandomForestBufSize {
+      get {
+        if (IsCompatibilityLoaded) return randomForest.innerobj.bufsize;
+        else return 0;
+      }
       set {
         randomForest.innerobj.bufsize = value;
       }
     }
-    [Storable(AllowOneWay = true)]
+    [Storable]
     private int RandomForestNClasses {
+      get {
+        if (IsCompatibilityLoaded) return randomForest.innerobj.nclasses;
+        else return 0;
+      }
       set {
         randomForest.innerobj.nclasses = value;
       }
     }
-    [Storable(AllowOneWay = true)]
+    [Storable]
     private int RandomForestNTrees {
+      get {
+        if (IsCompatibilityLoaded) return randomForest.innerobj.ntrees;
+        else return 0;
+      }
       set {
         randomForest.innerobj.ntrees = value;
       }
     }
-    [Storable(AllowOneWay = true)]
+    [Storable]
     private int RandomForestNVars {
+      get {
+        if (IsCompatibilityLoaded) return randomForest.innerobj.nvars;
+        else return 0;
+      }
       set {
         randomForest.innerobj.nvars = value;
       }
     }
-    [Storable(AllowOneWay = true)]
+    [Storable]
     private double[] RandomForestTrees {
+      get {
+        if (IsCompatibilityLoaded) return randomForest.innerobj.trees;
+        else return new double[] { };
+      }
       set {
         randomForest.innerobj.trees = value;
