Index: /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs
===================================================================
--- /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs	(revision 14755)
+++ /branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs	(revision 14756)
@@ -22,5 +22,4 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics.Contracts;
 using System.Linq;
 using AutoDiff;
@@ -110,7 +109,7 @@
     [StorableHook(HookType.AfterDeserialization)]
     private void AfterDeserialization() {
-      if (!Parameters.ContainsKey(UpdateConstantsInTreeParameterName))
+      if(!Parameters.ContainsKey(UpdateConstantsInTreeParameterName))
         Parameters.Add(new FixedValueParameter<BoolValue>(UpdateConstantsInTreeParameterName, "Determines if the constants in the tree should be overwritten by the optimized constants.", new BoolValue(true)));
-      if (!Parameters.ContainsKey(UpdateVariableWeightsParameterName))
+      if(!Parameters.ContainsKey(UpdateVariableWeightsParameterName))
         Parameters.Add(new FixedValueParameter<BoolValue>(UpdateVariableWeightsParameterName, "Determines if the variable weights in the tree should be  optimized.", new BoolValue(true)));
     }
@@ -119,10 +118,10 @@
       var solution = SymbolicExpressionTreeParameter.ActualValue;
       double quality;
-      if (RandomParameter.ActualValue.NextDouble() < ConstantOptimizationProbability.Value) {
+      if(RandomParameter.ActualValue.NextDouble() < ConstantOptimizationProbability.Value) {
         IEnumerable<int> constantOptimizationRows = GenerateRowsToEvaluate(ConstantOptimizationRowsPercentage.Value);
         quality = OptimizeConstants(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, solution, ProblemDataParameter.ActualValue,
            constantOptimizationRows, ApplyLinearScalingParameter.ActualValue.Value, ConstantOptimizationIterations.Value, updateVariableWeights: UpdateVariableWeights, lowerEstimationLimit: EstimationLimitsParameter.ActualValue.Lower, upperEstimationLimit: EstimationLimitsParameter.ActualValue.Upper, updateConstantsInTree: UpdateConstantsInTree);
 
-        if (ConstantOptimizationRowsPercentage.Value != RelativeNumberOfEvaluatedSamplesParameter.ActualValue.Value) {
+        if(ConstantOptimizationRowsPercentage.Value != RelativeNumberOfEvaluatedSamplesParameter.ActualValue.Value) {
           var evaluationRows = GenerateRowsToEvaluate();
           quality = SymbolicRegressionSingleObjectivePearsonRSquaredEvaluator.Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, solution, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, ProblemDataParameter.ActualValue, evaluationRows, ApplyLinearScalingParameter.ActualValue.Value);
@@ -177,21 +176,32 @@
 
 
-    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<string> categoricalVariableValues = 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>();
+      //List<string> variableNames = new List<string>();
+      //List<string> categoricalVariableValues = new List<string>();
+      //List<int> lags = new List<int>();
 
       AutoDiff.Term func;
-      if (!TryTransformToAutoDiff(tree.Root.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, 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; // gkronber: constant expressions always have a R² of 0.0 
-
-      AutoDiff.IParametricCompiledTerm compiledFunc = func.Compile(variables.ToArray(), parameters.ToArray());
+      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)
+      if(updateVariableWeights)
         terminalNodes = tree.Root.IterateNodesPrefix().OfType<SymbolicExpressionTreeTerminalNode>().ToList();
       else
@@ -207,18 +217,18 @@
         c[1] = 1.0;
         int i = 2;
-        foreach (var node in terminalNodes) {
+        foreach(var node in terminalNodes) {
           ConstantTreeNode constantTreeNode = node as ConstantTreeNode;
           VariableTreeNode variableTreeNode = node as VariableTreeNode;
           BinaryFactorVariableTreeNode binFactorVarTreeNode = node as BinaryFactorVariableTreeNode;
           FactorVariableTreeNode factorVarTreeNode = node as FactorVariableTreeNode;
-          if (constantTreeNode != null)
+          if(constantTreeNode != null)
             c[i++] = constantTreeNode.Value;
-          else if (updateVariableWeights && variableTreeNode != null)
+          else if(updateVariableWeights && variableTreeNode != null)
             c[i++] = variableTreeNode.Weight;
-          else if (updateVariableWeights && binFactorVarTreeNode != null)
+          else if(updateVariableWeights && binFactorVarTreeNode != null)
             c[i++] = binFactorVarTreeNode.Weight;
-          else if (factorVarTreeNode != null) {
+          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;
+            foreach(var w in factorVarTreeNode.Weights) c[i++] = w;
           }
         }
@@ -229,17 +239,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];
-          if (ds.VariableHasType<double>(variableNames[col])) {
-            x[row, col] = ds.GetDoubleValue(variableNames[col], r + lag);
-          } else if (ds.VariableHasType<string>(variableNames[col])) {
-            x[row, col] = ds.GetStringValue(variableNames[col], r) == categoricalVariableValues[col] ? 1 : 0;
+      foreach(var r in rows) {
+        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++;
@@ -258,17 +271,17 @@
         //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);
-      } catch (ArithmeticException) {
+        alglib.lsfitresults(state, out retVal, out c, out rep);
+      } catch(ArithmeticException) {
         return originalQuality;
-      } catch (alglib.alglibexception) {
+      } catch(alglib.alglibexception) {
         return originalQuality;
       }
 
-      //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);
 
-      if (!updateConstantsInTree) UpdateConstants(tree, originalConstants.Skip(2).ToArray(), updateVariableWeights);
-      if (originalQuality - quality > 0.001 || double.IsNaN(quality)) {
+      if(!updateConstantsInTree) UpdateConstants(tree, originalConstants.Skip(2).ToArray(), updateVariableWeights);
+      if(originalQuality - quality > 0.001 || double.IsNaN(quality)) {
         UpdateConstants(tree, originalConstants.Skip(2).ToArray(), updateVariableWeights);
         return originalQuality;
@@ -279,17 +292,17 @@
     private static void UpdateConstants(ISymbolicExpressionTree tree, double[] constants, bool updateVariableWeights) {
       int i = 0;
-      foreach (var node in tree.Root.IterateNodesPrefix().OfType<SymbolicExpressionTreeTerminalNode>()) {
+      foreach(var node in tree.Root.IterateNodesPrefix().OfType<SymbolicExpressionTreeTerminalNode>()) {
         ConstantTreeNode constantTreeNode = node as ConstantTreeNode;
         VariableTreeNode variableTreeNode = node as VariableTreeNode;
         BinaryFactorVariableTreeNode binFactorVarTreeNode = node as BinaryFactorVariableTreeNode;
         FactorVariableTreeNode factorVarTreeNode = node as FactorVariableTreeNode;
-        if (constantTreeNode != null)
+        if(constantTreeNode != null)
           constantTreeNode.Value = constants[i++];
-        else if (updateVariableWeights && variableTreeNode != null)
+        else if(updateVariableWeights && variableTreeNode != null)
           variableTreeNode.Weight = constants[i++];
-        else if (updateVariableWeights && binFactorVarTreeNode != null)
+        else if(updateVariableWeights && binFactorVarTreeNode != null)
           binFactorVarTreeNode.Weight = constants[i++];
-        else if (factorVarTreeNode != null) {
-          for (int j = 0; j < factorVarTreeNode.Weights.Length; j++)
+        else if(factorVarTreeNode != null) {
+          for(int j = 0; j < factorVarTreeNode.Weights.Length; j++)
             factorVarTreeNode.Weights[j] = constants[i++];
         }
@@ -311,7 +324,8 @@
     }
 
-    private static bool TryTransformToAutoDiff(ISymbolicExpressionTreeNode node, List<AutoDiff.Variable> variables, List<AutoDiff.Variable> parameters,
-      List<string> variableNames, List<int> lags, List<string> categoricalVariableValues, bool updateVariableWeights, out AutoDiff.Term term) {
-      if (node.Symbol is Constant) {
+    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();
         variables.Add(var);
@@ -319,13 +333,12 @@
         return true;
       }
-      if (node.Symbol is Variable || node.Symbol is BinaryFactorVariable) {
+      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(varNode.VariableName, varValue, parameters, variableNames, categoricalVariableValues);
-        lags.Add(0);
-
-        if (updateVariableWeights) {
+        var par = FindOrCreateParameter(parameters, varNode.VariableName, varValue);
+
+        if(updateVariableWeights) {
           var w = new AutoDiff.Variable();
           variables.Add(w);
@@ -336,10 +349,9 @@
         return true;
       }
-      if (node.Symbol is FactorVariable) {
+      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(factorVarNode.VariableName, variableValue, parameters, variableNames, categoricalVariableValues);
-          lags.Add(0);
+        foreach(var variableValue in factorVarNode.Symbol.GetVariableValues(factorVarNode.VariableName)) {
+          var par = FindOrCreateParameter(parameters, factorVarNode.VariableName, variableValue);
 
           var wVar = new AutoDiff.Variable();
@@ -351,12 +363,9 @@
         return true;
       }
-      if (node.Symbol is LaggedVariable) {
+      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);
-
-        if (updateVariableWeights) {
+        var par = FindOrCreateParameter(parameters, varNode.VariableName, string.Empty, varNode.Lag);
+
+        if(updateVariableWeights) {
           var w = new AutoDiff.Variable();
           variables.Add(w);
@@ -367,9 +376,9 @@
         return true;
       }
-      if (node.Symbol is Addition) {
+      if(node.Symbol is Addition) {
         List<AutoDiff.Term> terms = new List<Term>();
-        foreach (var subTree in node.Subtrees) {
+        foreach(var subTree in node.Subtrees) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(subTree, variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+          if(!TryTransformToAutoDiff(subTree, variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
@@ -380,24 +389,24 @@
         return true;
       }
-      if (node.Symbol is Subtraction) {
+      if(node.Symbol is Subtraction) {
         List<AutoDiff.Term> terms = new List<Term>();
-        for (int i = 0; i < node.SubtreeCount; i++) {
+        for(int i = 0; i < node.SubtreeCount; i++) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(node.GetSubtree(i), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+          if(!TryTransformToAutoDiff(node.GetSubtree(i), variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
           }
-          if (i > 0) t = -t;
+          if(i > 0) t = -t;
           terms.Add(t);
         }
-        if (terms.Count == 1) term = -terms[0];
+        if(terms.Count == 1) term = -terms[0];
         else term = AutoDiff.TermBuilder.Sum(terms);
         return true;
       }
-      if (node.Symbol is Multiplication) {
+      if(node.Symbol is Multiplication) {
         List<AutoDiff.Term> terms = new List<Term>();
-        foreach (var subTree in node.Subtrees) {
+        foreach(var subTree in node.Subtrees) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(subTree, variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+          if(!TryTransformToAutoDiff(subTree, variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
@@ -405,14 +414,14 @@
           terms.Add(t);
         }
-        if (terms.Count == 1) term = terms[0];
+        if(terms.Count == 1) term = terms[0];
         else term = terms.Aggregate((a, b) => new AutoDiff.Product(a, b));
         return true;
 
       }
-      if (node.Symbol is Division) {
+      if(node.Symbol is Division) {
         List<AutoDiff.Term> terms = new List<Term>();
-        foreach (var subTree in node.Subtrees) {
+        foreach(var subTree in node.Subtrees) {
           AutoDiff.Term t;
-          if (!TryTransformToAutoDiff(subTree, variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+          if(!TryTransformToAutoDiff(subTree, variables, parameters, updateVariableWeights, out t)) {
             term = null;
             return false;
@@ -420,11 +429,11 @@
           terms.Add(t);
         }
-        if (terms.Count == 1) term = 1.0 / terms[0];
+        if(terms.Count == 1) term = 1.0 / terms[0];
         else term = terms.Aggregate((a, b) => new AutoDiff.Product(a, 1.0 / b));
         return true;
       }
-      if (node.Symbol is Logarithm) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Logarithm) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -434,7 +443,7 @@
         }
       }
-      if (node.Symbol is Exponential) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Exponential) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -444,7 +453,7 @@
         }
       }
-      if (node.Symbol is Square) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Square) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -454,7 +463,7 @@
         }
       }
-      if (node.Symbol is SquareRoot) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is SquareRoot) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -464,7 +473,7 @@
         }
       }
-      if (node.Symbol is Sine) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Sine) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -474,7 +483,7 @@
         }
       }
-      if (node.Symbol is Cosine) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Cosine) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -484,7 +493,7 @@
         }
       }
-      if (node.Symbol is Tangent) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Tangent) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -494,7 +503,7 @@
         }
       }
-      if (node.Symbol is Erf) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Erf) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -504,7 +513,7 @@
         }
       }
-      if (node.Symbol is Norm) {
-        AutoDiff.Term t;
-        if (!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out t)) {
+      if(node.Symbol is Norm) {
+        AutoDiff.Term t;
+        if(!TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out t)) {
           term = null;
           return false;
@@ -514,5 +523,5 @@
         }
       }
-      if (node.Symbol is StartSymbol) {
+      if(node.Symbol is StartSymbol) {
         var alpha = new AutoDiff.Variable();
         var beta = new AutoDiff.Variable();
@@ -520,5 +529,5 @@
         variables.Add(alpha);
         AutoDiff.Term branchTerm;
-        if (TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, variableNames, lags, categoricalVariableValues, updateVariableWeights, out branchTerm)) {
+        if(TryTransformToAutoDiff(node.GetSubtree(0), variables, parameters, updateVariableWeights, out branchTerm)) {
           term = branchTerm * alpha + beta;
           return true;
@@ -534,24 +543,13 @@
     // 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(string varName, string varValue,
-      List<AutoDiff.Variable> parameters, List<string> variableNames, List<string> variableValues) {
-      Contract.Assert(variableNames.Count == variableValues.Count);
-      int idx = -1;
-      for (int i = 0; i < variableNames.Count; i++) {
-        if (variableNames[i] == varName && variableValues[i] == varValue) {
-          idx = i;
-          break;
-        }
-      }
+    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 (idx == -1) {
+      if(!parameters.TryGetValue(data, out par)) {
         // not found -> create new parameter and entries in names and values lists
         par = new AutoDiff.Variable();
-        parameters.Add(par);
-        variableNames.Add(varName);
-        variableValues.Add(varValue);
-      } else {
-        par = parameters[idx];
+        parameters.Add(data, par);
       }
       return par;
@@ -585,4 +583,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
   }
 }
