Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem.Views/3.4/GoalSeekingOptimizerView.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem.Views/3.4/GoalSeekingOptimizerView.cs	(revision 14525)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem.Views/3.4/GoalSeekingOptimizerView.cs	(revision 14526)
@@ -54,4 +54,7 @@
       if (Content.ProblemData != null)
         dataViewHost.Content = Content.ProblemData;
+
+      if (Content.Results != null)
+        resultsViewHost.Content = Content.Results;
     }
 
@@ -61,4 +64,5 @@
       Content.ProblemChanged += (o, e) => { problemViewHost.Content = Content.Problem; };
       Content.ProblemDataChanged += (o, e) => { dataViewHost.Content = Content.ProblemData; };
+      Content.ResultsChanged += (o, e) => { resultsViewHost.Content = Content.Results; };
     }
 
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem.Views/3.4/GoalSeekingProblem.Views.csproj
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem.Views/3.4/GoalSeekingProblem.Views.csproj	(revision 14525)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem.Views/3.4/GoalSeekingProblem.Views.csproj	(revision 14526)
@@ -22,4 +22,5 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <LangVersion>4</LangVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
@@ -32,4 +33,5 @@
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="HeuristicLab.Collections-3.3, Version=3.3.0.0, Culture=neutral, PublicKeyToken=ba48961d6f65dcec" />
     <Reference Include="HeuristicLab.Common-3.3">
       <HintPath>..\..\..\..\trunk\sources\bin\HeuristicLab.Common-3.3.dll</HintPath>
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Analyzers/BestSolutionAnalyzer.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Analyzers/BestSolutionAnalyzer.cs	(revision 14525)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Analyzers/BestSolutionAnalyzer.cs	(revision 14526)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingOptimizer.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingOptimizer.cs	(revision 14525)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingOptimizer.cs	(revision 14526)
@@ -24,6 +24,8 @@
 using System.Drawing;
 using System.Linq;
+using HeuristicLab.Analysis;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
+using HeuristicLab.Data;
 using HeuristicLab.Optimization;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
@@ -69,5 +71,8 @@
 
         optimizer = value;
-        optimizer.Problem = problem;
+
+        if (problem != null)
+          optimizer.Problem = problem;
+
         RegisterOptimizerEvents();
         OnAlgorithmChanged();
@@ -92,4 +97,15 @@
     }
 
+    [Storable]
+    private ResultCollection results;
+    public ResultCollection Results {
+      get { return results; }
+      private set {
+        if (value != null)
+          results = value;
+        OnResultsChanged();
+      }
+    }
+
     private GoalSeekingOptimizerAction gsoAction;
 
@@ -112,4 +128,5 @@
       this.Problem = (IGoalSeekingProblem)original.Problem.Clone(cloner);
       this.ProblemData = (IRegressionProblemData)original.ProblemData.Clone(cloner);
+      this.Results = (ResultCollection)original.Results.Clone();
     }
 
@@ -139,5 +156,14 @@
     }
 
+    public EventHandler ResultsChanged;
+    private void OnResultsChanged() {
+      var changed = ResultsChanged;
+      if (changed != null)
+        changed(this, EventArgs.Empty);
+    }
+
     private int calculatedRows;
+    private int currentRow;
+
     public void Start() {
       if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
@@ -148,7 +174,6 @@
         Optimizer.Prepare(clearRuns: true);
         calculatedRows = 0;
-
-        var row = problemData.TrainingIndices.Skip(calculatedRows).First();
-        problem.Configure(problemData, row);
+        currentRow = problemData.TrainingIndices.Skip(calculatedRows).First();
+        problem.Configure(problemData, currentRow);
         optimizer.Prepare();
       }
@@ -188,4 +213,8 @@
       if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
         throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
+
+      if (Results != null)
+        Results.Clear();
+
       if (optimizer != null) {
         ExecutionTime = TimeSpan.Zero;
@@ -223,5 +252,5 @@
     }
 
-    public IEnumerable<IOptimizer> NestedOptimizers { get; }
+    public IEnumerable<IOptimizer> NestedOptimizers { get; private set; }
 
     public string Filename { get; set; }
@@ -367,17 +396,177 @@
       var remainingRows = problemData.TrainingIndices.Skip(calculatedRows);
 
-      if (gsoAction == GoalSeekingOptimizerAction.Stop) OnStopped();
-      else if (!remainingRows.Any()) OnStopped();
-      else if (gsoAction == GoalSeekingOptimizerAction.Pause) OnPaused();
-      else if (gsoAction == GoalSeekingOptimizerAction.Start) {
-        var row = remainingRows.First();
-        problem.Configure(problemData, row);
-        optimizer.Prepare();
-        optimizer.Start();
-      } else if (ExecutionState == ExecutionState.Started) {
-        OnPaused();
-      } else OnStopped();
+      // extract results from the optimizer 
+      GetResults();
+
+      if (gsoAction == GoalSeekingOptimizerAction.Stop)
+        OnStopped();
+      else if (!remainingRows.Any()) {
+        OnStopped();
+      } else switch (gsoAction) {
+          case GoalSeekingOptimizerAction.Pause:
+            OnPaused();
+            break;
+          case GoalSeekingOptimizerAction.Start:
+            currentRow = remainingRows.First();
+            problem.Configure(problemData, currentRow);
+            optimizer.Prepare();
+            optimizer.Start();
+            break;
+          default:
+            if (ExecutionState == ExecutionState.Started) {
+              OnPaused();
+            } else OnStopped();
+            break;
+        }
     }
     #endregion
+    #endregion
+
+    #region aggregate results
+    private void GetResults() {
+      if (Problem is MultiObjectiveGoalSeekingProblem) {
+        var m = (DoubleMatrix)optimizer.Results["Pareto Front Solutions"].Value;
+        AggregateMultiObjectiveResults(m);
+      } else if (Problem is SingleObjectiveGoalSeekingProblem) {
+        var m = (DoubleMatrix)optimizer.Results["Best Solution"].Value;
+        AggregateSingleObjectiveResults(m);
+      }
+    }
+
+    private void AggregateSingleObjectiveResults(DoubleMatrix solutions) {
+      var indices = solutions.ColumnNames.Select((x, i) => Tuple.Create(x, i)).ToDictionary(x => x.Item1, x => x.Item2);
+
+      var goals = Problem.Goals.Where(x => x.Active).ToList();
+      var inputs = Problem.Inputs.Where(x => x.Active).ToList();
+
+      if (Results == null)
+        Results = new ResultCollection();
+      // goals
+      if (!Results.ContainsKey("Goals"))
+        Results.Add(new Result("Goals", new ResultCollection()));
+      var goalResults = (ResultCollection)Results["Goals"].Value;
+      foreach (var goal in goals) {
+        var estimatedGoalName = goal.Name + " (estimated)";
+        if (!goalResults.ContainsKey(goal.Name)) {
+          goalResults.Add(new Result(goal.Name, new ScatterPlot(goal.Name, "")));
+        }
+
+        var plot = (ScatterPlot)goalResults[goal.Name].Value;
+        if (!plot.Rows.ContainsKey(goal.Name)) {
+          plot.Rows.Add(new ScatterPlotDataRow { Name = goal.Name });
+          plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedGoalName });
+        }
+
+        plot.Rows[goal.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[goal.Name]]));
+        plot.Rows[estimatedGoalName].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[estimatedGoalName]]));
+      }
+
+      // inputs
+      if (!Results.ContainsKey("Inputs"))
+        Results.Add(new Result("Inputs", new ResultCollection()));
+      var inputResults = (ResultCollection)Results["Inputs"].Value;
+      foreach (var input in inputs) {
+        var estimatedInputName = input.Name + " (estimated)";
+        if (!inputResults.ContainsKey(input.Name)) {
+          inputResults.Add(new Result(input.Name, new ScatterPlot(input.Name, "")));
+        }
+
+        var plot = (ScatterPlot)inputResults[input.Name].Value;
+        if (!plot.Rows.ContainsKey(input.Name)) {
+          plot.Rows.Add(new ScatterPlotDataRow { Name = input.Name });
+          plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedInputName });
+        }
+
+        plot.Rows[input.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[input.Name]]));
+        plot.Rows[estimatedInputName].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[estimatedInputName]]));
+      }
+      // aggregate results as a matrix
+      UpdateResultsMatrix(solutions);
+    }
+
+    private void AggregateMultiObjectiveResults(DoubleMatrix solutions) {
+      int maxSolutionsCount = Math.Min(solutions.Rows, 10);
+
+      var indices = solutions.ColumnNames.Select((x, i) => Tuple.Create(x, i)).ToDictionary(x => x.Item1, x => x.Item2);
+
+      var goals = Problem.Goals.Where(x => x.Active).ToList();
+      var inputs = Problem.Inputs.Where(x => x.Active).ToList();
+
+      if (Results == null)
+        Results = new ResultCollection();
+      // goals
+      if (!Results.ContainsKey("Goals"))
+        Results.Add(new Result("Goals", new ResultCollection()));
+      var goalResults = (ResultCollection)Results["Goals"].Value;
+      foreach (var goal in goals) {
+        var estimatedGoalName = goal.Name + " (estimated)";
+        if (!goalResults.ContainsKey(goal.Name)) {
+          goalResults.Add(new Result(goal.Name, new ScatterPlot(goal.Name, "")));
+        }
+
+        var plot = (ScatterPlot)goalResults[goal.Name].Value;
+        if (!plot.Rows.ContainsKey(goal.Name)) {
+          plot.Rows.Add(new ScatterPlotDataRow { Name = goal.Name, VisualProperties = { PointSize = 5 } });
+        }
+
+        plot.Rows[goal.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[goal.Name]]));
+        for (int i = 0; i < maxSolutionsCount; ++i) {
+          if (!plot.Rows.ContainsKey(estimatedGoalName + i))
+            plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedGoalName + i, VisualProperties = { PointSize = 5 } });
+          plot.Rows[estimatedGoalName + i].Points.Add(new Point2D<double>(currentRow, solutions[i, indices[estimatedGoalName]]));
+        }
+      }
+      // inputs
+      if (!Results.ContainsKey("Inputs"))
+        Results.Add(new Result("Inputs", new ResultCollection()));
+      var inputResults = (ResultCollection)Results["Inputs"].Value;
+      foreach (var input in inputs) {
+        var estimatedInputName = input.Name + " (estimated)";
+        if (!inputResults.ContainsKey(input.Name)) {
+          inputResults.Add(new Result(input.Name, new ScatterPlot(input.Name, "")));
+        }
+
+        var plot = (ScatterPlot)inputResults[input.Name].Value;
+        if (!plot.Rows.ContainsKey(input.Name)) {
+          plot.Rows.Add(new ScatterPlotDataRow { Name = input.Name, VisualProperties = { PointSize = 5 } });
+        }
+
+        plot.Rows[input.Name].Points.Add(new Point2D<double>(currentRow, solutions[0, indices[input.Name]])); // it stays the same for all the rows
+        for (int i = 0; i < maxSolutionsCount; ++i) {
+          if (!plot.Rows.ContainsKey(estimatedInputName + i))
+            plot.Rows.Add(new ScatterPlotDataRow { Name = estimatedInputName + i, VisualProperties = { PointSize = 5 } });
+          plot.Rows[estimatedInputName + i].Points.Add(new Point2D<double>(currentRow, solutions[i, indices[estimatedInputName]]));
+        }
+      }
+      // also add a matrix containing the whole solution data including the row
+      UpdateResultsMatrix(solutions);
+    }
+
+    private void UpdateResultsMatrix(DoubleMatrix solutions) {
+      // also add a matrix containing the whole solution data including the row
+      var rows = problemData.TrainingIndices.Take(calculatedRows).ToList();
+      var m = new DoubleMatrix(calculatedRows, solutions.Columns + 1) {
+        RowNames = rows.Select(x => x.ToString()),
+        ColumnNames = new[] { "Row" }.Concat(solutions.ColumnNames)
+      };
+      DoubleMatrix old = null;
+      if (Results.ContainsKey("Solutions"))
+        old = (DoubleMatrix)Results["Solutions"].Value;
+      else
+        Results.Add(new Result("Solutions", m));
+
+      if (old != null && calculatedRows > 0) {
+        for (int i = 0; i < old.Rows; ++i) {
+          for (int j = 0; j < old.Columns; ++j) {
+            m[i, j] = old[i, j];
+          }
+        }
+      }
+      m[rows.Count - 1, 0] = currentRow;
+      for (int i = 1; i < m.Columns; ++i) {
+        m[rows.Count - 1, i] = solutions[0, i - 1];
+      }
+      Results["Solutions"].Value = m;
+    }
     #endregion
   }
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingProblem.csproj
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingProblem.csproj	(revision 14525)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/GoalSeekingProblem.csproj	(revision 14526)
@@ -25,4 +25,5 @@
     <Prefer32Bit>false</Prefer32Bit>
     <PlatformTarget>AnyCPU</PlatformTarget>
+    <LangVersion>4</LangVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/MultiObjectiveGoalSeekingProblem.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/MultiObjectiveGoalSeekingProblem.cs	(revision 14525)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/MultiObjectiveGoalSeekingProblem.cs	(revision 14526)
@@ -39,5 +39,4 @@
   public sealed class MultiObjectiveGoalSeekingProblem : MultiObjectiveBasicProblem<RealVectorEncoding>, IGoalSeekingProblem {
     #region parameter names
-    private const string ModifiableDatasetParameterName = "Dataset";
     private const string InputsParameterName = "Inputs";
     private const string GoalsParameterName = "Goals";
@@ -151,5 +150,4 @@
     public MultiObjectiveGoalSeekingProblem() {
       dataset = new ModifiableDataset();
-      Parameters.Add(new ValueParameter<IDataset>(ModifiableDatasetParameterName, dataset) { Hidden = true });
       Parameters.Add(new ValueParameter<CheckedItemList<InputParameter>>(InputsParameterName));
       Parameters.Add(new ValueParameter<CheckedItemList<GoalParameter>>(GoalsParameterName));
Index: anches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/Properties/AssemblyInfo.cs	(revision 14525)
+++ 	(revision )
@@ -1,56 +1,0 @@
-﻿#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.14380")]
Index: /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/SingleObjectiveGoalSeekingProblem.cs
===================================================================
--- /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/SingleObjectiveGoalSeekingProblem.cs	(revision 14525)
+++ /branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/SingleObjectiveGoalSeekingProblem.cs	(revision 14526)
@@ -26,4 +26,5 @@
 using HeuristicLab.Common;
 using HeuristicLab.Core;
+using HeuristicLab.Data;
 using HeuristicLab.Encodings.RealVectorEncoding;
 using HeuristicLab.Optimization;
@@ -38,5 +39,4 @@
   public sealed class SingleObjectiveGoalSeekingProblem : SingleObjectiveBasicProblem<RealVectorEncoding>, IGoalSeekingProblem {
     #region parameter names
-    private const string ModifiableDatasetParameterName = "Dataset";
     private const string InputsParameterName = "Inputs";
     private const string GoalsParameterName = "Goals";
@@ -136,5 +136,4 @@
     public SingleObjectiveGoalSeekingProblem() {
       dataset = new ModifiableDataset();
-      Parameters.Add(new ValueParameter<IDataset>(ModifiableDatasetParameterName, dataset) { Hidden = true });
       Parameters.Add(new ValueParameter<CheckedItemList<InputParameter>>(InputsParameterName));
       Parameters.Add(new ValueParameter<CheckedItemList<GoalParameter>>(GoalsParameterName));
@@ -161,6 +160,54 @@
       return quality;
     }
+
+    public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) {
+      var zipped = individuals.Zip(qualities, (ind, qual) => new { Individual = ind, Quality = qual }).OrderBy(x => x.Quality);
+      var best = Maximization ? zipped.Last() : zipped.First();
+      var realVector = best.Individual.RealVector();
+      const string resultName = "Best Solution";
+
+      var columnNames = new List<string>();
+      foreach (var goal in ActiveGoals) {
+        columnNames.Add(goal.Name);
+        columnNames.Add(goal.Name + " (estimated)");
+      }
+      foreach (var input in ActiveInputs) {
+        columnNames.Add(input.Name);
+        columnNames.Add(input.Name + " (estimated)");
+        columnNames.Add(input.Name + " (deviation)");
+      }
+
+      var m = new DoubleMatrix(1, columnNames.Count) { ColumnNames = columnNames };
+      int i = 0;
+
+      var goals = ActiveGoals.Zip(GetEstimatedGoalValues(realVector, round: true),
+        (goal, value) => new { TargetValue = goal.Goal, EstimatedValue = value });
+
+      foreach (var goal in goals) {
+        m[0, i] = goal.TargetValue;
+        m[0, i + 1] = goal.EstimatedValue;
+        i += 2;
+      }
+
+      var inputs = ActiveInputs.Zip(realVector,
+        (input, value) => new { ActualValue = input.Value, EstimatedValue = value });
+
+      foreach (var input in inputs) {
+        m[0, i] = input.ActualValue;
+        m[0, i + 1] = input.EstimatedValue;
+        m[0, i + 2] = m[0, i] - m[0, i + 1];
+        i += 3;
+      }
+
+      if (!results.ContainsKey(resultName)) {
+        results.Add(new Result(resultName, m));
+      } else {
+        results[resultName].Value = m;
+      }
+
+      base.Analyze(individuals, qualities, results, random);
+    }
+
     #region event handlers
-
     private void RegisterEvents() {
       ModelsParameter.Value.ItemsAdded += ModelCollection_ItemsChanged;
