klArbitraryPrecisionSolver
kl_arbitrary_precision_root.h
Go to the documentation of this file.
1 
2 #ifndef __kl_arbitrary_precision_root__
3 #define __kl_arbitrary_precision_root__
4 
5 //#define BOOST_MATH_INSTRUMENT
6 #include <boost/math/tools/roots.hpp>
7 #include <limits>
8 
9 namespace boost{ namespace math{ namespace tools{
10 
11 template <class F, class T>
12 T newton_raphson_iterate(F f, T guess, T min, T max, int digits);
13 
14 template <class F, class T>
15 T newton_raphson_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter);
16 
17 template <class F, class T>
18 T halley_iterate(F f, T guess, T min, T max, int digits);
19 
20 template <class F, class T>
21 T halley_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter);
22 
23 template <class F, class T>
24 T schroeder_iterate(F f, T guess, T min, T max, int digits);
25 
26 template <class F, class T>
27 T schroeder_iterate(F f, T guess, T min, T max, int digits, boost::uintmax_t& max_iter);
28 
29 }}} // namespaces
30 
31 /*
32 These functions all perform iterative root finding using derivatives:
33 
34 newton_raphson_iterateperforms second order Newton-Raphson iteration,
35 halley_iterate andschroeder_iterate perform third order Halley and Schroeder iteration.
36 The functions all take the same parameters:
37 
38 Parameters of the root finding functions
39 
40 F f
41 Type F must be a callable function object that accepts one parameter and returns a boost::math::tuple:
42 
43 For the second order iterative methods (Newton Raphson) the boost::math::tuple should have two elements containing the evaluation of the function and its first derivative.
44 
45 For the third order methods (Halley and Schroeder) the boost::math::tuple should have three elements containing the evaluation of the function and its first and second derivatives.
46 
47 T guess
48 The initial starting value. A good guess is crucial to quick convergence!
49 
50 T min
51 The minimum possible value for the result, this is used as an initial lower bracket.
52 
53 T max
54 The maximum possible value for the result, this is used as an initial upper bracket.
55 
56 int digits
57 The desired number of binary digits.
58 
59 uintmax_t max_iter
60 An optional maximum number of iterations to perform.
61 
62 When using these functions you should note that:
63 
64 Default max_iter = (std::numeric_limits<boost::uintmax_t>::max)() is effectively 'iterate for ever'!.
65 They may be very sensitive to the initial guess, typically they converge very rapidly if the initial guess has
66 two or three decimal digits correct. However convergence can be no better than bisection, or in some rare cases,
67 even worse than bisection if the initial guess is a long way from the correct value and the derivatives are close to zero.
68 These functions include special cases to handle zero first (and second where appropriate) derivatives, and fall back to
69 bisection in this case. However, it is helpful if functor F is defined to return an arbitrarily small value of the correct
70 sign rather than zero.
71 If the derivative at the current best guess for the result is infinite (or very close to being infinite) then these functions
72 may terminate prematurely. A large first derivative leads to a very small next step, triggering the termination condition.
73 Derivative based iteration may not be appropriate in such cases.
74 If the function is 'Really Well Behaved' (monotonic and has only one root) the bracket bounds min and max may as well be set
75 to the widest limits like zero and numeric_limits<T>::max().
76 But if the function more complex and may have more than one root or a pole, the choice of bounds is protection against
77 jumping out to seek the 'wrong' root.
78 These functions fall back to bisection if the next computed step would take the next value out of bounds. The bounds are
79 updated after each step to ensure this leads to convergence. However, a good initial guess backed up by asymptotically-tight
80 bounds will improve performance no end - rather than relying on bisection.
81 The value of digits is crucial to good performance of these functions, if it is set too high then at best you will get one
82 extra (unnecessary) iteration, and at worst the last few steps will proceed by bisection. Remember that the returned value can
83 never be more accurate than f(x) can be evaluated, and that if f(x) suffers from cancellation errors as it tends to zero then
84 the computed steps will be effectively random. The value of digits should be set so that iteration terminates before this
85 point: remember that for second and third order methods the number of correct digits in the result is increasing quite
86 substantially with each iteration, digits should be set by experiment so that the final iteration just takes the next
87 value into the zone where f(x) becomes inaccurate.
88 To get the binary digits of accuracy, use policies::get_max_root_iterations<Policy>()).
89 If you need some diagnostic output to see what is going on, you can #define BOOST_MATH_INSTRUMENT before the
90 #include <boost/math/tools/roots.hpp>, and also ensure that display of all the possibly significant digits with
91 cout.precision(std::numeric_limits<double>::max_digits10): but be warned, this may produce copious output!
92 Finally: you may well be able to do better than these functions by hand-coding the heuristics used so that they
93 are tailored to a specific function. You may also be able to compute the ratio of derivatives used by these methods
94 more efficiently than computing the derivatives themselves. As ever, algebraic simplification can be a big win.
95 
96 Newton Raphson Method
97 Given an initial guess x0 the subsequent values are computed using:
98 x_{n+1} = x{n}- \frac{f(x)}{f'(x)}
99 
100 Out of bounds steps revert to bisection of the current bounds.
101 
102 Under ideal conditions, the number of correct digits doubles with each iteration.
103 
104 Halley's Method
105 Given an initial guess x0 the subsequent values are computed using:
106 x_{n+1} = x{n}- \frac{2f(x)*f'(x)}{2(f'(x))^2 - f(x)*f''(x)}
107 
108 Over-compensation by the second derivative (one which would proceed in the wrong direction) causes the method to revert to a Newton-Raphson step.
109 
110 Out of bounds steps revert to bisection of the current bounds.
111 
112 Under ideal conditions, the number of correct digits trebles with each iteration.
113 
114 Schroeder's Method
115 Given an initial guess x0 the subsequent values are computed using:
116 x_{n+1} = x{n}- \frac{f(x)}{f'(x)} - \frac{f''(x)(f(x))^2}{2(f'(x))^3}
117 
118 Over-compensation by the second derivative (one which would proceed in the wrong direction) causes the method to revert to a Newton-Raphson step. Likewise a Newton step is used whenever that Newton step would change the next value by more than 10%.
119 
120 Out of bounds steps revert to bisection of the current bounds.
121 
122 Under ideal conditions, the number of correct digits trebles with each iteration.
123 
124 Example
125 Let's suppose we want to find the cube root of a number: the equation we want to solve along with its derivatives are:
126 f(x) = x^3-a
127 f'(x) = 3x^2;
128 f''(x)=6x;
129 
130 
131 To begin with lets solve the problem using Newton-Raphson iterations, we'll begin by defining a function object
132 (functor) that returns the evaluation of the function to solve, along with its first derivative f'(x):
133 The code below is for Newton-Rhaphson
134 
135 template <class T>
136 struct cbrt_functor
137 {
138  cbrt_functor(T const& target) : a(target)
139  { // Constructor stores value to be 'cube-rooted'.
140  }
141  boost::math::tuple<T, T> operator()(T const& z)
142  { // z is estimate so far.
143  return boost::math::make_tuple(
144  z*z*z - a, // return both f(x)
145  3 * z*z); // and f'(x)
146  }
147 private:
148  T a; // to be 'cube-rooted'.
149 };
150 
151 template <class T>T cbrt(T z){
152  using namespace std; // for frexp, ldexp, numeric_limits.
153  using namespace boost::math::tools; int exp;
154  frexp(z, &exp); // Get exponent of z (ignore mantissa).
155  T min = ldexp(0.5, exp/3); T max = ldexp(2.0, exp/3);
156  T guess = ldexp(1.0, exp/3); // Rough guess is to divide the exponent by three.
157  int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
158  return newton_raphson_iterate(detail::cbrt_functor<T>(z), guess, min, max, digits);
159 }
160 
161 The implementation below in this header uses Halley's method.
162 
163 */
164 
165 
166 using namespace boost;
167 template <class T>
168 
169 struct cbrt_functor
170 {
171  cbrt_functor(T const& target) : a(target){}
172  std::tr1::tuple<T, T, T> operator()(T const& z)
173  {
174  T sqr = z * z;
175  return std::tr1::make_tuple(sqr * z - a, 3 * sqr, 6 * z);
176  }
177 private:
178  T a;
179 };
180 
181 
182 template <class T>
183 T kl_arbitrary_precision_root(T z, unsigned int precision)
184 {
185  using namespace std;
186  int exp;
187  frexp(z, &exp);
188  T min = ldexp(0.5, exp/3);
189  T max = ldexp(2.0, exp/3);
190  T guess = ldexp(1.0, exp/3);
191  int digits = std::numeric_limits<T>::digits / 2;
192 
193  cout<<"The typeid for this datatype is : "<<typeid(z).name();
194 
195  string zType =typeid(z).name();
196  int usingArb = zType.compare(typeid(boost::math::ntl::RR).name());
197  if (usingArb==0)
198  {
199  cout<<"We're using arb precision"<<endl;
200  digits = precision /2;
201  }
202 
203  T ans =boost::math::tools::halley_iterate(cbrt_functor<T>(z), guess, min, max, digits);
204  return ans;
205  //return tools::halley_iterate(detail::cbrt_functor<T>(z), guess, min, max, digits);
206 }
207 
208 
209 #endif //__kl_arbitrary_precision_root__
std::tr1::tuple< T, T, T > operator()(T const &z)
T kl_arbitrary_precision_root(T z, unsigned int precision)
T schroeder_iterate(F f, T guess, T min, T max, int digits)
cbrt_functor(T const &target)
T newton_raphson_iterate(F f, T guess, T min, T max, int digits)
T halley_iterate(F f, T guess, T min, T max, int digits)