From 40a96e3a1c71c0c1c4da24f238155e6995be37f9 Mon Sep 17 00:00:00 2001
From: Gareth Tribello <gareth.tribello@gmail.com>
Date: Sun, 13 Nov 2016 19:11:10 +0000
Subject: [PATCH] Started adding stuff to calculate projections using PCA

---
 regtest/dimred/rt-pca-2/plumed.dat         |   3 +-
 regtest/dimred/rt-pca/plumed.dat           |   3 +-
 src/dimred/DimensionalityReductionBase.cpp |   2 +-
 src/dimred/DimensionalityReductionBase.h   |   4 +-
 src/dimred/OutputPCAProjections.cpp        | 114 +++++++++++++++++++++
 src/dimred/PCA.cpp                         |  94 ++++++++---------
 src/dimred/PCA.h                           |  58 +++++++++++
 src/dimred/ProjectNonLandmarkPoints.cpp    |  28 +++--
 8 files changed, 236 insertions(+), 70 deletions(-)
 create mode 100644 src/dimred/OutputPCAProjections.cpp
 create mode 100644 src/dimred/PCA.h

diff --git a/regtest/dimred/rt-pca-2/plumed.dat b/regtest/dimred/rt-pca-2/plumed.dat
index 7b8302858..0ec9d4852 100644
--- a/regtest/dimred/rt-pca-2/plumed.dat
+++ b/regtest/dimred/rt-pca-2/plumed.dat
@@ -2,4 +2,5 @@ c1: READ FILE=colvar.in VALUES=cv1
 c2: READ FILE=colvar.in VALUES=cv2
 
 ff: COLLECT_FRAMES ARG=c1,c2 
-PCA USE_OUTPUT_DATA_FROM=ff METRIC=EUCLIDEAN NLOW_DIM=2 OFILE=pca-comp.pdb FMT=%8.4f 
+pca: PCA USE_OUTPUT_DATA_FROM=ff METRIC=EUCLIDEAN NLOW_DIM=2 
+OUTPUT_PCA_PROJECTION USE_OUTPUT_DATA_FROM=pca FILE=pca-comp.pdb FMT=%8.4f 
diff --git a/regtest/dimred/rt-pca/plumed.dat b/regtest/dimred/rt-pca/plumed.dat
index ad9156983..40c098e4e 100644
--- a/regtest/dimred/rt-pca/plumed.dat
+++ b/regtest/dimred/rt-pca/plumed.dat
@@ -1,2 +1,3 @@
 ff: COLLECT_FRAMES ATOMS=1-22 STRIDE=1 
-PCA USE_OUTPUT_DATA_FROM=ff NLOW_DIM=2 OFILE=pca-comp.pdb METRIC=OPTIMAL
+pca: PCA USE_OUTPUT_DATA_FROM=ff NLOW_DIM=2 METRIC=OPTIMAL
+OUTPUT_PCA_PROJECTION USE_OUTPUT_DATA_FROM=pca FILE=pca-comp.pdb
diff --git a/src/dimred/DimensionalityReductionBase.cpp b/src/dimred/DimensionalityReductionBase.cpp
index 0aeea0384..5a6860910 100644
--- a/src/dimred/DimensionalityReductionBase.cpp
+++ b/src/dimred/DimensionalityReductionBase.cpp
@@ -40,7 +40,7 @@ dimredbase(NULL)
 {
   // Check that some dissimilarity information is available
   if( my_input_data ){
-      if( !dissimilaritiesWereSet() ) error("dissimilarities have not been calcualted in input actions");
+      if( getName()!="PCA" && !dissimilaritiesWereSet() ) error("dissimilarities have not been calcualted in input actions");
       // Now we check if the input was a dimensionality reduction object
       dimredbase = dynamic_cast<DimensionalityReductionBase*>( my_input_data );
   }
diff --git a/src/dimred/DimensionalityReductionBase.h b/src/dimred/DimensionalityReductionBase.h
index 902bfd5da..61a44e7cc 100644
--- a/src/dimred/DimensionalityReductionBase.h
+++ b/src/dimred/DimensionalityReductionBase.h
@@ -45,11 +45,11 @@ public:
   static void registerKeywords( Keywords& keys );
   DimensionalityReductionBase( const ActionOptions& );
 /// Get the ith data point (this returns the projection)
-  void getProjection( const unsigned& idata, std::vector<double>& point, double& weight );
+  virtual void getProjection( const unsigned& idata, std::vector<double>& point, double& weight );
 /// Get a reference configuration (this returns the projection)
   analysis::DataCollectionObject& getStoredData( const unsigned& idata, const bool& calcdist ); 
 /// Actually perform the analysis
-  void performAnalysis();
+  virtual void performAnalysis();
 /// Overwrite getArguments so we get arguments from underlying class
   std::vector<Value*> getArgumentList();
 /// Calculate the projections of points
diff --git a/src/dimred/OutputPCAProjections.cpp b/src/dimred/OutputPCAProjections.cpp
new file mode 100644
index 000000000..78cff11ee
--- /dev/null
+++ b/src/dimred/OutputPCAProjections.cpp
@@ -0,0 +1,114 @@
+/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+   Copyright (c) 2012 The plumed team
+   (see the PEOPLE file at the root of the distribution for a list of names)
+
+   See http://www.plumed-code.org for more information.
+
+   This file is part of plumed, version 2.0.
+
+   plumed is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   plumed 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with plumed.  If not, see <http://www.gnu.org/licenses/>.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+#include "analysis/AnalysisBase.h"
+#include "reference/ReferenceAtoms.h"
+#include "reference/ReferenceArguments.h"
+#include "core/ActionRegister.h"
+#include "core/PlumedMain.h"
+#include "core/ActionSet.h"
+#include "core/Atoms.h"
+#include "core/SetupMolInfo.h"
+#include "tools/PDB.h"
+#include "PCA.h"
+
+namespace PLMD {
+namespace dimred {
+
+//+PLUMEDOC ANALYSIS OUTPUT_PCA_PROJECTION
+/*
+This is used to output the projection calculated by principle component analysis
+
+\par Examples
+
+*/
+//+ENDPLUMEDOC
+
+class OutputPCAProjection : public analysis::AnalysisBase {
+private:
+  PDB mypdb;
+  PCA* mypca;
+  std::string fmt;
+  std::string filename;
+public:
+  static void registerKeywords( Keywords& keys );
+  OutputPCAProjection( const ActionOptions& );
+  void performTask( const unsigned& , const unsigned& , MultiValue& ) const { plumed_error(); }
+  void performAnalysis();
+};
+
+PLUMED_REGISTER_ACTION(OutputPCAProjection,"OUTPUT_PCA_PROJECTION")
+
+void OutputPCAProjection::registerKeywords( Keywords& keys ){
+  analysis::AnalysisBase::registerKeywords( keys );
+  keys.add("compulsory","FILE","the name of the file to output to");
+  keys.add("optional","FMT","the format to use in the output file");
+  keys.add("compulsory","STRIDE","0","the frequency with which to perform the required analysis and to output the data.  The default value of 0 tells plumed to use all the data");
+}
+
+OutputPCAProjection::OutputPCAProjection( const ActionOptions& ao ):
+Action(ao),
+analysis::AnalysisBase(ao),
+fmt("%f")
+{
+  // Setup the PCA object
+  mypca = dynamic_cast<PCA*>( my_input_data );
+  if( !mypca ) error("input must be a PCA object");
+
+  // Get setup the pdb
+  mypdb.setAtomNumbers( my_input_data->getAtomIndexes() );
+  mypdb.setArgumentNames( (mypca->my_input_data)->getArgumentNames() );
+
+  // Find a moldata object
+  std::vector<SetupMolInfo*> moldat=plumed.getActionSet().select<SetupMolInfo*>();
+  if( moldat.empty() ) warning("PDB output files do not have atom types unless you use MOLDATA");
+
+  parse("FILE",filename); parse("FMT",fmt);
+  if( !getRestart() ){ OFile ofile; ofile.link(*this); ofile.setBackupString("analysis"); ofile.backupAllFiles(filename); }
+  log.printf("  printing data to file named %s \n",filename.c_str() );
+}
+
+void OutputPCAProjection::performAnalysis(){
+  // Find a moldata object
+  std::vector<SetupMolInfo*> moldat=plumed.getActionSet().select<SetupMolInfo*>();
+  if( moldat.size()>1 ) error("you should only have one MOLINFO action in your input file"); 
+  SetupMolInfo* mymoldat=NULL; if( moldat.size()==1 ) mymoldat=moldat[0];
+  // Output the embedding in plumed pdb format
+  OFile afile; afile.link(*this); afile.setBackupString("analysis");
+  mypdb.setAtomPositions( (mypca->myref)->getReferencePositions() );
+  for(unsigned j=0;j<mypca->getArguments().size();++j) mypdb.setArgumentValue( (mypca->getArguments()[j])->getName(), (mypca->myref)->getReferenceArgument(j) );
+  // And output the first frame
+  afile.open( filename.c_str() ); afile.printf("REMARK TYPE=%s \n", mypca->mtype.c_str() );
+  if( plumed.getAtoms().usingNaturalUnits() ) mypdb.print( 1.0, mymoldat, afile, fmt );
+  else mypdb.print( atoms.getUnits().getLength()/0.1, mymoldat, afile, fmt ); 
+  // And now ouput the eigenvectors
+  for(unsigned dim=0;dim<mypca->nlow;++dim){
+      afile.printf("REMARK TYPE=DIRECTION \n");
+      mypdb.setAtomPositions( mypca->directions[dim].getReferencePositions() );
+      for(unsigned j=0;j<mypca->getArguments().size();++j) mypdb.setArgumentValue( (mypca->getArguments()[j])->getName(), mypca->directions[dim].getReferenceArgument(j) );
+      if( plumed.getAtoms().usingNaturalUnits() ) mypdb.print( 1.0, mymoldat, afile, fmt );
+      else mypdb.print( atoms.getUnits().getLength()/0.1, mymoldat, afile, fmt );
+  }
+  afile.close();
+}
+
+}
+}
diff --git a/src/dimred/PCA.cpp b/src/dimred/PCA.cpp
index 0f30f9ce8..c67c464cb 100644
--- a/src/dimred/PCA.cpp
+++ b/src/dimred/PCA.cpp
@@ -19,11 +19,9 @@
    You should have received a copy of the GNU Lesser General Public License
    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
-#include "analysis/AnalysisBase.h"
+#include "PCA.h"
 #include "tools/Matrix.h"
-#include "reference/Direction.h"
 #include "reference/MetricRegister.h"
-#include "reference/ReferenceConfiguration.h"
 #include "reference/ReferenceValuePack.h"
 #include "analysis/ReadAnalysisFrames.h"
 #include "core/ActionRegister.h"
@@ -93,39 +91,18 @@ PCA ARG=d1,d2,d3,d4,d5,d6 METRIC=EUCLIDEAN STRIDE=5 RUN=1000 NLOW_DIM=2 REWEIGHT
 namespace PLMD {
 namespace dimred {
 
-class PCA : public analysis::AnalysisBase {
-private:
-  unsigned ndim;
-/// The way we are measuring distances
-  std::string mtype;
-/// The position of the reference configuration (the one we align to)
-  PDB mypdb;
-/// The eigenvectors for the atomic displacements
-  Matrix<Vector> atom_eigv;
-/// The eigenvectors for the displacements in argument space
-  Matrix<double> arg_eigv;
-  std::string ofilename, fmt;
-public:
-  static void registerKeywords( Keywords& keys );
-  explicit PCA(const ActionOptions&ao);
-  void performAnalysis();
-  void performTask( const unsigned& , const unsigned& , MultiValue& ) const { plumed_error(); }
-};
-
 PLUMED_REGISTER_ACTION(PCA,"PCA")
 
 void PCA::registerKeywords( Keywords& keys ){
-  AnalysisBase::registerKeywords( keys ); keys.use("ARG"); keys.reset_style("ARG","optional");
+  DimensionalityReductionBase::registerKeywords( keys ); keys.use("ARG"); keys.reset_style("ARG","optional");
   keys.add("compulsory","METRIC","EUCLIDEAN","the method that you are going to use to measure the distances between points");
   keys.add("atoms","ATOMS","the list of atoms that you are going to use in the measure of distance that you are using");
-  keys.add("compulsory","NLOW_DIM","number of PCA coordinates required");
-  keys.add("compulsory","OFILE","the file on which to output the eigenvectors");
-  keys.add("compulsory","FMT","%f","the format to use for output files");
 }
 
 PCA::PCA(const ActionOptions&ao):
 Action(ao),
-AnalysisBase(ao)
+DimensionalityReductionBase(ao),
+myref(NULL)
 {
   // Get the input PDB file from the underlying ReadAnalysisFrames object
   analysis::ReadAnalysisFrames* myframes = dynamic_cast<analysis::ReadAnalysisFrames*>( my_input_data );
@@ -172,25 +149,19 @@ AnalysisBase(ao)
            mypdb.setArgumentNames( argnames ); requestArguments( myargs );
       }
   }
-  parse("NLOW_DIM",ndim);
-  if( mypdb.getPositions().size()>0 ) atom_eigv.resize( ndim, mypdb.getPositions().size() );
-  if( mypdb.getArgumentNames().size()>0 ) arg_eigv.resize( ndim, mypdb.getArgumentNames().size() );
-
-  // Read stuff for output file
-  parse("OFILE",ofilename); parse("FMT",fmt);
-  if( !getRestart() ){ OFile ofile; ofile.link(*this); ofile.setBackupString("analysis"); ofile.backupAllFiles(ofilename); } 
+  if( nlow==0 ) error("dimensionality of output not set");
   checkRead();
 }
 
 void PCA::performAnalysis(){
   // Align everything to the first frame
-  getStoredData( 0, false ).transferDataToPDB( mypdb ); ReferenceConfiguration* myconf0=metricRegister().create<ReferenceConfiguration>(mtype, mypdb);  
+  my_input_data->getStoredData( 0, false ).transferDataToPDB( mypdb ); 
+  ReferenceConfiguration* myconf0=metricRegister().create<ReferenceConfiguration>(mtype, mypdb);  
   MultiValue myval( 1, myconf0->getNumberOfReferenceArguments() + 3*myconf0->getNumberOfReferencePositions() + 9 );
   ReferenceValuePack mypack( myconf0->getNumberOfReferenceArguments(), myconf0->getNumberOfReferencePositions(), myval );
   for(unsigned i=0;i<myconf0->getNumberOfReferencePositions();++i) mypack.setAtomIndex( i, i );
   // Setup some PCA storage 
   myconf0->setupPCAStorage ( mypack );
-
   // Create some arrays to store the average position
   std::vector<double> sarg( myconf0->getNumberOfReferenceArguments(), 0 );
   std::vector<Vector> spos( myconf0->getNumberOfReferencePositions() );
@@ -199,7 +170,7 @@ void PCA::performAnalysis(){
   // Calculate the average displacement from the first frame
   double norm=getWeight(0); std::vector<double> args( getNumberOfArguments() );
   for(unsigned i=1;i<getNumberOfDataPoints();++i){
-      getStoredData( i, false ).transferDataToPDB( mypdb );
+      my_input_data->getStoredData( i, false ).transferDataToPDB( mypdb );
       for(unsigned j=0;j<getArguments().size();++j) mypdb.getArgumentValue( getArguments()[j]->getName(), args[j] );
       double d = myconf0->calc( mypdb.getPositions(), getPbc(), getArguments(), args, mypack, true );
       // Accumulate average displacement of arguments (Here PBC could do fucked up things - really needs Berry Phase ) GAT
@@ -216,9 +187,7 @@ void PCA::performAnalysis(){
   unsigned narg=myconf0->getNumberOfReferenceArguments(), natoms=myconf0->getNumberOfReferencePositions();
   Matrix<double> covar( narg+3*natoms, narg+3*natoms ); covar=0;
   for(unsigned i=0;i<getNumberOfDataPoints();++i){
-      // double d = data[i]->calc( spos, getPbc(), getArguments(), sarg, mypack, true );
-      // ReferenceConfiguration* myconf = getReferenceConfiguration( i, false );
-      getStoredData( i, false ).transferDataToPDB( mypdb );
+      my_input_data->getStoredData( i, false ).transferDataToPDB( mypdb );
       for(unsigned j=0;j<getArguments().size();++j) mypdb.getArgumentValue( getArguments()[j]->getName(), args[j] );
       double d = myconf0->calc( mypdb.getPositions(), getPbc(), getArguments(), args, mypack, true );
       for(unsigned jarg=0;jarg<narg;++jarg){
@@ -252,32 +221,49 @@ void PCA::performAnalysis(){
   Matrix<double> eigvec( narg+3*natoms, narg+3*natoms );
   diagMat( covar, eigval, eigvec );
 
-  // Open an output file
-  OFile ofile; ofile.link(*this); ofile.setBackupString("analysis");
-  ofile.open( ofilename ); 
   // Output the reference configuration
   mypdb.setAtomPositions( spos );
   for(unsigned j=0;j<sarg.size();++j) mypdb.setArgumentValue( getArguments()[j]->getName(), sarg[j] );
-  ofile.printf("REMARK TYPE=%s \n",mtype.c_str() );
-  mypdb.print( atoms.getUnits().getLength()/0.1, NULL, ofile, fmt );   
+  // Reset the reference configuration
+  if( myref ) delete myref;
+  myref = metricRegister().create<ReferenceConfiguration>( mtype, mypdb );
 
   // Store and print the eigenvectors
   std::vector<Vector> tmp_atoms( natoms ); 
-  for(unsigned dim=0;dim<ndim;++dim){
+  for(unsigned dim=0;dim<nlow;++dim){
      unsigned idim = covar.ncols() - 1 - dim;
-     for(unsigned i=0;i<narg;++i){ 
-         arg_eigv(dim,i)=eigvec(idim,i);
-         mypdb.setArgumentValue( getArguments()[i]->getName(), eigvec(idim,i) );
-     }
+     for(unsigned i=0;i<narg;++i) mypdb.setArgumentValue( getArguments()[i]->getName(), eigvec(idim,i) );
      for(unsigned i=0;i<natoms;++i){
-         for(unsigned k=0;k<3;++k) tmp_atoms[i][k]=atom_eigv(dim,i)[k]=eigvec(idim,narg+3*i+k);
+         for(unsigned k=0;k<3;++k) tmp_atoms[i][k]=eigvec(idim,narg+3*i+k);
      }  
      mypdb.setAtomPositions( tmp_atoms ); 
-     ofile.printf("REMARK TYPE=DIRECTION \n");
-     mypdb.print( atoms.getUnits().getLength()/0.1, NULL, ofile, fmt ); 
+     // Create a direction object so that we can calculate other PCA components
+     directions.push_back( Direction(ReferenceConfigurationOptions("DIRECTION"))); 
+     directions[dim].read( mypdb );
   } 
   // Close the output file   
-  delete myconf0; ofile.close();
+  delete myconf0; 
+}
+
+void PCA::getProjection( const unsigned& idata, std::vector<double>& point, double& weight ){
+  if( point.size()!=nlow ) point.resize( nlow );
+  // Retrieve the weight
+  weight = getWeight(idata); 
+  // Retrieve the data point
+  getProjection( my_input_data->getStoredData( idata, false ), point );
+}
+
+void PCA::getProjection( analysis::DataCollectionObject& myidata, std::vector<double>& point ){
+  myidata.transferDataToPDB( mypdb ); std::vector<double> args( getArguments().size() );
+  for(unsigned j=0;j<getArguments().size();++j) mypdb.getArgumentValue( getArguments()[j]->getName(), args[j] );
+  // Create some storage space
+  MultiValue myval( 1, 3*mypdb.getPositions().size() + args.size() + 9);
+  ReferenceValuePack mypack( args.size(), mypdb.getPositions().size(), myval );
+  for(unsigned i=0;i<mypdb.getPositions().size();++i) mypack.setAtomIndex( i, i );
+  myref->setupPCAStorage( mypack );
+  // And calculate
+  myref->calculate( mypdb.getPositions(), getPbc(), getArguments(), mypack, true );
+  for(unsigned i=0;i<nlow;++i) point[i]=myref->projectDisplacementOnVector( directions[i], mypdb.getPositions(), getArguments(), args, mypack );
 }
 
 }
diff --git a/src/dimred/PCA.h b/src/dimred/PCA.h
new file mode 100644
index 000000000..35dfd3f49
--- /dev/null
+++ b/src/dimred/PCA.h
@@ -0,0 +1,58 @@
+/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+   Copyright (c) 2012-2014 The plumed team
+   (see the PEOPLE file at the root of the distribution for a list of names)
+
+   See http://www.plumed-code.org for more information.
+
+   This file is part of plumed, version 2.
+
+   plumed is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   plumed 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with plumed.  If not, see <http://www.gnu.org/licenses/>.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+#ifndef __PLUMED_dimred_PCA_h
+#define __PLUMED_dimred_PCA_h
+
+#include "DimensionalityReductionBase.h"
+#include "reference/ReferenceConfiguration.h"
+#include "reference/Direction.h"
+#include "tools/PDB.h"
+
+namespace PLMD {
+namespace dimred {
+
+class PCA : public DimensionalityReductionBase {
+friend class OutputPCAProjection;
+private:
+/// The way we are measuring distances
+  std::string mtype;
+/// The position of the reference configuration (the one we align to)
+  PDB mypdb;
+/// The eigenvectors for the displacements in argument space
+  std::string ofilename, fmt;
+/// The eigenvectors that we are using
+  ReferenceConfiguration* myref;
+  std::vector<Direction> directions;
+public:
+  static void registerKeywords( Keywords& keys );
+  explicit PCA(const ActionOptions&ao);
+  void performAnalysis();
+  void getProjection( const unsigned& idata, std::vector<double>& point, double& weight );
+  void getProjection( analysis::DataCollectionObject& myidata, std::vector<double>& point );
+  void calculateProjections( const Matrix<double>& , Matrix<double>& ){ plumed_error(); }
+  void setTargetDistance( const unsigned& , const double& ){ plumed_error(); }
+  double calculateStress( const std::vector<double>& pp, std::vector<double>& der ){ plumed_error(); }
+};
+
+}
+}
+#endif
diff --git a/src/dimred/ProjectNonLandmarkPoints.cpp b/src/dimred/ProjectNonLandmarkPoints.cpp
index 47e9d1eb8..7bfa29f76 100644
--- a/src/dimred/ProjectNonLandmarkPoints.cpp
+++ b/src/dimred/ProjectNonLandmarkPoints.cpp
@@ -27,6 +27,7 @@
 #include "analysis/AnalysisBase.h"
 #include "reference/ReferenceConfiguration.h"
 #include "DimensionalityReductionBase.h"
+#include "PCA.h"
 
 //+PLUMEDOC DIMRED PROJECT_ALL_ANALYSIS_DATA
 /*
@@ -101,18 +102,23 @@ std::vector<Value*> ProjectNonLandmarkPoints::getArgumentList(){
 } 
 
 void ProjectNonLandmarkPoints::generateProjection( const unsigned& idat, std::vector<double>& point ){
-  ConjugateGradient<ProjectNonLandmarkPoints> myminimiser( this );
-  unsigned closest=0; double mindist = sqrt( getDissimilarity( mybase->getDataPointIndexInBase(0), idat ) );
-  mybase->setTargetDistance( 0, mindist ); 
-  for(unsigned i=1;i<mybase->getNumberOfDataPoints();++i){
-      double dist = sqrt( getDissimilarity( mybase->getDataPointIndexInBase(i), idat ) );
-      mybase->setTargetDistance( i, dist );
-      if( dist<mindist ){ mindist=dist; closest=i; }
+  PCA* ispca = dynamic_cast<PCA*>( mybase );
+  if( ispca ){
+      ispca->getProjection( getStoredData(idat,false), point ); 
+  } else {
+      ConjugateGradient<ProjectNonLandmarkPoints> myminimiser( this );
+      unsigned closest=0; double mindist = sqrt( getDissimilarity( mybase->getDataPointIndexInBase(0), idat ) );
+      mybase->setTargetDistance( 0, mindist ); 
+      for(unsigned i=1;i<mybase->getNumberOfDataPoints();++i){
+          double dist = sqrt( getDissimilarity( mybase->getDataPointIndexInBase(i), idat ) );
+          mybase->setTargetDistance( i, dist );
+          if( dist<mindist ){ mindist=dist; closest=i; }
+      }
+      // Put the initial guess near to the closest landmark  -- may wish to use grid here again Sandip??
+      Random random; random.setSeed(-1234);
+      for(unsigned j=0;j<nlow;++j) point[j]=mybase->projections(closest,j) + (random.RandU01() - 0.5)*0.01;
+      myminimiser.minimise( cgtol, point, &ProjectNonLandmarkPoints::calculateStress );
   }
-  // Put the initial guess near to the closest landmark  -- may wish to use grid here again Sandip??
-  Random random; random.setSeed(-1234);
-  for(unsigned j=0;j<nlow;++j) point[j]=mybase->projections(closest,j) + (random.RandU01() - 0.5)*0.01;
-  myminimiser.minimise( cgtol, point, &ProjectNonLandmarkPoints::calculateStress );
 }
 
 analysis::DataCollectionObject& ProjectNonLandmarkPoints::getStoredData( const unsigned& idat, const bool& calcdist ){
-- 
GitLab