Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2016-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 : #ifndef __PLUMED_tools_Brent1DRootSearch_h 23 : #define __PLUMED_tools_Brent1DRootSearch_h 24 : 25 : #include "Tools.h" 26 : 27 : #include <vector> 28 : #include <string> 29 : 30 : namespace PLMD { 31 : 32 : /// A class for doing parabolic interpolation and minimisation of 33 : /// 1D functions using Brent's method. 34 : template <class FCLASS> 35 1516 : class Brent1DRootSearch { 36 : private: 37 : /// Has the minimum been bracketed 38 : bool bracketed; 39 : /// The tolerance for the line minimiser 40 : double tol; 41 : /// Maximum number of interactions in line minimiser 42 : const unsigned ITMAX; 43 : /// A small number that protects against trying to achieve fractional 44 : /// accuracy for a minimum that happens to be exactly zero 45 : const double EPS; 46 : /// The factor by which to expand the range when bracketing 47 : const double EXPAND; 48 : /// This is the type specifier for the function to minimise 49 : typedef double(FCLASS::*eng_pointer)( const double& val ); 50 : /// Three points bracketting the minimum and the corresponding function values 51 : double ax,bx,fa,fb; 52 : /// The class containing the function we are trying to minimise 53 : FCLASS myclass_func; 54 : public: 55 1516 : explicit Brent1DRootSearch( const FCLASS& pf, const double& t=3.0E-8 ); 56 : /// Bracket the minium 57 : void bracket( const double& ax, const double& xx, eng_pointer eng ); 58 : /// Find the minimum between two brackets 59 : double search( eng_pointer eng ); 60 : }; 61 : 62 : template <class FCLASS> 63 1516 : Brent1DRootSearch<FCLASS>::Brent1DRootSearch( const FCLASS& pf, const double& t ): 64 1516 : bracketed(false), 65 1516 : tol(t), 66 1516 : ITMAX(100), 67 1516 : EPS(3.0E-8), 68 1516 : EXPAND(1.6), 69 1516 : ax(0), bx(0), 70 1516 : fa(0), fb(0), 71 1516 : myclass_func(pf) { 72 1516 : } 73 : 74 : template <class FCLASS> 75 1516 : void Brent1DRootSearch<FCLASS>::bracket( const double& a, const double& b, eng_pointer eng ) { 76 1516 : plumed_assert( a!=b ); 77 1516 : ax=a; 78 1516 : bx=b; 79 1516 : fa=(myclass_func.*eng)(a); 80 1516 : fb=(myclass_func.*eng)(b); 81 1516 : if ((fa > 0.0 && fb > 0.0) || (fa < 0.0 && fb < 0.0)) { 82 0 : plumed_merror("input points do not bracket root"); 83 : } 84 1516 : bracketed=true; 85 1516 : } 86 : 87 : template <class FCLASS> 88 1516 : double Brent1DRootSearch<FCLASS>::search( eng_pointer eng ) { 89 : plumed_dbg_assert( bracketed ); 90 : 91 1516 : double cx=bx, d, e, min1, min2, fc=fb, p, q, r, s, tol1, xm; 92 8928 : for(unsigned iter=0; iter<ITMAX; iter++) { 93 8928 : if ( (fb>0.0 && fc>0.0) || (fb<0.0 && fc<0.0) ) { 94 6702 : cx=ax; 95 6702 : fc=fa; 96 6702 : e=d=bx-ax; 97 : } 98 8928 : if( std::fabs(fc) < std::fabs(fb) ) { 99 2427 : ax=bx; 100 2427 : bx=cx; 101 : cx=ax; 102 2427 : fa=fb; 103 2427 : fb=fc; 104 : fc=fa; 105 : } 106 8928 : tol1=2*EPS*std::fabs(bx)+0.5*tol; 107 8928 : xm=0.5*(cx-bx); 108 8928 : if( std::fabs(xm) <= tol1 || fb == 0.0 ) { 109 1516 : return bx; 110 : } 111 7412 : if( std::fabs(e) >= tol1 && std::fabs(fa) > std::fabs(fb) ) { 112 7348 : s=fb/fa; 113 7348 : if( ax==cx ) { 114 5192 : p=2.0*xm*s; 115 5192 : q=1.0-s; 116 : } else { 117 2156 : q=fa/fc; 118 2156 : r=fb/fc; 119 2156 : p=s*(2.0*xm*q*(q-r)-(bx-ax)*(r-1.0)); 120 2156 : q=(q-1.0)*(r-1.0)*(s-1.0); 121 : } 122 7348 : if (p > 0.0) { 123 3730 : q = -q; 124 : } 125 7348 : p=std::fabs(p); 126 7348 : min1=3.0*xm*q-std::fabs(tol1*q); 127 7348 : min2=std::fabs(e*q); 128 14272 : if (2.0*p < (min1 < min2 ? min1 : min2)) { 129 : e=d; 130 7089 : d=p/q; 131 : } else { 132 : d=xm; 133 : e=d; 134 : } 135 : } else { 136 : d=xm; 137 : e=d; 138 : } 139 7412 : ax=bx; 140 7412 : fa=fb; 141 7412 : if( std::fabs(d) > tol1 ) { 142 5987 : bx+=d; 143 1425 : } else if(xm<0 ) { 144 728 : bx -= std::fabs(tol1); // SIGN(tol1,xm); 145 : } else { 146 697 : bx += tol1; 147 : } 148 7412 : fb = (myclass_func.*eng)(bx); 149 : } 150 : 151 0 : plumed_merror("Too many interactions in zbrent"); 152 : } 153 : 154 : } 155 : #endif