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 : }