Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2016-2020 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/ActionWithValue.h"
23 : #include "core/ActionWithArguments.h"
24 : #include "core/ActionRegister.h"
25 : #include "tools/Random.h"
26 :
27 : namespace PLMD {
28 : namespace generic {
29 :
30 : //+PLUMEDOC DIMRED CREATE_MASK
31 : /*
32 : Create a mask vector to use for landmark selection
33 :
34 : This action should be used in conjuction with the [SELECT_WITH_MASK](SELECT_WITH_MASK.md) action. As is explained in the documentation
35 : for [SELECT_WITH_MASK](SELECT_WITH_MASK.md), [SELECT_WITH_MASK](SELECT_WITH_MASK.md)
36 : allows you to output a scalar vector or matrix that contains a subset of the elements in the input vector or matrix. The following example
37 : shows how this action works in practice.
38 :
39 : ```plumed
40 : d: DISTANCE ATOMS1=1,2 ATOMS2=3,4 ATOMS3=5,6
41 : m: CONSTANT VALUES=1,0,1
42 : v: SELECT_WITH_MASK ARG=d MASK=m
43 : ```
44 :
45 : In the input above, The value, `m`, that is passed to the keyword MASK here is a vector with the same length as `d`.
46 : Elements of `d` that whose corresponding elements in `m` are zero are copied to the output value `v`.
47 : When elements of `m` are non-zero the corresponding elements in `d` are not transferred to the output
48 : value - they are masked. Consequently, although the input, `d`, to the select SELECT_WITH_MASK action is a vector
49 : with 3 elements the output, `v`, is a scalar.
50 :
51 : In the vast majority of cases you can use the [CONSTANT](CONSTANT.md) action to create the values that are passed to the
52 : SELECT_WITH_MASK action using the `MASK` keyword as the size of the input vector is known when you write the input file.
53 : The one exception to this is if you are using a [COLLECT](COLLECT.md) action like the one shown below:
54 :
55 : ```plumed
56 : d: DISTANCE ATOMS=1,2
57 : c: COLLECT ARG=d STRIDE=1
58 : ```
59 :
60 : The vector `c` here will have as many frames as there are frames in the trajectory that is being generated or analysed.
61 : Normally, with an input like this a user will want to analyse all the data in the whole trajectory at once without
62 : specifying the trajectories length to PLUMED. action should was desgined to create inputs to the MASK keyword for
63 : [SELECT_WITH_MASK](SELECT_WITH_MASK.md) action in this particular case. Basically it creates a vector of ones and
64 : zeros that has the same length as the input vector.
65 :
66 : There are three modes for creating these vectors and ones and zeros. This first one creates a mask vector in which
67 : every element is equal to zero. The output from the SELECT_WITH_MASK command, `v`, is thus identical to the output from
68 : the collect command, `d`.
69 :
70 : ```plumed
71 : d: DISTANCE ATOMS=1,2
72 : c: COLLECT ARG=d STRIDE=1
73 : m: CREATE_MASK TYPE=nomask ARG=c
74 : v: SELECT_WITH_MASK ARG=c MASK=m
75 : ```
76 :
77 : The second mode, which is illustrated below, sets N randomly-chosen elements of the mask equal to zero.
78 :
79 : ```plumed
80 : d: DISTANCE ATOMS=1,2
81 : c: COLLECT ARG=d STRIDE=1
82 : m: CREATE_MASK TYPE=random NZEROS=20 ARG=c
83 : v: SELECT_WITH_MASK ARG=c MASK=m
84 : ```
85 :
86 : The vector `v` that is output by the SELECT_WITH_MASK command contains 20 randomly selected elements from the input vector `c`.
87 :
88 : The third and final mode, which is illustrated below, selects a set of N evenly spaced points from the input vector.
89 :
90 : ```plumed
91 : d: DISTANCE ATOMS=1,2
92 : c: COLLECT ARG=d STRIDE=1
93 : m: CREATE_MASK TYPE=stride NZEROS=20 ARG=c
94 : v: SELECT_WITH_MASK ARG=c MASK=m
95 : ```
96 :
97 : The vector `v` that is output by the SELECT_WITH_MASK command contains 20 elements from the input vector `c`. If `c` has
98 : 200 elements then `v` will contain every 20th element of the input vector `c`.
99 :
100 : For more examples that demonstrate how this action is used look at the actions in the [landmarks](module_landmarks.md) module.
101 :
102 :
103 : */
104 : //+ENDPLUMEDOC
105 :
106 : class CreateMask :
107 : public ActionWithValue,
108 : public ActionWithArguments {
109 : private:
110 : Random r;
111 : unsigned nzeros;
112 : enum {nomask,stride,random} type;
113 : public:
114 : static void registerKeywords( Keywords& keys );
115 : CreateMask( const ActionOptions& );
116 0 : unsigned getNumberOfDerivatives() override {
117 0 : return 0;
118 : }
119 : void prepare() override ;
120 : void calculate() override ;
121 6 : void apply() override {}
122 : };
123 :
124 : PLUMED_REGISTER_ACTION(CreateMask,"CREATE_MASK")
125 :
126 28 : void CreateMask::registerKeywords( Keywords& keys ) {
127 28 : Action::registerKeywords( keys );
128 28 : ActionWithValue::registerKeywords( keys );
129 28 : ActionWithArguments::registerKeywords( keys );
130 56 : keys.addInputKeyword("compulsory","ARG","vector","the label of the vector that you would like to construct a mask for");
131 28 : keys.add("compulsory","TYPE","the way the zeros are supposed to be set");
132 28 : keys.add("compulsory","NZEROS","the number of zeros that you want to put in the mask");
133 28 : keys.add("optional","SEED","the seed to use for the random number generator");
134 56 : keys.setValueDescription("vector","a vector of zeros and ones that is used that can be used to mask some of the elements in a time series");
135 28 : }
136 :
137 :
138 15 : CreateMask::CreateMask( const ActionOptions& ao ) :
139 : Action(ao),
140 : ActionWithValue(ao),
141 : ActionWithArguments(ao),
142 15 : nzeros(0) {
143 15 : if( getNumberOfArguments()!=1 ) {
144 0 : error("should only be one argument to this action");
145 : }
146 15 : if( getPntrToArgument(0)->getRank()!=1 ) {
147 0 : error("argument should be a vector");
148 : }
149 : std::string stype;
150 30 : parse("TYPE",stype);
151 15 : if( stype!="nomask" ) {
152 18 : parse("NZEROS",nzeros);
153 : }
154 :
155 15 : if( stype=="nomask" ) {
156 6 : type=nomask;
157 6 : log.printf(" setting all points in output mask to zero \n");
158 9 : } else if( stype=="stride" ) {
159 8 : type=stride;
160 8 : log.printf(" setting every %d equally spaced points in output mask to zero \n", nzeros );
161 1 : } else if( stype=="random" ) {
162 1 : unsigned seed=230623;
163 1 : parse("SEED",seed);
164 1 : r.setSeed(-seed);
165 1 : getPntrToArgument(0)->buildDataStore();
166 1 : type=random;
167 1 : log.printf(" choosing %d points to set to non-zero in mask in accordance with input weights \n", nzeros );
168 : } else {
169 0 : error( stype + " is not a valid way input for TYPE");
170 : }
171 15 : std::vector<unsigned> shape(1);
172 15 : shape[0] = getPntrToArgument(0)->getShape()[0];
173 15 : addValue( shape );
174 15 : setNotPeriodic();
175 15 : getPntrToComponent(0)->buildDataStore();
176 53 : for(unsigned i=0; i<shape[0]; ++i) {
177 38 : getPntrToComponent(0)->set( i, 1.0 );
178 : }
179 : // We run calculate here to make a selection so that we can parse the input.
180 : // This initial selection is never used
181 15 : if( shape[0]>0 ) {
182 4 : calculate();
183 : }
184 15 : }
185 :
186 25 : void CreateMask::prepare() {
187 25 : Value* out=getPntrToComponent(0);
188 : Value* arg=getPntrToArgument(0);
189 25 : if( out->getShape()[0]!=arg->getShape()[0] ) {
190 11 : std::vector<unsigned> shape(1);
191 11 : shape[0] = arg->getShape()[0];
192 11 : out->setShape( shape );
193 : }
194 25 : if( type!=nomask ) {
195 325 : for(unsigned i=nzeros; i<out->getShape()[0]; ++i) {
196 306 : out->set( i, 1 );
197 : }
198 : }
199 25 : }
200 :
201 25 : void CreateMask::calculate() {
202 25 : Value* out=getPntrToComponent(0);
203 : Value* arg=getPntrToArgument(0);
204 25 : unsigned ns = arg->getShape()[0];
205 1213 : for(unsigned i=0; i<ns; ++i) {
206 1188 : out->set( i, 1.0 );
207 : }
208 :
209 25 : if( type==stride ) {
210 13 : std::size_t ss = int( std::floor( ns / nzeros ) );
211 310 : for(unsigned i=0; i<nzeros; ++i) {
212 297 : out->set( i*ss, 0.0 );
213 : }
214 12 : } else if( type==random ) {
215 25 : for(unsigned i=0; i<nzeros; ++i ) {
216 : double totweights = 0;
217 140 : for(unsigned j=0; j<ns; ++j) {
218 120 : if( out->get(j)>0 ) {
219 93 : totweights += arg->get(j);
220 : }
221 : }
222 20 : double rr = r.U01()*totweights;
223 : double accum=0;
224 55 : for(unsigned j=0; j<ns; ++j) {
225 55 : if( out->get(j)>0 ) {
226 43 : accum += arg->get(j);
227 : }
228 55 : if( accum<rr ) {
229 : continue;
230 : }
231 20 : out->set( j, 0 );
232 20 : break;
233 : }
234 : }
235 7 : } else if( type==nomask ) {
236 569 : for(unsigned i=0; i<ns; ++i) {
237 562 : out->set( i, 0.0 );
238 : }
239 : } else {
240 0 : error("invalid mask creation type");
241 : }
242 25 : }
243 :
244 : }
245 : }
|