// Copyright Maksym Zhelyeznyakov 2025-2026. // 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_reverse.hpp" #include #include #include BOOST_AUTO_TEST_SUITE(test_stl_supported_functions) using namespace rdiff; BOOST_AUTO_TEST_CASE_TEMPLATE(test_fabs, T, all_float_types) { RandomSample rng_neg{-10, 0}; T x_neg_v = rng_neg.next(); T test_fabs_neg_v = fabs(x_neg_v); RandomSample rng_pos{0, 10}; T x_pos_v = rng_pos.next(); T test_fabs_pos_v = fabs(x_pos_v); rvar x_neg = x_neg_v; rvar x_pos = x_pos_v; rvar fabs_neg = fabs(x_neg); rvar fabs_pos = fabs(x_pos); BOOST_REQUIRE_CLOSE_FRACTION(fabs_neg.item(), test_fabs_neg_v, boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(fabs_pos.item(), test_fabs_pos_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); fabs_neg.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_neg.adjoint(), -1, boost_close_tol()); ; tape.zero_grad(); fabs_pos.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_pos.adjoint(), 1, boost_close_tol()); rvar z = fabs(x_neg * x_neg * x_neg); tape.zero_grad(); z.backward(); T expected_grad = 3 * x_neg_v * x_neg_v * (-1); BOOST_REQUIRE_CLOSE_FRACTION(x_neg.adjoint(), expected_grad, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_abs, T, all_float_types) { RandomSample rng_neg{-1, 0}; T x_neg_v = rng_neg.next(); T test_abs_neg_v = fabs(x_neg_v); RandomSample rng_pos{0, 1}; T x_pos_v = rng_pos.next(); T test_abs_pos_v = abs(x_pos_v); rvar x_neg = x_neg_v; rvar x_pos = x_pos_v; rvar abs_neg = abs(x_neg); rvar abs_pos = abs(x_pos); BOOST_REQUIRE_CLOSE_FRACTION(abs_neg.item(), test_abs_neg_v, boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(abs_pos.item(), test_abs_pos_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); abs_neg.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_neg.adjoint(), -1, boost_close_tol()); tape.zero_grad(); abs_pos.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_pos.adjoint(), 1, boost_close_tol()); tape.zero_grad(); rvar z = abs(x_neg * x_neg * x_neg); z.backward(); T expected_grad = 3 * x_neg_v * x_neg_v * -1; BOOST_REQUIRE_CLOSE_FRACTION(x_neg.adjoint(), expected_grad, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_ceil, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_ceil_v = ceil(x_v); rvar x_rvar = x_v; rvar x_ceil = ceil(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_ceil.item(), test_ceil_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x_ceil.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), 0.0, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_floor, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_ceil_v = floor(x_v); rvar x_rvar = x_v; rvar x_floor = floor(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_floor.item(), test_ceil_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x_floor.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), 0.0, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_exp, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); rng.next(); T test_func_v = exp(x_v); rvar x_rvar = x_v; rvar x_func = exp(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = exp(-x_rvar * x_rvar); T expected_deriv = -2.0 * x_v * exp(-x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_pow, T, all_float_types) { RandomSample base_rng{0, 2}; RandomSample exp_rng{-5, 5}; T x_v = 2.0; base_rng.next(); T y_v = 0.5; exp_rng.next(); rvar x_rvar = x_v; rvar y_rvar = y_v; T expected_v = pow(x_v, y_v); rvar v_func = pow(x_rvar, y_rvar); BOOST_REQUIRE_CLOSE_FRACTION(expected_v, v_func.item(), boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); v_func.backward(); T df_dx_expected = y_v * pow(x_v, y_v - 1); T df_dy_expected = pow(x_v, y_v) * log(x_v); BOOST_REQUIRE_CLOSE_FRACTION(df_dx_expected, x_rvar.adjoint(), boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(df_dy_expected, y_rvar.adjoint(), boost_close_tol()); rvar v_rvar_by_float = pow(x_rvar, y_v); rvar v_float_by_rvar = pow(x_v, y_rvar); BOOST_REQUIRE_CLOSE_FRACTION(expected_v, v_rvar_by_float.item(), boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(expected_v, v_float_by_rvar.item(), boost_close_tol()); tape.zero_grad(); v_rvar_by_float.backward(); BOOST_REQUIRE_CLOSE_FRACTION(df_dx_expected, x_rvar.adjoint(), boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(0.0, y_rvar.adjoint(), boost_close_tol()); tape.zero_grad(); v_float_by_rvar.backward(); BOOST_REQUIRE_CLOSE_FRACTION(0.0, x_rvar.adjoint(), boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(df_dy_expected, y_rvar.adjoint(), boost_close_tol()); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_log, T, all_float_types) { RandomSample rng{0.0001, 2}; T x_v = rng.next(); T test_func_v = log(x_v); rvar x_rvar = x_v; rvar x_func = log(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = log(x_rvar * x_rvar); T expected_deriv = 2.0 / x_v; test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_sqrt, T, all_float_types) { RandomSample rng{0.0001, 2}; T x_v = rng.next(); T test_func_v = sqrt(x_v); rvar x_rvar = x_v; rvar x_func = sqrt(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = sqrt(x_rvar * x_rvar * x_rvar); T expected_deriv = 3.0 * sqrt(x_v) / 2.0; test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_frexp, T, all_float_types) { /** @brief frexp(x) returns mantissa in * x = m * 2 ^ e * * its implemented as x/2^e, and technically has * a derivative except at finitely many * discontinuous points. In reverse mode autodiff * its treated as a piecewise linear funcfion. */ RandomSample rng{-1, 1}; T x_v = rng.next(); int i1, i2; T test_func_v = frexp(x_v, &i1); rvar x_rvar = x_v; rvar x_func = frexp(x_rvar, &i2); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x_func.backward(); // int i3, i4; int i4; frexp(x_v, &i4); // T h = 0.001; // T f1 = frexp(x_v + h, &i3); // T f2 = frexp(x_v, &i4); T expected_deriv = 1.0 / pow(2.0, i4); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_cos, T, all_float_types) { RandomSample rng{-10, 10}; T x_v = rng.next(); T test_func_v = cos(x_v); rvar x_rvar = x_v; rvar x_func = cos(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, 10 * boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = cos(x_rvar * x_rvar); T expected_deriv = -2 * x_v * sin(x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, 10 * boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_sin, T, all_float_types) { RandomSample rng{-10, 10}; T x_v = rng.next(); T test_func_v = sin(x_v); rvar x_rvar = x_v; rvar x_func = sin(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, 10 * boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = sin(x_rvar * x_rvar); T expected_deriv = 2 * x_v * cos(x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_tan, T, all_float_types) { RandomSample rng{-10, 10}; T x_v = rng.next(); T test_func_v = tan(x_v); rvar x_rvar = x_v; rvar x_func = tan(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, 10 * boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = tan(x_rvar * x_rvar); T expected_deriv = 2 * x_v * 1 / (cos(x_v * x_v) * cos(x_v * x_v)); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, 10 * boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_acos, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_func_v = acos(x_v); rvar x_rvar = x_v; rvar x_func = acos(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = acos(x_rvar * x_rvar); T expected_deriv = -2 * x_v / sqrt(1 - x_v * x_v * x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_asin, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_func_v = asin(x_v); rvar x_rvar = x_v; rvar x_func = asin(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = asin(x_rvar * x_rvar); T expected_deriv = 2 * x_v / sqrt(1 - x_v * x_v * x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_atan, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_func_v = atan(x_v); rvar x_rvar = x_v; rvar x_func = atan(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = atan(x_rvar * x_rvar); T expected_deriv = 2 * x_v / (1 + x_v * x_v * x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_atan2, T, all_float_types) { RandomSample rng{-10, 10}; T y_v = rng.next(); T x_v = rng.next(); T expected = atan2(y_v, x_v); rvar y_rvar = y_v; rvar x_rvar = x_v; { rvar z = atan2(y_rvar, x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(z.item(), expected, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); z.backward(); T denom = x_v * x_v + y_v * y_v; T dy_expected = x_v / denom; T dx_expected = -y_v / denom; BOOST_REQUIRE_CLOSE_FRACTION(y_rvar.adjoint(), dy_expected, boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), dx_expected, boost_close_tol()); } { rvar y2 = y_v; rvar z = atan2(y2, x_v); BOOST_REQUIRE_CLOSE_FRACTION(z.item(), expected, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); z.backward(); T dy_expected = x_v / (x_v * x_v + y_v * y_v); BOOST_REQUIRE_CLOSE_FRACTION(y2.adjoint(), dy_expected, boost_close_tol()); } { rvar x2 = x_v; rvar z = atan2(y_v, x2); BOOST_REQUIRE_CLOSE_FRACTION(z.item(), expected, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); z.backward(); T dx_expected = -y_v / (x_v * x_v + y_v * y_v); BOOST_REQUIRE_CLOSE_FRACTION(x2.adjoint(), dx_expected, boost_close_tol()); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_trunc, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_trunc_v = trunc(x_v); rvar x_rvar = x_v; rvar x_trunc = trunc(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_trunc.item(), test_trunc_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x_trunc.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), 0.0, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_fmod, T, all_float_types) { using boost::math::tools::fmod_workaround; RandomSample rng_x{-1, 1}; RandomSample rng_y{-1, 1}; T x_v = rng_x.next(); T y_v = rng_y.next(); while (y_v == 0.0) { y_v = rng_y.next(); } T expected_fmod = fmod(x_v, y_v); rvar x_rvar = x_v; rvar y_rvar = y_v; rvar fmod_rvar = fmod(x_rvar, y_rvar); BOOST_REQUIRE_CLOSE_FRACTION(fmod_rvar.item(), expected_fmod, 10 * boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); fmod_rvar.backward(); T dx_expected = 1.0; T dy_expected = -1.0 * trunc(x_v / y_v); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), dx_expected, 10 * boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(y_rvar.adjoint(), dy_expected, 10 * boost_close_tol()); rvar fmod_rvar_float = fmod(x_rvar, y_v); BOOST_REQUIRE_CLOSE_FRACTION(fmod_rvar_float.item(), expected_fmod, 10 * boost_close_tol()); tape.zero_grad(); fmod_rvar_float.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), dx_expected, 10 * boost_close_tol()); rvar fmod_float_rvar = fmod(x_v, y_rvar); BOOST_REQUIRE_CLOSE_FRACTION(fmod_float_rvar.item(), expected_fmod, 10 * boost_close_tol()); tape.zero_grad(); fmod_float_rvar.backward(); BOOST_REQUIRE_CLOSE_FRACTION(y_rvar.adjoint(), dy_expected, 10 * boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_round, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_round_v = round(x_v); rvar x_rvar = x_v; rvar x_round = round(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_round.item(), test_round_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x_round.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), 0.0, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_iround, T, all_float_types) { using boost::math::iround; RandomSample rng{-1, 1}; T x_v = rng.next(); int test_round_v = iround(x_v); rvar x_rvar = x_v; int x_round = iround(x_rvar); BOOST_REQUIRE_EQUAL(x_round, test_round_v); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_lround, T, all_float_types) { using boost::math::lround; RandomSample rng{-1, 1}; T x_v = rng.next(); long test_round_v = lround(x_v); rvar x_rvar = x_v; long x_round = lround(x_rvar); BOOST_REQUIRE_EQUAL(x_round, test_round_v); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_llround, T, all_float_types) { using boost::math::llround; RandomSample rng{-1, 1}; T x_v = rng.next(); long long test_round_v = llround(x_v); rvar x_rvar = x_v; long long x_round = llround(x_rvar); BOOST_REQUIRE_EQUAL(x_round, test_round_v); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_itrunc, T, all_float_types) { using boost::math::itrunc; RandomSample rng{-1, 1}; T x_v = rng.next(); int test_trunc_v = itrunc(x_v); rvar x_rvar = x_v; int x_trunc = itrunc(x_rvar); BOOST_REQUIRE_EQUAL(x_trunc, test_trunc_v); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_ltrunc, T, all_float_types) { using boost::math::ltrunc; RandomSample rng{-1, 1}; T x_v = rng.next(); long test_trunc_v = ltrunc(x_v); rvar x_rvar = x_v; long x_trunc = ltrunc(x_rvar); BOOST_REQUIRE_EQUAL(x_trunc, test_trunc_v); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_lltrunc, T, all_float_types) { using boost::math::lltrunc; RandomSample rng{-1, 1}; T x_v = rng.next(); int test_trunc_v = lltrunc(x_v); rvar x_rvar = x_v; int x_trunc = lltrunc(x_rvar); BOOST_REQUIRE_EQUAL(x_trunc, test_trunc_v); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_sinh, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_func_v = sinh(x_v); rvar x_rvar = x_v; rvar x_func = sinh(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = sinh(x_rvar * x_rvar); T expected_deriv = 2 * x_v * cosh(x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_cosh, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_func_v = cosh(x_v); rvar x_rvar = x_v; rvar x_func = cosh(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = cosh(x_rvar * x_rvar); T expected_deriv = 2 * x_v * sinh(x_v * x_v); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_tanh, T, all_float_types) { RandomSample rng{-1, 1}; T x_v = rng.next(); T test_func_v = tanh(x_v); rvar x_rvar = x_v; rvar x_func = tanh(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = tanh(x_rvar * x_rvar); T expected_deriv = 2 * x_v / (cosh(x_v * x_v) * cosh(x_v * x_v)); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_ldexp, T, all_float_types) { RandomSample rng{-2, 2}; T x_v = rng.next(); int exp_i = 3; T test_func_v = ldexp(x_v, exp_i); rvar x_rvar = x_v; rvar x_func = ldexp(x_rvar, exp_i); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); // f(x) = ldexp(x^2, exp_i) = x^2 * 2^exp_i rvar test_func_2 = ldexp(x_rvar * x_rvar, exp_i); // Expected derivative: d/dx [x^2 * 2^exp_i] = 2x * 2^exp_i T expected_deriv = static_cast(2) * x_v * pow(static_cast(2.0), exp_i); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_ldexp_huge_val, T, all_float_types) { const T huge_val = std::numeric_limits::infinity(); const int exp = 1; T result_huge = ldexp(huge_val, exp); BOOST_REQUIRE_EQUAL(result_huge, huge_val); rvar x_huge = huge_val; rvar func_huge = ldexp(x_huge, exp); BOOST_REQUIRE_EQUAL(func_huge.item(), huge_val); func_huge.backward(); T expected_adjoint = static_cast(2); BOOST_REQUIRE_EQUAL(x_huge.adjoint(), expected_adjoint); get_active_tape().clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_log10, T, all_float_types) { RandomSample rng{0.0001, 10}; T x_v = rng.next(); T test_func_v = log10(x_v); rvar x_rvar = x_v; rvar x_func = log10(x_rvar); BOOST_REQUIRE_CLOSE_FRACTION(x_func.item(), test_func_v, boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); rvar test_func_2 = log10(x_rvar * x_rvar); T expected_deriv = T{2.0} / (x_v * log(T{10.0})); test_func_2.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_rvar.adjoint(), expected_deriv, boost_close_tol()); tape.clear(); } template T test_function_1(const T& x, const T& y) { /** @brief test function for * log * abs * exp * pow * sqrt */ auto y1 = log(1 + abs(x)) * exp(y); auto y2 = pow(x + y, 2.5); auto y3 = sqrt(1 + x * y); return y1 + y2 - y3; } template std::vector grad_test_func_1_analytical(T x, T y) { T f_x = static_cast(-1.0 / 2.0 * y / sqrt(x * y + 1) + 2.5 * pow(x + y, 1.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_y = static_cast(-1.0 / 2.0 * x / sqrt(x * y + 1) + 2.5 * pow(x + y, 1.5) + exp(y) * log(sqrt(pow(x, 2)) + 1)); return {f_x, f_y}; } template std::vector> grad2_test_func_1_analytical(T x, T y) { T f_xx = static_cast((1.0 / 4.0) * pow(y, 2) / pow(x * y + 1, 3.0 / 2.0) + 3.75 * sqrt(x + y) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2)); T f_xy = static_cast((1.0 / 4.0) * x * y / pow(x * y + 1, 3.0 / 2.0) + 3.75 * sqrt(x + y) - (1.0 / 2.0) / sqrt(x * y + 1) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yx = static_cast((1.0 / 4.0) * x * y / pow(x * y + 1, 3.0 / 2.0) + 3.75 * sqrt(x + y) - (1.0 / 2.0) / sqrt(x * y + 1) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yy = static_cast((1.0 / 4.0) * pow(x, 2) / pow(x * y + 1, 3.0 / 2.0) + 3.75 * sqrt(x + y) + exp(y) * log(sqrt(pow(x, 2)) + 1)); return {{f_xx, f_xy}, {f_yx, f_yy}}; } template std::vector>> grad3_test_func_1_analytical(T x, T y) { T f_xxx = static_cast(-3.0 / 8.0 * pow(y, 3) / pow(x * y + 1, 5.0 / 2.0) + 1.875 * pow(x + y, -0.5) + 2 * sqrt(pow(x, 2)) * exp(y) / (x * pow(sqrt(pow(x, 2)) + 1, 3))); T f_xxy = static_cast(-3.0 / 8.0 * x * pow(y, 2) / pow(x * y + 1, 5.0 / 2.0) + (1.0 / 2.0) * y / pow(x * y + 1, 3.0 / 2.0) + 1.875 * pow(x + y, -0.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2)); T f_xyx = static_cast(-3.0 / 8.0 * x * pow(y, 2) / pow(x * y + 1, 5.0 / 2.0) + (1.0 / 2.0) * y / pow(x * y + 1, 3.0 / 2.0) + 1.875 * pow(x + y, -0.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2)); T f_xyy = static_cast(-3.0 / 8.0 * pow(x, 2) * y / pow(x * y + 1, 5.0 / 2.0) + (1.0 / 2.0) * x / pow(x * y + 1, 3.0 / 2.0) + 1.875 * pow(x + y, -0.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yxx = static_cast(-3.0 / 8.0 * x * pow(y, 2) / pow(x * y + 1, 5.0 / 2.0) + (1.0 / 2.0) * y / pow(x * y + 1, 3.0 / 2.0) + 1.875 * pow(x + y, -0.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2)); T f_yxy = static_cast(-3.0 / 8.0 * pow(x, 2) * y / pow(x * y + 1, 5.0 / 2.0) + (1.0 / 2.0) * x / pow(x * y + 1, 3.0 / 2.0) + 1.875 * pow(x + y, -0.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yyx = static_cast(-3.0 / 8.0 * pow(x, 2) * y / pow(x * y + 1, 5.0 / 2.0) + (1.0 / 2.0) * x / pow(x * y + 1, 3.0 / 2.0) + 1.875 * pow(x + y, -0.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yyy = static_cast(-3.0 / 8.0 * pow(x, 3) / pow(x * y + 1, 5.0 / 2.0) + 1.875 * pow(x + y, -0.5) + exp(y) * log(sqrt(pow(x, 2)) + 1)); return {{{f_xxx, f_xxy}, {f_xyx, f_xyy}}, {{f_yxx, f_yxy}, {f_yyx, f_yyy}}}; } template std::vector>>> grad4_test_func_1_analytical(T x, T y) { T f_xxxx = static_cast((15.0 / 16.0) * pow(y, 4) / pow(x * y + 1, 7.0 / 2.0) - 0.9375 * pow(x + y, -1.5) - 6 * exp(y) / pow(sqrt(pow(x, 2)) + 1, 4)); T f_xxxy = static_cast((15.0 / 16.0) * x * pow(y, 3) / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(y, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + 2 * sqrt(pow(x, 2)) * exp(y) / (x * pow(sqrt(pow(x, 2)) + 1, 3))); T f_xxyx = static_cast((15.0 / 16.0) * x * pow(y, 3) / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(y, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + 2 * sqrt(pow(x, 2)) * exp(y) / (x * pow(sqrt(pow(x, 2)) + 1, 3))); T f_xxyy = static_cast((15.0 / 16.0) * pow(x, 2) * pow(y, 2) / pow(x * y + 1, 7.0 / 2.0) - 3.0 / 2.0 * x * y / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2) + (1.0 / 2.0) / pow(x * y + 1, 3.0 / 2.0)); T f_xyxx = static_cast((15.0 / 16.0) * x * pow(y, 3) / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(y, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + 2 * sqrt(pow(x, 2)) * exp(y) / (x * pow(sqrt(pow(x, 2)) + 1, 3))); T f_xyxy = static_cast((15.0 / 16.0) * pow(x, 2) * pow(y, 2) / pow(x * y + 1, 7.0 / 2.0) - 3.0 / 2.0 * x * y / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2) + (1.0 / 2.0) / pow(x * y + 1, 3.0 / 2.0)); T f_xyyx = static_cast((15.0 / 16.0) * pow(x, 2) * pow(y, 2) / pow(x * y + 1, 7.0 / 2.0) - 3.0 / 2.0 * x * y / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2) + (1.0 / 2.0) / pow(x * y + 1, 3.0 / 2.0)); T f_xyyy = static_cast((15.0 / 16.0) * pow(x, 3) * y / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(x, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yxxx = static_cast((15.0 / 16.0) * x * pow(y, 3) / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(y, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + 2 * sqrt(pow(x, 2)) * exp(y) / (x * pow(sqrt(pow(x, 2)) + 1, 3))); T f_yxxy = static_cast((15.0 / 16.0) * pow(x, 2) * pow(y, 2) / pow(x * y + 1, 7.0 / 2.0) - 3.0 / 2.0 * x * y / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2) + (1.0 / 2.0) / pow(x * y + 1, 3.0 / 2.0)); T f_yxyx = static_cast((15.0 / 16.0) * pow(x, 2) * pow(y, 2) / pow(x * y + 1, 7.0 / 2.0) - 3.0 / 2.0 * x * y / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2) + (1.0 / 2.0) / pow(x * y + 1, 3.0 / 2.0)); T f_yxyy = static_cast((15.0 / 16.0) * pow(x, 3) * y / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(x, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yyxx = static_cast((15.0 / 16.0) * pow(x, 2) * pow(y, 2) / pow(x * y + 1, 7.0 / 2.0) - 3.0 / 2.0 * x * y / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) - exp(y) / pow(sqrt(pow(x, 2)) + 1, 2) + (1.0 / 2.0) / pow(x * y + 1, 3.0 / 2.0)); T f_yyxy = static_cast((15.0 / 16.0) * pow(x, 3) * y / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(x, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yyyx = static_cast((15.0 / 16.0) * pow(x, 3) * y / pow(x * y + 1, 7.0 / 2.0) - 9.0 / 8.0 * pow(x, 2) / pow(x * y + 1, 5.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + sqrt(pow(x, 2)) * exp(y) / (x * (sqrt(pow(x, 2)) + 1))); T f_yyyy = static_cast((15.0 / 16.0) * pow(x, 4) / pow(x * y + 1, 7.0 / 2.0) - 0.9375 * pow(x + y, -1.5) + exp(y) * log(sqrt(pow(x, 2)) + 1)); return {{{{f_xxxx, f_xxxy}, {f_xxyx, f_xxyy}}, {{f_xyxx, f_xyxy}, {f_xyyx, f_xyyy}}}, {{{f_yxxx, f_yxxy}, {f_yxyx, f_yxyy}}, {{f_yyxx, f_yyxy}, {f_yyyx, f_yyyy}}}}; } BOOST_AUTO_TEST_CASE_TEMPLATE(test_func_1_first_derivative, T, all_float_types) { RandomSample rng{0.0001, 10}; T x = rng.next(); T y = rng.next(); rvar x_ad = x; rvar y_ad = y; //T fv = test_function_1(x, y); rvar f_ad = test_function_1(x_ad, y_ad); auto grad_f_analytical = grad_test_func_1_analytical(x, y); /* intended use case */ f_ad.backward(); BOOST_REQUIRE_CLOSE_FRACTION(x_ad.adjoint(), grad_f_analytical[0], boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(y_ad.adjoint(), grad_f_analytical[1], boost_close_tol()); gradient_tape& tape = get_active_tape(); tape.zero_grad(); /* grad test */ auto grad_func_test_grad = grad(f_ad, &x_ad, &y_ad); /* grad_nd test */ auto grad_nd_func_test_grad = grad_nd<1>(f_ad, &x_ad, &y_ad); for (int i = 0; i < 2; i++) { BOOST_REQUIRE_CLOSE_FRACTION(grad_func_test_grad[i], grad_f_analytical[i], boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(grad_nd_func_test_grad[i], grad_f_analytical[i], boost_close_tol()); } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_func_1_second_derivative_and_hessian, T, all_float_types) { RandomSample rng{0.0001, 10}; T x = rng.next(); T y = rng.next(); rvar x_ad = x; rvar y_ad = y; //T fv = test_function_1(x, y); rvar f_ad = test_function_1(x_ad, y_ad); gradient_tape& tape = get_active_tape(); tape.zero_grad(); auto hess_analytical = grad2_test_func_1_analytical(x, y); auto hess_func_test = hess(f_ad, &x_ad, &y_ad); auto grad_nd_func_test = grad_nd<2>(f_ad, &x_ad, &y_ad); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { BOOST_REQUIRE_CLOSE_FRACTION(hess_func_test[i][j], hess_analytical[i][j], boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(hess_func_test[i][j], grad_nd_func_test[i][j], boost_close_tol()); } } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_func_1_order_3_der, T, all_float_types) { RandomSample rng{0.0001, 10}; T x = rng.next(); T y = rng.next(); rvar x_ad = x; rvar y_ad = y; //T fv = test_function_1(x, y); rvar f_ad = test_function_1(x_ad, y_ad); gradient_tape& tape = get_active_tape(); tape.zero_grad(); auto df3 = grad3_test_func_1_analytical(x, y); auto grad_ad = grad(f_ad, &x_ad, &y_ad); std::vector>> grad_tensor; for (int i = 0; i < 2; i++) { auto df_hess = hess(grad_ad[i], &x_ad, &y_ad); grad_tensor.push_back(df_hess); } auto grad_nd_func_test = grad_nd<3>(f_ad, &x_ad, &y_ad); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { BOOST_REQUIRE_CLOSE_FRACTION(grad_tensor[i][j][k], df3[i][j][k], boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(grad_nd_func_test[i][j][k], df3[i][j][k], boost_close_tol()); } } } } BOOST_AUTO_TEST_CASE_TEMPLATE(test_func_1_fourth_derivative, T, all_float_types) { RandomSample rng{0.0001, 10}; T x_val = rng.next(); T y_val = rng.next(); rvar x_ad = x_val; rvar y_ad = y_val; rvar f_ad = test_function_1(x_ad, y_ad); auto df4 = grad4_test_func_1_analytical(x_val, y_val); auto gf = grad(f_ad, &x_ad, &y_ad); std::array, 2>, 2>, 2> ggggf; for (int i = 0; i < 2; ++i) { auto hess1 = grad(gf[i], &x_ad, &y_ad); for (int j = 0; j < 2; ++j) { auto hess2 = grad(hess1[j], &x_ad, &y_ad); for (int k = 0; k < 2; ++k) { auto hess3 = grad(hess2[k], &x_ad, &y_ad); for (int l = 0; l < 2; ++l) { ggggf[i][j][k][l] = hess3[l]; } } } } auto grad_nd_func_test = grad_nd<4>(f_ad, &x_ad, &y_ad); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { for (int l = 0; l < 2; l++) { BOOST_REQUIRE_CLOSE_FRACTION(ggggf[i][j][k][l], df4[i][j][k][l], 1 * boost_close_tol()); BOOST_REQUIRE_CLOSE_FRACTION(grad_nd_func_test[i][j][k][l], df4[i][j][k][l], 1 * boost_close_tol()); } } } } } BOOST_AUTO_TEST_SUITE_END()