Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2014-2017 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 "MatrixOperationBase.h" 23 : #include "core/ActionRegister.h" 24 : 25 : //+PLUMEDOC ANALYSIS DIAGONALIZE 26 : /* 27 : Calculate the eigenvalues and eigenvectors of a square matrix 28 : 29 : This action allows you to use the [dsyevr](https://www.netlib.org/lapack/explore-html/d1/d56/group__heevr_gaa334ac0c11113576db0fc37b7565e8b5.html#gaa334ac0c11113576db0fc37b7565e8b5) 30 : function from the [LAPACK](https://www.netlib.org/lapack/explore-html/) library to calculate the [eigenvalues and eigenvectors](https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix) 31 : of a real symmetric matrix. For example, the following input can be used to calculate and print all four eigenvalues of the input [DISTANCE_MATRIX](DISTANCE_MATRIX.md). 32 : 33 : ```plumed 34 : d: DISTANCE_MATRIX GROUP=1-4 35 : diag: DIAGONALIZE ARG=d 36 : PRINT ARG=diag.vals-1,diag.vals-2,diag.vals-3,diag.vals-4 FILE=colvar 37 : ``` 38 : 39 : If you wish to calculate only a subset of the eigenvalues and eigenvectors you would use an input like the one shown below. This input calculates and outputs the largest eigenvalue 40 : and its corresponding eigenvector. 41 : 42 : ```plumed 43 : d: DISTANCE_MATRIX GROUP=1-4 44 : diag: DIAGONALIZE ARG=d VECTORS=1 45 : PRINT ARG=diag.vals-1,diag.vecs-1 FILE=colvar 46 : ``` 47 : 48 : */ 49 : //+ENDPLUMEDOC 50 : 51 : namespace PLMD { 52 : namespace matrixtools { 53 : 54 : class DiagonalizeMatrix : public MatrixOperationBase { 55 : private: 56 : std::vector<unsigned> desired_vectors; 57 : Matrix<double> mymatrix; 58 : std::vector<double> eigvals; 59 : Matrix<double> eigvecs; 60 : public: 61 : static void registerKeywords( Keywords& keys ); 62 : /// Constructor 63 : explicit DiagonalizeMatrix(const ActionOptions&); 64 : /// This is required to set the number of derivatives for the eigenvalues 65 276 : unsigned getNumberOfDerivatives() override { 66 276 : return getPntrToArgument(0)->getNumberOfValues(); 67 : } 68 : /// 69 : void prepare() override ; 70 : /// 71 : void calculate() override ; 72 : /// 73 : double getForceOnMatrixElement( const unsigned& jrow, const unsigned& krow ) const override; 74 : }; 75 : 76 : PLUMED_REGISTER_ACTION(DiagonalizeMatrix,"DIAGONALIZE") 77 : 78 41 : void DiagonalizeMatrix::registerKeywords( Keywords& keys ) { 79 41 : MatrixOperationBase::registerKeywords( keys ); 80 41 : keys.add("compulsory","VECTORS","all","the eigenvalues and vectors that you would like to calculate. 1=largest, 2=second largest and so on"); 81 82 : keys.addOutputComponent("vals","default","scalar","the eigevalues of the input matrix"); 82 82 : keys.addOutputComponent("vecs","default","vector","the eigenvectors of the input matrix"); 83 41 : } 84 : 85 22 : DiagonalizeMatrix::DiagonalizeMatrix(const ActionOptions& ao): 86 : Action(ao), 87 22 : MatrixOperationBase(ao) { 88 22 : if( getPntrToArgument(0)->getShape()[0]!=getPntrToArgument(0)->getShape()[1] ) { 89 0 : error("input matrix should be square"); 90 : } 91 : 92 : std::vector<std::string> eigv; 93 44 : parseVector("VECTORS",eigv); 94 22 : if( eigv.size()>1 ) { 95 10 : Tools::interpretRanges(eigv); 96 10 : desired_vectors.resize( eigv.size() ); 97 30 : for(unsigned i=0; i<eigv.size(); ++i) { 98 20 : Tools::convert( eigv[i], desired_vectors[i] ); 99 : } 100 : } else { 101 12 : if( eigv.size()==0 ) { 102 0 : error("missing input to VECTORS keyword"); 103 : } 104 : unsigned ivec; 105 12 : if( eigv[0]=="all" ) { 106 7 : desired_vectors.resize( getPntrToArgument(0)->getShape()[0] ); 107 21 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 108 14 : desired_vectors[i] = i + 1; 109 : } 110 : } else { 111 5 : Tools::convert( eigv[0], ivec ); 112 5 : desired_vectors.resize(1); 113 5 : desired_vectors[0]=ivec; 114 : } 115 : } 116 : 117 : std::string num; 118 22 : std::vector<unsigned> eval_shape(0); 119 22 : std::vector<unsigned> evec_shape(1); 120 22 : evec_shape[0] = getPntrToArgument(0)->getShape()[0]; 121 61 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 122 39 : Tools::convert( desired_vectors[i], num ); 123 39 : addComponent( "vals-" + num, eval_shape ); 124 39 : componentIsNotPeriodic( "vals-" + num ); 125 39 : addComponent( "vecs-" + num, evec_shape ); 126 39 : componentIsNotPeriodic( "vecs-" + num ); 127 : // Make sure eigenvalues are always stored 128 39 : getPntrToComponent( 2*i+1 )->buildDataStore(); 129 : } 130 : 131 22 : std::vector<unsigned> eigvecs_shape(2); 132 22 : eigvecs_shape[0]=eigvecs_shape[1]=getPntrToArgument(0)->getShape()[0]; 133 22 : mymatrix.resize( eigvecs_shape[0], eigvecs_shape[1] ); 134 22 : eigvals.resize( eigvecs_shape[0] ); 135 22 : eigvecs.resize( eigvecs_shape[0], eigvecs_shape[1] ); 136 22 : } 137 : 138 124 : void DiagonalizeMatrix::prepare() { 139 124 : std::vector<unsigned> shape(1); 140 124 : shape[0]=getPntrToArgument(0)->getShape()[0]; 141 303 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 142 179 : if( getPntrToComponent( 2*i+1 )->getShape()[0]!=shape[0] ) { 143 9 : getPntrToComponent( 2*i+1 )->setShape( shape ); 144 : } 145 : } 146 : 147 124 : } 148 : 149 122 : void DiagonalizeMatrix::calculate() { 150 122 : if( getPntrToArgument(0)->getShape()[0]==0 ) { 151 : return ; 152 : } 153 : // Resize stuff that might need resizing 154 : unsigned nvals=getPntrToArgument(0)->getShape()[0]; 155 122 : if( eigvals.size()!=nvals ) { 156 : mymatrix.resize( nvals, nvals ); 157 5 : eigvals.resize( nvals ); 158 : eigvecs.resize( nvals, nvals ); 159 : } 160 : 161 : // Retrieve the matrix from input 162 122 : retrieveFullMatrix( mymatrix ); 163 : // Now diagonalize the matrix 164 122 : diagMat( mymatrix, eigvals, eigvecs ); 165 : // And set the eigenvalues and eigenvectors 166 297 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 167 175 : getPntrToComponent(2*i)->set( eigvals[ mymatrix.ncols()-desired_vectors[i]] ); 168 175 : Value* evec_out = getPntrToComponent(2*i+1); 169 175 : unsigned vreq = mymatrix.ncols()-desired_vectors[i]; 170 4526 : for(unsigned j=0; j<mymatrix.ncols(); ++j) { 171 4351 : evec_out->set( j, eigvecs( vreq, j ) ); 172 : } 173 : } 174 : } 175 : 176 12495 : double DiagonalizeMatrix::getForceOnMatrixElement( const unsigned& jrow, const unsigned& kcol ) const { 177 : double ff = 0; 178 31654 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 179 : // Deal with forces on eigenvalues 180 19159 : if( getConstPntrToComponent(2*i)->forcesWereAdded() ) { 181 8330 : unsigned ncol = mymatrix.ncols()-desired_vectors[i]; 182 8330 : ff += getConstPntrToComponent(2*i)->getForce(0)*eigvecs(ncol,jrow)*eigvecs(ncol,kcol); 183 : } 184 : // And forces on eigenvectors 185 19159 : if( !getConstPntrToComponent(2*i+1)->forcesWereAdded() ) { 186 : continue; 187 : } 188 : 189 7497 : unsigned ncol = mymatrix.ncols()-desired_vectors[i]; 190 106624 : for(unsigned n=0; n<mymatrix.nrows(); ++n) { 191 : double tmp2 = 0; 192 1446088 : for(unsigned m=0; m<mymatrix.nrows(); ++m) { 193 1346961 : if( m==ncol ) { 194 99127 : continue; 195 : } 196 1247834 : tmp2 += eigvecs(m,n)*eigvecs(m,jrow)*eigvecs(ncol,kcol) / (eigvals[ncol]-eigvals[m]); 197 : } 198 99127 : ff += getConstPntrToComponent(2*i+1)->getForce(n) * tmp2; 199 : } 200 : } 201 12495 : return ff; 202 : } 203 : 204 : } 205 : }