// Copyright Matthew Pulver 2018 - 2019. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_MATH_TEST_AUTODIFF_HPP #define BOOST_MATH_TEST_AUTODIFF_HPP #ifndef BOOST_TEST_MODULE #define BOOST_TEST_MODULE test_autodiff #endif #ifndef BOOST_ALLOW_DEPRECATED_HEADERS #define BOOST_ALLOW_DEPRECATED_HEADERS // artifact of sp_typeinfo.hpp inclusion from unit_test.hpp #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mp11 = boost::mp11; namespace bmp = boost::multiprecision; #if defined(BOOST_USE_VALGRIND) || defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) using bin_float_types = mp11::mp_list; #else using bin_float_types = mp11::mp_list; #endif // cpp_dec_float_50 cannot be used with close_at_tolerance /*using multiprecision_float_types = mp_list;*/ #if !defined(BOOST_VERSION) || BOOST_VERSION < 107000 || defined(BOOST_USE_VALGRIND) || defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) || defined(BOOST_NO_STRESS_TEST) using multiprecision_float_types = mp11::mp_list<>; #else #define BOOST_AUTODIFF_TESTING_INCLUDE_MULTIPRECISION using multiprecision_float_types = mp11::mp_list; #endif using all_float_types = mp11::mp_append; using namespace boost::math::differentiation; namespace test_detail { template using is_multiprecision_t = mp11::mp_or, bmp::is_number_expression>; template using if_c = mp11::mp_eval_if_c; template using if_t = if_c; /** * Simple struct to hold constants that are used in each test * since BOOST_AUTO_TEST_CASE_TEMPLATE doesn't support fixtures. */ template struct test_constants_t { static constexpr auto n_samples = if_t, bmp::is_number_expression>, mp11::mp_int<10>, mp11::mp_int<25>>::value; static constexpr auto order = OrderValue; static constexpr T pct_epsilon() BOOST_NOEXCEPT { return (is_multiprecision_t::value ? 2 : 1) * std::numeric_limits::epsilon() * 100; } }; /** * struct to emit pseudo-random values from a given interval. * Endpoints are closed or open depending on whether or not they're infinite). */ template struct RandomSample { using numeric_limits_t = std::numeric_limits; using is_integer_t = mp11::mp_bool::is_integer>; using distribution_param_t = if_t< is_multiprecision_t, if_t, long double>, T>; static_assert((std::numeric_limits::is_integer && std::numeric_limits::is_integer) || (!std::numeric_limits::is_integer && !std::numeric_limits::is_integer), "T and distribution_param_t must either both be integral or " "both be not integral"); using dist_t = if_t, std::uniform_real_distribution>; struct get_integral_endpoint { template constexpr distribution_param_t operator()(V finish) const noexcept { return static_cast(finish); } }; struct get_real_endpoint { template constexpr distribution_param_t operator()(V finish) const noexcept { return std::nextafter(static_cast(finish), (std::numeric_limits::max)()); } }; using get_endpoint_t = if_t; template RandomSample(U start, V finish) : rng_(std::random_device{}()), dist_(static_cast(start), get_endpoint_t{}(finish)) {} T next() noexcept { return static_cast(dist_(rng_)); } T normalize(const T& x) noexcept { return x / ((dist_.max)() - (dist_.min)()); } std::mt19937 rng_; dist_t dist_; }; static_assert(std::is_same::dist_t, std::uniform_real_distribution>::value, ""); static_assert(std::is_same::dist_t, std::uniform_int_distribution>::value, ""); static_assert(std::is_same::dist_t, std::uniform_int_distribution>::value, ""); static_assert(std::is_same::dist_t, std::uniform_real_distribution>::value, ""); } // namespace test_detail template auto isNearZero(const T& t) noexcept -> typename std::enable_if::value, bool>::type { using std::sqrt; using bmp::sqrt; using detail::sqrt; using std::fabs; using bmp::fabs; using detail::fabs; using boost::math::fpclassify; using std::sqrt; return fpclassify(fabs(t)) == FP_ZERO || fpclassify(fabs(t)) == FP_SUBNORMAL || boost::math::fpc::is_small(fabs(t), sqrt(std::numeric_limits::epsilon())); } template auto isNearZero(const T& t) noexcept -> typename std::enable_if::value, bool>::type { using root_type = typename T::root_type; return isNearZero(static_cast(t)); } template using test_constants_t = test_detail::test_constants_t; template promote mixed_partials_f(const W& w, const X& x, const Y& y, const Z& z) { return exp(w * sin(x * log(y) / z) + sqrt(w * z / (x * y))) + w * w / tan(z); } // Equations and function/variable names are from // https://en.wikipedia.org/wiki/Greeks_(finance)#Formulas_for_European_option_Greeks // // Standard normal probability density function template T phi(const T& x) { return boost::math::constants::one_div_root_two_pi() * exp(-0.5 * x * x); } // Standard normal cumulative distribution function template T Phi(const T& x) { return 0.5 * erfc(-boost::math::constants::one_div_root_two() * x); } enum class CP { call, put }; // Assume zero annual dividend yield (q=0). template promote black_scholes_option_price(CP cp, double K, const Price& S, const Sigma& sigma, const Tau& tau, const Rate& r) { const auto d1 = (log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau)); const auto d2 = (log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau)); if (cp == CP::call) { return S * Phi(d1) - exp(-r * tau) * K * Phi(d2); } return exp(-r * tau) * K * Phi(-d2) - S * Phi(-d1); } template T uncast_return(const T& x) { return x == 0 ? 0 : 1; } #endif // BOOST_MATH_TEST_AUTODIFF_HPP