Index: branches/NCA/HeuristicLab.Algorithms.NCA.Views/3.3/NCADimensionReductionView.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA.Views/3.3/NCADimensionReductionView.cs	(revision 8462)
+++ branches/NCA/HeuristicLab.Algorithms.NCA.Views/3.3/NCADimensionReductionView.cs	(revision 8466)
@@ -31,11 +31,11 @@
 namespace HeuristicLab.Algorithms.NCA.Views {
   [View("Dimension Reduction")]
-  [Content(typeof(INCAClassificationSolution), IsDefaultView = false)]
+  [Content(typeof(INcaClassificationSolution), IsDefaultView = false)]
   public partial class NCADimensionReductionView : DataAnalysisSolutionEvaluationView {
     private ViewHost viewHost = new ViewHost();
     private ScatterPlot scatterPlot = new ScatterPlot();
 
-    public new INCAClassificationSolution Content {
-      get { return (INCAClassificationSolution)base.Content; }
+    public new INcaClassificationSolution Content {
+      get { return (INcaClassificationSolution)base.Content; }
       set { base.Content = value; }
     }
@@ -73,5 +73,5 @@
 
       int idx = 0;
-      if (reduced.GetLength(1) == 1) {
+      if (reduced.GetLength(1) == 2) { // last column is the target variable
         foreach (var r in range) {
           var label = Content.ProblemData.Dataset.GetDoubleValue(Content.ProblemData.TargetVariable, r);
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/HeuristicLab.Algorithms.NCA-3.3.csproj
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/HeuristicLab.Algorithms.NCA-3.3.csproj	(revision 8462)
+++ branches/NCA/HeuristicLab.Algorithms.NCA/3.3/HeuristicLab.Algorithms.NCA-3.3.csproj	(revision 8466)
@@ -102,14 +102,14 @@
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="INCAClassificationSolution.cs" />
-    <Compile Include="Initialization\INCAInitializer.cs" />
-    <Compile Include="INCAModel.cs" />
-    <Compile Include="Initialization\LDAInitializer.cs" />
-    <Compile Include="Initialization\PCAInitializer.cs" />
+    <Compile Include="INcaClassificationSolution.cs" />
+    <Compile Include="Initialization\INcaInitializer.cs" />
+    <Compile Include="INcaModel.cs" />
+    <Compile Include="Initialization\LdaInitializer.cs" />
+    <Compile Include="Initialization\PcaInitializer.cs" />
     <Compile Include="Initialization\RandomInitializer.cs" />
     <Compile Include="Matrix.cs" />
-    <Compile Include="NeighborhoodComponentsAnalysis.cs" />
-    <Compile Include="NCAClassificationSolution.cs" />
-    <Compile Include="NCAModel.cs" />
+    <Compile Include="NcaAlgorithm.cs" />
+    <Compile Include="NcaClassificationSolution.cs" />
+    <Compile Include="NcaModel.cs" />
     <Compile Include="Plugin.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/INCAClassificationSolution.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/INCAClassificationSolution.cs	(revision 8462)
+++ branches/NCA/HeuristicLab.Algorithms.NCA/3.3/INCAClassificationSolution.cs	(revision 8466)
@@ -23,6 +23,6 @@
 
 namespace HeuristicLab.Algorithms.NCA {
-  public interface INCAClassificationSolution : IClassificationSolution {
-    new INCAModel Model { get; }
+  public interface INcaClassificationSolution : IClassificationSolution {
+    new INcaModel Model { get; }
   }
 }
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/INCAModel.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/INCAModel.cs	(revision 8462)
+++ branches/NCA/HeuristicLab.Algorithms.NCA/3.3/INCAModel.cs	(revision 8466)
@@ -24,5 +24,7 @@
 
 namespace HeuristicLab.Algorithms.NCA {
-  public interface INCAModel : IClassificationModel {
+  public interface INcaModel : IClassificationModel {
+    new INcaClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData);
+
     double[,] Reduce(Dataset dataset, IEnumerable<int> rows);
   }
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/Matrix.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/Matrix.cs	(revision 8462)
+++ branches/NCA/HeuristicLab.Algorithms.NCA/3.3/Matrix.cs	(revision 8466)
@@ -30,8 +30,8 @@
   [NonDiscoverableType]
   internal class Matrix : IEnumerable<double>, IDeepCloneable {
-
-    private IEnumerable<double> values;
-    public int Rows { get; private set; }
-    public int Columns { get; private set; }
+    // this type is immutable
+    private readonly IEnumerable<double> values;
+    public readonly int Rows;
+    public readonly int Columns;
 
     protected Matrix(Matrix original, Cloner cloner) {
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NCAClassificationSolution.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NCAClassificationSolution.cs	(revision 8462)
+++ branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NCAClassificationSolution.cs	(revision 8466)
@@ -26,19 +26,19 @@
 
 namespace HeuristicLab.Algorithms.NCA {
-  [Item("NCAClassificationSolution", "")]
+  [Item("NCA Classification Solution", "")]
   [StorableClass]
-  public class NCAClassificationSolution : ClassificationSolution, INCAClassificationSolution {
+  public class NcaClassificationSolution : ClassificationSolution, INcaClassificationSolution {
 
-    public new INCAModel Model {
-      get { return (INCAModel)base.Model; }
+    public new INcaModel Model {
+      get { return (INcaModel)base.Model; }
       set { base.Model = value; }
     }
 
     [StorableConstructor]
-    private NCAClassificationSolution(bool deserializing) : base(deserializing) { }
-    private NCAClassificationSolution(NCAClassificationSolution original, Cloner cloner)
+    private NcaClassificationSolution(bool deserializing) : base(deserializing) { }
+    private NcaClassificationSolution(NcaClassificationSolution original, Cloner cloner)
       : base(original, cloner) {
     }
-    public NCAClassificationSolution(IClassificationProblemData problemData, INCAModel ncaModel)
+    public NcaClassificationSolution(IClassificationProblemData problemData, INcaModel ncaModel)
       : base(ncaModel, problemData) {
       RecalculateResults();
@@ -46,5 +46,5 @@
 
     public override IDeepCloneable Clone(Cloner cloner) {
-      return new NCAClassificationSolution(this, cloner);
+      return new NcaClassificationSolution(this, cloner);
     }
 
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NCAModel.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NCAModel.cs	(revision 8462)
+++ branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NCAModel.cs	(revision 8466)
@@ -29,7 +29,7 @@
 
 namespace HeuristicLab.Algorithms.NCA {
-  [Item("NCAModel", "")]
+  [Item("NCA Model", "")]
   [StorableClass]
-  public class NCAModel : NamedItem, INCAModel {
+  public class NcaModel : NamedItem, INcaModel {
 
     [Storable]
@@ -47,11 +47,9 @@
     private INearestNeighbourModel nnModel;
     [Storable]
-    private Dictionary<double, double> nn2ncaClassMapping;
-    [Storable]
-    private Dictionary<double, double> nca2nnClassMapping;
+    private double[] classValues;
 
     [StorableConstructor]
-    protected NCAModel(bool deserializing) : base(deserializing) { }
-    protected NCAModel(NCAModel original, Cloner cloner)
+    protected NcaModel(bool deserializing) : base(deserializing) { }
+    protected NcaModel(NcaModel original, Cloner cloner)
       : base(original, cloner) {
       this.scaling = cloner.Clone(original.scaling);
@@ -60,46 +58,30 @@
       this.targetVariable = original.targetVariable;
       this.nnModel = cloner.Clone(original.nnModel);
-      this.nn2ncaClassMapping = original.nn2ncaClassMapping.ToDictionary(x => x.Key, y => y.Value);
-      this.nca2nnClassMapping = original.nca2nnClassMapping.ToDictionary(x => x.Key, y => y.Value);
+      this.classValues = (double[])original.classValues.Clone();
     }
-    public NCAModel(int k, double[,] scaledData, Scaling scaling, double[,] transformationMatrix, string targetVariable, IEnumerable<double> targetVector, IEnumerable<string> allowedInputVariables) {
+    public NcaModel(int k, double[,] transformationMatrix, Dataset dataset, IEnumerable<int> rows, string targetVariable, IEnumerable<string> allowedInputVariables, Scaling scaling, double[] classValues) {
       Name = ItemName;
       Description = ItemDescription;
       this.scaling = scaling;
-      this.transformationMatrix = transformationMatrix;
+      this.transformationMatrix = (double[,])transformationMatrix.Clone();
       this.allowedInputVariables = allowedInputVariables.ToArray();
       this.targetVariable = targetVariable;
+      this.classValues = (double[])classValues.Clone();
 
-      nca2nnClassMapping = targetVector.Distinct().OrderBy(x => x).Select((v, i) => new { Index = (double)i, Class = v }).ToDictionary(x => x.Class, y => y.Index);
-      nn2ncaClassMapping = nca2nnClassMapping.ToDictionary(x => x.Value, y => y.Key);
-
-      var transformedData = ReduceWithTarget(scaledData, targetVector.Select(x => nca2nnClassMapping[x]));
-
-      var kdtree = new alglib.nearestneighbor.kdtree();
-      alglib.nearestneighbor.kdtreebuild(transformedData, transformedData.GetLength(0), transformedData.GetLength(1) - 1, 1, 2, kdtree);
-
-      nnModel = new NearestNeighbourModel(kdtree, k, targetVariable,
-        Enumerable.Range(0, transformationMatrix.GetLength(1)).Select(x => x.ToString()),
-        nn2ncaClassMapping.Keys.ToArray());
+      var ds = ReduceDataset(dataset, rows);
+      nnModel = new NearestNeighbourModel(ds, Enumerable.Range(0, ds.Rows), k, ds.VariableNames.Last(), ds.VariableNames.Take(transformationMatrix.GetLength(1)), classValues);
     }
 
     public override IDeepCloneable Clone(Cloner cloner) {
-      return new NCAModel(this, cloner);
+      return new NcaModel(this, cloner);
     }
 
     public IEnumerable<double> GetEstimatedClassValues(Dataset dataset, IEnumerable<int> rows) {
-      var unknownClasses = dataset.GetDoubleValues(targetVariable, rows).Where(x => !nca2nnClassMapping.ContainsKey(x));
-      if (unknownClasses.Any())
-        foreach (var uc in unknownClasses) {
-          nca2nnClassMapping[uc] = nca2nnClassMapping.Count;
-          nn2ncaClassMapping[nca2nnClassMapping[uc]] = uc;
-        }
-      var transformedData = ReduceWithTarget(dataset, rows, dataset.GetDoubleValues(targetVariable, rows).Select(x => nca2nnClassMapping[x]));
-      var ds = new Dataset(Enumerable.Range(0, transformationMatrix.GetLength(1)).Select(x => x.ToString()).Concat(targetVariable.ToEnumerable()), transformedData);
-      return nnModel.GetEstimatedClassValues(ds, Enumerable.Range(0, ds.Rows)).Select(x => nn2ncaClassMapping[x]);
+      var ds = ReduceDataset(dataset, rows);
+      return nnModel.GetEstimatedClassValues(ds, Enumerable.Range(0, ds.Rows));
     }
 
-    public NCAClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
-      return new NCAClassificationSolution(problemData, this);
+    public INcaClassificationSolution CreateClassificationSolution(IClassificationProblemData problemData) {
+      return new NcaClassificationSolution(problemData, this);
     }
 
@@ -110,34 +92,22 @@
     public double[,] Reduce(Dataset dataset, IEnumerable<int> rows) {
       var scaledData = AlglibUtil.PrepareAndScaleInputMatrix(dataset, allowedInputVariables, rows, scaling);
-      return Reduce(scaledData);
-    }
-
-    private double[,] Reduce(double[,] scaledData) {
-      var result = new double[scaledData.GetLength(0), transformationMatrix.GetLength(1)];
+      var targets = dataset.GetDoubleValues(targetVariable, rows).ToArray();
+      var result = new double[scaledData.GetLength(0), transformationMatrix.GetLength(1) + 1];
       for (int i = 0; i < scaledData.GetLength(0); i++)
-        for (int j = 0; j < scaledData.GetLength(1); j++)
+        for (int j = 0; j < scaledData.GetLength(1); j++) {
           for (int x = 0; x < transformationMatrix.GetLength(1); x++) {
             result[i, x] += scaledData[i, j] * transformationMatrix[j, x];
           }
+          result[i, transformationMatrix.GetLength(1)] = targets[i];
+        }
       return result;
     }
 
-    private double[,] ReduceWithTarget(Dataset dataset, IEnumerable<int> rows, IEnumerable<double> targetValues) {
-      var scaledData = AlglibUtil.PrepareAndScaleInputMatrix(dataset, allowedInputVariables, rows, scaling);
-      return ReduceWithTarget(scaledData, targetValues);
-    }
-
-    private double[,] ReduceWithTarget(double[,] scaledData, IEnumerable<double> targetValues) {
-      var result = new double[scaledData.GetLength(0), transformationMatrix.GetLength(1) + 1];
-      for (int i = 0; i < scaledData.GetLength(0); i++)
-        for (int j = 0; j < scaledData.GetLength(1); j++)
-          for (int x = 0; x < transformationMatrix.GetLength(1); x++) {
-            result[i, x] += scaledData[i, j] * transformationMatrix[j, x];
-          }
-
-      int r = 0;
-      foreach (var d in targetValues) result[r++, transformationMatrix.GetLength(1)] = d;
-
-      return result;
+    public Dataset ReduceDataset(Dataset dataset, IEnumerable<int> rows) {
+      return new Dataset(Enumerable
+          .Range(0, transformationMatrix.GetLength(1))
+          .Select(x => "X" + x.ToString())
+          .Concat(targetVariable.ToEnumerable()),
+        Reduce(dataset, rows));
     }
   }
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NcaAlgorithm.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NcaAlgorithm.cs	(revision 8466)
+++ branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NcaAlgorithm.cs	(revision 8466)
@@ -0,0 +1,256 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using HeuristicLab.Algorithms.DataAnalysis;
+using HeuristicLab.Analysis;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Data;
+using HeuristicLab.Optimization;
+using HeuristicLab.Parameters;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+using HeuristicLab.PluginInfrastructure;
+using HeuristicLab.Problems.DataAnalysis;
+
+namespace HeuristicLab.Algorithms.NCA {
+  internal delegate void Reporter(double quality, double[] coefficients);
+  /// <summary>
+  /// Neighborhood Components Analysis
+  /// </summary>
+  [Item("Neighborhood Components Analysis (NCA)", "Implementation of Neighborhood Components Analysis based on the description of J. Goldberger, S. Roweis, G. Hinton, R. Salakhutdinov. 2005. Neighbourhood Component Analysis. Advances in Neural Information Processing Systems, 17. pp. 513-520.")]
+  [Creatable("Data Analysis")]
+  [StorableClass]
+  public sealed class NcaAlgorithm : FixedDataAnalysisAlgorithm<IClassificationProblem> {
+    #region Parameter Properties
+    public IValueLookupParameter<IntValue> KParameter {
+      get { return (IValueLookupParameter<IntValue>)Parameters["K"]; }
+    }
+    public IFixedValueParameter<IntValue> DimensionsParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters["Dimensions"]; }
+    }
+    public IConstrainedValueParameter<INCAInitializer> InitializationParameter {
+      get { return (IConstrainedValueParameter<INCAInitializer>)Parameters["Initialization"]; }
+    }
+    public IFixedValueParameter<IntValue> NeighborSamplesParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters["NeighborSamples"]; }
+    }
+    public IFixedValueParameter<IntValue> IterationsParameter {
+      get { return (IFixedValueParameter<IntValue>)Parameters["Iterations"]; }
+    }
+    #endregion
+
+    #region Properties
+    private IntValue K {
+      get { return KParameter.Value; }
+    }
+    private int Dimensions {
+      get { return DimensionsParameter.Value.Value; }
+      set { DimensionsParameter.Value.Value = value; }
+    }
+    private int NeighborSamples {
+      get { return NeighborSamplesParameter.Value.Value; }
+      set { NeighborSamplesParameter.Value.Value = value; }
+    }
+    private int Iterations {
+      get { return IterationsParameter.Value.Value; }
+      set { IterationsParameter.Value.Value = value; }
+    }
+    #endregion
+
+    [StorableConstructor]
+    private NcaAlgorithm(bool deserializing) : base(deserializing) { }
+    private NcaAlgorithm(NcaAlgorithm original, Cloner cloner) : base(original, cloner) { }
+    public NcaAlgorithm()
+      : base() {
+      Parameters.Add(new ValueLookupParameter<IntValue>("K", "The K for the nearest neighbor.", new IntValue(1)));
+      Parameters.Add(new FixedValueParameter<IntValue>("Dimensions", "The number of dimensions that NCA should reduce the data to.", new IntValue(2)));
+      Parameters.Add(new ConstrainedValueParameter<INCAInitializer>("Initialization", "Which method should be used to initialize the matrix. Typically LDA (linear discriminant analysis) should provide a good estimate."));
+      Parameters.Add(new FixedValueParameter<IntValue>("NeighborSamples", "How many of the neighbors should be sampled in order to speed up the calculation. This should be at least the value of k and at most the number of training instances minus one.", new IntValue(50)));
+      Parameters.Add(new FixedValueParameter<IntValue>("Iterations", "How many iterations the conjugate gradient (CG) method should be allowed to perform. The method might still terminate earlier if a local optima has already been reached.", new IntValue(20)));
+
+      INCAInitializer defaultInitializer = null;
+      foreach (var initializer in ApplicationManager.Manager.GetInstances<INCAInitializer>().OrderBy(x => x.ItemName)) {
+        if (initializer is LDAInitializer) defaultInitializer = initializer;
+        InitializationParameter.ValidValues.Add(initializer);
+      }
+      if (defaultInitializer != null) InitializationParameter.Value = defaultInitializer;
+
+      Problem = new ClassificationProblem();
+    }
+
+    public override IDeepCloneable Clone(Cloner cloner) {
+      return new NcaAlgorithm(this, cloner);
+    }
+
+    public override void Prepare() {
+      if (Problem != null) base.Prepare();
+    }
+
+    protected override void Run() {
+      var k = K.Value;
+      var initializer = InitializationParameter.Value;
+
+      var clonedProblem = (IClassificationProblemData)Problem.ProblemData.Clone();
+      var model = Train(clonedProblem, k, Dimensions, NeighborSamples, Iterations, initializer.Initialize(clonedProblem, Dimensions), ReportQuality, CancellationToken.None);
+      Results.Add(new Result("ClassificationSolution", "The classification solution.", model.CreateClassificationSolution(clonedProblem)));
+    }
+
+    public static INcaClassificationSolution CreateClassificationSolution(IClassificationProblemData data, int k, int dimensions, int neighborSamples, int iterations, INCAInitializer initializer) {
+      var clonedProblem = (IClassificationProblemData)data.Clone();
+      var model = Train(clonedProblem, k, dimensions, neighborSamples, iterations, initializer);
+      return model.CreateClassificationSolution(clonedProblem);
+    }
+
+    public static INcaModel Train(IClassificationProblemData problemData, int k, int dimensions, int neighborSamples, int iterations, INCAInitializer initializer) {
+      return Train(problemData, k, dimensions, neighborSamples, iterations, initializer.Initialize(problemData, dimensions), null, CancellationToken.None);
+    }
+
+    public static INcaModel Train(IClassificationProblemData problemData, int k, int neighborSamples, int iterations, double[,] initalMatrix) {
+      var matrix = new double[initalMatrix.Length];
+      for (int i = 0; i < initalMatrix.GetLength(0); i++)
+        for (int j = 0; j < initalMatrix.GetLength(1); j++)
+          matrix[i * initalMatrix.GetLength(1) + j] = initalMatrix[i, j];
+      return Train(problemData, k, initalMatrix.GetLength(1), neighborSamples, iterations, matrix, null, CancellationToken.None);
+    }
+
+    private static INcaModel Train(IClassificationProblemData data, int k, int dimensions, int neighborSamples, int iterations, double[] matrix, Reporter reporter, CancellationToken cancellation) {
+      var scaling = new Scaling(data.Dataset, data.AllowedInputVariables, data.TrainingIndices);
+      var scaledData = AlglibUtil.PrepareAndScaleInputMatrix(data.Dataset, data.AllowedInputVariables, data.TrainingIndices, scaling);
+      var classes = data.Dataset.GetDoubleValues(data.TargetVariable, data.TrainingIndices).ToArray();
+      var attributes = scaledData.GetLength(1);
+
+      alglib.mincgstate state;
+      alglib.mincgreport rep;
+      alglib.mincgcreate(matrix, out state);
+      alglib.mincgsetcond(state, 0, 0, 0, iterations);
+      alglib.mincgsetxrep(state, true);
+      int neighborSampleSize = neighborSamples;
+      Optimize(state, scaledData, classes, dimensions, neighborSampleSize, cancellation, reporter);
+      alglib.mincgresults(state, out matrix, out rep);
+
+      var transformationMatrix = new double[attributes, dimensions];
+      var counter = 0;
+      for (var i = 0; i < attributes; i++)
+        for (var j = 0; j < dimensions; j++)
+          transformationMatrix[i, j] = matrix[counter++];
+
+      return new NcaModel(k, transformationMatrix, data.Dataset, data.TrainingIndices, data.TargetVariable, data.AllowedInputVariables, scaling, data.ClassValues.ToArray());
+    }
+
+    private static void Optimize(alglib.mincgstate state, double[,] data, double[] classes, int dimensions, int neighborSampleSize, CancellationToken cancellation, Reporter reporter) {
+      while (alglib.mincgiteration(state)) {
+        if (cancellation.IsCancellationRequested) break;
+        if (state.needfg) {
+          Gradient(state.x, ref state.innerobj.f, state.innerobj.g, data, classes, dimensions, neighborSampleSize);
+          continue;
+        }
+        if (state.innerobj.xupdated) {
+          if (reporter != null)
+            reporter(state.innerobj.f, state.innerobj.x);
+          continue;
+        }
+        throw new InvalidOperationException("Neighborhood Components Analysis: Error in Optimize() (some derivatives were not provided?)");
+      }
+    }
+
+    private static void Gradient(double[] A, ref double func, double[] grad, double[,] data, double[] classes, int dimensions, int neighborSampleSize) {
+      var instances = data.GetLength(0);
+      var attributes = data.GetLength(1);
+
+      var AMatrix = new Matrix(A, A.Length / dimensions, dimensions);
+
+      alglib.sparsematrix probabilities;
+      alglib.sparsecreate(instances, instances, out probabilities);
+      var transformedDistances = new Dictionary<int, double>(instances);
+      for (int i = 0; i < instances; i++) {
+        var iVector = new Matrix(GetRow(data, i), data.GetLength(1));
+        for (int k = 0; k < instances; k++) {
+          if (k == i) {
+            transformedDistances.Remove(k);
+            continue;
+          }
+          var kVector = new Matrix(GetRow(data, k));
+          transformedDistances[k] = Math.Exp(-iVector.Multiply(AMatrix).Subtract(kVector.Multiply(AMatrix)).SquaredVectorLength());
+        }
+        var sample = transformedDistances.OrderByDescending(x => x.Value).Take(neighborSampleSize).ToArray();
+        var normalization = sample.Sum(x => x.Value);
+        if (normalization > 0) {
+          foreach (var s in sample) {
+            if (s.Value <= 0) break;
+            alglib.sparseset(probabilities, i, s.Key, s.Value / normalization);
+          }
+        }
+      }
+      alglib.sparseconverttocrs(probabilities); // needed to enumerate in order (top-down and left-right)
+
+      int t0 = 0, t1 = 0, r, c;
+      double val;
+      var pi = new double[instances];
+      while (alglib.sparseenumerate(probabilities, ref t0, ref t1, out r, out c, out val)) {
+        if (classes[r].IsAlmost(classes[c])) {
+          pi[r] += val;
+        }
+      }
+
+      var innerSum = new double[attributes, attributes];
+      while (alglib.sparseenumerate(probabilities, ref t0, ref t1, out r, out c, out val)) {
+        var vector = new Matrix(GetRow(data, r)).Subtract(new Matrix(GetRow(data, c)));
+        vector.OuterProduct(vector).Multiply(val * pi[r]).AddTo(innerSum);
+
+        if (classes[r].IsAlmost(classes[c])) {
+          vector.OuterProduct(vector).Multiply(-val).AddTo(innerSum);
+        }
+      }
+
+      func = -pi.Sum();
+
+      r = 0;
+      var newGrad = AMatrix.Multiply(-2.0).Transpose().Multiply(new Matrix(innerSum)).Transpose();
+      foreach (var g in newGrad) {
+        grad[r++] = g;
+      }
+    }
+
+    private void ReportQuality(double func, double[] coefficients) {
+      var instances = Problem.ProblemData.TrainingIndices.Count();
+      DataTable qualities;
+      if (!Results.ContainsKey("Optimization")) {
+        qualities = new DataTable("Optimization");
+        qualities.Rows.Add(new DataRow("Quality", string.Empty));
+        Results.Add(new Result("Optimization", qualities));
+      } else qualities = (DataTable)Results["Optimization"].Value;
+      qualities.Rows["Quality"].Values.Add(-func / instances);
+
+      if (!Results.ContainsKey("Quality")) {
+        Results.Add(new Result("Quality", new DoubleValue(-func / instances)));
+      } else ((DoubleValue)Results["Quality"].Value).Value = -func / instances;
+    }
+
+    private static IEnumerable<double> GetRow(double[,] data, int row) {
+      for (int i = 0; i < data.GetLength(1); i++)
+        yield return data[row, i];
+    }
+  }
+}
Index: branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NeighborhoodComponentsAnalysis.cs
===================================================================
--- branches/NCA/HeuristicLab.Algorithms.NCA/3.3/NeighborhoodComponentsAnalysis.cs	(revision 8462)
+++ 	(revision )
@@ -1,237 +1,0 @@
-#region License Information
-/* HeuristicLab
- * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
- *
- * This file is part of HeuristicLab.
- *
- * HeuristicLab is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * HeuristicLab is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
- */
-#endregion
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using HeuristicLab.Algorithms.DataAnalysis;
-using HeuristicLab.Analysis;
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using HeuristicLab.Data;
-using HeuristicLab.Optimization;
-using HeuristicLab.Parameters;
-using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
-using HeuristicLab.PluginInfrastructure;
-using HeuristicLab.Problems.DataAnalysis;
-
-namespace HeuristicLab.Algorithms.NCA {
-  internal delegate void Reporter(double quality, double[] coefficients);
-  /// <summary>
-  /// Neighborhood Components Analysis
-  /// </summary>
-  [Item("Neighborhood Components Analysis", "NCA is described in J. Goldberger, S. Roweis, G. Hinton, R. Salakhutdinov. 2005. Neighbourhood Component Analysis. Advances in Neural Information Processing Systems, 17. pp. 513-520.")]
-  [Creatable("Data Analysis")]
-  [StorableClass]
-  public sealed class NeighborhoodComponentsAnalysis : FixedDataAnalysisAlgorithm<IClassificationProblem> {
-    #region Parameter Properties
-    public IValueLookupParameter<IntValue> KParameter {
-      get { return (IValueLookupParameter<IntValue>)Parameters["k"]; }
-    }
-    public IValueLookupParameter<IntValue> ReduceDimensionsParameter {
-      get { return (IValueLookupParameter<IntValue>)Parameters["ReduceDimensions"]; }
-    }
-    private IConstrainedValueParameter<INCAInitializer> InitializationParameter {
-      get { return (IConstrainedValueParameter<INCAInitializer>)Parameters["Initialization"]; }
-    }
-    #endregion
-
-    #region Properties
-    public IntValue K {
-      get { return KParameter.Value; }
-    }
-    public IntValue ReduceDimensions {
-      get { return ReduceDimensionsParameter.Value; }
-    }
-    #endregion
-
-    [StorableConstructor]
-    private NeighborhoodComponentsAnalysis(bool deserializing) : base(deserializing) { }
-    private NeighborhoodComponentsAnalysis(NeighborhoodComponentsAnalysis original, Cloner cloner) : base(original, cloner) { }
-    public NeighborhoodComponentsAnalysis()
-      : base() {
-      Parameters.Add(new ValueLookupParameter<IntValue>("k", "The k for the nearest neighbor.", new IntValue(1)));
-      Parameters.Add(new ValueLookupParameter<IntValue>("ReduceDimensions", "The number of dimensions that NCA should reduce the data to.", new IntValue(2)));
-      Parameters.Add(new ConstrainedValueParameter<INCAInitializer>("Initialization", "Which method should be used to initialize the matrix. Typically LDA (linear discriminant analysis) should provide a good estimate."));
-
-      INCAInitializer defaultInitializer = null;
-      foreach (var initializer in ApplicationManager.Manager.GetInstances<INCAInitializer>().OrderBy(x => x.ItemName)) {
-        if (initializer is LDAInitializer) defaultInitializer = initializer;
-        InitializationParameter.ValidValues.Add(initializer);
-      }
-      if (defaultInitializer != null) InitializationParameter.Value = defaultInitializer;
-
-      Problem = new ClassificationProblem();
-    }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new NeighborhoodComponentsAnalysis(this, cloner);
-    }
-
-    public override void Prepare() {
-      if (Problem != null) base.Prepare();
-    }
-
-    protected override void Run() {
-      var k = K.Value;
-      var dimensions = ReduceDimensions.Value;
-      var initializer = InitializationParameter.Value;
-
-      var clonedProblem = (IClassificationProblemData)Problem.ProblemData.Clone();
-      var model = Train(clonedProblem, k, dimensions, initializer.Initialize(clonedProblem, dimensions), ReportQuality, CancellationToken.None);
-      var classification = new NCAClassificationSolution(clonedProblem, model);
-      Results.Add(new Result("ClassificationSolution", "The classification solution.", classification));
-    }
-
-    private void ReportQuality(double func, double[] coefficients) {
-      var instances = Problem.ProblemData.TrainingIndices.Count();
-      DataTable qualities;
-      if (!Results.ContainsKey("Optimization")) {
-        qualities = new DataTable("Optimization");
-        qualities.Rows.Add(new DataRow("Quality", string.Empty));
-        Results.Add(new Result("Optimization", qualities));
-      } else qualities = (DataTable)Results["Optimization"].Value;
-      qualities.Rows["Quality"].Values.Add(-func / instances);
-
-      if (!Results.ContainsKey("Quality")) {
-        Results.Add(new Result("Quality", new DoubleValue(-func / instances)));
-      } else ((DoubleValue)Results["Quality"].Value).Value = -func / instances;
-    }
-
-    public static INCAModel Train(IClassificationProblemData problemData, int k, int dimensions, INCAInitializer initializer) {
-      return Train(problemData, k, dimensions, initializer.Initialize(problemData, dimensions), null, CancellationToken.None);
-    }
-
-    public static INCAModel Train(IClassificationProblemData problemData, int k, int dimensions, double[,] initalMatrix) {
-      var matrix = new double[initalMatrix.Length];
-      for (int i = 0; i < initalMatrix.GetLength(0); i++)
-        for (int j = 0; j < initalMatrix.GetLength(1); j++)
-          matrix[i * initalMatrix.GetLength(1) + j] = initalMatrix[i, j];
-      return Train(problemData, k, dimensions, matrix, null, CancellationToken.None);
-    }
-
-    private static INCAModel Train(IClassificationProblemData data, int k, int dimensions, double[] matrix, Reporter reporter, CancellationToken cancellation) {
-      var scaling = new Scaling(data.Dataset, data.AllowedInputVariables, data.TrainingIndices);
-      var scaledData = AlglibUtil.PrepareAndScaleInputMatrix(data.Dataset, data.AllowedInputVariables, data.TrainingIndices, scaling);
-      var classes = data.Dataset.GetDoubleValues(data.TargetVariable, data.TrainingIndices).ToArray();
-      var instances = scaledData.GetLength(0);
-      var attributes = scaledData.GetLength(1);
-
-      alglib.mincgstate state;
-      alglib.mincgreport rep;
-      alglib.mincgcreate(matrix, out state);
-      alglib.mincgsetcond(state, 0, 0, 0, 20);
-      alglib.mincgsetxrep(state, true);
-      Optimize(state, scaledData, classes, dimensions, cancellation, reporter);
-      alglib.mincgresults(state, out matrix, out rep);
-
-      var transformationMatrix = new double[attributes, dimensions];
-      var counter = 0;
-      for (var i = 0; i < attributes; i++)
-        for (var j = 0; j < dimensions; j++)
-          transformationMatrix[i, j] = matrix[counter++];
-
-      return new NCAModel(k, scaledData, scaling, transformationMatrix, data.TargetVariable, data.Dataset.GetDoubleValues(data.TargetVariable, data.TrainingIndices), data.AllowedInputVariables);
-    }
-
-    private static void Optimize(alglib.mincgstate state, double[,] data, double[] classes, int dimensions, CancellationToken cancellation, Reporter reporter) {
-      while (alglib.mincgiteration(state)) {
-
-        if (cancellation.IsCancellationRequested) break;
-
-        if (state.needfg) {
-          Gradient(state.x, ref state.innerobj.f, state.innerobj.g, data, classes, dimensions);
-          continue;
-        }
-
-        if (state.innerobj.xupdated) {
-          if (reporter != null)
-            reporter(state.innerobj.f, state.innerobj.x);
-          continue;
-        }
-
-        throw new InvalidOperationException("Neighborhood Components Analysis: Error in Optimize() (some derivatives were not provided?)");
-      }
-    }
-
-    private static void Gradient(double[] A, ref double func, double[] grad, double[,] data, double[] classes, int dimensions) {
-      var instances = data.GetLength(0);
-      var attributes = data.GetLength(1);
-
-      var AMatrix = new Matrix(A, A.Length / dimensions, dimensions);
-
-      alglib.sparsematrix probabilities;
-      alglib.sparsecreate(instances, instances, out probabilities);
-      var transformedDistances = new double[instances];
-      for (int i = 0; i < instances - 1; i++) {
-        var iVector = new Matrix(GetRow(data, i), data.GetLength(1));
-        var denom = 0.0;
-        for (int k = 0; k < instances; k++) {
-          if (k == i) continue;
-          var kVector = new Matrix(GetRow(data, k));
-          transformedDistances[k] = Math.Exp(-iVector.Multiply(AMatrix).Subtract(kVector.Multiply(AMatrix)).SquaredVectorLength());
-          denom += transformedDistances[k];
-        }
-        if (denom > 1e-05) {
-          for (int j = i + 1; j < instances; j++) {
-            if (i == j) continue;
-            var v = transformedDistances[j] / denom;
-            alglib.sparseset(probabilities, i, j, v);
-          }
-        }
-      }
-      alglib.sparseconverttocrs(probabilities); // needed to enumerate in order (top-down and left-right)
-
-      int t0 = 0, t1 = 0, r, c;
-      double val;
-      var pi = new double[instances];
-      while (alglib.sparseenumerate(probabilities, ref t0, ref t1, out r, out c, out val)) {
-        if (classes[r].IsAlmost(classes[c])) {
-          pi[r] += val;
-        }
-      }
-
-      var innerSum = new double[attributes, attributes];
-      while (alglib.sparseenumerate(probabilities, ref t0, ref t1, out r, out c, out val)) {
-        var vector = new Matrix(GetRow(data, r)).Subtract(new Matrix(GetRow(data, c)));
-        vector.OuterProduct(vector).Multiply(2.0 * val * pi[r]).AddTo(innerSum);
-
-        if (classes[r].IsAlmost(classes[c])) {
-          vector.OuterProduct(vector).Multiply(-2.0 * val).AddTo(innerSum);
-        }
-      }
-
-      func = -2.0 * pi.Sum();
-
-      r = 0;
-      var newGrad = AMatrix.Multiply(-2.0).Transpose().Multiply(new Matrix(innerSum)).Transpose();
-      foreach (var g in newGrad) {
-        grad[r++] = g;
-      }
-    }
-
-    private static IEnumerable<double> GetRow(double[,] data, int row) {
-      for (int i = 0; i < data.GetLength(1); i++)
-        yield return data[row, i];
-    }
-  }
-}
