root_elliptic_finding.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. // Copyright Paul A. Bristow 2015
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. // Comparison of finding roots using TOMS748, Newton-Raphson, Halley & Schroder algorithms.
  7. // root_n_finding_algorithms.cpp Generalised for nth root version.
  8. // http://en.wikipedia.org/wiki/Cube_root
  9. // Note that this file contains Quickbook mark-up as well as code
  10. // and comments, don't change any of the special comment mark-ups!
  11. // This program also writes files in Quickbook tables mark-up format.
  12. #include <boost/cstdlib.hpp>
  13. #include <boost/config.hpp>
  14. #include <boost/array.hpp>
  15. #include <boost/type_traits/is_floating_point.hpp>
  16. #include <boost/math/tools/roots.hpp>
  17. #include <boost/math/special_functions/ellint_1.hpp>
  18. #include <boost/math/special_functions/ellint_2.hpp>
  19. //using boost::math::policies::policy;
  20. //using boost::math::tools::eps_tolerance; // Binary functor for specified number of bits.
  21. //using boost::math::tools::bracket_and_solve_root;
  22. //using boost::math::tools::toms748_solve;
  23. //using boost::math::tools::halley_iterate;
  24. //using boost::math::tools::newton_raphson_iterate;
  25. //using boost::math::tools::schroder_iterate;
  26. #include <boost/math/special_functions/next.hpp> // For float_distance.
  27. #include <boost/multiprecision/cpp_bin_float.hpp> // is binary.
  28. using boost::multiprecision::cpp_bin_float_100;
  29. using boost::multiprecision::cpp_bin_float_50;
  30. #include <boost/timer/timer.hpp>
  31. #include <boost/system/error_code.hpp>
  32. #include <boost/preprocessor/stringize.hpp>
  33. // STL
  34. #include <iostream>
  35. #include <iomanip>
  36. #include <string>
  37. #include <vector>
  38. #include <limits>
  39. #include <fstream> // std::ofstream
  40. #include <cmath>
  41. #include <typeinfo> // for type name using typid(thingy).name();
  42. #ifdef __FILE__
  43. std::string sourcefilename = __FILE__;
  44. #else
  45. std::string sourcefilename("");
  46. #endif
  47. std::string chop_last(std::string s)
  48. {
  49. std::string::size_type pos = s.find_last_of("\\/");
  50. if(pos != std::string::npos)
  51. s.erase(pos);
  52. else if(s.empty())
  53. abort();
  54. else
  55. s.erase();
  56. return s;
  57. }
  58. std::string make_root()
  59. {
  60. std::string result;
  61. if(sourcefilename.find_first_of(":") != std::string::npos)
  62. {
  63. result = chop_last(sourcefilename); // lose filename part
  64. result = chop_last(result); // lose /example/
  65. result = chop_last(result); // lose /math/
  66. result = chop_last(result); // lose /libs/
  67. }
  68. else
  69. {
  70. result = chop_last(sourcefilename); // lose filename part
  71. if(result.empty())
  72. result = ".";
  73. result += "/../../..";
  74. }
  75. return result;
  76. }
  77. std::string short_file_name(std::string s)
  78. {
  79. std::string::size_type pos = s.find_last_of("\\/");
  80. if(pos != std::string::npos)
  81. s.erase(0, pos + 1);
  82. return s;
  83. }
  84. std::string boost_root = make_root();
  85. std::string fp_hardware; // Any hardware features like SEE or AVX
  86. const std::string roots_name = "libs/math/doc/roots/";
  87. const std::string full_roots_name(boost_root + "/libs/math/doc/roots/");
  88. const std::size_t nooftypes = 4;
  89. const std::size_t noofalgos = 4;
  90. double digits_accuracy = 1.0; // 1 == maximum possible accuracy.
  91. std::stringstream ss;
  92. std::ofstream fout;
  93. std::vector<std::string> algo_names =
  94. {
  95. "TOMS748", "Newton", "Halley", "Schr'''&#xf6;'''der"
  96. };
  97. std::vector<std::string> names =
  98. {
  99. "float", "double", "long double", "cpp_bin_float50"
  100. };
  101. uintmax_t iters; // Global as value of iterations is not returned.
  102. struct root_info
  103. { // for a floating-point type, float, double ...
  104. std::size_t max_digits10; // for type.
  105. std::string full_typename; // for type from type_id.name().
  106. std::string short_typename; // for type "float", "double", "cpp_bin_float_50" ....
  107. std::size_t bin_digits; // binary in floating-point type numeric_limits<T>::digits;
  108. int get_digits; // fraction of maximum possible accuracy required.
  109. // = digits * digits_accuracy
  110. // Vector of values (4) for each algorithm, TOMS748, Newton, Halley & Schroder.
  111. //std::vector< boost::int_least64_t> times; converted to int.
  112. std::vector<int> times; // arbirary units (ticks).
  113. //boost::int_least64_t min_time = std::numeric_limits<boost::int_least64_t>::max(); // Used to normalize times (as int).
  114. std::vector<double> normed_times;
  115. int min_time = (std::numeric_limits<int>::max)(); // Used to normalize times.
  116. std::vector<uintmax_t> iterations;
  117. std::vector<long int> distances;
  118. std::vector<cpp_bin_float_100> full_results;
  119. }; // struct root_info
  120. std::vector<root_info> root_infos; // One element for each floating-point type used.
  121. inline std::string build_test_name(const char* type_name, const char* test_name)
  122. {
  123. std::string result(BOOST_COMPILER);
  124. result += "|";
  125. result += BOOST_STDLIB;
  126. result += "|";
  127. result += BOOST_PLATFORM;
  128. result += "|";
  129. result += type_name;
  130. result += "|";
  131. result += test_name;
  132. #if defined(_DEBUG) || !defined(NDEBUG)
  133. result += "|";
  134. result += " debug";
  135. #else
  136. result += "|";
  137. result += " release";
  138. #endif
  139. result += "|";
  140. return result;
  141. } // std::string build_test_name
  142. // Algorithms //////////////////////////////////////////////
  143. // No derivatives - using TOMS748 internally.
  144. //[elliptic_noderv_func
  145. template <typename T = double>
  146. struct elliptic_root_functor_noderiv
  147. { // Nth root of x using only function - no derivatives.
  148. elliptic_root_functor_noderiv(T const& arc, T const& radius) : m_arc(arc), m_radius(radius)
  149. { // Constructor just stores value a to find root of.
  150. }
  151. T operator()(T const& x)
  152. {
  153. using std::sqrt;
  154. // return the difference between required arc-length, and the calculated arc-length for an
  155. // ellipse with radii m_radius and x:
  156. T a = (std::max)(m_radius, x);
  157. T b = (std::min)(m_radius, x);
  158. T k = sqrt(1 - b * b / (a * a));
  159. return 4 * a * boost::math::ellint_2(k) - m_arc;
  160. }
  161. private:
  162. T m_arc; // length of arc.
  163. T m_radius; // one of the two radii of the ellipse
  164. }; // template <class T> struct elliptic_root_functor_noderiv
  165. //]
  166. //[elliptic_root_noderiv
  167. template <class T = double>
  168. T elliptic_root_noderiv(T radius, T arc)
  169. { // return the other radius of an ellipse, given one radii and the arc-length
  170. using namespace std; // Help ADL of std functions.
  171. using namespace boost::math::tools; // For bracket_and_solve_root.
  172. T guess = sqrt(arc * arc / 16 - radius * radius);
  173. T factor = 1.2; // How big steps to take when searching.
  174. const boost::uintmax_t maxit = 50; // Limit to maximum iterations.
  175. boost::uintmax_t it = maxit; // Initally our chosen max iterations, but updated with actual.
  176. bool is_rising = true; // arc-length increases if one radii increases, so function is rising
  177. // Define a termination condition, stop when nearly all digits are correct, but allow for
  178. // the fact that we are returning a range, and must have some inaccuracy in the elliptic integral:
  179. eps_tolerance<T> tol(std::numeric_limits<T>::digits - 2);
  180. // Call bracket_and_solve_root to find the solution, note that this is a rising function:
  181. std::pair<T, T> r = bracket_and_solve_root(elliptic_root_functor_noderiv<T>(arc, radius), guess, factor, is_rising, tol, it);
  182. //<-
  183. iters = it;
  184. //->
  185. // Result is midway between the endpoints of the range:
  186. return r.first + (r.second - r.first) / 2;
  187. } // template <class T> T elliptic_root_noderiv(T x)
  188. //]
  189. // Using 1st derivative only Newton-Raphson
  190. //[elliptic_1deriv_func
  191. template <class T = double>
  192. struct elliptic_root_functor_1deriv
  193. { // Functor also returning 1st derviative.
  194. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  195. elliptic_root_functor_1deriv(T const& arc, T const& radius) : m_arc(arc), m_radius(radius)
  196. { // Constructor just stores value a to find root of.
  197. }
  198. std::pair<T, T> operator()(T const& x)
  199. {
  200. using std::sqrt;
  201. // Return the difference between required arc-length, and the calculated arc-length for an
  202. // ellipse with radii m_radius and x, plus it's derivative.
  203. // See http://www.wolframalpha.com/input/?i=d%2Fda+[4+*+a+*+EllipticE%281+-+b^2%2Fa^2%29]
  204. // We require two elliptic integral calls, but from these we can calculate both
  205. // the function and it's derivative:
  206. T a = (std::max)(m_radius, x);
  207. T b = (std::min)(m_radius, x);
  208. T a2 = a * a;
  209. T b2 = b * b;
  210. T k = sqrt(1 - b2 / a2);
  211. T Ek = boost::math::ellint_2(k);
  212. T Kk = boost::math::ellint_1(k);
  213. T fx = 4 * a * Ek - m_arc;
  214. T dfx = 4 * (a2 * Ek - b2 * Kk) / (a2 - b2);
  215. return std::make_pair(fx, dfx);
  216. }
  217. private:
  218. T m_arc; // length of arc.
  219. T m_radius; // one of the two radii of the ellipse
  220. }; // struct elliptic_root__functor_1deriv
  221. //]
  222. //[elliptic_1deriv
  223. template <class T = double>
  224. T elliptic_root_1deriv(T radius, T arc)
  225. {
  226. using namespace std; // Help ADL of std functions.
  227. using namespace boost::math::tools; // For newton_raphson_iterate.
  228. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  229. T guess = sqrt(arc * arc / 16 - radius * radius);
  230. T min = 0; // Minimum possible value is zero.
  231. T max = arc; // Maximum possible value is the arc length.
  232. // Accuracy doubles at each step, so stop when just over half of the digits are
  233. // correct, and rely on that step to polish off the remainder:
  234. int get_digits = static_cast<int>(std::numeric_limits<T>::digits * 0.6);
  235. const boost::uintmax_t maxit = 20;
  236. boost::uintmax_t it = maxit;
  237. T result = newton_raphson_iterate(elliptic_root_functor_1deriv<T>(arc, radius), guess, min, max, get_digits, it);
  238. //<-
  239. iters = it;
  240. //->
  241. return result;
  242. } // T elliptic_root_1_deriv Newton-Raphson
  243. //]
  244. // Using 1st and 2nd derivatives with Halley algorithm.
  245. //[elliptic_2deriv_func
  246. template <class T = double>
  247. struct elliptic_root_functor_2deriv
  248. { // Functor returning both 1st and 2nd derivatives.
  249. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  250. elliptic_root_functor_2deriv(T const& arc, T const& radius) : m_arc(arc), m_radius(radius) {}
  251. std::tuple<T, T, T> operator()(T const& x)
  252. {
  253. using std::sqrt;
  254. // Return the difference between required arc-length, and the calculated arc-length for an
  255. // ellipse with radii m_radius and x, plus it's derivative.
  256. // See http://www.wolframalpha.com/input/?i=d^2%2Fda^2+[4+*+a+*+EllipticE%281+-+b^2%2Fa^2%29]
  257. // for the second derivative.
  258. T a = (std::max)(m_radius, x);
  259. T b = (std::min)(m_radius, x);
  260. T a2 = a * a;
  261. T b2 = b * b;
  262. T k = sqrt(1 - b2 / a2);
  263. T Ek = boost::math::ellint_2(k);
  264. T Kk = boost::math::ellint_1(k);
  265. T fx = 4 * a * Ek - m_arc;
  266. T dfx = 4 * (a2 * Ek - b2 * Kk) / (a2 - b2);
  267. T dfx2 = 4 * b2 * ((a2 + b2) * Kk - 2 * a2 * Ek) / (a * (a2 - b2) * (a2 - b2));
  268. return std::make_tuple(fx, dfx, dfx2);
  269. }
  270. private:
  271. T m_arc; // length of arc.
  272. T m_radius; // one of the two radii of the ellipse
  273. };
  274. //]
  275. //[elliptic_2deriv
  276. template <class T = double>
  277. T elliptic_root_2deriv(T radius, T arc)
  278. {
  279. using namespace std; // Help ADL of std functions.
  280. using namespace boost::math::tools; // For halley_iterate.
  281. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  282. T guess = sqrt(arc * arc / 16 - radius * radius);
  283. T min = 0; // Minimum possible value is zero.
  284. T max = arc; // radius can't be larger than the arc length.
  285. // Accuracy triples at each step, so stop when just over one-third of the digits
  286. // are correct, and the last iteration will polish off the remaining digits:
  287. int get_digits = static_cast<int>(std::numeric_limits<T>::digits * 0.4);
  288. const boost::uintmax_t maxit = 20;
  289. boost::uintmax_t it = maxit;
  290. T result = halley_iterate(elliptic_root_functor_2deriv<T>(arc, radius), guess, min, max, get_digits, it);
  291. //<-
  292. iters = it;
  293. //->
  294. return result;
  295. } // nth_2deriv Halley
  296. //]
  297. // Using 1st and 2nd derivatives using Schroder algorithm.
  298. template <class T = double>
  299. T elliptic_root_2deriv_s(T arc, T radius)
  300. { // return nth root of x using 1st and 2nd derivatives and Schroder.
  301. using namespace std; // Help ADL of std functions.
  302. using namespace boost::math::tools; // For schroder_iterate.
  303. BOOST_STATIC_ASSERT_MSG(boost::is_integral<T>::value == false, "Only floating-point type types can be used!");
  304. T guess = sqrt(arc * arc / 16 - radius * radius);
  305. T min = 0; // Minimum possible value is zero.
  306. T max = arc; // radius can't be larger than the arc length.
  307. int digits = std::numeric_limits<T>::digits; // Maximum possible binary digits accuracy for type T.
  308. int get_digits = static_cast<int>(digits * digits_accuracy);
  309. const boost::uintmax_t maxit = 20;
  310. boost::uintmax_t it = maxit;
  311. T result = schroder_iterate(elliptic_root_functor_2deriv<T>(arc, radius), guess, min, max, get_digits, it);
  312. iters = it;
  313. return result;
  314. } // T elliptic_root_2deriv_s Schroder
  315. //////////////////////////////////////////////////////// end of algorithms - perhaps in a separate .hpp?
  316. //! Print 4 floating-point types info: max_digits10, digits and required accuracy digits as a Quickbook table.
  317. int table_type_info(double digits_accuracy)
  318. {
  319. std::string qbk_name = full_roots_name; // Prefix by boost_root file.
  320. qbk_name += "type_info_table";
  321. std::stringstream ss;
  322. ss.precision(3);
  323. ss << "_" << digits_accuracy * 100;
  324. qbk_name += ss.str();
  325. #ifdef _MSC_VER
  326. qbk_name += "_msvc.qbk";
  327. #else // assume GCC
  328. qbk_name += "_gcc.qbk";
  329. #endif
  330. // Example: type_info_table_100_msvc.qbk
  331. fout.open(qbk_name, std::ios_base::out);
  332. if (fout.is_open())
  333. {
  334. std::cout << "Output type table to " << qbk_name << std::endl;
  335. }
  336. else
  337. { // Failed to open.
  338. std::cout << " Open file " << qbk_name << " for output failed!" << std::endl;
  339. std::cout << "errno " << errno << std::endl;
  340. return errno;
  341. }
  342. fout <<
  343. "[/"
  344. << qbk_name
  345. << "\n"
  346. "Copyright 2015 Paul A. Bristow.""\n"
  347. "Copyright 2015 John Maddock.""\n"
  348. "Distributed under the Boost Software License, Version 1.0.""\n"
  349. "(See accompanying file LICENSE_1_0.txt or copy at""\n"
  350. "http://www.boost.org/LICENSE_1_0.txt).""\n"
  351. "]""\n"
  352. << std::endl;
  353. fout << "[h6 Fraction of maximum possible bits of accuracy required is " << digits_accuracy << ".]\n" << std::endl;
  354. std::string table_id("type_info");
  355. table_id += ss.str(); // Fraction digits accuracy.
  356. #ifdef _MSC_VER
  357. table_id += "_msvc";
  358. #else // assume GCC
  359. table_id += "_gcc";
  360. #endif
  361. fout << "[table:" << table_id << " Digits for float, double, long double and cpp_bin_float_50\n"
  362. << "[[type name] [max_digits10] [binary digits] [required digits]]\n";// header.
  363. // For all fout types:
  364. fout << "[[" << "float" << "]"
  365. << "[" << std::numeric_limits<float>::max_digits10 << "]" // max_digits10
  366. << "[" << std::numeric_limits<float>::digits << "]"// < "Binary digits
  367. << "[" << static_cast<int>(std::numeric_limits<float>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  368. fout << "[[" << "float" << "]"
  369. << "[" << std::numeric_limits<double>::max_digits10 << "]" // max_digits10
  370. << "[" << std::numeric_limits<double>::digits << "]"// < "Binary digits
  371. << "[" << static_cast<int>(std::numeric_limits<double>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  372. fout << "[[" << "long double" << "]"
  373. << "[" << std::numeric_limits<long double>::max_digits10 << "]" // max_digits10
  374. << "[" << std::numeric_limits<long double>::digits << "]"// < "Binary digits
  375. << "[" << static_cast<int>(std::numeric_limits<long double>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  376. fout << "[[" << "cpp_bin_float_50" << "]"
  377. << "[" << std::numeric_limits<cpp_bin_float_50>::max_digits10 << "]" // max_digits10
  378. << "[" << std::numeric_limits<cpp_bin_float_50>::digits << "]"// < "Binary digits
  379. << "[" << static_cast<int>(std::numeric_limits<cpp_bin_float_50>::digits * digits_accuracy) << "]]\n"; // Accuracy digits.
  380. fout << "] [/table table_id_msvc] \n" << std::endl; // End of table.
  381. fout.close();
  382. return 0;
  383. } // type_table
  384. //! Evaluate root N timing for each algorithm, and for one floating-point type T.
  385. template <typename T>
  386. int test_root(cpp_bin_float_100 big_radius, cpp_bin_float_100 big_arc, cpp_bin_float_100 answer, const char* type_name, std::size_t type_no)
  387. {
  388. std::size_t max_digits = 2 + std::numeric_limits<T>::digits * 3010 / 10000;
  389. // For new versions use max_digits10
  390. // std::cout.precision(std::numeric_limits<T>::max_digits10);
  391. std::cout.precision(max_digits);
  392. std::cout << std::showpoint << std::endl; // Show trailing zeros too.
  393. root_infos.push_back(root_info());
  394. root_infos[type_no].max_digits10 = max_digits;
  395. root_infos[type_no].full_typename = typeid(T).name(); // Full typename.
  396. root_infos[type_no].short_typename = type_name; // Short typename.
  397. root_infos[type_no].bin_digits = std::numeric_limits<T>::digits;
  398. root_infos[type_no].get_digits = static_cast<int>(std::numeric_limits<T>::digits * digits_accuracy);
  399. T radius = static_cast<T>(big_radius);
  400. T arc = static_cast<T>(big_arc);
  401. T result; // root
  402. T sum = 0;
  403. T ans = static_cast<T>(answer);
  404. using boost::timer::nanosecond_type;
  405. using boost::timer::cpu_times;
  406. using boost::timer::cpu_timer;
  407. long eval_count = boost::is_floating_point<T>::value ? 1000000 : 10000; // To give a sufficiently stable timing for the fast built-in types,
  408. // This takes an inconveniently long time for multiprecision cpp_bin_float_50 etc types.
  409. cpu_times now; // Holds wall, user and system times.
  410. { // Evaluate times etc for each algorithm.
  411. //algorithm_names.push_back("TOMS748"); //
  412. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  413. ti.start();
  414. for(long i = eval_count; i >= 0; --i)
  415. {
  416. result = elliptic_root_noderiv(radius, arc); //
  417. sum += result;
  418. }
  419. now = ti.elapsed();
  420. int time = static_cast<int>(now.user / eval_count);
  421. root_infos[type_no].times.push_back(time); // CPU time taken.
  422. if (time < root_infos[type_no].min_time)
  423. {
  424. root_infos[type_no].min_time = time;
  425. }
  426. ti.stop();
  427. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  428. root_infos[type_no].distances.push_back(distance);
  429. root_infos[type_no].iterations.push_back(iters); //
  430. root_infos[type_no].full_results.push_back(result);
  431. }
  432. {
  433. // algorithm_names.push_back("Newton"); // algorithm
  434. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  435. ti.start();
  436. for(long i = eval_count; i >= 0; --i)
  437. {
  438. result = elliptic_root_1deriv(radius, arc); //
  439. sum += result;
  440. }
  441. now = ti.elapsed();
  442. int time = static_cast<int>(now.user / eval_count);
  443. root_infos[type_no].times.push_back(time); // CPU time taken.
  444. if (time < root_infos[type_no].min_time)
  445. {
  446. root_infos[type_no].min_time = time;
  447. }
  448. ti.stop();
  449. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  450. root_infos[type_no].distances.push_back(distance);
  451. root_infos[type_no].iterations.push_back(iters); //
  452. root_infos[type_no].full_results.push_back(result);
  453. }
  454. {
  455. //algorithm_names.push_back("Halley"); // algorithm
  456. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  457. ti.start();
  458. for(long i = eval_count; i >= 0; --i)
  459. {
  460. result = elliptic_root_2deriv(radius, arc); //
  461. sum += result;
  462. }
  463. now = ti.elapsed();
  464. int time = static_cast<int>(now.user / eval_count);
  465. root_infos[type_no].times.push_back(time); // CPU time taken.
  466. ti.stop();
  467. if (time < root_infos[type_no].min_time)
  468. {
  469. root_infos[type_no].min_time = time;
  470. }
  471. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  472. root_infos[type_no].distances.push_back(distance);
  473. root_infos[type_no].iterations.push_back(iters); //
  474. root_infos[type_no].full_results.push_back(result);
  475. }
  476. {
  477. // algorithm_names.push_back("Schr'''&#xf6;'''der"); // algorithm
  478. cpu_timer ti; // Can start, pause, resume and stop, and read elapsed.
  479. ti.start();
  480. for(long i = eval_count; i >= 0; --i)
  481. {
  482. result = elliptic_root_2deriv_s(arc, radius); //
  483. sum += result;
  484. }
  485. now = ti.elapsed();
  486. int time = static_cast<int>(now.user / eval_count);
  487. root_infos[type_no].times.push_back(time); // CPU time taken.
  488. if (time < root_infos[type_no].min_time)
  489. {
  490. root_infos[type_no].min_time = time;
  491. }
  492. ti.stop();
  493. long int distance = static_cast<int>(boost::math::float_distance<T>(result, ans));
  494. root_infos[type_no].distances.push_back(distance);
  495. root_infos[type_no].iterations.push_back(iters); //
  496. root_infos[type_no].full_results.push_back(result);
  497. }
  498. for (size_t i = 0; i != root_infos[type_no].times.size(); i++) // For each time.
  499. { // Normalize times.
  500. root_infos[type_no].normed_times.push_back(static_cast<double>(root_infos[type_no].times[i]) / root_infos[type_no].min_time);
  501. }
  502. std::cout << "Accumulated result was: " << sum << std::endl;
  503. return 4; // eval_count of how many algorithms used.
  504. } // test_root
  505. /*! Fill array of times, interations, etc for Nth root for all 4 types,
  506. and write a table of results in Quickbook format.
  507. */
  508. void table_root_info(cpp_bin_float_100 radius, cpp_bin_float_100 arc)
  509. {
  510. using std::abs;
  511. std::cout << nooftypes << " floating-point types tested:" << std::endl;
  512. #if defined(_DEBUG) || !defined(NDEBUG)
  513. std::cout << "Compiled in debug mode." << std::endl;
  514. #else
  515. std::cout << "Compiled in optimise mode." << std::endl;
  516. #endif
  517. std::cout << "FP hardware " << fp_hardware << std::endl;
  518. // Compute the 'right' answer for root N at 100 decimal digits.
  519. cpp_bin_float_100 full_answer = elliptic_root_noderiv(radius, arc);
  520. root_infos.clear(); // Erase any previous data.
  521. // Fill the elements of the array for each floating-point type.
  522. test_root<float>(radius, arc, full_answer, "float", 0);
  523. test_root<double>(radius, arc, full_answer, "double", 1);
  524. test_root<long double>(radius, arc, full_answer, "long double", 2);
  525. test_root<cpp_bin_float_50>(radius, arc, full_answer, "cpp_bin_float_50", 3);
  526. // Use info from 4 floating point types to
  527. // Prepare Quickbook table for a single root
  528. // with columns of times, iterations, distances repeated for various floating-point types,
  529. // and 4 rows for each algorithm.
  530. std::stringstream table_info;
  531. table_info.precision(3);
  532. table_info << "[table:elliptic root with radius " << radius << " and arc length " << arc << ") for float, double, long double and cpp_bin_float_50 types";
  533. if (fp_hardware != "")
  534. {
  535. table_info << ", using " << fp_hardware;
  536. }
  537. table_info << std::endl;
  538. fout << table_info.str()
  539. << "[[][float][][][] [][double][][][] [][long d][][][] [][cpp50][][]]\n"
  540. << "[[Algo ]";
  541. for (size_t tp = 0; tp != nooftypes; tp++)
  542. { // For all types:
  543. fout << "[Its]" << "[Times]" << "[Norm]" << "[Dis]" << "[ ]";
  544. }
  545. fout << "]" << std::endl;
  546. // Row for all algorithms.
  547. for (std::size_t algo = 0; algo != noofalgos; algo++)
  548. {
  549. fout << "[[" << std::left << std::setw(9) << algo_names[algo] << "]";
  550. for (size_t tp = 0; tp != nooftypes; tp++)
  551. { // For all types:
  552. fout
  553. << "[" << std::right << std::showpoint
  554. << std::setw(3) << std::setprecision(2) << root_infos[tp].iterations[algo] << "]["
  555. << std::setw(5) << std::setprecision(5) << root_infos[tp].times[algo] << "][";
  556. fout << std::setw(3) << std::setprecision(3);
  557. double normed_time = root_infos[tp].normed_times[algo];
  558. if (abs(normed_time - 1.00) <= 0.05)
  559. { // At or near the best time, so show as blue.
  560. fout << "[role blue " << normed_time << "]";
  561. }
  562. else if (abs(normed_time) > 4.)
  563. { // markedly poor so show as red.
  564. fout << "[role red " << normed_time << "]";
  565. }
  566. else
  567. { // Not the best, so normal black.
  568. fout << normed_time;
  569. }
  570. fout << "]["
  571. << std::setw(3) << std::setprecision(2) << root_infos[tp].distances[algo] << "][ ]";
  572. } // tp
  573. fout << "]" << std::endl;
  574. } // for algo
  575. fout << "] [/end of table root]\n";
  576. } // void table_root_info
  577. /*! Output program header, table of type info, and tables for 4 algorithms and 4 floating-point types,
  578. for Nth root required digits_accuracy.
  579. */
  580. int roots_tables(cpp_bin_float_100 radius, cpp_bin_float_100 arc, double digits_accuracy)
  581. {
  582. ::digits_accuracy = digits_accuracy;
  583. // Save globally so that it is available to root-finding algorithms. Ugly :-(
  584. #if defined(_DEBUG) || !defined(NDEBUG)
  585. std::string debug_or_optimize("Compiled in debug mode.");
  586. #else
  587. std::string debug_or_optimize("Compiled in optimise mode.");
  588. #endif
  589. // Create filename for roots_table
  590. std::string qbk_name = full_roots_name;
  591. qbk_name += "elliptic_table";
  592. std::stringstream ss;
  593. ss.precision(3);
  594. // ss << "_" << N // now put all the tables in one .qbk file?
  595. ss << "_" << digits_accuracy * 100
  596. << std::flush;
  597. // Assume only save optimize mode runs, so don't add any _DEBUG info.
  598. qbk_name += ss.str();
  599. #ifdef _MSC_VER
  600. qbk_name += "_msvc";
  601. #else // assume GCC
  602. qbk_name += "_gcc";
  603. #endif
  604. if (fp_hardware != "")
  605. {
  606. qbk_name += fp_hardware;
  607. }
  608. qbk_name += ".qbk";
  609. fout.open(qbk_name, std::ios_base::out);
  610. if (fout.is_open())
  611. {
  612. std::cout << "Output root table to " << qbk_name << std::endl;
  613. }
  614. else
  615. { // Failed to open.
  616. std::cout << " Open file " << qbk_name << " for output failed!" << std::endl;
  617. std::cout << "errno " << errno << std::endl;
  618. return errno;
  619. }
  620. fout <<
  621. "[/"
  622. << qbk_name
  623. << "\n"
  624. "Copyright 2015 Paul A. Bristow.""\n"
  625. "Copyright 2015 John Maddock.""\n"
  626. "Distributed under the Boost Software License, Version 1.0.""\n"
  627. "(See accompanying file LICENSE_1_0.txt or copy at""\n"
  628. "http://www.boost.org/LICENSE_1_0.txt).""\n"
  629. "]""\n"
  630. << std::endl;
  631. // Print out the program/compiler/stdlib/platform names as a Quickbook comment:
  632. fout << "\n[h6 Program [@../../example/" << short_file_name(sourcefilename) << " " << short_file_name(sourcefilename) << "],\n "
  633. << BOOST_COMPILER << ", "
  634. << BOOST_STDLIB << ", "
  635. << BOOST_PLATFORM << "\n"
  636. << debug_or_optimize
  637. << ((fp_hardware != "") ? ", " + fp_hardware : "")
  638. << "]" // [h6 close].
  639. << std::endl;
  640. //fout << "Fraction of full accuracy " << digits_accuracy << std::endl;
  641. table_root_info(radius, arc);
  642. fout.close();
  643. // table_type_info(digits_accuracy);
  644. return 0;
  645. } // roots_tables
  646. int main()
  647. {
  648. using namespace boost::multiprecision;
  649. using namespace boost::math;
  650. try
  651. {
  652. std::cout << "Tests run with " << BOOST_COMPILER << ", "
  653. << BOOST_STDLIB << ", " << BOOST_PLATFORM << ", ";
  654. // How to: Configure Visual C++ Projects to Target 64-Bit Platforms
  655. // https://msdn.microsoft.com/en-us/library/9yb4317s.aspx
  656. #ifdef _M_X64 // Defined for compilations that target x64 processors.
  657. std::cout << "X64 " << std::endl;
  658. fp_hardware += "_X64";
  659. #else
  660. # ifdef _M_IX86
  661. std::cout << "X32 " << std::endl;
  662. fp_hardware += "_X86";
  663. # endif
  664. #endif
  665. #ifdef _M_AMD64
  666. std::cout << "AMD64 " << std::endl;
  667. // fp_hardware += "_AMD64";
  668. #endif
  669. // https://msdn.microsoft.com/en-us/library/7t5yh4fd.aspx
  670. // /arch (x86) options /arch:[IA32|SSE|SSE2|AVX|AVX2]
  671. // default is to use SSE and SSE2 instructions by default.
  672. // https://msdn.microsoft.com/en-us/library/jj620901.aspx
  673. // /arch (x64) options /arch:AVX and /arch:AVX2
  674. // MSVC doesn't bother to set these SSE macros!
  675. // http://stackoverflow.com/questions/18563978/sse-sse2-is-enabled-control-in-visual-studio
  676. // https://msdn.microsoft.com/en-us/library/b0084kay.aspx predefined macros.
  677. // But some of these macros are *not* defined by MSVC,
  678. // unlike AVX (but *are* defined by GCC and Clang).
  679. // So the macro code above does define them.
  680. #if (defined(_M_AMD64) || defined (_M_X64))
  681. # define _M_X64
  682. # define __SSE2__
  683. #else
  684. # ifdef _M_IX86_FP // Expands to an integer literal value indicating which /arch compiler option was used:
  685. std::cout << "Floating-point _M_IX86_FP = " << _M_IX86_FP << std::endl;
  686. # if (_M_IX86_FP == 2) // 2 if /arch:SSE2, /arch:AVX or /arch:AVX2
  687. # define __SSE2__ // x32
  688. # elif (_M_IX86_FP == 1) // 1 if /arch:SSE was used.
  689. # define __SSE__ // x32
  690. # elif (_M_IX86_FP == 0) // 0 if /arch:IA32 was used.
  691. # define _X32 // No special FP instructions.
  692. # endif
  693. # endif
  694. #endif
  695. // Set the fp_hardware that is used in the .qbk filename.
  696. #ifdef __AVX2__
  697. std::cout << "Floating-point AVX2 " << std::endl;
  698. fp_hardware += "_AVX2";
  699. # else
  700. # ifdef __AVX__
  701. std::cout << "Floating-point AVX " << std::endl;
  702. fp_hardware += "_AVX";
  703. # else
  704. # ifdef __SSE2__
  705. std::cout << "Floating-point SSE2 " << std::endl;
  706. fp_hardware += "_SSE2";
  707. # else
  708. # ifdef __SSE__
  709. std::cout << "Floating-point SSE " << std::endl;
  710. fp_hardware += "_SSE";
  711. # endif
  712. # endif
  713. # endif
  714. # endif
  715. #ifdef _M_IX86
  716. std::cout << "Floating-point X86 _M_IX86 = " << _M_IX86 << std::endl;
  717. // https://msdn.microsoft.com/en-us/library/aa273918%28v=vs.60%29.aspx#_predir_table_1..3
  718. // 600 = Pentium Pro
  719. #endif
  720. #ifdef _MSC_FULL_VER
  721. std::cout << "Floating-point _MSC_FULL_VER " << _MSC_FULL_VER << std::endl;
  722. #endif
  723. #ifdef __MSVC_RUNTIME_CHECKS
  724. std::cout << "Runtime __MSVC_RUNTIME_CHECKS " << std::endl;
  725. #endif
  726. BOOST_MATH_CONTROL_FP;
  727. cpp_bin_float_100 radius("28.");
  728. cpp_bin_float_100 arc("300.");
  729. // Compute full answer to more than precision of tests.
  730. //T value = 28.; // integer (exactly representable as floating-point)
  731. // whose cube root is *not* exactly representable.
  732. // Wolfram Alpha command N[28 ^ (1 / 3), 100] computes cube root to 100 decimal digits.
  733. // 3.036588971875662519420809578505669635581453977248111123242141654169177268411884961770250390838097895
  734. std::cout.precision(100);
  735. std::cout << "radius 1" << radius << std::endl;
  736. std::cout << "arc length" << arc << std::endl;
  737. // std::cout << ",\n""answer = " << full_answer << std::endl;
  738. std::cout.precision(6);
  739. // cbrt cpp_bin_float_100 full_answer("3.036588971875662519420809578505669635581453977248111123242141654169177268411884961770250390838097895");
  740. // Output the table of types, maxdigits10 and digits and required digits for some accuracies.
  741. // Output tables for some roots at full accuracy.
  742. roots_tables(radius, arc, 1.);
  743. // Output tables for some roots at less accuracy.
  744. //roots_tables(full_value, 0.75);
  745. return boost::exit_success;
  746. }
  747. catch (std::exception const& ex)
  748. {
  749. std::cout << "exception thrown: " << ex.what() << std::endl;
  750. return boost::exit_failure;
  751. }
  752. } // int main()
  753. /*
  754. */