Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2015-2023 The plumed team 3 : (see the PEOPLE file at the root of the distribution for a list of names) 4 : 5 : See http://www.plumed.org for more information. 6 : 7 : This file is part of plumed, version 2. 8 : 9 : plumed is free software: you can redistribute it and/or modify 10 : it under the terms of the GNU Lesser General Public License as published by 11 : the Free Software Foundation, either version 3 of the License, or 12 : (at your option) any later version. 13 : 14 : plumed is distributed in the hope that it will be useful, 15 : but WITHOUT ANY WARRANTY; without even the implied warranty of 16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 : GNU Lesser General Public License for more details. 18 : 19 : You should have received a copy of the GNU Lesser General Public License 20 : along with plumed. If not, see <http://www.gnu.org/licenses/>. 21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 22 : #include "core/ActionRegister.h" 23 : #include "SketchMapBase.h" 24 : #include "SMACOF.h" 25 : 26 : //+PLUMEDOC DIMRED SKETCHMAP_SMACOF 27 : /* 28 : Optimize the sketch-map stress function using the SMACOF algorithm. 29 : 30 : \par Examples 31 : 32 : */ 33 : //+ENDPLUMEDOC 34 : 35 : namespace PLMD { 36 : namespace dimred { 37 : 38 : class SketchMapSmacof : public SketchMapBase { 39 : private: 40 : unsigned max_smap, maxiter; 41 : double smap_tol, iter_tol, regulariser; 42 : double recalculateWeights( const Matrix<double>& projections, Matrix<double>& weights ); 43 : public: 44 : static void registerKeywords( Keywords& keys ); 45 : explicit SketchMapSmacof( const ActionOptions& ao ); 46 : void minimise( Matrix<double>& ) override; 47 : }; 48 : 49 10421 : PLUMED_REGISTER_ACTION(SketchMapSmacof,"SKETCHMAP_SMACOF") 50 : 51 2 : void SketchMapSmacof::registerKeywords( Keywords& keys ) { 52 2 : SketchMapBase::registerKeywords( keys ); 53 4 : keys.add("compulsory","SMACOF_TOL","1E-4","the tolerance for each SMACOF cycle"); 54 4 : keys.add("compulsory","SMACOF_MAXCYC","1000","maximum number of optimization cycles for SMACOF algorithm"); 55 4 : keys.add("compulsory","SMAP_TOL","1E-4","the tolerance for sketch-map"); 56 4 : keys.add("compulsory","SMAP_MAXCYC","100","maximum number of optimization cycles for iterative sketch-map algorithm"); 57 6 : keys.add("compulsory","REGULARISE_PARAM","0.001","this is used to ensure that we don't divide by zero when updating weights"); 58 2 : } 59 : 60 1 : SketchMapSmacof::SketchMapSmacof( const ActionOptions& ao ): 61 : Action(ao), 62 1 : SketchMapBase(ao) 63 : { 64 1 : parse("REGULARISE_PARAM",regulariser); 65 2 : parse("SMACOF_MAXCYC",max_smap); parse("SMAP_MAXCYC",maxiter); 66 2 : parse("SMACOF_TOL",smap_tol); parse("SMAP_TOL",iter_tol); 67 1 : } 68 : 69 1 : void SketchMapSmacof::minimise( Matrix<double>& projections ) { 70 : Matrix<double> weights( distances.nrows(), distances.ncols() ); weights=0.; 71 1 : double filt = recalculateWeights( projections, weights ); 72 : 73 1 : for(unsigned i=0; i<maxiter; ++i) { 74 1 : SMACOF::run( weights, distances, smap_tol, max_smap, projections ); 75 : // Recalculate weights matrix and sigma 76 1 : double newsig = recalculateWeights( projections, weights ); 77 : // Test whether or not the algorithm has converged 78 1 : if( std::fabs( newsig - filt )<iter_tol ) break; 79 : // Make initial sigma into new sigma so that the value of new sigma is used every time so that the error can be reduced 80 : filt=newsig; 81 : } 82 1 : } 83 : 84 2 : double SketchMapSmacof::recalculateWeights( const Matrix<double>& projections, Matrix<double>& weights ) { 85 : double filt=0, totalWeight=0.;; double dr; 86 1000 : for(unsigned i=1; i<weights.nrows(); ++i) { 87 250498 : for(unsigned j=0; j<i; ++j) { 88 249500 : double ninj=getWeight(i)*getWeight(j); totalWeight += ninj; 89 : 90 : double tempd=0; 91 748500 : for(unsigned k=0; k<projections.ncols(); ++k) { 92 499000 : double tmp = projections(i,k) - projections(j,k); 93 499000 : tempd += tmp*tmp; 94 : } 95 249500 : double dij=std::sqrt(tempd); 96 : 97 249500 : double fij = transformLowDimensionalDistance( dij, dr ); 98 249500 : double filter=transformed(i,j)-fij; 99 249500 : double diff=distances(i,j) - dij; 100 : 101 249500 : if( std::fabs(diff)<regulariser ) weights(i,j)=weights(j,i)=0.0; 102 249403 : else weights(i,j)=weights(j,i) = ninj*( (1-mixparam)*( filter*dr )/diff + mixparam ); 103 249500 : filt += ninj*( (1-mixparam)*filter*filter + mixparam*diff*diff ); 104 : } 105 : } 106 2 : return filt / totalWeight; 107 : } 108 : 109 : } 110 : }