test_autodiff.hpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright Matthew Pulver 2018 - 2019.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // https://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_TEST_AUTODIFF_HPP
  6. #define BOOST_MATH_TEST_AUTODIFF_HPP
  7. #ifndef BOOST_TEST_MODULE
  8. #define BOOST_TEST_MODULE test_autodiff
  9. #endif
  10. #ifndef BOOST_ALLOW_DEPRECATED_HEADERS
  11. #define BOOST_ALLOW_DEPRECATED_HEADERS // artifact of sp_typeinfo.hpp inclusion from unit_test.hpp
  12. #endif
  13. #include <boost/math/tools/config.hpp>
  14. #include <boost/math/differentiation/autodiff.hpp>
  15. #include <boost/multiprecision/cpp_bin_float.hpp>
  16. #include <boost/multiprecision/cpp_dec_float.hpp>
  17. #include <boost/mp11/function.hpp>
  18. #include <boost/mp11/integral.hpp>
  19. #include <boost/mp11/list.hpp>
  20. #include <boost/mp11/utility.hpp>
  21. #include <boost/range/irange.hpp>
  22. #include <boost/test/included/unit_test.hpp>
  23. #include <algorithm>
  24. #include <cfenv>
  25. #include <cstdlib>
  26. #include <random>
  27. namespace mp11 = boost::mp11;
  28. namespace bmp = boost::multiprecision;
  29. #if defined(BOOST_USE_VALGRIND) || defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
  30. using bin_float_types = mp11::mp_list<float>;
  31. #else
  32. using bin_float_types = mp11::mp_list<float, double, long double>;
  33. #endif
  34. // cpp_dec_float_50 cannot be used with close_at_tolerance
  35. /*using multiprecision_float_types =
  36. mp_list<bmp::cpp_dec_float_50, bmp::cpp_bin_float_50>;*/
  37. #if !defined(BOOST_VERSION) || BOOST_VERSION < 107000 || defined(BOOST_USE_VALGRIND) || defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) || defined(BOOST_NO_STRESS_TEST)
  38. using multiprecision_float_types = mp11::mp_list<>;
  39. #else
  40. #define BOOST_AUTODIFF_TESTING_INCLUDE_MULTIPRECISION
  41. using multiprecision_float_types = mp11::mp_list<bmp::cpp_bin_float_50>;
  42. #endif
  43. using all_float_types = mp11::mp_append<bin_float_types, multiprecision_float_types>;
  44. using namespace boost::math::differentiation;
  45. namespace test_detail {
  46. template <typename T>
  47. using is_multiprecision_t =
  48. mp11::mp_or<bmp::is_number<T>, bmp::is_number_expression<T>>;
  49. template<bool IfValue, typename ThenType, typename ElseType>
  50. using if_c = mp11::mp_eval_if_c<IfValue, ThenType, mp11::mp_identity_t, ElseType>;
  51. template<typename IfType, typename ThenType, typename ElseType>
  52. using if_t = if_c<IfType::value, ThenType, ElseType>;
  53. /**
  54. * Simple struct to hold constants that are used in each test
  55. * since BOOST_AUTO_TEST_CASE_TEMPLATE doesn't support fixtures.
  56. */
  57. template <typename T, std::size_t OrderValue>
  58. struct test_constants_t {
  59. static constexpr auto n_samples = if_t<mp11::mp_or<bmp::is_number<T>, bmp::is_number_expression<T>>, mp11::mp_int<10>, mp11::mp_int<25>>::value;
  60. static constexpr auto order = OrderValue;
  61. static constexpr T pct_epsilon() BOOST_NOEXCEPT {
  62. return (is_multiprecision_t<T>::value ? 2 : 1) * std::numeric_limits<T>::epsilon() * 100;
  63. }
  64. };
  65. /**
  66. * struct to emit pseudo-random values from a given interval.
  67. * Endpoints are closed or open depending on whether or not they're infinite).
  68. */
  69. template <typename T>
  70. struct RandomSample {
  71. using numeric_limits_t = std::numeric_limits<T>;
  72. using is_integer_t = mp11::mp_bool<std::numeric_limits<T>::is_integer>;
  73. using distribution_param_t = if_t<
  74. is_multiprecision_t<T>,
  75. if_t<is_integer_t,
  76. if_c<numeric_limits_t::is_signed, int64_t, uint64_t>,
  77. long double>,
  78. T>;
  79. static_assert((std::numeric_limits<T>::is_integer &&
  80. std::numeric_limits<distribution_param_t>::is_integer) ||
  81. (!std::numeric_limits<T>::is_integer &&
  82. !std::numeric_limits<distribution_param_t>::is_integer),
  83. "T and distribution_param_t must either both be integral or "
  84. "both be not integral");
  85. using dist_t = if_t<is_integer_t,
  86. std::uniform_int_distribution<distribution_param_t>,
  87. std::uniform_real_distribution<distribution_param_t>>;
  88. struct get_integral_endpoint {
  89. template <typename V>
  90. constexpr distribution_param_t operator()(V finish) const noexcept {
  91. return static_cast<distribution_param_t>(finish);
  92. }
  93. };
  94. struct get_real_endpoint {
  95. template <typename V>
  96. constexpr distribution_param_t operator()(V finish) const noexcept {
  97. return std::nextafter(static_cast<distribution_param_t>(finish),
  98. (std::numeric_limits<distribution_param_t>::max)());
  99. }
  100. };
  101. using get_endpoint_t = if_t<is_integer_t, get_integral_endpoint, get_real_endpoint>;
  102. template <typename U, typename V>
  103. RandomSample(U start, V finish)
  104. : rng_(std::random_device{}()),
  105. dist_(static_cast<distribution_param_t>(start),
  106. get_endpoint_t{}(finish)) {}
  107. T next() noexcept { return static_cast<T>(dist_(rng_)); }
  108. T normalize(const T& x) noexcept {
  109. return x / ((dist_.max)() - (dist_.min)());
  110. }
  111. std::mt19937 rng_;
  112. dist_t dist_;
  113. };
  114. static_assert(std::is_same<RandomSample<float>::dist_t,
  115. std::uniform_real_distribution<float>>::value,
  116. "");
  117. static_assert(std::is_same<RandomSample<int64_t>::dist_t,
  118. std::uniform_int_distribution<int64_t>>::value,
  119. "");
  120. static_assert(std::is_same<RandomSample<bmp::uint512_t>::dist_t,
  121. std::uniform_int_distribution<uint64_t>>::value,
  122. "");
  123. static_assert(std::is_same<RandomSample<bmp::cpp_bin_float_50>::dist_t,
  124. std::uniform_real_distribution<long double>>::value,
  125. "");
  126. } // namespace test_detail
  127. template<typename T>
  128. auto isNearZero(const T& t) noexcept -> typename std::enable_if<!detail::is_fvar<T>::value, bool>::type
  129. {
  130. using std::sqrt;
  131. using bmp::sqrt;
  132. using detail::sqrt;
  133. using std::fabs;
  134. using bmp::fabs;
  135. using detail::fabs;
  136. using boost::math::fpclassify;
  137. using std::sqrt;
  138. return fpclassify(fabs(t)) == FP_ZERO || fpclassify(fabs(t)) == FP_SUBNORMAL || boost::math::fpc::is_small(fabs(t), sqrt(std::numeric_limits<T>::epsilon()));
  139. }
  140. template<typename T>
  141. auto isNearZero(const T& t) noexcept -> typename std::enable_if<detail::is_fvar<T>::value, bool>::type
  142. {
  143. using root_type = typename T::root_type;
  144. return isNearZero(static_cast<root_type>(t));
  145. }
  146. template <typename T, std::size_t Order = 5>
  147. using test_constants_t = test_detail::test_constants_t<T, Order>;
  148. template <typename W, typename X, typename Y, typename Z>
  149. promote<W, X, Y, Z> mixed_partials_f(const W& w, const X& x, const Y& y,
  150. const Z& z) {
  151. return exp(w * sin(x * log(y) / z) + sqrt(w * z / (x * y))) + w * w / tan(z);
  152. }
  153. // Equations and function/variable names are from
  154. // https://en.wikipedia.org/wiki/Greeks_(finance)#Formulas_for_European_option_Greeks
  155. //
  156. // Standard normal probability density function
  157. template <typename T>
  158. T phi(const T& x) {
  159. return boost::math::constants::one_div_root_two_pi<T>() * exp(-0.5 * x * x);
  160. }
  161. // Standard normal cumulative distribution function
  162. template <typename T>
  163. T Phi(const T& x) {
  164. return 0.5 * erfc(-boost::math::constants::one_div_root_two<T>() * x);
  165. }
  166. enum class CP { call, put };
  167. // Assume zero annual dividend yield (q=0).
  168. template <typename Price, typename Sigma, typename Tau, typename Rate>
  169. promote<Price, Sigma, Tau, Rate> black_scholes_option_price(CP cp, double K,
  170. const Price& S,
  171. const Sigma& sigma,
  172. const Tau& tau,
  173. const Rate& r) {
  174. const auto d1 =
  175. (log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
  176. const auto d2 =
  177. (log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
  178. if (cp == CP::call) {
  179. return S * Phi(d1) - exp(-r * tau) * K * Phi(d2);
  180. }
  181. return exp(-r * tau) * K * Phi(-d2) - S * Phi(-d1);
  182. }
  183. template <typename T>
  184. T uncast_return(const T& x) {
  185. return x == 0 ? 0 : 1;
  186. }
  187. #endif // BOOST_MATH_TEST_AUTODIFF_HPP