test.hpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // (C) Copyright John Maddock 2006.
  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. #ifndef BOOST_MATH_TOOLS_TEST_HPP
  6. #define BOOST_MATH_TOOLS_TEST_HPP
  7. #ifdef _MSC_VER
  8. #pragma once
  9. #endif
  10. #include <boost/math/tools/config.hpp>
  11. #include <boost/math/tools/stats.hpp>
  12. #include <boost/math/special_functions/fpclassify.hpp>
  13. #include <boost/test/test_tools.hpp>
  14. #include <stdexcept>
  15. #include <iostream>
  16. #include <iomanip>
  17. namespace boost{ namespace math{ namespace tools{
  18. template <class T>
  19. struct test_result
  20. {
  21. private:
  22. boost::math::tools::stats<T> stat; // Statistics for the test.
  23. unsigned worst_case; // Index of the worst case test.
  24. public:
  25. test_result() { worst_case = 0; }
  26. void set_worst(int i){ worst_case = i; }
  27. void add(const T& point){ stat.add(point); }
  28. // accessors:
  29. unsigned worst()const{ return worst_case; }
  30. T min BOOST_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.min)(); }
  31. T max BOOST_PREVENT_MACRO_SUBSTITUTION()const{ return (stat.max)(); }
  32. T total()const{ return stat.total(); }
  33. T mean()const{ return stat.mean(); }
  34. boost::uintmax_t count()const{ return stat.count(); }
  35. T variance()const{ return stat.variance(); }
  36. T variance1()const{ return stat.variance1(); }
  37. T rms()const{ return stat.rms(); }
  38. test_result& operator+=(const test_result& t)
  39. {
  40. if((t.stat.max)() > (stat.max)())
  41. worst_case = t.worst_case;
  42. stat += t.stat;
  43. return *this;
  44. }
  45. };
  46. template <class T>
  47. struct calculate_result_type
  48. {
  49. typedef typename T::value_type row_type;
  50. typedef typename row_type::value_type value_type;
  51. };
  52. template <class T>
  53. T relative_error(T a, T b)
  54. {
  55. BOOST_MATH_STD_USING
  56. #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  57. //
  58. // If math.h has no long double support we can't rely
  59. // on the math functions generating exponents outside
  60. // the range of a double:
  61. //
  62. T min_val = (std::max)(
  63. tools::min_value<T>(),
  64. static_cast<T>((std::numeric_limits<double>::min)()));
  65. T max_val = (std::min)(
  66. tools::max_value<T>(),
  67. static_cast<T>((std::numeric_limits<double>::max)()));
  68. #else
  69. T min_val = tools::min_value<T>();
  70. T max_val = tools::max_value<T>();
  71. #endif
  72. if((a != 0) && (b != 0))
  73. {
  74. // TODO: use isfinite:
  75. if(fabs(b) >= max_val)
  76. {
  77. if(fabs(a) >= max_val)
  78. return 0; // one infinity is as good as another!
  79. }
  80. // If the result is denormalised, treat all denorms as equivalent:
  81. if((a < min_val) && (a > 0))
  82. a = min_val;
  83. else if((a > -min_val) && (a < 0))
  84. a = -min_val;
  85. if((b < min_val) && (b > 0))
  86. b = min_val;
  87. else if((b > -min_val) && (b < 0))
  88. b = -min_val;
  89. return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
  90. }
  91. // Handle special case where one or both are zero:
  92. if(min_val == 0)
  93. return fabs(a-b);
  94. if(fabs(a) < min_val)
  95. a = min_val;
  96. if(fabs(b) < min_val)
  97. b = min_val;
  98. return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
  99. }
  100. #if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
  101. template <>
  102. inline double relative_error<double>(double a, double b)
  103. {
  104. BOOST_MATH_STD_USING
  105. //
  106. // On Mac OS X we evaluate "double" functions at "long double" precision,
  107. // but "long double" actually has a very slightly narrower range than "double"!
  108. // Therefore use the range of "long double" as our limits since results outside
  109. // that range may have been truncated to 0 or INF:
  110. //
  111. double min_val = (std::max)((double)tools::min_value<long double>(), tools::min_value<double>());
  112. double max_val = (std::min)((double)tools::max_value<long double>(), tools::max_value<double>());
  113. if((a != 0) && (b != 0))
  114. {
  115. // TODO: use isfinite:
  116. if(b > max_val)
  117. {
  118. if(a > max_val)
  119. return 0; // one infinity is as good as another!
  120. }
  121. // If the result is denormalised, treat all denorms as equivalent:
  122. if((a < min_val) && (a > 0))
  123. a = min_val;
  124. else if((a > -min_val) && (a < 0))
  125. a = -min_val;
  126. if((b < min_val) && (b > 0))
  127. b = min_val;
  128. else if((b > -min_val) && (b < 0))
  129. b = -min_val;
  130. return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
  131. }
  132. // Handle special case where one or both are zero:
  133. if(min_val == 0)
  134. return fabs(a-b);
  135. if(fabs(a) < min_val)
  136. a = min_val;
  137. if(fabs(b) < min_val)
  138. b = min_val;
  139. return (std::max)(fabs((a-b)/a), fabs((a-b)/b));
  140. }
  141. #endif
  142. template <class T>
  143. void set_output_precision(T)
  144. {
  145. #ifdef BOOST_MSVC
  146. #pragma warning(push)
  147. #pragma warning(disable:4127)
  148. #endif
  149. if(std::numeric_limits<T>::digits10)
  150. {
  151. std::cout << std::setprecision(std::numeric_limits<T>::digits10 + 2);
  152. }
  153. #ifdef BOOST_MSVC
  154. #pragma warning(pop)
  155. #endif
  156. }
  157. template <class Seq>
  158. void print_row(const Seq& row)
  159. {
  160. set_output_precision(row[0]);
  161. for(unsigned i = 0; i < row.size(); ++i)
  162. {
  163. if(i)
  164. std::cout << ", ";
  165. std::cout << row[i];
  166. }
  167. std::cout << std::endl;
  168. }
  169. //
  170. // Function test accepts an matrix of input values (probably a 2D boost::array)
  171. // and calls two functors for each row in the array - one calculates a value
  172. // to test, and one extracts the expected value from the array (or possibly
  173. // calculates it at high precision). The two functors are usually simple lambda
  174. // expressions.
  175. //
  176. template <class A, class F1, class F2>
  177. test_result<typename calculate_result_type<A>::value_type> test(const A& a, F1 test_func, F2 expect_func)
  178. {
  179. typedef typename A::value_type row_type;
  180. typedef typename row_type::value_type value_type;
  181. test_result<value_type> result;
  182. for(unsigned i = 0; i < a.size(); ++i)
  183. {
  184. const row_type& row = a[i];
  185. value_type point;
  186. try
  187. {
  188. point = test_func(row);
  189. }
  190. catch(const std::underflow_error&)
  191. {
  192. point = 0;
  193. }
  194. catch(const std::overflow_error&)
  195. {
  196. point = std::numeric_limits<value_type>::has_infinity ?
  197. std::numeric_limits<value_type>::infinity()
  198. : tools::max_value<value_type>();
  199. }
  200. catch(const std::exception& e)
  201. {
  202. std::cerr << e.what() << std::endl;
  203. print_row(row);
  204. BOOST_ERROR("Unexpected exception.");
  205. // so we don't get further errors:
  206. point = expect_func(row);
  207. }
  208. value_type expected = expect_func(row);
  209. value_type err = relative_error(point, expected);
  210. #ifdef BOOST_INSTRUMENT
  211. if(err != 0)
  212. {
  213. std::cout << row[0] << " " << err;
  214. if(std::numeric_limits<value_type>::is_specialized)
  215. {
  216. std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)";
  217. }
  218. std::cout << std::endl;
  219. }
  220. #endif
  221. if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected))
  222. {
  223. std::cout << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n";
  224. std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
  225. print_row(row);
  226. BOOST_ERROR("Unexpected non-finite result");
  227. }
  228. if(err > 0.5)
  229. {
  230. std::cout << "CAUTION: Gross error found at entry " << i << ".\n";
  231. std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
  232. print_row(row);
  233. BOOST_ERROR("Gross error");
  234. }
  235. result.add(err);
  236. if((result.max)() == err)
  237. result.set_worst(i);
  238. }
  239. return result;
  240. }
  241. template <class Real, class A, class F1, class F2>
  242. test_result<Real> test_hetero(const A& a, F1 test_func, F2 expect_func)
  243. {
  244. typedef typename A::value_type row_type;
  245. typedef Real value_type;
  246. test_result<value_type> result;
  247. for(unsigned i = 0; i < a.size(); ++i)
  248. {
  249. const row_type& row = a[i];
  250. value_type point;
  251. try
  252. {
  253. point = test_func(row);
  254. }
  255. catch(const std::underflow_error&)
  256. {
  257. point = 0;
  258. }
  259. catch(const std::overflow_error&)
  260. {
  261. point = std::numeric_limits<value_type>::has_infinity ?
  262. std::numeric_limits<value_type>::infinity()
  263. : tools::max_value<value_type>();
  264. }
  265. catch(const std::exception& e)
  266. {
  267. std::cerr << e.what() << std::endl;
  268. print_row(row);
  269. BOOST_ERROR("Unexpected exception.");
  270. // so we don't get further errors:
  271. point = expect_func(row);
  272. }
  273. value_type expected = expect_func(row);
  274. value_type err = relative_error(point, expected);
  275. #ifdef BOOST_INSTRUMENT
  276. if(err != 0)
  277. {
  278. std::cout << row[0] << " " << err;
  279. if(std::numeric_limits<value_type>::is_specialized)
  280. {
  281. std::cout << " (" << err / std::numeric_limits<value_type>::epsilon() << "eps)";
  282. }
  283. std::cout << std::endl;
  284. }
  285. #endif
  286. if(!(boost::math::isfinite)(point) && (boost::math::isfinite)(expected))
  287. {
  288. std::cout << "CAUTION: Found non-finite result, when a finite value was expected at entry " << i << "\n";
  289. std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
  290. print_row(row);
  291. BOOST_ERROR("Unexpected non-finite result");
  292. }
  293. if(err > 0.5)
  294. {
  295. std::cout << "CAUTION: Gross error found at entry " << i << ".\n";
  296. std::cout << "Found: " << point << " Expected " << expected << " Error: " << err << std::endl;
  297. print_row(row);
  298. BOOST_ERROR("Gross error");
  299. }
  300. result.add(err);
  301. if((result.max)() == err)
  302. result.set_worst(i);
  303. }
  304. return result;
  305. }
  306. } // namespace tools
  307. } // namespace math
  308. } // namespace boost
  309. #endif