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 13787 : PLUMED_REGISTER_ACTION(SketchMapSmacof,"SKETCHMAP_SMACOF")
50 :
51 5 : void SketchMapSmacof::registerKeywords( Keywords& keys ) {
52 5 : SketchMapBase::registerKeywords( keys );
53 10 : keys.add("compulsory","SMACOF_TOL","1E-4","the tolerance for each SMACOF cycle");
54 10 : keys.add("compulsory","SMACOF_MAXCYC","1000","maximum number of optimization cycles for SMACOF algorithm");
55 10 : keys.add("compulsory","SMAP_TOL","1E-4","the tolerance for sketch-map");
56 10 : keys.add("compulsory","SMAP_MAXCYC","100","maximum number of optimization cycles for iterative sketch-map algorithm");
57 10 : keys.add("compulsory","REGULARISE_PARAM","0.001","this is used to ensure that we don't divide by zero when updating weights");
58 5 : }
59 :
60 1 : SketchMapSmacof::SketchMapSmacof( const ActionOptions& ao ):
61 : Action(ao),
62 1 : SketchMapBase(ao) {
63 1 : parse("REGULARISE_PARAM",regulariser);
64 1 : parse("SMACOF_MAXCYC",max_smap);
65 1 : parse("SMAP_MAXCYC",maxiter);
66 1 : parse("SMACOF_TOL",smap_tol);
67 1 : parse("SMAP_TOL",iter_tol);
68 1 : }
69 :
70 1 : void SketchMapSmacof::minimise( Matrix<double>& projections ) {
71 : Matrix<double> weights( distances.nrows(), distances.ncols() );
72 : weights=0.;
73 1 : double filt = recalculateWeights( projections, weights );
74 :
75 1 : for(unsigned i=0; i<maxiter; ++i) {
76 1 : SMACOF::run( weights, distances, smap_tol, max_smap, projections );
77 : // Recalculate weights matrix and sigma
78 1 : double newsig = recalculateWeights( projections, weights );
79 : // Test whether or not the algorithm has converged
80 1 : if( std::fabs( newsig - filt )<iter_tol ) {
81 : break;
82 : }
83 : // Make initial sigma into new sigma so that the value of new sigma is used every time so that the error can be reduced
84 : filt=newsig;
85 : }
86 1 : }
87 :
88 2 : double SketchMapSmacof::recalculateWeights( const Matrix<double>& projections, Matrix<double>& weights ) {
89 : double filt=0, totalWeight=0.;;
90 : double dr;
91 1000 : for(unsigned i=1; i<weights.nrows(); ++i) {
92 250498 : for(unsigned j=0; j<i; ++j) {
93 249500 : double ninj=getWeight(i)*getWeight(j);
94 249500 : totalWeight += ninj;
95 :
96 : double tempd=0;
97 748500 : for(unsigned k=0; k<projections.ncols(); ++k) {
98 499000 : double tmp = projections(i,k) - projections(j,k);
99 499000 : tempd += tmp*tmp;
100 : }
101 249500 : double dij=std::sqrt(tempd);
102 :
103 249500 : double fij = transformLowDimensionalDistance( dij, dr );
104 249500 : double filter=transformed(i,j)-fij;
105 249500 : double diff=distances(i,j) - dij;
106 :
107 249500 : if( std::fabs(diff)<regulariser ) {
108 97 : weights(i,j)=weights(j,i)=0.0;
109 : } else {
110 249403 : weights(i,j)=weights(j,i) = ninj*( (1-mixparam)*( filter*dr )/diff + mixparam );
111 : }
112 249500 : filt += ninj*( (1-mixparam)*filter*filter + mixparam*diff*diff );
113 : }
114 : }
115 2 : return filt / totalWeight;
116 : }
117 :
118 : }
119 : }
|