// 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_DIFFERENTIATION_AUTODIFF_HPP #define BOOST_MATH_DIFFERENTIATION_AUTODIFF_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace math { namespace differentiation { // Automatic Differentiation v1 inline namespace autodiff_v1 { namespace detail { template struct promote_args_n { using type = typename tools::promote_args_2::type>::type; }; template struct promote_args_n { using type = typename tools::promote_arg::type; }; } // namespace detail template using promote = typename detail::promote_args_n::type; namespace detail { template class fvar; template struct is_fvar_impl : std::false_type {}; template struct is_fvar_impl> : std::true_type {}; template using is_fvar = is_fvar_impl::type>; template struct nest_fvar { using type = fvar::type, Order>; }; template struct nest_fvar { using type = fvar; }; template struct get_depth_impl : std::integral_constant {}; template struct get_depth_impl> : std::integral_constant::value + 1> {}; template using get_depth = get_depth_impl::type>; template struct get_order_sum_t : std::integral_constant {}; template struct get_order_sum_t> : std::integral_constant::value + Order> {}; template using get_order_sum = get_order_sum_t::type>; template struct get_root_type { using type = RealType; }; template struct get_root_type> { using type = typename get_root_type::type; }; template struct type_at { using type = RealType; }; template struct type_at, Depth> { using type = typename std::conditional, typename type_at::type>::type; }; template using get_type_at = typename type_at::type; // Satisfies Boost's Conceptual Requirements for Real Number Types. // https://www.boost.org/libs/math/doc/html/math_toolkit/real_concepts.html template class fvar { protected: std::array v; public: using root_type = typename get_root_type::type; // RealType in the root fvar. fvar() = default; // Initialize a variable or constant. fvar(root_type const&, bool const is_variable); // RealType(cr) | RealType | RealType is copy constructible. fvar(fvar const&) = default; // Be aware of implicit casting from one fvar<> type to another by this copy constructor. template fvar(fvar const&); // RealType(ca) | RealType | RealType is copy constructible from the arithmetic types. explicit fvar(root_type const&); // Initialize a constant. (No epsilon terms.) template fvar(RealType2 const& ca); // Supports any RealType2 for which static_cast(ca) compiles. // r = cr | RealType& | Assignment operator. fvar& operator=(fvar const&) = default; // r = ca | RealType& | Assignment operator from the arithmetic types. // Handled by constructor that takes a single parameter of generic type. // fvar& operator=(root_type const&); // Set a constant. // r += cr | RealType& | Adds cr to r. template fvar& operator+=(fvar const&); // r += ca | RealType& | Adds ar to r. fvar& operator+=(root_type const&); // r -= cr | RealType& | Subtracts cr from r. template fvar& operator-=(fvar const&); // r -= ca | RealType& | Subtracts ca from r. fvar& operator-=(root_type const&); // r *= cr | RealType& | Multiplies r by cr. template fvar& operator*=(fvar const&); // r *= ca | RealType& | Multiplies r by ca. fvar& operator*=(root_type const&); // r /= cr | RealType& | Divides r by cr. template fvar& operator/=(fvar const&); // r /= ca | RealType& | Divides r by ca. fvar& operator/=(root_type const&); // -r | RealType | Unary Negation. fvar operator-() const; // +r | RealType& | Identity Operation. fvar const& operator+() const; // cr + cr2 | RealType | Binary Addition template promote> operator+(fvar const&) const; // cr + ca | RealType | Binary Addition fvar operator+(root_type const&) const; // ca + cr | RealType | Binary Addition template friend fvar operator+(typename fvar::root_type const&, fvar const&); // cr - cr2 | RealType | Binary Subtraction template promote> operator-(fvar const&) const; // cr - ca | RealType | Binary Subtraction fvar operator-(root_type const&) const; // ca - cr | RealType | Binary Subtraction template friend fvar operator-(typename fvar::root_type const&, fvar const&); // cr * cr2 | RealType | Binary Multiplication template promote> operator*(fvar const&)const; // cr * ca | RealType | Binary Multiplication fvar operator*(root_type const&)const; // ca * cr | RealType | Binary Multiplication template friend fvar operator*(typename fvar::root_type const&, fvar const&); // cr / cr2 | RealType | Binary Subtraction template promote> operator/(fvar const&) const; // cr / ca | RealType | Binary Subtraction fvar operator/(root_type const&) const; // ca / cr | RealType | Binary Subtraction template friend fvar operator/(typename fvar::root_type const&, fvar const&); // For all comparison overloads, only the root term is compared. // cr == cr2 | bool | Equality Comparison template bool operator==(fvar const&) const; // cr == ca | bool | Equality Comparison bool operator==(root_type const&) const; // ca == cr | bool | Equality Comparison template friend bool operator==(typename fvar::root_type const&, fvar const&); // cr != cr2 | bool | Inequality Comparison template bool operator!=(fvar const&) const; // cr != ca | bool | Inequality Comparison bool operator!=(root_type const&) const; // ca != cr | bool | Inequality Comparison template friend bool operator!=(typename fvar::root_type const&, fvar const&); // cr <= cr2 | bool | Less than equal to. template bool operator<=(fvar const&) const; // cr <= ca | bool | Less than equal to. bool operator<=(root_type const&) const; // ca <= cr | bool | Less than equal to. template friend bool operator<=(typename fvar::root_type const&, fvar const&); // cr >= cr2 | bool | Greater than equal to. template bool operator>=(fvar const&) const; // cr >= ca | bool | Greater than equal to. bool operator>=(root_type const&) const; // ca >= cr | bool | Greater than equal to. template friend bool operator>=(typename fvar::root_type const&, fvar const&); // cr < cr2 | bool | Less than comparison. template bool operator<(fvar const&) const; // cr < ca | bool | Less than comparison. bool operator<(root_type const&) const; // ca < cr | bool | Less than comparison. template friend bool operator<(typename fvar::root_type const&, fvar const&); // cr > cr2 | bool | Greater than comparison. template bool operator>(fvar const&) const; // cr > ca | bool | Greater than comparison. bool operator>(root_type const&) const; // ca > cr | bool | Greater than comparison. template friend bool operator>(typename fvar::root_type const&, fvar const&); // Will throw std::out_of_range if Order < order. template get_type_at at(size_t order, Orders... orders) const; template get_type_at derivative(Orders... orders) const; const RealType& operator[](size_t) const; fvar inverse() const; // Multiplicative inverse. fvar& negate(); // Negate and return reference to *this. static constexpr size_t depth = get_depth::value; // Number of nested std::array. static constexpr size_t order_sum = get_order_sum::value; explicit operator root_type() const; // Must be explicit, otherwise overloaded operators are ambiguous. template ::type>::value>> explicit operator T() const; // Must be explicit; multiprecision has trouble without the std::enable_if fvar& set_root(root_type const&); // Apply coefficients using horner method. template promote, Fvar, Fvars...> apply_coefficients(size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const; template fvar apply_coefficients(size_t const order, Func const& f) const; // Use when function returns derivative(i)/factorial(i) and may have some infinite derivatives. template promote, Fvar, Fvars...> apply_coefficients_nonhorner(size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const; template fvar apply_coefficients_nonhorner(size_t const order, Func const& f) const; // Apply derivatives using horner method. template promote, Fvar, Fvars...> apply_derivatives(size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const; template fvar apply_derivatives(size_t const order, Func const& f) const; // Use when function returns derivative(i) and may have some infinite derivatives. template promote, Fvar, Fvars...> apply_derivatives_nonhorner(size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const; template fvar apply_derivatives_nonhorner(size_t const order, Func const& f) const; private: RealType epsilon_inner_product(size_t z0, size_t isum0, size_t m0, fvar const& cr, size_t z1, size_t isum1, size_t m1, size_t j) const; fvar epsilon_multiply(size_t z0, size_t isum0, fvar const& cr, size_t z1, size_t isum1) const; fvar epsilon_multiply(size_t z0, size_t isum0, root_type const& ca) const; fvar inverse_apply() const; fvar& multiply_assign_by_root_type(bool is_root, root_type const&); template friend class fvar; template friend std::ostream& operator<<(std::ostream&, fvar const&); // C++11 Compatibility #ifdef BOOST_NO_CXX17_IF_CONSTEXPR template void fvar_cpp11(std::true_type, RootType const& ca, bool const is_variable); template void fvar_cpp11(std::false_type, RootType const& ca, bool const is_variable); template get_type_at at_cpp11(std::true_type, size_t order, Orders... orders) const; template get_type_at at_cpp11(std::false_type, size_t order, Orders... orders) const; template fvar epsilon_multiply_cpp11(std::true_type, SizeType z0, size_t isum0, fvar const& cr, size_t z1, size_t isum1) const; template fvar epsilon_multiply_cpp11(std::false_type, SizeType z0, size_t isum0, fvar const& cr, size_t z1, size_t isum1) const; template fvar epsilon_multiply_cpp11(std::true_type, SizeType z0, size_t isum0, root_type const& ca) const; template fvar epsilon_multiply_cpp11(std::false_type, SizeType z0, size_t isum0, root_type const& ca) const; template fvar& multiply_assign_by_root_type_cpp11(std::true_type, bool is_root, RootType const& ca); template fvar& multiply_assign_by_root_type_cpp11(std::false_type, bool is_root, RootType const& ca); template fvar& negate_cpp11(std::true_type, RootType const&); template fvar& negate_cpp11(std::false_type, RootType const&); template fvar& set_root_cpp11(std::true_type, RootType const& root); template fvar& set_root_cpp11(std::false_type, RootType const& root); #endif }; // Standard Library Support Requirements // fabs(cr1) | RealType template fvar fabs(fvar const&); // abs(cr1) | RealType template fvar abs(fvar const&); // ceil(cr1) | RealType template fvar ceil(fvar const&); // floor(cr1) | RealType template fvar floor(fvar const&); // exp(cr1) | RealType template fvar exp(fvar const&); // pow(cr, ca) | RealType template fvar pow(fvar const&, typename fvar::root_type const&); // pow(ca, cr) | RealType template fvar pow(typename fvar::root_type const&, fvar const&); // pow(cr1, cr2) | RealType template promote, fvar> pow(fvar const&, fvar const&); // sqrt(cr1) | RealType template fvar sqrt(fvar const&); // log(cr1) | RealType template fvar log(fvar const&); // frexp(cr1, &i) | RealType template fvar frexp(fvar const&, int*); // ldexp(cr1, i) | RealType template fvar ldexp(fvar const&, int); // cos(cr1) | RealType template fvar cos(fvar const&); // sin(cr1) | RealType template fvar sin(fvar const&); // asin(cr1) | RealType template fvar asin(fvar const&); // tan(cr1) | RealType template fvar tan(fvar const&); // atan(cr1) | RealType template fvar atan(fvar const&); // atan2(cr, ca) | RealType template fvar atan2(fvar const&, typename fvar::root_type const&); // atan2(ca, cr) | RealType template fvar atan2(typename fvar::root_type const&, fvar const&); // atan2(cr1, cr2) | RealType template promote, fvar> atan2(fvar const&, fvar const&); // fmod(cr1,cr2) | RealType template promote, fvar> fmod(fvar const&, fvar const&); // round(cr1) | RealType template fvar round(fvar const&); // iround(cr1) | int template int iround(fvar const&); template long lround(fvar const&); template long long llround(fvar const&); // trunc(cr1) | RealType template fvar trunc(fvar const&); template long double truncl(fvar const&); // itrunc(cr1) | int template int itrunc(fvar const&); template long long lltrunc(fvar const&); // Additional functions template fvar acos(fvar const&); template fvar acosh(fvar const&); template fvar asinh(fvar const&); template fvar atanh(fvar const&); template fvar cosh(fvar const&); template fvar digamma(fvar const&); template fvar erf(fvar const&); template fvar erfc(fvar const&); template fvar lambert_w0(fvar const&); template fvar lgamma(fvar const&); template fvar sinc(fvar const&); template fvar sinh(fvar const&); template fvar tanh(fvar const&); template fvar tgamma(fvar const&); template struct zero : std::integral_constant {}; } // namespace detail template using autodiff_fvar = typename detail::nest_fvar::type; template autodiff_fvar make_fvar(RealType const& ca) { return autodiff_fvar(ca, true); } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR namespace detail { template auto make_fvar_for_tuple(std::index_sequence, RealType const& ca) { return make_fvar::value..., Order>(ca); } template auto make_ftuple_impl(std::index_sequence, RealTypes const&... ca) { return std::make_tuple(make_fvar_for_tuple(std::make_index_sequence{}, ca)...); } } // namespace detail template auto make_ftuple(RealTypes const&... ca) { static_assert(sizeof...(Orders) == sizeof...(RealTypes), "Number of Orders must match number of function parameters."); return detail::make_ftuple_impl(std::index_sequence_for{}, ca...); } #endif namespace detail { #ifndef BOOST_NO_CXX17_IF_CONSTEXPR template fvar::fvar(root_type const& ca, bool const is_variable) { if constexpr (is_fvar::value) { v.front() = RealType(ca, is_variable); if constexpr (0 < Order) std::fill(v.begin() + 1, v.end(), static_cast(0)); } else { v.front() = ca; if constexpr (0 < Order) v[1] = static_cast(static_cast(is_variable)); if constexpr (1 < Order) std::fill(v.begin() + 2, v.end(), static_cast(0)); } } #endif template template fvar::fvar(fvar const& cr) { for (size_t i = 0; i <= (std::min)(Order, Order2); ++i) v[i] = static_cast(cr.v[i]); BOOST_IF_CONSTEXPR (Order2 < Order) std::fill(v.begin() + (Order2 + 1), v.end(), static_cast(0)); } template fvar::fvar(root_type const& ca) : v{{static_cast(ca)}} {} // Can cause compiler error if RealType2 cannot be cast to root_type. template template fvar::fvar(RealType2 const& ca) : v{{static_cast(ca)}} {} /* template fvar& fvar::operator=(root_type const& ca) { v.front() = static_cast(ca); if constexpr (0 < Order) std::fill(v.begin()+1, v.end(), static_cast(0)); return *this; } */ template template fvar& fvar::operator+=(fvar const& cr) { for (size_t i = 0; i <= (std::min)(Order, Order2); ++i) v[i] += cr.v[i]; return *this; } template fvar& fvar::operator+=(root_type const& ca) { v.front() += ca; return *this; } template template fvar& fvar::operator-=(fvar const& cr) { for (size_t i = 0; i <= Order; ++i) v[i] -= cr.v[i]; return *this; } template fvar& fvar::operator-=(root_type const& ca) { v.front() -= ca; return *this; } template template fvar& fvar::operator*=(fvar const& cr) { using diff_t = typename std::array::difference_type; promote const zero(0); BOOST_IF_CONSTEXPR (Order <= Order2) for (size_t i = 0, j = Order; i <= Order; ++i, --j) v[j] = std::inner_product(v.cbegin(), v.cend() - diff_t(i), cr.v.crbegin() + diff_t(i), zero); else { for (size_t i = 0, j = Order; i <= Order - Order2; ++i, --j) v[j] = std::inner_product(cr.v.cbegin(), cr.v.cend(), v.crbegin() + diff_t(i), zero); for (size_t i = Order - Order2 + 1, j = Order2 - 1; i <= Order; ++i, --j) v[j] = std::inner_product(cr.v.cbegin(), cr.v.cbegin() + diff_t(j + 1), v.crbegin() + diff_t(i), zero); } return *this; } template fvar& fvar::operator*=(root_type const& ca) { return multiply_assign_by_root_type(true, ca); } template template fvar& fvar::operator/=(fvar const& cr) { using diff_t = typename std::array::difference_type; RealType const zero(0); v.front() /= cr.v.front(); BOOST_IF_CONSTEXPR (Order < Order2) for (size_t i = 1, j = Order2 - 1, k = Order; i <= Order; ++i, --j, --k) (v[i] -= std::inner_product( cr.v.cbegin() + 1, cr.v.cend() - diff_t(j), v.crbegin() + diff_t(k), zero)) /= cr.v.front(); else BOOST_IF_CONSTEXPR (0 < Order2) for (size_t i = 1, j = Order2 - 1, k = Order; i <= Order; ++i, j && --j, --k) (v[i] -= std::inner_product( cr.v.cbegin() + 1, cr.v.cend() - diff_t(j), v.crbegin() + diff_t(k), zero)) /= cr.v.front(); else for (size_t i = 1; i <= Order; ++i) v[i] /= cr.v.front(); return *this; } template fvar& fvar::operator/=(root_type const& ca) { std::for_each(v.begin(), v.end(), [&ca](RealType& x) { x /= ca; }); return *this; } template fvar fvar::operator-() const { fvar retval(*this); retval.negate(); return retval; } template fvar const& fvar::operator+() const { return *this; } template template promote, fvar> fvar::operator+( fvar const& cr) const { promote, fvar> retval; for (size_t i = 0; i <= (std::min)(Order, Order2); ++i) retval.v[i] = v[i] + cr.v[i]; BOOST_IF_CONSTEXPR (Order < Order2) for (size_t i = Order + 1; i <= Order2; ++i) retval.v[i] = cr.v[i]; else BOOST_IF_CONSTEXPR (Order2 < Order) for (size_t i = Order2 + 1; i <= Order; ++i) retval.v[i] = v[i]; return retval; } template fvar fvar::operator+(root_type const& ca) const { fvar retval(*this); retval.v.front() += ca; return retval; } template fvar operator+(typename fvar::root_type const& ca, fvar const& cr) { return cr + ca; } template template promote, fvar> fvar::operator-( fvar const& cr) const { promote, fvar> retval; for (size_t i = 0; i <= (std::min)(Order, Order2); ++i) retval.v[i] = v[i] - cr.v[i]; BOOST_IF_CONSTEXPR (Order < Order2) for (auto i = Order + 1; i <= Order2; ++i) retval.v[i] = -cr.v[i]; else BOOST_IF_CONSTEXPR (Order2 < Order) for (auto i = Order2 + 1; i <= Order; ++i) retval.v[i] = v[i]; return retval; } template fvar fvar::operator-(root_type const& ca) const { fvar retval(*this); retval.v.front() -= ca; return retval; } template fvar operator-(typename fvar::root_type const& ca, fvar const& cr) { fvar mcr = -cr; // Has same address as retval in operator-() due to NRVO. mcr += ca; return mcr; // <-- This allows for NRVO. The following does not. --> return mcr += ca; } template template promote, fvar> fvar::operator*( fvar const& cr) const { using diff_t = typename std::array::difference_type; promote const zero(0); promote, fvar> retval; BOOST_IF_CONSTEXPR (Order < Order2) for (size_t i = 0, j = Order, k = Order2; i <= Order2; ++i, j && --j, --k) retval.v[i] = std::inner_product(v.cbegin(), v.cend() - diff_t(j), cr.v.crbegin() + diff_t(k), zero); else for (size_t i = 0, j = Order2, k = Order; i <= Order; ++i, j && --j, --k) retval.v[i] = std::inner_product(cr.v.cbegin(), cr.v.cend() - diff_t(j), v.crbegin() + diff_t(k), zero); return retval; } template fvar fvar::operator*(root_type const& ca) const { fvar retval(*this); retval *= ca; return retval; } template fvar operator*(typename fvar::root_type const& ca, fvar const& cr) { return cr * ca; } template template promote, fvar> fvar::operator/( fvar const& cr) const { using diff_t = typename std::array::difference_type; promote const zero(0); promote, fvar> retval; retval.v.front() = v.front() / cr.v.front(); BOOST_IF_CONSTEXPR (Order < Order2) { for (size_t i = 1, j = Order2 - 1; i <= Order; ++i, --j) retval.v[i] = (v[i] - std::inner_product( cr.v.cbegin() + 1, cr.v.cend() - diff_t(j), retval.v.crbegin() + diff_t(j + 1), zero)) / cr.v.front(); for (size_t i = Order + 1, j = Order2 - Order - 1; i <= Order2; ++i, --j) retval.v[i] = -std::inner_product( cr.v.cbegin() + 1, cr.v.cend() - diff_t(j), retval.v.crbegin() + diff_t(j + 1), zero) / cr.v.front(); } else BOOST_IF_CONSTEXPR (0 < Order2) for (size_t i = 1, j = Order2 - 1, k = Order; i <= Order; ++i, j && --j, --k) retval.v[i] = (v[i] - std::inner_product( cr.v.cbegin() + 1, cr.v.cend() - diff_t(j), retval.v.crbegin() + diff_t(k), zero)) / cr.v.front(); else for (size_t i = 1; i <= Order; ++i) retval.v[i] = v[i] / cr.v.front(); return retval; } template fvar fvar::operator/(root_type const& ca) const { fvar retval(*this); retval /= ca; return retval; } template fvar operator/(typename fvar::root_type const& ca, fvar const& cr) { using diff_t = typename std::array::difference_type; fvar retval; retval.v.front() = ca / cr.v.front(); BOOST_IF_CONSTEXPR (0 < Order) { RealType const zero(0); for (size_t i = 1, j = Order - 1; i <= Order; ++i, --j) retval.v[i] = -std::inner_product( cr.v.cbegin() + 1, cr.v.cend() - diff_t(j), retval.v.crbegin() + diff_t(j + 1), zero) / cr.v.front(); } return retval; } template template bool fvar::operator==(fvar const& cr) const { return v.front() == cr.v.front(); } template bool fvar::operator==(root_type const& ca) const { return v.front() == ca; } template bool operator==(typename fvar::root_type const& ca, fvar const& cr) { return ca == cr.v.front(); } template template bool fvar::operator!=(fvar const& cr) const { return v.front() != cr.v.front(); } template bool fvar::operator!=(root_type const& ca) const { return v.front() != ca; } template bool operator!=(typename fvar::root_type const& ca, fvar const& cr) { return ca != cr.v.front(); } template template bool fvar::operator<=(fvar const& cr) const { return v.front() <= cr.v.front(); } template bool fvar::operator<=(root_type const& ca) const { return v.front() <= ca; } template bool operator<=(typename fvar::root_type const& ca, fvar const& cr) { return ca <= cr.v.front(); } template template bool fvar::operator>=(fvar const& cr) const { return v.front() >= cr.v.front(); } template bool fvar::operator>=(root_type const& ca) const { return v.front() >= ca; } template bool operator>=(typename fvar::root_type const& ca, fvar const& cr) { return ca >= cr.v.front(); } template template bool fvar::operator<(fvar const& cr) const { return v.front() < cr.v.front(); } template bool fvar::operator<(root_type const& ca) const { return v.front() < ca; } template bool operator<(typename fvar::root_type const& ca, fvar const& cr) { return ca < cr.v.front(); } template template bool fvar::operator>(fvar const& cr) const { return v.front() > cr.v.front(); } template bool fvar::operator>(root_type const& ca) const { return v.front() > ca; } template bool operator>(typename fvar::root_type const& ca, fvar const& cr) { return ca > cr.v.front(); } /*** Other methods and functions ***/ #ifndef BOOST_NO_CXX17_IF_CONSTEXPR // f : order -> derivative(order)/factorial(order) // Use this when you have the polynomial coefficients, rather than just the derivatives. E.g. See atan2(). template template promote, Fvar, Fvars...> fvar::apply_coefficients( size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const { fvar const epsilon = fvar(*this).set_root(0); size_t i = (std::min)(order, order_sum); promote, Fvar, Fvars...> accumulator = cr.apply_coefficients( order - i, [&f, i](auto... indices) { return f(i, indices...); }, std::forward(fvars)...); while (i--) (accumulator *= epsilon) += cr.apply_coefficients( order - i, [&f, i](auto... indices) { return f(i, indices...); }, std::forward(fvars)...); return accumulator; } #endif // f : order -> derivative(order)/factorial(order) // Use this when you have the polynomial coefficients, rather than just the derivatives. E.g. See atan(). template template fvar fvar::apply_coefficients(size_t const order, Func const& f) const { fvar const epsilon = fvar(*this).set_root(0); #ifndef BOOST_NO_CXX17_IF_CONSTEXPR size_t i = (std::min)(order, order_sum); #else // ODR-use of static constexpr size_t i = order < order_sum ? order : order_sum; #endif fvar accumulator = f(i); while (i--) (accumulator *= epsilon) += f(i); return accumulator; } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR // f : order -> derivative(order) template template promote, Fvar, Fvars...> fvar::apply_coefficients_nonhorner( size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const { fvar const epsilon = fvar(*this).set_root(0); fvar epsilon_i = fvar(1); // epsilon to the power of i promote, Fvar, Fvars...> accumulator = cr.apply_coefficients_nonhorner( order, [&f](auto... indices) { return f(0, static_cast(indices)...); }, std::forward(fvars)...); size_t const i_max = (std::min)(order, order_sum); for (size_t i = 1; i <= i_max; ++i) { epsilon_i = epsilon_i.epsilon_multiply(i - 1, 0, epsilon, 1, 0); accumulator += epsilon_i.epsilon_multiply( i, 0, cr.apply_coefficients_nonhorner( order - i, [&f, i](auto... indices) { return f(i, static_cast(indices)...); }, std::forward(fvars)...), 0, 0); } return accumulator; } #endif // f : order -> coefficient(order) template template fvar fvar::apply_coefficients_nonhorner(size_t const order, Func const& f) const { fvar const epsilon = fvar(*this).set_root(0); fvar epsilon_i = fvar(1); // epsilon to the power of i fvar accumulator = fvar(f(0u)); #ifndef BOOST_NO_CXX17_IF_CONSTEXPR size_t const i_max = (std::min)(order, order_sum); #else // ODR-use of static constexpr size_t const i_max = order < order_sum ? order : order_sum; #endif for (size_t i = 1; i <= i_max; ++i) { epsilon_i = epsilon_i.epsilon_multiply(i - 1, 0, epsilon, 1, 0); accumulator += epsilon_i.epsilon_multiply(i, 0, f(i)); } return accumulator; } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR // f : order -> derivative(order) template template promote, Fvar, Fvars...> fvar::apply_derivatives( size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const { fvar const epsilon = fvar(*this).set_root(0); size_t i = (std::min)(order, order_sum); promote, Fvar, Fvars...> accumulator = cr.apply_derivatives( order - i, [&f, i](auto... indices) { return f(i, indices...); }, std::forward(fvars)...) / factorial(static_cast(i)); while (i--) (accumulator *= epsilon) += cr.apply_derivatives( order - i, [&f, i](auto... indices) { return f(i, indices...); }, std::forward(fvars)...) / factorial(static_cast(i)); return accumulator; } #endif // f : order -> derivative(order) template template fvar fvar::apply_derivatives(size_t const order, Func const& f) const { fvar const epsilon = fvar(*this).set_root(0); #ifndef BOOST_NO_CXX17_IF_CONSTEXPR size_t i = (std::min)(order, order_sum); #else // ODR-use of static constexpr size_t i = order < order_sum ? order : order_sum; #endif fvar accumulator = f(i) / factorial(static_cast(i)); while (i--) (accumulator *= epsilon) += f(i) / factorial(static_cast(i)); return accumulator; } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR // f : order -> derivative(order) template template promote, Fvar, Fvars...> fvar::apply_derivatives_nonhorner( size_t const order, Func const& f, Fvar const& cr, Fvars&&... fvars) const { fvar const epsilon = fvar(*this).set_root(0); fvar epsilon_i = fvar(1); // epsilon to the power of i promote, Fvar, Fvars...> accumulator = cr.apply_derivatives_nonhorner( order, [&f](auto... indices) { return f(0, static_cast(indices)...); }, std::forward(fvars)...); size_t const i_max = (std::min)(order, order_sum); for (size_t i = 1; i <= i_max; ++i) { epsilon_i = epsilon_i.epsilon_multiply(i - 1, 0, epsilon, 1, 0); accumulator += epsilon_i.epsilon_multiply( i, 0, cr.apply_derivatives_nonhorner( order - i, [&f, i](auto... indices) { return f(i, static_cast(indices)...); }, std::forward(fvars)...) / factorial(static_cast(i)), 0, 0); } return accumulator; } #endif // f : order -> derivative(order) template template fvar fvar::apply_derivatives_nonhorner(size_t const order, Func const& f) const { fvar const epsilon = fvar(*this).set_root(0); fvar epsilon_i = fvar(1); // epsilon to the power of i fvar accumulator = fvar(f(0u)); #ifndef BOOST_NO_CXX17_IF_CONSTEXPR size_t const i_max = (std::min)(order, order_sum); #else // ODR-use of static constexpr size_t const i_max = order < order_sum ? order : order_sum; #endif for (size_t i = 1; i <= i_max; ++i) { epsilon_i = epsilon_i.epsilon_multiply(i - 1, 0, epsilon, 1, 0); accumulator += epsilon_i.epsilon_multiply(i, 0, f(i) / factorial(static_cast(i))); } return accumulator; } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR // Can throw "std::out_of_range: array::at: __n (which is 7) >= _Nm (which is 7)" template template get_type_at fvar::at(size_t order, Orders... orders) const { if constexpr (0 < sizeof...(Orders)) return v.at(order).at(static_cast(orders)...); else return v.at(order); } #endif #ifndef BOOST_NO_CXX17_IF_CONSTEXPR // Can throw "std::out_of_range: array::at: __n (which is 7) >= _Nm (which is 7)" template template get_type_at, sizeof...(Orders)> fvar::derivative( Orders... orders) const { static_assert(sizeof...(Orders) <= depth, "Number of parameters to derivative(...) cannot exceed fvar::depth."); return at(static_cast(orders)...) * (... * factorial(static_cast(orders))); } #endif template const RealType& fvar::operator[](size_t i) const { return v[i]; } template RealType fvar::epsilon_inner_product(size_t z0, size_t const isum0, size_t const m0, fvar const& cr, size_t z1, size_t const isum1, size_t const m1, size_t const j) const { static_assert(is_fvar::value, "epsilon_inner_product() must have 1 < depth."); RealType accumulator = RealType(); auto const i0_max = m1 < j ? j - m1 : 0; for (auto i0 = m0, i1 = j - m0; i0 <= i0_max; ++i0, --i1) accumulator += v[i0].epsilon_multiply(z0, isum0 + i0, cr.v[i1], z1, isum1 + i1); return accumulator; } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR template fvar fvar::epsilon_multiply(size_t z0, size_t isum0, fvar const& cr, size_t z1, size_t isum1) const { using diff_t = typename std::array::difference_type; RealType const zero(0); size_t const m0 = order_sum + isum0 < Order + z0 ? Order + z0 - (order_sum + isum0) : 0; size_t const m1 = order_sum + isum1 < Order + z1 ? Order + z1 - (order_sum + isum1) : 0; size_t const i_max = m0 + m1 < Order ? Order - (m0 + m1) : 0; fvar retval = fvar(); if constexpr (is_fvar::value) for (size_t i = 0, j = Order; i <= i_max; ++i, --j) retval.v[j] = epsilon_inner_product(z0, isum0, m0, cr, z1, isum1, m1, j); else for (size_t i = 0, j = Order; i <= i_max; ++i, --j) retval.v[j] = std::inner_product( v.cbegin() + diff_t(m0), v.cend() - diff_t(i + m1), cr.v.crbegin() + diff_t(i + m0), zero); return retval; } #endif #ifndef BOOST_NO_CXX17_IF_CONSTEXPR // When called from outside this method, z0 should be non-zero. Otherwise if z0=0 then it will give an // incorrect result of 0 when the root value is 0 and ca=inf, when instead the correct product is nan. // If z0=0 then use the regular multiply operator*() instead. template fvar fvar::epsilon_multiply(size_t z0, size_t isum0, root_type const& ca) const { fvar retval(*this); size_t const m0 = order_sum + isum0 < Order + z0 ? Order + z0 - (order_sum + isum0) : 0; if constexpr (is_fvar::value) for (size_t i = m0; i <= Order; ++i) retval.v[i] = retval.v[i].epsilon_multiply(z0, isum0 + i, ca); else for (size_t i = m0; i <= Order; ++i) if (retval.v[i] != static_cast(0)) retval.v[i] *= ca; return retval; } #endif template fvar fvar::inverse() const { return static_cast(*this) == 0 ? inverse_apply() : 1 / *this; } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR template fvar& fvar::negate() { if constexpr (is_fvar::value) std::for_each(v.begin(), v.end(), [](RealType& r) { r.negate(); }); else std::for_each(v.begin(), v.end(), [](RealType& a) { a = -a; }); return *this; } #endif // This gives log(0.0) = depth(1)(-inf,inf,-inf,inf,-inf,inf) // 1 / *this: log(0.0) = depth(1)(-inf,inf,-inf,-nan,-nan,-nan) template fvar fvar::inverse_apply() const { root_type derivatives[order_sum + 1]; // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. root_type const x0 = static_cast(*this); *derivatives = 1 / x0; for (size_t i = 1; i <= order_sum; ++i) derivatives[i] = -derivatives[i - 1] * i / x0; return apply_derivatives_nonhorner(order_sum, [&derivatives](size_t j) { return derivatives[j]; }); } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR template fvar& fvar::multiply_assign_by_root_type(bool is_root, root_type const& ca) { auto itr = v.begin(); if constexpr (is_fvar::value) { itr->multiply_assign_by_root_type(is_root, ca); for (++itr; itr != v.end(); ++itr) itr->multiply_assign_by_root_type(false, ca); } else { if (is_root || *itr != 0) *itr *= ca; // Skip multiplication of 0 by ca=inf to avoid nan, except when is_root. for (++itr; itr != v.end(); ++itr) if (*itr != 0) *itr *= ca; } return *this; } #endif template fvar::operator root_type() const { return static_cast(v.front()); } template template fvar::operator T() const { return static_cast(static_cast(v.front())); } #ifndef BOOST_NO_CXX17_IF_CONSTEXPR template fvar& fvar::set_root(root_type const& root) { if constexpr (is_fvar::value) v.front().set_root(root); else v.front() = root; return *this; } #endif // Standard Library Support Requirements template fvar fabs(fvar const& cr) { typename fvar::root_type const zero(0); return cr < zero ? -cr : cr == zero ? fvar() // Canonical fabs'(0) = 0. : cr; // Propagate NaN. } template fvar abs(fvar const& cr) { return fabs(cr); } template fvar ceil(fvar const& cr) { using std::ceil; return fvar(ceil(static_cast::root_type>(cr))); } template fvar floor(fvar const& cr) { using std::floor; return fvar(floor(static_cast::root_type>(cr))); } template fvar exp(fvar const& cr) { using std::exp; constexpr size_t order = fvar::order_sum; using root_type = typename fvar::root_type; root_type const d0 = exp(static_cast(cr)); return cr.apply_derivatives(order, [&d0](size_t) { return d0; }); } template fvar pow(fvar const& x, typename fvar::root_type const& y) { BOOST_MATH_STD_USING using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const x0 = static_cast(x); root_type derivatives[order + 1]{pow(x0, y)}; if (fabs(x0) < std::numeric_limits::epsilon()) { root_type coef = 1; for (size_t i = 0; i < order && y - i != 0; ++i) { coef *= y - i; derivatives[i + 1] = coef * pow(x0, y - (i + 1)); } return x.apply_derivatives_nonhorner(order, [&derivatives](size_t i) { return derivatives[i]; }); } else { for (size_t i = 0; i < order && y - i != 0; ++i) derivatives[i + 1] = (y - i) * derivatives[i] / x0; return x.apply_derivatives(order, [&derivatives](size_t i) { return derivatives[i]; }); } } template fvar pow(typename fvar::root_type const& x, fvar const& y) { BOOST_MATH_STD_USING using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const y0 = static_cast(y); root_type derivatives[order + 1]; *derivatives = pow(x, y0); root_type const logx = log(x); for (size_t i = 0; i < order; ++i) derivatives[i + 1] = derivatives[i] * logx; return y.apply_derivatives(order, [&derivatives](size_t i) { return derivatives[i]; }); } template promote, fvar> pow(fvar const& x, fvar const& y) { BOOST_MATH_STD_USING using return_type = promote, fvar>; using root_type = typename return_type::root_type; constexpr size_t order = return_type::order_sum; root_type const x0 = static_cast(x); root_type const y0 = static_cast(y); root_type dxydx[order + 1]{pow(x0, y0)}; BOOST_IF_CONSTEXPR (order == 0) return return_type(*dxydx); else { for (size_t i = 0; i < order && y0 - i != 0; ++i) dxydx[i + 1] = (y0 - i) * dxydx[i] / x0; std::array, order + 1> lognx; lognx.front() = fvar(1); #ifndef BOOST_NO_CXX17_IF_CONSTEXPR lognx[1] = log(make_fvar(x0)); #else // for compilers that compile this branch when order == 0. lognx[(std::min)(size_t(1), order)] = log(make_fvar(x0)); #endif for (size_t i = 1; i < order; ++i) lognx[i + 1] = lognx[i] * lognx[1]; auto const f = [&dxydx, &lognx](size_t i, size_t j) { size_t binomial = 1; root_type sum = dxydx[i] * static_cast(lognx[j]); for (size_t k = 1; k <= i; ++k) { (binomial *= (i - k + 1)) /= k; // binomial_coefficient(i,k) sum += binomial * dxydx[i - k] * lognx[j].derivative(k); } return sum; }; if (fabs(x0) < std::numeric_limits::epsilon()) return x.apply_derivatives_nonhorner(order, f, y); return x.apply_derivatives(order, f, y); } } template fvar sqrt(fvar const& cr) { using std::sqrt; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type derivatives[order + 1]; root_type const x = static_cast(cr); *derivatives = sqrt(x); BOOST_IF_CONSTEXPR (order == 0) return fvar(*derivatives); else { root_type numerator = 0.5; root_type powers = 1; #ifndef BOOST_NO_CXX17_IF_CONSTEXPR derivatives[1] = numerator / *derivatives; #else // for compilers that compile this branch when order == 0. derivatives[(std::min)(size_t(1), order)] = numerator / *derivatives; #endif using diff_t = typename std::array::difference_type; for (size_t i = 2; i <= order; ++i) { numerator *= static_cast(-0.5) * ((static_cast(i) << 1) - 3); powers *= x; derivatives[i] = numerator / (powers * *derivatives); } auto const f = [&derivatives](size_t i) { return derivatives[i]; }; if (cr < std::numeric_limits::epsilon()) return cr.apply_derivatives_nonhorner(order, f); return cr.apply_derivatives(order, f); } } // Natural logarithm. If cr==0 then derivative(i) may have nans due to nans from inverse(). template fvar log(fvar const& cr) { using std::log; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = log(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto const d1 = make_fvar(static_cast(cr)).inverse(); // log'(x) = 1 / x return cr.apply_coefficients_nonhorner(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar frexp(fvar const& cr, int* exp) { using std::exp2; using std::frexp; using root_type = typename fvar::root_type; frexp(static_cast(cr), exp); return cr * static_cast(exp2(-*exp)); } template fvar ldexp(fvar const& cr, int exp) { // argument to std::exp2 must be casted to root_type, otherwise std::exp2 returns double (always) using std::exp2; return cr * exp2(static_cast::root_type>(exp)); } template fvar cos(fvar const& cr) { BOOST_MATH_STD_USING using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = cos(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { root_type const d1 = -sin(static_cast(cr)); root_type const derivatives[4]{d0, d1, -d0, -d1}; return cr.apply_derivatives(order, [&derivatives](size_t i) { return derivatives[i & 3]; }); } } template fvar sin(fvar const& cr) { BOOST_MATH_STD_USING using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = sin(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { root_type const d1 = cos(static_cast(cr)); root_type const derivatives[4]{d0, d1, -d0, -d1}; return cr.apply_derivatives(order, [&derivatives](size_t i) { return derivatives[i & 3]; }); } } template fvar asin(fvar const& cr) { using std::asin; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = asin(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); auto const d1 = sqrt((x *= x).negate() += 1).inverse(); // asin'(x) = 1 / sqrt(1-x*x). return cr.apply_coefficients_nonhorner(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar tan(fvar const& cr) { using std::tan; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = tan(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto c = cos(make_fvar(static_cast(cr))); auto const d1 = (c *= c).inverse(); // tan'(x) = 1 / cos(x)^2 return cr.apply_coefficients_nonhorner(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar atan(fvar const& cr) { using std::atan; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = atan(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); auto const d1 = ((x *= x) += 1).inverse(); // atan'(x) = 1 / (x*x+1). return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar atan2(fvar const& cr, typename fvar::root_type const& ca) { using std::atan2; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = atan2(static_cast(cr), ca); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto y = make_fvar(static_cast(cr)); auto const d1 = ca / ((y *= y) += (ca * ca)); // (d/dy)atan2(y,x) = x / (y*y+x*x) return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar atan2(typename fvar::root_type const& ca, fvar const& cr) { using std::atan2; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = atan2(ca, static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); auto const d1 = -ca / ((x *= x) += (ca * ca)); // (d/dx)atan2(y,x) = -y / (x*x+y*y) return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template promote, fvar> atan2(fvar const& cr1, fvar const& cr2) { using std::atan2; using return_type = promote, fvar>; using root_type = typename return_type::root_type; constexpr size_t order = return_type::order_sum; root_type const y = static_cast(cr1); root_type const x = static_cast(cr2); root_type const d00 = atan2(y, x); BOOST_IF_CONSTEXPR (order == 0) return return_type(d00); else { constexpr size_t order1 = fvar::order_sum; constexpr size_t order2 = fvar::order_sum; auto x01 = make_fvar::root_type, order2 - 1>(x); auto const d01 = -y / ((x01 *= x01) += (y * y)); auto y10 = make_fvar::root_type, order1 - 1>(y); auto x10 = make_fvar::root_type, 0, order2>(x); auto const d10 = x10 / ((x10 * x10) + (y10 *= y10)); auto const f = [&d00, &d01, &d10](size_t i, size_t j) { return i ? d10[i - 1][j] / i : j ? d01[j - 1] / j : d00; }; return cr1.apply_coefficients(order, f, cr2); } } template promote, fvar> fmod(fvar const& cr1, fvar const& cr2) { using boost::math::trunc; auto const numer = static_cast::root_type>(cr1); auto const denom = static_cast::root_type>(cr2); return cr1 - cr2 * trunc(numer / denom); } template fvar round(fvar const& cr) { using boost::math::round; return fvar(round(static_cast::root_type>(cr))); } template int iround(fvar const& cr) { using boost::math::iround; return iround(static_cast::root_type>(cr)); } template long lround(fvar const& cr) { using boost::math::lround; return lround(static_cast::root_type>(cr)); } template long long llround(fvar const& cr) { using boost::math::llround; return llround(static_cast::root_type>(cr)); } template fvar trunc(fvar const& cr) { using boost::math::trunc; return fvar(trunc(static_cast::root_type>(cr))); } template long double truncl(fvar const& cr) { using std::truncl; return truncl(static_cast::root_type>(cr)); } template int itrunc(fvar const& cr) { using boost::math::itrunc; return itrunc(static_cast::root_type>(cr)); } template long long lltrunc(fvar const& cr) { using boost::math::lltrunc; return lltrunc(static_cast::root_type>(cr)); } template std::ostream& operator<<(std::ostream& out, fvar const& cr) { out << "depth(" << cr.depth << ")(" << cr.v.front(); for (size_t i = 1; i <= Order; ++i) out << ',' << cr.v[i]; return out << ')'; } // Additional functions template fvar acos(fvar const& cr) { using std::acos; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = acos(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); auto const d1 = sqrt((x *= x).negate() += 1).inverse().negate(); // acos'(x) = -1 / sqrt(1-x*x). return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar acosh(fvar const& cr) { using boost::math::acosh; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = acosh(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); auto const d1 = sqrt((x *= x) -= 1).inverse(); // acosh'(x) = 1 / sqrt(x*x-1). return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar asinh(fvar const& cr) { using boost::math::asinh; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = asinh(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); auto const d1 = sqrt((x *= x) += 1).inverse(); // asinh'(x) = 1 / sqrt(x*x+1). return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar atanh(fvar const& cr) { using boost::math::atanh; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = atanh(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); auto const d1 = ((x *= x).negate() += 1).inverse(); // atanh'(x) = 1 / (1-x*x) return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar cosh(fvar const& cr) { BOOST_MATH_STD_USING using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = cosh(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { root_type const derivatives[2]{d0, sinh(static_cast(cr))}; return cr.apply_derivatives(order, [&derivatives](size_t i) { return derivatives[i & 1]; }); } } template fvar digamma(fvar const& cr) { using boost::math::digamma; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const x = static_cast(cr); root_type const d0 = digamma(x); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { static_assert(order <= static_cast((std::numeric_limits::max)()), "order exceeds maximum derivative for boost::math::polygamma()."); return cr.apply_derivatives( order, [&x, &d0](size_t i) { return i ? boost::math::polygamma(static_cast(i), x) : d0; }); } } template fvar erf(fvar const& cr) { using boost::math::erf; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = erf(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); // d1 = 2/sqrt(pi)*exp(-x*x) auto const d1 = 2 * constants::one_div_root_pi() * exp((x *= x).negate()); return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar erfc(fvar const& cr) { using boost::math::erfc; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = erfc(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { auto x = make_fvar(static_cast(cr)); // erfc'(x) = -erf'(x) auto const d1 = -2 * constants::one_div_root_pi() * exp((x *= x).negate()); return cr.apply_coefficients(order, [&d0, &d1](size_t i) { return i ? d1[i - 1] / i : d0; }); } } template fvar lambert_w0(fvar const& cr) { using std::exp; using boost::math::lambert_w0; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type derivatives[order + 1]; *derivatives = lambert_w0(static_cast(cr)); BOOST_IF_CONSTEXPR (order == 0) return fvar(*derivatives); else { root_type const expw = exp(*derivatives); derivatives[1] = 1 / (static_cast(cr) + expw); BOOST_IF_CONSTEXPR (order == 1) return cr.apply_derivatives_nonhorner(order, [&derivatives](size_t i) { return derivatives[i]; }); else { using diff_t = typename std::array::difference_type; root_type d1powers = derivatives[1] * derivatives[1]; root_type const x = derivatives[1] * expw; derivatives[2] = d1powers * (-1 - x); std::array coef{{-1, -1}}; // as in derivatives[2]. for (size_t n = 3; n <= order; ++n) { coef[n - 1] = coef[n - 2] * -static_cast(2 * n - 3); for (size_t j = n - 2; j != 0; --j) (coef[j] *= -static_cast(n - 1)) -= (n + j - 2) * coef[j - 1]; coef[0] *= -static_cast(n - 1); d1powers *= derivatives[1]; derivatives[n] = d1powers * std::accumulate(coef.crend() - diff_t(n - 1), coef.crend(), coef[n - 1], [&x](root_type const& a, root_type const& b) { return a * x + b; }); } return cr.apply_derivatives_nonhorner(order, [&derivatives](size_t i) { return derivatives[i]; }); } } } template fvar lgamma(fvar const& cr) { using std::lgamma; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const x = static_cast(cr); root_type const d0 = lgamma(x); BOOST_IF_CONSTEXPR (order == 0) return fvar(d0); else { static_assert(order <= static_cast((std::numeric_limits::max)()) + 1, "order exceeds maximum derivative for boost::math::polygamma()."); return cr.apply_derivatives( order, [&x, &d0](size_t i) { return i ? boost::math::polygamma(static_cast(i - 1), x) : d0; }); } } template fvar sinc(fvar const& cr) { if (cr != 0) return sin(cr) / cr; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type taylor[order + 1]{1}; // sinc(0) = 1 BOOST_IF_CONSTEXPR (order == 0) return fvar(*taylor); else { for (size_t n = 2; n <= order; n += 2) taylor[n] = (1 - static_cast(n & 2)) / factorial(static_cast(n + 1)); return cr.apply_coefficients_nonhorner(order, [&taylor](size_t i) { return taylor[i]; }); } } template fvar sinh(fvar const& cr) { BOOST_MATH_STD_USING using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; root_type const d0 = sinh(static_cast(cr)); BOOST_IF_CONSTEXPR (fvar::order_sum == 0) return fvar(d0); else { root_type const derivatives[2]{d0, cosh(static_cast(cr))}; return cr.apply_derivatives(order, [&derivatives](size_t i) { return derivatives[i & 1]; }); } } template fvar tanh(fvar const& cr) { fvar retval = exp(cr * 2); fvar const denom = retval + 1; (retval -= 1) /= denom; return retval; } template fvar tgamma(fvar const& cr) { using std::tgamma; using root_type = typename fvar::root_type; constexpr size_t order = fvar::order_sum; BOOST_IF_CONSTEXPR (order == 0) return fvar(tgamma(static_cast(cr))); else { if (cr < 0) return constants::pi() / (sin(constants::pi() * cr) * tgamma(1 - cr)); return exp(lgamma(cr)).set_root(tgamma(static_cast(cr))); } } } // namespace detail } // namespace autodiff_v1 } // namespace differentiation } // namespace math } // namespace boost namespace std { // boost::math::tools::digits() is handled by this std::numeric_limits<> specialization, // and similarly for max_value, min_value, log_max_value, log_min_value, and epsilon. template class numeric_limits> : public numeric_limits::root_type> { }; } // namespace std namespace boost { namespace math { namespace tools { namespace detail { template using autodiff_fvar_type = differentiation::detail::fvar; template using autodiff_root_type = typename autodiff_fvar_type::root_type; } // namespace detail // See boost/math/tools/promotion.hpp template struct promote_args_2, detail::autodiff_fvar_type> { using type = detail::autodiff_fvar_type::type, #ifndef BOOST_NO_CXX14_CONSTEXPR (std::max)(Order0, Order1)>; #else Order0; #endif }; template struct promote_args> { using type = detail::autodiff_fvar_type::type, Order>; }; template struct promote_args_2, RealType1> { using type = detail::autodiff_fvar_type::type, Order0>; }; template struct promote_args_2> { using type = detail::autodiff_fvar_type::type, Order1>; }; template inline constexpr destination_t real_cast(detail::autodiff_fvar_type const& from_v) noexcept(BOOST_MATH_IS_FLOAT(destination_t) && BOOST_MATH_IS_FLOAT(RealType)) { return real_cast(static_cast>(from_v)); } } // namespace tools namespace policies { template using fvar_t = differentiation::detail::fvar; template struct evaluation, Policy> { using type = fvar_t::type, Order>; }; template struct evaluation, Policy> { using type = fvar_t::type, Order>; }; } // namespace policies } // namespace math } // namespace boost #ifdef BOOST_NO_CXX17_IF_CONSTEXPR #include "autodiff_cpp11.hpp" #endif #endif // BOOST_MATH_DIFFERENTIATION_AUTODIFF_HPP