• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // root_finding_fith.cpp
2 
3 // Copyright Paul A. Bristow 2014.
4 
5 // Use, modification and distribution are subject to the
6 // Boost Software License, Version 1.0.
7 // (See accompanying file LICENSE_1_0.txt
8 // or copy at http://www.boost.org/LICENSE_1_0.txt)
9 
10 // Example of finding fifth root using Newton-Raphson, Halley, Schroder, TOMS748 .
11 
12 // Note that this file contains Quickbook mark-up as well as code
13 // and comments, don't change any of the special comment mark-ups!
14 
15 // To get (copious!) diagnostic output, add make this define here or elsewhere.
16 //#define BOOST_MATH_INSTRUMENT
17 
18 
19 //[root_fifth_headers
20 /*
21 This example demonstrates how to use the Boost.Math tools for root finding,
22 taking the fifth root function (fifth_root) as an example.
23 It shows how use of derivatives can improve the speed.
24 
25 First some includes that will be needed.
26 Using statements are provided to list what functions are being used in this example:
27 you can of course qualify the names in other ways.
28 */
29 
30 #include <boost/math/tools/roots.hpp>
31 using boost::math::policies::policy;
32 using boost::math::tools::newton_raphson_iterate;
33 using boost::math::tools::halley_iterate;
34 using boost::math::tools::eps_tolerance; // Binary functor for specified number of bits.
35 using boost::math::tools::bracket_and_solve_root;
36 using boost::math::tools::toms748_solve;
37 
38 #include <boost/math/special_functions/next.hpp>
39 
40 #include <tuple>
41 #include <utility> // pair, make_pair
42 
43 //] [/root_finding_headers]
44 
45 #include <iostream>
46 using std::cout; using std::endl;
47 #include <iomanip>
48 using std::setw; using std::setprecision;
49 #include <limits>
50 using std::numeric_limits;
51 
52 /*
53 //[root_finding_fifth_1
54 Let's suppose we want to find the fifth root of a number.
55 
56 The equation we want to solve is:
57 
58 __spaces ['f](x) = x[fifth]
59 
60 We will first solve this without using any information
61 about the slope or curvature of the fifth function.
62 
63 If your differentiation is a little rusty
64 (or you are faced with an equation whose complexity is daunting,
65 then you can get help, for example from the invaluable
66 
67 http://www.wolframalpha.com/ site
68 
69 entering the command
70 
71   differentiate x^5
72 
73 or the Wolfram Language command
74 
75   D[x^5, x]
76 
77 gives the output
78 
79   d/dx(x^5) = 5 x^4
80 
81 and to get the second differential, enter
82 
83   second differentiate x^5
84 
85 or the Wolfram Language
86 
87   D[x^5, {x, 2}]
88 
89 to get the output
90 
91   d^2/dx^2(x^5) = 20 x^3
92 
93 or
94 
95   20 x^3
96 
97 To get a reference value we can enter
98 
99   fifth root 3126
100 
101 or
102 
103   N[3126^(1/5), 50]
104 
105 to get a result with a precision of  50 decimal digits
106 
107   5.0003199590478625588206333405631053401128722314376
108 
109 (We could also get a reference value using Boost.Multiprecision).
110 
111 We then show how adding what we can know, for this function, about the slope,
112 the 1st derivation /f'(x)/, will speed homing in on the solution,
113 and then finally how adding the curvature /f''(x)/ as well will improve even more.
114 
115 The 1st and 2nd derivatives of x[fifth] are:
116 
117 __spaces ['f]\'(x) = 2x[sup2]
118 
119 __spaces ['f]\'\'(x) = 6x
120 
121 */
122 
123 //] [/root_finding_fifth_1]
124 
125 //[root_finding_fifth_functor_noderiv
126 
127 template <class T>
128 struct fifth_functor_noderiv
129 { // fifth root of x using only function - no derivatives.
fifth_functor_noderivfifth_functor_noderiv130   fifth_functor_noderiv(T const& to_find_root_of) : value(to_find_root_of)
131   { // Constructor stores value to find root of.
132     // For example: calling fifth_functor<T>(x) to get fifth root of x.
133   }
operator ()fifth_functor_noderiv134   T operator()(T const& x)
135   { //! \returns f(x) - value.
136     T fx = x*x*x*x*x - value; // Difference (estimate x^5 - value).
137     return fx;
138   }
139 private:
140   T value; // to be 'fifth_rooted'.
141 };
142 
143 //] [/root_finding_fifth_functor_noderiv]
144 
145 //cout  << ", std::numeric_limits<" << typeid(T).name()  << ">::digits = " << digits
146 //   << ", accuracy " << get_digits << " bits."<< endl;
147 
148 
149 /*`Implementing the fifth root function itself is fairly trivial now:
150 the hardest part is finding a good approximation to begin with.
151 In this case we'll just divide the exponent by five.
152 (There are better but more complex guess algorithms used in 'real-life'.)
153 
154 fifth root function is 'Really Well Behaved' in that it is monotonic
155 and has only one root
156 (we leave negative values 'as an exercise for the student').
157 */
158 
159 //[root_finding_fifth_noderiv
160 
161 template <class T>
fifth_noderiv(T x)162 T fifth_noderiv(T x)
163 { //! \returns fifth root of x using bracket_and_solve (no derivatives).
164   using namespace std;  // Help ADL of std functions.
165   using namespace boost::math::tools; // For bracket_and_solve_root.
166 
167   int exponent;
168   frexp(x, &exponent); // Get exponent of z (ignore mantissa).
169   T guess = ldexp(1., exponent / 5); // Rough guess is to divide the exponent by five.
170   T factor = 2; // To multiply and divide guess to bracket.
171   // digits used to control how accurate to try to make the result.
172   // int digits =  3 * std::numeric_limits<T>::digits / 4; // 3/4 maximum possible binary digits accuracy for type T.
173   int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
174 
175   //boost::uintmax_t maxit = (std::numeric_limits<boost::uintmax_t>::max)();
176   // (std::numeric_limits<boost::uintmax_t>::max)() = 18446744073709551615
177   // which is more than anyone might wish to wait for!!!
178   // so better to choose some reasonable estimate of how many iterations may be needed.
179 
180   const boost::uintmax_t maxit = 50; // Chosen max iterations,
181   // but updated on exit with actual iteration count.
182 
183   // We could also have used a maximum iterations provided by any policy:
184   // boost::uintmax_t max_it = policies::get_max_root_iterations<Policy>();
185 
186   boost::uintmax_t it = maxit; // Initially our chosen max iterations,
187 
188   bool is_rising = true; // So if result if guess^5 is too low, try increasing guess.
189   eps_tolerance<double> tol(digits);
190   std::pair<T, T> r =
191     bracket_and_solve_root(fifth_functor_noderiv<T>(x), guess, factor, is_rising, tol, it);
192   // because the iteration count is updating,
193   // you can't call with a literal maximum iterations value thus:
194   //bracket_and_solve_root(fifth_functor_noderiv<T>(x), guess, factor, is_rising, tol, 20);
195 
196   // Can show how many iterations (this information is lost outside fifth_noderiv).
197   cout << "Iterations " << it << endl;
198   if (it >= maxit)
199   { // Failed to converge (or is jumping between bracket values).
200     cout << "Unable to locate solution in chosen iterations:"
201       " Current best guess is between " << r.first << " and " << r.second << endl;
202   }
203   T distance = float_distance(r.first, r.second);
204   if (distance > 0)
205   { //
206     std::cout << distance << " bits separate the bracketing values." << std::endl;
207     for (int i = 0; i < distance; i++)
208     { // Show all the values within the bracketing values.
209       std::cout << float_advance(r.first, i) << std::endl;
210     }
211   }
212   else
213   { // distance == 0 and  r.second == r.first
214     std::cout << "Converged to a single value " << r.first << std::endl;
215   }
216 
217   return r.first + (r.second - r.first) / 2;  // return midway between bracketed interval.
218 } // T fifth_noderiv(T x)
219 
220 //] [/root_finding_fifth_noderiv]
221 
222 
223 
224 // maxit = 10
225 // Unable to locate solution in chosen iterations: Current best guess is between 3.0365889718756613 and 3.0365889718756627
226 
227 
228 /*`
229 We now solve the same problem, but using more information about the function,
230 to show how this can speed up finding the best estimate of the root.
231 
232 For this function, the 1st differential (the slope of the tangent to a curve at any point) is known.
233 
234 [@http://en.wikipedia.org/wiki/Derivative#Derivatives_of_elementary_functions Derivatives]
235 gives some reminders.
236 
237 Using the rule that the derivative of x^n for positive n (actually all nonzero n) is nx^n-1,
238 allows use to get the 1st differential as 3x^2.
239 
240 To see how this extra information is used to find the root, view this demo:
241 [@http://en.wikipedia.org/wiki/Newton%27s_methodNewton Newton-Raphson iterations].
242 
243 We need to define a different functor that returns
244 both the evaluation of the function to solve, along with its first derivative:
245 
246 To \'return\' two values, we use a pair of floating-point values:
247 */
248 
249 //[root_finding_fifth_functor_1stderiv
250 
251 template <class T>
252 struct fifth_functor_1stderiv
253 { // Functor returning function and 1st derivative.
254 
fifth_functor_1stderivfifth_functor_1stderiv255   fifth_functor_1stderiv(T const& target) : value(target)
256   { // Constructor stores the value to be 'fifth_rooted'.
257   }
258 
operator ()fifth_functor_1stderiv259   std::pair<T, T> operator()(T const& z) // z is best estimate so far.
260   { // Return both f(x) and first derivative f'(x).
261     T fx = z*z*z*z*z - value; // Difference estimate fx = x^5 - value.
262     T d1x = 5 * z*z*z*z; // 1st derivative d1x = 5x^4.
263     return std::make_pair(fx, d1x); // 'return' both fx and d1x.
264   }
265 private:
266   T value; // to be 'fifth_rooted'.
267 }; // fifth_functor_1stderiv
268 
269 //] [/root_finding_fifth_functor_1stderiv]
270 
271 
272 /*`Our fifth root function using fifth_functor_1stderiv is now:*/
273 
274 //[root_finding_fifth_1deriv
275 
276 template <class T>
fifth_1deriv(T x)277 T fifth_1deriv(T x)
278 { //! \return fifth root of x using 1st derivative and Newton_Raphson.
279   using namespace std; // For frexp, ldexp, numeric_limits.
280   using namespace boost::math::tools; // For newton_raphson_iterate.
281 
282   int exponent;
283   frexp(x, &exponent); // Get exponent of x (ignore mantissa).
284   T guess = ldexp(1., exponent / 5); // Rough guess is to divide the exponent by three.
285   // Set an initial bracket interval.
286   T min = ldexp(0.5, exponent / 5); // Minimum possible value is half our guess.
287   T max = ldexp(2., exponent / 5);// Maximum possible value is twice our guess.
288 
289   // digits used to control how accurate to try to make the result.
290   int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
291 
292   const boost::uintmax_t maxit = 20; // Optionally limit the number of iterations.
293   boost::uintmax_t it = maxit; // limit the number of iterations.
294   //cout << "Max Iterations " << maxit << endl; //
295   T result = newton_raphson_iterate(fifth_functor_1stderiv<T>(x), guess, min, max, digits, it);
296   // Can check and show how many iterations (updated by newton_raphson_iterate).
297   cout << it << " iterations (from max of " << maxit << ")" << endl;
298   return result;
299 } // fifth_1deriv
300 
301 //] [/root_finding_fifth_1deriv]
302 
303 //  int get_digits = (digits * 2) /3; // Two thirds of maximum possible accuracy.
304 
305 //boost::uintmax_t maxit = (std::numeric_limits<boost::uintmax_t>::max)();
306 // the default (std::numeric_limits<boost::uintmax_t>::max)() = 18446744073709551615
307 // which is more than we might wish to wait for!!!  so we can reduce it
308 
309 /*`
310 Finally need to define yet another functor that returns
311 both the evaluation of the function to solve,
312 along with its first and second derivatives:
313 
314 f''(x) = 3 * 3x
315 
316 To \'return\' three values, we use a tuple of three floating-point values:
317 */
318 
319 //[root_finding_fifth_functor_2deriv
320 
321 template <class T>
322 struct fifth_functor_2deriv
323 { // Functor returning both 1st and 2nd derivatives.
fifth_functor_2derivfifth_functor_2deriv324   fifth_functor_2deriv(T const& to_find_root_of) : value(to_find_root_of)
325   { // Constructor stores value to find root of, for example:
326   }
327 
328   // using boost::math::tuple; // to return three values.
operator ()fifth_functor_2deriv329   std::tuple<T, T, T> operator()(T const& x)
330   { // Return both f(x) and f'(x) and f''(x).
331     T fx = x*x*x*x*x - value; // Difference (estimate x^3 - value).
332     T dx = 5 * x*x*x*x; // 1st derivative = 5x^4.
333     T d2x = 20 * x*x*x; // 2nd derivative = 20 x^3
334     return std::make_tuple(fx, dx, d2x); // 'return' fx, dx and d2x.
335   }
336 private:
337   T value; // to be 'fifth_rooted'.
338 }; // struct fifth_functor_2deriv
339 
340 //] [/root_finding_fifth_functor_2deriv]
341 
342 
343 /*`Our fifth function is now:*/
344 
345 //[root_finding_fifth_2deriv
346 
347 template <class T>
fifth_2deriv(T x)348 T fifth_2deriv(T x)
349 { // return fifth root of x using 1st and 2nd derivatives and Halley.
350   using namespace std;  // Help ADL of std functions.
351   using namespace boost::math; // halley_iterate
352 
353   int exponent;
354   frexp(x, &exponent); // Get exponent of z (ignore mantissa).
355   T guess = ldexp(1., exponent / 5); // Rough guess is to divide the exponent by three.
356   T min = ldexp(0.5, exponent / 5); // Minimum possible value is half our guess.
357   T max = ldexp(2., exponent / 5); // Maximum possible value is twice our guess.
358 
359   int digits = std::numeric_limits<T>::digits / 2; // Half maximum possible binary digits accuracy for type T.
360   const boost::uintmax_t maxit = 50;
361   boost::uintmax_t it = maxit;
362   T result = halley_iterate(fifth_functor_2deriv<T>(x), guess, min, max, digits, it);
363   // Can show how many iterations (updated by halley_iterate).
364   cout << it << " iterations (from max of " << maxit << ")" << endl;
365 
366   return result;
367 } // fifth_2deriv(x)
368 
369 //] [/root_finding_fifth_2deriv]
370 
main()371 int main()
372 {
373 
374   //[root_finding_example_1
375   cout << "fifth Root finding (fifth) Example." << endl;
376   // Show all possibly significant decimal digits.
377   cout.precision(std::numeric_limits<double>::max_digits10);
378   // or use   cout.precision(max_digits10 = 2 + std::numeric_limits<double>::digits * 3010/10000);
379   try
380   { // Always use try'n'catch blocks with Boost.Math to get any error messages.
381 
382     double v27 = 3125; // Example of a value that has an exact integer fifth root.
383     // exact value of fifth root is exactly 5.
384 
385     std::cout << "Fifth root  of " << v27 << " is " << 5 << std::endl;
386 
387     double v28 = v27+1; // Example of a value whose fifth root is *not* exactly representable.
388     // Value of fifth root is 5.0003199590478625588206333405631053401128722314376 (50 decimal digits precision)
389     // and to std::numeric_limits<double>::max_digits10 double precision (usually 17) is
390 
391     double root5v2 = static_cast<double>(5.0003199590478625588206333405631053401128722314376);
392 
393     std::cout << "Fifth root  of " << v28 << " is "  << root5v2 << std::endl;
394 
395     // Using bracketing:
396     double r = fifth_noderiv(v27);
397     cout << "fifth_noderiv(" << v27 << ") = " << r << endl;
398 
399     r = fifth_noderiv(v28);
400     cout << "fifth_noderiv(" << v28 << ") = " << r << endl;
401 
402     // Using 1st differential Newton-Raphson:
403     r = fifth_1deriv(v27);
404     cout << "fifth_1deriv(" << v27 << ") = " << r << endl;
405     r = fifth_1deriv(v28);
406     cout << "fifth_1deriv(" << v28 << ") = " << r << endl;
407 
408     // Using Halley with 1st and 2nd differentials.
409     r = fifth_2deriv(v27);
410     cout << "fifth_2deriv(" << v27 << ") = " << r << endl;
411     r = fifth_2deriv(v28);
412     cout << "fifth_2deriv(" << v28 << ") = " << r << endl;
413   }
414   catch (const std::exception& e)
415   { // Always useful to include try & catch blocks because default policies
416     // are to throw exceptions on arguments that cause errors like underflow, overflow.
417     // Lacking try & catch blocks, the program will abort without a message below,
418     // which may give some helpful clues as to the cause of the exception.
419     std::cout <<
420       "\n""Message from thrown exception was:\n   " << e.what() << std::endl;
421   }
422   //] [/root_finding_example_1
423   return 0;
424 }  // int main()
425 
426 //[root_finding_example_output
427 /*`
428 Normal output is:
429 
430 [pre
431 1>  Description: Autorun "J:\Cpp\MathToolkit\test\Math_test\Release\root_finding_fifth.exe"
432 1>  fifth Root finding (fifth) Example.
433 1>  Fifth root  of 3125 is 5
434 1>  Fifth root  of 3126 is 5.0003199590478626
435 1>  Iterations 10
436 1>  Converged to a single value 5
437 1>  fifth_noderiv(3125) = 5
438 1>  Iterations 11
439 1>  2 bits separate the bracketing values.
440 1>  5.0003199590478609
441 1>  5.0003199590478618
442 1>  fifth_noderiv(3126) = 5.0003199590478618
443 1>  6 iterations (from max of 20)
444 1>  fifth_1deriv(3125) = 5
445 1>  7 iterations (from max of 20)
446 1>  fifth_1deriv(3126) = 5.0003199590478626
447 1>  4 iterations (from max of 50)
448 1>  fifth_2deriv(3125) = 5
449 1>  4 iterations (from max of 50)
450 1>  fifth_2deriv(3126) = 5.0003199590478626
451 [/pre]
452 
453 to get some (much!) diagnostic output we can add
454 
455 #define BOOST_MATH_INSTRUMENT
456 
457 [pre
458 1>  fifth Root finding (fifth) Example.
459 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:537 a = 4 b = 8 fa = -2101 fb = 29643 count = 18
460 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:340  a = 4.264742943548387 b = 8
461 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:352  a = 4.264742943548387 b = 5.1409225585147951
462 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:259  a = 4.264742943548387 b = 5.1409225585147951 d = 8 e = 4 fa = -1714.2037505671719 fb = 465.91652114644285 fd = 29643 fe = -2101
463 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:267 q11 = -3.735257056451613 q21 = -0.045655399937094755 q31 = 0.68893005658139972 d21 = -2.9047328414222999 d31 = -0.18724955838500826
464 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:275 q22 = -0.15074699539567221 q32 = 0.007740525571111408 d32 = -0.13385363287680208 q33 = 0.074868009790687237 c = 5.0362815354915851
465 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:388  a = 4.264742943548387 b = 5.0362815354915851
466 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:259  a = 4.264742943548387 b = 5.0362815354915851 d = 5.1409225585147951 e = 8 fa = -1714.2037505671719 fb = 115.03721886368339 fd = 465.91652114644285 fe = 29643
467 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:267 q11 = -0.045655399937094755 q21 = -0.034306988726112195 q31 = 0.7230181097615842 d21 = -0.1389480117493222 d31 = -0.048520482181613811
468 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:275 q22 = -0.00036345624935100459 q32 = 0.011175908093791367 d32 = -0.0030375853617102483 q33 = 0.00014618657296010219 c = 4.999083147976723
469 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:408  a = 4.999083147976723 b = 5.0362815354915851
470 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:433  a = 4.999083147976723 b = 5.0008904277935091
471 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:434  tol = -0.00036152225583956088
472 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:259  a = 4.999083147976723 b = 5.0008904277935091 d = 5.0362815354915851 e = 4.264742943548387 fa = -2.8641119933622576 fb = 2.7835781082976609 fd = 115.03721886368339 fe = -1714.2037505671719
473 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:267 q11 = -0.048520482181613811 q21 = -0.00087760104664616457 q31 = 0.00091652546535745522 d21 = -0.036268708744722128 d31 = -0.00089075435142862297
474 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:275 q22 = -1.9862562616034592e-005 q32 = 3.1952597740788757e-007 d32 = -1.2833778805050512e-005 q33 = 1.1763429980834706e-008 c = 5.0000000047314881
475 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:388  a = 4.999083147976723 b = 5.0000000047314881
476 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:259  a = 4.999083147976723 b = 5.0000000047314881 d = 5.0008904277935091 e = 5.0362815354915851 fa = -2.8641119933622576 fb = 1.4785900475544622e-005 fd = 2.7835781082976609 fe = 115.03721886368339
477 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:267 q11 = -0.00087760104664616457 q21 = -4.7298032238887272e-009 q31 = 0.00091685202154135855 d21 = -0.00089042779182425238 d31 = -4.7332236912279757e-009
478 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:275 q22 = -1.6486403607318402e-012 q32 = 1.7346209428817704e-012 d32 = -1.6858463963666777e-012 q33 = 9.0382569995250912e-016 c = 5
479 1>  I:\modular-boost\boost/math/tools/toms748_solve.hpp:592 max_iter = 10 count = 7
480 1>  Iterations 20
481 1>  0 bits separate brackets.
482 1>  fifth_noderiv(3125) = 5
483 ]
484 */
485 //] [/root_finding_example_output]
486