Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2014-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/ActionRegister.h" 23 : #include "core/ActionShortcut.h" 24 : #include "core/ActionWithArguments.h" 25 : #include "core/PlumedMain.h" 26 : #include "FunctionSetup.h" 27 : #include "FunctionOfScalar.h" 28 : #include "FunctionWithSingleArgument.h" 29 : 30 : //+PLUMEDOC FUNCTION PRODUCT 31 : /* 32 : Calculate the product of the input quantities 33 : 34 : This shortcut can be used to calculate the product of a collection of scalar inputs as illustrated by the following 35 : simple example. 36 : 37 : ```plumed 38 : d1: DISTANCE ATOMS=1,2 39 : d2: DISTANCE ATOMS=3,4 40 : p: PRODUCT ARG=d1,d2 41 : ``` 42 : 43 : You can also calculate the product of all the elements of a vector: 44 : 45 : ```plumed 46 : d: DISTANCE ATOMS1=1,2 ATOMS2=3,4 ATOMS3=5,6 47 : p: PRODUCT ARG=d 48 : ``` 49 : 50 : The product is computed directly, so the input values can be negative or zero. The derivative with respect to 51 : each input is the product of all the other inputs. 52 : 53 : This action is used in the [DETERMINANT](DETERMINANT.md) shortcut. In that 54 : action the determinant of the input square matrix is found by finding the eigenvalues of the 55 : input matrix using [DIAGONALIZE](DIAGONALIZE.md). The determinant is then found by evaluating 56 : the product of these eigenvalues. 57 : 58 : */ 59 : //+ENDPLUMEDOC 60 : 61 : namespace PLMD { 62 : namespace function { 63 : 64 : class ProductFunction { 65 : public: 66 : static void registerKeywords( Keywords& keys ); 67 : static void read( ProductFunction& func, ActionWithArguments* action, FunctionOptions& options ); 68 : static void calc( const ProductFunction& func, bool noderiv, View<const double> args, FunctionOutput& funcout ); 69 : }; 70 : 71 : typedef FunctionOfScalar<ProductFunction> ScalarProduct; 72 : PLUMED_REGISTER_ACTION(ScalarProduct,"PRODUCT_SCALAR") 73 : typedef FunctionWithSingleArgument<ProductFunction> OneargProduct; 74 : PLUMED_REGISTER_ACTION(OneargProduct,"PRODUCT_ONEARG") 75 : 76 32 : void ProductFunction::registerKeywords( Keywords& keys ) { 77 64 : keys.setValueDescription("scalar","the product of the input values"); 78 32 : } 79 : 80 12 : void ProductFunction::read( ProductFunction& func, ActionWithArguments* action, FunctionOptions& options ) { 81 29 : for(unsigned i=0; i<action->getNumberOfArguments(); ++i) { 82 17 : if( action->getPntrToArgument(i)->getRank()>1 ) { 83 0 : action->error("the product of the elements of a matrix is not implemented"); 84 : } 85 : } 86 12 : } 87 : 88 24 : void ProductFunction::calc( const ProductFunction& func, bool noderiv, const View<const double> args, FunctionOutput& funcout ) { 89 : const std::size_t n=args.size(); 90 : double prod=1.0; 91 90 : for(std::size_t i=0; i<n; ++i) { 92 66 : prod *= args[i]; 93 : } 94 24 : funcout.values[0]=prod; 95 24 : if( noderiv ) { 96 : return; 97 : } 98 : // d prod / d x_i is the product of all the other inputs: it is computed 99 : // with a forward and a backward pass, without dividing by x_i, so zeros are fine 100 : double left=1.0; 101 65 : for(std::size_t i=0; i<n; ++i) { 102 50 : funcout.derivs[0][i]=left; 103 50 : left *= args[i]; 104 : } 105 : double right=1.0; 106 65 : for(std::size_t i=n; i>0; --i) { 107 50 : funcout.derivs[0][i-1] *= right; 108 50 : right *= args[i-1]; 109 : } 110 : } 111 : 112 : class Product : public ActionShortcut { 113 : public: 114 : static void registerKeywords( Keywords& keys ); 115 : explicit Product(const ActionOptions&ao); 116 : }; 117 : 118 : PLUMED_REGISTER_ACTION(Product,"PRODUCT") 119 : 120 26 : void Product::registerKeywords( Keywords& keys ) { 121 26 : ActionShortcut::registerKeywords(keys); 122 52 : keys.addInputKeyword("compulsory","ARG","scalar/vector","the scalars (or the vector) whose elements you would like to multiply together"); 123 52 : keys.setValueDescription("scalar","the product of all the input values"); 124 26 : keys.addActionNameSuffix("_SCALAR"); 125 26 : keys.addActionNameSuffix("_ONEARG"); 126 26 : keys.needsAction("CONCATENATE"); 127 26 : } 128 : 129 12 : Product::Product( const ActionOptions& ao): 130 : Action(ao), 131 12 : ActionShortcut(ao) { 132 12 : std::vector<std::string> args; 133 24 : parseVector("ARG",args); 134 12 : if( args.empty() ) { 135 0 : error("no arguments specified"); 136 : } 137 12 : std::vector<Value*> vals; 138 12 : ActionWithArguments::interpretArgumentList( args, plumed.getActionSet(), this, vals ); 139 12 : if( vals.empty() ) { 140 0 : error("found no input arguments to product"); 141 : } 142 12 : std::string allargs=args[0]; 143 14 : for(unsigned i=1; i<args.size(); ++i) { 144 4 : allargs += "," + args[i]; 145 : } 146 : bool allscalars=true; 147 29 : for(const auto & v : vals) { 148 17 : if( v->getRank()>1 || (v->getRank()>0 && v->hasDerivatives()) ) { 149 0 : error("PRODUCT only accepts scalars and vectors in input"); 150 : } 151 17 : if( v->getRank()!=0 ) { 152 : allscalars=false; 153 : } 154 : } 155 12 : if( allscalars ) { 156 8 : readInputLine( getShortcutLabel() + ": PRODUCT_SCALAR ARG=" + allargs ); 157 8 : } else if( vals.size()==1 ) { 158 16 : readInputLine( getShortcutLabel() + ": PRODUCT_ONEARG ARG=" + allargs ); 159 : } else { 160 0 : readInputLine( getShortcutLabel() + "_vec: CONCATENATE ARG=" + allargs ); 161 0 : readInputLine( getShortcutLabel() + ": PRODUCT_ONEARG ARG=" + getShortcutLabel() + "_vec" ); 162 : } 163 12 : } 164 : 165 : } 166 : }