// 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" BOOST_AUTO_TEST_SUITE(explicit_rvar_constructors) BOOST_AUTO_TEST_CASE_TEMPLATE(rvar_constructors_and_utils, T, all_float_types) { RandomSample rng{T(-100), T(100)}; /* raw constructors */ T x_value = rng.next(); rdiff::rvar x0(x_value); rdiff::rvar x1(x_value); rdiff::rvar x2(x_value); /* raw eval */ BOOST_CHECK_EQUAL(x_value, x0.evaluate()); BOOST_CHECK_EQUAL(x_value, x1.evaluate().evaluate()); BOOST_CHECK_EQUAL(x_value, x2.evaluate().evaluate().evaluate()); /* get item helper */ BOOST_CHECK_EQUAL(x_value, x0.item()); BOOST_CHECK_EQUAL(x_value, x1.item()); BOOST_CHECK_EQUAL(x_value, x2.item()); } BOOST_AUTO_TEST_CASE_TEMPLATE(make_rvar_constructors, T, all_float_types) { RandomSample rng{T(-100), T(100)}; T x_value = rng.next(); rdiff::rvar x1 = rdiff::make_rvar(x_value); rdiff::rvar x2 = rdiff::make_rvar(x_value); rdiff::rvar x3 = rdiff::make_rvar(x_value); BOOST_CHECK_EQUAL(x_value, x1.item()); BOOST_CHECK_EQUAL(x_value, x2.item()); BOOST_CHECK_EQUAL(x_value, x3.item()); T result = x_value+x_value; auto z1 = x1+x1; auto z2 = x2+x2; auto z3 = x3+x3; rdiff::rvar y1 = rdiff::make_rvar(z1); rdiff::rvar y2 = rdiff::make_rvar(z2); rdiff::rvar y3 = rdiff::make_rvar(z3); BOOST_CHECK_EQUAL(y1.item(),result); BOOST_CHECK_EQUAL(y2.item(),result); BOOST_CHECK_EQUAL(y3.item(),result); } BOOST_AUTO_TEST_CASE_TEMPLATE(copy_construction, T, all_float_types) { RandomSample rng{-100, 100}; T original_value_1 = rng.next(); /* copy constructor checks */ rdiff::rvar original_rvar_1(original_value_1); rdiff::rvar copied_rvar_1 = original_rvar_1; BOOST_CHECK_EQUAL(copied_rvar_1.item(), original_value_1); /* change original, copy stays same */ T new_value_1 = rng.next(); original_rvar_1 = new_value_1; BOOST_CHECK_EQUAL(copied_rvar_1.item(), original_value_1); BOOST_CHECK_EQUAL(original_rvar_1.item(), new_value_1); T original_value_2 = rng.next(); rdiff::rvar original_rvar_2(original_value_2); rdiff::rvar copied_rvar_2 = original_rvar_2; BOOST_CHECK_EQUAL(copied_rvar_2.item(), original_value_2); T new_value_2 = rng.next(); original_rvar_2 = new_value_2; BOOST_CHECK_EQUAL(copied_rvar_2.item(), original_value_2); BOOST_CHECK_EQUAL(original_rvar_2.item(), new_value_2); T original_value_3 = rng.next(); rdiff::rvar original_rvar_3(original_value_3); rdiff::rvar copied_rvar_3 = original_rvar_3; BOOST_CHECK_EQUAL(copied_rvar_3.item(), original_value_3); T new_value_3 = rng.next(); original_rvar_3 = new_value_3; BOOST_CHECK_EQUAL(copied_rvar_3.item(), original_value_3); BOOST_CHECK_EQUAL(original_rvar_3.item(), new_value_3); } BOOST_AUTO_TEST_CASE_TEMPLATE(copy_assignment, T, all_float_types) { RandomSample rng{-100, 100}; T source_value_1 = rng.next(); T initial_target_value_1 = rng.next(); rdiff::rvar source_rvar_1(source_value_1); rdiff::rvar target_rvar_1(initial_target_value_1); target_rvar_1 = source_rvar_1; BOOST_CHECK_EQUAL(target_rvar_1.item(), source_value_1); T new_source_value_1 = rng.next(); source_rvar_1 = new_source_value_1; BOOST_CHECK_EQUAL(target_rvar_1.item(), source_value_1); BOOST_CHECK_EQUAL(source_rvar_1.item(), new_source_value_1); rdiff::rvar self_assign_rvar_1(rng.next()); T value_before_self_assign_1 = self_assign_rvar_1.item(); self_assign_rvar_1 = self_assign_rvar_1; BOOST_CHECK_EQUAL(self_assign_rvar_1.item(), value_before_self_assign_1); T source_value_2 = rng.next(); T initial_target_value_2 = rng.next(); rdiff::rvar source_rvar_2(source_value_2); rdiff::rvar target_rvar_2(initial_target_value_2); target_rvar_2 = source_rvar_2; BOOST_CHECK_EQUAL(target_rvar_2.item(), source_value_2); T new_source_value_2 = rng.next(); source_rvar_2 = new_source_value_2; BOOST_CHECK_EQUAL(target_rvar_2.item(), source_value_2); BOOST_CHECK_EQUAL(source_rvar_2.item(), new_source_value_2); T source_value_3 = rng.next(); T initial_target_value_3 = rng.next(); rdiff::rvar source_rvar_3(source_value_3); rdiff::rvar target_rvar_3(initial_target_value_3); target_rvar_3 = source_rvar_3; BOOST_CHECK_EQUAL(target_rvar_3.item(), source_value_3); T new_source_value_3 = rng.next(); source_rvar_3 = new_source_value_3; BOOST_CHECK_EQUAL(target_rvar_3.item(), source_value_3); BOOST_CHECK_EQUAL(source_rvar_3.item(), new_source_value_3); } BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_addition, T, all_float_types) { using namespace rdiff; RandomSample rng{-1, 1}; T x1_v = rng.next(); T x2_v = rng.next(); T expected = x1_v + x2_v; rvar x1 = x1_v; rvar x2 = x2_v; rvar orig_x1 = x1; rvar orig_x2 = x2; x1 += x2; BOOST_REQUIRE_EQUAL(x1.item(), expected); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x1.backward(); BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), 1.0, 300 * std::numeric_limits::epsilon()); BOOST_REQUIRE_CLOSE(orig_x2.adjoint(), 1.0, 300 * std::numeric_limits::epsilon()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_subtraction, T, all_float_types) { using namespace rdiff; RandomSample rng{-1, 1}; T x1_v = rng.next(); T x2_v = rng.next(); T expected = x1_v - x2_v; rvar x1 = x1_v; rvar x2 = x2_v; rvar orig_x1 = x1; rvar orig_x2 = x2; x1 -= x2; BOOST_REQUIRE_EQUAL(x1.item(), expected); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x1.backward(); BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), 1.0, 300 * std::numeric_limits::epsilon()); BOOST_REQUIRE_CLOSE(orig_x2.adjoint(), -1.0, 300 * std::numeric_limits::epsilon()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_multiplication, T, all_float_types) { using namespace rdiff; RandomSample rng{-1, 1}; T x1_v = rng.next(); T x2_v = rng.next(); T expected = x1_v * x2_v; rvar x1 = x1_v; rvar x2 = x2_v; rvar orig_x1 = x1; rvar orig_x2 = x2; x1 *= x2; BOOST_REQUIRE_EQUAL(x1.item(), expected); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x1.backward(); BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), x2_v, 300 * std::numeric_limits::epsilon()); BOOST_REQUIRE_CLOSE(orig_x2.adjoint(), x1_v, 300 * std::numeric_limits::epsilon()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(inplace_division, T, all_float_types) { using namespace rdiff; RandomSample rng{T(-1), T(1)}; T x1_v = rng.next(); T x2_v = rng.next() + T(1e-2); T expected = x1_v / x2_v; rvar x1 = x1_v; rvar x2 = x2_v; rvar orig_x1 = x1; rvar orig_x2 = x2; x1 /= x2; BOOST_REQUIRE_EQUAL(x1.item(), expected); gradient_tape& tape = get_active_tape(); tape.zero_grad(); x1.backward(); BOOST_REQUIRE_CLOSE(orig_x1.adjoint(), 1.0 / x2_v, 300 * std::numeric_limits::epsilon()); BOOST_REQUIRE_CLOSE(orig_x2.adjoint(), -x1_v / (x2_v * x2_v), 300 * std::numeric_limits::epsilon()); tape.clear(); } BOOST_AUTO_TEST_CASE_TEMPLATE(test_rvar_ostream_output, T, all_float_types) { using namespace rdiff; rvar x = static_cast(2.0); rvar y = static_cast(3.0); rvar z = x * y; z.backward(); std::ostringstream oss_x, oss_y, oss_z; oss_x << x; oss_y << y; oss_z << z; BOOST_CHECK_EQUAL(oss_x.str(), "rvar<1>(2,3)"); BOOST_CHECK_EQUAL(oss_y.str(), "rvar<1>(3,2)"); BOOST_CHECK_EQUAL(oss_z.str(), "rvar<1>(6,1)"); } BOOST_AUTO_TEST_CASE_TEMPLATE(boost_math_tools_numeric_traits, T, all_float_types) { using namespace rdiff; using rvar_t = rvar; BOOST_CHECK_EQUAL(boost::math::tools::digits(), boost::math::tools::digits()); BOOST_CHECK_EQUAL(boost::math::tools::max_value(), boost::math::tools::max_value()); BOOST_CHECK_EQUAL(boost::math::tools::min_value(), boost::math::tools::min_value()); BOOST_CHECK_EQUAL(boost::math::tools::log_max_value(), boost::math::tools::log_max_value()); BOOST_CHECK_EQUAL(boost::math::tools::log_min_value(), boost::math::tools::log_min_value()); BOOST_CHECK_EQUAL(boost::math::tools::epsilon(), boost::math::tools::epsilon()); } BOOST_AUTO_TEST_SUITE_END()