Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/DirectedWalk.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/DirectedWalk.cs	(revision 16096)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Algorithms/DirectedWalk.cs	(revision 16096)
@@ -0,0 +1,146 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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 HeuristicLab.Common;
+
+namespace HeuristicLab.Analysis.FitnessLandscape {
+  /// <summary>
+  /// A generic implementation of a directed walk that can still be used in an efficient manner.
+  /// </summary>
+  /// <typeparam name="T">The type of the solution encoding.</typeparam>
+  public static class DirectedWalk<T> where T : IDeepCloneable {
+    /// <summary>
+    /// Provides regular directed walks between two solutions.
+    /// </summary>
+    /// <param name="maximization">Whether the problem is a maximization or minimization problem.</param>
+    /// <param name="distFunc">The distance function that compares two solutions.</param>
+    /// <param name="neighborFunc">The neighborhood functions that returns _all_ neighbors of a solution.</param>
+    /// <param name="start">The starting solution (to which distance should be increased in every step).</param>
+    /// <param name="startFitness">Fitness value of the starting solution.</param>
+    /// <param name="target">The destination solution (to which distance should be decreased in every step).</param>
+    /// <param name="firstImprovement">Whether the walk should choose the first improving neighbor instead of scanning the whole neighborhood.</param>
+    /// <returns>The trail of the directed walk including start and target solution.</returns>
+    public static IEnumerable<Tuple<T, double>> Walk(bool maximization, Func<T, T, double> distFunc, Func<Tuple<T, double>, IEnumerable<Tuple<T, double>>> neighborFunc, T start, double startFitness, T target, bool firstImprovement = false) {
+      Func<T, T, bool> restriction = (current, neighbor) => distFunc(neighbor, target) < distFunc(current, target);
+      if (firstImprovement)
+        return DoFirstImprovementWalk(maximization, neighborFunc, start, startFitness, restriction);
+      return DoBestImprovementWalk(maximization, neighborFunc, start, startFitness, restriction);
+    }
+
+    /// <summary>
+    /// Provides regular directed walks between two solutions when already a restricted neighborhood is given.
+    /// </summary>
+    /// <param name="maximization">Whether the problem is a maximization or minimization problem.</param>
+    /// <param name="restrictedNeighborFunc">The neighborhood functions that returns only those neighbors that move closer to some target solution.</param>
+    /// <param name="start">The starting solution.</param>
+    /// <param name="startFitness">Fitness value of the starting solution.</param>
+    /// <param name="firstImprovement">Whether the walk should choose the first improving neighbor instead of scanning the whole neighborhood.</param>
+    /// <returns>The trail of the directed walk including start solution (and potentially target solution if it was part of the final restricted neighborhood).</returns>
+    public static IEnumerable<Tuple<T, double>> WalkRestricted(bool maximization, Func<Tuple<T, double>, IEnumerable<Tuple<T, double>>> restrictedNeighborFunc, T start, double startFitness, bool firstImprovement = false) {
+      if (firstImprovement)
+        return DoFirstImprovementWalk(maximization, restrictedNeighborFunc, start, startFitness, (_, __) => true);
+      return DoBestImprovementWalk(maximization, restrictedNeighborFunc, start, startFitness, (_, __) => true);
+    }
+
+    /// <summary>
+    /// Provides inverse directed walks away from a starting solution.
+    /// </summary>
+    /// <param name="maximization">Whether the problem is a maximization or minimization problem.</param>
+    /// <param name="distFunc">The distance function that compares two solutions.</param>
+    /// <param name="neighborFunc">The neighborhood functions that returns _all_ neighbors of a solution.</param>
+    /// <param name="start">The starting solution (to which distance should be increased in every step).</param>
+    /// <param name="startFitness">Fitness value of the starting solution.</param>
+    /// <param name="firstImprovement">Whether the walk should choose the first improving neighbor instead of scanning the whole neighborhood.</param>
+    /// <returns>The trail of the directed walk including start solution.</returns>
+    public static IEnumerable<Tuple<T, double>> InverseWalk(bool maximization, Func<T, T, double> distFunc, Func<Tuple<T, double>, IEnumerable<Tuple<T, double>>> neighborFunc, T start, double startFitness, bool firstImprovement = false) {
+      Func<T, T, bool> inverseRestriction = (current, neighbor) => distFunc(neighbor, start) > distFunc(current, start);
+      if (firstImprovement)
+        return DoFirstImprovementWalk(maximization, neighborFunc, start, startFitness, inverseRestriction);
+      return DoBestImprovementWalk(maximization, neighborFunc, start, startFitness, inverseRestriction);
+    }
+
+    /// <summary>
+    /// Provides inverse directed walks away from a starting solution when already a restricted neighborhood is given.
+    /// </summary>
+    /// <param name="maximization">Whether the problem is a maximization or minimization problem.</param>
+    /// <param name="restrictedNeighborFunc">The neighborhood functions that returns only those neighbors that move away from the start solution.</param>
+    /// <param name="start">The starting solution.</param>
+    /// <param name="startFitness">Fitness value of the starting solution.</param>
+    /// <param name="firstImprovement">Whether the walk should choose the first improving neighbor instead of scanning the whole neighborhood.</param>
+    /// <returns>The trail of the directed walk including start solution.</returns>
+    public static IEnumerable<Tuple<T, double>> InverseWalkRestricted(bool maximization, Func<Tuple<T, double>, IEnumerable<Tuple<T, double>>> restrictedNeighborFunc, T start, double startFitness, bool firstImprovement = false) {
+      if (firstImprovement)
+        return DoFirstImprovementWalk(maximization, restrictedNeighborFunc, start, startFitness, (_, __) => true);
+      return DoBestImprovementWalk(maximization, restrictedNeighborFunc, start, startFitness, (_, __) => true);
+    }
+
+    private static IEnumerable<Tuple<T, double>> DoBestImprovementWalk(bool maximization, Func<Tuple<T, double>, IEnumerable<Tuple<T, double>>> neighborFunc, T start, double startFitness, Func<T, T, bool> restriction) {
+      var current = Tuple.Create(start, startFitness);
+
+      while (true) {
+        yield return current;
+
+        var iterator = neighborFunc(current).Where(n => restriction(current.Item1, n.Item1)).GetEnumerator();
+        if (!iterator.MoveNext()) yield break;
+
+        current = Tuple.Create((T)iterator.Current.Item1.Clone(), iterator.Current.Item2);
+
+        while (iterator.MoveNext()) {
+          var other = iterator.Current;
+          if (maximization && other.Item2 > current.Item2 || !maximization && other.Item2 < current.Item2) {
+            current = Tuple.Create((T)other.Item1.Clone(), other.Item2);
+          }
+        }
+      }
+    }
+
+    private static IEnumerable<Tuple<T, double>> DoFirstImprovementWalk(bool maximization, Func<Tuple<T, double>, IEnumerable<Tuple<T, double>>> neighborFunc, T start, double startFitness, Func<T, T, bool> restriction) {
+      var current = Tuple.Create(start, startFitness);
+
+      while (true) {
+        yield return current;
+
+        var iterator = neighborFunc(current).Where(n => restriction(current.Item1, n.Item1)).GetEnumerator();
+        if (!iterator.MoveNext()) yield break;
+
+        var prevFitness = current.Item2;
+
+        current = Tuple.Create((T)iterator.Current.Item1.Clone(), iterator.Current.Item2);
+
+        if (maximization && current.Item2 > prevFitness || !maximization && current.Item2 < prevFitness)
+          continue;
+
+        while (iterator.MoveNext()) {
+          var other = iterator.Current;
+          if (maximization && other.Item2 > current.Item2 || !maximization && other.Item2 < current.Item2) {
+            current = Tuple.Create((T)other.Item1.Clone(), other.Item2);
+
+            if (maximization && current.Item2 > prevFitness || !maximization && current.Item2 < prevFitness)
+              break;
+          }
+        }
+      }
+    }
+  }
+}
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalyzer.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalyzer.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/InformationAnalyzer.cs	(revision 16096)
@@ -20,4 +20,5 @@
 #endregion
 
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -27,6 +28,4 @@
 using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using System.Linq;
-using System;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
@@ -125,32 +124,32 @@
       var ic = new DoubleValue(analysis.InformationContent.FirstOrDefault());
       InformationContentParameter.ActualValue = ic;
-      AddOrUpdateResult(results, InformationContentParameter.Name, ic);
+      results.AddOrUpdateResult(InformationContentParameter.Name, ic);
       var pic = new DoubleValue(analysis.PartialInformationContent.FirstOrDefault());
       PartialInformationContentParameter.ActualValue = pic;
-      AddOrUpdateResult(results, PartialInformationContentParameter.Name, pic);
+      results.AddOrUpdateResult(PartialInformationContentParameter.Name, pic);
       var dbi = new DoubleValue(analysis.DensityBasinInformation.FirstOrDefault());
       DensityBasinInformationParameter.ActualValue = dbi;
-      AddOrUpdateResult(results, DensityBasinInformationParameter.Name, dbi);
+      results.AddOrUpdateResult(DensityBasinInformationParameter.Name, dbi);
       var @is = new DoubleValue(analysis.InformationStability);
       InformationStabilityParameter.ActualValue = @is;
-      AddOrUpdateResult(results, InformationStabilityParameter.Name, @is);
+      results.AddOrUpdateResult(InformationStabilityParameter.Name, @is);
       var regularity = new DoubleValue(1.0 * analysis.Regularity / qualities.Count);
       RegularityParameter.ActualValue = regularity;
-      AddOrUpdateResult(results, RegularityParameter.Name, regularity);
+      results.AddOrUpdateResult(RegularityParameter.Name, regularity);
       var diversity = new DoubleValue(1.0 * analysis.Diversity / qualities.Count);
       DiversityParameter.ActualValue = diversity;
-      AddOrUpdateResult(results, DiversityParameter.Name, diversity);
+      results.AddOrUpdateResult(DiversityParameter.Name, diversity);
       var totalEntropy = new DoubleValue(analysis.TotalEntropy.FirstOrDefault());
       TotalEntropyParameter.ActualValue = totalEntropy;
-      AddOrUpdateResult(results, TotalEntropyParameter.Name, totalEntropy);
+      results.AddOrUpdateResult(TotalEntropyParameter.Name, totalEntropy);
       var peakIc = new DoubleValue(analysis.PeakInformationContent.Value);
       PeakInformationContentParameter.ActualValue = peakIc;
-      AddOrUpdateResult(results, PeakInformationContentParameter.Name, peakIc);
+      results.AddOrUpdateResult(PeakInformationContentParameter.Name, peakIc);
       var peakDbi = new DoubleValue(analysis.PeakDensityBasinInformation.Value);
       PeakDensityBasinInformationParameter.ActualValue = peakDbi;
-      AddOrUpdateResult(results, PeakDensityBasinInformationParameter.Name, peakDbi);
+      results.AddOrUpdateResult(PeakDensityBasinInformationParameter.Name, peakDbi);
 
       var itable = GetInformationTable(analysis);
-      AddOrUpdateResult(results, InformationAnalysisParameter.Name, itable);
+      results.AddOrUpdateResult(InformationAnalysisParameter.Name, itable);
       return base.Apply();
     }
@@ -176,11 +175,4 @@
       return dt;
     }
-
-    private static void AddOrUpdateResult(ResultCollection results, string name, IItem item, bool clone = false) {
-      IResult r;
-      if (!results.TryGetValue(name, out r)) {
-        results.Add(new Result(name, clone ? (IItem)item.Clone() : item));
-      } else r.Value = clone ? (IItem)item.Clone() : item;
-    }
   }
 }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/RuggednessAnalyzer.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/RuggednessAnalyzer.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/RuggednessAnalyzer.cs	(revision 16096)
@@ -20,4 +20,5 @@
 #endregion
 
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -27,8 +28,7 @@
 using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using System.Linq;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
-  [Item("Ruggedness Analyzer", "Analyzes autocorrelation for one mutation step and correlation length.")]
+  [Item("Ruggedness Analyzer", "Analyzes autocorrelation for one mutation step and correlation length. Correlation length is calculated according to the statistical correlation length defined by Hordijk, W., 1996. A measure of landscapes. Evolutionary computation, 4(4), pp.335-360.")]
   [StorableClass]
   public class RuggednessAnalyzer : SingleSuccessorOperator, IQualityTrailAnalyzer {
@@ -50,4 +50,7 @@
       get { return (LookupParameter<IntValue>)Parameters["CorrelationLength"]; }
     }
+    public ValueLookupParameter<DoubleValue> LimitParameter {
+      get { return (ValueLookupParameter<DoubleValue>)Parameters["Limit"]; }
+    }
     #endregion
 
@@ -60,4 +63,5 @@
       Parameters.Add(new LookupParameter<DoubleValue>("AutoCorrelation1", "Autocorrelation for one mutation step."));
       Parameters.Add(new LookupParameter<IntValue>("CorrelationLength", "The correlation length."));
+      Parameters.Add(new ValueLookupParameter<DoubleValue>("Limit", "The limit for the statistical correlation length, if left empty 2 / sqrt(n), where n = length of walk will be used, as defined by Hordijk 1996."));
     }
 
@@ -70,20 +74,14 @@
       if (qualityTrail == null || qualityTrail.Rows.Count <= 0) return base.Apply();
       var results = ResultsParameter.ActualValue;
+      var limit = LimitParameter.ActualValue?.Value;
       double[] autocorrelationValues;
-      var correlationLength = RuggednessCalculator.CalculateCorrelationLength(qualityTrail.Rows.First().Values.ToArray(), out autocorrelationValues);
+      var correlationLength = RuggednessCalculator.CalculateCorrelationLength(qualityTrail.Rows.First().Values.ToArray(), out autocorrelationValues, limit);
       var cl = new IntValue(correlationLength);
       CorrelationLengthParameter.ActualValue = cl;
-      AddOrUpdateResult(results, CorrelationLengthParameter.Name, cl);
+      results.AddOrUpdateResult(CorrelationLengthParameter.Name, cl);
       var ac1 = new DoubleValue(autocorrelationValues.Length > 1 ? autocorrelationValues[1] : 0.0);
       AutoCorrelation1Parameter.ActualValue = ac1;
-      AddOrUpdateResult(results, AutoCorrelation1Parameter.Name, ac1);
+      results.AddOrUpdateResult(AutoCorrelation1Parameter.Name, ac1);
       return base.Apply();
-    }
-
-    private static void AddOrUpdateResult(ResultCollection results, string name, IItem item, bool clone = false) {
-      IResult r;
-      if (!results.TryGetValue(name, out r)) {
-        results.Add(new Result(name, clone ? (IItem)item.Clone() : item));
-      } else r.Value = clone ? (IItem)item.Clone() : item;
     }
   }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/RuggednessCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/RuggednessCalculator.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/RuggednessCalculator.cs	(revision 16096)
@@ -20,54 +20,18 @@
 #endregion
 
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Data;
-using HeuristicLab.Operators;
-using HeuristicLab.Parameters;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 using System;
 using System.Collections.Generic;
-using System.Linq;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
-  [Item("Ruggedness Calculator", "Calculates ruggedness descriptors froma given quality trail.")]
-  [StorableClass]
-  public class RuggednessCalculator : SingleSuccessorOperator {
-
-    #region Parameters
-    public LookupParameter<DataTable> QualityTrailParameter {
-      get { return (LookupParameter<DataTable>)Parameters["QualityTrail"]; }
-    }
-    public LookupParameter<IntValue> CorrelationLengthParameter {
-      get { return (LookupParameter<IntValue>)Parameters["CorrelationLength"]; }
-    }
-    public LookupParameter<DoubleArray> AutoCorrelationParameter {
-      get { return (LookupParameter<DoubleArray>)Parameters["AutoCorrelation"]; }
-    }
-    #endregion
-
-    #region Constructors & Cloning
-    [StorableConstructor]
-    protected RuggednessCalculator(bool deserializing) : base(deserializing) { }
-    protected RuggednessCalculator(RuggednessCalculator original, Cloner cloner) : base(original, cloner) { }
-    public RuggednessCalculator() {
-      Parameters.Add(new LookupParameter<DataTable>("QualityTrail", "Historical values of walk qualities"));
-      Parameters.Add(new LookupParameter<IntValue>("CorrelationLength", "Average maximum distances between correlated quality values."));
-      Parameters.Add(new LookupParameter<DoubleArray>("AutoCorrelation", "AutoCorrelation"));
-    }
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new RuggednessCalculator(this, cloner);
-    }
-    #endregion
-
-    public override IOperation Apply() {
-      double[] qualities = QualityTrailParameter.ActualValue.Rows.First().Values.ToArray();
-      double[] autocorrelation;
-      CorrelationLengthParameter.ActualValue = new IntValue(CalculateCorrelationLength(qualities, out autocorrelation));
-      AutoCorrelationParameter.ActualValue = new DoubleArray(autocorrelation);
-      return base.Apply();
-    }
-
-    public static int CalculateCorrelationLength(double[] qualities, out double[] acf) {
+  public static class RuggednessCalculator {
+    /// <summary>
+    /// Calculates statistical correlation length as defined by Hordijk, W., 1996. A measure of landscapes. Evolutionary computation, 4(4), pp.335-360.
+    /// </summary>
+    /// <param name="qualities">The quality trail observed.</param>
+    /// <param name="acf">The autocorrelation values for each step s, including 0 => acf[0] = 1.</param>
+    /// <param name="limit">The statistical limit, correlation length will be the last step before acf falls within this limit. If omitted it is calculated as 2 / sqrt(qualities.Length).</param>
+    /// <returns>The statistical correlation length</returns>
+    public static int CalculateCorrelationLength(double[] qualities, out double[] acf, double? limit = null) {
+      if (!limit.HasValue) limit = 2.0 / Math.Sqrt(qualities.Length);
       double[] correlations = new double[qualities.Length];
       alglib.corr.corrr1dcircular(qualities, qualities.Length, qualities, qualities.Length, ref correlations);
@@ -86,13 +50,9 @@
           value = 1;
         autocorrelation.Add(value);
-        if (value < 0 && correlationLength < 0) correlationLength = counter;
+        if (Math.Abs(value) < limit && correlationLength < 0) correlationLength = counter;
       }
       acf = autocorrelation.ToArray();
       return correlationLength - 1;
     }
-
-    public static bool AnyGreaterOne(double[] values) {
-      return values.Any(d => d > 1);
-    }
   }
 }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/UpDownWalkAnalyzer.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/UpDownWalkAnalyzer.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/Analysis/UpDownWalkAnalyzer.cs	(revision 16096)
@@ -20,4 +20,5 @@
 #endregion
 
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -27,5 +28,4 @@
 using HeuristicLab.Parameters;
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using System.Linq;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
@@ -123,11 +123,11 @@
               var uv = new DoubleValue(topVals.Variance());
               UpperVarianceParameter.ActualValue = uv;
-              AddOrUpdateResult(results, UpperVarianceParameter.Name, uv);
+              results.AddOrUpdateResult(UpperVarianceParameter.Name, uv);
               var ul = new DoubleValue(topLengths.Average());
               UpWalkLengthParameter.ActualValue = ul;
-              AddOrUpdateResult(results, UpWalkLengthParameter.Name, ul);
+              results.AddOrUpdateResult(UpWalkLengthParameter.Name, ul);
               var ulv = new DoubleValue(topLengths.Variance());
               UpWalkLenVarParameter.ActualValue = ulv;
-              AddOrUpdateResult(results, UpWalkLenVarParameter.Name, ulv);
+              results.AddOrUpdateResult(UpWalkLenVarParameter.Name, ulv);
             }
             if (bottoms.Count > 0) {
@@ -136,11 +136,11 @@
               var lv = new DoubleValue(bottomVals.Variance());
               LowerVarianceParameter.ActualValue = lv;
-              AddOrUpdateResult(results, LowerVarianceParameter.Name, lv);
+              results.AddOrUpdateResult(LowerVarianceParameter.Name, lv);
               var dl = new DoubleValue(bottomLengths.Average());
               DownWalkLengthParameter.ActualValue = dl;
-              AddOrUpdateResult(results, DownWalkLengthParameter.Name, dl);
+              results.AddOrUpdateResult(DownWalkLengthParameter.Name, dl);
               var dlv = new DoubleValue(bottomLengths.Variance());
               DownWalkLenVarParameter.ActualValue = dlv;
-              AddOrUpdateResult(results, DownWalkLenVarParameter.Name, dlv);
+              results.AddOrUpdateResult(DownWalkLenVarParameter.Name, dlv);
             }
           }
@@ -149,11 +149,4 @@
       return base.Apply();
     }
-
-    private static void AddOrUpdateResult(ResultCollection results, string name, IItem item, bool clone = false) {
-      IResult r;
-      if (!results.TryGetValue(name, out r)) {
-        results.Add(new Result(name, clone ? (IItem)item.Clone() : item));
-      } else r.Value = clone ? (IItem)item.Clone() : item;
-    }
   }
 }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/HeuristicLab.Analysis.FitnessLandscape-3.3.csproj
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/HeuristicLab.Analysis.FitnessLandscape-3.3.csproj	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/HeuristicLab.Analysis.FitnessLandscape-3.3.csproj	(revision 16096)
@@ -207,4 +207,5 @@
     <Compile Include="ProblemCharacteristicAnalysis\CharacteristicCalculator.cs" />
     <Compile Include="ProblemCharacteristicAnalysis\CurveAnalysis.cs" />
+    <Compile Include="Algorithms\DirectedWalk.cs" />
     <Compile Include="ProblemCharacteristicAnalysis\DoubleMatrixCharacteristicCalculator.cs" />
     <Compile Include="ProblemCharacteristicAnalysis\PermutationPathAnalysis.cs" />
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/CurveAnalysis.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/CurveAnalysis.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/CurveAnalysis.cs	(revision 16096)
@@ -1,3 +1,24 @@
-﻿using System;
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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;
@@ -7,13 +28,15 @@
     public static CurveAnalysisResult GetCharacteristics(List<List<Tuple<T, double>>> trajectories, Func<T, T, double> distFunc) {
       trajectories = trajectories.Where(x => x.Count > 5).ToList();
+      var symbols = GetSymbols(trajectories);
       var f1 = trajectories.Select(path => ApproximateDerivative(path, distFunc).ToList()).ToList();
       var f2 = f1.Select(d1 => ApproximateDerivative(d1, distFunc).ToList()).ToList();
 
-      var sharpness = f1.Average(x => x.Average(y => Math.Abs(y.Item2)));//f2.Average(x => Area(x, distFunc));
+      var sharpness = f1.Average(x => x.Average(y => Math.Abs(y.Item2)));
       var bumpiness = 0.0;
       var flatness = 0.0;
-
+      var count = 0;
       for (var p = 0; p < f2.Count; p++) {
         if (f2[p].Count <= 2) continue;
+        count++;
         var bump = 0;
         var flat = 0;
@@ -28,34 +51,23 @@
         flatness += flat / (f2[p].Count - 1.0);
       }
-      bumpiness /= f2.Count;
-      flatness /= f2.Count;
-      return new CurveAnalysisResult(sharpness, bumpiness, flatness);
+      bumpiness /= count;
+      flatness /= count;
+      var per = new[] { 25, 50, 75 };
+      return new CurveAnalysisResult(sharpness, bumpiness, flatness,
+        per.Select(p => symbols.Downward.GetPercentileOrDefault(p, 0)).ToArray(),
+        per.Select(p => symbols.Neutral.GetPercentileOrDefault(p, 0)).ToArray(),
+        per.Select(p => symbols.Upward.GetPercentileOrDefault(p, 0)).ToArray());
     }
 
-    private static double Area(IEnumerable<Tuple<T, double>> path, Func<T, T, double> distFunc) {
-      var iter = path.GetEnumerator();
-      if (!iter.MoveNext()) return 0.0;
-      var area = 0.0;
-      var prev = iter.Current;
-      while (iter.MoveNext()) {
-        area += TrapezoidArea(prev, iter.Current, distFunc);
-        prev = iter.Current;
+    private static Symbols GetSymbols(List<List<Tuple<T, double>>> trajectories) {
+      var sym = new Symbols();
+      foreach (var t in trajectories) {
+        var prev = t[0];
+        for (var i = 1; i < t.Count; i++) {
+          sym.Add(i / (double)t.Count, t[i].Item2, prev.Item2);
+          prev = t[i];
+        }
       }
-      return area;
-    }
-
-    private static double TrapezoidArea(Tuple<T, double> a, Tuple<T, double> b, Func<T, T, double> distFunc) {
-      var area = 0.0;
-      var dist = distFunc(a.Item1, b.Item1);
-      if ((a.Item2 <= 0 && b.Item2 <= 0) || (a.Item2 >= 0 && b.Item2 >= 0))
-        area += dist * (Math.Abs(a.Item2) + Math.Abs(b.Item2)) / 2.0;
-      else {
-        var k = (b.Item2 - a.Item2) / dist;
-        var d = a.Item2;
-        var x = -d / k;
-        area += Math.Abs(x * a.Item2 / 2.0);
-        area += Math.Abs((dist - x) * b.Item2 / 2.0);
-      }
-      return area;
+      return sym;
     }
 
@@ -80,21 +92,98 @@
   }
 
+  public enum CurveAnalysisFeature { Sharpness, Bumpiness, Flatness,
+                                     DownQ1, DownQ2, DownQ3,
+                                     NeutQ1, NeutQ2, NeutQ3,
+                                     UpQ1, UpQ2, UpQ3 }
+
   public class CurveAnalysisResult {
-    public double Sharpness { get; private set; }
-    public double Bumpiness { get; private set; }
-    public double Flatness { get; private set; }
+    private Dictionary<CurveAnalysisFeature, double> results = new Dictionary<CurveAnalysisFeature, double>();
 
-    public string[] GetNames() {
-      return new[] { "Sharpness", "Bumpiness", "Flatness" };
+    public double GetValue(CurveAnalysisFeature name) {
+      return results[name];
+    }
+
+    public static IEnumerable<CurveAnalysisFeature> AllFeatures {
+      get { return Enum.GetValues(typeof(CurveAnalysisFeature)).Cast<CurveAnalysisFeature>(); }
     }
 
     public double[] GetValues() {
-      return new[] { Sharpness, Bumpiness, Flatness };
+      return AllFeatures.Select(x => results[x]).ToArray();
     }
 
-    public CurveAnalysisResult(double sharpness, double bumpiness, double flatness) {
-      Sharpness = sharpness;
-      Bumpiness = bumpiness;
-      Flatness = flatness;
+    public CurveAnalysisResult(double sharpness, double bumpiness, double flatness, double[] down, double[] neut, double[] up) {
+      foreach (var v in AllFeatures.Zip(new[] { sharpness, bumpiness, flatness }.Concat(down).Concat(neut).Concat(up), (n, v) => Tuple.Create(n, v))) {
+        results[v.Item1] = v.Item2;
+      }
+    }
+  }
+
+  public class Symbols {
+    public Statistics Downward { get; } = new Statistics();
+    public Statistics Neutral { get; } = new Statistics();
+    public Statistics Upward { get; } = new Statistics();
+
+    public void Add(double step, double fit, double prev) {
+      if (fit < prev) Downward.Add(step);
+      else if (fit > prev) Upward.Add(step);
+      else Neutral.Add(step);
+    }
+  }
+
+  public sealed class Statistics {
+    private List<double> values = new List<double>();
+
+    public int Count { get { return values.Count; } }
+
+    public double Min { get; private set; }
+    public double Max { get; private set; }
+    public double Total { get; private set; }
+    public double Mean { get; private set; }
+    public double StdDev { get { return Math.Sqrt(Variance); } }
+    public double Variance { get { return Count > 0 ? variance / Count : 0.0; } }
+    
+    private double variance;
+    private bool sorted = false;
+    
+    public double GetPercentileOrDefault(int p, double @default = default(double)) {
+      if (p < 0 || p > 100) throw new ArgumentOutOfRangeException(nameof(p), p, "Must be in range [0;100]");
+      SortIfNecessary();
+      if (Count == 0) return @default;
+      else if (Count == 1) return values[0];
+      if (p == 100) return values[Count - 1];
+
+      var x = p / 100.0 * (Count - 1);
+      var inte = (int)x;
+      var frac = x - inte;
+
+      return values[inte] + frac * (values[inte + 1] - values[inte]);
+    }
+
+    public void Add(double value) {
+      sorted = false;
+      values.Add(value);
+
+      if (Count == 1) {
+        Min = Max = Mean = Total = value;
+      } else {
+        if (value < Min) Min = value;
+        if (value > Max) Max = value;
+
+        Total += value;
+        var oldMean = Mean;
+        Mean = oldMean + (value - oldMean) / Count;
+        variance = variance + (value - oldMean) * (value - Mean);
+      }
+    }
+
+    public void AddRange(IEnumerable<double> values) {
+      foreach (var v in values) Add(v);
+    }
+
+    private void SortIfNecessary() {
+      if (!sorted) {
+        values.Sort();
+        sorted = true;
+      }
     }
   }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/PermutationPathAnalysis.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/PermutationPathAnalysis.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/PermutationPathAnalysis.cs	(revision 16096)
@@ -1,3 +1,24 @@
-﻿using System;
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2018 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.Encodings.PermutationEncoding;
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/QAP/QAPCharacteristicCalculator.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/QAP/QAPCharacteristicCalculator.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/QAP/QAPCharacteristicCalculator.cs	(revision 16096)
@@ -20,4 +20,7 @@
 #endregion
 
+using System;
+using System.Collections.Generic;
+using System.Linq;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -26,7 +29,4 @@
 using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
 using HeuristicLab.Problems.QuadraticAssignment;
-using System;
-using System.Collections.Generic;
-using System.Linq;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
@@ -61,23 +61,23 @@
         if (chara == "Dimension")
           yield return new Result(chara, new IntValue(qap.Weights.Rows));
-        if (chara == "FlowDominance")
+        else if (chara == "FlowDominance")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.CoeffVariation(qap.Weights)));
-        if (chara == "DistanceDominance")
+        else if (chara == "DistanceDominance")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.CoeffVariation(qap.Distances)));
-        if (chara == "FlowAsymmetry")
+        else if (chara == "FlowAsymmetry")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Asymmetry(qap.Weights)));
-        if (chara == "DistanceAsymmetry")
+        else if (chara == "DistanceAsymmetry")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Asymmetry(qap.Distances)));
-        if (chara == "FlowSparsity")
+        else if (chara == "FlowSparsity")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Sparsity(qap.Weights)));
-        if (chara == "DistanceSparsity")
+        else if (chara == "DistanceSparsity")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Sparsity(qap.Distances)));
-        if (chara == "FlowSkewness")
+        else if (chara == "FlowSkewness")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Skewness(qap.Weights)));
-        if (chara == "DistanceSkewness")
+        else if (chara == "DistanceSkewness")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Skewness(qap.Distances)));
-        if (chara == "FlowDisparity")
+        else if (chara == "FlowDisparity")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Disparity(qap.Weights)));
-        if (chara == "DistanceDisparity")
+        else if (chara == "DistanceDisparity")
           yield return new Result(chara, new DoubleValue(DoubleMatrixCharacteristicCalculator.Disparity(qap.Distances)));
       }
Index: /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/QAP/QAPDirectedWalk.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/QAP/QAPDirectedWalk.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Analysis.FitnessLandscape/3.3/ProblemCharacteristicAnalysis/QAP/QAPDirectedWalk.cs	(revision 16096)
@@ -20,4 +20,8 @@
 #endregion
 
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -29,8 +33,4 @@
 using HeuristicLab.Problems.QuadraticAssignment;
 using HeuristicLab.Random;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
 
 namespace HeuristicLab.Analysis.FitnessLandscape {
@@ -38,4 +38,8 @@
   [StorableClass]
   public class QAPDirectedWalk : CharacteristicCalculator {
+
+    public enum WalkType { RandomRandom, RandomGlobal, RandomLocal, LocalLocal, LocalGlobal, LocalInverse }
+
+    private const string NBHOOD_STR = "Swap2";
     
     public IFixedValueParameter<IntValue> PathsParameter {
@@ -51,6 +55,6 @@
     }
 
-    public IFixedValueParameter<BoolValue> LocalOptimaParameter {
-      get { return (IFixedValueParameter<BoolValue>)Parameters["LocalOptima"]; }
+    public IFixedValueParameter<EnumValue<WalkType>> TypeParameter {
+      get { return (IFixedValueParameter<EnumValue<WalkType>>)Parameters["Type"]; }
     }
 
@@ -70,7 +74,7 @@
     }
 
-    public bool LocalOptima {
-      get { return LocalOptimaParameter.Value.Value; }
-      set { LocalOptimaParameter.Value.Value = value; }
+    public WalkType Type {
+      get { return TypeParameter.Value.Value; }
+      set { TypeParameter.Value.Value = value;}
     }
 
@@ -79,10 +83,14 @@
     private QAPDirectedWalk(QAPDirectedWalk original, Cloner cloner) : base(original, cloner) { }
     public QAPDirectedWalk() {
-      characteristics.AddRange(new[] { "Swap2.Sharpness", "Swap2.Bumpiness", "Swap2.Flatness" }
-        .Select(x => new StringValue(x)).ToList());
+      characteristics.AddRange(CurveAnalysisResult.AllFeatures.Select(x => new StringValue(x.ToString())));
+      characteristics.AddRange(new[] { "AutoCorrelation1", "CorrelationLength", "InformationContent",
+        "DensityBasinInformation", "PartialInformationContent", "InformationStability",
+        "Diversity", "Regularity", "TotalEntropy", "PeakInformationContent",
+        "PeakDensityBasinInformation" }.Select(x => new StringValue(x)));
+
       Parameters.Add(new FixedValueParameter<IntValue>("Paths", "The number of paths to explore (a path is a set of solutions that connect two randomly chosen solutions).", new IntValue(50)));
       Parameters.Add(new FixedValueParameter<BoolValue>("BestImprovement", "Whether the best of all alternatives should be chosen for each step in the path or just the first improving (least degrading) move should be made.", new BoolValue(true)));
       Parameters.Add(new OptionalValueParameter<IntValue>("Seed", "The seed for the random number generator."));
-      Parameters.Add(new FixedValueParameter<BoolValue>("LocalOptima", "Whether to perform walks between local optima.", new BoolValue(false)));
+      Parameters.Add(new FixedValueParameter<EnumValue<WalkType>>("Type", "Type of directed walk to perfom.", new EnumValue<WalkType>(WalkType.RandomRandom)));
     }
 
@@ -95,129 +103,191 @@
     }
 
+    private double _evaluatedSolutions;
+
     public override IEnumerable<IResult> Calculate() {
+      _evaluatedSolutions = 0;
+      var permutations = CalculateRelinkingPoints();
+
+      var trajectories = Run(permutations).ToList();
+      var result = PermutationPathAnalysis.GetCharacteristics(trajectories);
+
+      #region Calculate Information Analysis Features
+      double avgAc1 = 0, avgCorLen = 0, avgIc = 0, avgDbi = 0, avgPic = 0, avgIS = 0, avgDiv = 0,
+        avgReg = 0, avgEnt = 0, avgPkIc = 0, avgPkDbi = 0;
+      int count = 0;
+      foreach (var t in trajectories) {
+        var trail = t.Select(x => x.Item2).ToArray();
+        if (trail.Length < 4) continue;
+        count++;
+        double[] acf;
+        var len = RuggednessCalculator.CalculateCorrelationLength(trail, out acf);
+        avgAc1 += acf[0];
+        avgCorLen += len;
+        var analysis = new InformationAnalysis(trail, 20, 2);
+        avgIc += analysis.InformationContent[0];
+        avgDbi += analysis.DensityBasinInformation[0];
+        avgPic += analysis.PartialInformationContent[0];
+        avgIS += analysis.InformationStability;
+        avgDiv += analysis.Diversity;
+        avgReg += analysis.Regularity;
+        avgEnt += analysis.TotalEntropy[0];
+        avgPkIc += analysis.PeakInformationContent.Value;
+        avgPkDbi += analysis.PeakDensityBasinInformation.Value;
+      }
+      avgAc1 /= count;
+      avgCorLen /= count;
+      avgIc /= count;
+      avgDbi /= count;
+      avgPic /= count;
+      avgIS /= count;
+      avgDiv /= count;
+      avgReg /= count;
+      avgEnt /= count;
+      avgPkIc /= count;
+      avgPkDbi /= count;
+      var isResults = new Dictionary<string, double>() {
+        { "AutoCorrelation1", avgAc1 },
+        { "CorrelationLength", avgCorLen },
+        { "InformationContent", avgIc },
+        { "DensityBasinInformation", avgDbi },
+        { "PartialInformationContent", avgPic },
+        { "InformationStability", avgIS },
+        { "Diversity", avgDiv },
+        { "Regularity", avgReg },
+        { "TotalEntropy", avgEnt },
+        { "PeakInformationContent", avgPkIc },
+        { "PeakDensityBasinInformation", avgPkDbi }
+      };
+      #endregion
+
+      foreach (var chara in characteristics.CheckedItems.Select(x => x.Value.Value)) {
+        if (Enum.TryParse<CurveAnalysisFeature>(chara, out var caf))
+          yield return new Result(Type.ToString() + "-DW." + NBHOOD_STR + "." + chara, new DoubleValue(result.GetValue(caf)));
+        else yield return new Result(Type.ToString() + "-DW." + NBHOOD_STR + "." + chara, new DoubleValue(isResults[chara]));
+      }
+      yield return new Result("EvaluatedSolutions", new IntValue((int)Math.Round(_evaluatedSolutions)));
+    }
+
+    private IEnumerable<Permutation> CalculateRelinkingPoints() {
       IRandom random = Seed.HasValue ? new MersenneTwister((uint)Seed.Value) : new MersenneTwister();
       var qap = (QuadraticAssignmentProblem)Problem;
-      List<Permutation> permutations = CalculateRelinkingPoints(random, qap, Paths, LocalOptima);
-
-      var trajectories = Run(random, (QuadraticAssignmentProblem)Problem, permutations, BestImprovement).ToList();
-      var result = PermutationPathAnalysis.GetCharacteristics(trajectories);
-
-      foreach (var chara in characteristics.CheckedItems.Select(x => x.Value.Value)) {
-        if (chara == "Swap2.Sharpness") yield return new Result("Swap2.Sharpness", new DoubleValue(result.Sharpness));
-        if (chara == "Swap2.Bumpiness") yield return new Result("Swap2.Bumpiness", new DoubleValue(result.Bumpiness));
-        if (chara == "Swap2.Flatness") yield return new Result("Swap2.Flatness", new DoubleValue(result.Flatness));
-      }
-    }
-
-    public static List<Permutation> CalculateRelinkingPoints(IRandom random, QuadraticAssignmentProblem qap, int pathCount, bool localOptima) {
+      var pathCount = Paths;
+
       var perm = new Permutation(PermutationTypes.Absolute, qap.Weights.Rows, random);
-      if (localOptima) {
-        var fit = new DoubleValue(QAPEvaluator.Apply(perm, qap.Weights, qap.Distances));
-        QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, qap.Weights, qap.Distances, fit, new IntValue(0), new IntValue(0), qap.Maximization.Value, int.MaxValue, CancellationToken.None);
-      }
-      var permutations = new List<Permutation> { perm };
-      while (permutations.Count < pathCount + 1) {
-        perm = (Permutation)permutations.Last().Clone();
-        BiasedShuffle(perm, random);
-        if (localOptima) {
+      var startLocal = Type == WalkType.LocalGlobal || Type == WalkType.LocalLocal;
+      var targetLocal = Type == WalkType.LocalLocal || Type == WalkType.RandomLocal;
+      var targetGlobal = Type == WalkType.LocalGlobal || Type == WalkType.RandomGlobal;
+      var inverseWalk = Type == WalkType.LocalInverse;
+
+      if (Type == WalkType.LocalInverse) pathCount--; // inverse walks equal one walk per solution
+      for (var i = 0; i <= pathCount; i++) {
+        var start = i % 2 == 0;
+        var target = i % 2 == 1;
+        if (inverseWalk || start && startLocal) {
+          perm = new Permutation(PermutationTypes.Absolute, qap.Weights.Rows, random);
           var fit = new DoubleValue(QAPEvaluator.Apply(perm, qap.Weights, qap.Distances));
-          QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, qap.Weights, qap.Distances, fit, new IntValue(0), new IntValue(0), qap.Maximization.Value, int.MaxValue, CancellationToken.None);
+          _evaluatedSolutions++;
+          var evalSol = new IntValue(0);
+          QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, qap.Weights, qap.Distances, fit, new IntValue(0), evalSol, qap.Maximization.Value, int.MaxValue, CancellationToken.None);
+          _evaluatedSolutions += evalSol.Value;
+        } else if (target && (targetLocal || targetGlobal)) {
+          if (targetGlobal) {
+            perm = qap.BestKnownSolutions.SampleRandom(random);
+          } else {
+            perm = new Permutation(PermutationTypes.Absolute, qap.Weights.Rows, random);
+            var fit = new DoubleValue(QAPEvaluator.Apply(perm, qap.Weights, qap.Distances));
+            _evaluatedSolutions++;
+            var evalSol = new IntValue(0);
+            QAPExhaustiveSwap2LocalImprovement.ImproveFast(perm, qap.Weights, qap.Distances, fit, new IntValue(0), evalSol, qap.Maximization.Value, int.MaxValue, CancellationToken.None);
+            _evaluatedSolutions += evalSol.Value;
+          }
+        } else { // random
+          BiasedShuffle(perm, random);
         }
-        if (HammingSimilarityCalculator.CalculateSimilarity(permutations.Last(), perm) < 0.75)
-          permutations.Add(perm);
-      }
-
-      return permutations;
-    }
-
-    public static IEnumerable<List<Tuple<Permutation, double>>> Run(IRandom random, QuadraticAssignmentProblem qap, IEnumerable<Permutation> permutations, bool bestImprovement = true) {
+        yield return (Permutation)perm.Clone();
+      }
+    }
+
+    private IEnumerable<List<Tuple<Permutation, double>>> Run(IEnumerable<Permutation> permutations) {
+      if (Type == WalkType.LocalInverse) return RunInverse(permutations);
+      return RunRegular(permutations);
+    }
+
+    private IEnumerable<List<Tuple<Permutation, double>>> RunInverse(IEnumerable<Permutation> permutations) {
+      var qap = (QuadraticAssignmentProblem)Problem;
+      var min = qap.LowerBound.Value;
+      var max = qap.AverageQuality.Value;
+      var bestImprovement = BestImprovement;
+
+      foreach (var start in permutations) {
+        var startFitness = QAPEvaluator.Apply(start, qap.Weights, qap.Distances);
+        _evaluatedSolutions++;
+
+        // inverse walks are applied to all solutions
+        Func<Tuple<Permutation, double>, IEnumerable<Tuple<Permutation, double>>> inverseNeighborFunc = (p) => RestrictedInverseNeighborhood(qap, p.Item1, p.Item2, start);
+        var walk = DirectedWalk<Permutation>.WalkRestricted(qap.Maximization.Value, inverseNeighborFunc, start, startFitness, !bestImprovement);
+        yield return walk.Select(x => Tuple.Create(x.Item1, (x.Item2 - min) / (max - min))).ToList();
+      } // end paths
+    }
+
+    private IEnumerable<List<Tuple<Permutation, double>>> RunRegular(IEnumerable<Permutation> permutations) {
       var iter = permutations.GetEnumerator();
       if (!iter.MoveNext()) yield break;
-
+      var bestImprovement = BestImprovement;
+
+      var qap = (QuadraticAssignmentProblem)Problem;
       var min = qap.LowerBound.Value;
       var max = qap.AverageQuality.Value;
 
       var start = iter.Current;
+      
       while (iter.MoveNext()) {
+        var startFitness = QAPEvaluator.Apply(start, qap.Weights, qap.Distances);
+        _evaluatedSolutions++;
         var end = iter.Current;
 
-        var walk = (bestImprovement ? BestDirectedWalk(qap, start, end) : FirstDirectedWalk(random, qap, start, end)).ToList();
-        yield return walk.Select(x => Tuple.Create(x.Item1, (x.Item2 - min) / (max - min))).ToList();
+        var invSol = new int[start.Length];
+        Func<Tuple<Permutation, double>, IEnumerable<Tuple<Permutation, double>>> regularNeighborFunc = (p) => RestrictedNeighborhood(qap, p.Item1, p.Item2, end, invSol);
+        var walk = DirectedWalk<Permutation>.WalkRestricted(qap.Maximization.Value, regularNeighborFunc, start, startFitness, !bestImprovement);
+
+        var trail = new List<Tuple<Permutation, double>>();
+        foreach (var step in walk) {
+          for (var i = 0; i < invSol.Length; i++)
+            invSol[step.Item1[i]] = i;
+          trail.Add(Tuple.Create(step.Item1, (step.Item2 - min) / (max - min)));
+        }
+        yield return trail;
+
         start = end;
       } // end paths
     }
 
-    private static IEnumerable<Tuple<Permutation, double>> BestDirectedWalk(QuadraticAssignmentProblem qap, Permutation start, Permutation end) {
-      var N = qap.Weights.Rows;
-      var sol = start;
-      var fitness = QAPEvaluator.Apply(sol, qap.Weights, qap.Distances);
-      yield return Tuple.Create(sol, fitness);
-
-      var invSol = GetInverse(sol);
-      // we require at most N-1 steps to move from one permutation to another
-      for (var step = 0; step < N - 1; step++) {
-        var bestFitness = double.MaxValue;
-        var bestIndex = -1;
-        sol = (Permutation)sol.Clone();
-        for (var index = 0; index < N; index++) {
-          if (sol[index] == end[index]) continue;
-          var fit = QAPSwap2MoveEvaluator.Apply(sol, new Swap2Move(index, invSol[end[index]]), qap.Weights, qap.Distances);
-          if (fit < bestFitness) { // QAP is minimization
-            bestFitness = fit;
-            bestIndex = index;
-          }
+    private IEnumerable<Tuple<Permutation, double>> RestrictedInverseNeighborhood(QuadraticAssignmentProblem qap, Permutation current, double currentFit, Permutation start) {
+      var evalSolPerMove = 4.0 / current.Length;
+      var N = current.Length;
+      for (var index = 0; index < N; index++) {
+        if (current[index] != start[index]) continue;
+        for (var k = 0; k < N; k++) {
+          if (k == index) continue;
+          var fit = QAPSwap2MoveEvaluator.Apply(current, new Swap2Move(index, k), qap.Weights, qap.Distances);
+          _evaluatedSolutions += evalSolPerMove;
+          current.Swap(index, k);
+          yield return Tuple.Create(current, currentFit + fit);
+          current.Swap(index, k); // reverse
         }
-        if (bestIndex >= 0) {
-          var prev = sol[bestIndex];
-          Swap2Manipulator.Apply(sol, bestIndex, invSol[end[bestIndex]]);
-          fitness += bestFitness;
-          yield return Tuple.Create(sol, fitness);
-          invSol[prev] = invSol[end[bestIndex]];
-          invSol[sol[bestIndex]] = bestIndex;
-        } else break;
-      }
-    }
-
-    private static IEnumerable<Tuple<Permutation, double>> FirstDirectedWalk(IRandom random, QuadraticAssignmentProblem qap, Permutation start, Permutation end) {
-      var N = qap.Weights.Rows;
-      var sol = start;
-      var fitness = QAPEvaluator.Apply(sol, qap.Weights, qap.Distances);
-      yield return Tuple.Create(sol, fitness);
-
-      var invSol = GetInverse(sol);
-      // randomize the order in which improvements are tried
-      var order = Enumerable.Range(0, N).Shuffle(random).ToArray();
-      // we require at most N-1 steps to move from one permutation to another
-      for (var step = 0; step < N - 1; step++) {
-        var bestFitness = double.MaxValue;
-        var bestIndex = -1;
-        sol = (Permutation)sol.Clone();
-        for (var i = 0; i < N; i++) {
-          var index = order[i];
-          if (sol[index] == end[index]) continue;
-          var fit = QAPSwap2MoveEvaluator.Apply(sol, new Swap2Move(index, invSol[end[index]]), qap.Weights, qap.Distances);
-          if (fit < bestFitness) { // QAP is minimization
-            bestFitness = fit;
-            bestIndex = index;
-            if (bestFitness < 0) break;
-          }
-        }
-        if (bestIndex >= 0) {
-          var prev = sol[bestIndex];
-          Swap2Manipulator.Apply(sol, bestIndex, invSol[end[bestIndex]]);
-          fitness += bestFitness;
-          yield return Tuple.Create(sol, fitness);
-          invSol[prev] = invSol[end[bestIndex]];
-          invSol[sol[bestIndex]] = bestIndex;
-        } else break;
-      }
-    }
-
-    private static int[] GetInverse(Permutation p) {
-      var inv = new int[p.Length];
-      for (var i = 0; i < p.Length; i++) {
-        inv[p[i]] = i;
-      }
-      return inv;
+      }
+    }
+
+    private IEnumerable<Tuple<Permutation, double>> RestrictedNeighborhood(QuadraticAssignmentProblem qap, Permutation current, double currentFit, Permutation target, int[] inverse) {
+      var evalSolPerMove = 4.0 / current.Length;
+      for (var index = 0; index < current.Length; index++) {
+        if (current[index] == target[index]) continue;
+        var k = inverse[target[index]];
+        var fit = QAPSwap2MoveEvaluator.Apply(current, new Swap2Move(index, k), qap.Weights, qap.Distances);
+        _evaluatedSolutions += evalSolPerMove;
+        current.Swap(index, k);
+        yield return Tuple.Create(current, currentFit + fit);
+        current.Swap(index, k); // reverse
+      }
     }
 
Index: /branches/2457_ExpertSystem/HeuristicLab.Problems.Instances.QAPLIB/3.3/OneSizeInstanceProvider.cs
===================================================================
--- /branches/2457_ExpertSystem/HeuristicLab.Problems.Instances.QAPLIB/3.3/OneSizeInstanceProvider.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/HeuristicLab.Problems.Instances.QAPLIB/3.3/OneSizeInstanceProvider.cs	(revision 16096)
@@ -3,31 +3,68 @@
 using System.Linq;
 using System.Text.RegularExpressions;
+using HeuristicLab.Common;
 
 namespace HeuristicLab.Problems.Instances.QAPLIB {
+  public class OneSize10InstanceProvider : OneSizeInstanceProvider {
+    public OneSize10InstanceProvider() : base(10) { }
+  }
+  public class OneSize25InstanceProvider : OneSizeInstanceProvider {
+    public OneSize25InstanceProvider() : base(25) { }
+  }
+  public class OneSize50InstanceProvider : OneSizeInstanceProvider {
+    public OneSize50InstanceProvider() : base(50) { }
+  }
+  public class OneSize100InstanceProvider : OneSizeInstanceProvider {
+    public OneSize100InstanceProvider() : base(100) { }
+  }
+
   public class OneSizeInstanceProvider : ProblemInstanceProvider<QAPData> {
+    public override string Name { get { return "One Size (n = " + Dimension + ")"; } }
+    public override string Description { get { return "Instances from various libraries reduced to a dimension of " + Dimension + "."; } }
+    public override Uri WebLink { get { return null; } }
+    public override string ReferencePublication {
+      get {
+        return
+@"A. Beham, E. Pitzer, S. Wagner, M. Affenzeller. 2017.
+Integrating Exploratory Landscape Analysis into Metaheuristic Algorithms.
+Lecture Notes in Computer Science 10671, Las Palmas de Gran Canaria, Spanien, pp. 473-480";
+      }
+    }
+
+    public int Dimension { get; private set; }
+
+    public OneSizeInstanceProvider(int dimension) {
+      Dimension = dimension;
+    }
+
     public override IEnumerable<IDataDescriptor> GetDataDescriptors() {
       var drezner = new DreznerQAPInstanceProvider();
       foreach (var desc in drezner.GetDataDescriptors()) {
         var dim = int.Parse(Regex.Match(desc.Name, "dre(?<g>\\d+)").Groups["g"].Value);
-        if (dim < 25) continue;
-        yield return new OneSizeDataDescriptor(desc.Name + (dim == 25 ? "" : "-25"), desc.Description, drezner, desc);
+        if (dim < Dimension) continue;
+        yield return new OneSizeDataDescriptor(desc.Name + (dim == Dimension ? "" : "-" + Dimension), desc.Description, drezner, desc);
       }
       // Microarray instances are all greater than 25 dimensions
       var microarray = new MicroarrayQAPInstanceProvider();
       foreach (var desc in microarray.GetDataDescriptors()) {
-        yield return new OneSizeDataDescriptor(desc.Name + "-25", desc.Description, microarray, desc);
+        var instance = microarray.LoadData(desc);
+        if (instance.Dimension < Dimension) continue;
+        yield return new OneSizeDataDescriptor(desc.Name + (instance.Dimension == Dimension ? "" : "-" + Dimension), desc.Description, microarray, desc);
       }
       var qaplib = new QAPLIBInstanceProvider();
       foreach (var desc in qaplib.GetDataDescriptors()) {
         var instance = qaplib.LoadData(desc);
-        if (instance.Dimension < 25) continue;
-        yield return new OneSizeDataDescriptor(desc.Name + (instance.Dimension == 25 ? "" : "-25"), desc.Description, qaplib, desc);
+        if (instance.Dimension < Dimension) continue;
+        yield return new OneSizeDataDescriptor(desc.Name + (instance.Dimension == Dimension ? "" : "-" + Dimension), desc.Description, qaplib, desc);
       }
       // Taillard's instances are basically from the same distribution
       // to avoid over-representation in the set only the tai27e are taken and reduced to 25 dimension
       var taillard = new TaillardQAPInstanceProvider();
-      foreach (var desc in taillard.GetDataDescriptors()) {
-        if (!desc.Name.StartsWith("tai27e")) continue;
-        yield return new OneSizeDataDescriptor(desc.Name + "-25", desc.Description, taillard, desc);
+      var count = 0;
+      foreach (var desc in taillard.GetDataDescriptors().OrderBy(x => x.Name, new NaturalStringComparer())) {
+        var dim = int.Parse(Regex.Match(desc.Name, "tai(?<g>\\d+)e").Groups["g"].Value);
+        if (dim < Dimension) continue;
+        yield return new OneSizeDataDescriptor(desc.Name + (dim == Dimension ? "" : "-" + Dimension), desc.Description, taillard, desc);
+        if (++count == 10) break;
       }
     }
@@ -36,7 +73,6 @@
       var desc = (OneSizeDataDescriptor)descriptor;
       var data = desc.ActualProvider.LoadData(desc.ActualDescriptor);
-      data.BestKnownAssignment = null;
-      data.BestKnownQuality = null;
-      if (data.Dimension <= 25) {
+
+      if (data.Dimension <= Dimension) {
         return data;
       }
@@ -46,9 +82,9 @@
       Shuffle(tmp, rand);
       var throwAway = new bool[data.Dimension];
-      foreach (var t in tmp.Take(data.Dimension - 25))
+      foreach (var t in tmp.Take(data.Dimension - Dimension))
         throwAway[t] = true;
 
-      var weights = new double[25, 25];
-      var distances = new double[25, 25];
+      var weights = new double[Dimension, Dimension];
+      var distances = new double[Dimension, Dimension];
       var k = 0;
       for (var i = 0; i < data.Dimension; i++) {
@@ -68,15 +104,10 @@
       data.BestKnownAssignment = null;
       data.BestKnownQuality = null;
-      data.Dimension = 25;
-      data.Name += "-25";
-      data.Description += " (reduced to 25 dimensions)";
+      data.Dimension = Dimension;
+      data.Name += "-" + Dimension;
+      data.Description += " (reduced to " + Dimension + " dimensions)";
 
       return data;
     }
-
-    public override string Name { get { return "One Size"; } }
-    public override string Description { get { return string.Empty; } }
-    public override Uri WebLink { get { return null; } }
-    public override string ReferencePublication { get { return string.Empty; } }
     
     private static void Shuffle(int[] p, Random random) {
Index: /branches/2457_ExpertSystem/ProblemInstanceIdentifier/InstanceDescriptor.cs
===================================================================
--- /branches/2457_ExpertSystem/ProblemInstanceIdentifier/InstanceDescriptor.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/ProblemInstanceIdentifier/InstanceDescriptor.cs	(revision 16096)
@@ -1,9 +1,6 @@
-﻿using System;
-using System.Collections.Generic;
+﻿using System.Collections.Generic;
 using System.Linq;
 using System.Text.RegularExpressions;
-using HeuristicLab.Analysis.FitnessLandscape;
 using HeuristicLab.Common;
-using HeuristicLab.Encodings.PermutationEncoding;
 using HeuristicLab.Problems.QuadraticAssignment;
 
@@ -16,4 +13,6 @@
     public string[] FeatureNames { get; set; }
     public double[] FeatureValues { get; set; }
+
+    public double DescriptionEffort { get; set; }
     
     private InstanceDescriptor() { }
@@ -33,4 +32,5 @@
       FeatureNames = (string[])other.FeatureNames.Clone();
       FeatureValues = (double[]) other.FeatureValues.Clone();
+      DescriptionEffort = other.DescriptionEffort;
     }
 
@@ -41,17 +41,6 @@
         Dimension = qap.Weights.Rows,
         FeatureNames = new string[0],
-        FeatureValues = new double[0]
-      };
-    }
-
-    public static InstanceDescriptor FromPaths(QuadraticAssignmentProblem qap, List<List<Tuple<Permutation, double>>> trajectories) {
-      var result = PermutationPathAnalysis.GetCharacteristics(trajectories);
-
-      return new InstanceDescriptor() {
-        Name = qap.Name,
-        Cls = GetClass(qap.Name),
-        Dimension = qap.Weights.Rows,
-        FeatureNames = result.GetNames(),
-        FeatureValues = result.GetValues()
+        FeatureValues = new double[0],
+        DescriptionEffort = 0
       };
     }
Index: /branches/2457_ExpertSystem/ProblemInstanceIdentifier/InstanceExplorer.cs
===================================================================
--- /branches/2457_ExpertSystem/ProblemInstanceIdentifier/InstanceExplorer.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/ProblemInstanceIdentifier/InstanceExplorer.cs	(revision 16096)
@@ -1,10 +1,9 @@
 ﻿using System;
+using System.Collections.Generic;
 using System.Linq;
-using HeuristicLab.Algorithms.MemPR.Permutation;
 using HeuristicLab.Analysis.FitnessLandscape;
 using HeuristicLab.Data;
 using HeuristicLab.Encodings.PermutationEncoding;
 using HeuristicLab.Problems.QuadraticAssignment;
-using HeuristicLab.Random;
 using HeuristicLab.SequentialEngine;
 
@@ -21,9 +20,11 @@
   public class PathRelinkingExplorer : InstanceExplorer {
     public int Paths { get; set; }
-    public bool LocalOptima { get; set; }
+    public QAPDirectedWalk.WalkType Type { get; set; }
 
     public override string Name { get { return "Path-Relinking Explorer"; } }
     public override int Effort { get { return Paths; } }
 
+    public ISet<string> Features { get; set; }
+
     public PathRelinkingExplorer() {
       
@@ -31,78 +32,25 @@
 
     public override InstanceDescriptor Explore(QuadraticAssignmentProblem problem, int? seed = null) {
-      var walk = new QAPDirectedWalk {
+      var calculator = new QAPDirectedWalk {
         Problem = problem,
         BestImprovement = true,
         Paths = Paths,
         Seed = seed,
-        LocalOptima = LocalOptima
-      };
-      var features = walk.Calculate().ToDictionary(x => x.Name, x => x.Value);
-
-      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
-        features.Keys.ToArray(), features.Values.Select(x => ((DoubleValue)x).Value).ToArray());
-    }
-  }
-
-  public class PathRelinkingOldFeaturedExplorer : InstanceExplorer {
-    public int Paths { get; set; }
-    public bool LocalOptima { get; set; }
-
-    public override string Name { get { return "Path-Relinking Explorer"; } }
-    public override int Effort { get { return Paths; } }
-
-    public PathRelinkingOldFeaturedExplorer() {
-
-    }
-
-    public override InstanceDescriptor Explore(QuadraticAssignmentProblem problem, int? seed = null) {
-      var random = new MersenneTwister();
-      var samples = QAPDirectedWalk.CalculateRelinkingPoints(random, problem, Paths, LocalOptima);
-      var trajectories = QAPDirectedWalk.Run(random, problem, samples);
-
-      double avgAc1 = 0, avgCorLen = 0, avgIc = 0, avgDbi = 0, avgPic = 0, avgIs = 0, avgDiv = 0,
-        avgReg = 0, avgEnt = 0, avgPkIc = 0, avgPkDbi = 0;
-      int count = 0;
-      foreach (var t in trajectories) {
-        var trail = t.Select(x => x.Item2).ToArray();
-        if (trail.Length < 4) continue;
-        count++;
-        double[] acf;
-        var len = RuggednessCalculator.CalculateCorrelationLength(trail, out acf);
-        avgAc1 += acf[0];
-        avgCorLen += len;
-        var analysis = new InformationAnalysis(trail, 20, 2);
-        avgIc += analysis.InformationContent[0];
-        avgDbi += analysis.DensityBasinInformation[0];
-        avgPic += analysis.PartialInformationContent[0];
-        avgIs += analysis.InformationStability;
-        avgDiv += analysis.Diversity;
-        avgReg += analysis.Regularity;
-        avgEnt += analysis.TotalEntropy[0];
-        avgPkIc += analysis.PeakInformationContent.Value;
-        avgPkDbi += analysis.PeakDensityBasinInformation.Value;
-      }
-      avgAc1 /= count;
-      avgCorLen /= count;
-      avgIc /= count;
-      avgDbi /= count;
-      avgPic /= count;
-      avgIs /= count;
-      avgDiv /= count;
-      avgReg /= count;
-      avgEnt /= count;
-      avgPkIc /= count;
-      avgPkDbi /= count;
-
-      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
-        new[] { "Autocorrelation(1)", "CorrelationLength", "InformationContent", "DensityBasinInformation",
-          "PartialInformationContent", "InformationStability", "Diversity", "Regularity", "TotalEntropy",
-          "PeakInformationContent", "PeakDensityBasinInformation" },
-        new[] { avgAc1, avgCorLen, avgIc, avgDbi,
-          avgPic, avgIs, avgDiv, avgReg, avgEnt,
-          avgPkIc, avgPkDbi });
-    }
-  }
-
+        Type = Type
+      };
+      if (Features != null && Features.Count > 0) {
+        foreach (var c in calculator.Characteristics.ToList()) {
+          calculator.Characteristics.SetItemCheckedState(c, Features.Contains(c.Value));
+        }
+      }
+      var result = calculator.Calculate().ToDictionary(x => x.Name, x => x.Value);
+      var evaluations = ((IntValue)result["EvaluatedSolutions"]).Value;
+      result.Remove("EvaluatedSolutions");
+      if (Features != null && result.Count != Features.Count) throw new InvalidOperationException("Not all features in results");
+
+      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
+        result.Keys.ToArray(), result.Values.Select(x => ((DoubleValue)x).Value).ToArray()) { DescriptionEffort = evaluations };
+    }
+  }
 
   public class RandomWalkExplorer : InstanceExplorer {
@@ -111,4 +59,6 @@
     public override string Name { get { return "Random-Walk Explorer"; } }
     public override int Effort { get { return Iterations; } }
+
+    public ISet<string> Features { get; set; }
 
     public RandomWalkExplorer() {
@@ -127,8 +77,12 @@
       walk.MutatorParameter.Value = walk.MutatorParameter.ValidValues.First(x => x is Swap2Manipulator);
       var calculator = new RandomWalkCalculator(walk) { Problem = problem };
-      var features = calculator.Calculate().ToDictionary(x => x.Name, x => x.Value);
-      
-      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
-        features.Keys.ToArray(), features.Values.Select(x => ((DoubleValue)x).Value).ToArray());
+      foreach (var c in calculator.Characteristics.ToList()) {
+        calculator.Characteristics.SetItemCheckedState(c, Features.Contains(c.Value));
+      }
+      var result = calculator.Calculate().ToDictionary(x => x.Name, x => x.Value);
+      if (Features != null && result.Count != Features.Count) throw new InvalidOperationException("Not all features in results");
+
+      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
+        result.Keys.ToArray(), result.Values.Select(x => ((DoubleValue)x).Value).ToArray());
     }
   }
@@ -139,8 +93,11 @@
 
     public override string Name { get { return "Adaptive-Walk Explorer"; } }
-    public override int Effort { get { return Iterations * SampleSize; } }
+    private int _effort;
+    public override int Effort { get { return _effort; } }
+
+    public ISet<string> Features { get; set; }
 
     public AdaptiveWalkExplorer() {
-
+      _effort = 0;
     }
 
@@ -157,8 +114,12 @@
       walk.MutatorParameter.Value = walk.MutatorParameter.ValidValues.First(x => x is Swap2Manipulator);
       var calculator = new AdaptiveWalkCalculator(walk) { Problem = problem };
-      var features = calculator.Calculate().ToDictionary(x => x.Name, x => x.Value);
-
-      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
-        features.Keys.ToArray(), features.Values.Select(x => ((DoubleValue)x).Value).ToArray());
+      foreach (var c in calculator.Characteristics.ToList()) {
+        calculator.Characteristics.SetItemCheckedState(c, Features.Contains(c.Value));
+      }
+      var result = calculator.Calculate().ToDictionary(x => x.Name, x => x.Value);
+      if (Features != null && result.Count != Features.Count) throw new InvalidOperationException("Not all features in results");
+
+      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
+        result.Keys.ToArray(), result.Values.Select(x => ((DoubleValue)x).Value).ToArray());
     }
   }
@@ -170,4 +131,6 @@
     public override string Name { get { return "Up/Down-Walk Explorer"; } }
     public override int Effort { get { return Iterations * SampleSize; } }
+
+    public ISet<string> Features { get; set; }
 
     public UpDownWalkExplorer() {
@@ -187,11 +150,15 @@
       walk.MutatorParameter.Value = walk.MutatorParameter.ValidValues.First(x => x is Swap2Manipulator);
       var calculator = new UpDownWalkCalculator(walk) {  Problem = problem };
-      var features = calculator.Calculate().ToDictionary(x => x.Name, x => x.Value);
-
-      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
-        features.Keys.ToArray(), features.Values.Select(x => ((DoubleValue)x).Value).ToArray());
-    }
-  }
-
+      foreach (var c in calculator.Characteristics.ToList()) {
+        calculator.Characteristics.SetItemCheckedState(c, Features.Contains(c.Value));
+      }
+      var result = calculator.Calculate().ToDictionary(x => x.Name, x => x.Value);
+      if (Features != null && result.Count != Features.Count) throw new InvalidOperationException("Not all features in results");
+
+      return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows,
+        result.Keys.ToArray(), result.Values.Select(x => ((DoubleValue)x).Value).ToArray());
+    }
+  }
+  /*
   public class MemPRExplorer : InstanceExplorer {
     public int Seconds { get; set; }
@@ -230,4 +197,4 @@
       return new InstanceDescriptor(problem.Name, InstanceDescriptor.GetClass(problem.Name), problem.Weights.Rows, resultNames, result);
     }
-  }
+  }*/
 }
Index: /branches/2457_ExpertSystem/ProblemInstanceIdentifier/ProblemInstanceIdentifier.csproj
===================================================================
--- /branches/2457_ExpertSystem/ProblemInstanceIdentifier/ProblemInstanceIdentifier.csproj	(revision 16095)
+++ /branches/2457_ExpertSystem/ProblemInstanceIdentifier/ProblemInstanceIdentifier.csproj	(revision 16096)
@@ -97,8 +97,4 @@
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\HeuristicLab.Algorithms.MemPR\3.3\HeuristicLab.Algorithms.MemPR-3.3.csproj">
-      <Project>{9d274421-6332-4fbc-aae4-467ace27c368}</Project>
-      <Name>HeuristicLab.Algorithms.MemPR-3.3</Name>
-    </ProjectReference>
     <ProjectReference Include="..\HeuristicLab.Analysis.FitnessLandscape\3.3\HeuristicLab.Analysis.FitnessLandscape-3.3.csproj">
       <Project>{5fbdcd4a-3c2a-4ec6-83ce-34b29f43621a}</Project>
Index: /branches/2457_ExpertSystem/ProblemInstanceIdentifier/Program.cs
===================================================================
--- /branches/2457_ExpertSystem/ProblemInstanceIdentifier/Program.cs	(revision 16095)
+++ /branches/2457_ExpertSystem/ProblemInstanceIdentifier/Program.cs	(revision 16096)
@@ -2,8 +2,8 @@
 using System.Collections.Generic;
 using System.Globalization;
+using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
-using HeuristicLab.PluginInfrastructure;
-using HeuristicLab.Problems.Instances;
+using HeuristicLab.Analysis.FitnessLandscape;
 using HeuristicLab.Problems.Instances.QAPLIB;
 using HeuristicLab.Problems.QuadraticAssignment;
@@ -13,6 +13,4 @@
   class Program {
     static void Main(string[] args) {
-      //var instances = Get20DifferentClasses();
-      var instances = GetSomeRandomInstances(50, 13);
 
       /*var classes = instances.Select(InstanceDescriptor.FromProblemOnly).GroupBy(x => x.Cls);
@@ -21,30 +19,52 @@
       }*/
 
-      var prExplorer = new PathRelinkingExplorer() {
-        LocalOptima = false,
-        Paths = 200
-      };
-      var prOldExplorer = new PathRelinkingOldFeaturedExplorer() {
-        LocalOptima = false,
-        Paths = 200
-      };
-
-      var prLocalExplorer = new PathRelinkingExplorer() {
-        LocalOptima = true,
-        Paths = 200
-      };
-      var prOldLocalExplorer = new PathRelinkingOldFeaturedExplorer() {
-        LocalOptima = true,
-        Paths = 200
-      };
-
-      var memPrExplorer = new MemPRExplorer() {
-        IncludeLocalSearch = false,
-        Seconds = 10
-      };
-
-      //var training = GenerateData(instances, prOldExplorer, parallel: true);
+      var dwFeatureSets = new Dictionary<string, HashSet<string>>() {
+        { "SBF", new HashSet<string>() { "Sharpness", "Bumpiness", "Flatness" } },
+        { "FRQ", new HashSet<string>() { "DownQ2", "NeutQ2", "UpQ2", "DownQ1", "NeutQ1", "UpQ1", "DownQ3", "NeutQ3", "UpQ3" } },
+        { "SBF+FRQ", new HashSet<string>() { "Sharpness", "Bumpiness", "Flatness", "DownQ2", "NeutQ2", "UpQ2", "DownQ1", "NeutQ1", "UpQ1", "DownQ3", "NeutQ3", "UpQ3" } },
+        { "RUG", new HashSet<string>() { "AutoCorrelation1", "CorrelationLength" } },
+        { "IAL", new HashSet<string>() { "InformationContent", "DensityBasinInformation", "PartialInformationContent", "Regularity", "TotalEntropy", "PeakInformationContent", "PeakDensityBasinInformation" } },
+        { "RUG+IAL", new HashSet<string>() { "AutoCorrelation1", "CorrelationLength", "InformationContent", "DensityBasinInformation", "PartialInformationContent", "Regularity", "TotalEntropy", "PeakInformationContent", "PeakDensityBasinInformation" } },
+        { "SBF+IAL", new HashSet<string>() { "Sharpness", "Bumpiness", "Flatness", "InformationContent", "DensityBasinInformation", "PartialInformationContent", "Regularity", "TotalEntropy", "PeakInformationContent", "PeakDensityBasinInformation" } },
+        { "FRQ+IAL", new HashSet<string>() { "DownQ2", "NeutQ2", "UpQ2", "DownQ1", "NeutQ1", "UpQ1", "DownQ3", "NeutQ3", "UpQ3", "InformationContent", "DensityBasinInformation", "PartialInformationContent", "Regularity", "TotalEntropy", "PeakInformationContent", "PeakDensityBasinInformation" } },
+        { "SBF+FRQ+IAL", new HashSet<string>() { "Sharpness", "Bumpiness", "Flatness", "DownQ2", "NeutQ2", "UpQ2", "DownQ1", "NeutQ1", "UpQ1", "DownQ3", "NeutQ3", "UpQ3", "InformationContent", "DensityBasinInformation", "PartialInformationContent", "Regularity", "TotalEntropy", "PeakInformationContent", "PeakDensityBasinInformation" } },
+        { "SBF+FRQ+RUG+IAL", new HashSet<string>() { "Sharpness", "Bumpiness", "Flatness", "DownQ2", "NeutQ2", "UpQ2", "DownQ1", "NeutQ1", "UpQ1", "DownQ3", "NeutQ3", "UpQ3", "AutoCorrelation1", "CorrelationLength", "InformationContent", "DensityBasinInformation", "PartialInformationContent", "Regularity", "TotalEntropy", "PeakInformationContent", "PeakDensityBasinInformation" } },
+      };
+
+      var dwTypes = new Dictionary<string, QAPDirectedWalk.WalkType> {
+        { "(rr)-dw", QAPDirectedWalk.WalkType.RandomRandom },
+        { "(rl)-dw", QAPDirectedWalk.WalkType.RandomLocal },
+        { "(ll)-dw", QAPDirectedWalk.WalkType.LocalLocal },
+        { "(li)-dw", QAPDirectedWalk.WalkType.LocalInverse }
+      };
+
+      var dwPaths = new List<int> { 1, 2, 5, 10, 20, 50, 100, 200, 500 };
+
+      var random = new Random();
+      using (var writer = File.CreateText("results.csv")) {
+        writer.AutoFlush = true;
+        var header = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}\t{11}",
+          "Rep", "FSet", "Type", "TrainEff", "TestEff", "ExCnt", "ExRnk", "ClsCnt", "ClsRnk", "TotCnt", "TrainEff2", "TestEff2");
+        writer.WriteLine(header);
+        Console.WriteLine(header);
+        foreach (var dim in new[] { 20, 30, 40, 50, 100 }) {
+          foreach (var feat in dwFeatureSets) {
+            foreach (var type in dwTypes) {
+              for (var rep = 0; rep < 10; rep++) {
+                var instances = GetSomeRandomInstances(dimension: dim, totalInstances: 20, seed: random.Next());
+
+                var explorers = from p in dwPaths
+                                select new PathRelinkingExplorer() { Features = feat.Value, Paths = p, Type = type.Value };
+                ExploreMatching(writer, instances, explorers.ToArray(), explorers.ToArray(), string.Format("{0}\t{1}\t{2}", rep, feat.Key, type.Key));
+              }
+            }
+          }
+        }
+      }
+
+
+      //var training = GenerateData(instances, rrDwExplorer, parallel: true);
       //var standardizer = InstancesStandardizer.CreateAndApply(training);
-      //var test = GenerateData(instances, prOldExplorer, parallel: true);
+      //var test = GenerateData(instances, rrDwExplorer, parallel: true);
       //standardizer.Apply(test);
       //PrintMatchResult(Compare(training, test));
@@ -65,14 +85,14 @@
       //new[] { 300, 1500, 3000, 6000, 15000, 30000, 60000 }.Select(x => new AdaptiveWalkExplorer() { Iterations = x / 10, SampleSize = 10 }).ToArray(),
       //parallel: true);
-      Console.WriteLine("=== Up/Down Walk ===");
-      ExploreMatching(instances,
-      new[] { 300, 1500, 3000, 6000, 15000, 30000, 60000 }.Select(x => new UpDownWalkExplorer() { Iterations = x / 10, SampleSize = 10 }).ToArray(),
-      new[] { 300, 1500, 3000, 6000, 15000, 30000, 60000 }.Select(x => new UpDownWalkExplorer() { Iterations = x / 10, SampleSize = 10 }).ToArray(),
-      parallel: true);
-    }
-
-    private static List<QuadraticAssignmentProblem> GetSomeRandomInstances(int totalInstances, int seed) {
+      //Console.WriteLine("=== Up/Down Walk ===");
+      //ExploreMatching(instances,
+      //new[] { 300, 1500, 3000, 6000, 15000, 30000, 60000 }.Select(x => new UpDownWalkExplorer() { Iterations = x / 10, SampleSize = 10 }).ToArray(),
+      //new[] { 300, 1500, 3000, 6000, 15000, 30000, 60000 }.Select(x => new UpDownWalkExplorer() { Iterations = x / 10, SampleSize = 10 }).ToArray(),
+      //parallel: true);
+    }
+
+    private static List<QuadraticAssignmentProblem> GetSomeRandomInstances(int dimension, int totalInstances, int seed) {
       var sync = new object();
-      var provider = new OneSizeInstanceProvider();
+      var provider = new OneSizeInstanceProvider(dimension);
       var instances = new List<QuadraticAssignmentProblem>();
       var random = new FastRandom(seed);
@@ -87,31 +107,4 @@
         }
       });
-      return instances;
-    }
-
-    private static HashSet<string> selectedInstances = new HashSet<string>() {
-      "bur26a", "chr25a", "dre24", "els19", "esc32a", "had20", "kra32", "lipa30a", "lipa30b",
-      "nug30", "rou20", "scr20", "sko42", "ste36a", "tai25a", "tai25b", "tho30", "wil50",
-      "RAND-S-6x6-36-25-AFFY-00_rand_rand_bl", "RAND-S-6x6-36-25-AFFY-00_rand_rand_ci"
-    };
-    private static List<QuadraticAssignmentProblem> Get20DifferentClasses() {
-      var sync = new object();
-
-      var qapProviders = ApplicationManager.Manager.GetInstances<ProblemInstanceProvider<QAPData>>().ToList();
-      var instances = new List<QuadraticAssignmentProblem>();
-      foreach (var provider in qapProviders) {
-        if (provider is TaillardQAPInstanceProvider) continue;
-        Parallel.ForEach(provider.GetDataDescriptors(), desc => {
-          if (!selectedInstances.Contains(desc.Name)) return;
-          //if (desc.Name == "esc16f") return;
-          var qapData = provider.LoadData(desc);
-          //if (qapData.Dimension < 15 || qapData.Dimension > 150) return;
-          var qap = new QuadraticAssignmentProblem();
-          qap.Load(qapData);
-          lock (sync) {
-            instances.Add(qap);
-          }
-        });
-      }
       return instances;
     }
@@ -155,12 +148,8 @@
         TotalCount = totalCount,
         ExactAverageRank = exactRank / (double)totalCount,
-        ClsAverageRank = clsRank / (double)totalCount
-      };
-    }
-
-    private static void PrintMatchResult(MatchResult result) {
-      Console.WriteLine("{0}\t{1}\t{2}\t{3:F2}\t{4:F2}",
-        result.ExactCount, result.ClsCount, result.TotalCount,
-        result.ExactAverageRank, result.ClsAverageRank);
+        ClsAverageRank = clsRank / (double)totalCount,
+        TrainingDescriptionEffort = training.Average(x => x.DescriptionEffort),
+        TestDescriptionEffort = test.Average(x => x.DescriptionEffort)
+      };
     }
 
@@ -168,5 +157,5 @@
       using (var iter = instances.GetEnumerator()) {
         if (!iter.MoveNext()) return;
-        Console.WriteLine(string.Join(";", new[] {"Name", "Cls", "Dimension"}
+        Console.WriteLine(string.Join(";", new[] {"Name", "Cls", "Dimension", "Effort"}
           .Concat(iter.Current != null ? iter.Current.FeatureNames : new [] { "(null)" })));
         do {
@@ -178,9 +167,9 @@
     private static void PrintInstanceLine(InstanceDescriptor instance) {
       Console.WriteLine(string.Join(";",
-        new[] {instance.Name, instance.Cls, instance.Dimension.ToString(CultureInfo.CurrentCulture)}
+        new[] {instance.Name, instance.Cls, instance.Dimension.ToString(CultureInfo.CurrentCulture), instance.DescriptionEffort.ToString(CultureInfo.CurrentCulture)}
           .Concat(instance.FeatureValues.Select(x => x.ToString(CultureInfo.CurrentCulture)))));
     }
 
-    private static void ExploreMatching(List<QuadraticAssignmentProblem> instances, InstanceExplorer[] trainingExplorers, InstanceExplorer[] testExporers, bool parallel = false) {
+    private static void ExploreMatching(StreamWriter writer, List<QuadraticAssignmentProblem> instances, InstanceExplorer[] trainingExplorers, InstanceExplorer[] testExporers, string info, bool parallel = false) {
       var sync = new object();
 
@@ -223,7 +212,10 @@
           var result = Compare(point.Training.Value.Item2, normalizedTest);
           lock (sync) {
-            Console.WriteLine("{0}\t{1}\t{2}\t{3:F2}\t{4}\t{5:F2}\t{6}",
-              point.Training.Key.Effort, point.Test.Key.Effort, result.ExactCount,
-              result.ExactAverageRank, result.ClsCount, result.ClsAverageRank, result.TotalCount);
+            string output = string.Format("{0}\t{1}\t{2}\t{3:F2}\t{4}\t{5:F2}\t{6}\t{7}\t{8}\t{9}",
+              info, point.Training.Key.Effort, point.Test.Key.Effort, result.ExactCount,
+              result.ExactAverageRank, result.ClsCount, result.ClsAverageRank, result.TotalCount,
+              result.TrainingDescriptionEffort, result.TestDescriptionEffort);
+            writer.WriteLine(output);
+            Console.WriteLine(output);
           }
         });
@@ -233,7 +225,10 @@
           point.Training.Value.Item1.Apply(normalizedTest);
           var result = Compare(point.Training.Value.Item2, normalizedTest);
-          Console.WriteLine("{0}\t{1}\t{2}\t{3:F2}\t{4}\t{5:F2}\t{6}",
-            point.Training.Key.Effort, point.Test.Key.Effort, result.ExactCount,
-            result.ExactAverageRank, result.ClsCount, result.ClsAverageRank, result.TotalCount);
+          string output = string.Format("{0}\t{1}\t{2}\t{3:F2}\t{4}\t{5:F2}\t{6}\t{7}\t{8}\t{9}",
+            info, point.Training.Key.Effort, point.Test.Key.Effort, result.ExactCount,
+            result.ExactAverageRank, result.ClsCount, result.ClsAverageRank, result.TotalCount,
+            result.TrainingDescriptionEffort, result.TestDescriptionEffort);
+          writer.WriteLine(output);
+          Console.WriteLine(output);
         }
       }
@@ -246,4 +241,7 @@
       public double ExactAverageRank { get; set; }
       public double ClsAverageRank { get; set; }
+
+      public double TrainingDescriptionEffort { get; set; }
+      public double TestDescriptionEffort { get; set; }
     }
   }
