test_root_iterations.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // (C) Copyright John Maddock 2015.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #include <pch.hpp>
  6. #ifndef BOOST_NO_CXX11_HDR_TUPLE
  7. #define BOOST_TEST_MAIN
  8. #include <boost/test/unit_test.hpp>
  9. #include <boost/test/tools/floating_point_comparison.hpp>
  10. #include <boost/math/tools/roots.hpp>
  11. #include <boost/test/results_collector.hpp>
  12. #include <boost/test/unit_test.hpp>
  13. #include <boost/math/special_functions/cbrt.hpp>
  14. #include <boost/math/special_functions/beta.hpp>
  15. #include <iostream>
  16. #include <iomanip>
  17. #include <tuple>
  18. #include "table_type.hpp"
  19. // No derivatives - using TOMS748 internally.
  20. struct cbrt_functor_noderiv
  21. { // cube root of x using only function - no derivatives.
  22. cbrt_functor_noderiv(double to_find_root_of) : a(to_find_root_of)
  23. { // Constructor just stores value a to find root of.
  24. }
  25. double operator()(double x)
  26. {
  27. double fx = x*x*x - a; // Difference (estimate x^3 - a).
  28. return fx;
  29. }
  30. private:
  31. double a; // to be 'cube_rooted'.
  32. }; // template <class T> struct cbrt_functor_noderiv
  33. // Using 1st derivative only Newton-Raphson
  34. struct cbrt_functor_deriv
  35. { // Functor also returning 1st derviative.
  36. cbrt_functor_deriv(double const& to_find_root_of) : a(to_find_root_of)
  37. { // Constructor stores value a to find root of,
  38. // for example: calling cbrt_functor_deriv<double>(x) to use to get cube root of x.
  39. }
  40. std::pair<double, double> operator()(double const& x)
  41. { // Return both f(x) and f'(x).
  42. double fx = x*x*x - a; // Difference (estimate x^3 - value).
  43. double dx = 3 * x*x; // 1st derivative = 3x^2.
  44. return std::make_pair(fx, dx); // 'return' both fx and dx.
  45. }
  46. private:
  47. double a; // to be 'cube_rooted'.
  48. };
  49. // Using 1st and 2nd derivatives with Halley algorithm.
  50. struct cbrt_functor_2deriv
  51. { // Functor returning both 1st and 2nd derivatives.
  52. cbrt_functor_2deriv(double const& to_find_root_of) : a(to_find_root_of)
  53. { // Constructor stores value a to find root of, for example:
  54. // calling cbrt_functor_2deriv<double>(x) to get cube root of x,
  55. }
  56. std::tuple<double, double, double> operator()(double const& x)
  57. { // Return both f(x) and f'(x) and f''(x).
  58. double fx = x*x*x - a; // Difference (estimate x^3 - value).
  59. double dx = 3 * x*x; // 1st derivative = 3x^2.
  60. double d2x = 6 * x; // 2nd derivative = 6x.
  61. return std::make_tuple(fx, dx, d2x); // 'return' fx, dx and d2x.
  62. }
  63. private:
  64. double a; // to be 'cube_rooted'.
  65. };
  66. template <class T, class Policy>
  67. struct ibeta_roots_1 // for first order algorithms
  68. {
  69. ibeta_roots_1(T _a, T _b, T t, bool inv = false)
  70. : a(_a), b(_b), target(t), invert(inv) {}
  71. T operator()(const T& x)
  72. {
  73. return boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
  74. }
  75. private:
  76. T a, b, target;
  77. bool invert;
  78. };
  79. template <class T, class Policy>
  80. struct ibeta_roots_2 // for second order algorithms
  81. {
  82. ibeta_roots_2(T _a, T _b, T t, bool inv = false)
  83. : a(_a), b(_b), target(t), invert(inv) {}
  84. boost::math::tuple<T, T> operator()(const T& x)
  85. {
  86. typedef boost::math::lanczos::lanczos<T, Policy> S;
  87. typedef typename S::type L;
  88. T f = boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
  89. T f1 = invert ?
  90. -boost::math::detail::ibeta_power_terms(b, a, 1 - x, x, L(), true, Policy())
  91. : boost::math::detail::ibeta_power_terms(a, b, x, 1 - x, L(), true, Policy());
  92. T y = 1 - x;
  93. if (y == 0)
  94. y = boost::math::tools::min_value<T>() * 8;
  95. f1 /= y * x;
  96. // make sure we don't have a zero derivative:
  97. if (f1 == 0)
  98. f1 = (invert ? -1 : 1) * boost::math::tools::min_value<T>() * 64;
  99. return boost::math::make_tuple(f, f1);
  100. }
  101. private:
  102. T a, b, target;
  103. bool invert;
  104. };
  105. template <class T, class Policy>
  106. struct ibeta_roots_3 // for third order algorithms
  107. {
  108. ibeta_roots_3(T _a, T _b, T t, bool inv = false)
  109. : a(_a), b(_b), target(t), invert(inv) {}
  110. boost::math::tuple<T, T, T> operator()(const T& x)
  111. {
  112. typedef typename boost::math::lanczos::lanczos<T, Policy>::type L;
  113. T f = boost::math::detail::ibeta_imp(a, b, x, Policy(), invert, true) - target;
  114. T f1 = invert ?
  115. -boost::math::detail::ibeta_power_terms(b, a, 1 - x, x, L(), true, Policy())
  116. : boost::math::detail::ibeta_power_terms(a, b, x, 1 - x, L(), true, Policy());
  117. T y = 1 - x;
  118. if (y == 0)
  119. y = boost::math::tools::min_value<T>() * 8;
  120. f1 /= y * x;
  121. T f2 = f1 * (-y * a + (b - 2) * x + 1) / (y * x);
  122. if (invert)
  123. f2 = -f2;
  124. // make sure we don't have a zero derivative:
  125. if (f1 == 0)
  126. f1 = (invert ? -1 : 1) * boost::math::tools::min_value<T>() * 64;
  127. return boost::math::make_tuple(f, f1, f2);
  128. }
  129. private:
  130. T a, b, target;
  131. bool invert;
  132. };
  133. BOOST_AUTO_TEST_CASE( test_main )
  134. {
  135. int newton_limits = static_cast<int>(std::numeric_limits<double>::digits * 0.6);
  136. double arg = 1e-50;
  137. boost::uintmax_t iters;
  138. double guess;
  139. double dr;
  140. while(arg < 1e50)
  141. {
  142. double result = boost::math::cbrt(arg);
  143. //
  144. // Start with a really bad guess 5 times below the result:
  145. //
  146. guess = result / 5;
  147. iters = 1000;
  148. // TOMS algo first:
  149. std::pair<double, double> r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
  150. BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
  151. BOOST_CHECK_LE(iters, 14);
  152. // Newton next:
  153. iters = 1000;
  154. dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, guess / 2, result * 10, newton_limits, iters);
  155. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  156. BOOST_CHECK_LE(iters, 12);
  157. // Halley next:
  158. iters = 1000;
  159. dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  160. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  161. BOOST_CHECK_LE(iters, 7);
  162. // Schroder next:
  163. iters = 1000;
  164. dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  165. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  166. BOOST_CHECK_LE(iters, 11);
  167. //
  168. // Over again with a bad guess 5 times larger than the result:
  169. //
  170. iters = 1000;
  171. guess = result * 5;
  172. r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
  173. BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
  174. BOOST_CHECK_LE(iters, 14);
  175. // Newton next:
  176. iters = 1000;
  177. dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  178. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  179. BOOST_CHECK_LE(iters, 12);
  180. // Halley next:
  181. iters = 1000;
  182. dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  183. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  184. BOOST_CHECK_LE(iters, 7);
  185. // Schroder next:
  186. iters = 1000;
  187. dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  188. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  189. BOOST_CHECK_LE(iters, 11);
  190. //
  191. // A much better guess, 1% below result:
  192. //
  193. iters = 1000;
  194. guess = result * 0.9;
  195. r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
  196. BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
  197. BOOST_CHECK_LE(iters, 12);
  198. // Newton next:
  199. iters = 1000;
  200. dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  201. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  202. BOOST_CHECK_LE(iters, 5);
  203. // Halley next:
  204. iters = 1000;
  205. dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  206. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  207. BOOST_CHECK_LE(iters, 3);
  208. // Schroder next:
  209. iters = 1000;
  210. dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  211. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  212. BOOST_CHECK_LE(iters, 4);
  213. //
  214. // A much better guess, 1% above result:
  215. //
  216. iters = 1000;
  217. guess = result * 1.1;
  218. r = boost::math::tools::bracket_and_solve_root(cbrt_functor_noderiv(arg), guess, 2.0, true, boost::math::tools::eps_tolerance<double>(), iters);
  219. BOOST_CHECK_CLOSE_FRACTION((r.first + r.second) / 2, result, std::numeric_limits<double>::epsilon() * 4);
  220. BOOST_CHECK_LE(iters, 12);
  221. // Newton next:
  222. iters = 1000;
  223. dr = boost::math::tools::newton_raphson_iterate(cbrt_functor_deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  224. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  225. BOOST_CHECK_LE(iters, 5);
  226. // Halley next:
  227. iters = 1000;
  228. dr = boost::math::tools::halley_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  229. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  230. BOOST_CHECK_LE(iters, 3);
  231. // Schroder next:
  232. iters = 1000;
  233. dr = boost::math::tools::schroder_iterate(cbrt_functor_2deriv(arg), guess, result / 10, result * 10, newton_limits, iters);
  234. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 2);
  235. BOOST_CHECK_LE(iters, 4);
  236. arg *= 3.5;
  237. }
  238. //
  239. // Test ibeta as this triggers all the pathological cases!
  240. //
  241. #ifndef SC_
  242. #define SC_(x) x
  243. #endif
  244. #define T double
  245. # include "ibeta_small_data.ipp"
  246. for (unsigned i = 0; i < ibeta_small_data.size(); ++i)
  247. {
  248. //
  249. // These inverse tests are thrown off if the output of the
  250. // incomplete beta is too close to 1: basically there is insuffient
  251. // information left in the value we're using as input to the inverse
  252. // to be able to get back to the original value.
  253. //
  254. if (ibeta_small_data[i][5] == 0)
  255. {
  256. iters = 1000;
  257. dr = boost::math::tools::newton_raphson_iterate(ibeta_roots_2<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
  258. BOOST_CHECK_EQUAL(dr, 0.0);
  259. BOOST_CHECK_LE(iters, 27);
  260. iters = 1000;
  261. dr = boost::math::tools::halley_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
  262. BOOST_CHECK_EQUAL(dr, 0.0);
  263. BOOST_CHECK_LE(iters, 10);
  264. }
  265. else if ((1 - ibeta_small_data[i][5] > 0.001)
  266. && (fabs(ibeta_small_data[i][5]) > 2 * boost::math::tools::min_value<double>()))
  267. {
  268. iters = 1000;
  269. double result = ibeta_small_data[i][2];
  270. dr = boost::math::tools::newton_raphson_iterate(ibeta_roots_2<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
  271. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 200);
  272. #if defined(BOOST_MSVC) && (BOOST_MSVC == 1600)
  273. BOOST_CHECK_LE(iters, 40);
  274. #else
  275. BOOST_CHECK_LE(iters, 27);
  276. #endif
  277. iters = 1000;
  278. result = ibeta_small_data[i][2];
  279. dr = boost::math::tools::halley_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
  280. BOOST_CHECK_CLOSE_FRACTION(dr, result, std::numeric_limits<double>::epsilon() * 200);
  281. BOOST_CHECK_LE(iters, 40);
  282. }
  283. else if (1 == ibeta_small_data[i][5])
  284. {
  285. iters = 1000;
  286. dr = boost::math::tools::newton_raphson_iterate(ibeta_roots_2<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
  287. BOOST_CHECK_EQUAL(dr, 1.0);
  288. BOOST_CHECK_LE(iters, 27);
  289. iters = 1000;
  290. dr = boost::math::tools::halley_iterate(ibeta_roots_3<double, boost::math::policies::policy<> >(ibeta_small_data[i][0], ibeta_small_data[i][1], ibeta_small_data[i][5]), 0.5, 0.0, 1.0, 53, iters);
  291. BOOST_CHECK_EQUAL(dr, 1.0);
  292. BOOST_CHECK_LE(iters, 10);
  293. }
  294. }
  295. }
  296. #else
  297. int main() { return 0; }
  298. #endif