void ClassicalScaling::run( PointWiseMapping* mymap ){ // Retrieve the distances from the dimensionality reduction object double half=(-0.5); Matrix<double> distances( half*mymap->modifyDmat() ); // Apply centering transtion unsigned n=distances.nrows(); double sum; // First HM for(unsigned i=0;i<n;++i){ sum=0; for(unsigned j=0;j<n;++j) sum+=distances(i,j); for(unsigned j=0;j<n;++j) distances(i,j) -= sum/n; } // Now (HM)H for(unsigned i=0;i<n;++i){ sum=0; for(unsigned j=0;j<n;++j) sum+=distances(j,i); for(unsigned j=0;j<n;++j) distances(j,i) -= sum/n; } // Diagonalize matrix std::vector<double> eigval(n); Matrix<double> eigvec(n,n); diagMat( distances, eigval, eigvec ); // Pass final projections to map object for(unsigned i=0;i<n;++i){ for(unsigned j=0;j<mymap->getNumberOfProperties();++j) mymap->setProjectionCoordinate( i, j, sqrt(eigval[n-1-j])*eigvec(n-1-j,i) ); } }
int main () { // Define symmetric matrix PLMD::Matrix<double> mat1(3,3); PLMD::OFile out; out.open("output"); mat1(0,0)=1.0; mat1(0,1)=0.2; mat1(0,2)=0.3; mat1(1,0)=0.2; mat1(1,1)=0.2; mat1(1,2)=0.6; mat1(2,0)=0.3; mat1(2,1)=0.6; mat1(2,2)=0.4; // Test diagonalize std::vector<double> eigval(3); PLMD::Matrix<double> eigvec(3,3); diagMat( mat1, eigval, eigvec ); out<<"Eigenvalues "<<eigval[0]<<" "<<eigval[1]<<" "<<eigval[2]<<"\n"; out<<"Eigenvectors : \n"; for(unsigned i=0;i<3;++i){ out<<eigvec(i,0)<<" "<<eigvec(i,1)<<" "<<eigvec(i,2)<<"\n"; } // Test inverse out<<"Inverse : \n"; PLMD::Matrix<double> inverse(3,3); Invert( mat1, inverse ); for(unsigned i=0;i<3;++i){ for(unsigned j=0;j<3;++j) out<<inverse(i,j)<<" "; out<<"\n"; } // Test pseudoinverse out<<"Pseudoinverse : \n"; PLMD::Matrix<double> mat(3,2); mat(0,0)=0.1; mat(0,1)=0.2; mat(1,0)=0.3; mat(1,1)=0.5; mat(2,0)=0.4; mat(2,1)=0.6; PLMD::Matrix<double> pseu(2,3); pseudoInvert( mat, pseu ); for(unsigned i=0;i<pseu.nrows();++i){ for(unsigned j=0;j<pseu.ncols();++j) out<<" "<<pseu(i,j); out<<"\n"; } out.close(); return 0; }
double RMSD::optimalAlignment(const std::vector<double> & align, const std::vector<double> & displace, const std::vector<Vector> & positions, const std::vector<Vector> & reference , std::vector<Vector> & derivatives, bool squared) { plumed_massert(displace==align,"OPTIMAL_FAST version of RMSD can only be used when displace weights are same as align weights"); double dist(0); double norm(0); const unsigned n=reference.size(); // This is the trace of positions*positions + reference*reference double sum00w(0); // This is positions*reference Tensor sum01w; derivatives.resize(n); Vector cpositions; Vector creference; // first expensive loop: compute centers for(unsigned iat=0;iat<n;iat++){ double w=align[iat]; norm+=w; cpositions+=positions[iat]*w; creference+=reference[iat]*w; } double invnorm=1.0/norm; cpositions*=invnorm; creference*=invnorm; // second expensive loop: compute second moments wrt centers for(unsigned iat=0;iat<n;iat++){ double w=align[iat]; sum00w+=(dotProduct(positions[iat]-cpositions,positions[iat]-cpositions) +dotProduct(reference[iat]-creference,reference[iat]-creference))*w; sum01w+=Tensor(positions[iat]-cpositions,reference[iat]-creference)*w; } double rr00=sum00w*invnorm; Tensor rr01=sum01w*invnorm; Matrix<double> m=Matrix<double>(4,4); m[0][0]=rr00+2.0*(-rr01[0][0]-rr01[1][1]-rr01[2][2]); m[1][1]=rr00+2.0*(-rr01[0][0]+rr01[1][1]+rr01[2][2]); m[2][2]=rr00+2.0*(+rr01[0][0]-rr01[1][1]+rr01[2][2]); m[3][3]=rr00+2.0*(+rr01[0][0]+rr01[1][1]-rr01[2][2]); m[0][1]=2.0*(-rr01[1][2]+rr01[2][1]); m[0][2]=2.0*(+rr01[0][2]-rr01[2][0]); m[0][3]=2.0*(-rr01[0][1]+rr01[1][0]); m[1][2]=2.0*(-rr01[0][1]-rr01[1][0]); m[1][3]=2.0*(-rr01[0][2]-rr01[2][0]); m[2][3]=2.0*(-rr01[1][2]-rr01[2][1]); m[1][0] = m[0][1]; m[2][0] = m[0][2]; m[2][1] = m[1][2]; m[3][0] = m[0][3]; m[3][1] = m[1][3]; m[3][2] = m[2][3]; vector<double> eigenvals; Matrix<double> eigenvecs; int diagerror=diagMat(m, eigenvals, eigenvecs ); if (diagerror!=0){ string sdiagerror; Tools::convert(diagerror,sdiagerror); string msg="DIAGONALIZATION FAILED WITH ERROR CODE "+sdiagerror; plumed_merror(msg); } dist=eigenvals[0]; Matrix<double> ddist_dm(4,4); Vector4d q(eigenvecs[0][0],eigenvecs[0][1],eigenvecs[0][2],eigenvecs[0][3]); // This is the rotation matrix that brings reference to positions // i.e. matmul(rotation,reference[iat])+shift is fitted to positions[iat] Tensor rotation; rotation[0][0]=q[0]*q[0]+q[1]*q[1]-q[2]*q[2]-q[3]*q[3]; rotation[1][1]=q[0]*q[0]-q[1]*q[1]+q[2]*q[2]-q[3]*q[3]; rotation[2][2]=q[0]*q[0]-q[1]*q[1]-q[2]*q[2]+q[3]*q[3]; rotation[0][1]=2*(+q[0]*q[3]+q[1]*q[2]); rotation[0][2]=2*(-q[0]*q[2]+q[1]*q[3]); rotation[1][2]=2*(+q[0]*q[1]+q[2]*q[3]); rotation[1][0]=2*(-q[0]*q[3]+q[1]*q[2]); rotation[2][0]=2*(+q[0]*q[2]+q[1]*q[3]); rotation[2][1]=2*(-q[0]*q[1]+q[2]*q[3]); double prefactor=2.0*invnorm; Vector shift=cpositions-matmul(rotation,creference); if(!squared) prefactor*=0.5/sqrt(dist); // if "safe", recompute dist here to a better accuracy if(safe) dist=0.0; // If safe is set to "false", MSD is taken from the eigenvalue of the M matrix // If safe is set to "true", MSD is recomputed from the rotational matrix // For some reason, this last approach leads to less numerical noise but adds an overhead // third expensive loop: derivatives for(unsigned iat=0;iat<n;iat++){ // there is no need for derivatives of rotation and shift here as it is by construction zero // (similar to Hellman-Feynman forces) Vector d(positions[iat]-shift - matmul(rotation,reference[iat])); derivatives[iat]= prefactor*align[iat]*d; if(safe) dist+=align[iat]*invnorm*modulo2(d); } if(!squared) dist=sqrt(dist); return dist; }