Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Analyzers/BestSolutionAnalyzer.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Analyzers/BestSolutionAnalyzer.cs	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Analyzers/BestSolutionAnalyzer.cs	(revision 14321)
@@ -0,0 +1,150 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 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.Collections.Generic;
+using System.Linq;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.RealVectorEncoding;
+using HeuristicLab.Operators;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.ProcessParameterOptimization {
+  [StorableClass]
+  [Item("BestSolutionAnalyzer", "An analyzer which identifies the best solution from the SingleObjectiveProcessParameterOptimizationProblem")]
+  public class BestSolutionAnalyzer : SingleSuccessorOperator, IAnalyzer {
+    private const string RowParameterName = "Row";
+    private const string ProblemDataParameterName = "ProblemData";
+    private const string QualityParameterName = "Quality";
+    private const string MaximizationParameterName = "Maximization";
+
+    public bool EnabledByDefault {
+      get { return true; }
+    }
+    public IScopeTreeLookupParameter<DoubleValue> QualityParameter {
+      get { return (IScopeTreeLookupParameter<DoubleValue>)Parameters[QualityParameterName]; }
+    }
+    public ILookupParameter<BoolValue> MaximizationParameter {
+      get { return (ILookupParameter<BoolValue>)Parameters[MaximizationParameterName]; }
+    }
+    public IFixedValueParameter<StringValue> BestSolutionResultNameParameter {
+      get { return (IFixedValueParameter<StringValue>)Parameters["BestSolution ResultName"]; }
+    }
+    public ILookupParameter<DoubleValue> BestKnownQualityParameter {
+      get { return (ILookupParameter<DoubleValue>)Parameters["BestKnownQuality"]; }
+    }
+    public ILookupParameter<ResultCollection> ResultsParameter {
+      get { return (ILookupParameter<ResultCollection>)Parameters["Results"]; }
+    }
+    public ILookupParameter<IntValue> RowParameter {
+      get { return (ILookupParameter<IntValue>)Parameters[RowParameterName]; }
+    }
+    public ILookupParameter<IRegressionProblemData> ProblemDataParameter {
+      get { return (ILookupParameter<IRegressionProblemData>)Parameters[ProblemDataParameterName]; }
+    }
+
+    public BoolValue Maximization {
+      get { return MaximizationParameter.ActualValue; }
+    }
+
+    public ItemArray<DoubleValue> Quality {
+      get { return QualityParameter.ActualValue; }
+    }
+
+    public ResultCollection Results {
+      get { return ResultsParameter.ActualValue; }
+    }
+
+    public string BestSolutionResultName {
+      get { return BestSolutionResultNameParameter.Value.Value; }
+      set { BestSolutionResultNameParameter.Value.Value = value; }
+    }
+
+    public BestSolutionAnalyzer() {
+      Parameters.Add(new FixedValueParameter<StringValue>("BestSolution ResultName", "The name of the result for storing the best solution.", new StringValue("Best Solution")));
+      Parameters.Add(new LookupParameter<DoubleValue>("BestKnownQuality", "The quality of the best known solution."));
+      Parameters.Add(new LookupParameter<IntValue>(RowParameterName, "The current row"));
+      Parameters.Add(new LookupParameter<IRegressionProblemData>(ProblemDataParameterName, "The problem data"));
+      Parameters.Add(new LookupParameter<BoolValue>(MaximizationParameterName, "Specifies whether the problem is a minimization or a maximization problem."));
+      Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>(QualityParameterName, "The qualities of the individuals in the population"));
+      Parameters.Add(new LookupParameter<ResultCollection>("Results", "The result collection"));
+    }
+
+    protected BestSolutionAnalyzer(BestSolutionAnalyzer original, Cloner cloner) : base(original, cloner) { }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new BestSolutionAnalyzer(this, cloner);
+    }
+
+    [StorableConstructor]
+    protected BestSolutionAnalyzer(bool deserializing) : base(deserializing) { }
+
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if (!Parameters.ContainsKey(MaximizationParameterName))
+        Parameters.Add(new LookupParameter<BoolValue>(MaximizationParameterName, "Specifies whether the problem is a minimization or a maximization problem."));
+      if (!Parameters.ContainsKey(QualityParameterName))
+        Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>(QualityParameterName, "The qualities of the individuals in the population"));
+      if (!Parameters.ContainsKey("Results"))
+        Parameters.Add(new LookupParameter<ResultCollection>("Results", "The result collection"));
+    }
+
+    public override IOperation Apply() {
+      IEnumerable<IScope> scopes = new[] { ExecutionContext.Scope };
+      var zipped = Quality.Select((x, index) => new { Index = index, x.Value });
+      var best = Maximization.Value ? zipped.OrderBy(x => x.Value).First() : zipped.OrderByDescending(x => x.Value).First();
+      for (int j = 0; j < QualityParameter.Depth; j++)
+        scopes = scopes.SelectMany(x => x.SubScopes);
+      IScope currentBestScope = scopes.ToList()[best.Index];
+      var bestSolution = (RealVector)currentBestScope.Variables["RealVector"].Value;
+
+      var bestSolutionMatrix = new DoubleMatrix(bestSolution.Length, 3);
+      var targetNames = bestSolution.ElementNames.ToList();
+      bestSolutionMatrix.RowNames = targetNames;
+      bestSolutionMatrix.ColumnNames = new[] { "Estimated value", "Target value", "Deviation" };
+
+      var problemData = ProblemDataParameter.ActualValue;
+      var row = RowParameter.ActualValue.Value;
+
+      for (int i = 0; i < bestSolution.Length; ++i) {
+        var estimatedValue = bestSolution[i];
+        var targetValue = problemData.Dataset.GetDoubleValue(targetNames[i], row);
+        bestSolutionMatrix[i, 0] = estimatedValue;
+        bestSolutionMatrix[i, 1] = targetValue;
+        bestSolutionMatrix[i, 2] = estimatedValue - targetValue;
+      }
+
+      if (!Results.ContainsKey(BestSolutionResultName)) {
+        Results.Add(new Result(BestSolutionResultName, bestSolutionMatrix));
+      } else {
+        var result = Results[BestSolutionResultName];
+        result.Value = bestSolutionMatrix;
+      }
+
+      return base.Apply();
+    }
+  }
+}
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingProblem.csproj
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingProblem.csproj	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingProblem.csproj	(revision 14321)
@@ -0,0 +1,221 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{79271BC8-4446-40E2-BB89-9BE4E17174FE}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.ProcessParameterOptimization</RootNamespace>
+    <AssemblyName>HeuristicLab.ProcessParameterOptimization-3.3</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+  </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>
+    <Prefer32Bit>false</Prefer32Bit>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+  </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>
+    <Prefer32Bit>false</Prefer32Bit>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SignAssembly>true</SignAssembly>
+  </PropertyGroup>
+  <PropertyGroup>
+    <AssemblyOriginatorKeyFile>HeuristicLab.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <CodeAnalysisLogFile>HeuristicLab trunk sources\binDebug\Plugin.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
+    <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
+    <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
+    <CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
+    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
+    <CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <CodeAnalysisLogFile>HeuristicLab trunk sources\binRelease\Plugin.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
+    <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
+    <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
+    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
+    <CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <CodeAnalysisLogFile>HeuristicLab trunk sources\binDebug\Plugin.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
+    <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
+    <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
+    <CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
+    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
+    <CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>..\..\..\..\trunk\sources\bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <CodeAnalysisLogFile>HeuristicLab trunk sources\binRelease\Plugin.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
+    <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
+    <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
+    <CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
+    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
+    <CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
+    <CodeAnalysisFailOnMissingRules>false</CodeAnalysisFailOnMissingRules>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="HeuristicLab.Analysis-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\Projects\HeuristicLab\trunk\sources\HeuristicLab trunk sources\binHeuristicLab.Analysis-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Collections-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Collections-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Common-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Common-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Core-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Core-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Data-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Data-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Encodings.RealVectorEncoding-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Encodings.RealVectorEncoding-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Operators-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Operators-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Optimization-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Optimization-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Parameters-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Parameters-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Persistence-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Persistence-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.PluginInfrastructure-3.3">
+      <HintPath>C:\Program Files\HeuristicLab 3.3\PluginInfrastructure-3.3.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis-3.4, Version=3.4.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Problems.DataAnalysis-3.4.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis.Symbolic-3.4, Version=3.4.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Problems.DataAnalysis.Symbolic-3.4.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="HeuristicLab.Problems.DataAnalysis.Symbolic.Regression-3.4, Version=3.4.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab trunk sources\binHeuristicLab.Problems.DataAnalysis.Symbolic.Regression-3.4.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Plugin.cs.frame" />
+    <Compile Include="Analyzers\BestSolutionAnalyzer.cs" />
+    <Compile Include="IGoalSeekingProblem.cs" />
+    <Compile Include="Plugin.cs" />
+    <Compile Include="MultiObjectiveGoalSeekingProblem.cs" />
+    <Compile Include="SingleObjectiveGoalSeekingProblem.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <None Include="Properties\AssemblyInfo.cs.frame" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="HeuristicLab.snk" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PreBuildEvent>set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
+set ProjectDir=$(ProjectDir)
+set SolutionDir=$(SolutionDir)
+set Outdir=$(Outdir)
+
+call PreBuildEvent.cmd</PreBuildEvent>
+  </PropertyGroup>
+  <PropertyGroup>
+    <PostBuildEvent>
+    </PostBuildEvent>
+  </PropertyGroup>
+  <!-- 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>
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/IGoalSeekingProblem.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/IGoalSeekingProblem.cs	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/IGoalSeekingProblem.cs	(revision 14321)
@@ -0,0 +1,86 @@
+﻿#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 HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.ProcessParameterOptimization {
+  public interface IGoalSeekingProblem : IProblem {
+    IRegressionProblemData ProblemData { get; set; }
+    double GetVariableValue(string variableName);
+    void SetVariableValue(string variableName, double variableValue);
+
+    //rounding ? 
+    double[] GetEstimatedGoalValues(IEnumerable<double> parameters, bool round = false);
+    int Row { get; set; }
+
+    //Models?
+    IEnumerable<IRegressionModel> Models { get; }
+    void AddModel(IRegressionModel model);
+    void RemoveModel(IRegressionModel model);
+    event EventHandler ModelsChanged;
+
+    #region targets
+    IEnumerable<string> ActiveTargets { get; }
+    IEnumerable<string> Targets { get; }
+    ICheckedItemList<StringValue> TargetList { get; }
+    double GetTargetGoal(string target);
+    void SetTargetGoal(string target, double goal);
+
+    //variance ?
+    double GetTargetWeight(string target);
+    void SetTargetWeight(string target, double weight);
+
+    double GetTargetVariance(string target);
+    void SetTargetVariance(string target, double variance);
+
+    double GetTargetStepSize(string target);
+    void SetTargetStepSize(string target, double stepSize);
+
+    event EventHandler TargetsChanged;
+    #endregion
+
+    #region parameters
+    IEnumerable<string> ActiveParameters { get; }
+    ICheckedItemList<StringValue> ControllableParameters { get; }
+    DoubleMatrix ControllableParameterBounds { get; }
+
+    IEnumerable<string> GetControllableParameters();
+    void SetControllableParameters(IEnumerable<string> parameterNames);
+
+    bool GetParameterActive(string parameter);
+    void SetParameterActive(string parameter, bool active);
+
+    double[] GetParameterBounds(string parameterName);
+    void SetParameterBounds(string parameterName, double min, double max, double step);
+    // set get ParameterStepSize, set get parameters active
+    // parameters changed event
+    double GetParameterStepSize(string parameter);
+    void SetParameterStepSize(string parameter, double stepSize);
+
+    event EventHandler ParametersChanged;
+    #endregion
+  }
+}
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/MultiObjectiveGoalSeekingProblem.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/MultiObjectiveGoalSeekingProblem.cs	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/MultiObjectiveGoalSeekingProblem.cs	(revision 14321)
@@ -0,0 +1,730 @@
+﻿#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.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.RealVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.ProcessParameterOptimization {
+  [Item("Process parameter optimization problem (multi-objective)", "Represents a single objective optimization problem which uses configurable regression models to evaluate targets from a given dataset.")]
+  [Creatable("Problems")]
+  [StorableClass]
+  public sealed class MultiObjectiveGoalSeekingProblem : MultiObjectiveBasicProblem<RealVectorEncoding>, IGoalSeekingProblem {
+    private const string ModifiableDatasetParameterName = "Dataset";
+    private const string ProblemDataParameterName = "ProblemData";
+    private const string ControllableParametersParameterName = "ControllableParameters";
+    private const string ControllableParameterBoundsParameterName = "ControllableParameterBounds";
+    private const string TargetGoalsParameterName = "TargetGoals";
+    private const string TargetsParameterName = "Targets";
+    private const string ModelCollectionParameterName = "ModelCollection";
+    private const string RowParameterName = "Row";
+    // these parameters are used by the pareto folding analyzer
+    private const string QualitySumCutoffParameterName = "QualitySumCutoff";
+
+    #region parameters
+    public IFixedValueParameter<DoubleValue> QualitySumCutoffParameter {
+      get { return (IFixedValueParameter<DoubleValue>)Parameters[QualitySumCutoffParameterName]; }
+    }
+    public IValueParameter<IRegressionProblemData> ProblemDataParameter {
+      get { return (IValueParameter<IRegressionProblemData>)Parameters[ProblemDataParameterName]; }
+    }
+    public IValueParameter<IDataset> ModifiableDatasetParameter {
+      get { return (IValueParameter<IDataset>)Parameters[ModifiableDatasetParameterName]; }
+    }
+    public IValueParameter<CheckedItemList<StringValue>> ControllableParametersParameter {
+      get { return (IValueParameter<CheckedItemList<StringValue>>)Parameters[ControllableParametersParameterName]; }
+    }
+    public IValueParameter<DoubleMatrix> ControllableParameterBoundsParameter {
+      get { return (IValueParameter<DoubleMatrix>)Parameters[ControllableParameterBoundsParameterName]; }
+    }
+    public IFixedValueParameter<ItemCollection<IRegressionModel>> ModelCollectionParameter {
+      get { return (IFixedValueParameter<ItemCollection<IRegressionModel>>)Parameters[ModelCollectionParameterName]; }
+    }
+    public IValueParameter<CheckedItemList<StringValue>> TargetsParameter {
+      get { return (IValueParameter<CheckedItemList<StringValue>>)Parameters[TargetsParameterName]; }
+    }
+    public IValueParameter<DoubleMatrix> TargetGoalsParameter {
+      get { return (IValueParameter<DoubleMatrix>)Parameters[TargetGoalsParameterName]; }
+    }
+    public IFixedValueParameter<IntValue> RowParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[RowParameterName]; }
+    }
+    #endregion
+
+    #region parameter properties
+    private IItemCollection<IRegressionModel> ModelCollection {
+      get { return ModelCollectionParameter.Value; }
+    }
+    public DoubleMatrix TargetGoals {
+      get { return TargetGoalsParameter.Value; }
+      set { TargetGoalsParameter.Value = value; }
+    }
+    public double QualitySumCutoff {
+      get { return QualitySumCutoffParameter.Value.Value; }
+      set { QualitySumCutoffParameter.Value.Value = value; }
+    }
+    #endregion
+
+    #region IProcessParameterOptimizationProblem properties
+    [Storable]
+    private IRegressionProblemData problemData;
+    public IRegressionProblemData ProblemData {
+      get { return problemData; }
+      set {
+        if (value == null || value == problemData) return;
+        var variables = value.Dataset.DoubleVariables.ToList();
+        if (Models.Any()) {
+          var targets = Models.Select(x => x.TargetVariable);
+          var hashset = new HashSet<string>(variables);
+          foreach (var target in targets) {
+            if (!hashset.Contains(target)) {
+              throw new ArgumentException(string.Format("Incompatible problem data. Target \"{0}\" is missing.", target));
+            }
+          }
+        }
+        problemData = value;
+        dataset = new ModifiableDataset(variables, variables.Select(x => new List<double> { ProblemData.Dataset.GetDoubleValue(x, Row) }));
+        ProblemDataParameter.Value = ProblemData;
+        UpdateControllableParameters();
+        UpdateTargetList();
+      }
+    }
+
+    public int Row {
+      get { return RowParameter.Value.Value; }
+      set { RowParameter.Value.Value = value; }
+    }
+
+    public IEnumerable<IRegressionModel> Models {
+      get { return ModelCollectionParameter.Value; }
+    }
+
+    #region targets
+    public IEnumerable<string> ActiveTargets {
+      get { return TargetList.CheckedItems.Select(x => x.Value.Value); }
+    }
+
+    public IEnumerable<string> Targets {
+      get { return TargetList.Select(x => x.Value); }
+    }
+
+    public ICheckedItemList<StringValue> TargetList {
+      get { return TargetsParameter.Value; }
+      set { TargetsParameter.Value = (CheckedItemList<StringValue>)value; }
+    }
+    #endregion
+
+    #region parameters
+    public IEnumerable<string> ActiveParameters {
+      get { return ControllableParameters.CheckedItems.Select(x => x.Value.Value); }
+    }
+
+    public ICheckedItemList<StringValue> ControllableParameters {
+      get { return ControllableParametersParameter.Value; }
+      set { ControllableParametersParameter.Value = (CheckedItemList<StringValue>)value; }
+    }
+
+    public DoubleMatrix ControllableParameterBounds {
+      get { return ControllableParameterBoundsParameter.Value; }
+      set { ControllableParameterBoundsParameter.Value = value; }
+    }
+    #endregion
+    #endregion
+
+    #region IProcessParameterOptimizationProblem methods
+    #region models
+    public double GetVariableValue(string variableName) {
+      return dataset.GetDoubleValue(variableName, 0);
+    }
+
+    public void SetVariableValue(string variableName, double variableValue) {
+      dataset.SetVariableValue(variableValue, variableName, 0); // will throw if the variable is not present in the dataset
+    }
+
+    public double[] GetEstimatedGoalValues(IEnumerable<double> parameters, bool round = false) {
+      var ds = (ModifiableDataset)dataset.Clone();
+      var estimatedValues = new double[ActiveTargets.Count()];
+      var e1 = parameters.GetEnumerator();
+      var e2 = ActiveParameters.GetEnumerator();
+      while (e1.MoveNext() && e2.MoveNext())
+        ds.SetVariableValue(e1.Current, e2.Current, 0);
+      var rows = new[] { 0 }; // actually just one row
+      int i = 0;
+      var models = ModelCollection.ToList();
+      foreach (var target in TargetList.CheckedItems) {
+        var average = 0d;
+        var count = 0;
+        var targetName = target.Value.Value;
+        foreach (var model in models) {
+          if (targetName != model.TargetVariable)
+            continue;
+          average += model.GetEstimatedValues(ds, rows).Single();
+          count++;
+        }
+        var estimatedValue = average / count;
+        if (round) {
+          var accuracy = GetTargetStepSize(targetName);
+          estimatedValues[i] = RoundToNearestStepMultiple(estimatedValue, accuracy);
+        } else {
+          estimatedValues[i] = estimatedValue;
+        }
+        ++i;
+      }
+      return estimatedValues;
+    }
+
+    public void AddModel(IRegressionModel model) {
+      var target = model.TargetVariable;
+      CheckIfDatasetContainsTarget(target);
+      ModelCollection.Add(model);
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    // method which throws an exception that can be caught in the event handler if the check fails
+    private void CheckIfDatasetContainsTarget(string target) {
+      if (dataset.DoubleVariables.All(x => x != target))
+        throw new ArgumentException(string.Format("Model target \"{0}\" does not exist in the dataset.", target));
+    }
+
+    public void RemoveModel(IRegressionModel model) {
+      ModelCollection.Remove(model);
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    public event EventHandler ModelsChanged;
+    private void OnModelsChanged(object sender, EventArgs args) {
+      var changed = ModelsChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+    #endregion
+
+    #region targets
+    public bool GetTargetActive(string target) {
+      var item = TargetList.SingleOrDefault(x => x.Value == target);
+      if (item == null)
+        throw new ArgumentException(string.Format("SetTargetActive: Invalid target name {0}", target));
+      return TargetList.ItemChecked(item);
+    }
+
+    public void SetTargetActive(string target, bool active) {
+      var item = TargetList.SingleOrDefault(x => x.Value == target);
+      if (item == null)
+        throw new ArgumentException(string.Format("SetTargetActive: Invalid target name {0}", target));
+      TargetList.SetItemCheckedState(item, active);
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetGoal(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 0];
+    }
+
+    public void SetTargetGoal(string target, double goal) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 0] = goal;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetWeight(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 1];
+    }
+
+    public void SetTargetWeight(string target, double weight) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 1] = weight;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetVariance(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 2];
+    }
+
+    public void SetTargetVariance(string target, double variance) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 2] = variance;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetStepSize(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 3];
+    }
+
+    public void SetTargetStepSize(string target, double stepSize) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 3] = stepSize;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public event EventHandler TargetsChanged;
+    private void OnTargetsChanged(object sender, EventArgs args) {
+      var changed = TargetsChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+    #endregion // targets
+
+    #region process parameters
+    public void SetParameterBounds(string parameterName, double min, double max, double step) {
+      int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameterName).Count();
+      if (i < ControllableParameterBounds.Rows) {
+        ControllableParameterBounds[i, 0] = min;
+        ControllableParameterBounds[i, 1] = max;
+        ControllableParameterBounds[i, 2] = step;
+        UpdateEncoding();
+        OnParametersChanged(this, EventArgs.Empty);
+      } else {
+        throw new ArgumentException(string.Format("SetParameterBounds: Invalid parameter name {0}", parameterName));
+      }
+
+    }
+
+    public double GetParameterStepSize(string parameter) {
+      int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameter).Count();
+      if (i < ControllableParameterBounds.Rows)
+        return ControllableParameterBounds[i, 2];
+      throw new ArgumentException(string.Format("GetParameterStepSize: Invalid parameter name {0}", parameter));
+    }
+
+    public void SetParameterStepSize(string parameter, double stepSize) {
+      int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameter).Count();
+      if (i < ControllableParameterBounds.Rows) {
+        ControllableParameterBounds[i, 2] = stepSize;
+        OnParametersChanged(this, EventArgs.Empty);
+      } else {
+        throw new ArgumentException(string.Format("SetParameterStepSize: Invalid parameter name {0}", parameter));
+      }
+    }
+
+
+    public bool GetParameterActive(string parameter) {
+      var item = ControllableParameters.SingleOrDefault(x => x.Value == parameter);
+      if (item == null)
+        throw new ArgumentException(string.Format("GetParameterActive: Invalid target name {0}", parameter));
+      return ControllableParameters.ItemChecked(item);
+    }
+
+    public void SetParameterActive(string parameter, bool active) {
+      var item = ControllableParameters.SingleOrDefault(x => x.Value == parameter);
+      if (item == null)
+        throw new ArgumentException(string.Format("SetParameterActive: Invalid target name {0}", parameter));
+      ControllableParameters.SetItemCheckedState(item, active);
+      OnParametersChanged(this, EventArgs.Empty);
+    }
+
+    /// <summary>
+    /// Returns the parameter bounds (min and max) and the step size for the specified parameter
+    /// </summary>
+    /// <param name="parameterName"></param>
+    /// <returns>A double array containing the values (min, max, step) in this order</returns>
+    public double[] GetParameterBounds(string parameterName) {
+      var index = ControllableParameters.TakeWhile(x => x.Value != parameterName).Count();
+      if (index < ControllableParameters.Count) {
+        var min = ControllableParameterBounds[index, 0];
+        var max = ControllableParameterBounds[index, 1];
+        var step = ControllableParameterBounds[index, 2];
+        return new[] { min, max, step };
+      }
+      throw new ArgumentException(string.Format("GetParameterBounds: Unknown parameter {0}.", parameterName));
+    }
+
+    public void SetControllableParameters(IEnumerable<string> parameterNames) {
+      ControllableParameters = new CheckedItemList<StringValue>();
+      foreach (var v in parameterNames) {
+        ControllableParameters.Add(new StringValue(v), false);
+      }
+      ControllableParameters.CheckedItemsChanged += ControllableParameters_OnItemsChanged;
+      ControllableParameterBounds = new DoubleMatrix(ControllableParameters.Count, 3);
+      ControllableParameterBounds.RowNames = GetControllableParameters();
+      ControllableParameterBounds.ColumnNames = new[] { "Min", "Max", "Step" };
+
+      for (int i = 0; i < ControllableParameters.Count; ++i) {
+        var itemName = ControllableParameters[i].Value;
+        var values = ProblemData.Dataset.GetReadOnlyDoubleValues(itemName).Where(x => !double.IsNaN(x) && !double.IsInfinity(x)).ToList();
+        if (!values.Any()) continue;
+
+        // add a 20% margin to allow the optimization algorithm more freedom of exploration
+        ControllableParameterBounds[i, 0] = 0.8 * values.Min(); // min
+        ControllableParameterBounds[i, 1] = 1.2 * values.Max(); // max
+        ControllableParameterBounds[i, 2] = 1e-6;               // step
+      }
+      OnParametersChanged(this, EventArgs.Empty);
+    }
+
+    public IEnumerable<string> GetControllableParameters() {
+      return ControllableParameters.Select(x => x.Value);
+    }
+
+    public event EventHandler ParametersChanged;
+    private void OnParametersChanged(object sender, EventArgs args) {
+      var changed = ParametersChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+    #endregion // process parameters
+    #endregion // IProcessParameterOprimizationProblem methods
+
+    #region data members
+    [Storable]
+    private ModifiableDataset dataset; // modifiable dataset
+
+    [Storable]
+    private bool[] maximization;
+    public override bool[] Maximization {
+      get { return maximization ?? new bool[] { false }; }
+    }
+
+    public ValueParameter<BoolArray> MaximizationParameter {
+      get { return (ValueParameter<BoolArray>)Parameters["Maximization"]; }
+    }
+    #endregion
+
+    #region constructors
+    [StorableConstructor]
+    private MultiObjectiveGoalSeekingProblem(bool deserializing) : base(deserializing) { }
+
+    private MultiObjectiveGoalSeekingProblem(MultiObjectiveGoalSeekingProblem original, Cloner cloner)
+      : base(original, cloner) {
+      this.dataset = cloner.Clone(original.dataset);
+      this.problemData = cloner.Clone(original.problemData);
+
+      RegisterEvents();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new MultiObjectiveGoalSeekingProblem(this, cloner);
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      if (Parameters.ContainsKey("Accuracy"))
+        Parameters.Remove("Accuracy");
+
+      if (!Parameters.ContainsKey(QualitySumCutoffParameterName)) {
+        Parameters.Add(new FixedValueParameter<DoubleValue>(QualitySumCutoffParameterName, new DoubleValue(0.2)));
+        QualitySumCutoffParameter.Hidden = true;
+      }
+
+      if (ProblemData == null && Parameters.ContainsKey(ProblemDataParameterName)) {
+        ProblemData = ProblemDataParameter.Value;
+      }
+
+      if (!Parameters.ContainsKey(ModifiableDatasetParameterName)) {
+        Parameters.Add(new ValueParameter<IDataset>(ModifiableDatasetParameterName, dataset) { Hidden = true });
+      }
+
+      // backwards-compatibility
+      if (Parameters.ContainsKey("Models")) {
+        var solutions = ((IFixedValueParameter<ItemCollection<IRegressionSolution>>)Parameters["Models"]).Value;
+        var models = new ItemCollection<IRegressionModel>();
+        foreach (var solution in solutions) {
+          var model = solution.Model;
+          model.TargetVariable = solution.ProblemData.TargetVariable;
+          models.Add(model);
+        }
+        if (Parameters.ContainsKey(ModelCollectionParameterName))
+          Parameters.Remove(ModelCollectionParameterName);
+        Parameters.Add(new FixedValueParameter<ItemCollection<IRegressionModel>>(ModelCollectionParameterName, models));
+      }
+
+      RegisterEvents();
+    }
+
+    public MultiObjectiveGoalSeekingProblem() {
+      Parameters.Add(new ValueParameter<IRegressionProblemData>(ProblemDataParameterName, new RegressionProblemData()));
+      Parameters.Add(new ValueParameter<IDataset>(ModifiableDatasetParameterName, dataset) { Hidden = true });
+      Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>(ControllableParametersParameterName));
+      Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>(TargetsParameterName));
+      Parameters.Add(new ValueParameter<DoubleMatrix>(ControllableParameterBoundsParameterName));
+      Parameters.Add(new FixedValueParameter<ItemCollection<IRegressionModel>>(ModelCollectionParameterName, new ItemCollection<IRegressionModel>()));
+      Parameters.Add(new ValueParameter<DoubleMatrix>(TargetGoalsParameterName)); // model target weights
+      Parameters.Add(new FixedValueParameter<IntValue>(RowParameterName));
+      Parameters.Add(new FixedValueParameter<DoubleValue>(QualitySumCutoffParameterName, new DoubleValue(0.2)));
+
+      QualitySumCutoffParameter.Hidden = true;
+
+      // when the problem is created, the problem data parameter will be set to a default value
+      // set the internal property to the same value
+      ProblemData = ProblemDataParameter.Value;
+
+      UpdateControllableParameters();
+      UpdateTargetList();
+      RegisterEvents();
+    }
+    #endregion
+
+    public override double[] Evaluate(Individual individual, IRandom random) {
+      var vector = individual.RealVector();
+      vector.ElementNames = ActiveParameters;
+
+      int i = 0;
+      // round vector according to parameter step sizes
+      foreach (var parameter in ControllableParameters.CheckedItems) {
+        var step = ControllableParameterBounds[parameter.Index, 2];
+        vector[i] = RoundToNearestStepMultiple(vector[i], step);
+        ++i;
+      }
+      i = 0;
+      var qualities = new double[ActiveTargets.Count()];
+      var estimatedValues = GetEstimatedGoalValues(vector, round: true);
+      // round target estimated values and calculate qualities
+      foreach (var target in ActiveTargets) {
+        var estimatedValue = estimatedValues[i];
+        var goal = GetTargetGoal(target);
+        var weight = GetTargetWeight(target);
+        var variance = GetTargetVariance(target);
+        qualities[i] = weight * Math.Pow(estimatedValue - goal, 2) / variance;
+        ++i;
+      }
+      return qualities;
+    }
+
+    public override void Analyze(Individual[] individuals, double[][] qualities, ResultCollection results, IRandom random) {
+      var matrix = FilterFrontsByQualitySum(individuals, qualities, Math.Max(QualitySumCutoff, qualities.Min(x => x.Sum())));
+      const string resultName = "Pareto Front Solutions"; // disclaimer: not really a pareto front
+      if (!results.ContainsKey(resultName)) {
+        results.Add(new Result(resultName, matrix));
+      } else {
+        results[resultName].Value = matrix;
+      }
+      base.Analyze(individuals, qualities, results, random);
+    }
+
+    private DoubleMatrix FilterFrontsByQualitySum(Individual[] individuals, double[][] qualities, double qualitySumCutoff) {
+      var activeParameters = ActiveParameters.ToList();
+      var activeTargets = ActiveTargets.ToList();
+      var filteredModels = new List<double[]>();
+      var rowNames = new List<string>();
+      // build list of column names by combining target and parameter names (with their respective original and estimated values)
+      var columnNames = new List<string> { "Quality Sum" };
+      foreach (var target in activeTargets) {
+        columnNames.Add(target);
+        columnNames.Add(target + " (estimated)");
+      }
+      foreach (var controllableParameter in activeParameters) {
+        columnNames.Add(controllableParameter);
+        columnNames.Add(controllableParameter + " (estimated)");
+        columnNames.Add(controllableParameter + " (deviation)");
+      }
+      // filter models based on their quality sum; remove duplicate models
+      var dec = new DoubleEqualityComparer(); // comparer which uses the IsAlmost method for comparing floating point numbers
+      for (int i = 0; i < individuals.Length; ++i) {
+        var qualitySum = qualities[i].Sum();
+        if (qualitySum > qualitySumCutoff)
+          continue;
+        var vector = individuals[i].RealVector();
+        var estimatedValues = GetEstimatedGoalValues(vector);
+        var rowValues = new double[columnNames.Count];
+        rowValues[0] = qualitySum;
+        int offset = 1;
+        for (int j = 0; j < activeTargets.Count * 2; j += 2) {
+          int k = j + offset;
+          var goal = GetTargetGoal(activeTargets[j / 2]);
+          rowValues[k] = goal; // original value 
+          rowValues[k + 1] = estimatedValues[j / 2]; // estimated value
+        }
+        offset += activeTargets.Count * 2;
+        for (int j = 0; j < activeParameters.Count * 3; j += 3) {
+          int k = j + offset;
+          rowValues[k] = problemData.Dataset.GetDoubleValue(columnNames[k], Row);
+          rowValues[k + 1] = vector[j / 3];
+          rowValues[k + 2] = rowValues[k + 1] - rowValues[k];
+        }
+        if (!filteredModels.Any(x => x.SequenceEqual(rowValues, dec))) {
+          rowNames.Add((i + 1).ToString());
+          filteredModels.Add(rowValues);
+        }
+      }
+      var matrix = new DoubleMatrix(filteredModels.Count, columnNames.Count) { RowNames = rowNames, ColumnNames = columnNames, SortableView = true };
+      for (int i = 0; i < filteredModels.Count; ++i) {
+        for (int j = 0; j < filteredModels[i].Length; ++j) {
+          matrix[i, j] = filteredModels[i][j];
+        }
+      }
+      return matrix;
+    }
+
+    #region event handlers
+    private void RegisterEvents() {
+      ProblemDataParameter.ValueChanged += OnProblemDataChanged;
+      ModelCollectionParameter.Value.ItemsAdded += ModelCollection_OnItemsAdded;
+      ModelCollectionParameter.Value.ItemsRemoved += ModelCollection_OnItemsRemoved;
+      RowParameter.Value.ValueChanged += OnRowChanged;
+      ControllableParameters.CheckedItemsChanged += ControllableParameters_OnItemsChanged;
+      ControllableParameterBounds.ItemChanged += ControllableParameterBounds_ItemChanged;
+    }
+
+    private void OnRowChanged(object o, EventArgs e) {
+      // set variables in the modifiable dataset according to the new row
+      foreach (var v in dataset.DoubleVariables)
+        dataset.SetVariableValue(ProblemData.Dataset.GetDoubleValue(v, Row), v, 0);
+      // set the correct targets
+      UpdateTargetList();
+    }
+
+    private void OnProblemDataChanged(object o, EventArgs e) {
+      try {
+        ProblemData = ProblemDataParameter.Value;
+      }
+      catch (ArgumentException exception) {
+        MessageBox.Show(exception.Message, "Update Problem Data", MessageBoxButtons.OK, MessageBoxIcon.Error);
+        ProblemDataParameter.Value = problemData;
+      }
+    }
+
+    private void ModelCollection_OnItemsAdded(object sender, CollectionItemsChangedEventArgs<IRegressionModel> e) {
+      if (e.Items == null) return;
+
+      var collection = (IObservableCollection<IRegressionModel>)sender;
+      var newItems = e.Items.ToList();
+
+      foreach (var model in e.Items) {
+        try {
+          CheckIfDatasetContainsTarget(model.TargetVariable);
+        }
+        catch (ArgumentException exception) {
+          MessageBox.Show(exception.Message, "Add Model", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          newItems.Remove(model);
+          collection.Remove(model);
+        }
+      }
+      UpdateTargetList();
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    private void ModelCollection_OnItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRegressionModel> e) {
+      if (e.Items == null) return;
+      UpdateTargetList();
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    private void ControllableParameters_OnItemsChanged(object o, CollectionItemsChangedEventArgs<IndexedItem<StringValue>> e) {
+      UpdateEncoding();
+    }
+
+    private void ControllableParameterBounds_ItemChanged(object o, EventArgs e) {
+      UpdateEncoding();
+    }
+    #endregion
+
+    #region helper methods
+    private void UpdateControllableParameters() {
+      if (ProblemData == null) return;
+      SetControllableParameters(ProblemData.Dataset.DoubleVariables);
+    }
+
+    private void UpdateTargetList() {
+      if (ProblemData == null) return;
+      if (!Models.Any()) {
+        TargetGoals = new DoubleMatrix();
+        maximization = new[] { false };
+        MaximizationParameter.Value = (BoolArray)new BoolArray(maximization).AsReadOnly();
+        TargetList = new CheckedItemList<StringValue>();
+        return;
+      }
+
+      var targetNames = Models.Select(x => x.TargetVariable).Distinct().ToList();
+      var oldTargetGoals = (DoubleMatrix)TargetGoals.Clone();
+      var oldRowIndices = oldTargetGoals.RowNames.Select((x, i) => new { x, i }).ToDictionary(x => x.x, x => x.i);
+      TargetGoals = new DoubleMatrix(targetNames.Count, 4);
+      TargetGoals.RowNames = targetNames;
+      TargetGoals.ColumnNames = new[] { "Goal", "Weight", "Variance", "Step size" };
+
+      TargetList = new CheckedItemList<StringValue>();
+      for (int i = 0; i < targetNames.Count; ++i) {
+        TargetList.Add(new StringValue(targetNames[i]), true);
+        int rowIndex;
+        if (oldRowIndices.TryGetValue(targetNames[i], out rowIndex)) {
+          for (int j = 0; j < TargetGoals.Columns; ++j)
+            TargetGoals[i, j] = oldTargetGoals[rowIndex, j];
+        } else {
+          TargetGoals[i, 0] = ProblemData.Dataset.GetDoubleValue(targetNames[i], Row);
+          TargetGoals[i, 1] = 1.0;
+          TargetGoals[i, 2] = ProblemData.Dataset.GetReadOnlyDoubleValues(targetNames[i]).Variance();
+          TargetGoals[i, 3] = 1e-6;
+        }
+      }
+      maximization = new bool[targetNames.Count];
+      MaximizationParameter.Value = (BoolArray)new BoolArray(maximization).AsReadOnly();
+    }
+
+    private void UpdateEncoding() {
+      var activeParameters = ActiveParameters.ToList();
+      if (Encoding == null)
+        Encoding = new RealVectorEncoding(activeParameters.Count);
+      else
+        Encoding.Length = activeParameters.Count;
+
+      Encoding.Bounds = new DoubleMatrix(activeParameters.Count, 2); // only two columns: min and max
+      Encoding.Bounds.RowNames = activeParameters;
+      Encoding.Bounds.ColumnNames = new[] { "Min.", "Max." };
+
+      int i = 0;
+      foreach (var item in ControllableParameters.CheckedItems) {
+        var index = item.Index;
+        Encoding.Bounds[i, 0] = ControllableParameterBounds[index, 0];
+        Encoding.Bounds[i, 1] = ControllableParameterBounds[index, 1];
+        ++i;
+      }
+    }
+    #endregion
+
+    #region util
+    private static double RoundToNearestStepMultiple(double value, double step) {
+      return step * (long)Math.Round(value / step);
+    }
+
+    private class DoubleEqualityComparer : IEqualityComparer<double> {
+      public bool Equals(double x, double y) { return x.IsAlmost(y); }
+      public int GetHashCode(double obj) { return obj.GetHashCode(); }
+    }
+    #endregion
+  }
+}
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Plugin.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Plugin.cs	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Plugin.cs	(revision 14321)
@@ -0,0 +1,41 @@
+﻿#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.ProcessParameterOptimization {
+  [Plugin("HeuristicLab.ProcessParameterOptimization", "3.3.0.0")]
+  [PluginFile("HeuristicLab.ProcessParameterOptimization-3.3.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.Collections", "3.3")]
+  [PluginDependency("HeuristicLab.Common", "3.3")]
+  [PluginDependency("HeuristicLab.Core", "3.3")]
+  [PluginDependency("HeuristicLab.Data", "3.3")]
+  [PluginDependency("HeuristicLab.Encodings.RealVectorEncoding", "3.3")]
+  [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")]
+  public class HeuristicLabProcessParameterOptimizationPlugin : PluginBase {
+  }
+}
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Plugin.cs.frame
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Plugin.cs.frame	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Plugin.cs.frame	(revision 14321)
@@ -0,0 +1,41 @@
+﻿#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.GoalSeekingProblem {
+  [Plugin("HeuristicLab.GoalSeekingProblem", "3.4.0.0")]
+  [PluginFile("HeuristicLab.GoalSeekingProblem-3.4.dll", PluginFileType.Assembly)]
+  [PluginDependency("HeuristicLab.Collections", "3.3")]
+  [PluginDependency("HeuristicLab.Common", "3.3")]
+  [PluginDependency("HeuristicLab.Core", "3.3")]
+  [PluginDependency("HeuristicLab.Data", "3.3")]
+  [PluginDependency("HeuristicLab.Encodings.RealVectorEncoding", "3.3")]
+  [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")]
+  public class HeuristicLabProcessParameterOptimizationPlugin : PluginBase {
+  }
+}
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs	(revision 14321)
@@ -0,0 +1,56 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-$year$ 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.Reflection;
+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.ProcessParameterOptimization")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HEAL")]
+[assembly: AssemblyProduct("HeuristicLab")]
+[assembly: AssemblyCopyright("(c) 2002-2015 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("7c14407e-6980-465b-ba3a-4a916d48989b")]
+
+// 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("3.3.0.0")]
+[assembly: AssemblyFileVersion("3.3.11.480")]
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs.frame
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs.frame	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs.frame	(revision 14321)
@@ -0,0 +1,56 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-$year$ 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.Reflection;
+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.ProcessParameterOptimization")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HEAL")]
+[assembly: AssemblyProduct("HeuristicLab")]
+[assembly: AssemblyCopyright("(c) 2002-2015 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("7c14407e-6980-465b-ba3a-4a916d48989b")]
+
+// 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("3.3.0.0")]
+[assembly: AssemblyFileVersion("3.3.11.$WCREV$")]
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/SingleObjectiveGoalSeekingProblem.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/SingleObjectiveGoalSeekingProblem.cs	(revision 14321)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/SingleObjectiveGoalSeekingProblem.cs	(revision 14321)
@@ -0,0 +1,638 @@
+﻿#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.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Collections;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Encodings.RealVectorEncoding;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.ProcessParameterOptimization {
+  [Item("Process parameter optimization problem (single-objective)", "Represents a single objective optimization problem which uses configurable regression solutions to evaluate targets from a given dataset.")]
+  [Creatable("Problems")]
+  [StorableClass]
+  public sealed class SingleObjectiveGoalSeekingProblem : SingleObjectiveBasicProblem<RealVectorEncoding>, IGoalSeekingProblem {
+    private const string ModifiableDatasetParameterName = "Dataset";
+    private const string ProblemDataParameterName = "ProblemData";
+    private const string ControllableParametersParameterName = "ControllableParameters";
+    private const string ControllableParameterBoundsParameterName = "ControllableParameterBounds";
+    private const string TargetGoalsParameterName = "TargetGoals";
+    private const string TargetsParameterName = "Targets";
+    private const string ModelCollectionParameterName = "ModelCollection";
+    private const string RowParameterName = "Row";
+    // these parameters are used by the pareto folding analyzer
+    private const string AllowedRangesParameterName = "AllowedRanges";
+    private const string QualitySumCutoffParameterName = "QualitySumCutoff";
+
+    #region parameters
+    public IValueParameter<RealVector> AllowedRangesParameter {
+      get { return (IValueParameter<RealVector>)Parameters[AllowedRangesParameterName]; }
+    }
+    public IFixedValueParameter<DoubleValue> QualitySumCutoffParameter {
+      get { return (IFixedValueParameter<DoubleValue>)Parameters[QualitySumCutoffParameterName]; }
+    }
+    public IValueParameter<IRegressionProblemData> ProblemDataParameter {
+      get { return (IValueParameter<IRegressionProblemData>)Parameters[ProblemDataParameterName]; }
+    }
+    public IValueParameter<IDataset> ModifiableDatasetParameter {
+      get { return (IValueParameter<IDataset>)Parameters[ModifiableDatasetParameterName]; }
+    }
+    public IValueParameter<CheckedItemList<StringValue>> ControllableParametersParameter {
+      get { return (IValueParameter<CheckedItemList<StringValue>>)Parameters[ControllableParametersParameterName]; }
+    }
+    public IValueParameter<DoubleMatrix> ControllableParameterBoundsParameter {
+      get { return (IValueParameter<DoubleMatrix>)Parameters[ControllableParameterBoundsParameterName]; }
+    }
+    public IFixedValueParameter<ItemCollection<IRegressionModel>> ModelCollectionParameter {
+      get { return (IFixedValueParameter<ItemCollection<IRegressionModel>>)Parameters[ModelCollectionParameterName]; }
+    }
+    public IValueParameter<CheckedItemList<StringValue>> TargetsParameter {
+      get { return (IValueParameter<CheckedItemList<StringValue>>)Parameters[TargetsParameterName]; }
+    }
+    public IValueParameter<DoubleMatrix> TargetGoalsParameter {
+      get { return (IValueParameter<DoubleMatrix>)Parameters[TargetGoalsParameterName]; }
+    }
+    public IFixedValueParameter<IntValue> RowParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters[RowParameterName]; }
+    }
+    #endregion
+
+    #region parameter properties
+    private IItemCollection<IRegressionModel> ModelCollection {
+      get { return ModelCollectionParameter.Value; }
+    }
+    public DoubleMatrix TargetGoals {
+      get { return TargetGoalsParameter.Value; }
+      set { TargetGoalsParameter.Value = value; }
+    }
+    // the AllowedRanges property is only used by the pareto folding analyzer
+    public RealVector AllowedRanges {
+      get { return AllowedRangesParameter.Value; }
+      set { AllowedRangesParameter.Value = value; }
+    }
+    public double QualitySumCutoff {
+      get { return QualitySumCutoffParameter.Value.Value; }
+      set { QualitySumCutoffParameter.Value.Value = value; }
+    }
+    #endregion
+
+    #region IProcessParameterOptimizationProblem properties
+    [Storable]
+    private IRegressionProblemData problemData;
+    public IRegressionProblemData ProblemData {
+      get { return problemData; }
+      set {
+        if (value == null || value == problemData) return;
+        var variables = value.Dataset.DoubleVariables.ToList();
+        if (Models.Any()) {
+          var targets = Models.Select(x => x.TargetVariable);
+          var hashset = new HashSet<string>(variables);
+          foreach (var target in targets) {
+            if (!hashset.Contains(target)) {
+              throw new ArgumentException(string.Format("Incompatible problem data. Target \"{0}\" is missing.", target));
+            }
+          }
+        }
+        problemData = value;
+        dataset = new ModifiableDataset(variables, variables.Select(x => new List<double> { ProblemData.Dataset.GetDoubleValue(x, Row) }));
+        ProblemDataParameter.Value = ProblemData;
+        UpdateControllableParameters();
+        UpdateTargetList();
+      }
+    }
+
+    public int Row {
+      get { return RowParameter.Value.Value; }
+      set { RowParameter.Value.Value = value; }
+    }
+
+    public IEnumerable<IRegressionModel> Models {
+      get { return ModelCollectionParameter.Value; }
+    }
+
+    #region targets
+    public IEnumerable<string> ActiveTargets {
+      get { return TargetList.CheckedItems.Select(x => x.Value.Value); }
+    }
+
+    public IEnumerable<string> Targets {
+      get { return TargetList.Select(x => x.Value); }
+    }
+
+    public ICheckedItemList<StringValue> TargetList {
+      get { return TargetsParameter.Value; }
+      set { TargetsParameter.Value = (CheckedItemList<StringValue>)value; }
+    }
+    #endregion
+
+    #region parameters
+    public IEnumerable<string> ActiveParameters {
+      get { return ControllableParameters.CheckedItems.Select(x => x.Value.Value); }
+    }
+
+    public ICheckedItemList<StringValue> ControllableParameters {
+      get { return ControllableParametersParameter.Value; }
+      set { ControllableParametersParameter.Value = (CheckedItemList<StringValue>)value; }
+    }
+
+    public DoubleMatrix ControllableParameterBounds {
+      get { return ControllableParameterBoundsParameter.Value; }
+      set { ControllableParameterBoundsParameter.Value = value; }
+    }
+    #endregion
+    #endregion
+
+    #region IProcessParameterOptimizationProblem methods
+    #region solutions
+    public double GetVariableValue(string variableName) {
+      return dataset.GetDoubleValue(variableName, 0);
+    }
+
+    public void SetVariableValue(string variableName, double variableValue) {
+      dataset.SetVariableValue(variableValue, variableName, 0); // will throw if the variable is not present in the dataset
+    }
+
+    public double[] GetEstimatedGoalValues(IEnumerable<double> parameters, bool round = false) {
+      var ds = (ModifiableDataset)dataset.Clone();
+      var estimatedValues = new double[ActiveTargets.Count()];
+      var e1 = parameters.GetEnumerator();
+      var e2 = ActiveParameters.GetEnumerator();
+      while (e1.MoveNext() && e2.MoveNext())
+        ds.SetVariableValue(e1.Current, e2.Current, 0);
+      var rows = new[] { 0 }; // actually just one row
+      int i = 0;
+      var models = ModelCollection.ToList();
+      foreach (var target in TargetList.CheckedItems) {
+        var average = 0d;
+        var count = 0;
+        var targetName = target.Value.Value;
+
+        foreach (var model in models) {
+          if (targetName != model.TargetVariable)
+            continue;
+          average += model.GetEstimatedValues(ds, rows).Single();
+          count++;
+        }
+        var estimatedValue = average / count;
+        if (round) {
+          var accuracy = GetTargetStepSize(targetName);
+          estimatedValues[i] = RoundToNearestStepMultiple(estimatedValue, accuracy);
+        } else {
+          estimatedValues[i] = estimatedValue;
+        }
+        ++i;
+      }
+      return estimatedValues;
+    }
+
+    public void AddModel(IRegressionModel model) {
+      var target = model.TargetVariable;
+      CheckIfDatasetContainsTarget(target);
+      ModelCollection.Add(model);
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    // method which throws an exception that can be caught in the event handler if the check fails
+    private void CheckIfDatasetContainsTarget(string target) {
+      if (dataset.DoubleVariables.All(x => x != target))
+        throw new ArgumentException(string.Format("Model target \"{0}\" does not exist in the dataset.", target));
+    }
+
+    public void RemoveModel(IRegressionModel model) {
+      ModelCollection.Remove(model);
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    public event EventHandler ModelsChanged;
+    private void OnModelsChanged(object sender, EventArgs args) {
+      var changed = ModelsChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+    #endregion
+
+    #region targets
+    public bool GetTargetActive(string target) {
+      var item = TargetList.SingleOrDefault(x => x.Value == target);
+      if (item == null)
+        throw new ArgumentException(string.Format("SetTargetActive: Invalid target name {0}", target));
+      return TargetList.ItemChecked(item);
+    }
+
+    public void SetTargetActive(string target, bool active) {
+      var item = TargetList.SingleOrDefault(x => x.Value == target);
+      if (item == null)
+        throw new ArgumentException(string.Format("SetTargetActive: Invalid target name {0}", target));
+      TargetList.SetItemCheckedState(item, active);
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetGoal(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 0];
+    }
+
+    public void SetTargetGoal(string target, double goal) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 0] = goal;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetWeight(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 1];
+    }
+
+    public void SetTargetWeight(string target, double weight) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 1] = weight;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetVariance(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 2];
+    }
+
+    public void SetTargetVariance(string target, double variance) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 2] = variance;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public double GetTargetStepSize(string target) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      return TargetGoals[i, 3];
+    }
+
+    public void SetTargetStepSize(string target, double stepSize) {
+      if (!Targets.Contains(target))
+        throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
+      int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
+      TargetGoals[i, 3] = stepSize;
+      OnTargetsChanged(this, EventArgs.Empty);
+    }
+
+    public event EventHandler TargetsChanged;
+    private void OnTargetsChanged(object sender, EventArgs args) {
+      var changed = TargetsChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+    #endregion // targets
+
+    #region process parameters
+    public void SetParameterBounds(string parameterName, double min, double max, double step) {
+      int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameterName).Count();
+      if (i < ControllableParameterBounds.Rows) {
+        ControllableParameterBounds[i, 0] = min;
+        ControllableParameterBounds[i, 1] = max;
+        ControllableParameterBounds[i, 2] = step;
+        UpdateEncoding();
+        OnParametersChanged(this, EventArgs.Empty);
+      } else {
+        throw new ArgumentException(string.Format("SetParameterBounds: Invalid parameter name {0}", parameterName));
+      }
+    }
+
+    public double GetParameterStepSize(string parameter) {
+      int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameter).Count();
+      if (i < ControllableParameterBounds.Rows)
+        return ControllableParameterBounds[i, 2];
+      throw new ArgumentException(string.Format("GetParameterStepSize: Invalid parameter name {0}", parameter));
+    }
+
+    public void SetParameterStepSize(string parameter, double stepSize) {
+      int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameter).Count();
+      if (i < ControllableParameterBounds.Rows) {
+        ControllableParameterBounds[i, 2] = stepSize;
+        OnParametersChanged(this, EventArgs.Empty);
+      } else {
+        throw new ArgumentException(string.Format("SetParameterStepSize: Invalid parameter name {0}", parameter));
+      }
+    }
+
+    public bool GetParameterActive(string parameter) {
+      var item = ControllableParameters.SingleOrDefault(x => x.Value == parameter);
+      if (item == null)
+        throw new ArgumentException(string.Format("GetParameterActive: Invalid target name {0}", parameter));
+      return ControllableParameters.ItemChecked(item);
+    }
+
+    public void SetParameterActive(string parameter, bool active) {
+      var item = ControllableParameters.SingleOrDefault(x => x.Value == parameter);
+      if (item == null)
+        throw new ArgumentException(string.Format("SetParameterActive: Invalid target name {0}", parameter));
+      ControllableParameters.SetItemCheckedState(item, active);
+      OnParametersChanged(this, EventArgs.Empty);
+    }
+
+    /// <summary>
+    /// Returns the parameter bounds (min and max) and the step size for the specified parameter
+    /// </summary>
+    /// <param name="parameterName"></param>
+    /// <returns>A double array containing the values (min, max, step) in this order</returns>
+    public double[] GetParameterBounds(string parameterName) {
+      var index = ControllableParameters.TakeWhile(x => x.Value != parameterName).Count();
+      if (index < ControllableParameters.Count) {
+        var min = ControllableParameterBounds[index, 0];
+        var max = ControllableParameterBounds[index, 1];
+        var step = ControllableParameterBounds[index, 2];
+        return new[] { min, max, step };
+      }
+      throw new ArgumentException(string.Format("GetParameterBounds: Unknown parameter {0}.", parameterName));
+    }
+
+    public void SetControllableParameters(IEnumerable<string> parameterNames) {
+      ControllableParameters = new CheckedItemList<StringValue>();
+      foreach (var v in parameterNames) {
+        ControllableParameters.Add(new StringValue(v), false);
+      }
+      ControllableParameters.CheckedItemsChanged += ControllableParameters_OnItemsChanged;
+      ControllableParameterBounds = new DoubleMatrix(ControllableParameters.Count, 3);
+      ControllableParameterBounds.RowNames = GetControllableParameters();
+      ControllableParameterBounds.ColumnNames = new[] { "Min", "Max", "Step" };
+
+      for (int i = 0; i < ControllableParameters.Count; ++i) {
+        var itemName = ControllableParameters[i].Value;
+        var values = ProblemData.Dataset.GetReadOnlyDoubleValues(itemName).Where(x => !double.IsNaN(x) && !double.IsInfinity(x)).ToList();
+        if (!values.Any()) continue;
+
+        // add a 20% margin to allow the optimization algorithm more freedom of exploration
+        ControllableParameterBounds[i, 0] = 0.8 * values.Min(); // min
+        ControllableParameterBounds[i, 1] = 1.2 * values.Max(); // max
+        ControllableParameterBounds[i, 2] = 1e-6;               // step
+      }
+      OnParametersChanged(this, EventArgs.Empty);
+    }
+
+    public IEnumerable<string> GetControllableParameters() {
+      return ControllableParameters.Select(x => x.Value);
+    }
+
+    public event EventHandler ParametersChanged;
+    private void OnParametersChanged(object sender, EventArgs args) {
+      var changed = ParametersChanged;
+      if (changed == null) return;
+      changed(sender, args);
+    }
+    #endregion // process parameters
+    #endregion // IProcessParameterOprimizationProblem methods
+
+    #region data members
+    [Storable]
+    private ModifiableDataset dataset; // modifiable dataset
+
+    public override bool Maximization {
+      get { return true; }
+    }
+    #endregion
+
+    #region constructors
+    [StorableConstructor]
+    private SingleObjectiveGoalSeekingProblem(bool deserializing) : base(deserializing) { }
+
+    private SingleObjectiveGoalSeekingProblem(SingleObjectiveGoalSeekingProblem original, Cloner cloner)
+      : base(original, cloner) {
+      this.dataset = cloner.Clone(original.dataset);
+      this.problemData = cloner.Clone(original.problemData);
+
+      RegisterEvents();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new SingleObjectiveGoalSeekingProblem(this, cloner);
+    }
+
+    [StorableHook(HookType.AfterDeserialization)]
+    private void AfterDeserialization() {
+      // backwards-compatibility
+      if (Parameters.ContainsKey("Models")) {
+        var solutions = ((IFixedValueParameter<ItemCollection<IRegressionSolution>>)Parameters["Models"]).Value;
+        var models = new ItemCollection<IRegressionModel>();
+        foreach (var solution in solutions) {
+          var model = solution.Model;
+          model.TargetVariable = solution.ProblemData.TargetVariable;
+          models.Add(model);
+        }
+        if (Parameters.ContainsKey(ModelCollectionParameterName))
+          Parameters.Remove(ModelCollectionParameterName);
+        Parameters.Add(new FixedValueParameter<ItemCollection<IRegressionModel>>(ModelCollectionParameterName, models));
+      }
+
+      RegisterEvents();
+    }
+
+    public SingleObjectiveGoalSeekingProblem() {
+      Parameters.Add(new ValueParameter<IRegressionProblemData>(ProblemDataParameterName, new RegressionProblemData()));
+      Parameters.Add(new ValueParameter<IDataset>(ModifiableDatasetParameterName, dataset) { Hidden = true });
+      Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>(ControllableParametersParameterName));
+      Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>(TargetsParameterName));
+      Parameters.Add(new ValueParameter<DoubleMatrix>(ControllableParameterBoundsParameterName));
+      Parameters.Add(new FixedValueParameter<ItemCollection<IRegressionModel>>(ModelCollectionParameterName, new ItemCollection<IRegressionModel>()));
+      Parameters.Add(new ValueParameter<DoubleMatrix>(TargetGoalsParameterName)); // model target weights
+      Parameters.Add(new FixedValueParameter<IntValue>(RowParameterName));
+
+      // when the problem is created, the problem data parameter will be set to a default value
+      // set the internal property to the same value
+      ProblemData = ProblemDataParameter.Value;
+
+      UpdateControllableParameters();
+      UpdateTargetList();
+      RegisterEvents();
+    }
+    #endregion
+
+    public override double Evaluate(Individual individual, IRandom random) {
+      var vector = individual.RealVector();
+      vector.ElementNames = ActiveParameters;
+
+      int i = 0;
+      // round vector according to parameter step sizes
+      foreach (var parameter in ControllableParameters.CheckedItems) {
+        var step = ControllableParameterBounds[parameter.Index, 2];
+        vector[i] = RoundToNearestStepMultiple(vector[i], step);
+        ++i;
+      }
+      i = 0;
+      var quality = 0d;
+      var estimatedValues = GetEstimatedGoalValues(vector, round: true);
+      // round target estimated values and calculate qualities
+      foreach (var target in ActiveTargets) {
+        var estimatedValue = estimatedValues[i];
+        var goal = GetTargetGoal(target);
+        var weight = GetTargetWeight(target);
+        var variance = GetTargetVariance(target);
+        quality += weight * Math.Pow(estimatedValue - goal, 2) / variance;
+        ++i;
+      }
+      return quality / i;
+    }
+
+    #region event handlers
+    private void RegisterEvents() {
+      ProblemDataParameter.ValueChanged += OnProblemDataChanged;
+      ModelCollectionParameter.Value.ItemsAdded += ModelCollection_OnItemsAdded;
+      ModelCollectionParameter.Value.ItemsRemoved += ModelCollection_OnItemsRemoved;
+      RowParameter.Value.ValueChanged += OnRowChanged;
+      ControllableParameters.CheckedItemsChanged += ControllableParameters_OnItemsChanged;
+      ControllableParameterBounds.ItemChanged += ControllableParameterBounds_ItemChanged;
+    }
+
+    private void OnRowChanged(object o, EventArgs e) {
+      // set variables in the modifiable dataset according to the new row
+      foreach (var v in dataset.DoubleVariables)
+        dataset.SetVariableValue(ProblemData.Dataset.GetDoubleValue(v, Row), v, 0);
+      // set the correct targets
+      UpdateTargetList();
+    }
+
+    private void OnProblemDataChanged(object o, EventArgs e) {
+      try {
+        ProblemData = ProblemDataParameter.Value;
+      }
+      catch (ArgumentException exception) {
+        MessageBox.Show(exception.Message, "Update Problem Data", MessageBoxButtons.OK, MessageBoxIcon.Error);
+        ProblemDataParameter.Value = problemData;
+      }
+    }
+
+    private void ModelCollection_OnItemsAdded(object sender, CollectionItemsChangedEventArgs<IRegressionModel> e) {
+      if (e.Items == null) return;
+
+      var collection = (IObservableCollection<IRegressionModel>)sender;
+      var newItems = e.Items.ToList();
+
+      foreach (var model in e.Items) {
+        try {
+          CheckIfDatasetContainsTarget(model.TargetVariable);
+        }
+        catch (ArgumentException exception) {
+          MessageBox.Show(exception.Message, "Add Model", MessageBoxButtons.OK, MessageBoxIcon.Error);
+          newItems.Remove(model);
+          collection.Remove(model);
+        }
+      }
+      UpdateTargetList();
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    private void ModelCollection_OnItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRegressionModel> e) {
+      if (e.Items == null) return;
+      UpdateTargetList();
+      OnModelsChanged(this, EventArgs.Empty);
+    }
+
+    private void ControllableParameters_OnItemsChanged(object o, CollectionItemsChangedEventArgs<IndexedItem<StringValue>> e) {
+      UpdateEncoding();
+    }
+
+    private void ControllableParameterBounds_ItemChanged(object o, EventArgs e) {
+      UpdateEncoding();
+    }
+    #endregion
+
+    #region helper methods
+    private void UpdateControllableParameters() {
+      if (ProblemData == null) return;
+      SetControllableParameters(ProblemData.Dataset.DoubleVariables);
+    }
+
+    private void UpdateTargetList() {
+      if (ProblemData == null) return;
+      if (!Models.Any()) {
+        TargetGoals = new DoubleMatrix();
+        TargetList = new CheckedItemList<StringValue>();
+        return;
+      }
+      var targetNames = Models.Select(x => x.TargetVariable).Distinct().ToList();
+      var oldTargetGoals = (DoubleMatrix)TargetGoals.Clone();
+      var oldRowIndices = oldTargetGoals.RowNames.Select((x, i) => new { x, i }).ToDictionary(x => x.x, x => x.i);
+      TargetGoals = new DoubleMatrix(targetNames.Count, 4);
+      TargetGoals.RowNames = targetNames;
+      TargetGoals.ColumnNames = new[] { "Goal", "Weight", "Variance", "Step size" };
+
+      TargetList = new CheckedItemList<StringValue>();
+      for (int i = 0; i < targetNames.Count; ++i) {
+        TargetList.Add(new StringValue(targetNames[i]), true);
+        int rowIndex;
+        if (oldRowIndices.TryGetValue(targetNames[i], out rowIndex)) {
+          for (int j = 0; j < TargetGoals.Columns; ++j)
+            TargetGoals[i, j] = oldTargetGoals[rowIndex, j];
+        } else {
+          TargetGoals[i, 0] = ProblemData.Dataset.GetDoubleValue(targetNames[i], Row);
+          TargetGoals[i, 1] = 1.0;
+          TargetGoals[i, 2] = ProblemData.Dataset.GetReadOnlyDoubleValues(targetNames[i]).Variance();
+          TargetGoals[i, 3] = 1e-6;
+        }
+      }
+    }
+
+    private void UpdateEncoding() {
+      var activeParameters = ActiveParameters.ToList();
+      if (Encoding == null)
+        Encoding = new RealVectorEncoding(activeParameters.Count);
+      else
+        Encoding.Length = activeParameters.Count;
+
+      Encoding.Bounds = new DoubleMatrix(activeParameters.Count, 2); // only two columns: min and max
+      Encoding.Bounds.RowNames = activeParameters;
+      Encoding.Bounds.ColumnNames = new[] { "Min.", "Max." };
+
+      int i = 0;
+      foreach (var item in ControllableParameters.CheckedItems) {
+        var index = item.Index;
+        Encoding.Bounds[i, 0] = ControllableParameterBounds[index, 0];
+        Encoding.Bounds[i, 1] = ControllableParameterBounds[index, 1];
+        ++i;
+      }
+    }
+    #endregion
+
+    #region util
+    private static double RoundToNearestStepMultiple(double value, double step) {
+      return step * (long)Math.Round(value / step);
+    }
+    #endregion
+  }
+}
