1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl 5 6 This file is part of QuantLib, a free-software/open-source library 7 for financial quantitative analysts and developers - http://quantlib.org/ 8 9 QuantLib is free software: you can redistribute it and/or modify it 10 under the terms of the QuantLib license. You should have received a 11 copy of the license along with this program; if not, please email 12 <quantlib-dev@lists.sf.net>. The license is also available online at 13 <http://quantlib.org/license.shtml>. 14 15 This program is distributed in the hope that it will be useful, but WITHOUT 16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17 FOR A PARTICULAR PURPOSE. See the license for more details. 18 */ 19 20 /*! \file solver1d.hpp 21 \brief Abstract 1-D solver class 22 */ 23 24 #ifndef quantlib_solver1d_hpp 25 #define quantlib_solver1d_hpp 26 27 #include <ql/math/comparison.hpp> 28 #include <ql/utilities/null.hpp> 29 #include <ql/patterns/curiouslyrecurring.hpp> 30 #include <ql/errors.hpp> 31 #include <algorithm> 32 #include <iomanip> 33 34 namespace QuantLib { 35 36 #define MAX_FUNCTION_EVALUATIONS 100 37 38 //! Base class for 1-D solvers 39 /*! The implementation of this class uses the so-called 40 "Barton-Nackman trick", also known as "the curiously recurring 41 template pattern". Concrete solvers will be declared as: 42 \code 43 class Foo : public Solver1D<Foo> { 44 public: 45 ... 46 template <class F> 47 Real solveImpl(const F& f, Real accuracy) const { 48 ... 49 } 50 }; 51 \endcode 52 Before calling <tt>solveImpl</tt>, the base class will set its 53 protected data members so that: 54 - <tt>xMin_</tt> and <tt>xMax_</tt> form a valid bracket; 55 - <tt>fxMin_</tt> and <tt>fxMax_</tt> contain the values of 56 the function in <tt>xMin_</tt> and <tt>xMax_</tt>; 57 - <tt>root_</tt> is a valid initial guess. 58 The implementation of <tt>solveImpl</tt> can safely assume all 59 of the above. 60 61 \todo 62 - clean up the interface so that it is clear whether the 63 accuracy is specified for \f$ x \f$ or \f$ f(x) \f$. 64 - add target value (now the target value is 0.0) 65 */ 66 template <class Impl> 67 class Solver1D : public CuriouslyRecurringTemplate<Impl> { 68 public: Solver1D()69 Solver1D() 70 : maxEvaluations_(MAX_FUNCTION_EVALUATIONS), 71 lowerBoundEnforced_(false), upperBoundEnforced_(false) {} 72 //! \name Modifiers 73 //@{ 74 /*! This method returns the zero of the function \f$ f \f$, 75 determined with the given accuracy \f$ \epsilon \f$; 76 depending on the particular solver, this might mean that 77 the returned \f$ x \f$ is such that \f$ |f(x)| < \epsilon 78 \f$, or that \f$ |x-\xi| < \epsilon \f$ where \f$ \xi \f$ 79 is the real zero. 80 81 This method contains a bracketing routine to which an 82 initial guess must be supplied as well as a step used to 83 scan the range of the possible bracketing values. 84 */ 85 template <class F> solve(const F & f,Real accuracy,Real guess,Real step) const86 Real solve(const F& f, 87 Real accuracy, 88 Real guess, 89 Real step) const { 90 91 QL_REQUIRE(accuracy>0.0, 92 "accuracy (" << accuracy << ") must be positive"); 93 // check whether we really want to use epsilon 94 accuracy = std::max(accuracy, QL_EPSILON); 95 96 const Real growthFactor = 1.6; 97 Integer flipflop = -1; 98 99 root_ = guess; 100 fxMax_ = f(root_); 101 102 // monotonically crescent bias, as in optionValue(volatility) 103 if (close(fxMax_,0.0)) 104 return root_; 105 else if (fxMax_ > 0.0) { 106 xMin_ = enforceBounds_(root_ - step); 107 fxMin_ = f(xMin_); 108 xMax_ = root_; 109 } else { 110 xMin_ = root_; 111 fxMin_ = fxMax_; 112 xMax_ = enforceBounds_(root_+step); 113 fxMax_ = f(xMax_); 114 } 115 116 evaluationNumber_ = 2; 117 while (evaluationNumber_ <= maxEvaluations_) { 118 if (fxMin_*fxMax_ <= 0.0) { 119 if (close(fxMin_, 0.0)) 120 return xMin_; 121 if (close(fxMax_, 0.0)) 122 return xMax_; 123 root_ = (xMax_+xMin_)/2.0; 124 return this->impl().solveImpl(f, accuracy); 125 } 126 if (std::fabs(fxMin_) < std::fabs(fxMax_)) { 127 xMin_ = enforceBounds_(xMin_+growthFactor*(xMin_ - xMax_)); 128 fxMin_= f(xMin_); 129 } else if (std::fabs(fxMin_) > std::fabs(fxMax_)) { 130 xMax_ = enforceBounds_(xMax_+growthFactor*(xMax_ - xMin_)); 131 fxMax_= f(xMax_); 132 } else if (flipflop == -1) { 133 xMin_ = enforceBounds_(xMin_+growthFactor*(xMin_ - xMax_)); 134 fxMin_= f(xMin_); 135 evaluationNumber_++; 136 flipflop = 1; 137 } else if (flipflop == 1) { 138 xMax_ = enforceBounds_(xMax_+growthFactor*(xMax_ - xMin_)); 139 fxMax_= f(xMax_); 140 flipflop = -1; 141 } 142 evaluationNumber_++; 143 } 144 145 QL_FAIL("unable to bracket root in " << maxEvaluations_ 146 << " function evaluations (last bracket attempt: " 147 << "f[" << xMin_ << "," << xMax_ << "] " 148 << "-> [" << fxMin_ << "," << fxMax_ << "])"); 149 } 150 /*! This method returns the zero of the function \f$ f \f$, 151 determined with the given accuracy \f$ \epsilon \f$; 152 depending on the particular solver, this might mean that 153 the returned \f$ x \f$ is such that \f$ |f(x)| < \epsilon 154 \f$, or that \f$ |x-\xi| < \epsilon \f$ where \f$ \xi \f$ 155 is the real zero. 156 157 An initial guess must be supplied, as well as two values 158 \f$ x_\mathrm{min} \f$ and \f$ x_\mathrm{max} \f$ which 159 must bracket the zero (i.e., either \f$ f(x_\mathrm{min}) 160 \leq 0 \leq f(x_\mathrm{max}) \f$, or \f$ 161 f(x_\mathrm{max}) \leq 0 \leq f(x_\mathrm{min}) \f$ must 162 be true). 163 */ 164 template <class F> solve(const F & f,Real accuracy,Real guess,Real xMin,Real xMax) const165 Real solve(const F& f, 166 Real accuracy, 167 Real guess, 168 Real xMin, 169 Real xMax) const { 170 171 QL_REQUIRE(accuracy>0.0, 172 "accuracy (" << accuracy << ") must be positive"); 173 // check whether we really want to use epsilon 174 accuracy = std::max(accuracy, QL_EPSILON); 175 176 xMin_ = xMin; 177 xMax_ = xMax; 178 179 QL_REQUIRE(xMin_ < xMax_, 180 "invalid range: xMin_ (" << xMin_ 181 << ") >= xMax_ (" << xMax_ << ")"); 182 QL_REQUIRE(!lowerBoundEnforced_ || xMin_ >= lowerBound_, 183 "xMin_ (" << xMin_ 184 << ") < enforced low bound (" << lowerBound_ << ")"); 185 QL_REQUIRE(!upperBoundEnforced_ || xMax_ <= upperBound_, 186 "xMax_ (" << xMax_ 187 << ") > enforced hi bound (" << upperBound_ << ")"); 188 189 fxMin_ = f(xMin_); 190 if (close(fxMin_, 0.0)) 191 return xMin_; 192 193 fxMax_ = f(xMax_); 194 if (close(fxMax_, 0.0)) 195 return xMax_; 196 197 evaluationNumber_ = 2; 198 199 QL_REQUIRE(fxMin_*fxMax_ < 0.0, 200 "root not bracketed: f[" 201 << xMin_ << "," << xMax_ << "] -> [" 202 << std::scientific 203 << fxMin_ << "," << fxMax_ << "]"); 204 205 QL_REQUIRE(guess > xMin_, 206 "guess (" << guess << ") < xMin_ (" << xMin_ << ")"); 207 QL_REQUIRE(guess < xMax_, 208 "guess (" << guess << ") > xMax_ (" << xMax_ << ")"); 209 210 root_ = guess; 211 212 return this->impl().solveImpl(f, accuracy); 213 } 214 215 /*! This method sets the maximum number of function 216 evaluations for the bracketing routine. An error is thrown 217 if a bracket is not found after this number of 218 evaluations. 219 */ 220 void setMaxEvaluations(Size evaluations); 221 //! sets the lower bound for the function domain 222 void setLowerBound(Real lowerBound); 223 //! sets the upper bound for the function domain 224 void setUpperBound(Real upperBound); 225 //@} 226 protected: 227 mutable Real root_, xMin_, xMax_, fxMin_, fxMax_; 228 Size maxEvaluations_; 229 mutable Size evaluationNumber_; 230 private: 231 Real enforceBounds_(Real x) const; 232 Real lowerBound_, upperBound_; 233 bool lowerBoundEnforced_, upperBoundEnforced_; 234 }; 235 236 237 // inline definitions 238 239 template <class T> setMaxEvaluations(Size evaluations)240 inline void Solver1D<T>::setMaxEvaluations(Size evaluations) { 241 maxEvaluations_ = evaluations; 242 } 243 244 template <class T> setLowerBound(Real lowerBound)245 inline void Solver1D<T>::setLowerBound(Real lowerBound) { 246 lowerBound_ = lowerBound; 247 lowerBoundEnforced_ = true; 248 } 249 250 template <class T> setUpperBound(Real upperBound)251 inline void Solver1D<T>::setUpperBound(Real upperBound) { 252 upperBound_ = upperBound; 253 upperBoundEnforced_ = true; 254 } 255 256 template <class T> enforceBounds_(Real x) const257 inline Real Solver1D<T>::enforceBounds_(Real x) const { 258 if (lowerBoundEnforced_ && x < lowerBound_) 259 return lowerBound_; 260 if (upperBoundEnforced_ && x > upperBound_) 261 return upperBound_; 262 return x; 263 } 264 265 } 266 267 #endif 268