Index: /branches/3022-FastFunctionExtraction/FFX/Approach.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/Approach.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/Approach.cs	(revision 17737)
@@ -0,0 +1,42 @@
+﻿using System;
+using System.Collections.Generic;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    internal struct Approach {
+        public bool AllowInter { get; set; }
+        public bool AllowDenom { get; set; }
+        public bool AllowExp { get; set; }
+        public bool AllowNonlinFuncs { get; set; }
+        public bool AllowHinge { get; set; }
+        public HashSet<double> Exponents { get; set; }
+        public HashSet<NonlinearOperator> NonlinFuncs { get; set; }
+        public double MinHingeThr { get; set; }
+        public double MaxHingeThr { get; set; }
+        public int NumHingeThrs { get; set; }
+        public double ElnetPenalty { get; set; }
+        public int MaxNumBases { get; set; }
+
+        private static int Int(bool b) => b ? 1 : 0;
+
+        public override string ToString() {
+            return $"Inter{Int(AllowInter)} Denom{Int(AllowDenom)} Exp{Int((AllowExp))} Nonlin{Int(AllowNonlinFuncs)} Hinge{Int(AllowHinge)}";
+        }
+
+        public Approach(bool inter, bool denom, bool exp, bool nonlin, bool hinge, HashSet<double> exponents, HashSet<NonlinearOperator> nonlinFuncs, int maxNumBases, double penalty, double minHingeThr, double maxHingeThr, int numHingeThrs) {
+            this.AllowInter = inter;
+            this.AllowDenom = denom;
+            this.AllowExp = exp;
+            this.AllowNonlinFuncs = nonlin;
+            this.AllowHinge = hinge;
+            if (AllowExp && exponents == null) throw new ArgumentNullException(nameof(exponents));
+            Exponents = exponents;
+            if (AllowNonlinFuncs && nonlinFuncs == null) throw new ArgumentNullException(nameof(nonlinFuncs));
+            NonlinFuncs = nonlinFuncs;
+            MinHingeThr = minHingeThr;
+            MaxHingeThr = maxHingeThr;
+            NumHingeThrs = numHingeThrs;
+            ElnetPenalty = penalty;
+            MaxNumBases = maxNumBases;
+        }
+    }
+}
Index: /branches/3022-FastFunctionExtraction/FFX/BFUtils.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/BFUtils.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/BFUtils.cs	(revision 17737)
@@ -0,0 +1,196 @@
+﻿using HeuristicLab.Algorithms.DataAnalysis.Glmnet;
+using HeuristicLab.Data;
+using HeuristicLab.Problems.DataAnalysis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("UnitTests")]
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    // utility functions for creating Basis Functions
+    internal static class BFUtils {
+        public static IEnumerable<IBasisFunction> CreateBasisFunctions(IRegressionProblemData data, Approach approach) {
+            var exponents = approach.AllowExp ? approach.Exponents : new HashSet<double> { 1 };
+            var funcs = approach.AllowNonlinFuncs ? approach.NonlinFuncs : new HashSet<NonlinearOperator> { NonlinearOperator.None };
+            var simpleBasisFuncs = CreateSimpleBases(data, exponents, funcs);
+
+            if (approach.AllowHinge) {
+                // only allow hinge functions for features with exponent = 1 (deemed too complex otherwise)
+                var linearSimpleBasisFuncs = simpleBasisFuncs.Where(simpleBf => simpleBf.Exponent == 1);
+                simpleBasisFuncs = simpleBasisFuncs.Concat(CreateHingeBases(data, linearSimpleBasisFuncs, approach.MinHingeThr, approach.MaxHingeThr, approach.NumHingeThrs));
+            }
+
+            IEnumerable<IBasisFunction> functions = simpleBasisFuncs;
+
+            if (approach.AllowInter) {
+                var multivariateBases = CreateMultivariateBases(data, simpleBasisFuncs.ToArray());
+                functions = functions.Concat(multivariateBases);
+            }
+
+            if (approach.AllowDenom) {
+                var denominatorBases = CreateDenominatorBases(functions);
+                functions = functions.Concat(denominatorBases);
+            }
+            return functions;
+        }
+
+        public static IEnumerable<ISimpleBasisFunction> CreateSimpleBases(IRegressionProblemData problemData, HashSet<double> exponents, HashSet<NonlinearOperator> nonlinFuncs) {
+            var simpleBasisFunctions = new List<ISimpleBasisFunction>();
+            foreach (var variableName in problemData.AllowedInputVariables) {
+                var vals = problemData.Dataset.GetDoubleValues(variableName).ToArray();
+                var min = vals.Min();
+                foreach (var exp in exponents) {
+                    var simpleBase = new SimpleBasisFunction(variableName, exp, NonlinearOperator.None);
+                    // if the basis function is not valid without any operator, then it won't be valid in combination with any nonlinear operator -> skip
+                    if (!Ok(simpleBase.Simulate(problemData))) continue;
+
+                    foreach (NonlinearOperator op in nonlinFuncs) {
+                        // ignore cases where op has no effect
+                        if (op.Equals(NonlinearOperator.Abs) && new[] { -2.0, 2.0 }.Contains(exp) && nonlinFuncs.Contains(NonlinearOperator.None)) continue;
+                        if (op.Equals(NonlinearOperator.Abs) && min >= 0) continue;
+                        var nonsimpleBase = (SimpleBasisFunction)simpleBase.DeepCopy();
+                        nonsimpleBase.Operator = op;
+                        if (!Ok(nonsimpleBase.Simulate(problemData))) continue;
+                        simpleBasisFunctions.Add(nonsimpleBase);
+                    }
+                }
+            }
+            return simpleBasisFunctions;
+        }
+
+        public static IEnumerable<IBasisFunction> CreateMultivariateBases(IRegressionProblemData data, IList<ISimpleBasisFunction> univariateBases) {
+            var orderedFuncs = OrderBasisFuncsByImportance(data, univariateBases).ToArray();
+            var multivariateBases = new List<IBasisFunction>();
+            int maxSize = 2 * orderedFuncs.Length;
+            foreach (var bf in orderedFuncs) {
+                // disallow bases with exponents
+                if (bf.Exponent != 1) continue;
+                multivariateBases.Add(new ProductBaseFunction(bf, bf, true));
+            }
+
+            for (int i = 0; i < orderedFuncs.Count(); i++) {
+                var b_i = orderedFuncs.ElementAt(i);
+                for (int j = 0; j < i; j++) {
+                    var b_j = orderedFuncs.ElementAt(j);
+                    if (b_j.Operator != NonlinearOperator.None) continue; // disallow op() * op(); deemed to complex
+                    var b_inter = new ProductBaseFunction(b_i, b_j, true);
+                    if (!Ok(b_inter.Simulate(data))) continue;
+                    multivariateBases.Add(b_inter);
+                    if (multivariateBases.Count() >= maxSize)
+                        return multivariateBases;
+                }
+            }
+            return multivariateBases;
+        }
+
+        // order basis functions by importance (decr)
+        // the importance of a basis function is measured by the absolute value of its coefficient when optimized on the data
+        public static IEnumerable<ISimpleBasisFunction> OrderBasisFuncsByImportance(IRegressionProblemData data, IList<ISimpleBasisFunction> candidateFunctions) {
+            var elnetData = PrepareData(Normalize(data), candidateFunctions);
+            var coeff = ElasticNetLinearRegression.CalculateModelCoefficients(elnetData, 0, 0, out var trainNMSE, out var testNMSE); // LS-fit
+            var intercept = coeff.Last();
+            coeff = coeff.Take(coeff.Length - 1).ToArray();
+            var order = Utils.Argsort(coeff);
+            Array.Reverse(order);
+            return order.Select(idx => candidateFunctions[idx]);
+        }
+
+        public static IList<ISimpleBasisFunction> CreateHingeBases(IRegressionProblemData data, IEnumerable<ISimpleBasisFunction> simple_bfs, double relative_start_thr = 0.0, double relative_end_thr = 1.0, int num_thrs = 3, IntRange trainingPartition = null) {
+            var hingeBases = new List<ISimpleBasisFunction>();
+
+            foreach (var simple_bf in simple_bfs) {
+                hingeBases.AddRange(CreateHingeBases(data, simple_bf, relative_start_thr, relative_end_thr, num_thrs, trainingPartition));
+            }
+            return hingeBases;
+        }
+
+        private static IEnumerable<ISimpleBasisFunction> CreateHingeBases(IRegressionProblemData data, ISimpleBasisFunction simple_bf, double relative_start_thr, double relative_end_thr, int num_thrs, IntRange trainingPartition) {
+            if (relative_start_thr >= relative_end_thr) throw new ArgumentException($"{nameof(relative_start_thr)} must be smaller than {nameof(relative_end_thr)}.");
+            var ans = new List<ISimpleBasisFunction>();
+
+            var vals = simple_bf.Simulate(data);
+            var temp = trainingPartition ?? data.TrainingPartition;
+            double min = Double.MaxValue;
+            double max = Double.MinValue;
+            for (int i = temp.Start; i < temp.End; i++) {
+                min = Math.Min(min, vals[i]);
+                max = Math.Max(max, vals[i]);
+            }
+            if (max - min == 0) return ans;
+            var full_range = max - min;
+            var start_thr = min + relative_start_thr * full_range;
+            var end_thr = min + relative_end_thr * full_range;
+            var thresholds = Utils.Linspace(start_thr, end_thr, num_thrs);
+
+            foreach (var thr in thresholds) {
+                ans.Add(new SimpleBasisFunction(simple_bf.Feature, 1, NonlinearOperator.Gth, true, thr));
+                ans.Add(new SimpleBasisFunction(simple_bf.Feature, 1, NonlinearOperator.Lth, true, thr));
+            }
+            return ans;
+        }
+
+        public static IEnumerable<IBasisFunction> CreateDenominatorBases(IEnumerable<IBasisFunction> basisFunctions) {
+            List<IBasisFunction> ans = new List<IBasisFunction>();
+            foreach (var bf in basisFunctions) {
+                if (!bf.IsNominator) continue;
+                var denomFunc = bf.DeepCopy();
+                denomFunc.IsNominator = false;
+                ans.Add(denomFunc);
+            }
+            return ans;
+        }
+
+        public static IRegressionProblemData PrepareData(IRegressionProblemData problemData, IEnumerable<IBasisFunction> basisFunctions) {
+            int numRows = problemData.Dataset.Rows;
+            int numCols = basisFunctions.Count();
+            HashSet<string> allowedInputVars = new HashSet<string>();
+            double[,] variableValues = new double[numRows, numCols + 1]; // +1 for target var
+
+            int col = 0;
+            foreach (var basisFunc in basisFunctions) {
+                allowedInputVars.Add(basisFunc.ToString() + (!basisFunc.IsNominator ? " * " + problemData.TargetVariable : ""));
+                var vals = basisFunc.Simulate(problemData);
+                for (int i = 0; i < numRows; i++) {
+                    variableValues[i, col] = vals[i];
+                }
+                col++;
+            }
+
+            // add the unmodified target variable to the dataset
+            var allVariables = new HashSet<string>(allowedInputVars);
+            allVariables.Add(problemData.TargetVariable);
+
+            var targetVals = problemData.TargetVariableValues.ToArray();
+            for (int i = 0; i < numRows; i++) {
+                variableValues[i, col] = targetVals[i];
+            }
+
+            var temp = new Dataset(allVariables, variableValues);
+
+            IRegressionProblemData rpd = new RegressionProblemData(temp, allowedInputVars, problemData.TargetVariable);
+            rpd.TrainingPartition.Start = problemData.TrainingPartition.Start;
+            rpd.TrainingPartition.End = problemData.TrainingPartition.End;
+            rpd.TestPartition.Start = problemData.TestPartition.Start;
+            rpd.TestPartition.End = problemData.TestPartition.End;
+            return rpd;
+        }
+
+        private static IRegressionProblemData Normalize(IRegressionProblemData data)
+            => new RegressionProblemData(Normalize(data.Dataset), data.AllowedInputVariables, data.TargetVariable);
+
+        // return a normalized version of IDataset ds
+        private static IDataset Normalize(IDataset ds) {
+            var doubleNames = ds.DoubleVariables.ToArray();
+            if (ds.VariableNames.Count() != doubleNames.Length) throw new ArgumentException(nameof(ds));
+            var variableVals = new List<List<double>>();
+            foreach (var name in doubleNames) {
+                var vals = Utils.Normalize(ds.GetDoubleValues(name).ToArray());
+                variableVals.Add(vals.ToList());
+            }
+            return new Dataset(doubleNames, variableVals);
+        }
+
+        private static bool Ok(IEnumerable<double> data) => data.All(x => !double.IsNaN(x) && !double.IsInfinity(x));
+    }
+}
Index: anches/3022-FastFunctionExtraction/FFX/BasisFunction.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/BasisFunction.cs	(revision 17736)
+++ 	(revision )
@@ -1,46 +1,0 @@
-﻿using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction
-{
-
-    struct BasisFunction
-    {
-        public string Var { get; set; }     // e.g. "Abs(Column1 ** 2)"
-        public double[] Val { get; set; }   // this holds the already calculated values, i.e. the function written in Var
-        public bool IsOperator { get; set; }// alg needs to check if basis function has an operator
-        public NonlinOp Operator { get; }
-
-        public BasisFunction(string var, double[] val, bool isOperator, NonlinOp op = NonlinOp.None)
-        {
-            this.Var = var;
-            this.Val= val;
-            this.IsOperator = isOperator;
-            this.Operator = op;
-        }
-
-        public static BasisFunction operator *(BasisFunction a, BasisFunction b)
-        {
-            Debug.Assert(a.Val.Length == b.Val.Length);
-            double[] newVal = new double[a.Val.Length];
-            for(int i = 0; i < a.Val.Length; i++)
-            {
-                newVal[i] = a.Val[i] * b.Val[i];
-            }
-            return new BasisFunction(a.Var + " * " + b.Var, newVal, false);
-        }
-
-        public int Complexity() => 1;
-
-        public ISymbolicExpressionTree Tree()
-        {
-            return null;
-        }
-        
-    }
-}
Index: /branches/3022-FastFunctionExtraction/FFX/FFXModel.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/FFXModel.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/FFXModel.cs	(revision 17737)
@@ -0,0 +1,90 @@
+﻿using HeuristicLab.Algorithms.DataAnalysis.Glmnet;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.DataAnalysis.Symbolic;
+using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    internal class FFXModel {
+        public FFXModel(double intercept, IEnumerable<(double coeff, IBasisFunction function)> basisFunctions) {
+            Intercept = intercept;
+            BasisFunctions = basisFunctions ?? throw new ArgumentNullException(nameof(basisFunctions));
+        }
+
+        public double Intercept { get; set; }
+        public IEnumerable<(double coeff, IBasisFunction function)> BasisFunctions { get; set; }
+        public IEnumerable<(double coeff, IBasisFunction function)> NominatorFunctions =>
+            BasisFunctions.Where(bf => bf.function.IsNominator);
+        public IEnumerable<(double coeff, IBasisFunction function)> DenominatorFunctions =>
+            BasisFunctions.Where(bf => !bf.function.IsNominator);
+
+        public int NumNumeratorFunctions => NominatorFunctions != null ? NominatorFunctions.Count() : 0;
+        public int NumDenominatorFunctions => DenominatorFunctions != null ? DenominatorFunctions.Count() : 0;
+        public int NumBases => NumNumeratorFunctions + NumDenominatorFunctions;
+        public IEnumerable<IBasisFunction> Bases =>
+            NominatorFunctions.Select(tuple => tuple.function)
+            .Concat(DenominatorFunctions.Select(tuple => tuple.function));
+        public int Complexity {
+            get {
+                // We have a leading constant, then for each base we have a coefficient, 
+                // a multiply, and a plus, plus the complexity of the base itself.
+                int numerator_complexity = 1 + NominatorFunctions.Select(bf => 3 + bf.function.Complexity).Sum();
+                if (NumDenominatorFunctions == 0) {
+                    return numerator_complexity;
+                } else {
+                    int denominator_complexity = 1 + DenominatorFunctions.Select(bf => 3 + bf.function.Complexity).Sum();
+                    return 1 + numerator_complexity + denominator_complexity;
+                }
+            }
+        }
+
+        public override string ToString() {
+            var culture = new CultureInfo("en-US");
+            var result = Intercept.ToString(culture);
+            foreach (var (coeff, function) in NominatorFunctions) {
+                var str = $" + ({Math.Round(coeff, 4).ToString(culture)}) * {function}";
+                result += str;
+            }
+            if (NumDenominatorFunctions == 0) return result;
+
+            result = "(" + result + ") / (1.0";
+            foreach (var (coeff, function) in DenominatorFunctions) {
+                var str = $" + ({coeff.ToString(culture)}) * {function}";
+                result += str;
+            }
+            result += ")";
+            return result;
+        }
+
+        public double[] Simulate(IRegressionProblemData data, IEnumerable<int> rows) {
+            var symbolicRegressionModel = ToSymbolicRegressionModel(data.TargetVariable);
+            return symbolicRegressionModel.GetEstimatedValues(data.Dataset, rows).ToArray();
+        }
+
+        public ISymbolicRegressionModel ToSymbolicRegressionModel(string targetVariable) {
+            var str = this.ToString();
+            var tree = new InfixExpressionParser().Parse(str);
+            return new SymbolicRegressionModel(targetVariable, tree, new SymbolicDataAnalysisExpressionTreeInterpreter());
+        }
+
+        public void OptimizeCoefficients(IRegressionProblemData data) {
+            var elnetData = BFUtils.PrepareData(data, this.BasisFunctions.Select(tuple => tuple.function));
+            var oldCoeff = this.BasisFunctions.Select(tuple => tuple.coeff).ToArray();
+            var oldIntercept = this.Intercept;
+            var newCoeff = ElasticNetLinearRegression.CalculateModelCoefficients(elnetData, 0, 0, out var _, out var _);
+            var newIntercept = newCoeff.Last();
+            newCoeff = newCoeff.Take(newCoeff.Length - 1).ToArray();
+
+            var bfArray = this.BasisFunctions.ToArray();
+            for (int i = 0; i < bfArray.Length; i++) {
+                bfArray[i] = (newCoeff[i], bfArray[i].function);
+            }
+
+            this.BasisFunctions = bfArray;
+            this.Intercept = newIntercept;
+        }
+    }
+}
Index: /branches/3022-FastFunctionExtraction/FFX/FastFunctionExtraction.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/FastFunctionExtraction.cs	(revision 17736)
+++ /branches/3022-FastFunctionExtraction/FFX/FastFunctionExtraction.cs	(revision 17737)
@@ -2,463 +2,252 @@
 using System.Threading;
 using System.Linq;
-using HeuristicLab.Common; // required for parameters collection
-using HeuristicLab.Core; // required for parameters collection
-using HeuristicLab.Data; // IntValue, ...
-using HeuristicLab.Encodings.BinaryVectorEncoding;
-using HeuristicLab.Optimization; // BasicAlgorithm
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
 using HeuristicLab.Parameters;
-using HeuristicLab.Problems.Binary;
-using HeuristicLab.Random; // MersenneTwister
 using HEAL.Attic;
 using HeuristicLab.Algorithms.DataAnalysis.Glmnet;
 using HeuristicLab.Problems.DataAnalysis;
 using System.Collections.Generic;
-using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
-using System.Collections;
-using System.Diagnostics;
-using HeuristicLab.Problems.DataAnalysis.Symbolic;
 using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
-using HeuristicLab.Analysis;
-using HeuristicLab.Collections;
 
 namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
 
-  [Item(Name = "FastFunctionExtraction", Description = "An FFX algorithm.")]
-  [Creatable(Category = CreatableAttribute.Categories.Algorithms, Priority = 999)]
-  [StorableType("689280F7-E371-44A2-98A5-FCEDF22CA343")] // for persistence (storing your algorithm to a files or transfer to HeuristicLab.Hive
-  public sealed class FastFunctionExtraction : FixedDataAnalysisAlgorithm<RegressionProblem> {
-
-    private static readonly double[] exponents = { 0.5, 1, 2 };
-    private static readonly OpCode[] nonlinFuncs = { OpCode.Absolute, OpCode.Log, OpCode.Sin, OpCode.Cos };
-
-    private static readonly BidirectionalDictionary<OpCode, string> OpCodeToString = new BidirectionalDictionary<OpCode, string> {
-        { OpCode.Log, "LOG" },
-        { OpCode.Absolute, "ABS"},
-        { OpCode.Sin, "SIN"},
-        { OpCode.Cos, "COS"},
-        { OpCode.Square, "SQR"},
-        { OpCode.SquareRoot, "SQRT"},
-        { OpCode.Cube, "CUBE"},
-        { OpCode.CubeRoot, "CUBEROOT"}
-    };
-
-    private const string ConsiderInteractionsParameterName = "Consider Interactions";
-    private const string ConsiderDenominationParameterName = "Consider Denomination";
-    private const string ConsiderExponentiationParameterName = "Consider Exponentiation";
-    private const string ConsiderNonlinearFuncsParameterName = "Consider Nonlinear functions";
-    private const string ConsiderHingeFuncsParameterName = "Consider Hinge Functions";
-    private const string PenaltyParameterName = "Penalty";
-    private const string LambdaParameterName = "Lambda";
-    private const string NonlinearFuncsParameterName = "Nonlinear Functions";
-
-    #region parameters
-    public IValueParameter<BoolValue> ConsiderInteractionsParameter
-    {
-      get { return (IValueParameter<BoolValue>)Parameters[ConsiderInteractionsParameterName]; }
+    [Item(Name = "FastFunctionExtraction", Description = "An FFX algorithm.")]
+    [Creatable(Category = CreatableAttribute.Categories.Algorithms, Priority = 999)]
+    [StorableType("689280F7-E371-44A2-98A5-FCEDF22CA343")] // for persistence (storing your algorithm to a files or transfer to HeuristicLab.Hive
+    public sealed class FastFunctionExtraction : FixedDataAnalysisAlgorithm<RegressionProblem> {
+
+        #region constants
+        private static readonly HashSet<double> exponents = new HashSet<double> { -1.0, -0.5, +0.5, +1.0 };
+        private static readonly HashSet<NonlinearOperator> nonlinFuncs = new HashSet<NonlinearOperator> { NonlinearOperator.Abs, NonlinearOperator.Log, NonlinearOperator.None };
+        private static readonly double minHingeThr = 0.2;
+        private static readonly double maxHingeThr = 0.8;
+        private static readonly int numHingeThrs = 5;
+
+        private const string ConsiderInteractionsParameterName = "Consider Interactions";
+        private const string ConsiderDenominationParameterName = "Consider Denomination";
+        private const string ConsiderExponentiationParameterName = "Consider Exponentiation";
+        private const string ConsiderHingeFuncsParameterName = "Consider Hinge Functions";
+        private const string ConsiderNonlinearFuncsParameterName = "Consider Nonlinear Functions";
+        private const string LambdaParameterName = "Elastic Net Lambda";
+        private const string PenaltyParameterName = "Elastic Net Penalty";
+        private const string MaxNumBasisFuncsParameterName = "Maximum Number of Basis Functions";
+
+        #endregion
+
+        #region parameters
+        public IValueParameter<BoolValue> ConsiderInteractionsParameter {
+            get { return (IValueParameter<BoolValue>)Parameters[ConsiderInteractionsParameterName]; }
+        }
+        public IValueParameter<BoolValue> ConsiderDenominationsParameter {
+            get { return (IValueParameter<BoolValue>)Parameters[ConsiderDenominationParameterName]; }
+        }
+        public IValueParameter<BoolValue> ConsiderExponentiationsParameter {
+            get { return (IValueParameter<BoolValue>)Parameters[ConsiderExponentiationParameterName]; }
+        }
+        public IValueParameter<BoolValue> ConsiderNonlinearFuncsParameter {
+            get { return (IValueParameter<BoolValue>)Parameters[ConsiderNonlinearFuncsParameterName]; }
+        }
+        public IValueParameter<BoolValue> ConsiderHingeFuncsParameter {
+            get { return (IValueParameter<BoolValue>)Parameters[ConsiderHingeFuncsParameterName]; }
+        }
+        public IValueParameter<DoubleValue> PenaltyParameter {
+            get { return (IValueParameter<DoubleValue>)Parameters[PenaltyParameterName]; }
+        }
+        public IValueParameter<DoubleValue> LambdaParameter {
+            get { return (IValueParameter<DoubleValue>)Parameters[LambdaParameterName]; }
+        }
+        public IValueParameter<IntValue> MaxNumBasisFuncsParameter {
+            get { return (IValueParameter<IntValue>)Parameters[MaxNumBasisFuncsParameterName]; }
+        }
+        #endregion
+
+        #region properties
+        public bool ConsiderInteractions {
+            get { return ConsiderInteractionsParameter.Value.Value; }
+            set { ConsiderInteractionsParameter.Value.Value = value; }
+        }
+        public bool ConsiderDenominations {
+            get { return ConsiderDenominationsParameter.Value.Value; }
+            set { ConsiderDenominationsParameter.Value.Value = value; }
+        }
+        public bool ConsiderExponentiations {
+            get { return ConsiderExponentiationsParameter.Value.Value; }
+            set { ConsiderExponentiationsParameter.Value.Value = value; }
+        }
+        public bool ConsiderNonlinearFunctions {
+            get { return ConsiderNonlinearFuncsParameter.Value.Value; }
+            set { ConsiderNonlinearFuncsParameter.Value.Value = value; }
+        }
+        public bool ConsiderHingeFunctions {
+            get { return ConsiderHingeFuncsParameter.Value.Value; }
+            set { ConsiderHingeFuncsParameter.Value.Value = value; }
+        }
+        public double Penalty {
+            get { return PenaltyParameter.Value.Value; }
+            set { PenaltyParameter.Value.Value = value; }
+        }
+        public DoubleValue Lambda {
+            get { return LambdaParameter.Value; }
+            set { LambdaParameter.Value = value; }
+        }
+        public int MaxNumBasisFuncs {
+            get { return MaxNumBasisFuncsParameter.Value.Value; }
+            set { MaxNumBasisFuncsParameter.Value.Value = value; }
+        }
+        #endregion
+
+        #region ctor
+
+        [StorableConstructor]
+        private FastFunctionExtraction(StorableConstructorFlag _) : base(_) { }
+        public FastFunctionExtraction(FastFunctionExtraction original, Cloner cloner) : base(original, cloner) {
+        }
+        public FastFunctionExtraction() : base() {
+            base.Problem = new RegressionProblem();
+            Parameters.Add(new ValueParameter<BoolValue>(ConsiderInteractionsParameterName, "True if you want the models to include interactions, otherwise false.", new BoolValue(true)));
+            Parameters.Add(new ValueParameter<BoolValue>(ConsiderDenominationParameterName, "True if you want the models to include denominations, otherwise false.", new BoolValue(true)));
+            Parameters.Add(new ValueParameter<BoolValue>(ConsiderExponentiationParameterName, "True if you want the models to include exponentiation, otherwise false.", new BoolValue(true)));
+            Parameters.Add(new ValueParameter<BoolValue>(ConsiderNonlinearFuncsParameterName, "True if you want the models to include nonlinear functions(abs, log,...), otherwise false.", new BoolValue(true)));
+            Parameters.Add(new ValueParameter<BoolValue>(ConsiderHingeFuncsParameterName, "True if you want the models to include Hinge Functions, otherwise false.", new BoolValue(true)));
+            Parameters.Add(new ValueParameter<IntValue>(MaxNumBasisFuncsParameterName, "Set how many basis functions the models can have at most. if Max Num Basis Funcs is negative => no restriction on size", new IntValue(20)));
+            Parameters.Add(new OptionalValueParameter<DoubleValue>(LambdaParameterName, "Optional: the value of lambda for which to calculate an elastic-net solution. lambda == null => calculate the whole path of all lambdas"));
+            Parameters.Add(new FixedValueParameter<DoubleValue>(PenaltyParameterName, "Penalty factor (alpha) for balancing between ridge (0.0) and lasso (1.0) regression", new DoubleValue(0.05)));
+        }
+
+        [StorableHook(HookType.AfterDeserialization)]
+        private void AfterDeserialization() { }
+
+        public override IDeepCloneable Clone(Cloner cloner) {
+            return new FastFunctionExtraction(this, cloner);
+        }
+        #endregion
+
+        public override Type ProblemType { get { return typeof(RegressionProblem); } }
+        public new RegressionProblem Problem { get { return (RegressionProblem)base.Problem; } }
+
+        protected override void Run(CancellationToken cancellationToken) {
+            var models = Fit(Problem.ProblemData, Penalty, out var numBases, ConsiderExponentiations,
+                ConsiderNonlinearFunctions, ConsiderInteractions,
+                ConsiderDenominations, ConsiderHingeFunctions, MaxNumBasisFuncs
+            );
+
+            int i = 0;
+            var numBasesArr = numBases.ToArray();
+            var solutionsArr = new List<ISymbolicRegressionSolution>();
+
+            foreach (var model in models) {
+                Results.Add(new Result(
+                    "Num Bases: " + numBasesArr[i++],
+                    model
+                ));
+                solutionsArr.Add(new SymbolicRegressionSolution(model, Problem.ProblemData));
+            }
+            Results.Add(new Result("Model Accuracies", new ItemCollection<ISymbolicRegressionSolution>(solutionsArr)));
+        }
+
+        public static IEnumerable<ISymbolicRegressionModel> Fit(IRegressionProblemData data, double elnetPenalty, out IEnumerable<int> numBases, bool exp = true, bool nonlinFuncs = true, bool interactions = true, bool denoms = false, bool hingeFuncs = true, int maxNumBases = -1) {
+            var approaches = CreateApproaches(interactions, denoms, exp, nonlinFuncs, hingeFuncs, maxNumBases, elnetPenalty);
+
+            var allFFXModels = approaches
+                .SelectMany(approach => CreateFFXModels(data, approach)).ToList();
+
+            // Final pareto filter over all generated models from all different approaches
+            var nondominatedFFXModels = NondominatedModels(data, allFFXModels);
+
+            numBases = nondominatedFFXModels
+                .Select(ffxModel => ffxModel.NumBases).ToArray();
+            return nondominatedFFXModels.Select(ffxModel => ffxModel.ToSymbolicRegressionModel(data.TargetVariable));
+        }
+
+        private static IEnumerable<FFXModel> NondominatedModels(IRegressionProblemData data, IEnumerable<FFXModel> ffxModels) {
+            var numBases = ffxModels.Select(ffxModel => (double)ffxModel.NumBases).ToArray();
+            var errors = ffxModels.Select(ffxModel => {
+                var originalValues = data.TargetVariableTestValues.ToArray();
+                var estimatedValues = ffxModel.Simulate(data, data.TestIndices);
+                // do not create a regressionSolution here for better performance: 
+                // RegressionSolutions calculate all kinds of errors when calling the ctor, but we only need testMSE
+                var testMSE = OnlineMeanSquaredErrorCalculator.Calculate(originalValues, estimatedValues, out var state);
+                if (state != OnlineCalculatorError.None) throw new ArrayTypeMismatchException("could not calculate TestMSE");
+                return testMSE;
+            }).ToArray();
+
+            int n = numBases.Length;
+            double[][] qualities = new double[n][];
+            for (int i = 0; i < n; i++) {
+                qualities[i] = new double[2];
+                qualities[i][0] = numBases[i];
+                qualities[i][1] = errors[i];
+            }
+
+            return DominationCalculator<FFXModel>.CalculateBestParetoFront(ffxModels.ToArray(), qualities, new bool[] { false, false })
+                .Select(tuple => tuple.Item1).OrderBy(ffxModel => ffxModel.NumBases);
+        }
+
+        // Build FFX models
+        private static IEnumerable<FFXModel> CreateFFXModels(IRegressionProblemData data, Approach approach) {
+            // FFX Step 1
+            var basisFunctions = BFUtils.CreateBasisFunctions(data, approach).ToArray();
+
+            var funcsArr = basisFunctions.ToArray();
+            var elnetData = BFUtils.PrepareData(data, funcsArr);
+
+            // FFX Step 2
+            ElasticNetLinearRegression.RunElasticNetLinearRegression(elnetData, approach.ElnetPenalty, out var _, out var _, out var _, out var candidateCoeffs, out var intercept, maxVars: approach.MaxNumBases);
+
+            // create models out of the learned coefficients
+            var ffxModels = GetUniqueModelsFromCoeffs(candidateCoeffs, intercept, funcsArr, approach);
+             
+            // one last LS-optimization step on the training data
+            foreach (var ffxModel in ffxModels) {
+                //if (ffxModel.NumBases > 0) ffxModel.OptimizeCoefficients(data);
+            }
+            return ffxModels;
+        }
+
+        // finds all models with unique combinations of basis functions
+        private static IEnumerable<FFXModel> GetUniqueModelsFromCoeffs(double[,] candidateCoeffs, double[] intercept, IBasisFunction[] funcsArr, Approach approach) {
+            List<FFXModel> ffxModels = new List<FFXModel>();
+            List<int[]> bfCombinations = new List<int[]>();
+
+            for (int i = 0; i < intercept.Length; i++) {
+                var row = candidateCoeffs.GetRow(i);
+                var nonzeroIndices = row.FindAllIndices(val => val != 0).ToArray();
+                if (nonzeroIndices.Count() > approach.MaxNumBases) continue;
+                // ignore duplicate models (models with same combination of basis functions)
+                if (bfCombinations.Any(arr => Enumerable.SequenceEqual(arr, nonzeroIndices))) continue;
+                var ffxModel = new FFXModel(intercept[i], nonzeroIndices.Select(idx => (row[idx], funcsArr[idx])));
+                bfCombinations.Add(nonzeroIndices);
+                ffxModels.Add(ffxModel);
+            }
+            return ffxModels;
+        }
+
+        private static IEnumerable<Approach> CreateApproaches(bool interactions, bool denominator, bool exponentiations, bool nonlinearFuncs, bool hingeFunctions, int maxNumBases, double penalty) {
+            var approaches = new List<Approach>();
+            var valids = new bool[5] { interactions, denominator, exponentiations, nonlinearFuncs, hingeFunctions };
+
+            // return true if ALL indices of true values of arr1 also have true values in arr2
+            bool follows(bool[] arr1, bool[] arr2) {
+                if (arr1.Length != arr2.Length) throw new ArgumentException("invalid lengths");
+                for (int i = 0; i < arr1.Length; i++) {
+                    if (arr1[i] && !arr2[i]) return false;
+                }
+                return true;
+            }
+
+            for (int i = 0; i < 32; i++) {
+                // map i to a bool array of length 5
+                var arr = i.ToBoolArray(5);
+                if (!follows(arr, valids)) continue;
+                int sum = arr.Where(b => b).Count(); // how many features are enabled?
+                if (sum >= 4) continue; // not too many features at once
+                if (arr[0] && arr[2]) continue; // never need both exponent and inter
+                approaches.Add(new Approach(arr[0], arr[1], arr[2], arr[3], arr[4], exponents, nonlinFuncs, maxNumBases, penalty, minHingeThr, maxHingeThr, numHingeThrs));
+            }
+            return approaches;
+        }
     }
-    public IValueParameter<BoolValue> ConsiderDenominationsParameter
-    {
-      get { return (IValueParameter<BoolValue>)Parameters[ConsiderDenominationParameterName]; }
-    }
-    public IValueParameter<BoolValue> ConsiderExponentiationsParameter
-    {
-      get { return (IValueParameter<BoolValue>)Parameters[ConsiderExponentiationParameterName]; }
-    }
-    public IValueParameter<BoolValue> ConsiderNonlinearFuncsParameter
-    {
-      get { return (IValueParameter<BoolValue>)Parameters[ConsiderNonlinearFuncsParameterName]; }
-    }
-    public IValueParameter<BoolValue> ConsiderHingeFuncsParameter
-    {
-      get { return (IValueParameter<BoolValue>)Parameters[ConsiderHingeFuncsParameterName]; }
-    }
-    public IValueParameter<DoubleValue> PenaltyParameter
-    {
-      get { return (IValueParameter<DoubleValue>)Parameters[PenaltyParameterName]; }
-    }
-    public IValueParameter<DoubleValue> LambdaParameter
-    {
-      get { return (IValueParameter<DoubleValue>)Parameters[LambdaParameterName]; }
-    }
-    public IValueParameter<CheckedItemCollection<EnumValue<OpCode>>> NonlinearFuncsParameter
-    {
-      get { return (IValueParameter<CheckedItemCollection<EnumValue<OpCode>>>)Parameters[NonlinearFuncsParameterName]; }
-    }
-    #endregion
-
-    #region properties
-    public bool ConsiderInteractions
-    {
-      get { return ConsiderInteractionsParameter.Value.Value; }
-      set { ConsiderInteractionsParameter.Value.Value = value; }
-    }
-    public bool ConsiderDenominations
-    {
-      get { return ConsiderDenominationsParameter.Value.Value; }
-      set { ConsiderDenominationsParameter.Value.Value = value; }
-    }
-    public bool ConsiderExponentiations
-    {
-      get { return ConsiderExponentiationsParameter.Value.Value; }
-      set { ConsiderExponentiationsParameter.Value.Value = value; }
-    }
-    public bool ConsiderNonlinearFuncs
-    {
-      get { return ConsiderNonlinearFuncsParameter.Value.Value; }
-      set { ConsiderNonlinearFuncsParameter.Value.Value = value; }
-    }
-    public bool ConsiderHingeFuncs
-    {
-      get { return ConsiderHingeFuncsParameter.Value.Value; }
-      set { ConsiderHingeFuncsParameter.Value.Value = value; }
-    }
-    public double Penalty
-    {
-      get { return PenaltyParameter.Value.Value; }
-      set { PenaltyParameter.Value.Value = value; }
-    }
-    public DoubleValue Lambda
-    {
-      get { return LambdaParameter.Value; }
-      set { LambdaParameter.Value = value; }
-    }
-    public CheckedItemCollection<EnumValue<OpCode>> NonlinearFuncs
-    {
-      get { return NonlinearFuncsParameter.Value; }
-      set { NonlinearFuncsParameter.Value = value; }
-    }
-    #endregion
-
-
-    [StorableConstructor]
-    private FastFunctionExtraction(StorableConstructorFlag _) : base(_) { }
-    public FastFunctionExtraction(FastFunctionExtraction original, Cloner cloner) : base(original, cloner) {
-    }
-    public FastFunctionExtraction() : base() {
-      var items = new CheckedItemCollection<EnumValue<OpCode>>();
-      foreach (var op in nonlinFuncs) {
-        items.Add(new EnumValue<OpCode>(op));
-      }
-      base.Problem = new RegressionProblem();
-      Parameters.Add(new ValueParameter<BoolValue>(ConsiderInteractionsParameterName, "True if you want the models to include interactions, otherwise false.", new BoolValue(true)));
-      Parameters.Add(new ValueParameter<BoolValue>(ConsiderDenominationParameterName, "True if you want the models to include denominations, otherwise false.", new BoolValue(true)));
-      Parameters.Add(new ValueParameter<BoolValue>(ConsiderExponentiationParameterName, "True if you want the models to include exponentiation, otherwise false.", new BoolValue(true)));
-      Parameters.Add(new ValueParameter<BoolValue>(ConsiderNonlinearFuncsParameterName, "True if you want the models to include nonlinear functions(abs, log,...), otherwise false.", new BoolValue(true)));
-      Parameters.Add(new ValueParameter<BoolValue>(ConsiderHingeFuncsParameterName, "True if you want the models to include Hinge Functions, otherwise false.", new BoolValue(true)));
-      Parameters.Add(new FixedValueParameter<DoubleValue>(PenaltyParameterName, "Penalty factor (alpha) for balancing between ridge (0.0) and lasso (1.0) regression", new DoubleValue(0.9)));
-      Parameters.Add(new OptionalValueParameter<DoubleValue>(LambdaParameterName, "Optional: the value of lambda for which to calculate an elastic-net solution. lambda == null => calculate the whole path of all lambdas"));
-      Parameters.Add(new ValueParameter<CheckedItemCollection<EnumValue<OpCode>>>(NonlinearFuncsParameterName, "What nonlinear functions the models should be able to include.", items));
-    }
-
-    [StorableHook(HookType.AfterDeserialization)]
-    private void AfterDeserialization() { }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new FastFunctionExtraction(this, cloner);
-    }
-
-    public override Type ProblemType { get { return typeof(RegressionProblem); } }
-    public new RegressionProblem Problem { get { return (RegressionProblem)base.Problem; } }
-
-    public override bool SupportsPause { get { return true; } }
-
-    protected override void Run(CancellationToken cancellationToken) {
-      var basisFunctions = createBasisFunctions(Problem.ProblemData);
-      Results.Add(new Result("Basis Functions", "A Dataset consisting of the generated Basis Functions from FFX Alg Step 1.", createProblemData(Problem.ProblemData, basisFunctions)));
-
-      // add denominator bases to the already existing basis functions
-      if (ConsiderDenominations) basisFunctions = basisFunctions.Concat(createDenominatorBases(Problem.ProblemData, basisFunctions)).ToList();
-
-      // create either path of solutions, or one solution for given lambda
-      LearnModels(Problem.ProblemData, basisFunctions);
-    }
-
-    private List<BasisFunction> createBasisFunctions(IRegressionProblemData problemData) {
-      var basisFunctions = createUnivariateBases(problemData);
-      basisFunctions = basisFunctions.Concat(createMultivariateBases(basisFunctions)).ToList();
-      return basisFunctions;
-    }
-
-    private List<BasisFunction> createUnivariateBases(IRegressionProblemData problemData) {
-      var B1 = new List<BasisFunction>();
-      var inputVariables = problemData.AllowedInputVariables;
-      var validExponents = ConsiderExponentiations ? exponents : new double[] { 1 };
-      var validFuncs = NonlinearFuncs.CheckedItems.Select(val => val.Value);
-      // TODO: add Hinge functions
-
-      foreach (var variableName in inputVariables) {
-        foreach (var exp in validExponents) {
-          var data = problemData.Dataset.GetDoubleValues(variableName).Select(x => Math.Pow(x, exp)).ToArray();
-          if (!ok(data)) continue;
-          var name = expToString(exp, variableName);
-          B1.Add(new BasisFunction(name, data, false));
-          foreach (OpCode _op in validFuncs) {
-            var inner_data = data.Select(x => eval(_op, x)).ToArray();
-            if (!ok(inner_data)) continue;
-            var inner_name = OpCodeToString.GetByFirst(_op) + "(" + name + ")";
-            B1.Add(new BasisFunction(inner_name, inner_data, true));
-          }
-        }
-      }
-      return B1;
-    }
-
-    private List<BasisFunction> createMultivariateBases(List<BasisFunction> B1) {
-      if (!ConsiderInteractions) return B1;
-      var B2 = new List<BasisFunction>();
-      for (int i = 0; i < B1.Count(); i++) {
-        var b_i = B1.ElementAt(i);
-        for (int j = 0; j < i; j++) {
-          var b_j = B1.ElementAt(j);
-          if (b_j.IsOperator) continue; // disallow op() * op()
-          var b_inter = b_i * b_j;
-          B2.Add(b_inter);
-        }
-      }
-
-      return B2;
-      // return union of B1 and B2
-    }
-
-    // creates 1 denominator basis function for each corresponding basis function from basisFunctions
-    private IEnumerable<BasisFunction> createDenominatorBases(IRegressionProblemData problemData, IEnumerable<BasisFunction> basisFunctions) {
-      var y = new BasisFunction(problemData.TargetVariable, problemData.TargetVariableValues.ToArray(), false);
-      var denomBasisFuncs = new List<BasisFunction>();
-      foreach (var func in basisFunctions) {
-        var denomFunc = y * func;
-        denomBasisFuncs.Add(denomFunc);
-      }
-      return denomBasisFuncs;
-    }
-
-    private static string expToString(double exponent, string varname) {
-      if (exponent.IsAlmost(1)) return varname;
-      if (exponent.IsAlmost(1 / 2)) return OpCodeToString.GetByFirst(OpCode.SquareRoot) + "(" + varname + ")";
-      if (exponent.IsAlmost(1 / 3)) return OpCodeToString.GetByFirst(OpCode.CubeRoot) + "(" + varname + ")";
-      if (exponent.IsAlmost(2)) return OpCodeToString.GetByFirst(OpCode.Square) + "(" + varname + ")";
-      if (exponent.IsAlmost(3)) return OpCodeToString.GetByFirst(OpCode.Cube) + "(" + varname + ")";
-      else return varname + " ^ " + exponent;
-    }
-
-    public static double eval(OpCode op, double x) {
-      switch (op) {
-        case OpCode.Absolute:
-          return Math.Abs(x);
-        case OpCode.Log:
-          return Math.Log10(x);
-        case OpCode.Sin:
-          return Math.Sin(x);
-        case OpCode.Cos:
-          return Math.Cos(x);
-        default:
-          throw new Exception("Unimplemented operator: " + op.ToString());
-      }
-    }
-
-    private void PathwiseLearning(IRegressionProblemData problemData, List<BasisFunction> basisFunctions) {
-      ElasticNetLinearRegression reg = new ElasticNetLinearRegression();
-      reg.Lambda = Lambda;
-      reg.Penality = Penalty;
-      reg.Problem.ProblemData = createProblemData(problemData, basisFunctions);
-      reg.Start();
-      Results.AddRange(reg.Results);
-    }
-
-    private void LearnModels(IRegressionProblemData problemData, List<BasisFunction> basisFunctions) {
-      double[] lambda;
-      double[] trainNMSE;
-      double[] testNMSE;
-      double[,] coeff;
-      double[] intercept;
-      int numNominatorBases = ConsiderDenominations ? basisFunctions.Count / 2 : basisFunctions.Count;
-
-      // wraps the list of basis functions in a dataset, so that it can be passed on to the ElNet function
-      var X_b = createProblemData(problemData, basisFunctions);
-
-      ElasticNetLinearRegression.RunElasticNetLinearRegression(X_b, Penalty, out lambda, out trainNMSE, out testNMSE, out coeff, out intercept);
-
-      var errorTable = NMSEGraph(coeff, lambda, trainNMSE, testNMSE);
-      Results.Add(new Result(errorTable.Name, errorTable.Description, errorTable));
-      var coeffTable = CoefficientGraph(coeff, lambda, X_b.AllowedInputVariables, X_b.Dataset);
-      Results.Add(new Result(coeffTable.Name, coeffTable.Description, coeffTable));
-
-      ItemCollection<IResult> models = new ItemCollection<IResult>();
-      for (int modelIdx = 0; modelIdx < coeff.GetUpperBound(0); modelIdx++) {
-        var tree = Tree(basisFunctions, GetRow(coeff, modelIdx), intercept[modelIdx]);
-        ISymbolicRegressionModel m = new SymbolicRegressionModel(Problem.ProblemData.TargetVariable, tree, new SymbolicDataAnalysisExpressionTreeInterpreter());
-        ISymbolicRegressionSolution s = new SymbolicRegressionSolution(m, Problem.ProblemData);
-        models.Add(new Result("Solution " + modelIdx, s));
-      }
-
-      Results.Add(new Result("Models", "The model path returned by the Elastic Net Regression (not only the pareto-optimal subset). ", models));
-    }
-
-    private static IndexedDataTable<double> CoefficientGraph(double[,] coeff, double[] lambda, IEnumerable<string> allowedVars, IDataset ds) {
-      var coeffTable = new IndexedDataTable<double>("Coefficients", "The paths of standarized coefficient values over different lambda values");
-      coeffTable.VisualProperties.YAxisMaximumAuto = false;
-      coeffTable.VisualProperties.YAxisMinimumAuto = false;
-      coeffTable.VisualProperties.XAxisMaximumAuto = false;
-      coeffTable.VisualProperties.XAxisMinimumAuto = false;
-
-      coeffTable.VisualProperties.XAxisLogScale = true;
-      coeffTable.VisualProperties.XAxisTitle = "Lambda";
-      coeffTable.VisualProperties.YAxisTitle = "Coefficients";
-      coeffTable.VisualProperties.SecondYAxisTitle = "Number of variables";
-
-      var nLambdas = lambda.Length;
-      var nCoeff = coeff.GetLength(1);
-      var dataRows = new IndexedDataRow<double>[nCoeff];
-      var numNonZeroCoeffs = new int[nLambdas];
-
-      var doubleVariables = allowedVars.Where(ds.VariableHasType<double>);
-      var factorVariableNames = allowedVars.Where(ds.VariableHasType<string>);
-      var factorVariablesAndValues = ds.GetFactorVariableValues(factorVariableNames, Enumerable.Range(0, ds.Rows)); // must consider all factor values (in train and test set)
-      {
-        int i = 0;
-        foreach (var factorVariableAndValues in factorVariablesAndValues) {
-          foreach (var factorValue in factorVariableAndValues.Value) {
-            double sigma = ds.GetStringValues(factorVariableAndValues.Key)
-              .Select(s => s == factorValue ? 1.0 : 0.0)
-              .StandardDeviation(); // calc std dev of binary indicator
-            var path = Enumerable.Range(0, nLambdas).Select(r => Tuple.Create(lambda[r], coeff[r, i] * sigma)).ToArray();
-            dataRows[i] = new IndexedDataRow<double>(factorVariableAndValues.Key + "=" + factorValue, factorVariableAndValues.Key + "=" + factorValue, path);
-            i++;
-          }
-        }
-
-        foreach (var doubleVariable in doubleVariables) {
-          double sigma = ds.GetDoubleValues(doubleVariable).StandardDeviation();
-          var path = Enumerable.Range(0, nLambdas).Select(r => Tuple.Create(lambda[r], coeff[r, i] * sigma)).ToArray();
-          dataRows[i] = new IndexedDataRow<double>(doubleVariable, doubleVariable, path);
-          i++;
-        }
-        // add to coeffTable by total weight (larger area under the curve => more important);
-        foreach (var r in dataRows.OrderByDescending(r => r.Values.Select(t => t.Item2).Sum(x => Math.Abs(x)))) {
-          coeffTable.Rows.Add(r);
-        }
-      }
-
-      for (int i = 0; i < coeff.GetLength(0); i++) {
-        for (int j = 0; j < coeff.GetLength(1); j++) {
-          if (!coeff[i, j].IsAlmost(0.0)) {
-            numNonZeroCoeffs[i]++;
-          }
-        }
-      }
-      if (lambda.Length > 2) {
-        coeffTable.VisualProperties.XAxisMinimumFixedValue = Math.Pow(10, Math.Floor(Math.Log10(lambda.Last())));
-        coeffTable.VisualProperties.XAxisMaximumFixedValue = Math.Pow(10, Math.Ceiling(Math.Log10(lambda.Skip(1).First())));
-      }
-      coeffTable.Rows.Add(new IndexedDataRow<double>("Number of variables", "The number of non-zero coefficients for each step in the path", lambda.Zip(numNonZeroCoeffs, (l, v) => Tuple.Create(l, (double)v))));
-      coeffTable.Rows["Number of variables"].VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
-      coeffTable.Rows["Number of variables"].VisualProperties.SecondYAxis = true;
-
-      return coeffTable;
-    }
-
-    private static IndexedDataTable<double> NMSEGraph(double[,] coeff, double[] lambda, double[] trainNMSE, double[] testNMSE) {
-      var errorTable = new IndexedDataTable<double>("NMSE", "Path of NMSE values over different lambda values");
-      var numNonZeroCoeffs = new int[lambda.Length];
-      errorTable.VisualProperties.YAxisMaximumAuto = false;
-      errorTable.VisualProperties.YAxisMinimumAuto = false;
-      errorTable.VisualProperties.XAxisMaximumAuto = false;
-      errorTable.VisualProperties.XAxisMinimumAuto = false;
-
-      for (int i = 0; i < coeff.GetLength(0); i++) {
-        for (int j = 0; j < coeff.GetLength(1); j++) {
-          if (!coeff[i, j].IsAlmost(0.0)) {
-            numNonZeroCoeffs[i]++;
-          }
-        }
-      }
-
-      errorTable.VisualProperties.YAxisMinimumFixedValue = 0;
-      errorTable.VisualProperties.YAxisMaximumFixedValue = 1.0;
-      errorTable.VisualProperties.XAxisLogScale = true;
-      errorTable.VisualProperties.XAxisTitle = "Lambda";
-      errorTable.VisualProperties.YAxisTitle = "Normalized mean of squared errors (NMSE)";
-      errorTable.VisualProperties.SecondYAxisTitle = "Number of variables";
-      errorTable.Rows.Add(new IndexedDataRow<double>("NMSE (train)", "Path of NMSE values over different lambda values", lambda.Zip(trainNMSE, (l, v) => Tuple.Create(l, v))));
-      errorTable.Rows.Add(new IndexedDataRow<double>("NMSE (test)", "Path of NMSE values over different lambda values", lambda.Zip(testNMSE, (l, v) => Tuple.Create(l, v))));
-      errorTable.Rows.Add(new IndexedDataRow<double>("Number of variables", "The number of non-zero coefficients for each step in the path", lambda.Zip(numNonZeroCoeffs, (l, v) => Tuple.Create(l, (double)v))));
-      if (lambda.Length > 2) {
-        errorTable.VisualProperties.XAxisMinimumFixedValue = Math.Pow(10, Math.Floor(Math.Log10(lambda.Last())));
-        errorTable.VisualProperties.XAxisMaximumFixedValue = Math.Pow(10, Math.Ceiling(Math.Log10(lambda.Skip(1).First())));
-      }
-      errorTable.Rows["NMSE (train)"].VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
-      errorTable.Rows["NMSE (test)"].VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
-      errorTable.Rows["Number of variables"].VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
-      errorTable.Rows["Number of variables"].VisualProperties.SecondYAxis = true;
-
-      return errorTable;
-    }
-
-    private ISymbolicExpressionTree Tree(List<BasisFunction> basisFunctions, double[] coeffs, double offset) {
-      Debug.Assert(basisFunctions.Count() == coeffs.Length);
-      //SymbolicExpressionTree
-      var numNumeratorFuncs = ConsiderDenominations ? basisFunctions.Count() / 2 : basisFunctions.Count();
-      var numeratorBasisFuncs = basisFunctions.Take(numNumeratorFuncs);
-
-      // returns true if there exists at least 1 coefficient value in the model that is part of the denominator 
-      // (i.e. if there exists at least 1 non-zero value in the second half of the array)
-      bool withDenom(double[] coeffarr) => coeffarr.Take(coeffarr.Length / 2).ToArray().Any(val => !val.IsAlmost(0.0));
-      string model = "(" + offset.ToString();
-      for (int i = 0; i < numNumeratorFuncs; i++) {
-        var func = basisFunctions.ElementAt(i);
-        // only generate nodes for relevant basis functions (those with non-zero coeffs)
-        if (!coeffs[i].IsAlmost(0.0))
-          model += " + (" + coeffs[i] + ") * " + func.Var;
-      }
-      if (ConsiderDenominations && withDenom(coeffs)) {
-        model += ") / (1";
-        for (int i = numNumeratorFuncs; i < basisFunctions.Count(); i++) {
-          var func = basisFunctions.ElementAt(i);
-          // only generate nodes for relevant basis functions (those with non-zero coeffs)
-          if (!coeffs[i].IsAlmost(0.0))
-            model += " + (" + coeffs[i] + ") * " + func.Var.Substring(4);
-        }
-      }
-      model += ")";
-      InfixExpressionParser p = new InfixExpressionParser();
-      return p.Parse(model);
-    }
-
-    // wraps the list of basis functions into an IRegressionProblemData object
-    private static IRegressionProblemData createProblemData(IRegressionProblemData problemData, List<BasisFunction> basisFunctions) {
-      List<string> variableNames = new List<string>();
-      List<IList> variableVals = new List<IList>();
-      foreach (var basisFunc in basisFunctions) {
-        variableNames.Add(basisFunc.Var);
-        // basisFunctions already contains the calculated values of the corresponding basis function, so you can just take that value 
-        variableVals.Add(new List<double>(basisFunc.Val));
-      }
-      var matrix = new ModifiableDataset(variableNames, variableVals);
-
-      // add the unmodified target variable to the matrix
-      matrix.AddVariable(problemData.TargetVariable, problemData.TargetVariableValues.ToList());
-      var allowedInputVars = matrix.VariableNames.Where(x => !x.Equals(problemData.TargetVariable));
-      IRegressionProblemData rpd = new RegressionProblemData(matrix, allowedInputVars, problemData.TargetVariable);
-      rpd.TrainingPartition.Start = problemData.TrainingPartition.Start;
-      rpd.TrainingPartition.End = problemData.TrainingPartition.End;
-      rpd.TestPartition.Start = problemData.TestPartition.Start;
-      rpd.TestPartition.End = problemData.TestPartition.End;
-      return rpd;
-    }
-
-    private static bool ok(double[] data) => data.All(x => !double.IsNaN(x) && !double.IsInfinity(x));
-
-    // helper function which returns a row of a 2D array
-    private static T[] GetRow<T>(T[,] matrix, int row) {
-      var columns = matrix.GetLength(1);
-      var array = new T[columns];
-      for (int i = 0; i < columns; ++i)
-        array[i] = matrix[row, i];
-      return array;
-    }
-
-    // returns all models with pareto-optimal tradeoff between error and complexity
-    private static List<IRegressionSolution> nondominatedFilter(double[][] coefficientVectorSet, BasisFunction[] basisFunctions) {
-      return null;
-    }
-  }
 }
Index: /branches/3022-FastFunctionExtraction/FFX/FastFunctionExtraction.csproj
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/FastFunctionExtraction.csproj	(revision 17736)
+++ /branches/3022-FastFunctionExtraction/FFX/FastFunctionExtraction.csproj	(revision 17737)
@@ -1,4 +1,6 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\NUnit.3.12.0\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.12.0\build\NUnit.props')" />
+  <Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.props" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
@@ -13,4 +15,6 @@
     <FileAlignment>512</FileAlignment>
     <TargetFrameworkProfile />
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -104,10 +108,25 @@
     </Reference>
     <Reference Include="HeuristicLab.Problems.Instances-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec" />
+    <Reference Include="HeuristicLab.Problems.TestFunctions.MultiObjective-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\trunk\bin\HeuristicLab.Problems.TestFunctions.MultiObjective-3.3.dll</HintPath>
+    </Reference>
     <Reference Include="HeuristicLab.Random-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\..\..\trunk\bin\HeuristicLab.Random-3.3.dll</HintPath>
     </Reference>
+    <Reference Include="MathNet.Numerics, Version=4.9.1.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\MathNet.Numerics.4.9.1\lib\net461\MathNet.Numerics.dll</HintPath>
+    </Reference>
+    <Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
+      <HintPath>..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Numerics" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
+    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
@@ -116,13 +135,41 @@
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Xml" />
+    <Reference Include="xunit.execution.desktop, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
+      <HintPath>..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="BasisFunction.cs" />
+    <Compile Include="Approach.cs" />
+    <Compile Include="BFUtils.cs" />
+    <Compile Include="FFXModel.cs" />
+    <Compile Include="ISimpleBasisFunction.cs" />
+    <Compile Include="ProductBaseFunction.cs" />
+    <Compile Include="SimpleBasisFunction.cs" />
     <Compile Include="FastFunctionExtraction.cs" />
-    <Compile Include="Operator.cs" />
+    <Compile Include="IBasisFunction.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="NonlinearOperator.cs" />
+    <Compile Include="Utils.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="data\" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Analyzer Include="..\packages\xunit.analyzers.0.10.0\analyzers\dotnet\cs\xunit.analyzers.dll" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.props'))" />
+    <Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.targets'))" />
+    <Error Condition="!Exists('..\packages\NUnit.3.12.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.12.0\build\NUnit.props'))" />
+  </Target>
+  <Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
Index: /branches/3022-FastFunctionExtraction/FFX/IBasisFunction.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/IBasisFunction.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/IBasisFunction.cs	(revision 17737)
@@ -0,0 +1,10 @@
+﻿using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    internal interface IBasisFunction {
+        int Complexity { get; }
+        bool IsNominator { get; set; }
+        double[] Simulate(IRegressionProblemData data);
+        IBasisFunction DeepCopy();
+    }
+}
Index: /branches/3022-FastFunctionExtraction/FFX/ISimpleBasisFunction.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/ISimpleBasisFunction.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/ISimpleBasisFunction.cs	(revision 17737)
@@ -0,0 +1,9 @@
+﻿
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    interface ISimpleBasisFunction : IBasisFunction {
+        double Exponent { get; set; }
+        NonlinearOperator Operator { get; set; }
+        double Threshold { get; set; }
+        string Feature { get; set; }
+    }
+}
Index: /branches/3022-FastFunctionExtraction/FFX/NonlinearOperator.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/NonlinearOperator.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/NonlinearOperator.cs	(revision 17737)
@@ -0,0 +1,6 @@
+﻿
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    enum NonlinearOperator {
+        None, Abs, Log, Sin, Cos, Gth, Lth
+    }
+}
Index: anches/3022-FastFunctionExtraction/FFX/Operator.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/Operator.cs	(revision 17736)
+++ 	(revision )
@@ -1,15 +1,0 @@
-﻿using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction
-{
-    public enum NonlinOp { None, Abs, Log, Sin, Cos };
-    static class Operator
-    {
-        
-        public static string ToString(NonlinOp op, string val) => op != NonlinOp.None ? op.ToString() + "(" + val + ")" : val;
-    }
-}
Index: /branches/3022-FastFunctionExtraction/FFX/ProductBaseFunction.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/ProductBaseFunction.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/ProductBaseFunction.cs	(revision 17737)
@@ -0,0 +1,33 @@
+﻿using HeuristicLab.Problems.DataAnalysis;
+using System;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    internal struct ProductBaseFunction : IBasisFunction {
+        public IBasisFunction B1 { get; set; }
+        public IBasisFunction B2 { get; set; }
+
+        public int Complexity
+            => 1 + B1.Complexity + B2.Complexity;
+
+        public bool IsNominator { get; set; }
+
+        public ProductBaseFunction(IBasisFunction b1, IBasisFunction b2, bool nominator) {
+            B1 = b1 ?? throw new ArgumentNullException(nameof(b1));
+            B2 = b2 ?? throw new ArgumentNullException(nameof(b2));
+            IsNominator = nominator;
+        }
+
+        public double[] Simulate(IRegressionProblemData data) {
+            return B1.Simulate(data).Zip(B2.Simulate(data), (a, b) => a * b).ToArray();
+        }
+
+        public override string ToString() {
+            return $"{B1} * {B2}";
+        }
+
+        public IBasisFunction DeepCopy() {
+            return new ProductBaseFunction(B1, B2, IsNominator);
+        }
+    }
+}
Index: /branches/3022-FastFunctionExtraction/FFX/SimpleBasisFunction.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/SimpleBasisFunction.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/SimpleBasisFunction.cs	(revision 17737)
@@ -0,0 +1,105 @@
+﻿using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Problems.DataAnalysis;
+using System;
+using System.Globalization;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+
+    internal struct SimpleBasisFunction : ISimpleBasisFunction {
+        public double Exponent { get; set; }
+        public NonlinearOperator Operator { get; set; }
+        public bool IsNominator { get; set; }
+        public string Feature { get; set; }
+        public double Threshold { get; set; } // only relevant for hinge function 
+        public bool HasExponent => !Exponent.IsAlmost(1.0);
+        public bool HasOperator => Operator != NonlinearOperator.None;
+        public int Complexity => Operator == NonlinearOperator.Gth || Operator == NonlinearOperator.Lth ? 3 : 1;
+
+        public SimpleBasisFunction(string feature, double exponent = 1, NonlinearOperator op = NonlinearOperator.None, bool isNominator = true, double thr = 0) {
+            Feature = feature ?? throw new ArgumentNullException(nameof(feature));
+            Exponent = exponent;
+            Operator = op;
+            IsNominator = isNominator;
+            Threshold = thr;
+        }
+
+        public IBasisFunction DeepCopy() {
+            return new SimpleBasisFunction(Feature, Exponent, Operator, IsNominator, Threshold);
+        }
+
+        public override string ToString() {
+            var culture = new CultureInfo("en-US");
+            string str = $"\"{Feature}\"";
+
+            if (HasExponent) {
+                if (Exponent.IsAlmost((double)-1 / 2)) str = $"1 / SQRT({str})";
+                else if (Exponent.IsAlmost((double)1 / 2)) str = $"SQRT({str})";
+                else if (Exponent.IsAlmost(1)) str = $"1 / {str}";
+                else if (Exponent.IsAlmost(2)) str = $"SQR({str})";
+                else if (Exponent.IsAlmost(3)) str = $"CUBE({str})";
+                else str = $"({str}) ^ ({Exponent.ToString(culture)})";
+            }
+
+            if (!HasOperator) return str;
+
+            var thr = Threshold.ToString(culture);
+            if (Operator == NonlinearOperator.Lth) {
+                var expr = $"{str} {((Threshold >= 0) ? " - " + Math.Abs(Threshold).ToString(culture) : " + " + Math.Abs(Threshold).ToString(culture))}";
+                str = $"IF(GT(0, {expr}), 0, {expr})";
+            } else if (Operator == NonlinearOperator.Gth) {
+                var expr = $"{thr} - {str}";
+                str = $"IF(GT(0, {expr}), 0, {expr})";
+            } else {
+                str = $"{opToStr.GetByFirst(Operator)}({str})";
+            }
+            return str;
+        }
+
+        public double[] Simulate(IRegressionProblemData data) {
+            var exp = Exponent;
+            // exponentVals : e.g. "x3^2"
+            var exponentVals = data.Dataset.GetDoubleValues(Feature)
+                .Select(val => Math.Pow(val, exp))
+                .ToArray();
+            // vals: e.g. "log(x3^2)
+            var vals = Eval(exponentVals);
+
+            if (!IsNominator) {
+                var y = data.TargetVariableValues;
+                vals = vals.Zip(y, (a, b) => -a * b).ToArray();
+            }
+            return vals;
+        }
+
+        private double[] Eval(double[] x) {
+            var thr = this.Threshold;
+            switch (Operator) {
+                case NonlinearOperator.None:
+                    return x;
+                case NonlinearOperator.Abs:
+                    return x.Select(val => Math.Abs(val)).ToArray();
+                case NonlinearOperator.Log:
+                    return x.Select(val => Math.Log(val)).ToArray();
+                case NonlinearOperator.Sin:
+                    return x.Select(val => Math.Sin(val)).ToArray();
+                case NonlinearOperator.Cos:
+                    return x.Select(val => Math.Cos(val)).ToArray();
+                case NonlinearOperator.Gth:
+                    return x.Select(val => Math.Max(0, thr - val)).ToArray();
+                case NonlinearOperator.Lth:
+                    return x.Select(val => Math.Max(0, val - thr)).ToArray();
+                default:
+                    throw new Exception("Unimplemented operator: " + Operator.ToString());
+            }
+        }
+
+        private static readonly BidirectionalDictionary<NonlinearOperator, string> opToStr = new BidirectionalDictionary<NonlinearOperator, string>() {
+            { NonlinearOperator.Abs, "ABS"},
+            { NonlinearOperator.Log, "LOG"},
+            { NonlinearOperator.Sin, "SIN"},
+            { NonlinearOperator.Cos, "COS"}
+        };
+    }
+}
Index: /branches/3022-FastFunctionExtraction/FFX/Utils.cs
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/Utils.cs	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/Utils.cs	(revision 17737)
@@ -0,0 +1,71 @@
+﻿using HeuristicLab.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.FastFunctionExtraction {
+    internal static class Utils {
+        // return the nth row of a matrix
+        public static T[] GetRow<T>(this T[,] matrix, int n) {
+            var columns = matrix.GetLength(1);
+            var array = new T[columns];
+            for (int i = 0; i < columns; ++i)
+                array[i] = matrix[n, i];
+            return array;
+        }
+
+        public static IEnumerable<int> FindAllIndices<T>(this IEnumerable<T> values, Predicate<T> match) {
+            return values.Select((b, i) => match(b) ? i : -1).Where(i => i != -1).ToArray();
+        }
+
+        public static int[] Argsort<T>(T[] vals) where T : IComparable {
+            var indices = Enumerable.Range(0, vals.Length).ToArray();
+            Array.Sort(indices, (idx1, idx2) => vals[idx1].CompareTo(vals[idx2]));
+            return indices;
+        }
+
+        // find the array containing n (linearly) evenly spaced double values ranging from start to end (including both)
+        public static IEnumerable<double> Linspace(double start, double end, int n) {
+            if (end < start) throw new ArgumentException("end must be higher that start");
+            if (n <= 0) return Enumerable.Empty<double>();
+            if (n == 1) return new double[1] { start };
+            var range = end - start;
+            return Enumerable.Range(0, n).Select(idx => start + range * idx / (n - 1));
+        }
+
+        public static double[] Normalize(double[] vals)
+            => Bias(vals, 0, 1);
+
+        public static double[] Bias(double[] vals, double targetAvg, double targetStd) {
+            var result = new double[vals.Length];
+
+            // bias standard deviation
+            var stdRatio = vals.StandardDeviationPop() / targetStd;
+            for (int i = 0; i < vals.Length; i++) {
+                result[i] = vals[i] / stdRatio;
+            }
+
+            // bias average
+            var avgDiff = result.Average() - targetAvg;
+            for (int i = 0; i < vals.Length; i++) {
+                result[i] = result[i] - avgDiff;
+            }
+
+            return result;
+        }
+
+        public static bool[] ToBoolArray(this int val, int min_length) {
+            var temp = Convert.ToString(val, 2).Select(s => s.Equals('1')).ToArray();
+            if (temp.Length >= min_length) return temp;
+
+            var ans = new bool[min_length];
+            for(int i = 0; i < min_length - temp.Length; i++) {
+                ans[i] = false;
+            }
+            for(int i = 0; i < temp.Length; i++) {
+                ans[i + min_length - temp.Length] = temp[i];
+            }
+            return ans;
+        }
+    }
+}
Index: /branches/3022-FastFunctionExtraction/FFX/packages.config
===================================================================
--- /branches/3022-FastFunctionExtraction/FFX/packages.config	(revision 17737)
+++ /branches/3022-FastFunctionExtraction/FFX/packages.config	(revision 17737)
@@ -0,0 +1,13 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="MathNet.Numerics" version="4.9.1" targetFramework="net461" />
+  <package id="NUnit" version="3.12.0" targetFramework="net461" />
+  <package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
+  <package id="xunit" version="2.4.1" targetFramework="net461" />
+  <package id="xunit.abstractions" version="2.0.3" targetFramework="net461" />
+  <package id="xunit.analyzers" version="0.10.0" targetFramework="net461" />
+  <package id="xunit.assert" version="2.4.1" targetFramework="net461" />
+  <package id="xunit.core" version="2.4.1" targetFramework="net461" />
+  <package id="xunit.extensibility.core" version="2.4.1" targetFramework="net461" />
+  <package id="xunit.extensibility.execution" version="2.4.1" targetFramework="net461" />
+</packages>
