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 SEED=23623 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 30 : void CreateMask::registerKeywords( Keywords& keys ) {
127 30 : Action::registerKeywords( keys );
128 30 : ActionWithValue::registerKeywords( keys );
129 30 : ActionWithArguments::registerKeywords( keys );
130 30 : keys.remove("NUMERICAL_DERIVATIVES");
131 60 : keys.addInputKeyword("compulsory","ARG","vector","the label of the vector that you would like to construct a mask for");
132 30 : keys.add("compulsory","TYPE","the way the zeros are supposed to be set");
133 30 : keys.add("compulsory","NZEROS","the number of zeros that you want to put in the mask");
134 30 : keys.add("optional","SEED","the seed to use for the random number generator");
135 60 : 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");
136 30 : }
137 :
138 :
139 15 : CreateMask::CreateMask( const ActionOptions& ao ) :
140 : Action(ao),
141 : ActionWithValue(ao),
142 : ActionWithArguments(ao),
143 15 : nzeros(0) {
144 15 : if( getNumberOfArguments()!=1 ) {
145 0 : error("should only be one argument to this action");
146 : }
147 15 : if( getPntrToArgument(0)->getRank()!=1 ) {
148 0 : error("argument should be a vector");
149 : }
150 : std::string stype;
151 15 : parse("TYPE",stype);
152 15 : if( stype!="nomask" ) {
153 18 : parse("NZEROS",nzeros);
154 : }
155 :
156 15 : if( stype=="nomask" ) {
157 6 : type=nomask;
158 6 : log.printf(" setting all points in output mask to zero \n");
159 9 : } else if( stype=="stride" ) {
160 8 : if( nzeros==0 ) {
161 0 : error("NZEROS should be greater than zero");
162 : }
163 8 : type=stride;
164 8 : log.printf(" setting every %d equally spaced points in output mask to zero \n", nzeros );
165 1 : } else if( stype=="random" ) {
166 1 : unsigned seed=230623;
167 1 : parse("SEED",seed);
168 1 : r.setSeed(-seed);
169 1 : type=random;
170 1 : log.printf(" choosing %d points to set to non-zero in mask in accordance with input weights \n", nzeros );
171 : } else {
172 0 : error( stype + " is not a valid way input for TYPE");
173 : }
174 15 : std::vector<std::size_t> shape(1);
175 15 : shape[0] = getPntrToArgument(0)->getShape()[0];
176 15 : addValue( shape );
177 15 : setNotPeriodic();
178 53 : for(unsigned i=0; i<shape[0]; ++i) {
179 38 : getPntrToComponent(0)->set( i, 1.0 );
180 : }
181 : // We run calculate here to make a selection so that we can parse the input.
182 : // This initial selection is never used
183 15 : if( shape[0]>0 ) {
184 4 : calculate();
185 : }
186 30 : }
187 :
188 25 : void CreateMask::prepare() {
189 25 : Value* out=getPntrToComponent(0);
190 : Value* arg=getPntrToArgument(0);
191 25 : if( out->getShape()[0]!=arg->getShape()[0] ) {
192 11 : std::vector<std::size_t> shape(1);
193 11 : shape[0] = arg->getShape()[0];
194 11 : out->setShape( shape );
195 11 : }
196 25 : if( type!=nomask ) {
197 325 : for(unsigned i=nzeros; i<out->getShape()[0]; ++i) {
198 306 : out->set( i, 1 );
199 : }
200 : }
201 25 : }
202 :
203 25 : void CreateMask::calculate() {
204 25 : Value* out=getPntrToComponent(0);
205 : Value* arg=getPntrToArgument(0);
206 25 : unsigned ns = arg->getShape()[0];
207 1213 : for(unsigned i=0; i<ns; ++i) {
208 1188 : out->set( i, 1.0 );
209 : }
210 :
211 25 : if( type==stride ) {
212 13 : std::size_t ss = int( std::floor( ns / nzeros ) );
213 310 : for(unsigned i=0; i<nzeros; ++i) {
214 297 : out->set( i*ss, 0.0 );
215 : }
216 12 : } else if( type==random ) {
217 25 : for(unsigned i=0; i<nzeros; ++i ) {
218 : double totweights = 0;
219 140 : for(unsigned j=0; j<ns; ++j) {
220 120 : if( out->get(j)>0 ) {
221 93 : totweights += arg->get(j);
222 : }
223 : }
224 20 : double rr = r.U01()*totweights;
225 : double accum=0;
226 55 : for(unsigned j=0; j<ns; ++j) {
227 55 : if( out->get(j)>0 ) {
228 43 : accum += arg->get(j);
229 : }
230 55 : if( accum<rr ) {
231 : continue;
232 : }
233 20 : out->set( j, 0 );
234 20 : break;
235 : }
236 : }
237 7 : } else if( type==nomask ) {
238 569 : for(unsigned i=0; i<ns; ++i) {
239 562 : out->set( i, 0.0 );
240 : }
241 : } else {
242 0 : error("invalid mask creation type");
243 : }
244 25 : }
245 :
246 : }
247 : }
|