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 288 : unsigned getNumberOfDerivatives() override { 66 288 : 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 43 : void DiagonalizeMatrix::registerKeywords( Keywords& keys ) { 79 43 : MatrixOperationBase::registerKeywords( keys ); 80 43 : keys.add("compulsory","VECTORS","all","the eigenvalues and vectors that you would like to calculate. 1=largest, 2=second largest and so on"); 81 86 : keys.addOutputComponent("vals","default","scalar","the eigevalues of the input matrix"); 82 86 : keys.addOutputComponent("vecs","default","vector","the eigenvectors of the input matrix"); 83 43 : } 84 : 85 23 : DiagonalizeMatrix::DiagonalizeMatrix(const ActionOptions& ao): 86 : Action(ao), 87 23 : MatrixOperationBase(ao) { 88 23 : 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 46 : parseVector("VECTORS",eigv); 94 23 : 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 13 : if( eigv.size()==0 ) { 102 0 : error("missing input to VECTORS keyword"); 103 : } 104 : unsigned ivec; 105 13 : if( eigv[0]=="all" ) { 106 8 : desired_vectors.resize( getPntrToArgument(0)->getShape()[0] ); 107 25 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 108 17 : 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 23 : std::vector<std::size_t> eval_shape(0); 119 23 : std::vector<std::size_t> evec_shape(1); 120 23 : evec_shape[0] = getPntrToArgument(0)->getShape()[0]; 121 65 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 122 42 : Tools::convert( desired_vectors[i], num ); 123 42 : addComponent( "vals-" + num, eval_shape ); 124 42 : componentIsNotPeriodic( "vals-" + num ); 125 42 : addComponent( "vecs-" + num, evec_shape ); 126 84 : componentIsNotPeriodic( "vecs-" + num ); 127 : // Make sure eigenvalues are always stored 128 : } 129 : 130 23 : std::vector<unsigned> eigvecs_shape(2); 131 23 : eigvecs_shape[0]=eigvecs_shape[1]=getPntrToArgument(0)->getShape()[0]; 132 23 : mymatrix.resize( eigvecs_shape[0], eigvecs_shape[1] ); 133 23 : eigvals.resize( eigvecs_shape[0] ); 134 23 : eigvecs.resize( eigvecs_shape[0], eigvecs_shape[1] ); 135 23 : } 136 : 137 709 : void DiagonalizeMatrix::prepare() { 138 709 : std::vector<std::size_t> shape(1); 139 709 : shape[0]=getPntrToArgument(0)->getShape()[0]; 140 2645 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 141 1936 : if( getPntrToComponent( 2*i+1 )->getShape()[0]!=shape[0] ) { 142 9 : getPntrToComponent( 2*i+1 )->setShape( shape ); 143 : } 144 : } 145 : 146 709 : } 147 : 148 708 : void DiagonalizeMatrix::calculate() { 149 708 : if( getPntrToArgument(0)->getShape()[0]==0 ) { 150 : return ; 151 : } 152 : // Resize stuff that might need resizing 153 708 : unsigned nvals=getPntrToArgument(0)->getShape()[0]; 154 708 : if( eigvals.size()!=nvals ) { 155 5 : mymatrix.resize( nvals, nvals ); 156 5 : eigvals.resize( nvals ); 157 5 : eigvecs.resize( nvals, nvals ); 158 : } 159 : 160 : // Retrieve the matrix from input 161 708 : retrieveFullMatrix( mymatrix ); 162 : // Now diagonalize the matrix 163 708 : diagMat( mymatrix, eigvals, eigvecs ); 164 : // And set the eigenvalues and eigenvectors 165 2642 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 166 1934 : getPntrToComponent(2*i)->set( eigvals[ mymatrix.ncols()-desired_vectors[i]] ); 167 1934 : Value* evec_out = getPntrToComponent(2*i+1); 168 1934 : unsigned vreq = mymatrix.ncols()-desired_vectors[i]; 169 11468 : for(unsigned j=0; j<mymatrix.ncols(); ++j) { 170 9534 : evec_out->set( j, eigvecs( vreq, j ) ); 171 : } 172 : } 173 : } 174 : 175 17540 : double DiagonalizeMatrix::getForceOnMatrixElement( const unsigned& jrow, const unsigned& kcol ) const { 176 : double ff = 0; 177 52310 : for(unsigned i=0; i<desired_vectors.size(); ++i) { 178 : // Deal with forces on eigenvalues 179 34770 : if( getConstPntrToComponent(2*i)->forcesWereAdded() ) { 180 13494 : unsigned ncol = mymatrix.ncols()-desired_vectors[i]; 181 13494 : ff += getConstPntrToComponent(2*i)->getForce(0)*eigvecs(ncol,jrow)*eigvecs(ncol,kcol); 182 : } 183 : // And forces on eigenvectors 184 34770 : if( !getConstPntrToComponent(2*i+1)->forcesWereAdded() ) { 185 : continue; 186 : } 187 : 188 7378 : unsigned ncol = mymatrix.ncols()-desired_vectors[i]; 189 105672 : for(unsigned n=0; n<mymatrix.nrows(); ++n) { 190 : double tmp2 = 0; 191 1439424 : for(unsigned m=0; m<mymatrix.nrows(); ++m) { 192 1341130 : if( m==ncol ) { 193 98294 : continue; 194 : } 195 1242836 : tmp2 += eigvecs(m,n)*eigvecs(m,jrow)*eigvecs(ncol,kcol) / (eigvals[ncol]-eigvals[m]); 196 : } 197 98294 : ff += getConstPntrToComponent(2*i+1)->getForce(n) * tmp2; 198 : } 199 : } 200 17540 : return ff; 201 : } 202 : 203 : } 204 : }