Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithm.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithm.cs	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithm.cs	(revision 17045)
@@ -269,5 +269,5 @@
 
         if (ModelCreation == ModelCreation.SurrogateModel) {
-          model = new GradientBoostedTreesModelSurrogate(problemData, (uint)Seed, lossFunction, Iterations, MaxSize, R, M, Nu, (GradientBoostedTreesModel)model);
+          model = new GradientBoostedTreesModelSurrogate((GradientBoostedTreesModel)model, problemData, (uint)Seed, lossFunction, Iterations, MaxSize, R, M, Nu);
         }
 
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesModel.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesModel.cs	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesModel.cs	(revision 17045)
@@ -34,7 +34,4 @@
   // this is essentially a collection of weighted regression models
   public sealed class GradientBoostedTreesModel : RegressionModel, IGradientBoostedTreesModel {
-    // BackwardsCompatibility3.4 for allowing deserialization & serialization of old models
-    #region Backwards compatible code, remove with 3.5
-
     [Storable(Name = "models")]
     private IList<IRegressionModel> __persistedModels {
@@ -53,5 +50,4 @@
       get { return weights; }
     }
-    #endregion
 
     public override IEnumerable<string> VariablesUsedForPrediction {
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesModelSurrogate.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesModelSurrogate.cs	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesModelSurrogate.cs	(revision 17045)
@@ -90,10 +90,10 @@
     private Func<IGradientBoostedTreesModel> CreateLazyInitFunc(IGradientBoostedTreesModel clonedModel) {
       return () => {
-        return clonedModel == null ? RecalculateModel() : clonedModel;
+        return clonedModel ?? RecalculateModel();
       };
     }
 
     // create only the surrogate model without an actual model
-    public GradientBoostedTreesModelSurrogate(IRegressionProblemData trainingProblemData, uint seed,
+    private GradientBoostedTreesModelSurrogate(IRegressionProblemData trainingProblemData, uint seed,
       ILossFunction lossFunction, int iterations, int maxSize, double r, double m, double nu)
       : base(trainingProblemData.TargetVariable, "Gradient boosted tree model", string.Empty) {
@@ -106,10 +106,11 @@
       this.m = m;
       this.nu = nu;
+
+      actualModel = new Lazy<IGradientBoostedTreesModel>(() => RecalculateModel());
     }
 
     // wrap an actual model in a surrograte
-    public GradientBoostedTreesModelSurrogate(IRegressionProblemData trainingProblemData, uint seed,
-      ILossFunction lossFunction, int iterations, int maxSize, double r, double m, double nu,
-      IGradientBoostedTreesModel model)
+    public GradientBoostedTreesModelSurrogate(IGradientBoostedTreesModel model, IRegressionProblemData trainingProblemData, uint seed,
+      ILossFunction lossFunction, int iterations, int maxSize, double r, double m, double nu)
       : this(trainingProblemData, seed, lossFunction, iterations, maxSize, r, m, nu) {
       actualModel = new Lazy<IGradientBoostedTreesModel>(() => model);
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis-3.4.csproj	(revision 17045)
@@ -394,6 +394,8 @@
     <Compile Include="RandomForest\RandomForestClassification.cs" />
     <Compile Include="RandomForest\RandomForestModel.cs" />
+    <Compile Include="RandomForest\RandomForestModelFull.cs" />
     <Compile Include="RandomForest\RandomForestRegression.cs" />
     <Compile Include="RandomForest\RandomForestRegressionSolution.cs" />
+    <Compile Include="RandomForest\RandomForestModelSurrogate.cs" />
     <Compile Include="RandomForest\RandomForestUtil.cs" />
     <Compile Include="SupportVectorMachine\SupportVectorClassification.cs" />
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestClassification.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestClassification.cs	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestClassification.cs	(revision 17045)
@@ -21,4 +21,5 @@
 
 using System.Threading;
+using HEAL.Attic;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -26,5 +27,4 @@
 using HeuristicLab.Optimization;
 using HeuristicLab.Parameters;
-using HEAL.Attic;
 using HeuristicLab.Problems.DataAnalysis;
 
@@ -144,5 +144,5 @@
 
       if (CreateSolution) {
-        var solution = new RandomForestClassificationSolution(model, (IClassificationProblemData)Problem.ProblemData.Clone());
+        var solution = model.CreateClassificationSolution(Problem.ProblemData);
         Results.Add(new Result(RandomForestClassificationModelResultName, "The random forest classification solution.", solution));
       }
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModel.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModel.cs	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModel.cs	(revision 17045)
@@ -23,8 +23,8 @@
 using System.Collections.Generic;
 using System.Linq;
+using HEAL.Attic;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
 using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
-using HEAL.Attic;
 using HeuristicLab.Problems.DataAnalysis;
 using HeuristicLab.Problems.DataAnalysis.Symbolic;
@@ -34,5 +34,6 @@
   /// Represents a random forest model for regression and classification
   /// </summary>
-  [StorableType("A4F688CD-1F42-4103-8449-7DE52AEF6C69")]
+  [Obsolete("This class only exists for backwards compatibility reasons. Use RFModelSurrogate or RFModelFull instead.")]
+  [StorableType("9AA4CCC2-CD75-4471-8DF6-949E5B783642")]
   [Item("RandomForestModel", "Represents a random forest for regression and classification.")]
   public sealed class RandomForestModel : ClassificationModel, IRandomForestModel {
@@ -139,5 +140,5 @@
     public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
       double[,] inputData = dataset.ToArray(AllowedInputVariables, rows);
-      AssertInputMatrix(inputData);
+      RandomForestUtil.AssertInputMatrix(inputData);
 
       int n = inputData.GetLength(0);
@@ -157,5 +158,5 @@
     public IEnumerable<double> GetEstimatedVariances(IDataset dataset, IEnumerable<int> rows) {
       double[,] inputData = dataset.ToArray(AllowedInputVariables, rows);
-      AssertInputMatrix(inputData);
+      RandomForestUtil.AssertInputMatrix(inputData);
 
       int n = inputData.GetLength(0);
@@ -175,5 +176,5 @@
     public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
       double[,] inputData = dataset.ToArray(AllowedInputVariables, rows);
-      AssertInputMatrix(inputData);
+      RandomForestUtil.AssertInputMatrix(inputData);
 
       int n = inputData.GetLength(0);
@@ -315,5 +316,5 @@
 
       alglib.dfreport rep;
-      var dForest = CreateRandomForestModel(seed, inputMatrix, nTrees, r, m, 1, out rep);
+      var dForest = RandomForestUtil.CreateRandomForestModel(seed, inputMatrix, nTrees, r, m, 1, out rep);
 
       rmsError = rep.rmserror;
@@ -353,5 +354,5 @@
 
       alglib.dfreport rep;
-      var dForest = CreateRandomForestModel(seed, inputMatrix, nTrees, r, m, nClasses, out rep);
+      var dForest = RandomForestUtil.CreateRandomForestModel(seed, inputMatrix, nTrees, r, m, nClasses, out rep);
 
       rmsError = rep.rmserror;
@@ -361,32 +362,4 @@
 
       return new RandomForestModel(problemData.TargetVariable, dForest, seed, problemData, nTrees, r, m, classValues);
-    }
-
-    private static alglib.decisionforest CreateRandomForestModel(int seed, double[,] inputMatrix, int nTrees, double r, double m, int nClasses, out alglib.dfreport rep) {
-      AssertParameters(r, m);
-      AssertInputMatrix(inputMatrix);
-
-      int info = 0;
-      alglib.math.rndobject = new System.Random(seed);
-      var dForest = new alglib.decisionforest();
-      rep = new alglib.dfreport();
-      int nRows = inputMatrix.GetLength(0);
-      int nColumns = inputMatrix.GetLength(1);
-      int sampleSize = Math.Max((int)Math.Round(r * nRows), 1);
-      int nFeatures = Math.Max((int)Math.Round(m * (nColumns - 1)), 1);
-
-      alglib.dforest.dfbuildinternal(inputMatrix, nRows, nColumns - 1, nClasses, nTrees, sampleSize, nFeatures, alglib.dforest.dfusestrongsplits + alglib.dforest.dfuseevs, ref info, dForest.innerobj, rep.innerobj);
-      if (info != 1) throw new ArgumentException("Error in calculation of random forest model");
-      return dForest;
-    }
-
-    private static void AssertParameters(double r, double m) {
-      if (r <= 0 || r > 1) throw new ArgumentException("The R parameter for random forest modeling must be between 0 and 1.");
-      if (m <= 0 || m > 1) throw new ArgumentException("The M parameter for random forest modeling must be between 0 and 1.");
-    }
-
-    private static void AssertInputMatrix(double[,] inputMatrix) {
-      if (inputMatrix.ContainsNanOrInfinity())
-        throw new NotSupportedException("Random forest modeling does not support NaN or infinity values in the input dataset.");
     }
 
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModelFull.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModelFull.cs	(revision 17045)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModelFull.cs	(revision 17045)
@@ -0,0 +1,276 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HEAL.Attic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.DataAnalysis.Symbolic;
+
+namespace HeuristicLab.Algorithms.DataAnalysis {
+  [StorableType("9C797DF0-1169-4381-A732-6DAB90802839")]
+  [Item("RandomForestModelFull", "Represents a random forest for regression and classification.")]
+  public sealed class RandomForestModelFull : ClassificationModel, IRandomForestModel {
+
+    public override IEnumerable<string> VariablesUsedForPrediction {
+      get { return inputVariables; }
+    }
+
+    [Storable]
+    private double[] classValues;
+
+    [Storable]
+    private string[] inputVariables;
+
+    public int NumberOfTrees {
+      get { return RandomForestNTrees; }
+    }
+
+    // not persisted
+    private alglib.decisionforest randomForest;
+
+    [Storable]
+    private int RandomForestBufSize {
+      get { return randomForest.innerobj.bufsize; }
+      set { randomForest.innerobj.bufsize = value; }
+    }
+    [Storable]
+    private int RandomForestNClasses {
+      get { return randomForest.innerobj.nclasses; }
+      set { randomForest.innerobj.nclasses = value; }
+    }
+    [Storable]
+    private int RandomForestNTrees {
+      get { return randomForest.innerobj.ntrees; }
+      set { randomForest.innerobj.ntrees = value; }
+    }
+    [Storable]
+    private int RandomForestNVars {
+      get { return randomForest.innerobj.nvars; }
+      set { randomForest.innerobj.nvars = value; }
+    }
+    [Storable]
+    private double[] RandomForestTrees {
+      get { return randomForest.innerobj.trees; }
+      set { randomForest.innerobj.trees = value; }
+    }
+
+    [StorableConstructor]
+    private RandomForestModelFull(StorableConstructorFlag _) : base(_) {
+      randomForest = new alglib.decisionforest();
+    }
+
+    private RandomForestModelFull(RandomForestModelFull original, Cloner cloner) : base(original, cloner) {
+      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;
+      randomForest.innerobj.trees = (double[])original.randomForest.innerobj.trees.Clone();
+
+      // following fields are immutable so we don't need to clone them
+      inputVariables = original.inputVariables;
+      classValues = original.classValues;
+    }
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RandomForestModelFull(this, cloner);
+    }
+
+    public RandomForestModelFull(alglib.decisionforest decisionForest, string targetVariable, IEnumerable<string> inputVariables, IEnumerable<double> classValues = null) : base(targetVariable) {
+      randomForest = decisionForest;
+
+      this.inputVariables = inputVariables.ToArray();
+
+      //classValues are only use for classification models
+      if (classValues == null) this.classValues = new double[0];
+      this.classValues = classValues.ToArray();
+    }
+
+
+    public IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
+      return new RandomForestRegressionSolution(this, new RegressionProblemData(problemData));
+    }
+    public override IClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
+      return new RandomForestClassificationSolution(this, new ClassificationProblemData(problemData));
+    }
+
+    public bool IsProblemDataCompatible(IRegressionProblemData problemData, out string errorMessage) {
+      return RegressionModel.IsProblemDataCompatible(this, problemData, out errorMessage);
+    }
+
+    public override bool IsProblemDataCompatible(IDataAnalysisProblemData problemData, out string errorMessage) {
+      if (problemData == null) throw new ArgumentNullException("problemData", "The provided problemData is null.");
+
+      var regressionProblemData = problemData as IRegressionProblemData;
+      if (regressionProblemData != null)
+        return IsProblemDataCompatible(regressionProblemData, out errorMessage);
+
+      var classificationProblemData = problemData as IClassificationProblemData;
+      if (classificationProblemData != null)
+        return IsProblemDataCompatible(classificationProblemData, out errorMessage);
+
+      throw new ArgumentException("The problem data is not compatible with this random forest. Instead a " + problemData.GetType().GetPrettyName() + " was provided.", "problemData");
+    }
+
+    public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
+      double[,] inputData = dataset.ToArray(inputVariables, rows);
+      RandomForestUtil.AssertInputMatrix(inputData);
+
+      int n = inputData.GetLength(0);
+      int columns = inputData.GetLength(1);
+      double[] x = new double[columns];
+      double[] y = new double[1];
+
+      for (int row = 0; row < n; row++) {
+        for (int column = 0; column < columns; column++) {
+          x[column] = inputData[row, column];
+        }
+        alglib.dfprocess(randomForest, x, ref y);
+        yield return y[0];
+      }
+    }
+    public IEnumerable<double> GetEstimatedVariances(IDataset dataset, IEnumerable<int> rows) {
+      double[,] inputData = dataset.ToArray(inputVariables, rows);
+      RandomForestUtil.AssertInputMatrix(inputData);
+
+      int n = inputData.GetLength(0);
+      int columns = inputData.GetLength(1);
+      double[] x = new double[columns];
+      double[] ys = new double[this.randomForest.innerobj.ntrees];
+
+      for (int row = 0; row < n; row++) {
+        for (int column = 0; column < columns; column++) {
+          x[column] = inputData[row, column];
+        }
+        alglib.dforest.dfprocessraw(randomForest.innerobj, x, ref ys);
+        yield return ys.VariancePop();
+      }
+    }
+
+    public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
+      double[,] inputData = dataset.ToArray(inputVariables, rows);
+      RandomForestUtil.AssertInputMatrix(inputData);
+
+      int n = inputData.GetLength(0);
+      int columns = inputData.GetLength(1);
+      double[] x = new double[columns];
+      double[] y = new double[randomForest.innerobj.nclasses];
+
+      for (int row = 0; row < n; row++) {
+        for (int column = 0; column < columns; column++) {
+          x[column] = inputData[row, column];
+        }
+        alglib.dfprocess(randomForest, x, ref y);
+        // find class for with the largest probability value
+        int maxProbClassIndex = 0;
+        double maxProb = y[0];
+        for (int i = 1; i < y.Length; i++) {
+          if (maxProb < y[i]) {
+            maxProb = y[i];
+            maxProbClassIndex = i;
+          }
+        }
+        yield return classValues[maxProbClassIndex];
+      }
+    }
+
+    public ISymbolicExpressionTree ExtractTree(int treeIdx) {
+      var rf = randomForest;
+      // hoping that the internal representation of alglib is stable
+
+      // TREE FORMAT
+      // W[Offs]      -   size of sub-array (for the tree)
+      //     node info:
+      // W[K+0]       -   variable number        (-1 for leaf mode)
+      // W[K+1]       -   threshold              (class/value for leaf node)
+      // W[K+2]       -   ">=" branch index      (absent for leaf node)
+
+      // skip irrelevant trees
+      int offset = 0;
+      for (int i = 0; i < treeIdx - 1; i++) {
+        offset = offset + (int)Math.Round(rf.innerobj.trees[offset]);
+      }
+
+      var constSy = new Constant();
+      var varCondSy = new VariableCondition() { IgnoreSlope = true };
+
+      var node = CreateRegressionTreeRec(rf.innerobj.trees, offset, offset + 1, constSy, varCondSy);
+
+      var startNode = new StartSymbol().CreateTreeNode();
+      startNode.AddSubtree(node);
+      var root = new ProgramRootSymbol().CreateTreeNode();
+      root.AddSubtree(startNode);
+      return new SymbolicExpressionTree(root);
+    }
+
+    private ISymbolicExpressionTreeNode CreateRegressionTreeRec(double[] trees, int offset, int k, Constant constSy, VariableCondition varCondSy) {
+
+      // alglib source for evaluation of one tree (dfprocessinternal)
+      // offs = 0
+      //
+      // Set pointer to the root
+      //
+      // k = offs + 1;
+      // 
+      // //
+      // // Navigate through the tree
+      // //
+      // while (true) {
+      //   if ((double)(df.trees[k]) == (double)(-1)) {
+      //     if (df.nclasses == 1) {
+      //       y[0] = y[0] + df.trees[k + 1];
+      //     } else {
+      //       idx = (int)Math.Round(df.trees[k + 1]);
+      //       y[idx] = y[idx] + 1;
+      //     }
+      //     break;
+      //   }
+      //   if ((double)(x[(int)Math.Round(df.trees[k])]) < (double)(df.trees[k + 1])) {
+      //     k = k + innernodewidth;
+      //   } else {
+      //     k = offs + (int)Math.Round(df.trees[k + 2]);
+      //   }
+      // }
+
+      if ((double)(trees[k]) == (double)(-1)) {
+        var constNode = (ConstantTreeNode)constSy.CreateTreeNode();
+        constNode.Value = trees[k + 1];
+        return constNode;
+      } else {
+        var condNode = (VariableConditionTreeNode)varCondSy.CreateTreeNode();
+        condNode.VariableName = inputVariables[(int)Math.Round(trees[k])];
+        condNode.Threshold = trees[k + 1];
+        condNode.Slope = double.PositiveInfinity;
+
+        var left = CreateRegressionTreeRec(trees, offset, k + 3, constSy, varCondSy);
+        var right = CreateRegressionTreeRec(trees, offset, offset + (int)Math.Round(trees[k + 2]), constSy, varCondSy);
+
+        condNode.AddSubtree(left); // not 100% correct because interpreter uses: if(x <= thres) left() else right() and RF uses if(x < thres) left() else right() (see above)
+        condNode.AddSubtree(right);
+        return condNode;
+      }
+    }
+  }
+}
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModelSurrogate.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModelSurrogate.cs	(revision 17045)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestModelSurrogate.cs	(revision 17045)
@@ -0,0 +1,159 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using HEAL.Attic;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.Algorithms.DataAnalysis {
+  [StorableType("A4F688CD-1F42-4103-8449-7DE52AEF6C69")]
+  [Item("RandomForestSurrogateModel", "Represents a random forest for regression and classification.")]
+  public sealed class RandomForestModelSurrogate : ClassificationModel, IRandomForestModel {
+
+    #region parameters for recalculation of the model
+    [Storable]
+    private int seed;
+    [Storable]
+    private IDataAnalysisProblemData originalTrainingData;
+    [Storable]
+    private double[] classValues;
+    [Storable]
+    private int nTrees;
+    [Storable]
+    private double r;
+    [Storable]
+    private double m;
+    #endregion
+
+    // don't store the actual model!
+    // the actual model is only recalculated when necessary
+    private readonly Lazy<IRandomForestModel> actualModel;
+    private IRandomForestModel ActualModel {
+      get { return actualModel.Value; }
+    }
+
+    public int NumberOfTrees => ActualModel.NumberOfTrees;
+    public override IEnumerable<string> VariablesUsedForPrediction {
+      get { return ActualModel.VariablesUsedForPrediction; }
+    }
+
+    public RandomForestModelSurrogate(string targetVariable, IDataAnalysisProblemData originalTrainingData,
+      int seed, int nTrees, double r, double m, double[] classValues = null)
+      : base(targetVariable) {
+      this.name = ItemName;
+      this.description = ItemDescription;
+
+      // data which is necessary for recalculation of the model
+      this.seed = seed;
+      this.originalTrainingData = (IDataAnalysisProblemData)originalTrainingData.Clone();
+      this.classValues = classValues;
+      this.nTrees = nTrees;
+      this.r = r;
+      this.m = m;
+
+      actualModel = new Lazy<IRandomForestModel>(() => RecalculateModel());
+    }
+
+    // wrap an actual model in a surrograte
+    public RandomForestModelSurrogate(IRandomForestModel model, string targetVariable, IDataAnalysisProblemData originalTrainingData,
+      int seed, int nTrees, double r, double m, double[] classValues = null) : this(targetVariable, originalTrainingData, seed, nTrees, r, m, classValues) {
+      actualModel = new Lazy<IRandomForestModel>(() => model);
+    }
+
+    [StorableConstructor]
+    private RandomForestModelSurrogate(StorableConstructorFlag _) : base(_) {
+      actualModel = new Lazy<IRandomForestModel>(() => RecalculateModel());
+    }
+
+    private RandomForestModelSurrogate(RandomForestModelSurrogate original, Cloner cloner) : base(original, cloner) {
+      IRandomForestModel clonedModel = null;
+      if (original.ActualModel != null) clonedModel = cloner.Clone(original.ActualModel);
+      actualModel = new Lazy<IRandomForestModel>(CreateLazyInitFunc(clonedModel)); // only capture clonedModel in the closure
+
+      // clone data which is necessary to rebuild the model
+      this.originalTrainingData = cloner.Clone(original.originalTrainingData);
+      this.seed = original.seed;
+      this.classValues = original.classValues;
+      this.nTrees = original.nTrees;
+      this.r = original.r;
+      this.m = original.m;
+    }
+
+    private Func<IRandomForestModel> CreateLazyInitFunc(IRandomForestModel clonedModel) {
+      return () => {
+        return clonedModel ?? RecalculateModel();
+      };
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new RandomForestModelSurrogate(this, cloner);
+    }
+
+    private IRandomForestModel RecalculateModel() {
+      IRandomForestModel randomForestModel = null;
+
+      //TODO Refactor to new methods
+      double rmsError, oobRmsError, relClassError, oobRelClassError;
+      var classificationProblemData = originalTrainingData as IClassificationProblemData;
+
+      if (originalTrainingData is IRegressionProblemData regressionProblemData) {
+        randomForestModel = RandomForestModel.CreateRegressionModel(regressionProblemData,
+                                              nTrees, r, m, seed, out rmsError, out oobRmsError,
+                                              out relClassError, out oobRelClassError);
+      } else if (classificationProblemData != null) {
+        randomForestModel = RandomForestModel.CreateClassificationModel(classificationProblemData,
+                                              nTrees, r, m, seed, out rmsError, out oobRmsError,
+                                              out relClassError, out oobRelClassError);
+      }
+      return randomForestModel;
+    }
+
+    //RegressionModel methods
+    public bool IsProblemDataCompatible(IRegressionProblemData problemData, out string errorMessage) {
+      return ActualModel.IsProblemDataCompatible(problemData, out errorMessage);
+    }
+    public IEnumerable<double> GetEstimatedValues(IDataset dataset, IEnumerable<int> rows) {
+      return ActualModel.GetEstimatedValues(dataset, rows);
+    }
+    public IEnumerable<double> GetEstimatedVariances(IDataset dataset, IEnumerable<int> rows) {
+      return ActualModel.GetEstimatedVariances(dataset, rows);
+    }
+    public IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
+      return new RandomForestRegressionSolution(this, (IRegressionProblemData)problemData.Clone());
+    }
+
+    //ClassificationModel methods
+    public override IEnumerable<double> GetEstimatedClassValues(IDataset dataset, IEnumerable<int> rows) {
+      return ActualModel.GetEstimatedClassValues(dataset, rows);
+    }
+    public override IClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
+      return new RandomForestClassificationSolution(this, (IClassificationProblemData)problemData.Clone());
+    }
+
+    public ISymbolicExpressionTree ExtractTree(int treeIdx) {
+      return ActualModel.ExtractTree(treeIdx);
+    }
+  }
+}
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestRegression.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestRegression.cs	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestRegression.cs	(revision 17045)
@@ -20,5 +20,8 @@
 #endregion
 
+using System.Collections.Generic;
+using System.Linq;
 using System.Threading;
+using HEAL.Attic;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -26,5 +29,4 @@
 using HeuristicLab.Optimization;
 using HeuristicLab.Parameters;
-using HEAL.Attic;
 using HeuristicLab.Problems.DataAnalysis;
 
@@ -144,8 +146,9 @@
 
       if (CreateSolution) {
-        var solution = new RandomForestRegressionSolution(model, (IRegressionProblemData)Problem.ProblemData.Clone());
+        var solution = model.CreateRegressionSolution(Problem.ProblemData);
         Results.Add(new Result(RandomForestRegressionModelResultName, "The random forest regression solution.", solution));
       }
     }
+
 
     // keep for compatibility with old API
@@ -157,9 +160,28 @@
     }
 
-    public static RandomForestModel CreateRandomForestRegressionModel(IRegressionProblemData problemData, int nTrees,
-      double r, double m, int seed,
-      out double rmsError, out double avgRelError, out double outOfBagRmsError, out double outOfBagAvgRelError) {
-      return RandomForestModel.CreateRegressionModel(problemData, nTrees, r, m, seed,
-        rmsError: out rmsError, avgRelError: out avgRelError, outOfBagRmsError: out outOfBagRmsError, outOfBagAvgRelError: out outOfBagAvgRelError);
+    public static RandomForestModelFull CreateRandomForestRegressionModel(IRegressionProblemData problemData, int nTrees,
+     double r, double m, int seed,
+     out double rmsError, out double avgRelError, out double outOfBagRmsError, out double outOfBagAvgRelError) {
+      return CreateRandomForestRegressionModel(problemData, problemData.TrainingIndices, nTrees, r, m, seed, out rmsError, out avgRelError, out outOfBagRmsError, out outOfBagAvgRelError);
+    }
+
+    public static RandomForestModelFull CreateRandomForestRegressionModel(IRegressionProblemData problemData, IEnumerable<int> trainingIndices, int nTrees, double r, double m, int seed,
+    out double rmsError, out double avgRelError, out double outOfBagRmsError, out double outOfBagAvgRelError) {
+
+      var variables = problemData.AllowedInputVariables.Concat(new string[] { problemData.TargetVariable });
+      double[,] inputMatrix = problemData.Dataset.ToArray(variables, trainingIndices);
+
+      alglib.dfreport rep;
+      var dForest = RandomForestUtil.CreateRandomForestModel(seed, inputMatrix, nTrees, r, m, 1, out rep);
+
+      rmsError = rep.rmserror;
+      outOfBagRmsError = rep.oobrmserror;
+      avgRelError = rep.avgrelerror;
+      outOfBagAvgRelError = rep.oobavgrelerror;
+
+      return new RandomForestModelFull(dForest, problemData.TargetVariable, problemData.AllowedInputVariables);
+
+      //return RandomForestModel.CreateRegressionModel(problemData, nTrees, r, m, seed,
+      //rmsError: out rmsError, avgRelError: out avgRelError, outOfBagRmsError: out outOfBagRmsError, outOfBagAvgRelError: out outOfBagAvgRelError);
     }
 
Index: /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestUtil.cs
===================================================================
--- /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestUtil.cs	(revision 17044)
+++ /branches/2952_RF-ModelStorage/HeuristicLab.Algorithms.DataAnalysis/3.4/RandomForest/RandomForestUtil.cs	(revision 17045)
@@ -27,9 +27,9 @@
 using System.Linq.Expressions;
 using System.Threading.Tasks;
+using HEAL.Attic;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
 using HeuristicLab.Data;
 using HeuristicLab.Parameters;
-using HEAL.Attic;
 using HeuristicLab.Problems.DataAnalysis;
 using HeuristicLab.Random;
@@ -89,4 +89,33 @@
 
   public static class RandomForestUtil {
+    public static void AssertParameters(double r, double m) {
+      if (r <= 0 || r > 1) throw new ArgumentException("The R parameter for random forest modeling must be between 0 and 1.");
+      if (m <= 0 || m > 1) throw new ArgumentException("The M parameter for random forest modeling must be between 0 and 1.");
+    }
+
+    public static void AssertInputMatrix(double[,] inputMatrix) {
+      if (inputMatrix.ContainsNanOrInfinity())
+        throw new NotSupportedException("Random forest modeling does not support NaN or infinity values in the input dataset.");
+    }
+
+    internal static alglib.decisionforest CreateRandomForestModel(int seed, double[,] inputMatrix, int nTrees, double r, double m, int nClasses, out alglib.dfreport rep) {
+      RandomForestUtil.AssertParameters(r, m);
+      RandomForestUtil.AssertInputMatrix(inputMatrix);
+
+      int info = 0;
+      alglib.math.rndobject = new System.Random(seed);
+      var dForest = new alglib.decisionforest();
+      rep = new alglib.dfreport();
+      int nRows = inputMatrix.GetLength(0);
+      int nColumns = inputMatrix.GetLength(1);
+      int sampleSize = Math.Max((int)Math.Round(r * nRows), 1);
+      int nFeatures = Math.Max((int)Math.Round(m * (nColumns - 1)), 1);
+
+      alglib.dforest.dfbuildinternal(inputMatrix, nRows, nColumns - 1, nClasses, nTrees, sampleSize, nFeatures, alglib.dforest.dfusestrongsplits + alglib.dforest.dfuseevs, ref info, dForest.innerobj, rep.innerobj);
+      if (info != 1) throw new ArgumentException("Error in calculation of random forest model");
+      return dForest;
+    }
+
+
     private static void CrossValidate(IRegressionProblemData problemData, Tuple<IEnumerable<int>, IEnumerable<int>>[] partitions, int nTrees, double r, double m, int seed, out double avgTestMse) {
       avgTestMse = 0;
