Index: /branches/2796_SymbReg/Build.cmd
===================================================================
--- /branches/2796_SymbReg/Build.cmd	(revision 15650)
+++ /branches/2796_SymbReg/Build.cmd	(revision 15650)
@@ -0,0 +1,104 @@
+@ECHO OFF
+
+SET CLEANBEFOREBUILD=
+
+SET SELECTED=
+SET CONFIGURATION=
+SET PLATFORM=
+
+IF "%~1"=="" GOTO :prompt_solution
+
+SET SELECTED=%1
+IF NOT EXIST %SELECTED% (
+  ECHO Solution file %SELECTED% could not be found.
+  GOTO :end
+)
+ECHO Building solution %SELECTED% ...
+GOTO :config_selection
+
+:prompt_solution
+SET /A COUNT=0
+FOR /F "tokens=*" %%A IN ('dir /B *.sln') DO (
+  CALL :forloopbody "%%A"
+)
+
+IF "%COUNT%"=="1" (
+  SET SELECTED=%SOLUTIONS.1%
+  ECHO Building %SOLUTIONS.1% as it is the only solution that was found ...
+  GOTO :config_selection
+)
+
+ECHO Found the following solutions:
+FOR /F "tokens=2* delims=.=" %%A IN ('SET SOLUTIONS.') DO ECHO %%A = %%B
+ECHO.
+SET /P SOLUTIONINDEX=Which solution to build? Type the number: 
+
+SET SELECTED=""
+FOR /F "tokens=2* delims=.=" %%A IN ('SET SOLUTIONS.') DO (
+  IF "%%A"=="%SOLUTIONINDEX%" SET SELECTED=%%B
+)
+
+IF %SELECTED%=="" GOTO :eof
+
+:config_selection
+IF "%~2"==""  GOTO :prompt_config
+
+SET CONFIGURATION=%~2
+ECHO Building configuration %CONFIGURATION% ...
+GOTO :platform_selection
+
+:prompt_config
+SET /P CONFIGURATION=Which configuration to build [Release]: 
+IF "%CONFIGURATION%"=="" SET CONFIGURATION=Release
+
+:platform_selection
+IF "%~3"=="" GOTO :prompt_platform
+  
+SET PLATFORM=%~3
+ECHO Building platform %PLATFORM% ...
+GOTO :clean
+
+:prompt_platform
+SET /P PLATFORM=Which platform to build [Any CPU]: 
+IF "%PLATFORM%"=="" SET PLATFORM=Any CPU
+
+:clean
+IF "%~4"=="" GOTO :prompt_clean
+
+SET CLEANBEFOREBUILD=%~4
+GOTO :main
+
+:prompt_clean
+SET /P CLEANBEFOREBUILD=Would you like to clean before building [n]: 
+IF "%CLEANBEFOREBUILD%"=="" SET CLEANBEFOREBUILD=n
+
+:main
+REM First find the path to the msbuild.exe by performing a registry query
+FOR /F "tokens=1,3 delims=	 " %%A IN ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0"') DO (
+  IF "%%A"=="MSBuildToolsPath" SET MSBUILDPATH=%%B)
+
+REM Then execute msbuild to clean and build the solution
+REM Disable that msbuild creates a cache file of the solution
+SET MSBuildUseNoSolutionCache=1
+REM Run msbuild to clean and then build
+IF "%CLEANBEFOREBUILD%" NEQ "n" (
+  ECHO Cleaning ...
+  %MSBUILDPATH%msbuild.exe %SELECTED% /target:Clean /p:Configuration="%CONFIGURATION%",Platform="%PLATFORM%" /m:2 /nologo /verbosity:q /clp:ErrorsOnly
+)
+ECHO Building ...
+%MSBUILDPATH%msbuild.exe %SELECTED% /target:Build /p:Configuration="%CONFIGURATION%",Platform="%PLATFORM%" /m:2 /nologo /verbosity:q /clp:ErrorsOnly
+
+ECHO.
+ECHO DONE.
+
+:end
+
+PAUSE
+
+GOTO :eof
+
+REM This workaround is necessary so that COUNT gets reevaluated
+:forloopbody
+SET /A COUNT+=1
+SET SOLUTIONS.%COUNT%=%1
+GOTO :eof
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj	(revision 15650)
@@ -0,0 +1,144 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{D97F91B5-B71B-412D-90A5-177EED948A3A}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg</RootNamespace>
+    <AssemblyName>HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="ALGLIB-3.7.0">
+      <HintPath>..\..\..\..\trunk\sources\bin\ALGLIB-3.7.0.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Algorithms.DataAnalysis-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Algorithms.DataAnalysis-3.4.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Analysis-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Analysis-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Collections-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Collections-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Common-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Common-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Core-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Core-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Data-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Data-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Encodings.SymbolicExpressionTreeEncoding-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Encodings.SymbolicExpressionTreeEncoding-3.4.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Operators-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Operators-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Optimization-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Optimization-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Parameters-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Parameters-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Persistence-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Persistence-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.PluginInfrastructure-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.PluginInfrastructure-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Problems.DataAnalysis-3.4.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis.Symbolic-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis.Symbolic.Regression-3.4">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Problems.DataAnalysis.Symbolic.Regression-3.4.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.Instances-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Problems.Instances-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Random-3.3">
+      <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Random-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Heuristics.cs" />
+    <Compile Include="MctsSymbolicRegression\ApproximateDoubleEqualityComparer.cs" />
+    <Compile Include="MctsSymbolicRegression\Automaton.cs" />
+    <Compile Include="MctsSymbolicRegression\CodeGenerator.cs" />
+    <Compile Include="MctsSymbolicRegression\Disassembler.cs" />
+    <Compile Include="MctsSymbolicRegression\ExpressionEvaluator.cs" />
+    <Compile Include="MctsSymbolicRegression\ExprHashSymbolic.cs" />
+    <Compile Include="MctsSymbolicRegression\MctsSymbolicRegressionAlgorithm.cs" />
+    <Compile Include="MctsSymbolicRegression\MctsSymbolicRegressionStatic.cs" />
+    <Compile Include="MctsSymbolicRegression\OpCodes.cs" />
+    <Compile Include="MctsSymbolicRegression\SymbolicExpressionGenerator.cs" />
+    <Compile Include="MctsSymbolicRegression\Tree.cs" />
+    <Compile Include="Plugin.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Plugin.cs.frame" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="MctsSymbolicRegression\Policies\" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <PropertyGroup>
+    <PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
+set ProjectDir=$(ProjectDir)
+set SolutionDir=$(SolutionDir)
+set Outdir=$(Outdir)
+
+call PreBuildEvent.cmd
+</PreBuildEvent>
+    <PreBuildEvent Condition=" '$(OS)' != 'Windows_NT' ">
+export ProjectDir=$(ProjectDir)
+export SolutionDir=$(SolutionDir)
+
+$SolutionDir/PreBuildEvent.sh
+</PreBuildEvent>
+  </PropertyGroup>
+</Project>
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Heuristics.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Heuristics.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Heuristics.cs	(revision 15650)
@@ -0,0 +1,76 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg {
+  // experimenting with heuristics
+  // 
+  // question: how can relevant interacting terms be reliably detected? 
+  //           - is this even feasible?
+  //           - even if variables are colinear?
+  //           - even for non-linear transformations
+  // 
+  // Also see Multi-variate adaptive regression splines (MARS)
+  // Maybe we could use MARS-style basis functions to identify the relevant interaction terms. (tune split points and find optimal interaction term with max spearmans rank) 
+  //
+  // assuming we interactions of have scaled/shifted variables (x + xo) * (y + yo) with constant xo and yo
+  // this leads to: x y + x yo + y xo + yo xo. 
+  // We only need to identify the x y as we assume that all other terms are accounted for
+  public static class Heuristics {
+    public static double CorrelationForInteraction(double[] a, double[] b, double[] c, double[] target) {
+      var am = a.Average();
+      var bm = b.Average();
+      var cm = c.Average();
+      var p1 = Enumerable.Range(0, a.Length).Where(i => a[i] < am);
+      var p2 = Enumerable.Range(0, a.Length).Where(i => a[i] > am);
+      var p3 = Enumerable.Range(0, a.Length).Where(i => b[i] < bm);
+      var p4 = Enumerable.Range(0, a.Length).Where(i => b[i] > bm);
+      var p5 = Enumerable.Range(0, a.Length).Where(i => c[i] < cm);
+      var p6 = Enumerable.Range(0, a.Length).Where(i => c[i] > cm);
+
+      return 1.0 / (p1.Count() + p2.Count() + p3.Count() + p4.Count() + p5.Count() + p6.Count()) * 
+        (
+        p1.Count() * CorrelationForInteraction(b, c, target, p1) +
+        p2.Count() * CorrelationForInteraction(b, c, target, p2) +
+        p3.Count() * CorrelationForInteraction(a, c, target, p3) +
+        p4.Count() * CorrelationForInteraction(a, c, target, p3) +
+        p5.Count() * CorrelationForInteraction(a, b, target, p5) +
+        p6.Count() * CorrelationForInteraction(a, b, target, p6)
+      );
+    }
+    public static double CorrelationForInteraction(double[] a, double[] b, double[] z) {
+      return CorrelationForInteraction(a, b, z, Enumerable.Range(0, a.Length));
+    }
+    public static double CorrelationForInteraction(double[] a, double[] b, double[] z, IEnumerable<int> idx) {
+      // 
+      var am = a.Average();
+      var bm = b.Average();
+      var p1 = idx.Where(i => a[i] < am);
+      var p2 = idx.Where(i => a[i] > am);
+      var p3 = idx.Where(i => b[i] < bm);
+      var p4 = idx.Where(i => b[i] > bm);
+
+      return 1.0 / (p1.Count() + p2.Count() + p3.Count() + p4.Count()) *
+             (p1.Count() * CorrelForPartition(b, z, p1) +
+             p2.Count() * CorrelForPartition(b, z, p2) +
+             p3.Count() * CorrelForPartition(a, z, p3) +
+             p4.Count() * CorrelForPartition(a, z, p4));
+    }
+
+    public static double CorrelForPartition(double[] a, double[] z, IEnumerable<int> idx) {
+      var zp = new List<double>();
+      var ap = new List<double>();
+      foreach (var i in idx) {
+        zp.Add(z[i]);
+        ap.Add(a[i]);
+      }
+      OnlineCalculatorError error;
+      var r = SpearmansRankCorrelationCoefficientCalculator.CalculateSpearmansRank(zp, ap, out error);
+      if (error != OnlineCalculatorError.None) r = 0;
+      return r * r;
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ApproximateDoubleEqualityComparer.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ApproximateDoubleEqualityComparer.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ApproximateDoubleEqualityComparer.cs	(revision 15650)
@@ -0,0 +1,26 @@
+﻿using System;
+using System.Collections.Generic;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  // unused?
+  internal class ApproximateDoubleEqualityComparer : IEqualityComparer<double> {
+    public bool Equals(double x, double y) {
+      // TODO: check
+      var xl = (ulong)BitConverter.DoubleToInt64Bits(x);
+      var yl = (ulong)BitConverter.DoubleToInt64Bits(y);
+
+      xl = xl & 0xFFFFFFFFFFFFFFE0; // ignore least significant bits
+      yl = yl & 0xFFFFFFFFFFFFFFE0;
+
+      return xl == yl;
+    }
+
+    public int GetHashCode(double obj) {
+      var bits = (ulong)BitConverter.DoubleToInt64Bits(obj);
+
+      bits = bits & 0xFFFFFFFFFFFFFFE0; // ignore least significant bits
+
+      return (int)bits;
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Automaton.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Automaton.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Automaton.cs	(revision 15650)
@@ -0,0 +1,491 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  // This is the core class for generating expressions. 
+  // It represents a finite state automaton, each state transition can be associated with an action (e.g. to produce code).
+  // The automaton determines the possible structures for expressions.
+  // 
+  // To understand this code, it is worthwhile to generate a graphical visualization of the automaton (see PrintAutomaton).
+  // If the code is compiled in debug mode, the automaton produces a Graphviz file into the folder of the application
+  // whenever an instance of the automaton is constructed.
+  //
+  // This class relies on two other classes: 
+  // - CodeGenerator to produce code for a stack-based evaluator and
+  // - ConstraintHandler to restrict the allowed set of expressions. 
+  //
+  // The ConstraintHandler extends the automaton and adds semantic restrictions for expressions produced by the automaton.
+  // 
+  // 
+  internal class Automaton {
+
+    // there is a single final state (ExprEnd)
+    // states with lower values are closer to the final state 
+    // (this is helpful when we try to navigate to the final state)
+
+    // we cannot use an enum type here because the set of states is dynamic (including states for variables)
+    public const int StateExprEnd = 1;
+    public const int StateTermEnd = 2;
+    public const int StateFactorEnd = 3;
+
+    public const int StateVariableFactorEnd = 4;
+    public const int StateExpFactorEnd = 5;
+    public const int StateLogFactorEnd = 6;
+    public const int StateInvFactorEnd = 7;
+
+    public const int StateExpFEnd = 8;
+    public const int StateLogTEnd = 9;
+    public const int StateInvTEnd = 10;
+
+    public const int StateLogTFEnd = 11;
+    public const int StateInvTFEnd = 12;
+
+    public const int StateLogTFStart = 13;
+    public const int StateInvTFStart = 14;
+
+    public const int StateExpFStart = 15;
+    public const int StateLogTStart = 16;
+    public const int StateInvTStart = 17;
+
+    public const int StateVariableFactorStart = 18;
+    public const int StateExpFactorStart = 19;
+    public const int StateLogFactorStart = 20;
+    public const int StateInvFactorStart = 21;
+
+    public const int StateFactorStart = 22;
+    public const int StateTermStart = 23;
+    public const int StateExpr = 24;
+    public const int FirstDynamicState = 25;
+    // more states for individual variables are created dynamically
+    public readonly List<string> stateNames = new List<string>() {
+       string.Empty,
+      "ExprEnd",
+      "TermEnd",
+      "FactorEnd",
+      "VariableFactorEnd",
+      "ExpFactorEnd",
+      "LogFactorEnd",
+      "InvFactorEnd",
+      "ExpFEnd",
+      "LogTEnd",
+      "InvTEnd",
+      "LogTFEnd",
+      "InfTFEnd",
+      "LogTFStart",
+      "InvTFStart",
+      "ExpFStart",
+      "LogTStart",
+      "InvTStart",
+      "VariableFactorStart",
+      "ExpFactorStart",
+      "LogFactorStart",
+      "InvFactorStart",
+      "FactorStart",
+      "TermStart",
+      "Expr",
+    };
+
+
+    private const int StartState = StateExpr;
+    public int CurrentState { get; private set; }
+
+    private List<int>[] followStates;
+    private List<Action>[,] actions; // not every follow state is possible but this representation should be efficient
+    private List<string>[,] actionStrings; // just for printing 
+    private readonly CodeGenerator codeGenerator;
+    private int numVarRefs;
+    private int maximumNumberOfVariables;
+
+    public Automaton(double[][] vars,
+       bool allowProdOfVars = true,
+       bool allowExp = true,
+       bool allowLog = true,
+       bool allowInv = true,
+       bool allowMultipleTerms = false,
+       int maxNumberOfVariables = 5) {
+      int nVars = vars.Length;
+      this.maximumNumberOfVariables = maxNumberOfVariables;
+      codeGenerator = new CodeGenerator();
+      BuildAutomaton(nVars, allowProdOfVars, allowExp, allowLog, allowInv, allowMultipleTerms);
+
+      Reset();
+#if DEBUG
+      PrintAutomaton();
+#endif
+    }
+
+    // Produce postfix notation for expression:
+    // Expr -> 0 Term { '+' Term } '+' 'exit'
+    // Term -> c Fact { '*' Fact } '*'
+    // Fact -> VarFact | ExpFact | LogFact | InvFact
+    // VarFact -> var_1 ... var_n
+    // ExpFact -> 1 ExpF { '*' ExpF } '*' c '*' 'exp' // c must be at end to allow scaling in evaluator
+    // ExpF    -> var_1 ... var_n 
+    // LogFact -> 0 LogT { '+' LogT } '+' c '+' 'log' // c must be at end to allow scaling in evaluator
+    // LogT    -> c LogTF { '*' LogTF } '*' 
+    // LogTF   -> var_1 ... var_n 
+    // InvFact -> 1 InvT { '+' InvT } '+' 'inv'
+    // InvT    -> (var_1 ... var_n) c '*' 
+    private void BuildAutomaton(int nVars,
+      bool allowProdOfVars = true,
+       bool allowExp = true,
+       bool allowLog = true,
+       bool allowInv = true,
+       bool allowMultipleTerms = false) {
+
+      int nStates = FirstDynamicState + 4 * nVars;
+      followStates = new List<int>[nStates];
+      actions = new List<Action>[nStates, nStates];
+      actionStrings = new List<string>[nStates, nStates];
+
+      // Expr -> 0 Term { '+' Term } '+' 'exit'
+      AddTransition(StateExpr, StateTermStart, () => {
+        codeGenerator.Reset();
+        codeGenerator.Emit1(OpCodes.LoadConst0);
+        numVarRefs = 0;
+      }, "0");
+      AddTransition(StateTermEnd, StateExprEnd, () => {
+        codeGenerator.Emit1(OpCodes.Add);
+        codeGenerator.Emit1(OpCodes.Exit);
+      }, "+ exit");
+      if (allowMultipleTerms)
+        AddTransition(StateTermEnd, StateTermStart, () => {
+          codeGenerator.Emit1(OpCodes.Add);
+        }, "+");
+
+      // Term -> c Fact { '*' Fact } '*'
+      AddTransition(StateTermStart, StateFactorStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.LoadParamN);
+        },
+        "c");
+      AddTransition(StateFactorEnd, StateTermEnd,
+        () => {
+          codeGenerator.Emit1(OpCodes.Mul);
+        },
+        "*");
+
+      AddTransition(StateFactorEnd, StateFactorStart,
+        () => { codeGenerator.Emit1(OpCodes.Mul); },
+        "*");
+
+
+      // Fact -> VarFact | ExpFact | LogFact | InvFact
+      if (allowProdOfVars)
+        AddTransition(StateFactorStart, StateVariableFactorStart, () => {
+        }, "");
+      if (allowExp)
+        AddTransition(StateFactorStart, StateExpFactorStart, () => {
+        }, "");
+      if (allowLog)
+        AddTransition(StateFactorStart, StateLogFactorStart, () => {
+        }, "");
+      if (allowInv)
+        AddTransition(StateFactorStart, StateInvFactorStart, () => {
+        }, "");
+      AddTransition(StateVariableFactorEnd, StateFactorEnd);
+      AddTransition(StateExpFactorEnd, StateFactorEnd);
+      AddTransition(StateLogFactorEnd, StateFactorEnd);
+      AddTransition(StateInvFactorEnd, StateFactorEnd);
+
+      // VarFact -> var_1 ... var_n
+      // add dynamic states for each variable 
+      int curDynVarState = FirstDynamicState;
+      for (int i = 0; i < nVars; i++) {
+        short varIdx = (short)i;
+        var varState = curDynVarState;
+        stateNames.Add("var_1");
+        AddTransition(StateVariableFactorStart, curDynVarState,
+          () => {
+            codeGenerator.Emit2(OpCodes.LoadVar, varIdx);
+            numVarRefs++;
+          },
+          "var_" + varIdx + "");
+        AddTransition(curDynVarState, StateVariableFactorEnd);
+        curDynVarState++;
+      }
+
+      // ExpFact -> 1 ExpF { '*' ExpF } '*' c '*' 'exp' 
+      AddTransition(StateExpFactorStart, StateExpFStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.LoadConst1);
+        },
+        "1");
+      AddTransition(StateExpFEnd, StateExpFactorEnd,
+        () => {
+          codeGenerator.Emit1(OpCodes.LoadParamN);
+          codeGenerator.Emit1(OpCodes.Mul);
+          codeGenerator.Emit1(OpCodes.Exp);
+        },
+        "c*exp");
+      AddTransition(StateExpFEnd, StateExpFStart,
+        () => { },
+        "");
+
+      // ExpF    -> var_1 ... var_n 
+      for (int i = 0; i < nVars; i++) {
+        short varIdx = (short)i;
+        int varState = curDynVarState;
+        stateNames.Add("var_2");
+        AddTransition(StateExpFStart, curDynVarState,
+          () => {
+            codeGenerator.Emit2(OpCodes.LoadVar, varIdx);
+            numVarRefs++;
+          },
+          "var_" + varIdx + "");
+        AddTransition(curDynVarState, StateExpFEnd,
+          () => {
+            codeGenerator.Emit1(OpCodes.Mul);
+          }, "*");
+        curDynVarState++;
+      }
+
+      // must have c at end because of adjustment of c in evaluator
+      // LogFact -> 0 LogT { '+' LogT } '+' c '+' 'log'
+      AddTransition(StateLogFactorStart, StateLogTStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.LoadConst0);
+        },
+        "0");
+      AddTransition(StateLogTEnd, StateLogFactorEnd,
+        () => {
+          codeGenerator.Emit1(OpCodes.Add);
+          codeGenerator.Emit1(OpCodes.LoadParamN);
+          codeGenerator.Emit1(OpCodes.Add);
+          codeGenerator.Emit1(OpCodes.Log);
+        },
+        "+c+log");
+      AddTransition(StateLogTEnd, StateLogTStart,
+        () => { codeGenerator.Emit1(OpCodes.Add); },
+        "+");
+
+      // LogT    -> c LogTF { '*' LogTF } '*' 
+      AddTransition(StateLogTStart, StateLogTFStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.LoadParamN);
+        },
+        "c");
+      AddTransition(StateLogTFEnd, StateLogTEnd,
+        () => {
+          codeGenerator.Emit1(OpCodes.Mul);
+        },
+        "*");
+      AddTransition(StateLogTFEnd, StateLogTFStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.Mul);
+        },
+        "*");
+
+      // LogTF   -> var_1 ... var_n
+      for (int i = 0; i < nVars; i++) {
+        short varIdx = (short)i;
+        int varState = curDynVarState;
+        stateNames.Add("var_3");
+        AddTransition(StateLogTFStart, curDynVarState,
+          () => {
+            codeGenerator.Emit2(OpCodes.LoadVar, varIdx);
+            numVarRefs++;
+          },
+          "var_" + varIdx + "");
+        AddTransition(curDynVarState, StateLogTFEnd);
+        curDynVarState++;
+      }
+
+      // InvFact -> 1 InvT { '+' InvT } '+' 'inv'
+      AddTransition(StateInvFactorStart, StateInvTStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.LoadConst1);
+        },
+        "c");
+      AddTransition(StateInvTEnd, StateInvFactorEnd,
+        () => {
+          codeGenerator.Emit1(OpCodes.Add);
+          codeGenerator.Emit1(OpCodes.Inv);
+        },
+        "+inv");
+      AddTransition(StateInvTEnd, StateInvTStart,
+        () => { codeGenerator.Emit1(OpCodes.Add); },
+        "+");
+
+      // InvT    -> c InvTF { '*' InvTF } '*' 
+      AddTransition(StateInvTStart, StateInvTFStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.LoadParamN);
+        },
+        "c");
+      AddTransition(StateInvTFEnd, StateInvTEnd,
+        () => {
+          codeGenerator.Emit1(OpCodes.Mul);
+        },
+        "*");
+      AddTransition(StateInvTFEnd, StateInvTFStart,
+        () => {
+          codeGenerator.Emit1(OpCodes.Mul);
+        },
+        "*");
+
+      // InvTF    -> (var_1 ... var_n) c '*' 
+      for (int i = 0; i < nVars; i++) {
+        short varIdx = (short)i;
+        int varState = curDynVarState;
+        stateNames.Add("var_4");
+        AddTransition(StateInvTFStart, curDynVarState,
+          () => {
+            codeGenerator.Emit2(OpCodes.LoadVar, varIdx);
+            numVarRefs++;
+          },
+          "var_" + varIdx + "");
+        AddTransition(curDynVarState, StateInvTFEnd);
+        curDynVarState++;
+      }
+
+      followStates[StateExprEnd] = new List<int>(); // no follow states
+
+      // order all follow states (the first follow state leads to the final state)
+      foreach (var list in followStates) {
+        if (list != null)
+          list.Sort();
+      }
+    }
+
+    private void AddTransition(int fromState, int toState) {
+      if (followStates[fromState] == null) followStates[fromState] = new List<int>();
+      followStates[fromState].Add(toState);
+    }
+    private void AddTransition(int fromState, int toState, Action action, string str) {
+      if (followStates[fromState] == null) followStates[fromState] = new List<int>();
+      followStates[fromState].Add(toState);
+
+      if (actions[fromState, toState] == null) {
+        actions[fromState, toState] = new List<Action>();
+        actionStrings[fromState, toState] = new List<string>();
+      }
+
+      actions[fromState, toState].Add(action);
+      actionStrings[fromState, toState].Add(str);
+    }
+
+    public void FollowStates(int state, ref int[] buf, out int nElements) {
+      var fs = followStates[state];
+      int j = 0;
+      for (int i = 0; i < fs.Count; i++) {
+        var s = fs[i];
+        if (IsAllowedFollowState(state, s)) {
+          buf[j++] = s;
+        }
+      }
+      nElements = j;
+    }
+
+    private bool IsAllowedFollowState(int state, int nextState) {
+      // any state is allowed if we have not reached the max number of variable references
+      // otherwise we can only go towards the final state (smaller state numbers)
+      if (numVarRefs < maximumNumberOfVariables) return true;
+      else return state > nextState;
+    }
+
+    public void Goto(int targetState) {
+      Debug.Assert(followStates[CurrentState].Contains(targetState));
+      if (actions[CurrentState, targetState] != null)
+        actions[CurrentState, targetState].ForEach(a => a()); // execute all actions
+      CurrentState = targetState;
+    }
+
+    public bool IsFinalState(int s) {
+      return s == StateExprEnd && numVarRefs <= maximumNumberOfVariables;
+    }
+
+    public bool IsEvalState(int v) {
+      return v == StateFactorEnd ||
+        v == StateLogTFEnd ||
+        v == StateInvTFEnd ||
+        v == StateExpFEnd ||
+        v == StateLogTEnd ||
+        v == StateInvTEnd ||
+        v == StateTermEnd 
+        ;
+    }
+
+
+    // Always returns valid code.
+    // If the method is called in an intermediate state the expression is completed by 
+    // taking the shortest route to the final state. 
+    // After that state of the automaton is restored to the current state.
+    public void GetCode(out byte[] code, out int nParams) {
+      int storedState = CurrentState;
+      int storedPC = codeGenerator.ProgramCounter;
+
+      if (CurrentState != StateExprEnd) {
+        // save state and code,
+        storedState = CurrentState;
+        storedPC = codeGenerator.ProgramCounter;
+
+        // take shortest route to final state (smaller state values are closer to the final state)
+        while (CurrentState != StateExprEnd) {
+          Debug.Assert(followStates[CurrentState][0] == followStates[CurrentState].Min());
+          var nextState = followStates[CurrentState][0];
+          Goto(nextState);
+        }
+      }
+
+      codeGenerator.GetCode(out code, out nParams);
+
+      // restore 
+      codeGenerator.ProgramCounter = storedPC;
+      CurrentState = storedState;
+    }
+
+    public void Reset() {
+      CurrentState = StartState;
+      codeGenerator.Reset();
+      numVarRefs = 0;
+    }
+
+    internal string GetActionString(int fromState, int toState) {
+      return actionStrings[fromState, toState] != null ? string.Join(" , ", actionStrings[fromState, toState]) : "";
+    }
+
+#if DEBUG
+    public void PrintAutomaton() {
+      using (var writer = new StreamWriter("automaton.gv")) {
+        writer.WriteLine("digraph {");
+        // writer.WriteLine("rankdir=LR");
+        for (int s = 1; s < stateNames.Count; s++) {
+          for (int i = 0; i < followStates[s].Count; i++) {
+            if (followStates[s][i] <= 0) continue;
+            var followS = followStates[s][i];
+            var label = actionStrings[s, followS] != null ? string.Join(" , ", actionStrings[s, followS]) : "";
+            writer.WriteLine("{0} -> {1} [ label = \"{2}\" ];", stateNames[s], stateNames[followS], label);
+          }
+        }
+        writer.WriteLine("}");
+      }
+    }
+
+#endif
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/CodeGenerator.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/CodeGenerator.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/CodeGenerator.cs	(revision 15650)
@@ -0,0 +1,62 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Diagnostics.Contracts;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  public class CodeGenerator {
+
+    public const int MaxCodeLength = 10000;
+    public const int MaxConstants = 100;
+
+    private readonly byte[] codeArray = new byte[MaxCodeLength];
+    private int pc;
+    private int nParams;
+
+    // for retrieving and restoring the PC
+    internal int ProgramCounter {
+      get { return pc; }
+      set { pc = value; }
+    }
+
+    public void Reset() {
+      pc = 0;
+      nParams = 0;
+    }
+
+    public void Emit1(OpCodes code) {
+      codeArray[pc++] = (byte)code;
+      if (code == OpCodes.LoadParamN) nParams++;
+    }
+
+    public void Emit2(OpCodes code, short arg) {
+      Contract.Assert(code == OpCodes.LoadVar); // only loadVar opcode has params
+      Emit1(code);
+      codeArray[pc++] = (byte)(arg >> 8);
+      codeArray[pc++] = (byte)(arg & 0xFF);
+    }
+
+    public void GetCode(out byte[] code, out int nParams) {
+      code = codeArray;
+      nParams = this.nParams;
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Disassembler.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Disassembler.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Disassembler.cs	(revision 15650)
@@ -0,0 +1,77 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Text;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  internal class Disassembler {
+    public static string CodeToString(byte[] code, double[] consts) {
+      var sb = new StringBuilder();
+      int pc = 0;
+      int nextParamIdx = -1;
+      while (code[pc] != (byte)OpCodes.Exit) {
+        var op = code[pc++];
+        switch (op) {
+          case (byte)OpCodes.Add: sb.Append(" + "); break;
+          case (byte)OpCodes.Mul: sb.Append(" * "); break;
+          case (byte)OpCodes.LoadConst1: sb.Append(" 1 "); break;
+          case (byte)OpCodes.LoadConst0: sb.Append(" 0 "); break;
+          case (byte)OpCodes.LoadParamN: sb.AppendFormat(" {0:N3} ", consts[++nextParamIdx]); break;
+          case (byte)OpCodes.LoadVar:
+          {
+              short arg = (short)((code[pc] << 8) | code[pc + 1]);
+              pc += 2;
+            sb.AppendFormat(" var{0} ", arg); break;
+          }
+          case (byte)OpCodes.Exp: sb.Append(" exp "); break;
+          case (byte)OpCodes.Log: sb.Append(" log "); break;
+          case (byte)OpCodes.Inv: sb.Append(" inv "); break;          
+        }
+      }
+      return sb.ToString();
+    }
+
+    public static string CodeToString(byte[] code) {
+      var sb = new StringBuilder();
+      int pc = 0;
+      int nextParamIdx = -1;
+      while (code[pc] != (byte)OpCodes.Exit) {
+        var op = code[pc++];
+        switch (op) {
+          case (byte)OpCodes.Add: sb.Append(" + "); break;
+          case (byte)OpCodes.Mul: sb.Append(" * "); break;
+          case (byte)OpCodes.LoadConst1: sb.Append(" 1 ");  break;
+          case (byte)OpCodes.LoadConst0: sb.Append(" 0 "); break;
+          case (byte)OpCodes.LoadParamN: sb.Append(" c "); break;
+          case (byte)OpCodes.LoadVar: {
+              short arg = (short)((code[pc] << 8) | code[pc + 1]);
+              pc += 2;
+              sb.AppendFormat("{0}", (char)('a'+arg)); break;
+            }
+          case (byte)OpCodes.Exp: sb.Append(" exp "); break;
+          case (byte)OpCodes.Log: sb.Append(" log "); break;
+          case (byte)OpCodes.Inv: sb.Append(" inv "); break;
+        }
+      }
+      return sb.ToString();
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHashSymbolic.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHashSymbolic.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExprHashSymbolic.cs	(revision 15650)
@@ -0,0 +1,273 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+// code for hashing of expressions based on symbolic analysis of monomials and polynomials.
+// slow, but easy to implement correctly.
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  internal enum UnaryFunctionType { Log, Exp, Inv };
+  internal interface Factor { }
+  internal class SymbolFactor : Factor {
+    internal char symbolId;
+    public SymbolFactor(char symbolId) {
+      this.symbolId = symbolId;
+    }
+    public override int GetHashCode() {
+      return symbolId.GetHashCode();
+    }
+    public override bool Equals(object obj) {
+      SymbolFactor other = obj as SymbolFactor;
+      if (other == null) return false;
+      else return other.symbolId == this.symbolId;
+    }
+  }
+    internal class FunctionFactor : Factor {
+    internal UnaryFunctionType functionType;
+    internal Polynomial argument;
+
+    public FunctionFactor(UnaryFunctionType functionType, Polynomial argument) {
+      this.functionType = functionType;
+      this.argument = argument;
+    }
+
+    public override int GetHashCode() {
+      var h = functionType.GetHashCode();
+      return ((h<<5)+h) ^ argument.GetHashCode();
+    }
+    public override bool Equals(object obj) {
+      FunctionFactor other = obj as FunctionFactor;
+      if (other == null) return false;
+      return
+        other.functionType == this.functionType &&
+        other.argument == this.argument;
+    }
+  }
+
+  internal class Monomial {
+    internal List<Factor> factors = new List<Factor>();
+    public Monomial(params Factor[] factor) {
+      foreach (var f in factor) factors.Add(f);
+    }
+    public override int GetHashCode() {
+      return factors.OrderBy(ti => ti.GetHashCode()).Aggregate(0, (a, ti) => ((a  << 5) + a) ^ ti.GetHashCode());
+    }
+    public override bool Equals(object obj) {
+      Monomial other = obj as Monomial;
+      if (other == null) return false;
+      if (other.factors.Count != this.factors.Count) return false;
+      return factors.All(ti => other.factors.Contains(ti)) &&
+        other.factors.All(ti => factors.Contains(ti));
+    }
+
+    public static Monomial Product(Monomial a, Monomial b) {
+      var p = new Monomial();
+      var invFactorArg = new Polynomial();
+      var expFactorArg = new Polynomial();
+      var expFactor = new FunctionFactor(UnaryFunctionType.Exp, expFactorArg);
+
+      foreach (var aFactor in a.factors.Concat(b.factors)) {
+        // collect all exp and inv factors into one and simplify
+        var funcFactor = aFactor as FunctionFactor;
+        if (funcFactor != null && funcFactor.functionType == UnaryFunctionType.Exp) {
+          expFactorArg.Add(funcFactor.argument);
+        } else if (funcFactor != null && funcFactor.functionType == UnaryFunctionType.Inv) {
+          if (!invFactorArg.terms.Any()) invFactorArg.terms = new HashSet<Monomial>(funcFactor.argument.terms);
+          else invFactorArg.Mul(funcFactor.argument);
+        } else {
+          p.factors.Add(aFactor);
+        }
+      }
+      if (expFactorArg.terms.Any()) {
+        p.factors.Add(expFactor);
+      }
+      if (invFactorArg.terms.Any()) {
+        var invFactor = new FunctionFactor(UnaryFunctionType.Inv, invFactorArg);
+        p.factors.Add(invFactor);
+      }
+      return p;
+    }
+  }
+
+  internal class Polynomial {
+    internal HashSet<Monomial> terms = new HashSet<Monomial>();
+    public Polynomial(params Monomial[] term) {
+      foreach (var t in term) terms.Add(t);
+    }
+    public void Add(Polynomial other) {
+      this.terms.UnionWith(other.terms);
+    }
+    public void Mul(Polynomial other) {
+      var myTerms = terms;
+      var otherTerms = other.terms;
+      var newTerms = new HashSet<Monomial>();
+      foreach (var a in myTerms) {
+        foreach (var b in otherTerms) {
+          newTerms.Add(Monomial.Product(a, b));
+        }
+      }
+      terms = newTerms;
+    }
+    public override int GetHashCode() {
+      return terms.OrderBy(ti => ti.GetHashCode()).Aggregate(0, (a, ti) => ((a<<5)+a) ^ ti.GetHashCode());
+    }
+    public override bool Equals(object obj) {
+      Polynomial other = obj as Polynomial;
+      if (other == null) return false;
+      if (other.terms.Count != this.terms.Count) return false;
+      return terms.All(ti => other.terms.Contains(ti)) && 
+        other.terms.All(ti => terms.Contains(ti));
+    }
+  }
+
+
+  // calculates a hash-code for expressions.
+  public static class ExprHashSymbolic {
+
+
+    const int MaxStackSize = 100;
+    const int MaxVariables = 26;
+    private static SymbolFactor[] varSymbols;
+    private static SymbolFactor zero;
+    private static SymbolFactor one;
+
+
+    static ExprHashSymbolic() {
+      const string symbols = "abcdefghijklmnopqrstuvwxyz"; // limited to 26 variables -> TODO
+
+      varSymbols = new SymbolFactor[MaxVariables];
+      for (int i = 0; i < MaxVariables; i++) {
+        varSymbols[i] = new SymbolFactor(symbols[i]);
+      }
+      zero = new SymbolFactor('0');
+      one = new SymbolFactor('1');
+    }
+
+    public static int GetHash(byte[] code, int nParams) {
+      return Eval(code, nParams);
+    }
+
+	
+	// takes an array of code and transforms it into a polynomial for hashing.
+	// slow!
+    private static int Eval(byte[] code, int nParams) {
+      // The hash code calculation already preserves commutativity, associativity and distributivity of operations.
+      // We assume that the structure contains numeric parameters 
+      // x = c*x
+      // exp(x) = c*exp(c*x)
+      // log(x) = c*log(x+c)
+      // inv(x) = c/(x+c)
+      // Accordingly, each structure represents a class of functions.
+
+      // The following expressions should hash to the same value as they represent 
+      // equivalent function classes
+      // - x1 + x1 is equivalent to x1
+      // - exp(x1) * exp(x1) is equivalent to exp(x1) 
+      // 
+      // The following expressions must not hash to the same value.
+      // - exp(x1) + exp(x1) is different from exp(x1), because c1*exp(c2*x1) + c3*exp(c4*x1) cannot be simplified to c5*exp(c6*x1) for all values of c2 and c4 
+      // - log(x1) + log(x1) is different from log(x1), same as above
+      // - 1/x1 + 1/x1 is different from 1/x1, same as above 1/(x1 + c1) + 1/(x1 + c2)
+      // - TODO: list further exceptions
+   
+
+      var stack = new Polynomial[MaxStackSize];
+      int topOfStack = -1;
+      int pc = 0;
+      int nextParamIdx = -1;
+      OpCodes op;
+      short arg;
+      while (true) {
+        ReadNext(code, ref pc, out op, out arg);
+        switch (op) {
+          case OpCodes.Nop: break;
+          case OpCodes.LoadConst0: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(zero));
+              break;
+            }
+          case OpCodes.LoadConst1: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(one));
+              break;
+            }
+          case OpCodes.LoadParamN: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(one)); // TODO empty, or unique?
+              break;
+            }
+          case OpCodes.LoadVar: {
+              ++topOfStack;
+              stack[topOfStack] = new Polynomial(new Monomial(varSymbols[arg]));
+
+              break;
+            }
+          case OpCodes.Add: {
+              stack[topOfStack - 1].Add(stack[topOfStack]);
+              topOfStack--;
+              break;
+            }
+          case OpCodes.Mul: {
+              stack[topOfStack - 1].Mul(stack[topOfStack]);
+              topOfStack--;
+              break;
+            }
+          case OpCodes.Log: {
+              var v1 = stack[topOfStack];
+              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Log, v1)));
+              break;
+            }
+          case OpCodes.Exp: {
+              var v1 = stack[topOfStack];
+              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Exp, v1)));
+              break;
+            }
+          case OpCodes.Inv: {
+              var v1 = stack[topOfStack];
+              stack[topOfStack] = new Polynomial(new Monomial(new FunctionFactor(UnaryFunctionType.Inv, v1)));
+              break;
+            }
+          case OpCodes.Exit:
+            Contract.Assert(topOfStack == 0);
+            return stack[topOfStack].GetHashCode();
+          default: throw new InvalidOperationException();
+        }
+      }
+    }
+
+    private static void EvalTerms(HashSet<double> terms, double[] stack, ref int topOfStack) {
+      ++topOfStack;
+      stack[topOfStack] = terms.Sum();
+      terms.Clear();
+    }
+
+    private static void ReadNext(byte[] code, ref int pc, out OpCodes op, out short s) {
+      op = (OpCodes)Enum.ToObject(typeof(OpCodes), code[pc++]);
+      s = 0;
+      if (op == OpCodes.LoadVar) {
+        s = (short)((code[pc] << 8) | code[pc + 1]);
+        pc += 2;
+      }
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExpressionEvaluator.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExpressionEvaluator.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/ExpressionEvaluator.cs	(revision 15650)
@@ -0,0 +1,520 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  // evaluates expressions (on vectors)
+  internal class ExpressionEvaluator {
+    // manages it's own vector buffers
+    private readonly double[][] vectorBuffers;
+    private readonly double[][] scalarBuffers; // scalars are vectors of length 1 (to allow mixing scalars and vectors on the same stack)
+    private int lastVecBufIdx;
+    private int lastScalarBufIdx;
+
+
+    private double[] GetVectorBuffer() {
+      return vectorBuffers[--lastVecBufIdx];
+    }
+    private double[] GetScalarBuffer() {
+      return scalarBuffers[--lastScalarBufIdx];
+    }
+
+    private void ReleaseBuffer(double[] buf) {
+      if (buf.Length == 1) {
+        scalarBuffers[lastScalarBufIdx++] = buf;
+      } else {
+        vectorBuffers[lastVecBufIdx++] = buf;
+      }
+    }
+
+    public const int MaxStackSize = 100;
+    public const int MaxParams = 50;
+    private readonly int vLen;
+    private readonly double lowerEstimationLimit;
+    private readonly double upperEstimationLimit;
+    private readonly double nanReplacementValue;
+
+    private readonly double[][] stack;
+    private readonly double[][][] gradientStack;
+
+    // preallocate stack and gradient stack 
+    public ExpressionEvaluator(int vLen, double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue) {
+      if (vLen <= 1) throw new ArgumentException("number of elements in a variable must be > 1", "vlen");
+      this.vLen = vLen;
+      this.lowerEstimationLimit = lowerEstimationLimit;
+      this.upperEstimationLimit = upperEstimationLimit;
+      this.nanReplacementValue = (upperEstimationLimit - lowerEstimationLimit) / 2.0 + lowerEstimationLimit;
+
+      stack = new double[MaxStackSize][];
+      gradientStack = new double[MaxParams][][];
+
+      for (int k = 0; k < MaxParams; k++) {
+        gradientStack[k] = new double[MaxStackSize][];
+      }
+
+      // preallocate buffers 
+      vectorBuffers = new double[MaxStackSize * (1 + MaxParams)][];
+      scalarBuffers = new double[MaxStackSize * (1 + MaxParams)][];
+      for (int i = 0; i < MaxStackSize; i++) {
+        ReleaseBuffer(new double[vLen]);
+        ReleaseBuffer(new double[1]);
+
+        for (int k = 0; k < MaxParams; k++) {
+          ReleaseBuffer(new double[vLen]);
+          ReleaseBuffer(new double[1]);
+        }
+      }
+    }
+
+    // pred must be allocated by the caller
+    // if adjustOffsetForLogAndExp is set to true we determine c in log(c + f(x)) to make sure that c + f(x) is positive
+    public void Exec(byte[] code, double[][] vars, double[] consts, double[] pred, bool adjustOffsetForLogAndExp = false) {
+      Contract.Assert(pred != null && pred.Length >= vLen);
+      int topOfStack = -1;
+      int pc = 0;
+      int curParamIdx = -1;
+      byte op;
+      short arg;
+      // checked at the end to make sure we do not leak buffers
+      int initialScalarCount = lastScalarBufIdx;
+      int initialVectorCount = lastVecBufIdx;
+
+      while (true) {
+        ReadNext(code, ref pc, out op, out arg);
+        switch (op) {
+          case (byte)OpCodes.Nop: throw new InvalidProgramException(); // not allowed
+          case (byte)OpCodes.LoadConst0: {
+              ++topOfStack;
+              var z = GetScalarBuffer();
+              z[0] = 0;
+              stack[topOfStack] = z;
+              break;
+            }
+          case (byte)OpCodes.LoadConst1: {
+              ++topOfStack;
+              var z = GetScalarBuffer();
+              z[0] = 1.0;
+              stack[topOfStack] = z;
+              break;
+            }
+          case (byte)OpCodes.LoadParamN: {
+              ++topOfStack;
+              var c = consts[++curParamIdx];
+              var z = GetScalarBuffer();
+              z[0] = c;
+              stack[topOfStack] = z;
+              break;
+            }
+          case (byte)OpCodes.LoadVar: {
+              ++topOfStack;
+              var z = GetVectorBuffer();
+              Array.Copy(vars[arg], z, vars[arg].Length);
+              stack[topOfStack] = z;
+              break;
+            }
+          case (byte)OpCodes.Add: {
+              topOfStack--;
+              var a = stack[topOfStack + 1];
+              var b = stack[topOfStack];
+              stack[topOfStack] = Add(a, b);
+              ReleaseBuffer(a);
+              ReleaseBuffer(b);
+              break;
+            }
+          case (byte)OpCodes.Mul: {
+              topOfStack--;
+              var a = stack[topOfStack + 1];
+              var b = stack[topOfStack];
+              stack[topOfStack] = Mul(a, b);
+              ReleaseBuffer(a);
+              ReleaseBuffer(b);
+              break;
+            }
+          case (byte)OpCodes.Log: {
+              if (adjustOffsetForLogAndExp) {
+                // here we assume that the last used parameter is c in log(f(x) + c)
+                // this must match actions for producing code in the automaton!
+
+                // we can easily adjust c to make sure that f(x) + c is positive because at this point we all values for f(x)
+                var fxc = stack[topOfStack];
+                var minFx = fxc.Min() - consts[curParamIdx]; // stack[topOfStack] is f(x) + c
+
+                // adjust c so that minFx + c = e and therefore log(minFx + c) = log(e) = 1
+                // this initialization works in combination with the gradient check (instead of initializing such that log(minFx + c) = 0
+                var delta = Math.E - minFx - consts[curParamIdx];
+                consts[curParamIdx] += delta;
+
+                // also adjust values on stack
+                for (int i = 0; i < fxc.Length; i++) fxc[i] += delta;
+              }
+              var x = stack[topOfStack];
+              for (int i = 0; i < x.Length; i++)
+                x[i] = Math.Log(x[i]);
+              break;
+            }
+          case (byte)OpCodes.Exp: {
+              if (adjustOffsetForLogAndExp) {
+                // here we assume that the last used parameter is c in exp(f(x) * c)
+                // this must match actions for producing code in the automaton!
+
+                // adjust c to make sure that exp(f(x) * c) is not too large
+                var fxc = stack[topOfStack];
+                var maxFx = fxc.Max() / consts[curParamIdx]; // stack[topOfStack] is f(x) * c
+
+                var f = 1.0 / (maxFx * consts[curParamIdx]);
+                // adjust c so that maxFx*c = 1 TODO: this is not ideal as it enforces positive arguments to exp()
+                consts[curParamIdx] *= f;
+
+                // also adjust values on stack
+                for (int i = 0; i < fxc.Length; i++) fxc[i] *= f;
+              }
+
+              var x = stack[topOfStack];
+              for (int i = 0; i < x.Length; i++)
+                x[i] = Math.Exp(x[i]);
+              break;
+            }
+          case (byte)OpCodes.Inv: {
+              var x = stack[topOfStack];
+              for (int i = 0; i < x.Length; i++)
+                x[i] = 1.0 / (x[i]);
+              break;
+            }
+          case (byte)OpCodes.Exit:
+            Contract.Assert(topOfStack == 0);
+            var r = stack[topOfStack];
+            if (r.Length == 1) {
+              var v = double.IsNaN(r[0]) ? nanReplacementValue : Math.Min(upperEstimationLimit, Math.Max(lowerEstimationLimit, r[0]));
+              for (int i = 0; i < vLen; i++)
+                pred[i] = v;
+            } else {
+              for (int i = 0; i < vLen; i++) {
+                var v = double.IsNaN(r[i]) ? nanReplacementValue : Math.Min(upperEstimationLimit, Math.Max(lowerEstimationLimit, r[i]));
+                pred[i] = v;
+              }
+            }
+            ReleaseBuffer(r);
+            Contract.Assert(lastVecBufIdx == initialVectorCount);
+            Contract.Assert(lastScalarBufIdx == initialScalarCount);
+            return;
+        }
+      }
+    }
+
+
+    // evaluation with forward autodiff
+    // pred and gradients must be allocated by the caller
+    public void ExecGradient(byte[] code, double[][] vars, double[] consts, double[] pred, double[][] gradients) {
+      Contract.Assert(pred != null && pred.Length >= vLen);
+      int topOfStack = -1;
+      int pc = 0;
+      int curParamIdx = -1;
+      byte op;
+      short arg;
+      int nParams = consts.Length;
+      Contract.Assert(gradients != null && gradients.Length >= nParams && gradients.All(g => g.Length >= vLen));
+
+      // checked at the end to make sure we do not leak buffers
+      int initialScalarCount = lastScalarBufIdx;
+      int initialVectorCount = lastVecBufIdx;
+
+      while (true) {
+        ReadNext(code, ref pc, out op, out arg);
+        switch (op) {
+          case (byte)OpCodes.Nop: throw new InvalidProgramException(); // not allowed
+          case (byte)OpCodes.LoadConst0: {
+              ++topOfStack;
+              var z = GetScalarBuffer();
+              z[0] = 0;
+              stack[topOfStack] = z;
+              for (int k = 0; k < nParams; ++k) {
+                var b = GetScalarBuffer();
+                b[0] = 0.0;
+                gradientStack[k][topOfStack] = b;
+              }
+              break;
+            }
+          case (byte)OpCodes.LoadConst1: {
+              ++topOfStack;
+              var z = GetScalarBuffer();
+              z[0] = 1.0;
+              stack[topOfStack] = z;
+              for (int k = 0; k < nParams; ++k) {
+                var b = GetScalarBuffer();
+                b[0] = 0.0;
+                gradientStack[k][topOfStack] = b;
+              }
+              break;
+            }
+          case (byte)OpCodes.LoadParamN: {
+              var c = consts[++curParamIdx];
+              ++topOfStack;
+              var z = GetScalarBuffer();
+              z[0] = c;
+              stack[topOfStack] = z;
+              for (int k = 0; k < nParams; ++k) {
+                var b = GetScalarBuffer();
+                b[0] = k == curParamIdx ? 1.0 : 0.0;
+                gradientStack[k][topOfStack] = b;
+              }
+              break;
+            }
+          case (byte)OpCodes.LoadVar: {
+              ++topOfStack;
+              var z = GetVectorBuffer();
+              Array.Copy(vars[arg], z, vars[arg].Length);
+              stack[topOfStack] = z;
+              for (int k = 0; k < nParams; ++k) {
+                var b = GetScalarBuffer();
+                b[0] = 0.0;
+                gradientStack[k][topOfStack] = b;
+              }
+            }
+            break;
+          case (byte)OpCodes.Add: {
+              topOfStack--;
+              var a = stack[topOfStack + 1];
+              var b = stack[topOfStack];
+              stack[topOfStack] = Add(a, b);
+              ReleaseBuffer(a);
+              ReleaseBuffer(b);
+
+              // same for gradient
+              for (int k = 0; k < nParams; ++k) {
+                var ag = gradientStack[k][topOfStack + 1];
+                var bg = gradientStack[k][topOfStack];
+                gradientStack[k][topOfStack] = Add(ag, bg);
+                ReleaseBuffer(ag);
+                ReleaseBuffer(bg);
+              }
+              break;
+            }
+          case (byte)OpCodes.Mul: {
+              topOfStack--;
+              var a = stack[topOfStack + 1];
+              var b = stack[topOfStack];
+              stack[topOfStack] = Mul(a, b);
+
+              // same for gradient
+              // f(x) g(x)	f '(x) g(x) + f(x) g'(x)
+              for (int k = 0; k < nParams; ++k) {
+                var ag = gradientStack[k][topOfStack + 1];
+                var bg = gradientStack[k][topOfStack];
+                var t1 = Mul(ag, b);
+                var t2 = Mul(a, bg);
+                gradientStack[k][topOfStack] = Add(t1, t2);
+                ReleaseBuffer(ag);
+                ReleaseBuffer(bg);
+                ReleaseBuffer(t1);
+                ReleaseBuffer(t2);
+              }
+
+              ReleaseBuffer(a);
+              ReleaseBuffer(b);
+
+              break;
+            }
+          case (byte)OpCodes.Log: {
+              var x = stack[topOfStack];
+              // calc gradients first before destroying x
+              // log(f(x))' = f(x)'/f(x)
+              for (int k = 0; k < nParams; k++) {
+                var xg = gradientStack[k][topOfStack];
+                gradientStack[k][topOfStack] = Frac(xg, x);
+                ReleaseBuffer(xg);
+              }
+
+              for (int i = 0; i < x.Length; i++)
+                x[i] = Math.Log(x[i]);
+
+              break;
+            }
+          case (byte)OpCodes.Exp: {
+              var x = stack[topOfStack];
+              for (int i = 0; i < x.Length; i++)
+                x[i] = Math.Exp(x[i]);
+
+              for (int k = 0; k < nParams; k++) {
+                var xg = gradientStack[k][topOfStack];
+                gradientStack[k][topOfStack] = Mul(x, xg);  // e(f(x))' = e(f(x)) * f(x)'
+                ReleaseBuffer(xg);
+              }
+              break;
+            }
+          case (byte)OpCodes.Inv: {
+              var x = stack[topOfStack];
+              for (int i = 0; i < x.Length; i++)
+                x[i] = 1.0 / x[i];
+
+              for (int k = 0; k < nParams; k++) {
+                var xg = gradientStack[k][topOfStack];
+                // x has already been inverted above
+                // (1/f)' = -f' / f²
+                var invF = Mul(xg, x);
+                gradientStack[k][topOfStack] = Mul(invF, x, factor: -1.0);
+                ReleaseBuffer(xg);
+                ReleaseBuffer(invF);
+              }
+              break;
+            }
+          case (byte)OpCodes.Exit:
+            Contract.Assert(topOfStack == 0);
+            var r = stack[topOfStack];
+            if (r.Length == 1) {
+              var v = double.IsNaN(r[0]) ? nanReplacementValue : Math.Min(upperEstimationLimit, Math.Max(lowerEstimationLimit, r[0]));
+              for (int i = 0; i < vLen; i++)
+                pred[i] = v;
+            } else {
+              for (int i = 0; i < vLen; i++) {
+                var v = double.IsNaN(r[i]) ? nanReplacementValue : Math.Min(upperEstimationLimit, Math.Max(lowerEstimationLimit, r[i]));
+                pred[i] = v;
+              }
+            }
+            ReleaseBuffer(r);
+
+            // same for gradients
+            for (int k = 0; k < nParams; k++) {
+              var g = gradientStack[k][topOfStack];
+              if (g.Length == 1) {
+                for (int i = 0; i < vLen; i++)
+                  gradients[k][i] = g[0];
+              } else
+                Array.Copy(g, gradients[k], vLen);
+              ReleaseBuffer(g);
+            }
+
+            Contract.Assert(lastVecBufIdx == initialVectorCount);
+            Contract.Assert(lastScalarBufIdx == initialScalarCount);
+            return; // break loop
+        }
+      }
+    }
+
+    private double[] Add(double[] a, double[] b) {
+      double[] target = null;
+      if (a.Length > 1) {
+        target = GetVectorBuffer();
+        if (b.Length > 1) {
+          for (int i = 0; i < vLen; i++)
+            target[i] = a[i] + b[i];
+        } else {
+          // b == scalar
+          for (int i = 0; i < vLen; i++)
+            target[i] = a[i] + b[0];
+        }
+      } else {
+        // a == scalar
+        if (b.Length > 1) {
+          target = GetVectorBuffer();
+          for (int i = 0; i < vLen; i++)
+            target[i] = a[0] + b[i];
+        } else {
+          // b == scalar
+          target = GetScalarBuffer();
+          target[0] = a[0] + b[0];
+        }
+      }
+      return target;
+    }
+
+    private double[] Mul(double[] a, double[] b, double factor = 1.0) {
+      double[] target = null;
+      if (a.Length > 1) {
+        if (b.Length > 1) {
+          target = GetVectorBuffer();
+          for (int i = 0; i < vLen; i++)
+            target[i] = factor * a[i] * b[i];
+        } else {
+          // b == scalar
+          if (Math.Abs(b[0]) < 1E-12 /* == 0 */) {
+            target = GetScalarBuffer();
+            target[0] = 0.0;
+          } else {
+            target = GetVectorBuffer();
+            for (int i = 0; i < vLen; i++)
+              target[i] = factor * a[i] * b[0];
+          }
+        }
+      } else {
+        // a == scalar
+        if (b.Length > 1) {
+          if (Math.Abs(a[0]) < 1E-12 /* == 0 */) {
+            target = GetScalarBuffer();
+            target[0] = 0.0;
+          } else {
+            target = GetVectorBuffer();
+            for (int i = 0; i < vLen; i++)
+              target[i] = factor * a[0] * b[i];
+          }
+        } else {
+          // b == scalar
+          target = GetScalarBuffer();
+          target[0] = factor * a[0] * b[0];
+        }
+      }
+      return target;
+    }
+
+    private double[] Frac(double[] a, double[] b) {
+      double[] target = null;
+      if (a.Length > 1) {
+        target = GetVectorBuffer();
+        if (b.Length > 1) {
+          for (int i = 0; i < vLen; i++)
+            target[i] = a[i] / b[i];
+        } else {
+          // b == scalar
+          for (int i = 0; i < vLen; i++)
+            target[i] = a[i] / b[0];
+        }
+      } else {
+        // a == scalar
+        if (b.Length > 1) {
+          if (Math.Abs(a[0]) < 1E-12 /* == 0 */) {
+            target = GetScalarBuffer();
+            target[0] = 0.0;
+          } else {
+            target = GetVectorBuffer();
+            for (int i = 0; i < vLen; i++)
+              target[i] = a[0] / b[i];
+          }
+        } else {
+          // b == scalar
+          target = GetScalarBuffer();
+          target[0] = a[0] / b[0];
+        }
+      }
+      return target;
+    }
+
+    private void ReadNext(byte[] code, ref int pc, out byte op, out short s) {
+      op = code[pc++];
+      s = 0;
+      if (op == (byte)OpCodes.LoadVar) {
+        s = (short)((code[pc] << 8) | code[pc + 1]);
+        pc += 2;
+      }
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionAlgorithm.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionAlgorithm.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionAlgorithm.cs	(revision 15650)
@@ -0,0 +1,353 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Analysis;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  // TODO: support pause (persisting/cloning the state)
+  [Item("Symbolic Regression Tree Search", "tree search for symbolic regression.")]
+  [StorableClass]
+  [Creatable(CreatableAttribute.Categories.DataAnalysisRegression, Priority = 250)]
+  public class MctsSymbolicRegressionAlgorithm : FixedDataAnalysisAlgorithm<IRegressionProblem> {
+
+    #region ParameterNames
+    private const string IterationsParameterName = "Iterations";
+    private const string MaxVariablesParameterName = "Maximum variables";
+    private const string ScaleVariablesParameterName = "Scale variables";
+    private const string AllowedFactorsParameterName = "Allowed factors";
+    private const string ConstantOptimizationIterationsParameterName = "Iterations (constant optimization)";
+    private const string PolicyParameterName = "Policy";
+    private const string SeedParameterName = "Seed";
+    private const string SetSeedRandomlyParameterName = "SetSeedRandomly";
+    private const string UpdateIntervalParameterName = "UpdateInterval";
+    private const string CreateSolutionParameterName = "CreateSolution";
+    private const string PunishmentFactorParameterName = "PunishmentFactor";
+    private const string CollectParetoOptimalSolutionsParameterName = "CollectParetoOptimalSolutions";
+    private const string LambdaParameterName = "Lambda";
+
+    private const string VariableProductFactorName = "x * y * ...";
+    private const string ExpFactorName = "exp(c * x * y ...)";
+    private const string LogFactorName = "log(c + c1 x + c2 x + ...)";
+    private const string InvFactorName = "1 / (1 + c1 x + c2 x + ...)";
+    private const string FactorSumsName = "t1(x) + t2(x) + ... ";
+    #endregion
+
+    #region ParameterProperties
+    public IFixedValueParameter<IntValue> IterationsParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[IterationsParameterName]; }
+    }
+    public IFixedValueParameter<IntValue> MaxVariableReferencesParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[MaxVariablesParameterName]; }
+    }
+    public IFixedValueParameter<BoolValue> ScaleVariablesParameter {
+      get { return (IFixedValueParameter<BoolValue>)Parameters[ScaleVariablesParameterName]; }
+    }
+    public IFixedValueParameter<IntValue> ConstantOptimizationIterationsParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[ConstantOptimizationIterationsParameterName]; }
+    }
+    public IFixedValueParameter<DoubleValue> PunishmentFactorParameter {
+      get { return (IFixedValueParameter<DoubleValue>)Parameters[PunishmentFactorParameterName]; }
+    }
+    public IValueParameter<ICheckedItemList<StringValue>> AllowedFactorsParameter {
+      get { return (IValueParameter<ICheckedItemList<StringValue>>)Parameters[AllowedFactorsParameterName]; }
+    }
+    public IFixedValueParameter<IntValue> SeedParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[SeedParameterName]; }
+    }
+    public FixedValueParameter<BoolValue> SetSeedRandomlyParameter {
+      get { return (FixedValueParameter<BoolValue>)Parameters[SetSeedRandomlyParameterName]; }
+    }
+    public IFixedValueParameter<IntValue> UpdateIntervalParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[UpdateIntervalParameterName]; }
+    }
+    public IFixedValueParameter<BoolValue> CreateSolutionParameter {
+      get { return (IFixedValueParameter<BoolValue>)Parameters[CreateSolutionParameterName]; }
+    }
+    public IFixedValueParameter<BoolValue> CollectParetoOptimalSolutionsParameter {
+      get { return (IFixedValueParameter<BoolValue>)Parameters[CollectParetoOptimalSolutionsParameterName]; }
+    }
+    public IFixedValueParameter<DoubleValue> LambdaParameter {
+      get { return (IFixedValueParameter<DoubleValue>)Parameters[LambdaParameterName]; }
+    }
+    #endregion
+
+    #region Properties
+    public int Iterations {
+      get { return IterationsParameter.Value.Value; }
+      set { IterationsParameter.Value.Value = value; }
+    }
+    public int Seed {
+      get { return SeedParameter.Value.Value; }
+      set { SeedParameter.Value.Value = value; }
+    }
+    public bool SetSeedRandomly {
+      get { return SetSeedRandomlyParameter.Value.Value; }
+      set { SetSeedRandomlyParameter.Value.Value = value; }
+    }
+    public int MaxVariableReferences {
+      get { return MaxVariableReferencesParameter.Value.Value; }
+      set { MaxVariableReferencesParameter.Value.Value = value; }
+    }
+    public double PunishmentFactor {
+      get { return PunishmentFactorParameter.Value.Value; }
+      set { PunishmentFactorParameter.Value.Value = value; }
+    }
+    public ICheckedItemList<StringValue> AllowedFactors {
+      get { return AllowedFactorsParameter.Value; }
+    }
+    public int ConstantOptimizationIterations {
+      get { return ConstantOptimizationIterationsParameter.Value.Value; }
+      set { ConstantOptimizationIterationsParameter.Value.Value = value; }
+    }
+    public bool ScaleVariables {
+      get { return ScaleVariablesParameter.Value.Value; }
+      set { ScaleVariablesParameter.Value.Value = value; }
+    }
+    public bool CreateSolution {
+      get { return CreateSolutionParameter.Value.Value; }
+      set { CreateSolutionParameter.Value.Value = value; }
+    }
+    public bool CollectParetoOptimalSolutions {
+      get { return CollectParetoOptimalSolutionsParameter.Value.Value; }
+      set { CollectParetoOptimalSolutionsParameter.Value.Value = value; }
+    }
+    public double Lambda {
+      get { return LambdaParameter.Value.Value; }
+      set { LambdaParameter.Value.Value = value; }
+    }
+    #endregion
+
+    [StorableConstructor]
+    protected MctsSymbolicRegressionAlgorithm(bool deserializing) : base(deserializing) { }
+
+    protected MctsSymbolicRegressionAlgorithm(MctsSymbolicRegressionAlgorithm original, Cloner cloner)
+      : base(original, cloner) {
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MctsSymbolicRegressionAlgorithm(this, cloner);
+    }
+
+    public MctsSymbolicRegressionAlgorithm() {
+      Problem = new RegressionProblem(); // default problem
+
+      var defaultFactorsList = new CheckedItemList<StringValue>(
+        new string[] { VariableProductFactorName, ExpFactorName, LogFactorName, InvFactorName, FactorSumsName }
+        .Select(s => new StringValue(s).AsReadOnly())
+        ).AsReadOnly();
+      defaultFactorsList.SetItemCheckedState(defaultFactorsList.First(s => s.Value == FactorSumsName), false);
+
+      Parameters.Add(new FixedValueParameter<IntValue>(IterationsParameterName,
+        "Number of iterations", new IntValue(100000)));
+      Parameters.Add(new FixedValueParameter<IntValue>(SeedParameterName,
+        "The random seed used to initialize the new pseudo random number generator.", new IntValue(0)));
+      Parameters.Add(new FixedValueParameter<BoolValue>(SetSeedRandomlyParameterName,
+        "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true)));
+      Parameters.Add(new FixedValueParameter<IntValue>(MaxVariablesParameterName,
+        "Maximal number of variables references in the symbolic regression models (multiple usages of the same variable are counted)", new IntValue(5)));
+      Parameters.Add(new ValueParameter<ICheckedItemList<StringValue>>(AllowedFactorsParameterName,
+        "Choose which expressions are allowed as factors in the model.", defaultFactorsList));
+
+      Parameters.Add(new FixedValueParameter<IntValue>(ConstantOptimizationIterationsParameterName,
+        "Number of iterations for constant optimization. A small number of iterations should be sufficient for most models. " +
+        "Set to 0 to let the algorithm stop automatically when it converges. Set to -1 to disable constants optimization.", new IntValue(10)));
+      Parameters.Add(new FixedValueParameter<BoolValue>(ScaleVariablesParameterName,
+        "Set to true to all input variables to the range [0..1]", new BoolValue(true)));
+      Parameters[ScaleVariablesParameterName].Hidden = true;
+      Parameters.Add(new FixedValueParameter<DoubleValue>(PunishmentFactorParameterName, "Estimations of models can be bounded. The estimation limits are calculated in the following way (lb = mean(y) - punishmentFactor*range(y), ub = mean(y) + punishmentFactor*range(y))", new DoubleValue(10)));
+      Parameters[PunishmentFactorParameterName].Hidden = true;
+      Parameters.Add(new FixedValueParameter<IntValue>(UpdateIntervalParameterName,
+        "Number of iterations until the results are updated", new IntValue(100)));
+      Parameters[UpdateIntervalParameterName].Hidden = true;
+      Parameters.Add(new FixedValueParameter<BoolValue>(CreateSolutionParameterName,
+        "Optionally produce a solution at the end of the run", new BoolValue(true)));
+      Parameters[CreateSolutionParameterName].Hidden = true;
+
+      Parameters.Add(new FixedValueParameter<BoolValue>(CollectParetoOptimalSolutionsParameterName,
+        "Optionally collect a set of Pareto-optimal solutions minimizing error and complexity.", new BoolValue(false)));
+      Parameters[CollectParetoOptimalSolutionsParameterName].Hidden = true;
+
+      Parameters.Add(new FixedValueParameter<DoubleValue>(LambdaParameterName,
+        "Lambda is the factor for the regularization term in the objective function (Obj = (y - f(x,p))² + lambda * |p|²)", new DoubleValue(0.0)));
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+    }
+
+    // TODO: support pause and restart
+    protected override void Run(CancellationToken cancellationToken) {
+      // Set up the algorithm
+      if (SetSeedRandomly) Seed = new System.Random().Next();
+      var collectPareto = CollectParetoOptimalSolutions;
+
+      // Set up the results display
+      var iterations = new IntValue(0);
+      Results.Add(new Result("Iterations", iterations));
+
+      var bestSolutionIteration = new IntValue(0);
+      Results.Add(new Result("Best solution iteration", bestSolutionIteration));
+
+      var table = new DataTable("Qualities");
+      table.Rows.Add(new DataRow("Best quality"));
+      table.Rows.Add(new DataRow("Current best quality"));
+      table.Rows.Add(new DataRow("Average quality"));
+      Results.Add(new Result("Qualities", table));
+
+      var bestQuality = new DoubleValue();
+      Results.Add(new Result("Best quality", bestQuality));
+
+      var curQuality = new DoubleValue();
+      Results.Add(new Result("Current best quality", curQuality));
+
+      var avgQuality = new DoubleValue();
+      Results.Add(new Result("Average quality", avgQuality));
+
+      var totalRollouts = new IntValue();
+      Results.Add(new Result("Total rollouts", totalRollouts));
+      var effRollouts = new IntValue();
+      Results.Add(new Result("Effective rollouts", effRollouts));
+      var funcEvals = new IntValue();
+      Results.Add(new Result("Function evaluations", funcEvals));
+      var gradEvals = new IntValue();
+      Results.Add(new Result("Gradient evaluations", gradEvals));
+
+      Result paretoBestModelsResult = new Result("ParetoBestModels", typeof(ItemList<ISymbolicRegressionSolution>));
+      if (collectPareto) {
+        Results.Add(paretoBestModelsResult);
+      }
+
+      // same as in SymbolicRegressionSingleObjectiveProblem
+      var y = Problem.ProblemData.Dataset.GetDoubleValues(Problem.ProblemData.TargetVariable,
+        Problem.ProblemData.TrainingIndices);
+      var avgY = y.Average();
+      var minY = y.Min();
+      var maxY = y.Max();
+      var range = maxY - minY;
+      var lowerLimit = avgY - PunishmentFactor * range;
+      var upperLimit = avgY + PunishmentFactor * range;
+
+      // init
+      var problemData = (IRegressionProblemData)Problem.ProblemData.Clone();
+      if (!AllowedFactors.CheckedItems.Any()) throw new ArgumentException("At least on type of factor must be allowed");
+      var state = MctsSymbolicRegressionStatic.CreateState(problemData, (uint)Seed, MaxVariableReferences, ScaleVariables,
+        ConstantOptimizationIterations, Lambda,
+        collectPareto,
+        lowerLimit, upperLimit,
+        allowProdOfVars: AllowedFactors.CheckedItems.Any(s => s.Value.Value == VariableProductFactorName),
+        allowExp: AllowedFactors.CheckedItems.Any(s => s.Value.Value == ExpFactorName),
+        allowLog: AllowedFactors.CheckedItems.Any(s => s.Value.Value == LogFactorName),
+        allowInv: AllowedFactors.CheckedItems.Any(s => s.Value.Value == InvFactorName),
+        allowMultipleTerms: AllowedFactors.CheckedItems.Any(s => s.Value.Value == FactorSumsName)
+        );
+
+      var updateInterval = UpdateIntervalParameter.Value.Value;
+      double sumQ = 0.0;
+      double bestQ = 0.0;
+      double curBestQ = 0.0;
+      int n = 0;
+
+      // canceled before we actually started
+      cancellationToken.ThrowIfCancellationRequested();
+
+      // Loop until iteration limit reached or canceled.
+      for (int i = 0; i < Iterations && !state.Done && !cancellationToken.IsCancellationRequested; i++) {
+        var q = MctsSymbolicRegressionStatic.MakeStep(state);
+        sumQ += q; // sum of qs in the last updateinterval iterations
+        curBestQ = Math.Max(q, curBestQ); // the best q in the last updateinterval iterations
+        bestQ = Math.Max(q, bestQ); // the best q overall
+        n++;
+        // iteration results
+        if (n == updateInterval) {
+          if (bestQ > bestQuality.Value) {
+            bestSolutionIteration.Value = i;
+            if (state.BestSolutionTrainingQuality > 0.99999) break;
+          }
+          bestQuality.Value = bestQ;
+          curQuality.Value = curBestQ;
+          avgQuality.Value = sumQ / n;
+          sumQ = 0.0;
+          curBestQ = 0.0;
+
+          funcEvals.Value = state.FuncEvaluations;
+          gradEvals.Value = state.GradEvaluations;
+          effRollouts.Value = state.EffectiveRollouts;
+          totalRollouts.Value = state.TotalRollouts;
+
+          if (collectPareto) {
+            paretoBestModelsResult.Value = new ItemList<ISymbolicRegressionSolution>(state.ParetoBestModels);
+          }
+
+          table.Rows["Best quality"].Values.Add(bestQuality.Value);
+          table.Rows["Current best quality"].Values.Add(curQuality.Value);
+          table.Rows["Average quality"].Values.Add(avgQuality.Value);
+          iterations.Value += n;
+          n = 0;
+        }
+      }
+
+      // final results (assumes that at least one iteration was calculated)
+      if (n > 0) {
+        if (bestQ > bestQuality.Value) {
+          bestSolutionIteration.Value = iterations.Value + n;
+        }
+        bestQuality.Value = bestQ;
+        curQuality.Value = curBestQ;
+        avgQuality.Value = sumQ / n;
+
+        funcEvals.Value = state.FuncEvaluations;
+        gradEvals.Value = state.GradEvaluations;
+        effRollouts.Value = state.EffectiveRollouts;
+        totalRollouts.Value = state.TotalRollouts;
+
+        table.Rows["Best quality"].Values.Add(bestQuality.Value);
+        table.Rows["Current best quality"].Values.Add(curQuality.Value);
+        table.Rows["Average quality"].Values.Add(avgQuality.Value);
+        iterations.Value = iterations.Value + n;
+
+      }
+
+
+      Results.Add(new Result("Best solution quality (train)", new DoubleValue(state.BestSolutionTrainingQuality)));
+      Results.Add(new Result("Best solution quality (test)", new DoubleValue(state.BestSolutionTestQuality)));
+
+
+      // produce solution 
+      if (CreateSolution) {
+        var model = state.BestModel;
+
+        // otherwise we produce a regression solution
+        Results.Add(new Result("Solution", model.CreateRegressionSolution(problemData)));
+      }
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionStatic.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionStatic.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/MctsSymbolicRegressionStatic.cs	(revision 15650)
@@ -0,0 +1,896 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using HeuristicLab.Core;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.DataAnalysis.Symbolic;
+using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
+using HeuristicLab.Random;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  public static class MctsSymbolicRegressionStatic {
+    // OBJECTIVES:
+    // 1) solve toy problems without numeric constants (to show that structure search is effective / efficient)
+    //    - e.g. Keijzer, Nguyen ... where no numeric constants are involved
+    //    - assumptions: 
+    //      - we don't know the necessary operations or functions -> all available functions could be necessary
+    //      - but we do not need to tune numeric constants -> no scaling of input variables x!
+    // 2) Solve toy problems with numeric constants to make the algorithm invariant concerning variable scale.
+    //    This is important for real world applications.
+    //    - e.g. Korns or Vladislavleva problems where numeric constants are involved
+    //    - assumptions:
+    //      - any numeric constant is possible (a-priori we might assume that small abs. constants are more likely)
+    //      - standardization of variables is possible (or might be necessary) as we adjust numeric parameters of the expression anyway
+    //      - to simplify the problem we can restrict the set of functions e.g. we assume which functions are necessary for the problem instance
+    //        -> several steps: (a) polynomials, (b) rational polynomials, (c) exponential or logarithmic functions, rational functions with exponential and logarithmic parts
+    // 3) efficiency and effectiveness for real-world problems
+    //    - e.g. Tower problem
+    //    - (1) and (2) combined, structure search must be effective in combination with numeric optimization of constants
+    //    
+
+    // TODO: The samples of x1*... or x2*... do not give any information about the relevance of the interaction term x1*x2 in general!
+    //       --> E.g. if x1, x2 ~ N(0, 1) or U(-1, 1) this is trivial to show
+    //       --> Therefore, looking at roll-out statistics for arm selection (MCTS-style) is useless in the general case!
+    //       --> It is necessary to rely on other features for the arm selection. 
+    //       --> TODO: Which heuristics can we apply? 
+    // TODO: Solve Poly-10
+    // TODO: rename everything as this is not MCTS anymore
+    // TODO: when a path to an expression is explored first (e.g. x1 + x2)
+    //       and later we find the a longer form x1 + x1 + x2 where the number of variable references
+    //       exceeds the maximum in the automaton this leads to an error (see unit tests) 
+    // TODO: unit tests for benchmark problems which contain log / exp / x^-1 but without numeric constants 
+    // TODO: check if transformation of y is correct and works (Obj 2)
+    // TODO: The algorithm is not invariant to location and scale of variables. 
+    //       Include offset for variables as parameter (for Objective 2)
+    // TODO: why does LM optimization converge so slowly with exp(x), log(x), and 1/x allowed (Obj 2)?
+    // TODO: support e(-x) and possibly (1/-x) (Obj 1)
+    // TODO: is it OK to initialize all constants to 1 (Obj 2)?
+    // TODO: improve memory usage
+    // TODO: analyze / improve perf of ExprHashing (canonical form for expressions)
+    // TODO: support empty test partition
+    // TODO: the algorithm should be invariant to linear transformations of the space (y = f(x') = f( Ax ) ) for invertible transformations A --> see unit tests
+    #region static API
+
+    public interface IState {
+      bool Done { get; }
+      ISymbolicRegressionModel BestModel { get; }
+      double BestSolutionTrainingQuality { get; }
+      double BestSolutionTestQuality { get; }
+      IEnumerable<ISymbolicRegressionSolution> ParetoBestModels { get; }
+      int TotalRollouts { get; }
+      int EffectiveRollouts { get; }
+      int FuncEvaluations { get; }
+      int GradEvaluations { get; } // number of gradient evaluations (* num parameters) to get a value representative of the effort comparable to the number of function evaluations
+      // TODO other stats on LM optimizer might be interesting here
+    }
+
+    // created through factory method
+    private class State : IState {
+      private const int MaxParams = 100;
+
+      // state variables used by MCTS
+      internal readonly Automaton automaton;
+      internal IRandom random { get; private set; }
+      internal readonly Tree tree;
+      internal readonly Func<byte[], int, double> evalFun;
+      // MCTS might get stuck. Track statistics on the number of effective roll-outs
+      internal int totalRollouts;
+      internal int effectiveRollouts;
+
+
+      // state variables used only internally (for eval function)
+      private readonly IRegressionProblemData problemData;
+      private readonly double[][] x;
+      private readonly double[] y;
+      private readonly double[][] testX;
+      private readonly double[] testY;
+      private readonly double[] scalingFactor;
+      private readonly double[] scalingOffset;
+      private readonly double yStdDev; // for scaling parameters (e.g. stopping condition for LM)
+      private readonly int constOptIterations;
+      private readonly double lambda; // weight of penalty term for regularization
+      private readonly double lowerEstimationLimit, upperEstimationLimit;
+      private readonly bool collectParetoOptimalModels;
+      private readonly List<ISymbolicRegressionSolution> paretoBestModels = new List<ISymbolicRegressionSolution>();
+      private readonly List<double[]> paretoFront = new List<double[]>(); // matching the models
+
+      private readonly ExpressionEvaluator evaluator, testEvaluator;
+
+      internal readonly Dictionary<Tree, List<Tree>> children = new Dictionary<Tree, List<Tree>>();
+      internal readonly Dictionary<Tree, List<Tree>> parents = new Dictionary<Tree, List<Tree>>();
+      internal readonly Dictionary<ulong, Tree> nodes = new Dictionary<ulong, Tree>();
+
+      // values for best solution
+      private double bestR;
+      private byte[] bestCode;
+      private int bestNParams;
+      private double[] bestConsts;
+
+      // stats
+      private int funcEvaluations;
+      private int gradEvaluations;
+
+      // buffers
+      private readonly double[] ones; // vector of ones (as default params)
+      private readonly double[] constsBuf;
+      private readonly double[] predBuf, testPredBuf;
+      private readonly double[][] gradBuf;
+
+      public State(IRegressionProblemData problemData, uint randSeed, int maxVariables, bool scaleVariables,
+        int constOptIterations, double lambda,
+        bool collectParetoOptimalModels = false,
+        double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue,
+        bool allowProdOfVars = true,
+        bool allowExp = true,
+        bool allowLog = true,
+        bool allowInv = true,
+        bool allowMultipleTerms = false) {
+
+        if (lambda < 0) throw new ArgumentException("Lambda must be larger or equal zero", "lambda");
+
+        this.problemData = problemData;
+        this.constOptIterations = constOptIterations;
+        this.lambda = lambda;
+        this.evalFun = this.Eval;
+        this.lowerEstimationLimit = lowerEstimationLimit;
+        this.upperEstimationLimit = upperEstimationLimit;
+        this.collectParetoOptimalModels = collectParetoOptimalModels;
+
+        random = new MersenneTwister(randSeed);
+
+        // prepare data for evaluation
+        double[][] x;
+        double[] y;
+        double[][] testX;
+        double[] testY;
+        double[] scalingFactor;
+        double[] scalingOffset;
+        // get training and test datasets (scale linearly based on training set if required)
+        GenerateData(problemData, scaleVariables, problemData.TrainingIndices, out x, out y, out scalingFactor, out scalingOffset);
+        GenerateData(problemData, problemData.TestIndices, scalingFactor, scalingOffset, out testX, out testY);
+        this.x = x;
+        this.y = y;
+        this.yStdDev = HeuristicLab.Common.EnumerableStatisticExtensions.StandardDeviation(y);
+        this.testX = testX;
+        this.testY = testY;
+        this.scalingFactor = scalingFactor;
+        this.scalingOffset = scalingOffset;
+        this.evaluator = new ExpressionEvaluator(y.Length, lowerEstimationLimit, upperEstimationLimit);
+        // we need a separate evaluator because the vector length for the test dataset might differ 
+        this.testEvaluator = new ExpressionEvaluator(testY.Length, lowerEstimationLimit, upperEstimationLimit);
+
+        this.automaton = new Automaton(x, allowProdOfVars, allowExp, allowLog, allowInv, allowMultipleTerms, maxVariables);
+        this.tree = new Tree() {
+          state = automaton.CurrentState,
+          expr = "",
+          level = 0
+        };
+
+        // reset best solution
+        this.bestR = 0;
+        // code for default solution (constant model)
+        this.bestCode = new byte[] { (byte)OpCodes.LoadConst0, (byte)OpCodes.Exit };
+        this.bestNParams = 0;
+        this.bestConsts = null;
+
+        // init buffers
+        this.ones = Enumerable.Repeat(1.0, MaxParams).ToArray();
+        constsBuf = new double[MaxParams];
+        this.predBuf = new double[y.Length];
+        this.testPredBuf = new double[testY.Length];
+
+        this.gradBuf = Enumerable.Range(0, MaxParams).Select(_ => new double[y.Length]).ToArray();
+      }
+
+      #region IState inferface
+      public bool Done { get { return tree != null && tree.Done; } }
+
+      public double BestSolutionTrainingQuality {
+        get {
+          evaluator.Exec(bestCode, x, bestConsts, predBuf);
+          return Rho(y, predBuf);
+        }
+      }
+
+      public double BestSolutionTestQuality {
+        get {
+          testEvaluator.Exec(bestCode, testX, bestConsts, testPredBuf);
+          return Rho(testY, testPredBuf);
+        }
+      }
+
+      // takes the code of the best solution and creates and equivalent symbolic regression models
+      public ISymbolicRegressionModel BestModel {
+        get {
+          var treeGen = new SymbolicExpressionTreeGenerator(problemData.AllowedInputVariables.ToArray());
+          var interpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
+
+          var t = new SymbolicExpressionTree(treeGen.Exec(bestCode, bestConsts, bestNParams, scalingFactor, scalingOffset));
+          var model = new SymbolicRegressionModel(problemData.TargetVariable, t, interpreter, lowerEstimationLimit, upperEstimationLimit);
+          model.Scale(problemData); // apply linear scaling
+          return model;
+        }
+      }
+      public IEnumerable<ISymbolicRegressionSolution> ParetoBestModels {
+        get { return paretoBestModels; }
+      }
+
+      public int TotalRollouts { get { return totalRollouts; } }
+      public int EffectiveRollouts { get { return effectiveRollouts; } }
+      public int FuncEvaluations { get { return funcEvaluations; } }
+      public int GradEvaluations { get { return gradEvaluations; } } // number of gradient evaluations (* num parameters) to get a value representative of the effort comparable to the number of function evaluations
+
+      #endregion
+
+
+#if DEBUG
+      public string ExprStr(Automaton automaton) {
+        byte[] code;
+        int nParams;
+        automaton.GetCode(out code, out nParams);
+        var generator = new SymbolicExpressionTreeGenerator(problemData.AllowedInputVariables.ToArray());
+        var @params = Enumerable.Repeat(1.0, nParams).ToArray();
+        var root = generator.Exec(code, @params, nParams, null, null);
+        var formatter = new InfixExpressionFormatter();
+        return formatter.Format(new SymbolicExpressionTree(root));
+      }
+#endif
+
+      private double Eval(byte[] code, int nParams) {
+        double[] optConsts;
+        double q;
+        Eval(code, nParams, out q, out optConsts);
+
+        // single objective best
+        if (q > bestR) {
+          bestR = q;
+          bestNParams = nParams;
+          this.bestCode = new byte[code.Length];
+          this.bestConsts = new double[bestNParams];
+
+          Array.Copy(code, bestCode, code.Length);
+          Array.Copy(optConsts, bestConsts, bestNParams);
+        }
+        if (collectParetoOptimalModels) {
+          // multi-objective best
+          var complexity = // SymbolicDataAnalysisModelComplexityCalculator.CalculateComplexity() TODO: implement Kommenda's tree complexity directly in the evaluator
+            Array.FindIndex(code, (opc) => opc == (byte)OpCodes.Exit);  // use length of expression as surrogate for complexity
+          UpdateParetoFront(q, complexity, code, optConsts, nParams, scalingFactor, scalingOffset);
+        }
+        return q;
+      }
+
+      private void Eval(byte[] code, int nParams, out double rho, out double[] optConsts) {
+        // we make a first pass to determine a valid starting configuration for all constants
+        // constant c in log(c + f(x)) is adjusted to guarantee that x is positive (see expression evaluator)
+        // scale and offset are set to optimal starting configuration
+        // assumes scale is the first param and offset is the last param
+
+        // reset constants
+        Array.Copy(ones, constsBuf, nParams);
+        evaluator.Exec(code, x, constsBuf, predBuf, adjustOffsetForLogAndExp: true);
+        funcEvaluations++;
+
+        if (nParams == 0 || constOptIterations < 0) {
+          // if we don't need to optimize parameters then we are done
+          // changing scale and offset does not influence r²
+          rho = Rho(y, predBuf);
+          optConsts = constsBuf;
+        } else {
+          // optimize constants using the starting point calculated above
+          OptimizeConstsLm(code, constsBuf, nParams, 0.0, nIters: constOptIterations);
+
+          evaluator.Exec(code, x, constsBuf, predBuf);
+          funcEvaluations++;
+
+          rho = Rho(y, predBuf);
+          optConsts = constsBuf;
+        }
+      }
+
+
+
+      #region helpers
+      private static double Rho(IEnumerable<double> x, IEnumerable<double> y) {
+        OnlineCalculatorError error;
+        double r = OnlinePearsonsRCalculator.Calculate(x, y, out error);
+        return error == OnlineCalculatorError.None ? r : 0.0;
+      }
+
+
+      private void OptimizeConstsLm(byte[] code, double[] consts, int nParams, double epsF = 0.0, int nIters = 100) {
+        double[] optConsts = new double[nParams]; // allocate a smaller buffer for constants opt (TODO perf?)
+        Array.Copy(consts, optConsts, nParams);
+
+        // direct usage of LM is recommended in alglib manual for better performance than the lsfit interface (which uses lm internally).
+        alglib.minlmstate state;
+        alglib.minlmreport rep = null;
+        alglib.minlmcreatevj(y.Length + 1, optConsts, out state); // +1 for penalty term
+        // Using the change of the gradient as stopping criterion is recommended in alglib manual.
+        // However, the most recent version of alglib (as of Oct 2017) only supports epsX as stopping criterion
+        alglib.minlmsetcond(state, epsg: 1E-6 * yStdDev, epsf: epsF, epsx: 0.0, maxits: nIters);
+        // alglib.minlmsetgradientcheck(state, 1E-5);
+        alglib.minlmoptimize(state, Func, FuncAndJacobian, null, code);
+        alglib.minlmresults(state, out optConsts, out rep);
+        funcEvaluations += rep.nfunc;
+        gradEvaluations += rep.njac * nParams;
+
+        if (rep.terminationtype < 0) throw new ArgumentException("lm failed: termination type = " + rep.terminationtype);
+
+        // only use optimized constants if successful
+        if (rep.terminationtype >= 0) {
+          Array.Copy(optConsts, consts, optConsts.Length);
+        }
+      }
+
+      private void Func(double[] arg, double[] fi, object obj) {
+        var code = (byte[])obj;
+        int n = predBuf.Length;
+        evaluator.Exec(code, x, arg, predBuf); // gradients are nParams x vLen
+        for (int r = 0; r < n; r++) {
+          var res = predBuf[r] - y[r];
+          fi[r] = res;
+        }
+
+        var penaltyIdx = fi.Length - 1;
+        fi[penaltyIdx] = 0.0;
+        // calc length of parameter vector for regularization
+        var aa = 0.0;
+        for (int i = 0; i < arg.Length; i++) {
+          aa += arg[i] * arg[i];
+        }
+        if (lambda > 0 && aa > 0) {
+          // scale lambda using stdDev(y) to make the parameter independent of the scale of y
+          // scale lambda using n to make parameter independent of the number of training points
+          // take the root because LM squares the result
+          fi[penaltyIdx] = Math.Sqrt(n * lambda / yStdDev * aa);
+        }
+      }
+
+      private void FuncAndJacobian(double[] arg, double[] fi, double[,] jac, object obj) {
+        int n = predBuf.Length;
+        int nParams = arg.Length;
+        var code = (byte[])obj;
+        evaluator.ExecGradient(code, x, arg, predBuf, gradBuf); // gradients are nParams x vLen
+        for (int r = 0; r < n; r++) {
+          var res = predBuf[r] - y[r];
+          fi[r] = res;
+
+          for (int k = 0; k < nParams; k++) {
+            jac[r, k] = gradBuf[k][r];
+          }
+        }
+        // calc length of parameter vector for regularization
+        double aa = 0.0;
+        for (int i = 0; i < arg.Length; i++) {
+          aa += arg[i] * arg[i];
+        }
+
+        var penaltyIdx = fi.Length - 1;
+        if (lambda > 0 && aa > 0) {
+          fi[penaltyIdx] = 0.0;
+          // scale lambda using stdDev(y) to make the parameter independent of the scale of y
+          // scale lambda using n to make parameter independent of the number of training points
+          // take the root because alglib LM squares the result
+          fi[penaltyIdx] = Math.Sqrt(n * lambda / yStdDev * aa);
+
+          for (int i = 0; i < arg.Length; i++) {
+            jac[penaltyIdx, i] = 0.5 / fi[penaltyIdx] * 2 * n * lambda / yStdDev * arg[i];
+          }
+        } else {
+          fi[penaltyIdx] = 0.0;
+          for (int i = 0; i < arg.Length; i++) {
+            jac[penaltyIdx, i] = 0.0;
+          }
+        }
+      }
+
+
+      private void UpdateParetoFront(double q, int complexity, byte[] code, double[] param, int nParam,
+        double[] scalingFactor, double[] scalingOffset) {
+        double[] best = new double[2];
+        double[] cur = new double[2] { q, complexity };
+        bool[] max = new[] { true, false };
+        var isNonDominated = true;
+        foreach (var e in paretoFront) {
+          var domRes = DominationCalculator<int>.Dominates(cur, e, max, true);
+          if (domRes == DominationResult.IsDominated) {
+            isNonDominated = false;
+            break;
+          }
+        }
+        if (isNonDominated) {
+          paretoFront.Add(cur);
+
+          // create model
+          var treeGen = new SymbolicExpressionTreeGenerator(problemData.AllowedInputVariables.ToArray());
+          var interpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
+
+          var t = new SymbolicExpressionTree(treeGen.Exec(code, param, nParam, scalingFactor, scalingOffset));
+          var model = new SymbolicRegressionModel(problemData.TargetVariable, t, interpreter, lowerEstimationLimit, upperEstimationLimit);
+          model.Scale(problemData); // apply linear scaling
+
+          var sol = model.CreateRegressionSolution(this.problemData);
+          sol.Name = string.Format("{0:N5} {1}", q, complexity);
+
+          paretoBestModels.Add(sol);
+        }
+        for (int i = paretoFront.Count - 2; i >= 0; i--) {
+          var @ref = paretoFront[i];
+          var domRes = DominationCalculator<int>.Dominates(cur, @ref, max, true);
+          if (domRes == DominationResult.Dominates) {
+            paretoFront.RemoveAt(i);
+            paretoBestModels.RemoveAt(i);
+          }
+        }
+      }
+
+      #endregion
+
+
+    }
+
+
+    /// <summary>
+    /// Static method to initialize a state for the algorithm
+    /// </summary>
+    /// <param name="problemData">The problem data</param>
+    /// <param name="randSeed">Random seed.</param>
+    /// <param name="maxVariables">Maximum number of variable references that are allowed in the expression.</param>
+    /// <param name="scaleVariables">Optionally scale input variables to the interval [0..1] (recommended)</param>
+    /// <param name="constOptIterations">Maximum number of iterations for constants optimization (Levenberg-Marquardt)</param>
+    /// <param name="lambda">Penalty factor for regularization (0..inf.), small penalty disabled regularization.</param>
+    /// <param name="policy">Tree search policy (random, ucb, eps-greedy, ...)</param>
+    /// <param name="collectParameterOptimalModels">Optionally collect all Pareto-optimal solutions having minimal length and error.</param>
+    /// <param name="lowerEstimationLimit">Optionally limit the result of the expression to this lower value.</param>
+    /// <param name="upperEstimationLimit">Optionally limit the result of the expression to this upper value.</param>
+    /// <param name="allowProdOfVars">Allow products of expressions.</param>
+    /// <param name="allowExp">Allow expressions with exponentials.</param>
+    /// <param name="allowLog">Allow expressions with logarithms</param>
+    /// <param name="allowInv">Allow expressions with 1/x</param>
+    /// <param name="allowMultipleTerms">Allow expressions which are sums of multiple terms.</param>
+    /// <returns></returns>
+
+    public static IState CreateState(IRegressionProblemData problemData, uint randSeed, int maxVariables = 3,
+      bool scaleVariables = true, int constOptIterations = -1, double lambda = 0.0,
+      bool collectParameterOptimalModels = false,
+      double lowerEstimationLimit = double.MinValue, double upperEstimationLimit = double.MaxValue,
+      bool allowProdOfVars = true,
+      bool allowExp = true,
+      bool allowLog = true,
+      bool allowInv = true,
+      bool allowMultipleTerms = false
+      ) {
+      return new State(problemData, randSeed, maxVariables, scaleVariables, constOptIterations, lambda,
+        collectParameterOptimalModels,
+        lowerEstimationLimit, upperEstimationLimit,
+        allowProdOfVars, allowExp, allowLog, allowInv, allowMultipleTerms);
+    }
+
+    // returns the quality of the evaluated solution
+    public static double MakeStep(IState state) {
+      var mctsState = state as State;
+      if (mctsState == null) throw new ArgumentException("state");
+      if (mctsState.Done) throw new NotSupportedException("The tree search has enumerated all possible solutions.");
+
+      return TreeSearch(mctsState);
+    }
+    #endregion
+
+    private static double TreeSearch(State mctsState) {
+      var automaton = mctsState.automaton;
+      var tree = mctsState.tree;
+      var eval = mctsState.evalFun;
+      var rand = mctsState.random;
+      double q = 0;
+      bool success = false;
+      do {
+
+        automaton.Reset();
+        success = TryTreeSearchRec2(rand, tree, automaton, eval, mctsState, out q);
+        mctsState.totalRollouts++;
+      } while (!success && !tree.Done);
+      if (success) {
+        mctsState.effectiveRollouts++;
+
+#if DEBUG
+        // Console.WriteLine(mctsState.ExprStr(automaton));
+#endif
+
+        return q;
+      } else return 0.0;
+    }
+
+    // search forward
+    private static bool TryTreeSearchRec2(IRandom rand, Tree tree, Automaton automaton,
+      Func<byte[], int, double> eval,
+      State state,
+      out double q) {
+      // ROLLOUT AND EXPANSION
+      // We are navigating a graph (states might be reached via different paths) instead of a tree.
+      // State equivalence is checked through ExprHash (based on the generated code through the path).
+
+      // We switch between rollout-mode and expansion mode.
+      // Rollout-mode means we are navigating an existing path through the tree (using a rollout policy, e.g. UCB).
+      // Expansion mode means we expand the graph, creating new nodes and edges (using an expansion policy, e.g. shortest route to a complete expression).
+      // In expansion mode we might re-enter the graph and switch back to rollout-mode.
+      // We do this until we reach a complete expression (final state).
+
+      // Loops in the graph are prevented by checking that the level of a child must be larger than the level of the parent.
+      // Sub-graphs which have been completely searched are marked as done.
+      // Roll-out could lead to a state where all follow-states are done. In this case we call the rollout ineffective.
+
+      while (!automaton.IsFinalState(automaton.CurrentState)) {
+        // Console.WriteLine(automaton.stateNames[automaton.CurrentState]);
+        if (state.children.ContainsKey(tree)) {
+          if (state.children[tree].All(ch => ch.Done)) {
+            tree.Done = true;
+            break;
+          }
+          // ROLLOUT INSIDE TREE
+          // UCT selection within tree
+          int selectedIdx = 0;
+          if (state.children[tree].Count > 1) {
+            selectedIdx = SelectInternal(state.children[tree], rand);
+          }
+
+          tree = state.children[tree][selectedIdx];
+
+          // all steps where no alternatives could be taken immediately (without expanding the tree)
+          // TODO: simplification of the automaton 
+          int[] possibleFollowStates = new int[1000];
+          int nFs;
+          automaton.FollowStates(automaton.CurrentState, ref possibleFollowStates, out nFs);
+          Debug.Assert(possibleFollowStates.Contains(tree.state));
+          automaton.Goto(tree.state);
+        } else {
+          // EXPAND 
+          int[] possibleFollowStates = new int[1000];
+          int nFs;
+          string actionString = "";
+          automaton.FollowStates(automaton.CurrentState, ref possibleFollowStates, out nFs);
+
+          if (nFs == 0) {
+            // stuck in a dead end (no final state and no allowed follow states)
+            tree.Done = true;
+            break;
+          }
+          var newChildren = new List<Tree>(nFs);
+          state.children.Add(tree, newChildren);
+          for (int i = 0; i < nFs; i++) {
+            Tree child = null;
+            // for selected states (EvalStates) we introduce state unification (detection of equivalent states)
+            if (automaton.IsEvalState(possibleFollowStates[i])) {
+              var hc = Hashcode(automaton);
+              hc = ((hc << 5) + hc) ^ (ulong)tree.state; // TODO fix unit test for structure enumeration
+              if (!state.nodes.TryGetValue(hc, out child)) {
+                // Console.WriteLine("New expression (hash: {0}, state: {1})", Hashcode(automaton), automaton.stateNames[possibleFollowStates[i]]);
+                child = new Tree() {
+                  state = possibleFollowStates[i],
+                  expr = actionString + automaton.GetActionString(automaton.CurrentState, possibleFollowStates[i]),
+                  level = tree.level + 1
+                };
+                state.nodes.Add(hc, child);
+              }
+              // only allow forward edges (don't add the child if we would go back in the graph)
+              else if (child.level > tree.level) {
+                // Console.WriteLine("Existing expression (hash: {0}, state: {1})", Hashcode(automaton), automaton.stateNames[possibleFollowStates[i]]);
+                // whenever we join paths we need to propagate back the statistics of the existing node through the newly created link
+                // to all parents 
+                BackpropagateStatistics(tree, state, child.visits);
+              } else {
+                // Console.WriteLine("Cycle (hash: {0}, state: {1})", Hashcode(automaton), automaton.stateNames[possibleFollowStates[i]]);
+                // prevent cycles
+                Debug.Assert(child.level <= tree.level);
+                child = null;
+              }
+            } else {
+              child = new Tree() {
+                state = possibleFollowStates[i],
+                expr = actionString + automaton.GetActionString(automaton.CurrentState, possibleFollowStates[i]),
+                level = tree.level + 1
+              };
+            }
+            if (child != null)
+              newChildren.Add(child);
+          }
+
+          if (!newChildren.Any()) {
+            // stuck in a dead end (no final state and no allowed follow states)
+            tree.Done = true;
+            break;
+          }
+
+          foreach (var ch in newChildren) {
+            if (!state.parents.ContainsKey(ch)) {
+              state.parents.Add(ch, new List<Tree>());
+            }
+            state.parents[ch].Add(tree);
+          }
+
+
+          // follow one of the children 
+          tree = SelectStateLeadingToFinal(automaton, tree, rand, state);
+          automaton.Goto(tree.state);
+        }
+      }
+
+      bool success;
+
+      // EVALUATE TREE
+      if (!tree.Done && automaton.IsFinalState(automaton.CurrentState)) {
+        tree.Done = true;
+        // for debugging
+        // tree.expr = state.ExprStr(automaton);
+        byte[] code; int nParams;
+        automaton.GetCode(out code, out nParams);
+        q = eval(code, nParams);
+        success = true;
+        BackpropagateQuality(tree, q, state);
+      } else {
+        // we got stuck in roll-out (not evaluation necessary!)
+        q = 0.0;
+        success = false;
+      }
+
+      // RECURSIVELY BACKPROPAGATE RESULTS TO ALL PARENTS
+      // Update statistics
+      // Set branch to done if all children are done.
+      BackpropagateDone(tree, state);
+      BackpropagateDebugStats(tree, q, state);
+
+
+      return success;
+    }
+
+    private static int SelectInternal(List<Tree> list, IRandom rand) {
+      Debug.Assert(list.Any(t => !t.Done));
+      
+      // check if there is any node which has not been visited
+      for(int i=0;i<list.Count;i++) {
+        if (!list[i].Done && list[i].visits == 0) return i;
+      }
+
+      // choose a random node.
+      var idx = rand.Next(list.Count);
+      while (list[idx].Done) { idx = rand.Next(list.Count); }
+      return idx;
+    }
+
+    // backpropagate existing statistics to all parents
+    private static void BackpropagateStatistics(Tree tree, State state, int numVisits) {
+      tree.visits += numVisits;
+
+      if (state.parents.ContainsKey(tree)) {
+        foreach (var parent in state.parents[tree]) {
+          BackpropagateStatistics(parent, state, numVisits);
+        }
+      }
+    }
+
+    private static ulong Hashcode(Automaton automaton) {
+      byte[] code;
+      int nParams;
+      automaton.GetCode(out code, out nParams);
+      return (ulong)ExprHashSymbolic.GetHash(code, nParams);
+    }
+
+    private static void BackpropagateQuality(Tree tree, double q, State state) {
+      tree.visits++;
+      // TODO: q is ignored for now
+
+      if (state.parents.ContainsKey(tree)) {
+        foreach (var parent in state.parents[tree]) {
+          BackpropagateQuality(parent, q, state);
+        }
+      }
+    }
+
+    private static void BackpropagateDone(Tree tree, State state) {
+      if (state.children.ContainsKey(tree) && state.children[tree].All(ch => ch.Done)) {
+        tree.Done = true;
+        // children[tree] = null; keep all nodes
+      }
+
+      if (state.parents.ContainsKey(tree)) {
+        foreach (var parent in state.parents[tree]) {
+          BackpropagateDone(parent, state);
+        }
+      }
+    }
+
+    private static void BackpropagateDebugStats(Tree tree, double q, State state) {
+      if (state.parents.ContainsKey(tree)) {
+        foreach (var parent in state.parents[tree]) {
+          BackpropagateDebugStats(parent, q, state);
+        }
+      }
+
+    }
+
+    private static Tree SelectStateLeadingToFinal(Automaton automaton, Tree tree, IRandom rand, State state) {
+      // find the child with the smallest state value (smaller values are closer to the final state)
+      int selectedChildIdx = 0;
+      var children = state.children[tree];
+      Tree minChild = children.First();
+      for (int i = 1; i < children.Count; i++) {
+        if (children[i].state < minChild.state)
+          selectedChildIdx = i;
+      }
+      return children[selectedChildIdx];
+    }
+
+    // scales data and extracts values from dataset into arrays
+    private static void GenerateData(IRegressionProblemData problemData, bool scaleVariables, IEnumerable<int> rows,
+      out double[][] xs, out double[] y, out double[] scalingFactor, out double[] scalingOffset) {
+      xs = new double[problemData.AllowedInputVariables.Count()][];
+
+      var i = 0;
+      if (scaleVariables) {
+        scalingFactor = new double[xs.Length + 1];
+        scalingOffset = new double[xs.Length + 1];
+      } else {
+        scalingFactor = null;
+        scalingOffset = null;
+      }
+      foreach (var var in problemData.AllowedInputVariables) {
+        if (scaleVariables) {
+          var minX = problemData.Dataset.GetDoubleValues(var, rows).Min();
+          var maxX = problemData.Dataset.GetDoubleValues(var, rows).Max();
+          var range = maxX - minX;
+
+          // scaledX = (x - min) / range
+          var sf = 1.0 / range;
+          var offset = -minX / range;
+          scalingFactor[i] = sf;
+          scalingOffset[i] = offset;
+          i++;
+        }
+      }
+
+      if (scaleVariables) {
+        // transform target variable to zero-mean
+        scalingFactor[i] = 1.0;
+        scalingOffset[i] = -problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows).Average();
+      }
+
+      GenerateData(problemData, rows, scalingFactor, scalingOffset, out xs, out y);
+    }
+
+    // extract values from dataset into arrays
+    private static void GenerateData(IRegressionProblemData problemData, IEnumerable<int> rows, double[] scalingFactor, double[] scalingOffset,
+     out double[][] xs, out double[] y) {
+      xs = new double[problemData.AllowedInputVariables.Count()][];
+
+      int i = 0;
+      foreach (var var in problemData.AllowedInputVariables) {
+        var sf = scalingFactor == null ? 1.0 : scalingFactor[i];
+        var offset = scalingFactor == null ? 0.0 : scalingOffset[i];
+        xs[i++] =
+          problemData.Dataset.GetDoubleValues(var, rows).Select(xi => xi * sf + offset).ToArray();
+      }
+
+      {
+        var sf = scalingFactor == null ? 1.0 : scalingFactor[i];
+        var offset = scalingFactor == null ? 0.0 : scalingOffset[i];
+        y = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows).Select(yi => yi * sf + offset).ToArray();
+      }
+    }
+
+    // for debugging only
+    #region debugging
+
+    private static string TraceTree(Tree tree, State state) {
+      var sb = new StringBuilder();
+      sb.Append(
+@"digraph {
+  ratio = fill;
+  node [style=filled];
+");
+      int nodeId = 0;
+
+      TraceTreeRec(tree, 0, sb, ref nodeId, state);
+      sb.Append("}");
+      return sb.ToString();
+    }
+
+    private static void TraceTreeRec(Tree tree, int parentId, StringBuilder sb, ref int nextId, State state) {
+      var tries = tree.visits;
+
+      sb.AppendFormat("{0} [label=\"{1}\"]; ", parentId, tries).AppendLine();
+
+      var list = new List<Tuple<int, int, Tree>>();
+      if (state.children.ContainsKey(tree)) {
+        foreach (var ch in state.children[tree]) {
+          nextId++;
+          tries = ch.visits;
+          sb.AppendFormat("{0} [label=\"{1}\"]; ", nextId, tries).AppendLine();
+          sb.AppendFormat("{0} -> {1} [label=\"{2}\"]", parentId, nextId, ch.expr).AppendLine();
+          list.Add(Tuple.Create(tries, nextId, ch));
+        }
+
+        foreach (var tup in list) {
+          var ch = tup.Item3;
+          var chId = tup.Item2;
+          if (state.children.ContainsKey(ch) && state.children[ch].Count == 1) {
+            var chch = state.children[ch].First();
+            nextId++;
+            tries = chch.visits;
+            sb.AppendFormat("{0} [label=\"{1}\"]; ", nextId, tries).AppendLine();
+            sb.AppendFormat("{0} -> {1} [label=\"{2}\"]", chId, nextId, chch.expr).AppendLine();
+          }
+        }
+
+        foreach (var tup in list.OrderByDescending(t => t.Item1).Take(1)) {
+          TraceTreeRec(tup.Item3, tup.Item2, sb, ref nextId, state);
+        }
+      }
+    }
+
+    private static string WriteTree(Tree tree, State state) {
+      var sb = new System.IO.StringWriter(System.Globalization.CultureInfo.InvariantCulture);
+      var nodeIds = new Dictionary<Tree, int>();
+      sb.Write(
+@"digraph {
+  ratio = fill;
+  node [style=filled];
+");
+      int threshold = /* state.nodes.Count > 500 ? 10 : */ 0;
+      foreach (var kvp in state.children) {
+        var parent = kvp.Key;
+        int parentId;
+        if (!nodeIds.TryGetValue(parent, out parentId)) {
+          parentId = nodeIds.Count + 1;
+          var tries = parent.visits;
+          if (tries > threshold)
+            sb.Write("{0} [label=\"{1}\"]; ", parentId, tries);
+          nodeIds.Add(parent, parentId);
+        }
+        foreach (var child in kvp.Value) {
+          int childId;
+          if (!nodeIds.TryGetValue(child, out childId)) {
+            childId = nodeIds.Count + 1;
+            nodeIds.Add(child, childId);
+          }
+          var tries = child.visits;
+          if (tries < 1) continue;
+          if (tries > threshold) {
+            sb.Write("{0} [label=\"{1}\"]; ", childId, tries);
+            var edgeLabel = child.expr;
+            // if (parent.expr.Length > 0) edgeLabel = edgeLabel.Replace(parent.expr, "");
+            sb.Write("{0} -> {1} [label=\"{2}\"]", parentId, childId, edgeLabel);
+          }
+        }
+      }
+
+      sb.Write("}");
+      return sb.ToString();
+    }
+	#endregion
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/OpCodes.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/OpCodes.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/OpCodes.cs	(revision 15650)
@@ -0,0 +1,36 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  public enum OpCodes : byte {
+    Nop,
+    LoadConst0,
+    LoadConst1, // load the constant 1
+    LoadParamN, // load next parameter 
+    LoadVar,    // + 2 byte parameters
+    Mul,
+    Add,
+    Log,
+    Exp,
+    Inv,
+    Exit
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/SymbolicExpressionGenerator.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/SymbolicExpressionGenerator.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/SymbolicExpressionGenerator.cs	(revision 15650)
@@ -0,0 +1,184 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+using System;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+using HeuristicLab.Problems.DataAnalysis.Symbolic;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+
+  // helper class.
+  // Translates byte code into a symbolic expression tree, e.g. for the final solution.
+  internal class SymbolicExpressionTreeGenerator {
+    const int MaxStackSize = 100;
+    private readonly ISymbolicExpressionTreeNode[] stack;
+    private readonly ConstantTreeNode const0;
+    private readonly ConstantTreeNode const1;
+    private readonly Addition addSy;
+    private readonly Multiplication mulSy;
+    private readonly Exponential expSy;
+    private readonly Logarithm logSy;
+    private readonly Division divSy;
+    private readonly VariableTreeNode varNode;
+    private readonly string[] variableNames;
+    private readonly StartSymbol startSy;
+    private readonly ProgramRootSymbol progRootSy;
+
+    public SymbolicExpressionTreeGenerator(string[] variableNames) {
+      stack = new ISymbolicExpressionTreeNode[MaxStackSize];
+      var grammar = new TypeCoherentExpressionGrammar();
+      this.variableNames = variableNames;
+
+      grammar.ConfigureAsDefaultRegressionGrammar();
+      const0 = (ConstantTreeNode)grammar.Symbols.OfType<Constant>().First().CreateTreeNode();
+      const0.Value = 0;
+      const1 = (ConstantTreeNode)grammar.Symbols.OfType<Constant>().First().CreateTreeNode();
+      const1.Value = 1;
+      varNode = (VariableTreeNode)grammar.Symbols.OfType<Variable>().First().CreateTreeNode();
+
+      addSy = grammar.AllowedSymbols.OfType<Addition>().First();
+      mulSy = grammar.AllowedSymbols.OfType<Multiplication>().First();
+      logSy = grammar.AllowedSymbols.OfType<Logarithm>().First();
+      expSy = grammar.AllowedSymbols.OfType<Exponential>().First();
+      divSy = grammar.AllowedSymbols.OfType<Division>().First();
+
+      progRootSy = grammar.AllowedSymbols.OfType<ProgramRootSymbol>().First();
+      startSy = grammar.AllowedSymbols.OfType<StartSymbol>().First();
+    }
+
+    public ISymbolicExpressionTreeNode Exec(byte[] code, double[] consts, int nParams, double[] scalingFactor, double[] scalingOffset) {
+      int topOfStack = -1;
+      int pc = 0;
+      int nextParamIdx = -1;
+      OpCodes op;
+      short arg;
+      while (true) {
+        ReadNext(code, ref pc, out op, out arg);
+        switch (op) {
+          case OpCodes.Nop: break;
+          case OpCodes.LoadConst0: {
+              ++topOfStack;
+              stack[topOfStack] = (ISymbolicExpressionTreeNode)const0.Clone();
+              break;
+            }
+          case OpCodes.LoadConst1: {
+              ++topOfStack;
+              stack[topOfStack] = (ISymbolicExpressionTreeNode)const1.Clone();
+              break;
+            }
+          case OpCodes.LoadParamN: {
+              ++topOfStack;
+              var p = (ConstantTreeNode)const1.Clone(); // value will be tuned later (evaluator and tree generator both use 1 as initial values)
+              p.Value = consts[++nextParamIdx];
+              stack[topOfStack] = p;
+              break;
+            }
+          case OpCodes.LoadVar:
+            ++topOfStack;
+            if (scalingOffset != null) {
+              var sumNode = addSy.CreateTreeNode();
+              var varNode = (VariableTreeNode)this.varNode.Clone();
+              var constNode = (ConstantTreeNode)const0.Clone();
+              varNode.Weight = scalingFactor[arg];
+              varNode.VariableName = variableNames[arg];
+              constNode.Value = scalingOffset[arg];
+              sumNode.AddSubtree(varNode);
+              sumNode.AddSubtree(constNode);
+              stack[topOfStack] = sumNode;
+            } else {
+              var varNode = (VariableTreeNode)this.varNode.Clone();
+              varNode.Weight = 1.0;
+              varNode.VariableName = variableNames[arg];
+              stack[topOfStack] = varNode;
+            }
+            break;
+          case OpCodes.Add: {
+              var t1 = stack[topOfStack - 1];
+              var t2 = stack[topOfStack];
+              topOfStack--;
+              if (t1.Symbol is Addition) {
+                t1.AddSubtree(t2);
+              } else {
+                var addNode = addSy.CreateTreeNode();
+                addNode.AddSubtree(t1);
+                addNode.AddSubtree(t2);
+                stack[topOfStack] = addNode;
+              }
+              break;
+            }
+          case OpCodes.Mul: {
+              var t1 = stack[topOfStack - 1];
+              var t2 = stack[topOfStack];
+              topOfStack--;
+              if (t1.Symbol is Multiplication) {
+                t1.AddSubtree(t2);
+              } else {
+                var mulNode = mulSy.CreateTreeNode();
+                mulNode.AddSubtree(t1);
+                mulNode.AddSubtree(t2);
+                stack[topOfStack] = mulNode;
+              }
+              break;
+            }
+          case OpCodes.Log: {
+              var v1 = stack[topOfStack];
+              var logNode = logSy.CreateTreeNode();
+              logNode.AddSubtree(v1);
+              stack[topOfStack] = logNode;
+              break;
+            }
+          case OpCodes.Exp: {
+              var v1 = stack[topOfStack];
+              var expNode = expSy.CreateTreeNode();
+              expNode.AddSubtree(v1);
+              stack[topOfStack] = expNode;
+              break;
+            }
+          case OpCodes.Inv: {
+              var v1 = stack[topOfStack];
+              var divNode = divSy.CreateTreeNode();
+              divNode.AddSubtree(v1);
+              stack[topOfStack] = divNode;
+              break;
+            }
+          case OpCodes.Exit:
+            Contract.Assert(topOfStack == 0);
+            var rootNode = progRootSy.CreateTreeNode();
+            var startNode = startSy.CreateTreeNode();
+            startNode.AddSubtree(stack[topOfStack]);
+            rootNode.AddSubtree(startNode);
+            return rootNode;
+        }
+      }
+    }
+
+    private void ReadNext(byte[] code, ref int pc, out OpCodes op, out short s) {
+      op = (OpCodes)Enum.ToObject(typeof(OpCodes), code[pc++]);
+      s = 0;
+      if (op == OpCodes.LoadVar) {
+        s = (short)((code[pc] << 8) | code[pc + 1]);
+        pc += 2;
+      }
+    }
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Tree.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Tree.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/MctsSymbolicRegression/Tree.cs	(revision 15650)
@@ -0,0 +1,30 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * 
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+                                                      
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  internal class Tree {
+    public int state;
+    public int level;
+    public string expr;
+    public bool Done { get; set; }
+    public int visits;
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Plugin.cs.frame
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Plugin.cs.frame	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Plugin.cs.frame	(revision 15650)
@@ -0,0 +1,47 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg {
+  [Plugin("HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg", "", "3.4.0.$WCREV$")]
+  [PluginFile("HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.ALGLIB", "3.7")]
+  [PluginDependency("HeuristicLab.Algorithms.DataAnalysis", "3.4")]
+  [PluginDependency("HeuristicLab.Analysis", "3.3")]
+  [PluginDependency("HeuristicLab.Collections", "3.3")]
+  [PluginDependency("HeuristicLab.Common", "3.3")]
+  [PluginDependency("HeuristicLab.Core", "3.3")]
+  [PluginDependency("HeuristicLab.Data", "3.3")]
+  [PluginDependency("HeuristicLab.Encodings.SymbolicExpressionTreeEncoding", "3.4")]
+  [PluginDependency("HeuristicLab.Operators", "3.3")]
+  [PluginDependency("HeuristicLab.Optimization", "3.3")]
+  [PluginDependency("HeuristicLab.Parameters", "3.3")]
+  [PluginDependency("HeuristicLab.Persistence", "3.3")]
+  [PluginDependency("HeuristicLab.Problems.DataAnalysis", "3.4")]
+  [PluginDependency("HeuristicLab.Problems.DataAnalysis.Symbolic", "3.4")]
+  [PluginDependency("HeuristicLab.Problems.DataAnalysis.Symbolic.Regression", "3.4")]
+  [PluginDependency("HeuristicLab.Problems.Instances", "3.3")]
+  [PluginDependency("HeuristicLab.Random", "3.3")]
+
+  public class Plugin : PluginBase {
+  }
+}
Index: /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Properties/AssemblyInfo.cs
===================================================================
--- /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Properties/AssemblyInfo.cs	(revision 15650)
+++ /branches/2796_SymbReg/HeuristicLab.Algorithms.DataAnalysis/3.4/Properties/AssemblyInfo.cs	(revision 15650)
@@ -0,0 +1,36 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HEAL")]
+[assembly: AssemblyProduct("HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg")]
+[assembly: AssemblyCopyright("HEAL")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d97f91b5-b71b-412d-90a5-177eed948a3a")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /branches/2796_SymbReg/PreBuildEvent.cmd
===================================================================
--- /branches/2796_SymbReg/PreBuildEvent.cmd	(revision 15650)
+++ /branches/2796_SymbReg/PreBuildEvent.cmd	(revision 15650)
@@ -0,0 +1,2 @@
+IF EXIST "%ProjectDir%\Properties\AssemblyInfo.cs.frame" SubWCRev "%ProjectDir%\" "%ProjectDir%\Properties\AssemblyInfo.cs.frame" "%ProjectDir%\Properties\AssemblyInfo.cs"
+IF EXIST "%ProjectDir%\Plugin.cs.frame" SubWCRev "%ProjectDir%\" "%ProjectDir%\Plugin.cs.frame" "%ProjectDir%\Plugin.cs"
Index: /branches/2796_SymbReg/Solution.sln
===================================================================
--- /branches/2796_SymbReg/Solution.sln	(revision 15650)
+++ /branches/2796_SymbReg/Solution.sln	(revision 15650)
@@ -0,0 +1,28 @@
+﻿
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg", "HeuristicLab.Algorithms.DataAnalysis\3.4\HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj", "{D97F91B5-B71B-412D-90A5-177EED948A3A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Tests\Test.csproj", "{80009A4F-F55F-4E95-AD44-5AB01551B964}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{D97F91B5-B71B-412D-90A5-177EED948A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D97F91B5-B71B-412D-90A5-177EED948A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D97F91B5-B71B-412D-90A5-177EED948A3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D97F91B5-B71B-412D-90A5-177EED948A3A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{80009A4F-F55F-4E95-AD44-5AB01551B964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{80009A4F-F55F-4E95-AD44-5AB01551B964}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{80009A4F-F55F-4E95-AD44-5AB01551B964}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{80009A4F-F55F-4E95-AD44-5AB01551B964}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
Index: /branches/2796_SymbReg/Tests/HeuristicLab.Algorithms.DataAnalysis-3.4/MctsSymbolicRegressionTest.cs
===================================================================
--- /branches/2796_SymbReg/Tests/HeuristicLab.Algorithms.DataAnalysis-3.4/MctsSymbolicRegressionTest.cs	(revision 15650)
+++ /branches/2796_SymbReg/Tests/HeuristicLab.Algorithms.DataAnalysis-3.4/MctsSymbolicRegressionTest.cs	(revision 15650)
@@ -0,0 +1,1650 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Problems.DataAnalysis;
+using HeuristicLab.Problems.Instances.DataAnalysis;
+using HeuristicLab.Random;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
+  [TestClass()]
+  public class MctsSymbolicRegressionTest {
+    #region heuristics
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void TestSimple2dInteractions() {
+      {
+        // a, b ~ U(0, 1) should be trivial
+        var nRand = new MersenneTwister(1234);
+
+        int n = 10000; // large sample so that we can use the thresholds below
+        var a = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var b = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var x = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var y = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+
+        var z = a.Zip(b, (ai, bi) => ai * bi).ToArray();
+
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, z) > 0.05); // should be detected as relevant
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, x, z) > 0.05); // a and b > 0 so these should be detected as well
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, y, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, x, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, y, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(x, y, z) < 0.05);
+      }
+      {
+        // a, b ~ U(1000, 2000) also trivial
+        var nRand = new UniformDistributedRandom(new MersenneTwister(1234), 1000, 2000);
+
+        int n = 10000; // large sample so that we can use the thresholds below
+        var a = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var b = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var x = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var y = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+
+        var z = a.Zip(b, (ai, bi) => ai * bi).ToArray();
+
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, z) > 0.05); // should be detected as relevant
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, x, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, y, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, x, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, y, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(x, y, z) < 0.05);
+      }
+      {
+        // a, b ~ U(-1, 1) 
+        var nRand = new UniformDistributedRandom(new MersenneTwister(1234), -1, 1);
+
+        int n = 10000; // large sample so that we can use the thresholds below
+        var a = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var b = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var x = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var y = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+
+        var z = a.Zip(b, (ai, bi) => ai * bi).ToArray();
+
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, z) > 0.05); // should be detected as relevant
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, x, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, y, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, x, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, y, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(x, y, z) < 0.05);
+      }
+      {
+        // a, b ~ N(0, 1)
+        var nRand = new NormalDistributedRandom(new MersenneTwister(1234), 0, 1);
+
+        int n = 10000; // large sample so that we can use the thresholds below
+        var a = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var b = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var x = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var y = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+
+        var z = a.Zip(b, (ai, bi) => ai * bi).ToArray();
+
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, z) > 0.05); // should be detected as relevant
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, x, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, y, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, x, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, y, z) < 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(x, y, z) < 0.05);
+      }
+      {
+        var rand = new MersenneTwister(1234);
+        // a ~ N(100, 1), b ~ N(-100, 1)
+        var nRand = new NormalDistributedRandom(rand, 0, 1);
+        var aRand = new NormalDistributedRandom(rand, 100, 1);
+        var bRand = new NormalDistributedRandom(rand, -100, 1);
+
+        int n = 10000; // large sample so that we can use the thresholds below
+        var a = Enumerable.Range(0, n).Select(_ => aRand.NextDouble()).ToArray();
+        var b = Enumerable.Range(0, n).Select(_ => bRand.NextDouble()).ToArray();
+        var x = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+        var y = Enumerable.Range(0, n).Select(_ => nRand.NextDouble()).ToArray();
+
+        var z = a.Zip(b, (ai, bi) => ai * bi).ToArray();
+
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, z) > 0.05); // should be detected as relevant
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, x, z) > 0.05); // a > 0
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, y, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, x, z) > 0.05); // b < 0
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, y, z) > 0.05);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(x, y, z) < 0.05); // random variables are not correlated
+      }
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void TestGeneral2dInteractions() {
+      {
+        // we should be able to reliably detect when a product of two variables is correlated with the target variable        
+
+        // the test samples x from a two dimensional normal distribution
+        // the covariance matrix for the normal distribution is randomly sampled 
+        // this means x_1 and x_2 might be highly correlated
+        // the mean of the normal distribution is randomly sampled (most critical are probably zero-mean distributions)
+        // y is calculated as x_1*x_2
+
+        var conditionNumber = 10000;
+        for (int iter = 0; iter < 100; iter++) {
+          double m0 = 0.0; double m1 = 0.0;
+          alglib.hqrndstate randState;
+          alglib.hqrndseed(1234, 31415, out randState);
+
+          // uncomment if non-zero mean distributions should be tested
+          //alglib.hqrndnormal2(randState, out m0, out m1);
+
+          double[,] cov_ab = new double[2, 2];
+          double[,] cov_xy = new double[2, 2];
+          alglib.matgen.spdmatrixrndcond(2, conditionNumber, ref cov_ab);
+          alglib.spdmatrixcholesky(ref cov_ab, 2, true);
+
+          alglib.matgen.spdmatrixrndcond(2, conditionNumber, ref cov_xy);
+          alglib.spdmatrixcholesky(ref cov_xy, 2, true);
+
+          // generate a, b by sampling from a 2dim multivariate normal distribution
+          // generate x, y by sampling from another 2dim multivariate normal distribution
+          // a,b and x,y might be correlated but x,y are not correlated to a,b
+          int N = 1000; // large sample size to make sure the test thresholds hold
+          double[] a = new double[N];
+          double[] b = new double[N];
+          double[] x = new double[N];
+          double[] y = new double[N];
+          double[] z = new double[N];
+
+          for (int i = 0; i < N; i++) {
+            double r1, r2, r3, r4;
+            alglib.hqrndnormal2(randState, out r1, out r2);
+            alglib.hqrndnormal2(randState, out r3, out r4);
+
+            var r_ab = new double[] { r1, r2 };
+            var r_xy = new double[] { r3, r4 };
+            double[] s_ab = new double[2];
+            double[] s_xy = new double[2];
+            alglib.ablas.rmatrixmv(2, 2, cov_ab, 0, 0, 0, r_ab, 0, ref s_ab, 0);
+            alglib.ablas.rmatrixmv(2, 2, cov_xy, 0, 0, 0, r_xy, 0, ref s_xy, 0);
+
+            a[i] = s_ab[0] + m0;
+            b[i] = s_ab[1] + m1;
+            x[i] = s_xy[0] + m0; // use same mean (just for the sake of it)
+            y[i] = s_xy[1] + m1;
+
+            z[i] = a[i] * b[i];
+          }
+
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, z) > 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(x, y, z) < 0.05);
+
+          /* we might see correlations when only using one of the two relevant factors.
+           * this depends on the distribution / location of a and b 
+          // for zero-mean distributions the following should all be quasi-zero
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(a, x, z) < 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(b, x, z) < 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(a, y, z) < 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(b, y, z) < 0.05);
+          */
+          Console.WriteLine("a,b: {0:N3}\tx,y: {1:N3}\ta,x: {2:N3}\tb,x: {3:N3}\ta,y: {4:N3}\tb,y: {5:N3}\tcov(a,b): {6:N3}",
+            Heuristics.CorrelationForInteraction(a, b, z),
+            Heuristics.CorrelationForInteraction(x, y, z),
+            Heuristics.CorrelationForInteraction(a, x, z),
+            Heuristics.CorrelationForInteraction(b, x, z),
+            Heuristics.CorrelationForInteraction(a, y, z),
+            Heuristics.CorrelationForInteraction(b, y, z),
+            alglib.cov2(a, b)
+            );
+        }
+      }
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void TestGeneral3dInteractions() {
+      {
+        // same as TestGeneral2dInteractions but for terms with three variables
+
+        var conditionNumber = 100;
+        for (int iter = 0; iter < 100; iter++) {
+          double m0 = 0.0; double m1 = 0.0; double m2 = 0.0;
+          alglib.hqrndstate randState;
+          alglib.hqrndseed(1234, 31415, out randState);
+
+          // uncomment if non-zero mean distributions should be tested
+          //alglib.hqrndnormal2(randState, out m0, out m1);
+          //alglib.hqrndnormal2(randState, out m1, out m2);
+
+          double[,] cov_abc = new double[3, 3];
+          double[,] cov_xyz = new double[3, 3];
+          alglib.matgen.spdmatrixrndcond(3, conditionNumber, ref cov_abc);
+          alglib.spdmatrixcholesky(ref cov_abc, 3, true);
+
+          alglib.matgen.spdmatrixrndcond(3, conditionNumber, ref cov_xyz);
+          alglib.spdmatrixcholesky(ref cov_xyz, 3, true);
+
+          int N = 1000; // large sample size to make sure the test thresholds hold
+          double[] a = new double[N];
+          double[] b = new double[N];
+          double[] c = new double[N];
+          double[] x = new double[N];
+          double[] y = new double[N];
+          double[] z = new double[N];
+          double[] t = new double[N];
+
+          for (int i = 0; i < N; i++) {
+            double r1, r2, r3, r4, r5, r6;
+            alglib.hqrndnormal2(randState, out r1, out r2);
+            alglib.hqrndnormal2(randState, out r3, out r4);
+            alglib.hqrndnormal2(randState, out r5, out r6);
+
+            var r_abc = new double[] { r1, r2, r3 };
+            var r_xyz = new double[] { r4, r5, r6 };
+            double[] s_abc = new double[3];
+            double[] s_xyz = new double[3];
+            alglib.ablas.rmatrixmv(3, 3, cov_abc, 0, 0, 0, r_abc, 0, ref s_abc, 0);
+            alglib.ablas.rmatrixmv(3, 3, cov_xyz, 0, 0, 0, r_xyz, 0, ref s_xyz, 0);
+
+            a[i] = s_abc[0] + m0;
+            b[i] = s_abc[1] + m1;
+            c[i] = s_abc[2] + m2;
+            x[i] = s_xyz[0] + m0; // use same mean (just for the sake of it)
+            y[i] = s_xyz[1] + m1;
+            z[i] = s_xyz[2] + m2;
+
+            t[i] = a[i] * b[i] * c[i];
+          }
+
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, c, t) > 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(x, y, z, t) < 0.05);
+
+          /* we might see correlations when only using one of the two relevant factors.
+           * this depends on the distribution / location of a and b 
+          // for zero-mean distributions the following should all be quasi-zero
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(a, x, z) < 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(b, x, z) < 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(a, y, z) < 0.05);
+          Assert.IsTrue(Heuristics.CorrelationForInteraction(b, y, z) < 0.05);
+          */
+          Console.WriteLine("a,b,c: {0:N3}\tx,y,z: {1:N3}\ta,b,x: {2:N3}\tb,c,x: {3:N3}",
+            Heuristics.CorrelationForInteraction(a, b, c, t),
+            Heuristics.CorrelationForInteraction(x, y, z, t),
+            Heuristics.CorrelationForInteraction(a, b, x, t),
+            Heuristics.CorrelationForInteraction(b, c, x, t)
+            );
+        }
+      }
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void TestPoly10Interactions() {
+      {
+        alglib.hqrndstate randState;
+        alglib.hqrndseed(1234, 31415, out randState);
+
+        int N = 25000; // large sample size to make sure the test thresholds hold
+        double[] a = new double[N];
+        double[] b = new double[N];
+        double[] c = new double[N];
+        double[] d = new double[N];
+        double[] e = new double[N];
+        double[] f = new double[N];
+        double[] g = new double[N];
+        double[] h = new double[N];
+        double[] i = new double[N];
+        double[] j = new double[N];
+        double[] y = new double[N];
+
+        for(int k=0;k<N;k++) {
+          a[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          b[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          c[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          d[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          e[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          f[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          g[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          h[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          i[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          j[k] = alglib.hqrnduniformr(randState) * 2 - 1;
+          y[k] = a[k] * b[k] + c[k] * d[k] + e[k] * f[k] + a[k] * g[k] * i[k] + c[k] * f[k] * j[k];
+        }
+
+        var x = new[] { a, b, c, d, e, f, g, h, i, j };
+        var all2Combinations = HeuristicLab.Common.EnumerableExtensions.Combinations(new[] {1,2,3,4,5,6,7,8,9,10}, 2);
+
+        var resultList = new List<Tuple<string, double>>();
+        foreach(var entry in all2Combinations) {
+          var aIdx = entry.First();
+          var bIdx = entry.Skip(1).First();
+          resultList.Add(Tuple.Create(aIdx + " " + bIdx, Heuristics.CorrelationForInteraction(x[aIdx - 1], x[bIdx - 1], y)));
+        }
+
+        foreach(var entry in resultList.OrderByDescending(t => t.Item2)) {
+          Console.WriteLine("{0} {1:N3}", entry.Item1, entry.Item2);
+        }
+
+        var all3Combinations = HeuristicLab.Common.EnumerableExtensions.Combinations(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 3);
+
+        resultList = new List<Tuple<string, double>>();
+        foreach (var entry in all3Combinations) {
+          var aIdx = entry.First();
+          var bIdx = entry.Skip(1).First();
+          var cIdx = entry.Skip(2).First();
+          resultList.Add(Tuple.Create(aIdx + " " + bIdx + " " + cIdx, Heuristics.CorrelationForInteraction(x[aIdx - 1], x[bIdx - 1], x[cIdx - 1], y)));
+        }
+
+        //  Y = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9 + X3*X6*X10
+
+        foreach (var entry in resultList.OrderByDescending(t => t.Item2)) {
+          Console.WriteLine("{0} {1:N3}", entry.Item1, entry.Item2);
+        }
+
+
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, b, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(b, a, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(c, d, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(d, c, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(e, f, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(f, e, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, g, i, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(a, i, g, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(g, a, i, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(g, i, a, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(i, g, a, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(i, a, g, y) > 0.01);
+
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(c, f, j, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(c, j, f, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(f, c, j, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(f, j, c, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(j, c, f, y) > 0.01);
+        Assert.IsTrue(Heuristics.CorrelationForInteraction(j, f, c, y) > 0.01);
+      }
+    }
+    #endregion
+
+
+    #region expression hashing
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void ExprHashSymbolicTest() {
+      int nParams;
+      byte[] code;
+
+      {
+        // addition of variables
+        var codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+
+      {
+        // multiplication of variables
+        var codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+
+      {
+        // distributivity 
+        var codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit2(OpCodes.LoadVar, 3);
+        codeGen.Emit1(OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 3);
+        codeGen.Emit1(OpCodes.Mul);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(OpCodes.LoadVar, 3);
+        codeGen.Emit1(OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+
+
+      { // 1/(x1x2) = 1/x1 * 1/x2
+        var codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Inv);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(OpCodes.Inv);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(OpCodes.Inv);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+      {
+        // exp
+        var codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exp);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(OpCodes.Exp);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.GetCode(out code, out nParams);
+        codeGen.Emit1(OpCodes.Exit);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+      {
+        // log
+        var codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Log);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(OpCodes.Log);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 2);
+        codeGen.Emit1(OpCodes.Log);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+
+      {
+        // x1 + x1 is equivalent to x1
+        var codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+      {
+        // c1*x1 + c2*x1 is equivalent to c3*x1
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+
+      { // c1 x1 + c2 x1 = c3 x1 (extended version)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(OpCodes.Add);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+      {
+        // exp(x1) * exp(x1) = exp(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+
+        codeGen.Emit1(OpCodes.Mul);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreEqual(h1, h2);
+      }
+      {
+        // inv(x1) + inv(x1) != inv(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Inv);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Inv);
+
+        codeGen.Emit1(OpCodes.Add);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Inv);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreNotEqual(h1, h2);
+      }
+
+      {
+        // exp(x1) + exp(x1) != exp(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+
+        codeGen.Emit1(OpCodes.Add);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Exp);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreNotEqual(h1, h2);
+      }
+      {
+        // log(x1) + log(x1) != log(x1)
+        var codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Log);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Log);
+
+        codeGen.Emit1(OpCodes.Add);
+
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h1 = ExprHashSymbolic.GetHash(code, nParams);
+
+        codeGen = new CodeGenerator();
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst0);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadConst1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.LoadParamN);
+        codeGen.Emit2(MctsSymbolicRegression.OpCodes.LoadVar, 1);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Log);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Mul);
+        codeGen.Emit1(MctsSymbolicRegression.OpCodes.Add);
+        codeGen.Emit1(OpCodes.Exit);
+        codeGen.GetCode(out code, out nParams);
+        var h2 = ExprHashSymbolic.GetHash(code, nParams);
+
+        Assert.AreNotEqual(h1, h2);
+      }
+
+    }
+    #endregion
+
+    #region number of solutions
+    // the algorithm should visits each solution only once
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegNumberOfSolutionsOneVariable() {
+      // this problem has only one variable
+      var provider = new NguyenInstanceProvider();
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F1 ")));
+      {
+        // possible solutions with max one variable reference:
+        // x
+        // log(x)
+        // exp(x)
+        // 1/x
+        TestMctsNumberOfSolutions(regProblem, 1, 4);
+      }
+      {
+        // possible solutions with max 4 variable references:
+        // without exp, log and inv
+        // x       
+        // x*x
+        // x*x*x
+        // x+x*x
+        // x+x*x*x
+        // x*x*x*x
+
+        TestMctsNumberOfSolutions(regProblem, 4, 6, allowLog: false, allowInv: false, allowExp: false);
+      }
+      {
+        // possible solutions with max 5 variable references:
+        // without exp, log and inv
+        // x       
+        // xx
+        // xxx
+        // x+xx
+        // xxxx
+        // x+xxx
+        // xxxxx 
+        // x+xxxx
+        // xx+xxx
+        TestMctsNumberOfSolutions(regProblem, 5, 9, allowLog: false, allowInv: false, allowExp: false);
+      }
+      {
+        // possible solutions with max two variable references:
+        // x
+        // log(x+c)
+        // exp(x)
+        // 1/(x+c)
+        //              -- 4
+        // x * x
+        // x * log(x+c)
+        // x * exp(x)
+        // x * 1/(x + c)
+        // x + log(x+c)
+        // x + exp(x)
+        // x + 1/(x+c)
+        //              -- 7
+        // log(x + c) * log(x + c)
+        // log(x + c) * exp(x)
+        // log(x + c) * 1/(x + c)
+        // log(x + c) + log(x + c)                        
+        // log(x + c) + exp(x)
+        // log(x + c) + 1/(x+c)
+        //              -- 6
+        // exp(cx) * 1/(x+c)
+        // exp(cx) + exp(cx)                              
+        // exp(cx) + 1/(x+c)
+        //              -- 3
+        // 1/(x+c) * 1/(x+c)                              
+        // 1/(x+c) + 1/(x+c)                              
+        //              -- 2
+        // log(x*x)
+        // exp(x*x)
+        // inv(x*x+c)
+        //              -- 3
+
+
+        TestMctsNumberOfSolutions(regProblem, 2, 25);
+      }
+      {
+        // possible solutions with max three variable references:
+        // without log and inv
+        // x
+        // exp(x)
+        //              -- 2
+        // x * x
+        // x * exp(x)
+        // x + exp(x)
+        // exp(x) + exp(x)
+        // exp(x*x)
+        //              -- 5
+        // x * x * x
+        // x + x * x                                       
+        // x * x * exp(x)
+        // x + x * exp(x)                                   
+        // exp(x) + x*x
+        // exp(x) + x*exp(x)                                
+        // x + exp(x) + exp(x)                              
+        // x * exp(x*x)
+        // x + exp(x*x)
+        //              -- 9
+
+        // exp(x) + exp(x) + exp(x)                         
+        //              -- 1
+
+        // exp(x)   * exp(x*x)
+        // exp(x)   + exp(x*x)
+        //              -- 2
+        // exp(x*x*x)
+        //              -- 1
+        TestMctsNumberOfSolutions(regProblem, 3, 2+5+9+1+2+1, allowLog: false, allowInv: false);
+      }      
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegNumberOfSolutionsTwoVariables() {
+      // this problem has only two input variables
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider();
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F9 ")));
+      {
+        // possible solutions with max one variable reference:
+        // x
+        // log(x)
+        // exp(x)
+        // 1/x
+        // y
+        // log(y)
+        // exp(y)
+        // 1/y
+        TestMctsNumberOfSolutions(regProblem, 1, 8);
+      }
+      {
+        // possible solutions with max one variable reference:
+        // without log and inv
+
+        // x
+        // exp(x)
+        // y
+        // exp(y)
+        TestMctsNumberOfSolutions(regProblem, 1, 4, allowLog: false, allowInv: false);
+      }
+      {
+        // possible solutions with max two variable references:
+        // without log and inv
+
+        // x
+        // y
+        // exp(x)
+        // exp(y)
+        //                  -- 4
+        // x (*) x
+        // x (*|+) exp(x)
+        // x (*|+) y
+        // x (*|+) exp(y)
+        //                  -- 7
+        // exp(x) (+) exp(x)
+        // exp(x) (*|+) exp(y)
+        //                  -- 3
+        // y (*) y
+        // y (*|+) exp(x)
+        // y (*|+) exp(y)
+        //                  -- 5
+        // exp(y) (+) exp(y)
+        //                  -- 1
+        //
+        // exp(x*x)
+        // exp(x*y)
+        // exp(y*y)
+        //                  -- 3
+
+        TestMctsNumberOfSolutions(regProblem, 2, 4 + 7 + 3 + 5 + 1 + 3, allowLog: false, allowInv: false);
+      }
+
+      {
+        // possible solutions with max two variable references:
+        // without exp and sum
+        // x
+        // y
+        // log(x)
+        // log(y)
+        // inv(x)
+        // inv(y)
+        //              -- 6
+        // x * x
+        // x * y
+        // x * log(x)
+        // x * log(y)
+        // x * inv(x)
+        // x * inv(y)
+        //              -- 6
+        // log(x) * log(x)
+        // log(x) * log(y)
+        // log(x) * inv(x)
+        // log(x) * inv(y)
+        //              -- 4
+        // inv(x) * inv(x)
+        // inv(x) * inv(y)
+        //              -- 2
+        // y * y
+        // y * log(x)
+        // y * log(y)
+        // y * inv(x)
+        // y * inv(y)
+        //              -- 5
+        // log(y) * log(y)
+        // log(y) * inv(x)
+        // log(y) * inv(y)
+        //              -- 3
+        // inv(y) * inv(y)
+        //              -- 1
+        // log(x*x)
+        // log(x*y)
+        // log(y*y)
+
+        // inv(x*x)
+        // inv(x*y)
+        // inv(y*y)
+        //             -- 6
+        // log(x+y)
+        // inv(x+y)
+        //             -- 2
+        TestMctsNumberOfSolutions(regProblem, 2, 6 + 6 + 4 + 2 + 5 + 3 + 1 + 6 + 2, allowExp: false, allowSum: false);
+      }
+    }
+    #endregion
+
+
+    #region test structure search (no constants)
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Nguyen1() {
+      // x³ + x² + x
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F1 ")));
+      TestMctsWithoutConstants(regProblem, nVarRefs: 10, allowExp: false, allowLog: false, allowInv: false);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Nguyen2() {
+      // x^4 + x³ + x² + x
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F2 ")));
+      TestMctsWithoutConstants(regProblem, allowExp: false, allowLog: false, allowInv: false);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Nguyen3() {
+      // x^5 + x^4 + x³ + x² + x
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F3 ")));
+      TestMctsWithoutConstants(regProblem, nVarRefs: 15, iterations: 1000000, allowExp: false, allowLog: false, allowInv: false);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Nguyen4() {
+      // x^6 + x^5 + x^4 + x³ + x² + x
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F4 ")));
+      TestMctsWithoutConstants(regProblem, nVarRefs: 25, iterations: 1000000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Nguyen7() {
+      // log(x + 1) + log(x² + 1)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F7 ")));
+      TestMctsWithoutConstants(regProblem, nVarRefs: 10, iterations: 100000, allowExp: false, allowLog: true, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Poly10_Part1() {
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.VariousInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Poly-10")));
+
+      //  Y = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9 + X3*X6*X10
+      //  Y' = X1*X2 + X3*X4 + X5*X6
+      // simplify problem by changing target
+      var ds = ((Dataset)regProblem.Dataset).ToModifiable();
+      var ys = ds.GetDoubleValues("Y").ToArray();
+      var x1 = ds.GetDoubleValues("X1").ToArray();
+      var x2 = ds.GetDoubleValues("X2").ToArray();
+      var x3 = ds.GetDoubleValues("X3").ToArray();
+      var x4 = ds.GetDoubleValues("X4").ToArray();
+      var x5 = ds.GetDoubleValues("X5").ToArray();
+      var x6 = ds.GetDoubleValues("X6").ToArray();
+      var x7 = ds.GetDoubleValues("X7").ToArray();
+      var x8 = ds.GetDoubleValues("X8").ToArray();
+      var x9 = ds.GetDoubleValues("X9").ToArray();
+      var x10 = ds.GetDoubleValues("X10").ToArray();
+      for (int i = 0; i < ys.Length; i++) {
+        ys[i] -= x1[i] * x7[i] * x9[i];
+        ys[i] -= x3[i] * x6[i] * x10[i];
+      }
+      ds.ReplaceVariable("Y", ys.ToList());
+
+      var modifiedProblemData = new RegressionProblemData(ds, regProblem.AllowedInputVariables, regProblem.TargetVariable);
+
+
+      TestMctsWithoutConstants(modifiedProblemData, nVarRefs: 15, iterations: 100000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Poly10_Part2() {
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.VariousInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Poly-10")));
+
+      //  Y = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9 + X3*X6*X10
+      //  Y' = X1*X7*X9 + X3*X6*X10
+      // simplify problem by changing target
+      var ds = ((Dataset)regProblem.Dataset).ToModifiable();
+      var ys = ds.GetDoubleValues("Y").ToArray();
+      var x1 = ds.GetDoubleValues("X1").ToArray();
+      var x2 = ds.GetDoubleValues("X2").ToArray();
+      var x3 = ds.GetDoubleValues("X3").ToArray();
+      var x4 = ds.GetDoubleValues("X4").ToArray();
+      var x5 = ds.GetDoubleValues("X5").ToArray();
+      var x6 = ds.GetDoubleValues("X6").ToArray();
+      var x7 = ds.GetDoubleValues("X7").ToArray();
+      var x8 = ds.GetDoubleValues("X8").ToArray();
+      var x9 = ds.GetDoubleValues("X9").ToArray();
+      var x10 = ds.GetDoubleValues("X10").ToArray();
+      for (int i = 0; i < ys.Length; i++) {
+        ys[i] -= x1[i] * x2[i];
+        ys[i] -= x3[i] * x4[i];
+        ys[i] -= x5[i] * x6[i];
+      }
+      ds.ReplaceVariable("Y", ys.ToList());
+
+      var modifiedProblemData = new RegressionProblemData(ds, regProblem.AllowedInputVariables, regProblem.TargetVariable);
+
+
+      TestMctsWithoutConstants(modifiedProblemData, nVarRefs: 15, iterations: 100000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Poly10_Part3() {
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.VariousInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Poly-10")));
+
+      //  Y = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9 + X3*X6*X10
+      //  Y' = X1*X2 + X1*X7*X9
+      // simplify problem by changing target
+      var ds = ((Dataset)regProblem.Dataset).ToModifiable();
+      var ys = ds.GetDoubleValues("Y").ToArray();
+      var x1 = ds.GetDoubleValues("X1").ToArray();
+      var x2 = ds.GetDoubleValues("X2").ToArray();
+      var x3 = ds.GetDoubleValues("X3").ToArray();
+      var x4 = ds.GetDoubleValues("X4").ToArray();
+      var x5 = ds.GetDoubleValues("X5").ToArray();
+      var x6 = ds.GetDoubleValues("X6").ToArray();
+      var x7 = ds.GetDoubleValues("X7").ToArray();
+      var x8 = ds.GetDoubleValues("X8").ToArray();
+      var x9 = ds.GetDoubleValues("X9").ToArray();
+      var x10 = ds.GetDoubleValues("X10").ToArray();
+      for (int i = 0; i < ys.Length; i++) {
+        ys[i] -= x3[i] * x4[i];
+        ys[i] -= x5[i] * x6[i];
+        ys[i] -= x3[i] * x6[i] * x10[i];
+      }
+      ds.ReplaceVariable("Y", ys.ToList());
+
+      var modifiedProblemData = new RegressionProblemData(ds, regProblem.AllowedInputVariables, regProblem.TargetVariable);
+
+
+      TestMctsWithoutConstants(modifiedProblemData, nVarRefs: 15, iterations: 100000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Poly10_Part4() {
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.VariousInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Poly-10")));
+
+      //  Y = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9 + X3*X6*X10
+      //  Y' = X3*X4 + X5*X6 + X3*X6*X10
+      // simplify problem by changing target
+      var ds = ((Dataset)regProblem.Dataset).ToModifiable();
+      var ys = ds.GetDoubleValues("Y").ToArray();
+      var x1 = ds.GetDoubleValues("X1").ToArray();
+      var x2 = ds.GetDoubleValues("X2").ToArray();
+      var x3 = ds.GetDoubleValues("X3").ToArray();
+      var x4 = ds.GetDoubleValues("X4").ToArray();
+      var x5 = ds.GetDoubleValues("X5").ToArray();
+      var x6 = ds.GetDoubleValues("X6").ToArray();
+      var x7 = ds.GetDoubleValues("X7").ToArray();
+      var x8 = ds.GetDoubleValues("X8").ToArray();
+      var x9 = ds.GetDoubleValues("X9").ToArray();
+      var x10 = ds.GetDoubleValues("X10").ToArray();
+      for (int i = 0; i < ys.Length; i++) {
+        ys[i] -= x1[i] * x2[i];
+        ys[i] -= x1[i] * x7[i] * x9[i];
+      }
+      ds.ReplaceVariable("Y", ys.ToList());
+      var modifiedProblemData = new RegressionProblemData(ds, regProblem.AllowedInputVariables, regProblem.TargetVariable);
+
+
+      TestMctsWithoutConstants(modifiedProblemData, nVarRefs: 15, iterations: 100000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Poly10_Part5() {
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.VariousInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Poly-10")));
+
+      //  Y = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9 + X3*X6*X10
+      //  Y' = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9
+      // simplify problem by changing target
+      var ds = ((Dataset)regProblem.Dataset).ToModifiable();
+      var ys = ds.GetDoubleValues("Y").ToArray();
+      var x1 = ds.GetDoubleValues("X1").ToArray();
+      var x2 = ds.GetDoubleValues("X2").ToArray();
+      var x3 = ds.GetDoubleValues("X3").ToArray();
+      var x4 = ds.GetDoubleValues("X4").ToArray();
+      var x5 = ds.GetDoubleValues("X5").ToArray();
+      var x6 = ds.GetDoubleValues("X6").ToArray();
+      var x7 = ds.GetDoubleValues("X7").ToArray();
+      var x8 = ds.GetDoubleValues("X8").ToArray();
+      var x9 = ds.GetDoubleValues("X9").ToArray();
+      var x10 = ds.GetDoubleValues("X10").ToArray();
+      for (int i = 0; i < ys.Length; i++) {
+        ys[i] -= x3[i] * x6[i] * x10[i];
+      }
+      ds.ReplaceVariable("Y", ys.ToList());
+      var modifiedProblemData = new RegressionProblemData(ds, regProblem.AllowedInputVariables, regProblem.TargetVariable);
+
+
+      TestMctsWithoutConstants(modifiedProblemData, nVarRefs: 15, iterations: 100000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Poly10_Part6() {
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.VariousInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Poly-10")));
+
+      //  Y = X1*X2 + X3*X4 + X5*X6 + X1*X7*X9 + X3*X6*X10
+      //  Y' = X1*X2 + X3*X4 + X5*X6 + X3*X6*X10
+      // simplify problem by changing target
+      var ds = ((Dataset)regProblem.Dataset).ToModifiable();
+      var ys = ds.GetDoubleValues("Y").ToArray();
+      var x1 = ds.GetDoubleValues("X1").ToArray();
+      var x2 = ds.GetDoubleValues("X2").ToArray();
+      var x3 = ds.GetDoubleValues("X3").ToArray();
+      var x4 = ds.GetDoubleValues("X4").ToArray();
+      var x5 = ds.GetDoubleValues("X5").ToArray();
+      var x6 = ds.GetDoubleValues("X6").ToArray();
+      var x7 = ds.GetDoubleValues("X7").ToArray();
+      var x8 = ds.GetDoubleValues("X8").ToArray();
+      var x9 = ds.GetDoubleValues("X9").ToArray();
+      var x10 = ds.GetDoubleValues("X10").ToArray();
+      for (int i = 0; i < ys.Length; i++) {
+        ys[i] -= x1[i] * x7[i] * x9[i];
+      }
+      ds.ReplaceVariable("Y", ys.ToList());
+      var modifiedProblemData = new RegressionProblemData(ds, regProblem.AllowedInputVariables, regProblem.TargetVariable);
+
+
+      TestMctsWithoutConstants(modifiedProblemData, nVarRefs: 9, iterations: 100000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "long")]
+    public void MctsSymbReg_NoConstants_Poly10_250rows() {
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.VariousInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Poly-10")));
+      regProblem.TrainingPartition.Start = 0;
+      regProblem.TrainingPartition.End = regProblem.Dataset.Rows;
+      regProblem.TestPartition.Start = 0;
+      regProblem.TestPartition.End = 2;
+      TestMctsWithoutConstants(regProblem, nVarRefs: 15, iterations: 200000, allowExp: false, allowLog: false, allowInv: false);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "long")]
+    public void MctsSymbReg_NoConstants_Poly10_10000rows() {
+      // as poly-10 but more rows
+      var rand = new FastRandom(1234);
+      var x1 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x2 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x3 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x4 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x5 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x6 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x7 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x8 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x9 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var x10 = Enumerable.Range(0, 10000).Select(_ => rand.NextDouble()).ToList();
+      var ys = new List<double>();
+      for (int i = 0; i < x1.Count; i++) {
+        ys.Add(x1[i] * x2[i] + x3[i] * x4[i] + x5[i] * x6[i] + x1[i] * x7[i] * x9[i] + x3[i] * x6[i] * x10[i]);
+      }
+
+      var ds = new Dataset(new string[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "y" },
+        new[] { x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, ys });
+
+
+      var problemData = new RegressionProblemData(ds, new string[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" }, "y");
+
+      problemData.TrainingPartition.Start = 0;
+      problemData.TrainingPartition.End = problemData.Dataset.Rows;
+      problemData.TestPartition.Start = 0;
+      problemData.TestPartition.End = 2; // must not be empty
+
+
+      TestMctsWithoutConstants(problemData, nVarRefs: 15, iterations: 100000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_TwoVars() {
+
+      // y = x1 + x2 + x1*x2 + x1*x2*x2 + x1*x1*x2
+      var rand = new FastRandom(1234);
+      var x1 = Enumerable.Range(0, 100).Select(_ => rand.NextDouble()).ToList();
+      var x2 = Enumerable.Range(0, 100).Select(_ => rand.NextDouble()).ToList();
+      var ys = x1.Zip(x2, (x1i, x2i) => x1i + x2i + x1i * x2i + x1i * x2i * x2i + x1i * x1i * x2i).ToList();
+
+      var ds = new Dataset(new string[] { "a", "b", "y" }, new[] { x1, x2, ys });
+
+      var problemData = new RegressionProblemData(ds, new string[] { "a", "b" }, "y");
+
+
+      TestMctsWithoutConstants(problemData, nVarRefs: 10, iterations: 10000, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbReg_NoConstants_Misleading() {
+
+      // y = a + baaaaa (the effect of the second term should be very small)
+      // the alg will quickly find that a has big effect and will search below a
+      // since we prevent a + a... the algorithm must find the correct expression via a + b...
+      // however b has a small effect so the branch might not be identified as relevant
+
+      var rand = new FastRandom(1234);
+      var @as = Enumerable.Range(0, 100).Select(_ => rand.NextDouble()).ToList();
+      var bs = Enumerable.Range(0, 100).Select(_ => rand.NextDouble()).ToList();
+      var cs = Enumerable.Range(0, 100).Select(_ => rand.NextDouble() * 1.0e-3).ToList();
+      var ds = Enumerable.Range(0, 100).Select(_ => rand.NextDouble()).ToList();
+      var es = Enumerable.Range(0, 100).Select(_ => rand.NextDouble()).ToList();
+      var ys = new double[@as.Count];
+      for (int i = 0; i < ys.Length; i++)
+        ys[i] = @as[i] + bs[i] + @as[i] * bs[i] * cs[i];
+
+      var dataset = new Dataset(new string[] { "a", "b", "c", "d", "e", "y" }, new[] { @as, bs, cs, ds, es, ys.ToList() });
+
+      var problemData = new RegressionProblemData(dataset, new string[] { "a", "b", "c", "d", "e" }, "y");
+
+
+      TestMctsWithoutConstants(problemData, nVarRefs: 10, iterations: 10000, allowExp: false, allowLog: false, allowInv: false);
+    }
+    #endregion
+
+    #region restricted structure but including numeric constants
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegKeijzer7() {
+      // ln(x)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 7 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, allowExp: false, allowLog: true, allowInv: false);
+    }
+
+    /*
+    // [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen5() {
+      // sin(x²)cos(x) - 1
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider();
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F5 ")));
+      TestMcts(regProblem);
+    }
+    // [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen6() {
+      // sin(x) + sin(x + x²)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider();
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F6 ")));
+      TestMcts(regProblem);
+    }
+    */
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen7() {
+      //  log(x + 1) + log(x² + 1)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F7 ")));
+      TestMcts(regProblem, maxVariableReferences: 5, allowExp: false, allowLog: true, allowInv: false);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen8() {
+      // Sqrt(x)
+      // = x ^ 0.5
+      // = exp(0.5 * log(x))
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F8 ")));
+      TestMcts(regProblem, maxVariableReferences: 5, allowExp: true, allowLog: true, allowInv: false);
+    }
+    /*
+    // [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen9() {
+      //  sin(x) + sin(y²)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider();
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F9 ")));
+      TestMcts(regProblem);
+    }
+    // [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen10() {
+      // 2sin(x)cos(y)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider();
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F10 ")));
+      TestMcts(regProblem);
+    }
+    */
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen11() {
+      // x ^ y  , x > 0, y > 0   
+      // = exp(y * log(x))
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F11 ")));
+      TestMcts(regProblem, maxVariableReferences: 5, allowExp: true, allowLog: true, allowInv: false);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkNguyen12() {
+      // x^4 - x³ + y²/2 - y
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.NguyenInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("F12 ")));
+      TestMcts(regProblem, maxVariableReferences: 20, allowExp: false, allowLog: false, allowInv: false);
+    }
+
+    #endregion
+
+    #region keijzer
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "long")]
+    public void MctsSymbRegBenchmarkKeijzer5() {
+      // (30 * x * z) / ((x - 10)  * y²)
+      // = 30 x z / (xy² - y²)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 5 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, maxVariableReferences: 10, allowExp: false, allowLog: false, allowInv: true);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkKeijzer6() {
+      // Keijzer 6 f(x) = Sum(1 / i) From 1 to X  , x \in [0..120]
+      // we can only approximate this
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 6 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, maxVariableReferences: 20, allowExp: false, allowLog: false, allowInv: true);
+    }
+
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkKeijzer8() {
+      // sqrt(x)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 8 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, maxVariableReferences: 5, allowExp: true, allowLog: true, allowInv: false);
+    }
+
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkKeijzer9() {
+      // arcsinh(x)  i.e. ln(x + sqrt(x² + 1))
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 9 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, maxVariableReferences: 5, allowExp: true, allowLog: true, allowInv: false);
+    }
+
+    /* 
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkKeijzer11() {
+      // xy + sin( (x-1) (y-1) )
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider();
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 11 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, successThreshold: 0.99); // cannot solve this yet
+    }
+     */
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkKeijzer12() {
+      // x^4 - x³ + y² / 2 - y,  same as Nguyen 12             
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 12 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, maxVariableReferences: 15, allowExp: false, allowLog: false, allowInv: false);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkKeijzer14() {
+      // 8 / (2 + x² + y²)
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 14 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, maxVariableReferences: 10, allowExp: false, allowLog: false, allowInv: true);
+    }
+    [TestMethod]
+    [TestCategory("Algorithms.DataAnalysis")]
+    [TestProperty("Time", "short")]
+    public void MctsSymbRegBenchmarkKeijzer15() {
+      // x³ / 5 + y³ / 2 - y - x
+      var provider = new HeuristicLab.Problems.Instances.DataAnalysis.KeijzerInstanceProvider(seed: 1234);
+      var regProblem = provider.LoadData(provider.GetDataDescriptors().Single(x => x.Name.Contains("Keijzer 15 f(")));
+      // some Keijzer problem instances have very large test partitions (here we are not concerened about test performance)
+      if (regProblem.TestPartition.End - regProblem.TestPartition.Start > 1000) regProblem.TestPartition.End = regProblem.TestPartition.Start + 1000;
+      TestMcts(regProblem, maxVariableReferences: 10, allowExp: false, allowLog: false, allowInv: false);
+    }
+    #endregion
+
+    private void TestMcts(IRegressionProblemData problemData,
+      int iterations = 20000,
+      double successThreshold = 0.99999,
+      int maxVariableReferences = 5,
+      bool allowExp = true,
+      bool allowLog = true,
+      bool allowInv = true,
+      bool allowSum = true
+      ) {
+      var mctsSymbReg = new MctsSymbolicRegressionAlgorithm();
+      var regProblem = new RegressionProblem();
+      regProblem.ProblemDataParameter.Value = problemData;
+      #region Algorithm Configuration
+      mctsSymbReg.Problem = regProblem;
+      mctsSymbReg.Iterations = iterations;
+      mctsSymbReg.MaxVariableReferences = maxVariableReferences;
+
+      mctsSymbReg.SetSeedRandomly = false;
+      mctsSymbReg.Seed = 1234;
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("exp")), allowExp);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("log")), allowLog);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("1 /")), allowInv);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("t1(x) + t2(x) + ... ")), allowSum);
+
+      mctsSymbReg.ScaleVariables = true;
+      mctsSymbReg.ConstantOptimizationIterations = 0;
+
+      #endregion
+      RunAlgorithm(mctsSymbReg);
+
+      Console.WriteLine(mctsSymbReg.ExecutionTime);
+      var eps = 1.0 - successThreshold;
+      Assert.AreEqual(1.0, ((DoubleValue)mctsSymbReg.Results["Best solution quality (train)"].Value).Value, eps);
+      Assert.AreEqual(1.0, ((DoubleValue)mctsSymbReg.Results["Best solution quality (test)"].Value).Value, eps);
+    }
+
+
+    private void TestMctsWithoutConstants(IRegressionProblemData problemData,
+      int nVarRefs = 10,
+      int iterations = 200000, double successThreshold = 0.99999,
+      bool allowExp = true,
+      bool allowLog = true,
+      bool allowInv = true,
+      bool allowSum = true
+      ) {
+      var mctsSymbReg = new MctsSymbolicRegressionAlgorithm();
+      var regProblem = new RegressionProblem();
+      regProblem.ProblemDataParameter.Value = problemData;
+      #region Algorithm Configuration
+      mctsSymbReg.Problem = regProblem;
+      mctsSymbReg.Iterations = iterations;
+      mctsSymbReg.MaxVariableReferences = nVarRefs;
+      mctsSymbReg.SetSeedRandomly = false;
+      mctsSymbReg.Seed = 1234;
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("exp")), allowExp);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("log")), allowLog);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("1 /")), allowInv);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("t1(x) + t2(x) + ... ")), allowSum);
+
+      // no constants
+      mctsSymbReg.ScaleVariables = false;
+      mctsSymbReg.ConstantOptimizationIterations = -1;
+
+
+      #endregion
+      RunAlgorithm(mctsSymbReg);
+
+      Console.WriteLine(mctsSymbReg.ExecutionTime);
+      var eps = 1.0 - successThreshold;
+      Assert.AreEqual(1.0, ((DoubleValue)mctsSymbReg.Results["Best solution quality (train)"].Value).Value, eps);
+      Assert.AreEqual(1.0, ((DoubleValue)mctsSymbReg.Results["Best solution quality (test)"].Value).Value, eps);
+    }
+
+    private void TestMctsNumberOfSolutions(IRegressionProblemData problemData, int maxNumberOfVariables, int expectedNumberOfSolutions,
+      bool allowProd = true,
+      bool allowExp = true,
+      bool allowLog = true,
+      bool allowInv = true,
+      bool allowSum = true
+  ) {
+      var mctsSymbReg = new MctsSymbolicRegressionAlgorithm();
+      var regProblem = new RegressionProblem();
+      regProblem.ProblemDataParameter.Value = problemData;
+      #region Algorithm Configuration
+
+      mctsSymbReg.SetSeedRandomly = false;
+      mctsSymbReg.Seed = 1234;
+      mctsSymbReg.Problem = regProblem;
+      mctsSymbReg.Iterations = int.MaxValue; // stopping when all solutions have been enumerated
+      mctsSymbReg.MaxVariableReferences = maxNumberOfVariables;
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.StartsWith("x * y * ...")), allowProd);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("exp(c * x * y ...)")), allowExp);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("log(c + c1 x + c2 x + ...)")), allowLog);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("1 / (1 + c1 x + c2 x + ...)")), allowInv);
+      mctsSymbReg.AllowedFactors.SetItemCheckedState(mctsSymbReg.AllowedFactors.Single(s => s.Value.Contains("t1(x) + t2(x) + ... ")), allowSum);
+      #endregion
+      RunAlgorithm(mctsSymbReg);
+
+      Console.WriteLine(mctsSymbReg.ExecutionTime);
+      Assert.AreEqual(expectedNumberOfSolutions, ((IntValue)mctsSymbReg.Results["Effective rollouts"].Value).Value);
+    }
+
+
+    // same as in SamplesUtil
+    private void RunAlgorithm(IAlgorithm a) {
+      var trigger = new EventWaitHandle(false, EventResetMode.ManualReset);
+      Exception ex = null;
+      a.Stopped += (src, e) => { trigger.Set(); };
+      a.ExceptionOccurred += (src, e) => { ex = e.Value; trigger.Set(); };
+      a.Prepare();
+      a.Start();
+      trigger.WaitOne();
+
+      Assert.AreEqual(ex, null);
+    }
+
+  }
+}
Index: /branches/2796_SymbReg/Tests/Properties/AssemblyInfo.cs
===================================================================
--- /branches/2796_SymbReg/Tests/Properties/AssemblyInfo.cs	(revision 15650)
+++ /branches/2796_SymbReg/Tests/Properties/AssemblyInfo.cs	(revision 15650)
@@ -0,0 +1,36 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Test")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HP Inc.")]
+[assembly: AssemblyProduct("Test")]
+[assembly: AssemblyCopyright("Copyright © HP Inc. 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("80009a4f-f55f-4e95-ad44-5ab01551b964")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /branches/2796_SymbReg/Tests/Test.csproj
===================================================================
--- /branches/2796_SymbReg/Tests/Test.csproj	(revision 15650)
+++ /branches/2796_SymbReg/Tests/Test.csproj	(revision 15650)
@@ -0,0 +1,124 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{80009A4F-F55F-4E95-AD44-5AB01551B964}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Test</RootNamespace>
+    <AssemblyName>Test</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+    <IsCodedUITest>False</IsCodedUITest>
+    <TestProjectType>UnitTest</TestProjectType>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="ALGLIB-3.7.0, Version=3.7.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\trunk\sources\bin\ALGLIB-3.7.0.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Algorithms.DataAnalysis-3.4">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Algorithms.DataAnalysis-3.4.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Collections-3.3">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Collections-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Common-3.3">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Common-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Core-3.3">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Core-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Data-3.3">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Data-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Optimization-3.3">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Optimization-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis-3.4">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Problems.DataAnalysis-3.4.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.Instances-3.3">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Problems.Instances-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.Instances.DataAnalysis-3.3">
+      <HintPath>..\..\..\trunk\sources\bin\HeuristicLab.Problems.Instances.DataAnalysis-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\sources\bin\HeuristicLab.Random-3.3.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+  </ItemGroup>
+  <Choose>
+    <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+      </ItemGroup>
+    </When>
+    <Otherwise>
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
+      </ItemGroup>
+    </Otherwise>
+  </Choose>
+  <ItemGroup>
+    <Compile Include="HeuristicLab.Algorithms.DataAnalysis-3.4\MctsSymbolicRegressionTest.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HeuristicLab.Algorithms.DataAnalysis\3.4\HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg.csproj">
+      <Project>{d97f91b5-b71b-412d-90a5-177eed948a3a}</Project>
+      <Name>HeuristicLab.Algorithms.DataAnalysis.MCTSSymbReg</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Choose>
+    <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+      </ItemGroup>
+    </When>
+  </Choose>
+  <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
