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 "SMACOF.h"
23 :
24 : namespace PLMD {
25 : namespace dimred {
26 :
27 1 : void SMACOF::run( const Matrix<double>& Weights, const Matrix<double>& Distances, const double& tol, const unsigned& maxloops, Matrix<double>& InitialZ ) {
28 : unsigned M = Distances.nrows();
29 :
30 : // Calculate V
31 : Matrix<double> V(M,M);
32 : double totalWeight=0.;
33 501 : for(unsigned i=0; i<M; ++i) {
34 250500 : for(unsigned j=0; j<M; ++j) {
35 250000 : if(i==j) {
36 500 : continue;
37 : }
38 249500 : V(i,j)=-Weights(i,j);
39 249500 : if( j<i ) {
40 124750 : totalWeight+=Weights(i,j);
41 : }
42 : }
43 250500 : for(unsigned j=0; j<M; ++j) {
44 250000 : if(i==j) {
45 500 : continue;
46 : }
47 249500 : V(i,i)-=V(i,j);
48 : }
49 : }
50 :
51 : // And pseudo invert V
52 : Matrix<double> mypseudo(M, M);
53 1 : pseudoInvert(V, mypseudo);
54 : Matrix<double> dists( M, M );
55 1 : double myfirstsig = calculateSigma( Weights, Distances, InitialZ, dists ) / totalWeight;
56 :
57 : // initial sigma is made up of the original distances minus the distances between the projections all squared.
58 : Matrix<double> temp( M, M ), BZ( M, M ), newZ( M, InitialZ.ncols() );
59 14 : for(unsigned n=0; n<maxloops; ++n) {
60 14 : if(n==maxloops-1) {
61 0 : plumed_merror("ran out of steps in SMACOF algorithm");
62 : }
63 :
64 : // Recompute BZ matrix
65 7014 : for(unsigned i=0; i<M; ++i) {
66 3507000 : for(unsigned j=0; j<M; ++j) {
67 3500000 : if(i==j) {
68 7000 : continue; //skips over the diagonal elements
69 : }
70 :
71 3493000 : if( dists(i,j)>0 ) {
72 3493000 : BZ(i,j) = -Weights(i,j)*Distances(i,j) / dists(i,j);
73 : } else {
74 0 : BZ(i,j)=0.;
75 : }
76 : }
77 : //the diagonal elements are -off diagonal elements BZ(i,i)-=BZ(i,j) (Equation 8.25)
78 7000 : BZ(i,i)=0; //create the space memory for the diagonal elements which are scalars
79 3507000 : for(unsigned j=0; j<M; ++j) {
80 3500000 : if(i==j) {
81 7000 : continue;
82 : }
83 3493000 : BZ(i,i)-=BZ(i,j);
84 : }
85 : }
86 :
87 14 : mult( mypseudo, BZ, temp);
88 14 : mult(temp, InitialZ, newZ);
89 : //Compute new sigma
90 14 : double newsig = calculateSigma( Weights, Distances, newZ, dists ) / totalWeight;
91 : //Computing whether the algorithm has converged (has the mass of the potato changed
92 : //when we put it back in the oven!)
93 14 : if( std::fabs( newsig - myfirstsig )<tol ) {
94 : break;
95 : }
96 : myfirstsig=newsig;
97 : InitialZ = newZ;
98 : }
99 1 : }
100 :
101 15 : double SMACOF::calculateSigma( const Matrix<double>& Weights, const Matrix<double>& Distances, const Matrix<double>& InitialZ, Matrix<double>& dists ) {
102 : unsigned M = Distances.nrows();
103 : double sigma=0;
104 7500 : for(unsigned i=1; i<M; ++i) {
105 1878735 : for(unsigned j=0; j<i; ++j) {
106 : double dlow=0;
107 5613750 : for(unsigned k=0; k<InitialZ.ncols(); ++k) {
108 3742500 : double tmp=InitialZ(i,k) - InitialZ(j,k);
109 3742500 : dlow+=tmp*tmp;
110 : }
111 1871250 : dists(i,j)=dists(j,i)=std::sqrt(dlow);
112 1871250 : double tmp3 = Distances(i,j) - dists(i,j);
113 1871250 : sigma += Weights(i,j)*tmp3*tmp3;
114 : }
115 : }
116 15 : return sigma;
117 : }
118 :
119 : }
120 : }
|