LCOV - code coverage report
Current view: top level - refdist - MatrixProductDiagonal.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 84 101 83.2 %
Date: 2026-11-05 10:19:04 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-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/ActionWithVector.h"
      23             : #include "core/ParallelTaskManager.h"
      24             : #include "core/ActionRegister.h"
      25             : #include <limits>
      26             : 
      27             : //+PLUMEDOC FUNCTION MATRIX_PRODUCT_DIAGONAL
      28             : /*
      29             : Calculate the product of two matrices and return a vector that contains the diagonal elements of the ouptut vector
      30             : 
      31             : This action allows you to [multiply](https://en.wikipedia.org/wiki/Matrix_multiplication) two matrices and recover the diagonal of the
      32             : product of the matrices. The following input shows an example where two contact matrices are multiplied together.
      33             : 
      34             : ```plumed
      35             : c1: CONTACT_MATRIX GROUP=1-100 SWITCH={RATIONAL R_0=0.1}
      36             : c2: CONTACT_MATRIX GROUP=1-100 SWITCH={RATIONAL R_0=0.2}
      37             : m: MATRIX_PRODUCT_DIAGONAL ARG=c1,c2
      38             : PRINT ARG=m FILE=colvar
      39             : ```
      40             : 
      41             : As you can see from the documentation for the shortcut [EUCLIDEAN_DISTANCE](EUCLIDEAN_DISTANCE.md) this action is useful for computing distances between
      42             : vectors.
      43             : 
      44             : */
      45             : //+ENDPLUMEDOC
      46             : 
      47             : namespace PLMD {
      48             : namespace refdist {
      49             : 
      50             : class MatrixProductDiagonalInput {
      51             : public:
      52             :   // the assignment is customized (see below): copy construction is the default one
      53             :   MatrixProductDiagonalInput() = default;
      54             :   MatrixProductDiagonalInput(const MatrixProductDiagonalInput&) = default;
      55             :   MatrixProductDiagonalInput& operator=( const MatrixProductDiagonalInput& m ) {
      56             :     // Don't need to copy A and B here as they will be set in calculate
      57             :     return *this;
      58             :   }
      59             : };
      60             : 
      61             : class MatrixProductDiagonal : public ActionWithVector {
      62             : public:
      63             :   using input_type = MatrixProductDiagonalInput;
      64             :   using PTM = ParallelTaskManager<MatrixProductDiagonal>;
      65             :   typedef typename PTM::ParallelActionsInput ParallelActionsInput;
      66             :   typedef typename PTM::ParallelActionsOutput ParallelActionsOutput;
      67             : private:
      68             :   PTM taskmanager;
      69             : public:
      70             :   static void registerKeywords( Keywords& keys );
      71             :   explicit MatrixProductDiagonal(const ActionOptions&);
      72             :   unsigned getNumberOfDerivatives() override ;
      73             :   void calculate() override ;
      74             :   void applyNonZeroRankForces( std::vector<double>& outforces ) override ;
      75             :   static void performTask( std::size_t task_index, const MatrixProductDiagonalInput& actiondata, ParallelActionsInput& input, ParallelActionsOutput& output );
      76             :   static int getNumberOfValuesPerTask( std::size_t task_index, const MatrixProductDiagonalInput& actiondata );
      77             :   static void getForceIndices( std::size_t task_index, std::size_t colno, std::size_t ntotal_force, const MatrixProductDiagonalInput& actiondata, const ParallelActionsInput& input, ForceIndexHolder force_indices );
      78             : };
      79             : 
      80             : PLUMED_REGISTER_ACTION(MatrixProductDiagonal,"MATRIX_PRODUCT_DIAGONAL")
      81             : 
      82         114 : void MatrixProductDiagonal::registerKeywords( Keywords& keys ) {
      83         114 :   ActionWithVector::registerKeywords(keys);
      84         228 :   keys.addInputKeyword("compulsory","ARG","vector/matrix","the two vectors/matrices whose product are to be taken");
      85         228 :   keys.setValueDescription("scalar/vector","a vector containing the diagonal elements of the matrix that obtaned by multiplying the two input matrices together");
      86         114 :   PTM::registerKeywords( keys );
      87         114 : }
      88             : 
      89          55 : MatrixProductDiagonal::MatrixProductDiagonal(const ActionOptions&ao):
      90             :   Action(ao),
      91             :   ActionWithVector(ao),
      92          55 :   taskmanager(this) {
      93          55 :   if( getNumberOfArguments()!=2 ) {
      94           0 :     error("should be two vectors or matrices in argument to this action");
      95             :   }
      96             : 
      97         110 :   const unsigned ncols = [&]()->unsigned{
      98          55 :     if( getPntrToArgument(0)->getRank()==1 ) {
      99           2 :       if( getPntrToArgument(0)->hasDerivatives() ) {
     100           0 :         error("first argument to this action should be a vector or matrix");
     101             :       }
     102             :       return 1;
     103          53 :     } else if( getPntrToArgument(0)->getRank()==2 ) {
     104          53 :       if( getPntrToArgument(0)->hasDerivatives() ) {
     105           0 :         error("first argument to this action should be a matrix");
     106             :       }
     107          53 :       return getPntrToArgument(0)->getShape()[1];
     108             :     }
     109             :     //you should not be here!
     110           0 :     error("first argument to this action should be a vector or matrix");
     111             :     //this is a dummy returns(in fact the code won't get here.
     112             :     //this lambda is there due to a -Wmaybe-uninitialized
     113             :     return std::numeric_limits<unsigned>::max();
     114          55 :   }();
     115             : 
     116          55 :   if( getPntrToArgument(1)->getRank()==1 ) {
     117          32 :     if( getPntrToArgument(1)->hasDerivatives() ) {
     118           0 :       error("second argument to this action should be a vector or matrix");
     119             :     }
     120          32 :     if( ncols!=getPntrToArgument(1)->getShape()[0] ) {
     121           0 :       error("number of columns in first matrix does not equal number of elements in vector");
     122             :     }
     123          32 :     if( getPntrToArgument(0)->getShape()[0]!=1 ) {
     124           0 :       error("matrix output by this action must be square");
     125             :     }
     126          32 :     addValueWithDerivatives();
     127          32 :     setNotPeriodic();
     128             :   } else {
     129          23 :     if( getPntrToArgument(1)->getRank()!=2 || getPntrToArgument(1)->hasDerivatives() ) {
     130           0 :       error("second argument to this action should be a vector or a matrix");
     131             :     }
     132          23 :     if( ncols!=getPntrToArgument(1)->getShape()[0] ) {
     133           0 :       error("number of columns in first matrix does not equal number of rows in second matrix");
     134             :     }
     135          23 :     if( getPntrToArgument(0)->getShape()[0]!=getPntrToArgument(1)->getShape()[1] ) {
     136           0 :       error("matrix output by this action must be square");
     137             :     }
     138          23 :     std::vector<std::size_t> shape(1);
     139          23 :     shape[0]=getPntrToArgument(0)->getShape()[0];
     140          23 :     addValue( shape );
     141          23 :     setNotPeriodic();
     142          23 :     taskmanager.setupParallelTaskManager( ncols + getPntrToArgument(1)->getShape()[0], 0 );
     143             :     taskmanager.setActionInput( MatrixProductDiagonalInput() );
     144          23 :   }
     145          55 : }
     146             : 
     147        1239 : unsigned MatrixProductDiagonal::getNumberOfDerivatives() {
     148        1239 :   if( doNotCalculateDerivatives() ) {
     149             :     return 0;
     150             :   }
     151          93 :   return getPntrToArgument(0)->getNumberOfValues() + getPntrToArgument(1)->getNumberOfValues();;
     152             : }
     153             : 
     154        2792 : void MatrixProductDiagonal::calculate() {
     155        2792 :   if( getPntrToArgument(0)->getRank()==1 && getPntrToArgument(1)->getRank()==1 ) {
     156           0 :     double val1 = getPntrToArgument(0)->get(0);
     157           0 :     double val2 = getPntrToArgument(1)->get(0);
     158           0 :     Value* myval = getPntrToComponent(0);
     159           0 :     myval->set( val1*val2 );
     160             : 
     161           0 :     if( doNotCalculateDerivatives() ) {
     162             :       return;
     163             :     }
     164             : 
     165             :     myval->setDerivative( 0, val2 );
     166             :     myval->setDerivative( 1, val1 );
     167        2792 :   } else if( getPntrToArgument(1)->getRank()==1 ) {
     168             :     Value* arg1 = getPntrToArgument(0);
     169             :     Value* arg2 = getPntrToArgument(1);
     170             :     unsigned nmult = arg1->getRowLength(0);
     171        1179 :     unsigned nvals1 = arg1->getNumberOfValues();
     172             : 
     173             :     double matval=0;
     174        1179 :     Value* myval = getPntrToComponent(0);
     175        4674 :     for(unsigned i=0; i<nmult; ++i) {
     176        3495 :       unsigned kind = arg1->getRowIndex( 0, i );
     177        3495 :       double val1 = arg1->get( i, false );
     178        3495 :       double val2 = arg2->get( kind );
     179        3495 :       matval += val1*val2;
     180             : 
     181        3495 :       if( doNotCalculateDerivatives() ) {
     182        3420 :         continue;
     183             :       }
     184             : 
     185             :       myval->addDerivative( kind, val2 );
     186          75 :       myval->addDerivative( nvals1 + kind, val1 );
     187             :     }
     188             :     myval->set( matval );
     189             :   } else {
     190        1613 :     taskmanager.runAllTasks();
     191             :   }
     192             : }
     193             : 
     194        1204 : void MatrixProductDiagonal::applyNonZeroRankForces( std::vector<double>& outforces ) {
     195        1204 :   taskmanager.applyForces( outforces );
     196        1204 : }
     197             : 
     198      130004 : void MatrixProductDiagonal::performTask( std::size_t task_index,
     199             :     const MatrixProductDiagonalInput& actiondata,
     200             :     ParallelActionsInput& input,
     201             :     ParallelActionsOutput& output ) {
     202      130004 :   auto arg0=ArgumentBookeepingHolder::create( 0, input );
     203      130004 :   auto arg1=ArgumentBookeepingHolder::create( 1, input );
     204      130004 :   std::size_t fpos = task_index*(1+arg0.ncols);
     205      130004 :   std::size_t nmult = arg0.bookeeping[fpos];
     206      130004 :   std::size_t vstart = task_index*arg0.ncols;
     207      130004 :   output.values[0] = 0;
     208      130004 :   if( arg1.ncols<arg1.shape[1] ) {
     209           0 :     plumed_merror("multiplying by a sparse matrix is not implemented as I don't think it is needed");
     210             :   } else {
     211      130004 :     std::size_t base = arg1.start + task_index;
     212     3905172 :     for(unsigned i=0; i<nmult; ++i) {
     213     3775168 :       double val1 = input.inputdata[vstart+i];
     214     3775168 :       double val2 = input.inputdata[ base + arg1.ncols*arg0.bookeeping[fpos+1+i] ];
     215     3775168 :       output.values[0] += val1*val2;
     216     3775168 :       output.derivatives[i] = val2;
     217     3775168 :       output.derivatives[nmult + i] = val1;
     218             :     }
     219             :   }
     220      130004 : }
     221             : 
     222       56018 : int MatrixProductDiagonal::getNumberOfValuesPerTask( std::size_t task_index, const MatrixProductDiagonalInput& actiondata ) {
     223       56018 :   return 1;
     224             : }
     225             : 
     226       56018 : void MatrixProductDiagonal::getForceIndices( std::size_t task_index,
     227             :     std::size_t colno,
     228             :     std::size_t ntotal_force,
     229             :     const MatrixProductDiagonalInput& actiondata,
     230             :     const ParallelActionsInput& input,
     231             :     ForceIndexHolder force_indices ) {
     232       56018 :   auto arg0=ArgumentBookeepingHolder::create( 0, input );
     233       56018 :   auto arg1=ArgumentBookeepingHolder::create( 1, input );
     234       56018 :   std::size_t fpos = task_index*(1+arg0.ncols);
     235       56018 :   std::size_t nmult = arg0.bookeeping[fpos];
     236       56018 :   std::size_t vstart = task_index*arg0.ncols;
     237       56018 :   if( arg1.ncols<arg1.shape[1] ) {
     238           0 :     plumed_merror("multiplying by a sparse matrix is not implemented as I don't think it is needed");
     239             :   } else {
     240       56018 :     std::size_t base = arg1.start + task_index;
     241     1910866 :     for(unsigned i=0; i<nmult; ++i) {
     242     1854848 :       force_indices.indices[0][i] = vstart + i;
     243     1854848 :       force_indices.indices[0][nmult+i] = base + arg1.ncols*arg0.bookeeping[fpos+1+i];
     244             :     }
     245       56018 :     force_indices.threadsafe_derivatives_end[0] = 2*nmult;
     246       56018 :     force_indices.tot_indices[0] = 2*nmult;
     247             :   }
     248       56018 : }
     249             : 
     250             : }
     251             : }

Generated by: LCOV version 1.16