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