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