2
0
mirror of https://github.com/boostorg/math.git synced 2026-01-26 06:42:12 +00:00
Files
math/test/test_autodiff_2.cpp

463 lines
15 KiB
C++

// 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)
#include "test_autodiff.hpp"
BOOST_AUTO_TEST_SUITE(test_autodiff_2)
struct one_over_one_plus_x_squared_test
{
template<typename T>
void operator()(const T&) const
{
constexpr int m = 4;
constexpr float cx = 1.0;
auto f = make_fvar<T,m>(cx);
//f = 1 / ((f *= f) += 1);
f = ((f *= f) += 1).inverse();
BOOST_REQUIRE(f.derivative(0) == 0.5);
BOOST_REQUIRE(f.derivative(1) == -0.5);
BOOST_REQUIRE(f.derivative(2) == 0.5);
BOOST_REQUIRE(f.derivative(3) == 0.0);
BOOST_REQUIRE(f.derivative(4) == -3.0);
}
};
BOOST_AUTO_TEST_CASE(one_over_one_plus_x_squared)
{
boost::fusion::for_each(bin_float_types, one_over_one_plus_x_squared_test());
boost::fusion::for_each(multiprecision_float_types, one_over_one_plus_x_squared_test());
}
struct exp_test_test
{
template<typename T>
void operator()(const T&) const
{
using std::exp;
constexpr int m = 4;
const T cx = 2.0;
const auto x = make_fvar<T,m>(cx);
auto y = exp(x);
for (int i=0 ; i<=m ; ++i)
{
//std::cout.precision(100);
//std::cout << "y.derivative("<<i<<") = " << y.derivative(i) << ", std::exp(cx) = " << std::exp(cx) << std::endl;
BOOST_REQUIRE(y.derivative(i) == exp(cx));
}
}
};
BOOST_AUTO_TEST_CASE(exp_test)
{
boost::fusion::for_each(bin_float_types, exp_test_test());
boost::fusion::for_each(multiprecision_float_types, exp_test_test());
}
struct pow_test_test
{
template<typename T>
void operator()(const T&) const
{
const T eps = 201*std::numeric_limits<T>::epsilon(); // percent
using std::exp;
using std::log;
using std::pow;
constexpr int m = 5;
constexpr int n = 4;
const T cx = 2.0;
const T cy = 3.0;
const auto x = make_fvar<T,m>(cx);
const auto y = make_fvar<T,m,n>(cy);
auto z0 = pow(x,cy);
BOOST_REQUIRE(z0.derivative(0) == pow(cx,cy));
BOOST_REQUIRE(z0.derivative(1) == cy*pow(cx,cy-1));
BOOST_REQUIRE(z0.derivative(2) == cy*(cy-1)*pow(cx,cy-2));
BOOST_REQUIRE(z0.derivative(3) == cy*(cy-1)*(cy-2)*pow(cx,cy-3));
BOOST_REQUIRE(z0.derivative(4) == 0.0);
BOOST_REQUIRE(z0.derivative(5) == 0.0);
auto z1 = pow(cx,y);
BOOST_REQUIRE_CLOSE(z1.derivative(0,0), pow(cx,cy), eps);
for (int j=1 ; j<=n ; ++j)
BOOST_REQUIRE_CLOSE(z1.derivative(0,j), pow(log(cx),j)*exp(cy*log(cx)), eps);
for (int i=1 ; i<=m ; ++i)
for (int j=0 ; j<=n ; ++j)
BOOST_REQUIRE(z1.derivative(i,j) == 0.0);
auto z2 = pow(x,y);
for (int j=0 ; j<=n ; ++j)
BOOST_REQUIRE_CLOSE(z2.derivative(0,j), pow(cx,cy)*pow(log(cx),j), eps);
for (int j=0 ; j<=n ; ++j)
BOOST_REQUIRE_CLOSE(z2.derivative(1,j), pow(cx,cy-1)*pow(log(cx),j-1)*(cy*log(cx)+j), eps);
BOOST_REQUIRE_CLOSE(z2.derivative(2,0), pow(cx,cy-2)*cy*(cy-1), eps);
BOOST_REQUIRE_CLOSE(z2.derivative(2,1), pow(cx,cy-2)*(cy*(cy-1)*log(cx)+2*cy-1), eps);
for (int j=2 ; j<=n ; ++j)
BOOST_REQUIRE_CLOSE(z2.derivative(2,j), pow(cx,cy-2)*pow(log(cx),j-2)*(j*(2*cy-1)*log(cx)+(j-1)*j+(cy-1)*cy*pow(log(cx),2)), eps);
BOOST_REQUIRE_CLOSE(z2.derivative(2,4), pow(cx,cy-2)*pow(log(cx),2)*(4*(2*cy-1)*log(cx)+(4-1)*4+(cy-1)*cy*pow(log(cx),2)), eps);
}
};
BOOST_AUTO_TEST_CASE(pow_test)
{
boost::fusion::for_each(bin_float_types, pow_test_test());
}
struct sqrt_test_test
{
template<typename T>
void operator()(const T&) const
{
using std::sqrt;
using std::pow;
constexpr int m = 5;
constexpr float cx = 4.0;
auto x = make_fvar<T,m>(cx);
auto y = sqrt(x);
BOOST_REQUIRE(y.derivative(0) == sqrt(cx));
BOOST_REQUIRE(y.derivative(1) == 0.5*pow(cx,-0.5));
BOOST_REQUIRE(y.derivative(2) == -0.5*0.5*pow(cx,-1.5));
BOOST_REQUIRE(y.derivative(3) == 0.5*0.5*1.5*pow(cx,-2.5));
BOOST_REQUIRE(y.derivative(4) == -0.5*0.5*1.5*2.5*pow(cx,-3.5));
BOOST_REQUIRE(y.derivative(5) == 0.5*0.5*1.5*2.5*3.5*pow(cx,-4.5));
x = make_fvar<T,m>(0);
y = sqrt(x);
//std::cout << "sqrt(0) = " << y << std::endl; // (0,inf,-inf,inf,-inf,inf)
BOOST_REQUIRE(y.derivative(0) == 0.0);
for (int i=1; i<=m ; ++i)
BOOST_REQUIRE(y.derivative(i) == (i&1?1:-1)*std::numeric_limits<T>::infinity());
}
};
BOOST_AUTO_TEST_CASE(sqrt_test)
{
boost::fusion::for_each(bin_float_types, sqrt_test_test());
boost::fusion::for_each(multiprecision_float_types, sqrt_test_test());
}
struct log_test_test
{
template<typename T>
void operator()(const T&) const
{
using std::log;
using std::pow;
constexpr int m = 5;
const T cx = 2.0;
auto x = make_fvar<T,m>(cx);
auto y = log(x);
BOOST_REQUIRE(y.derivative(0) == log(cx));
BOOST_REQUIRE(y.derivative(1) == 1/cx);
BOOST_REQUIRE(y.derivative(2) == -1/pow(cx,2));
BOOST_REQUIRE(y.derivative(3) == 2/pow(cx,3));
BOOST_REQUIRE(y.derivative(4) == -6/pow(cx,4));
BOOST_REQUIRE(y.derivative(5) == 24/pow(cx,5));
x = make_fvar<T,m>(0);
y = log(x);
//std::cout << "log(0) = " << y << std::endl; // log(0) = depth(1)(-inf,inf,-inf,inf,-inf,inf)
for (int i=0; i<=m ; ++i)
BOOST_REQUIRE(y.derivative(i) == (i&1?1:-1)*std::numeric_limits<T>::infinity());
}
};
BOOST_AUTO_TEST_CASE(log_test)
{
boost::fusion::for_each(bin_float_types, log_test_test());
boost::fusion::for_each(multiprecision_float_types, log_test_test());
}
struct ylogx_test
{
template<typename T>
void operator()(const T&) const
{
using std::log;
using std::pow;
const T eps = 100*std::numeric_limits<T>::epsilon(); // percent
constexpr int m = 5;
constexpr int n = 4;
const T cx = 2.0;
const T cy = 3.0;
const auto x = make_fvar<T,m>(cx);
const auto y = make_fvar<T,m,n>(cy);
auto z = y*log(x);
BOOST_REQUIRE(z.derivative(0,0) == cy*log(cx));
BOOST_REQUIRE(z.derivative(0,1) == log(cx));
BOOST_REQUIRE(z.derivative(0,2) == 0.0);
BOOST_REQUIRE(z.derivative(0,3) == 0.0);
BOOST_REQUIRE(z.derivative(0,4) == 0.0);
for (size_t i=1 ; i<=m ; ++i)
BOOST_REQUIRE_CLOSE(z.derivative(i,0), pow(-1,i-1)*boost::math::factorial<T>(i-1)*cy/pow(cx,i), eps);
for (size_t i=1 ; i<=m ; ++i)
BOOST_REQUIRE_CLOSE(z.derivative(i,1), pow(-1,i-1)*boost::math::factorial<T>(i-1)/pow(cx,i), eps);
for (size_t i=1 ; i<=m ; ++i)
for (size_t j=2 ; j<=n ; ++j)
BOOST_REQUIRE(z.derivative(i,j) == 0.0);
auto z1 = exp(z);
// RHS is confirmed by
// https://www.wolframalpha.com/input/?i=D%5Bx%5Ey,%7Bx,2%7D,%7By,4%7D%5D+%2F.+%7Bx-%3E2.0,+y-%3E3.0%7D
BOOST_REQUIRE_CLOSE(z1.derivative(2,4),
pow(cx,cy-2)*pow(log(cx),2)*(4*(2*cy-1)*log(cx)+(4-1)*4+(cy-1)*cy*pow(log(cx),2)), eps);
}
};
BOOST_AUTO_TEST_CASE(ylogx)
{
boost::fusion::for_each(bin_float_types, ylogx_test());
}
struct frexp_test_test
{
template<typename T>
void operator()(const T&) const
{
using std::frexp;
using std::exp2;
constexpr int m = 3;
const T cx = 3.5;
const auto x = make_fvar<T,m>(cx);
int exp, testexp;
auto y = frexp(x,&exp);
BOOST_REQUIRE(y.derivative(0) == frexp(cx,&testexp));
BOOST_REQUIRE(exp == testexp);
BOOST_REQUIRE(y.derivative(1) == exp2(-exp));
BOOST_REQUIRE(y.derivative(2) == 0.0);
BOOST_REQUIRE(y.derivative(3) == 0.0);
}
};
BOOST_AUTO_TEST_CASE(frexp_test)
{
boost::fusion::for_each(bin_float_types, frexp_test_test());
boost::fusion::for_each(multiprecision_float_types, frexp_test_test());
}
struct ldexp_test_test
{
template<typename T>
void operator()(const T&) const
{
using std::ldexp;
using std::exp2;
constexpr int m = 3;
const T cx = 3.5;
const auto x = make_fvar<T,m>(cx);
constexpr int exp = 3;
auto y = ldexp(x,exp);
BOOST_REQUIRE(y.derivative(0) == ldexp(cx,exp));
BOOST_REQUIRE(y.derivative(1) == exp2(exp));
BOOST_REQUIRE(y.derivative(2) == 0.0);
BOOST_REQUIRE(y.derivative(3) == 0.0);
}
};
BOOST_AUTO_TEST_CASE(ldexp_test)
{
boost::fusion::for_each(bin_float_types, ldexp_test_test());
boost::fusion::for_each(multiprecision_float_types, ldexp_test_test());
}
struct cos_and_sin_test
{
template<typename T>
void operator()(const T&) const
{
using std::cos;
using std::sin;
const T eps = 200*std::numeric_limits<T>::epsilon(); // percent
constexpr int m = 5;
const T cx = boost::math::constants::third_pi<T>();
const auto x = make_fvar<T,m>(cx);
auto cos5 = cos(x);
BOOST_REQUIRE_CLOSE(cos5.derivative(0), cos(cx), eps);
BOOST_REQUIRE_CLOSE(cos5.derivative(1), -sin(cx), eps);
BOOST_REQUIRE_CLOSE(cos5.derivative(2), -cos(cx), eps);
BOOST_REQUIRE_CLOSE(cos5.derivative(3), sin(cx), eps);
BOOST_REQUIRE_CLOSE(cos5.derivative(4), cos(cx), eps);
BOOST_REQUIRE_CLOSE(cos5.derivative(5), -sin(cx), eps);
auto sin5 = sin(x);
BOOST_REQUIRE_CLOSE(sin5.derivative(0), sin(cx), eps);
BOOST_REQUIRE_CLOSE(sin5.derivative(1), cos(cx), eps);
BOOST_REQUIRE_CLOSE(sin5.derivative(2), -sin(cx), eps);
BOOST_REQUIRE_CLOSE(sin5.derivative(3), -cos(cx), eps);
BOOST_REQUIRE_CLOSE(sin5.derivative(4), sin(cx), eps);
BOOST_REQUIRE_CLOSE(sin5.derivative(5), cos(cx), eps);
// Test Order = 0 for codecov
auto cos0 = cos(make_fvar<T,0>(cx));
BOOST_REQUIRE_CLOSE(cos0.derivative(0), cos(cx), eps);
auto sin0 = sin(make_fvar<T,0>(cx));
BOOST_REQUIRE_CLOSE(sin0.derivative(0), sin(cx), eps);
}
};
BOOST_AUTO_TEST_CASE(cos_and_sin)
{
boost::fusion::for_each(bin_float_types, cos_and_sin_test());
}
struct acos_test_test
{
template<typename T>
void operator()(const T&) const
{
const T eps = 300*std::numeric_limits<T>::epsilon(); // percent
using std::acos;
using std::pow;
using std::sqrt;
constexpr int m = 5;
const T cx = 0.5;
auto x = make_fvar<T,m>(cx);
auto y = acos(x);
BOOST_REQUIRE_CLOSE(y.derivative(0), acos(cx), eps);
BOOST_REQUIRE_CLOSE(y.derivative(1), -1/sqrt(1-cx*cx), eps);
BOOST_REQUIRE_CLOSE(y.derivative(2), -cx/pow(1-cx*cx,1.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(3), -(2*cx*cx+1)/pow(1-cx*cx,2.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(4), -3*cx*(2*cx*cx+3)/pow(1-cx*cx,3.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(5), -(24*(cx*cx+3)*cx*cx+9)/pow(1-cx*cx,4.5), eps);
}
};
BOOST_AUTO_TEST_CASE(acos_test)
{
boost::fusion::for_each(bin_float_types, acos_test_test());
}
struct acosh_test_test
{
template<typename T>
void operator()(const T&) const
{
const T eps = 300*std::numeric_limits<T>::epsilon(); // percent
using std::acosh;
constexpr int m = 5;
const T cx = 2;
auto x = make_fvar<T,m>(cx);
auto y = acosh(x);
//BOOST_REQUIRE(y.derivative(0) == acosh(cx)); // FAILS! acosh(2) is overloaded for integral types
BOOST_REQUIRE(y.derivative(0) == acosh(static_cast<T>(x)));
BOOST_REQUIRE_CLOSE(y.derivative(1), 1/boost::math::constants::root_three<T>(), eps);
BOOST_REQUIRE_CLOSE(y.derivative(2), -2/(3*boost::math::constants::root_three<T>()), eps);
BOOST_REQUIRE_CLOSE(y.derivative(3), 1/boost::math::constants::root_three<T>(), eps);
BOOST_REQUIRE_CLOSE(y.derivative(4), -22/(9*boost::math::constants::root_three<T>()), eps);
BOOST_REQUIRE_CLOSE(y.derivative(5), 227/(27*boost::math::constants::root_three<T>()), eps);
}
};
BOOST_AUTO_TEST_CASE(acosh_test)
{
boost::fusion::for_each(bin_float_types, acosh_test_test());
}
struct asin_test_test
{
template<typename T>
void operator()(const T&) const
{
const T eps = 300*std::numeric_limits<T>::epsilon(); // percent
using std::asin;
using std::pow;
using std::sqrt;
constexpr int m = 5;
const T cx = 0.5;
auto x = make_fvar<T,m>(cx);
auto y = asin(x);
BOOST_REQUIRE_CLOSE(y.derivative(0), asin(cx), eps);
BOOST_REQUIRE_CLOSE(y.derivative(1), 1/sqrt(1-cx*cx), eps);
BOOST_REQUIRE_CLOSE(y.derivative(2), cx/pow(1-cx*cx,1.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(3), (2*cx*cx+1)/pow(1-cx*cx,2.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(4), 3*cx*(2*cx*cx+3)/pow(1-cx*cx,3.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(5), (24*(cx*cx+3)*cx*cx+9)/pow(1-cx*cx,4.5), eps);
}
};
BOOST_AUTO_TEST_CASE(asin_test)
{
boost::fusion::for_each(bin_float_types, asin_test_test());
}
struct asin_infinity_test
{
template<typename T>
void operator()(const T&) const
{
const T eps = 100*std::numeric_limits<T>::epsilon(); // percent
constexpr int m = 5;
auto x = make_fvar<T,m>(1);
auto y = asin(x);
//std::cout << "asin(1) = " << y << std::endl; // depth(1)(1.5707963267949,inf,inf,-nan,-nan,-nan)
BOOST_REQUIRE_CLOSE(y.derivative(0), boost::math::constants::half_pi<T>(), eps); // MacOS is not exact
BOOST_REQUIRE(y.derivative(1) == std::numeric_limits<T>::infinity());
}
};
BOOST_AUTO_TEST_CASE(asin_infinity)
{
boost::fusion::for_each(bin_float_types, asin_infinity_test());
boost::fusion::for_each(multiprecision_float_types, asin_infinity_test());
}
struct asin_derivative_test
{
template<typename T>
void operator()(const T&) const
{
const T eps = 300*std::numeric_limits<T>::epsilon(); // percent
using std::pow;
using std::sqrt;
constexpr int m = 4;
const T cx = 0.5;
auto x = make_fvar<T,m>(cx);
auto y = 1-x*x;
BOOST_REQUIRE(y.derivative(0) == 1-cx*cx);
BOOST_REQUIRE(y.derivative(1) == -2*cx);
BOOST_REQUIRE(y.derivative(2) == -2);
BOOST_REQUIRE(y.derivative(3) == 0);
BOOST_REQUIRE(y.derivative(4) == 0);
y = sqrt(y);
BOOST_REQUIRE(y.derivative(0) == sqrt(1-cx*cx));
BOOST_REQUIRE_CLOSE(y.derivative(1), -cx/sqrt(1-cx*cx), eps);
BOOST_REQUIRE_CLOSE(y.derivative(2), -1/pow(1-cx*cx,1.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(3), -3*cx/pow(1-cx*cx,2.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(4), -(12*cx*cx+3)/pow(1-cx*cx,3.5), eps);
y = y.inverse(); // asin'(x) = 1 / sqrt(1-x*x).
BOOST_REQUIRE_CLOSE(y.derivative(0), 1/sqrt(1-cx*cx), eps);
BOOST_REQUIRE_CLOSE(y.derivative(1), cx/pow(1-cx*cx,1.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(2), (2*cx*cx+1)/pow(1-cx*cx,2.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(3), 3*cx*(2*cx*cx+3)/pow(1-cx*cx,3.5), eps);
BOOST_REQUIRE_CLOSE(y.derivative(4), (24*(cx*cx+3)*cx*cx+9)/pow(1-cx*cx,4.5), eps);
}
};
BOOST_AUTO_TEST_CASE(asin_derivative)
{
boost::fusion::for_each(bin_float_types, asin_derivative_test());
}
struct asinh_test_test
{
template<typename T>
void operator()(const T&) const
{
const T eps = 300*std::numeric_limits<T>::epsilon(); // percent
using std::asinh;
constexpr int m = 5;
const T cx = 1;
auto x = make_fvar<T,m>(cx);
auto y = asinh(x);
//BOOST_REQUIRE(y.derivative(0) == asinh(cx)); // Fails for gcc-mingw - similar to acosh()?
BOOST_REQUIRE(y.derivative(0) == asinh(static_cast<T>(x)));
BOOST_REQUIRE_CLOSE(y.derivative(1), 1/boost::math::constants::root_two<T>(), eps);
BOOST_REQUIRE_CLOSE(y.derivative(2), -1/(2*boost::math::constants::root_two<T>()), eps);
BOOST_REQUIRE_CLOSE(y.derivative(3), 1/(4*boost::math::constants::root_two<T>()), eps);
BOOST_REQUIRE_CLOSE(y.derivative(4), 3/(8*boost::math::constants::root_two<T>()), eps);
BOOST_REQUIRE_CLOSE(y.derivative(5), -39/(16*boost::math::constants::root_two<T>()), eps);
}
};
BOOST_AUTO_TEST_CASE(asinh_test)
{
boost::fusion::for_each(bin_float_types, asinh_test_test());
}
BOOST_AUTO_TEST_SUITE_END()