LCOV - code coverage report
Current view: top level - landmarks - LandmarkSelection.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 83 92 90.2 %
Date: 2025-11-25 13:55:50 Functions: 2 3 66.7 %

          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 "core/ActionShortcut.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "core/ActionWithValue.h"
      25             : #include "core/ActionPilot.h"
      26             : #include "core/PlumedMain.h"
      27             : #include "core/ActionSet.h"
      28             : 
      29             : //+PLUMEDOC LANDMARKS LANDMARK_SELECT_STRIDE
      30             : /*
      31             : Select every ith frame from the stored data
      32             : 
      33             : \par Examples
      34             : 
      35             : */
      36             : //+ENDPLUMEDOC
      37             : 
      38             : //+PLUMEDOC LANDMARKS LANDMARK_SELECT_RANDOM
      39             : /*
      40             : Select a random set of landmarks from a large set of configurations.
      41             : 
      42             : \par Examples
      43             : 
      44             : */
      45             : //+ENDPLUMEDOC
      46             : 
      47             : //+PLUMEDOC LANDMARKS LANDMARK_SELECT_FPS
      48             : /*
      49             : Select a of landmarks from a large set of configurations using farthest point sampling.
      50             : 
      51             : \par Examples
      52             : 
      53             : */
      54             : //+ENDPLUMEDOC
      55             : 
      56             : namespace PLMD {
      57             : namespace landmarks {
      58             : 
      59             : class LandmarkSelection : public ActionShortcut {
      60             : public:
      61             :   static void registerKeywords( Keywords& keys );
      62             :   explicit LandmarkSelection( const ActionOptions& ao );
      63             : };
      64             : 
      65             : PLUMED_REGISTER_ACTION(LandmarkSelection,"LANDMARK_SELECT_STRIDE")
      66             : PLUMED_REGISTER_ACTION(LandmarkSelection,"LANDMARK_SELECT_RANDOM")
      67             : PLUMED_REGISTER_ACTION(LandmarkSelection,"LANDMARK_SELECT_FPS")
      68             : 
      69          17 : void LandmarkSelection::registerKeywords( Keywords& keys ) {
      70          17 :   ActionShortcut::registerKeywords( keys );
      71          34 :   keys.add("optional","ARG","the COLLECT_FRAMES action that you used to get the data");
      72          34 :   keys.add("optional","DISSIMILARITIES","the matrix of dissimilarities if this is not provided the squared dissimilarities are calculated");
      73          34 :   keys.add("compulsory","NLANDMARKS","the numbe rof landmarks you would like to create");
      74          34 :   keys.add("optional","SEED","a random number seed");
      75          34 :   keys.addFlag("NOVORONOI",false,"do not do a Voronoi analysis of the data to determine weights of final points");
      76          34 :   keys.addFlag("NODISSIMILARITIES",false,"do not calculate the dissimilarities");
      77          34 :   keys.addOutputComponent("data","ARG","the data that is being collected by this action");
      78          34 :   keys.addOutputComponent("logweights","ARG","the logarithms of the weights of the data points");
      79          34 :   keys.addOutputComponent("rectdissims","DISSIMILARITIES","a rectangular matrix containing the distances between the landmark points and the rest of the points");
      80          34 :   keys.addOutputComponent("sqrdissims","DISSIMILARITIES","a square matrix containing the distances between each pair of landmark points");
      81          17 :   keys.needsAction("LOGSUMEXP");
      82          17 :   keys.needsAction("TRANSPOSE");
      83          17 :   keys.needsAction("DISSIMILARITIES");
      84          17 :   keys.needsAction("ONES");
      85          17 :   keys.needsAction("CREATE_MASK");
      86          17 :   keys.needsAction("FARTHEST_POINT_SAMPLING");
      87          17 :   keys.needsAction("SELECT_WITH_MASK");
      88          17 :   keys.needsAction("COMBINE");
      89          17 :   keys.needsAction("VORONOI");
      90          17 :   keys.needsAction("MATRIX_PRODUCT");
      91          17 :   keys.needsAction("CUSTOM");
      92          17 : }
      93             : 
      94           8 : LandmarkSelection::LandmarkSelection( const ActionOptions& ao ):
      95             :   Action(ao),
      96           8 :   ActionShortcut(ao) {
      97             :   std::string nlandmarks;
      98           8 :   parse("NLANDMARKS",nlandmarks);
      99             :   bool novoronoi;
     100           8 :   parseFlag("NOVORONOI",novoronoi);
     101             : 
     102             :   bool nodissims;
     103          16 :   parseFlag("NODISSIMILARITIES",nodissims);
     104             :   std::string argn, dissims;
     105           8 :   parse("ARG",argn);
     106          16 :   parse("DISSIMILARITIES",dissims);
     107           8 :   if( argn.length()>0 ) {
     108           7 :     ActionShortcut* as = plumed.getActionSet().getShortcutActionWithLabel( argn );
     109           7 :     if( !as || as->getName()!="COLLECT_FRAMES" ) {
     110           0 :       error("found no COLLECT_FRAMES action with label " + argn );
     111             :     }
     112             :     // Get the weights
     113          14 :     readInputLine( getShortcutLabel() + "_allweights: LOGSUMEXP ARG=" + argn + "_logweights");
     114             :   }
     115           8 :   if( dissims.length()>0 ) {
     116           4 :     ActionWithValue* ds = plumed.getActionSet().selectWithLabel<ActionWithValue*>( dissims );
     117           4 :     if( (ds->copyOutput(0))->getRank()!=2 ) {
     118           0 :       error("input for dissimilarities shoudl be a matrix");
     119             :     }
     120             :     // Calculate the dissimilarities if the user didn't specify them
     121           4 :   } else if( !nodissims ) {
     122           2 :     readInputLine( getShortcutLabel() + "_" + argn + "_dataT: TRANSPOSE ARG=" + argn + "_data");
     123           1 :     dissims = getShortcutLabel() + "_dissims";
     124           2 :     readInputLine( getShortcutLabel() + "_dissims: DISSIMILARITIES SQUARED ARG=" + argn + "_data," + getShortcutLabel() + "_" + argn + "_dataT");
     125             :   }
     126             :   // This deals with a corner case whereby users have a matrix of dissimilarities but no corresponding coordinates for these frames
     127           8 :   if( argn.length()==0 && dissims.size()>0 ) {
     128           1 :     ActionWithValue* ds = plumed.getActionSet().selectWithLabel<ActionWithValue*>( dissims );
     129           1 :     if( ds->getName()!="CONSTANT" || (ds->copyOutput(0))->getRank()!=2 ) {
     130           0 :       error("set ARG as well as DISSIMILARITIES");
     131             :     }
     132             :     std::string size;
     133           1 :     Tools::convert(  (ds->copyOutput(0))->getShape()[0], size );
     134           2 :     readInputLine( getShortcutLabel() + "_allweights: ONES SIZE=" + size );
     135             :   }
     136             : 
     137           8 :   if( getName()=="LANDMARK_SELECT_STRIDE" ) {
     138          12 :     readInputLine( getShortcutLabel() + "_mask: CREATE_MASK ARG=" + getShortcutLabel() + "_allweights TYPE=stride NZEROS=" + nlandmarks );
     139           2 :   } else if( getName()=="LANDMARK_SELECT_RANDOM" ) {
     140           1 :     if( argn.length()==0 ) {
     141           0 :       error("must set COLLECT_FRAMES object for landmark selection using ARG keyword");
     142             :     }
     143             :     std::string seed;
     144           2 :     parse("SEED",seed);
     145           1 :     if( seed.length()>0 ) {
     146           2 :       seed = " SEED=" + seed;
     147             :     }
     148           2 :     readInputLine( getShortcutLabel() + "_mask: CREATE_MASK ARG=" + getShortcutLabel() + "_allweights TYPE=random NZEROS=" + nlandmarks + seed );
     149           1 :   } else if( getName()=="LANDMARK_SELECT_FPS" ) {
     150           1 :     if( dissims.length()==0 ) {
     151           0 :       error("dissimiarities must be defined to use FPS sampling");
     152             :     }
     153             :     std::string seed;
     154           2 :     parse("SEED",seed);
     155           1 :     if( seed.length()>0 ) {
     156           0 :       seed = " SEED=" + seed;
     157             :     }
     158           2 :     readInputLine( getShortcutLabel() + "_mask: FARTHEST_POINT_SAMPLING ARG=" + dissims + " NZEROS=" + nlandmarks + seed );
     159             :   }
     160             : 
     161           8 :   if( argn.length()>0 ) {
     162          14 :     readInputLine( getShortcutLabel() + "_data: SELECT_WITH_MASK ARG=" + argn + "_data ROW_MASK=" + getShortcutLabel() + "_mask");
     163             :   }
     164             : 
     165             :   unsigned nland;
     166           8 :   Tools::convert( nlandmarks, nland );
     167           8 :   if( dissims.length()>0 ) {
     168           5 :     ActionWithValue* ds = plumed.getActionSet().selectWithLabel<ActionWithValue*>( dissims );
     169           5 :     if( (ds->copyOutput(0))->getShape()[0]==nland ) {
     170           1 :       if( !novoronoi ) {
     171           0 :         warning("cannot use voronoi procedure to give weights as not all distances between points are known");
     172           0 :         novoronoi=true;
     173             :       }
     174           2 :       readInputLine( getShortcutLabel() + "_sqrdissims: COMBINE ARG=" + dissims + " PERIODIC=NO");
     175             :     } else {
     176           8 :       readInputLine( getShortcutLabel() + "_rmask: CREATE_MASK ARG=" + getShortcutLabel() + "_allweights TYPE=nomask");
     177           8 :       readInputLine( getShortcutLabel() + "_rectdissims: SELECT_WITH_MASK ARG=" + dissims + " COLUMN_MASK=" + getShortcutLabel() + "_mask ROW_MASK=" + getShortcutLabel() + "_rmask");
     178           8 :       readInputLine( getShortcutLabel() + "_sqrdissims: SELECT_WITH_MASK ARG=" + dissims + " ROW_MASK=" + getShortcutLabel() + "_mask COLUMN_MASK=" + getShortcutLabel() + "_mask");
     179             :     }
     180             :   }
     181             : 
     182           8 :   if( !novoronoi && argn.length()>0 && dissims.length()>0 ) {
     183           6 :     readInputLine( getShortcutLabel() + "_voronoi: VORONOI ARG=" + getShortcutLabel() + "_rectdissims");
     184           6 :     readInputLine( getShortcutLabel() + "_allweightsT: TRANSPOSE ARG=" + getShortcutLabel() + "_allweights");
     185           6 :     readInputLine( getShortcutLabel() + "_weightsT: MATRIX_PRODUCT ARG=" + getShortcutLabel() + "_allweightsT," + getShortcutLabel() + "_voronoi");
     186           6 :     readInputLine( getShortcutLabel() + "_weights: TRANSPOSE ARG=" + getShortcutLabel() + "_weightsT");
     187           6 :     readInputLine( getShortcutLabel() + "_logweights: CUSTOM ARG=" + getShortcutLabel() + "_weights FUNC=log(x) PERIODIC=NO");
     188           5 :   } else if( argn.length()>0 ) {
     189           4 :     if( !novoronoi ) {
     190           0 :       warning("cannot use voronoi procedure to give weights to landmark points as DISSIMILARITIES was not set");
     191             :     }
     192           8 :     readInputLine( getShortcutLabel() + "_logweights: SELECT_WITH_MASK ARG=" + argn + "_logweights MASK=" + getShortcutLabel() + "_mask");
     193             :   }
     194             :   // Create the vector of ones that is needed by Classical MDS
     195           8 :   if( argn.length()>0 ) {
     196          14 :     readInputLine( getShortcutLabel() + "_ones: SELECT_WITH_MASK ARG=" + argn + "_ones MASK=" + getShortcutLabel() + "_mask");
     197             :   }
     198           8 : }
     199             : 
     200             : }
     201             : }

Generated by: LCOV version 1.16