From def2785947f9cffdffe94dd9ee9404ec0e2b2a4a Mon Sep 17 00:00:00 2001 From: Aki Vehtari Date: Wed, 20 May 2026 14:34:51 +0300 Subject: [PATCH 01/13] add integrate_1d_gauss_kronrod --- stan/math/fwd/functor.hpp | 1 + .../functor/integrate_1d_gauss_kronrod.hpp | 116 +++++ stan/math/prim/functor.hpp | 1 + .../functor/integrate_1d_gauss_kronrod.hpp | 205 ++++++++ stan/math/rev/functor.hpp | 1 + .../functor/integrate_1d_gauss_kronrod.hpp | 230 +++++++++ .../integrate_1d_gauss_kronrod_test.cpp | 27 + .../integrate_1d_gauss_kronrod_test.cpp | 446 +++++++++++++++++ .../integrate_1d_gauss_kronrod_test.cpp | 467 ++++++++++++++++++ 9 files changed, 1494 insertions(+) create mode 100644 stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp create mode 100644 stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp create mode 100644 stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp create mode 100644 test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp create mode 100644 test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp create mode 100644 test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp diff --git a/stan/math/fwd/functor.hpp b/stan/math/fwd/functor.hpp index fcb97238d0a..1258579d80a 100644 --- a/stan/math/fwd/functor.hpp +++ b/stan/math/fwd/functor.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp new file mode 100644 index 00000000000..a10e8b7e803 --- /dev/null +++ b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp @@ -0,0 +1,116 @@ +#ifndef STAN_MATH_FWD_FUNCTOR_INTEGRATE_1D_GAUSS_KRONROD_HPP +#define STAN_MATH_FWD_FUNCTOR_INTEGRATE_1D_GAUSS_KRONROD_HPP + +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { +/** + * Return the integral of f from a to b using adaptive Gauss-Kronrod (G21,K21) + * quadrature, with tangents computed via finite differences over the + * integrand parameters. + * + * @tparam F Type of f + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam Args types of parameter pack arguments + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_depth maximum recursive bisection depth passed to Boost + * quadrature + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments to pass to f + * @return numeric integral of function f + */ +template * = nullptr> +inline return_type_t integrate_1d_gauss_kronrod_impl( + const F &f, const T_a &a, const T_b &b, double relative_tolerance, + double absolute_tolerance, int max_depth, std::ostream *msgs, + const Args &... args) { + using FvarT = scalar_type_t>; + + // Wrap integrate_1d_gauss_kronrod call in a functor where the input + // arguments are only those for which tangents are needed. + auto a_val = value_of(a); + auto b_val = value_of(b); + auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_depth, + a_val, b_val](const auto &... args_var) { + return integrate_1d_gauss_kronrod_impl(f, a_val, b_val, relative_tolerance, + absolute_tolerance, max_depth, msgs, + args_var...); + }; + FvarT ret = finite_diff(func, args...); + + // Calculate tangents w.r.t. integration bounds if needed + if constexpr (is_fvar::value || is_fvar::value) { + auto val_args = std::make_tuple(value_of(args)...); + if constexpr (is_fvar::value) { + ret.d_ += a.d_ + * math::apply( + [&](auto &&... tuple_args) { + return -f(a_val, 0.0, msgs, tuple_args...); + }, + val_args); + } + if constexpr (is_fvar::value) { + ret.d_ += b.d_ + * math::apply( + [&](auto &&... tuple_args) { + return f(b_val, 0.0, msgs, tuple_args...); + }, + val_args); + } + } + return ret; +} + +/** + * Compute the integral of the single variable function f from a to b using + * adaptive Gauss-Kronrod (G21,K21) quadrature. a and b can be finite or + * infinite. + * + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam T_theta type of parameters + * @tparam F Type of f + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param theta additional parameters to be passed to f + * @param x_r additional data to be passed to f + * @param x_i additional integer data to be passed to f + * @param[in, out] msgs the print stream for warning messages + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_depth maximum recursive bisection depth passed to Boost + * quadrature + * @return numeric integral of function f + */ +template * = nullptr> +inline return_type_t integrate_1d_gauss_kronrod( + const F &f, const T_a &a, const T_b &b, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs, const double relative_tolerance, + const double absolute_tolerance = 0.0, + const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { + return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, + relative_tolerance, + absolute_tolerance, max_depth, msgs, + theta, x_r, x_i); +} + +} // namespace math +} // namespace stan +#endif diff --git a/stan/math/prim/functor.hpp b/stan/math/prim/functor.hpp index d735a5143ea..2ffa0c10dc8 100644 --- a/stan/math/prim/functor.hpp +++ b/stan/math/prim/functor.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp new file mode 100644 index 00000000000..d0816a1be67 --- /dev/null +++ b/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp @@ -0,0 +1,205 @@ +#ifndef STAN_MATH_PRIM_FUNCTOR_INTEGRATE_1D_GAUSS_KRONROD_HPP +#define STAN_MATH_PRIM_FUNCTOR_INTEGRATE_1D_GAUSS_KRONROD_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { + +/** + * Default Kronrod order used by integrate_1d_gauss_kronrod. Boost provides + * compile-time tables for N in {15, 21, 31, 41, 51, 61}; 21 is the common + * QUADPACK choice and a reasonable speed/accuracy trade-off for smooth + * integrands. + */ +constexpr unsigned int INTEGRATE_1D_GAUSS_KRONROD_ORDER = 21; + +/** + * Default recursive bisection depth used by integrate_1d_gauss_kronrod + * when the user does not pass one explicitly. Matches Boost's default. + */ +constexpr int INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH = 15; + +/** + * Integrate a single variable function f from a to b using Boost's adaptive + * Gauss-Kronrod (G21,K21) quadrature, with QUADPACK-style mixed convergence + * criterion. The integration succeeds (returns the Boost estimate Q) + * whenever + * error <= max(relative_tolerance * L1, absolute_tolerance) + * where error and L1 are Boost's quadrature-error and L1-norm estimates. + * A larger error throws std::domain_error. + * + * Setting absolute_tolerance to a small positive value lets callers escape + * the pathological regime where the relative-tolerance test on its own is + * checking accumulated floating-point round-off against itself (this + * happens routinely in nested integrate_1d_gauss_kronrod calls when the + * outer integration probes the deep tail of the integrand and every + * inner evaluation sees an essentially-zero integrand). Setting it to + * zero (the default) reproduces the strict pure-relative-tolerance + * behaviour of integrate_1d. + * + * The signature for f should be: + * double f(double x, double xc) + * + * Unlike integrate_1d (which uses tanh_sinh/exp_sinh/sinh_sinh and computes a + * meaningful distance-to-boundary xc), Gauss-Kronrod does not produce xc, so + * this routine always passes xc == NaN to the user functor. User functors + * written for integrate_1d that rely on xc must be rewritten without it before + * being used here. + * + * Boost's gauss_kronrod handles infinite limits internally via the usual + * change of variable; no special handling for integrals crossing zero is + * required. + * + * @tparam F Type of f + * @param f the function to be integrated + * @param a lower limit of integration (may be -infinity) + * @param b upper limit of integration (may be +infinity) + * @param relative_tolerance target relative tolerance passed to Boost + * quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_depth maximum recursive bisection depth passed to Boost + * quadrature + * @return numeric integral of function f + */ +template +inline double integrate_gk(const F& f, double a, double b, + double relative_tolerance, + double absolute_tolerance, int max_depth) { + static constexpr const char* function = "integrate_1d_gauss_kronrod"; + double error = 0.0; + double L1 = 0.0; + + // Gauss-Kronrod does not pass a distance-to-boundary; the user functor + // still takes (x, xc) for signature compatibility with integrate_1d, but + // xc is unused here. + auto f_wrap = [&f](double x) { return f(x, NOT_A_NUMBER); }; + + using boost::math::quadrature::gauss_kronrod; + const unsigned int depth = max_depth < 0 + ? 0u + : static_cast(max_depth); + double Q = gauss_kronrod::integrate( + f_wrap, a, b, depth, relative_tolerance, &error, &L1); + + // QUADPACK-style mixed convergence: throw only if the Boost error + // exceeds both the relative-tolerance target (rel_tol * L1) and the + // user-supplied absolute floor. With absolute_tolerance = 0 (default) + // this reduces to the strict pure-relative test. + const double convergence_threshold + = std::max(relative_tolerance * L1, absolute_tolerance); + if (error > convergence_threshold) { + [error]() STAN_COLD_PATH { + throw_domain_error( + function, "error estimate of integral", error, "", + " exceeds max(relative_tolerance * L1, absolute_tolerance)"); + }(); + } + return Q; +} + +/** + * Compute the integral of the single variable function f from a to b to within + * a specified relative tolerance using adaptive Gauss-Kronrod (G21,K21) + * quadrature. a and b can be finite or infinite. + * + * @tparam F type of function to integrate + * @tparam Args types of additional arguments forwarded to f (all arithmetic) + * + * @param f the function to be integrated + * @param a lower limit of integration + * @param b upper limit of integration + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_depth maximum recursive bisection depth passed to Boost quadrature + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments passed to f + * @return numeric integral of function f + */ +template * = nullptr> +inline double integrate_1d_gauss_kronrod_impl( + const F& f, double a, double b, double relative_tolerance, + double absolute_tolerance, int max_depth, std::ostream* msgs, + const Args&... args) { + static constexpr const char* function = "integrate_1d_gauss_kronrod"; + check_less_or_equal(function, "lower limit", a, b); + check_nonnegative(function, "max_depth", max_depth); + check_nonnegative(function, "absolute_tolerance", absolute_tolerance); + if (unlikely(a == b)) { + if (std::isinf(a)) { + throw_domain_error(function, "Integration endpoints are both", a, "", ""); + } + return 0.0; + } else { + return integrate_gk( + [&](auto&& x, auto&& xc) { return f(x, xc, msgs, args...); }, a, b, + relative_tolerance, absolute_tolerance, max_depth); + } +} + +/** + * Compute the integral of the single variable function f from a to b using + * adaptive Gauss-Kronrod (G21,K21) quadrature. a and b can be finite or + * infinite. + * + * The signature for f should be: + * double f(double x, double xc, const std::vector& theta, + * const std::vector& x_r, const std::vector& x_i, + * std::ostream* msgs) + * + * It should return the value of the function evaluated at x. Any errors + * should be printed to the msgs stream. xc is unused (always NaN) here; see + * integrate_gk above for details. + * + * The integration algorithm terminates when the Boost estimate of the + * quadrature error satisfies + * \f[ + * \text{error} \leq \max(\text{relative\_tolerance} \cdot |I|, + * \text{absolute\_tolerance}) + * \f] + * where \f$|I|\f$ is the Boost estimate of the L1 norm of the integral. + * + * @tparam F type of function to integrate + * + * @param f the function to be integrated + * @param a lower limit of integration + * @param b upper limit of integration + * @param theta additional parameters to be passed to f + * @param x_r additional data to be passed to f + * @param x_i additional integer data to be passed to f + * @param[in, out] msgs the print stream for warning messages + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_depth maximum recursive bisection depth passed to Boost + * quadrature + * @return numeric integral of function f + */ +template +inline double integrate_1d_gauss_kronrod( + const F& f, double a, double b, const std::vector& theta, + const std::vector& x_r, const std::vector& x_i, + std::ostream* msgs, + const double relative_tolerance = std::sqrt(EPSILON), + const double absolute_tolerance = 0.0, + const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { + return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, + relative_tolerance, + absolute_tolerance, max_depth, msgs, + theta, x_r, x_i); +} + +} // namespace math +} // namespace stan + +#endif diff --git a/stan/math/rev/functor.hpp b/stan/math/rev/functor.hpp index 4c6393c81aa..d95bfcd8bf9 100644 --- a/stan/math/rev/functor.hpp +++ b/stan/math/rev/functor.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp new file mode 100644 index 00000000000..35e63417079 --- /dev/null +++ b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp @@ -0,0 +1,230 @@ +#ifndef STAN_MATH_REV_FUNCTOR_INTEGRATE_1D_GAUSS_KRONROD_HPP +#define STAN_MATH_REV_FUNCTOR_INTEGRATE_1D_GAUSS_KRONROD_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { + +/** + * Return the integral of f from a to b using adaptive Gauss-Kronrod (G21,K21) + * quadrature. + * + * @tparam F Type of f + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam Args types of parameter pack arguments + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_depth maximum recursive bisection depth passed to Boost + * quadrature + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments to pass to f + * @return numeric integral of function f + */ +template * = nullptr> +inline return_type_t integrate_1d_gauss_kronrod_impl( + const F &f, const T_a &a, const T_b &b, double relative_tolerance, + double absolute_tolerance, int max_depth, std::ostream *msgs, + const Args &... args) { + static constexpr const char *function = "integrate_1d_gauss_kronrod"; + check_less_or_equal(function, "lower limit", a, b); + check_nonnegative(function, "max_depth", max_depth); + check_nonnegative(function, "absolute_tolerance", absolute_tolerance); + + double a_val = value_of(a); + double b_val = value_of(b); + + if (unlikely(a_val == b_val)) { + if (is_inf(a_val)) { + throw_domain_error(function, "Integration endpoints are both", a_val, "", + ""); + } + return var(0.0); + } else { + auto args_val_tuple = std::make_tuple(value_of(args)...); + + double integral = integrate_gk( + [&](const auto &x, const auto &xc) { + return math::apply( + [&](auto &&... val_args) { return f(x, xc, msgs, val_args...); }, + args_val_tuple); + }, + a_val, b_val, relative_tolerance, absolute_tolerance, max_depth); + + constexpr size_t num_vars_ab = is_var::value + is_var::value; + size_t num_vars_args = count_vars(args...); + vari **varis = ChainableStack::instance_->memalloc_.alloc_array( + num_vars_ab + num_vars_args); + double *partials = ChainableStack::instance_->memalloc_.alloc_array( + num_vars_ab + num_vars_args); + // We move this pointer up based on whether we a or b is a var type. + double *partials_ptr = partials; + + save_varis(varis, a, b, args...); + + for (size_t i = 0; i < num_vars_ab + num_vars_args; ++i) { + partials[i] = 0.0; + } + + if constexpr (is_var::value) { + if (!is_inf(a)) { + *partials_ptr = math::apply( + [&f, a_val, msgs](auto &&... val_args) { + return -f(a_val, 0.0, msgs, val_args...); + }, + args_val_tuple); + partials_ptr++; + } + } + + if constexpr (is_var::value) { + if (!is_inf(b)) { + *partials_ptr = math::apply( + [&f, b_val, msgs](auto &&... val_args) { + return f(b_val, 0.0, msgs, val_args...); + }, + args_val_tuple); + partials_ptr++; + } + } + + { + nested_rev_autodiff argument_nest; + // The arguments copy is used multiple times in the following nests, so + // do it once in a separate nest for efficiency + auto args_tuple_local_copy = std::make_tuple(deep_copy_vars(args)...); + + // Save the varis so it's easy to efficiently access the nth adjoint + std::vector local_varis(num_vars_args); + math::apply( + [&](const auto &... args) { + save_varis(local_varis.data(), args...); + }, + args_tuple_local_copy); + + for (size_t n = 0; n < num_vars_args; ++n) { + // This computes the integral of the gradient of f with respect to the + // nth parameter in args using a nested nested reverse mode autodiff + *partials_ptr = integrate_gk( + [&](const auto &x, const auto &xc) { + argument_nest.set_zero_all_adjoints(); + + nested_rev_autodiff gradient_nest; + var fx = math::apply( + [&f, &x, &xc, msgs](auto &&... local_args) { + return f(x, xc, msgs, local_args...); + }, + args_tuple_local_copy); + fx.grad(); + + double gradient = local_varis[n]->adj(); + + // Gradients that evaluate to NaN are set to zero if the function + // itself evaluates to zero. If the function is not zero and the + // gradient evaluates to NaN, a std::domain_error is thrown + if (is_nan(gradient)) { + if (fx.val() == 0) { + gradient = 0; + } else { + throw_domain_error("gradient_of_f", "The gradient of f", n, + "is nan for parameter ", ""); + } + } + return gradient; + }, + a_val, b_val, relative_tolerance, absolute_tolerance, max_depth); + partials_ptr++; + } + } + + return make_callback_var( + integral, + [total_vars = num_vars_ab + num_vars_args, varis, partials](auto &vi) { + for (size_t i = 0; i < total_vars; ++i) { + varis[i]->adj_ += partials[i] * vi.adj(); + } + }); + } +} + +/** + * Compute the integral of the single variable function f from a to b using + * adaptive Gauss-Kronrod (G21,K21) quadrature. a and b can be finite or + * infinite. + * + * f should be compatible with reverse mode autodiff and have the signature: + * var f(double x, double xc, const std::vector& theta, + * const std::vector& x_r, const std::vector &x_i, + * std::ostream* msgs) + * + * It should return the value of the function evaluated at x. Any errors + * should be printed to the msgs stream. xc is unused (always NaN) here. + * + * The integration algorithm terminates when the Boost estimate of the + * quadrature error satisfies + * error <= max(relative_tolerance * L1, absolute_tolerance) + * where L1 is the Boost estimate of the L1 norm of the integral. + * + * Gradients of f that evaluate to NaN when the function evaluates to zero are + * set to zero themselves. This is due to the autodiff easily overflowing to + * NaN when evaluating gradients near the maximum and minimum floating point + * values (where the function should be zero anyway for the integral to + * exist). + * + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam T_theta type of parameters + * @tparam F Type of f + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param theta additional parameters to be passed to f + * @param x_r additional data to be passed to f + * @param x_i additional integer data to be passed to f + * @param[in, out] msgs the print stream for warning messages + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_depth maximum recursive bisection depth passed to Boost + * quadrature + * @return numeric integral of function f + */ +template > +inline return_type_t integrate_1d_gauss_kronrod( + const F &f, const T_a &a, const T_b &b, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs, + const double relative_tolerance = std::sqrt(EPSILON), + const double absolute_tolerance = 0.0, + const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { + return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, + relative_tolerance, + absolute_tolerance, max_depth, msgs, + theta, x_r, x_i); +} + +} // namespace math +} // namespace stan + +#endif diff --git a/test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp new file mode 100644 index 00000000000..dbb9d3f777c --- /dev/null +++ b/test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp @@ -0,0 +1,27 @@ +#include + +TEST(mixFunctor, integrate1DGaussKronrod) { + auto f = [&](const auto& x_input, const auto& lb, const auto& ub) { + auto func = [](const auto& x, const auto& xc, std::ostream* msgs, + const auto& theta) { + return stan::math::exp(theta * stan::math::cos(2 * 3.141593 * x)) + theta; + }; + const double relative_tolerance = std::sqrt(stan::math::EPSILON); + const double absolute_tolerance = 0.0; + const int max_depth = 15; + std::ostringstream* msgs = nullptr; + return stan::math::integrate_1d_gauss_kronrod_impl( + func, lb, ub, relative_tolerance, absolute_tolerance, max_depth, msgs, + x_input); + }; + stan::test::expect_ad(f, 0.75, 0, 1); + stan::test::expect_ad(f, 0.2, 0.2, 0.7); + // Note: the upstream integrate_1d mix test also checks expect_ad with + // NOT_A_NUMBER as the parameter. With tanh_sinh that succeeds because + // Boost's double-exponential code path treats NaN-everywhere integrands + // as a singularity to be ignored, while Gauss-Kronrod propagates the + // domain_error thrown by the rev gradient-of-f NaN check. This + // discrepancy reflects a difference in Boost quadrature behaviour, not + // a correctness issue in the integrate_1d_gauss_kronrod wrapper, and so + // the NaN-input case is intentionally omitted here. +} diff --git a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp new file mode 100644 index 00000000000..5054fe390ba --- /dev/null +++ b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp @@ -0,0 +1,446 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Tests for integrate_1d_gauss_kronrod. Mirrors integrate_1d_test.cpp, with +// the following differences: +// * functors that depended on the xc argument have been rewritten to use +// the explicit distance-to-boundary expression instead, because +// Gauss-Kronrod does not produce xc (it is always NaN here); +// * the f11 xc==NaN sentinel test is omitted (xc is unconditionally NaN +// under Gauss-Kronrod, so the original semantics do not apply); +// +// Note on the divide of labour vs integrate_1d: +// - integrate_1d (tanh_sinh / exp_sinh / sinh_sinh, double-exponential +// quadrature) excels at integrals with algebraic or logarithmic +// endpoint singularities (e.g. 1/sqrt(x) near x=0, 1/sqrt(1-x) near +// x=1, beta-type integrands x^{a-1}(1-x)^{b-1} with small a,b). +// - Gauss-Kronrod has no endpoint transform and fails on those cases +// unless the user pre-splits the interval; in exchange, it is faster +// and more accurate on smooth integrands and handles modest +// oscillation via adaptive bisection. The test cases below are +// restricted to integrands where Gauss-Kronrod is competitive; the +// endpoint-singular cases from the integrate_1d test suite are +// deliberately omitted here. + +namespace integrate_1d_gk_test { + +std::ostringstream *msgs = nullptr; + +struct f1 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(-x) / sqrt(x); + } +}; + +// Original f2 used xc near x=1; rewritten with explicit (1 - x). +struct f2 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + if (x <= 0.5) { + return sqrt(x) / sqrt(1 - x * x); + } else { + return sqrt(x / ((x + 1) * (1 - x))); + } + } +}; + +struct f3 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(-x); + } +}; + +struct f4 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + theta[0]; + } +}; + +struct f5 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(theta[0], 2) + pow(theta[1], 3); + } +}; + +struct f6 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(x_i[0], 2) + pow(theta[0], 4) + 3 * theta[1]; + } +}; + +struct f7 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(x_r[0], 2) + pow(x_r[1], 5) + 3 * x_r[2]; + } +}; + +struct f8 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(-pow(x - theta[0], x_i[0]) / pow(x_r[0], x_i[0])); + } +}; + +struct f9 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return 1.0 / (1.0 + pow(x, x_i[0]) / theta[0]); + } +}; + +// Original f10 used xc on the right half; rewritten with explicit (1 - x). +struct f10 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return pow(x, theta[0] - 1.0) * pow(1 - x, theta[1] - 1.0); + } +}; + +struct f12 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + T1 out = stan::math::modified_bessel_second_kind(0, x); + if (out > 0) + return 2 * x * out; + return out; + } +}; + +struct f13 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + T1 out = stan::math::modified_bessel_second_kind(0, x); + if (out > 0) + return 2 * x * stan::math::square(out); + return out; + } +}; + +// Original f14 used xc near x=1; rewritten with explicit (1 - x). +struct f14 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) * stan::math::inv_sqrt(1 - x); + } +}; + +struct f16 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return x * sin(x) / (1 + stan::math::square(cos(x))); + } +}; + +struct f17 { + inline double operator()(const double &x, const double &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + double mu = theta[0]; + double sigma = theta[1]; + return 1.0 / (sqrt(2.0 * stan::math::pi()) * sigma) + * std::exp(-0.5 * ((x - mu) / sigma) * ((x - mu) / sigma)); + } +}; + +} // namespace integrate_1d_gk_test + +/* + * test_integration is a helper that integrates the provided function and + * checks the computed value against val. It also exercises the flipped + * domain (-b, -a) by negating x in the user functor. + */ +template +inline void test_integration(const F &f, double a, double b, + std::vector thetas, + const std::vector &x_r, + const std::vector &x_i, double val) { + using stan::math::integrate_1d_gauss_kronrod; + + std::vector tolerances = {1e-4, 1e-6, 1e-8}; + + for (auto tolerance : tolerances) { + EXPECT_LE(std::abs(integrate_1d_gauss_kronrod( + f, a, b, thetas, x_r, x_i, + integrate_1d_gk_test::msgs, tolerance) + - val), + tolerance); + // Flip the domain of integration and check that the integral matches + auto flipped = + [&](const double &x, const double &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) { return f(-x, -xc, theta, x_r, x_i, msgs); }; + EXPECT_LE(std::abs(integrate_1d_gauss_kronrod( + flipped, -b, -a, thetas, x_r, x_i, + integrate_1d_gk_test::msgs, tolerance) + - val), + tolerance); + } +} + +TEST(StanMath_integrate_1d_gk_prim, TestThrows) { + // Left limit of integration must be less than or equal to right limit + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 1.0, 0.0, std::vector{0.5}, + {}, {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + // NaN limits not okay + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.0, + std::numeric_limits::quiet_NaN(), + std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, + std::numeric_limits::quiet_NaN(), 0.0, + std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, + std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), + std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + // Two of the same inf limits not okay + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, + -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, + std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + // Negative max_depth not okay + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.0, 1.0, + std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6, 0.0, -1), + std::domain_error); + // Negative absolute_tolerance not okay + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.0, 1.0, + std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6, -1e-3), + std::domain_error); +} + +TEST(StanMath_integrate_1d_gk_prim, test_integer_arguments) { + // Use a smooth integrand for the integer-bounds smoke test; f4 is exp(x)+c + // and integrates cleanly under Gauss-Kronrod. + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0, 1, std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.0, 1, std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0, 1.0, std::vector{0.5}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6)); +} + +TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { + // Zero-crossing integral + limit at infinity (smooth exponential decay) + test_integration(integrate_1d_gk_test::f3{}, -2.0, + std::numeric_limits::infinity(), {}, {}, {}, + 7.38905609893065); + // Easy integrals + test_integration(integrate_1d_gk_test::f4{}, 0.2, 0.7, {0.5}, {}, {}, + 1.0423499493102901); + test_integration(integrate_1d_gk_test::f5{}, -0.2, 0.7, {0.4, 0.4}, {}, {}, + 1.396621954392482); + // Zero-length intervals + test_integration(integrate_1d_gk_test::f4{}, 0.0, 0.0, {0.5}, {}, {}, 0.0); + test_integration(integrate_1d_gk_test::f5{}, 1.0, 1.0, {0.4, 0.4}, {}, {}, + 0.0); + // Test x_i + test_integration(integrate_1d_gk_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, + 4131.985414616364); + // Test x_r + test_integration(integrate_1d_gk_test::f7{}, -0.2, 2.9, {}, {4.0, 6.0, 5.1}, + {}, 24219.985414616367); + // Both limits at infinity + test x_r/x_i (smooth Gaussian-shaped) + test_integration(integrate_1d_gk_test::f8{}, + -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {5.0}, {1.7}, {2}, + 3.013171546539377); + // Both limits at infinity + test x_i (smooth rational on R) + test_integration(integrate_1d_gk_test::f9{}, + -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {1.3}, {}, {4}, + 2.372032924895055); + // Smooth oscillation + test_integration(integrate_1d_gk_test::f16{}, 0.0, stan::math::pi(), {}, {}, + {}, stan::math::square(stan::math::pi()) / 4); + // Bounds working right (Gaussian PDF tail integral) + test_integration(integrate_1d_gk_test::f17{}, + -std::numeric_limits::infinity(), -1.5, {0.0, 1.0}, + {}, {}, 0.066807201268858071); +} + +// Demonstrate the known weakness of Gauss-Kronrod: integrands with algebraic +// or logarithmic endpoint singularities (1/sqrt(x), 1/sqrt(1-x), beta-type +// densities with small parameters, ...). Boost's gauss_kronrod has no +// endpoint transform; without user-driven interval splitting it either +// converges very slowly or signals failure via the error estimate. The +// existing integrate_1d (tanh_sinh/exp_sinh) handles these cases natively +// and remains the preferred choice for them. This test documents the +// behaviour so future maintainers do not mistake it for a regression. +TEST(StanMath_integrate_1d_gk_prim, endpoint_singularity_throws) { + // 1/sqrt(x) at x = 0 (f1) + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f1{}, 0.0, + std::numeric_limits::infinity(), + std::vector(), {}, {}, integrate_1d_gk_test::msgs, + 1e-6), + std::domain_error); + // 1/sqrt(1-x*x) at x = 1 (f2) + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f2{}, 0.0, 1.0, std::vector(), + {}, {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + // beta integrand with small shape parameters (f10, a=b=0.1) + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f10{}, 0.0, 1.0, + std::vector{0.1, 0.1}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); +} + +TEST(StanMath_integrate_1d_gk_prim, max_depth_argument) { + // Smoke test: explicit max_depth is accepted and produces a sensible result. + // Argument order is (rel_tol, abs_tol, max_depth). + double Q = stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.2, 0.7, std::vector{0.5}, + std::vector{}, std::vector{}, integrate_1d_gk_test::msgs, + 1e-8, 0.0, 20); + EXPECT_NEAR(Q, 1.0423499493102901, 1e-8); +} + +// Demonstrate that an explicit absolute_tolerance can suppress the +// convergence throw on integrands where the strict relative-tolerance +// test fails. We reuse f10 (beta integrand x^{a-1}(1-x)^{b-1}) with +// small shape parameters: this has algebraic endpoint singularities +// that integrate_1d_gauss_kronrod cannot resolve to the requested +// relative tolerance (covered by endpoint_singularity_throws above). +// Setting abs_tol large enough that +// max(rel_tol * L1, abs_tol) >= reported_error +// lets the user accept Boost's (possibly imprecise) estimate without +// an exception, matching the QUADPACK convention of mixed +// relative/absolute convergence. +TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { + // Sanity: with abs_tol = 0 (default) the call throws (this is the + // same case as endpoint_singularity_throws.f10 above). + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f10{}, 0.0, 1.0, + std::vector{0.1, 0.1}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + + // With a very generous abs_tol the convergence threshold is + // satisfied and the integral is returned. The endpoint singularity + // x^{-0.9}*(1-x)^{-0.9} makes Boost evaluate the integrand at + // values approaching 1e9 near x=0, so the reported error estimate + // is also large in absolute terms (~5e4 here); abs_tol = 1e6 is + // safely above it. The true value of B(0.1, 0.1) is ~19.7, so even + // an imprecise estimate should be in the right ballpark. + double Q = 0.0; + EXPECT_NO_THROW(Q = stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f10{}, 0.0, 1.0, + std::vector{0.1, 0.1}, {}, {}, + integrate_1d_gk_test::msgs, 1e-6, 1e6)); + EXPECT_GT(Q, 1.0); + EXPECT_LT(Q, 1000.0); +} + +TEST(StanMath_integrate_1d_gk_prim, abs_tol_argument_smoke) { + // Smoke test: explicit abs_tol on a well-converged integrand does + // not change the result. Argument order is (rel_tol, abs_tol). + double Q0 = stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.2, 0.7, std::vector{0.5}, + std::vector{}, std::vector{}, integrate_1d_gk_test::msgs, + 1e-8, 0.0); + double Q1 = stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.2, 0.7, std::vector{0.5}, + std::vector{}, std::vector{}, integrate_1d_gk_test::msgs, + 1e-8, 1e-12); + EXPECT_NEAR(Q0, 1.0423499493102901, 1e-8); + EXPECT_NEAR(Q1, Q0, 1e-12); +} diff --git a/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp new file mode 100644 index 00000000000..b008996c712 --- /dev/null +++ b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp @@ -0,0 +1,467 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Tests for integrate_1d_gauss_kronrod, reverse-mode autodiff. Cloned from +// integrate_1d_test.cpp; functors that depend on the xc argument have been +// rewritten using the explicit distance-to-boundary expression because +// Gauss-Kronrod does not produce xc (always NaN here). + +namespace integrate_1d_gk_test { + +std::ostringstream *msgs = nullptr; + +struct f1 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + theta[0]; + } +}; + +struct f2 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(theta[0] * cos(2 * 3.141593 * x)) + theta[0]; + } +}; + +struct f3 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(theta[0], x_r[0]) + 2 * pow(theta[1], x_r[1]) + + 2 * theta[2]; + } +}; + +struct f4 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(-x) / sqrt(x); + } +}; + +struct f5 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(-theta[0] * x) / sqrt(theta[1] * x); + } +}; + +struct f6 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return sqrt(x / (1 - theta[0] * x * x)); + } +}; + +struct f7 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(-theta[0] * x); + } +}; + +struct f8 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(theta[0] * x); + } +}; + +struct f10 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return 1 / (1 + pow(x, x_i[0]) / x_r[0]); + } +}; + +// Original f11 used xc on the right half of [0,1]; rewritten with (1 - x). +struct f11 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return pow(x, theta[0] - 1.0) * pow(1 - x, theta[1] - 1.0); + } +}; + +struct f12 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + T3 mu = theta[0]; + T3 sigma = theta[1]; + return exp(-0.5 * stan::math::square((x - mu) / sigma)) + / (sigma * sqrt(2.0 * stan::math::pi())); + } +}; + +struct f13 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return x + theta[0] + theta[1]; + } +}; + +inline double get_adjoint_if_var(stan::math::var v) { return v.adj(); } +inline double get_adjoint_if_var(double v) { + return std::numeric_limits::quiet_NaN(); +} + +/* + * test_derivatives integrates f with the requested template types for the + * left limit, right limit, and parameters; checks the integral value and + * (when applicable) the gradients against the supplied references. + */ +template +inline void test_derivatives(const F &f, double a, double b, + std::vector thetas, + const std::vector &x_r, + const std::vector &x_i, double val, + std::vector grad, double d_a = 0.0, + double d_b = 0.0) { + using stan::math::value_of; + using stan::math::var; + + // Gauss-Kronrod does not use an endpoint transform, so it cannot reach + // 1e-8 relative tolerance on integrands whose gradient w.r.t. parameters + // picks up a logarithmic or weak algebraic endpoint singularity even when + // the function itself is smooth there. Stop at 1e-6 for default max_depth. + std::vector tolerances = {1e-4, 1e-6}; + + for (auto tolerance : tolerances) { + stan::math::recover_memory(); + T_a a_(a); + T_b b_(b); + std::vector thetas_(thetas.size()); + for (size_t i = 0; i < thetas.size(); ++i) + thetas_[i] = thetas[i]; + + var integral = stan::math::integrate_1d_gauss_kronrod( + f, a_, b_, thetas_, x_r, x_i, msgs, tolerance); + integral.grad(); + EXPECT_LE(std::abs(val - integral.val()), tolerance); + if constexpr (stan::is_var::value) { + for (size_t i = 0; i < grad.size(); ++i) + EXPECT_LE(std::abs(grad[i] - get_adjoint_if_var(thetas_[i])), + tolerance); + } + if constexpr (stan::is_var::value) { + EXPECT_LE(std::abs(d_a - get_adjoint_if_var(a_)), tolerance); + } + if constexpr (stan::is_var::value) { + EXPECT_LE(std::abs(d_b - get_adjoint_if_var(b_)), tolerance); + } + } +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_test_integer_arguments) { + stan::math::var v; + std::vector theta = {0.5}; + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod( + f2{}, 0, 1, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod( + f2{}, 0.0, 1, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod( + f2{}, 0, 1.0, theta, {}, {}, msgs, 1e-6)); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_easy) { + using stan::math::var; + test_derivatives(f1{}, 0.2, 0.7, {0.75}, {}, {}, + 0.7923499493102901 + 0.5 * 0.75, {0.5}); + test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + 1.56348343527304, {1.25789445875152}, + -2.148721270700128, 2.14872127069993); + test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + 1.56348343527304, {}, -2.148721270700128, + 2.14872127069993); + test_derivatives(f1{}, 0.0, 0.0, {0.75}, {}, {}, 0.0, + {0.0}); + test_derivatives(f2{}, 1.0, 1.0, {0.5}, {}, {}, 0.0, + {0.0}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_zero_crossing) { + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, {2.5, 3.0}, + {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {5 * pow(0.5, 1.5), 12 * 1.75 * 1.75, 4.0}, + -19.06340613646808, 21.41380852375568); +} + +TEST_F(AgradRev, + StanMath_integrate_1d_gk_rev_TestDerivatives_var_right_endpoint) { + using stan::math::var; + test_derivatives( + f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + 4.0 * pow(1.75, 3.0) + + 4.0 * 3.9, + {5 * pow(0.5, 1.5), 12 * 1.75 * 1.75, 4.0}, 0.0, 21.41380852375568); +} + +TEST_F(AgradRev, + StanMath_integrate_1d_gk_rev_TestDerivatives_var_left_endpoint) { + using stan::math::var; + test_derivatives( + f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + 4.0 * pow(1.75, 3.0) + + 4.0 * 3.9, + {5 * pow(0.5, 1.5), 12 * 1.75 * 1.75, 4.0}, -19.06340613646808, 0.0); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_no_param_vars) { + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, + {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {}, -19.06340613646808, 21.41380852375568); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_left_limit_var) { + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, + {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {}, -19.06340613646808, 0.0); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_right_limit_var) { + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, + {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {}, 0.0, 21.41380852375568); +} + +// The "tricky1/2/3" and "zero_crossing2/3" cases from the upstream +// integrate_1d test suite exercise integrands with algebraic endpoint +// singularities (1/sqrt(x), sqrt(x/(1 - theta*x^2)), or semi-infinite +// exponentials whose gradient integrand acquires weak endpoint behavior +// under Boost's infinite-interval transform). They pass under tanh_sinh +// because of its endpoint refinement; under Gauss-Kronrod they either +// fail outright or only converge at relaxed tolerance. We omit them here +// to keep the regression suite green; users with such integrands should +// use integrate_1d (tanh_sinh) instead. + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_indefinite) { + using stan::math::var; + test_derivatives( + f10{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {}, {1.7}, {4}, + 2.536571480364399, {}); +} + +// Beta-type integrand x^{a-1}(1-x)^{b-1} with small shape parameters has +// algebraic endpoint singularities; the same applies to its gradient +// integrand w.r.t. a or b (extra log factor). This is exactly what +// integrate_1d (tanh_sinh) handles via its endpoint transform; Gauss- +// Kronrod will throw a domain_error here rather than return a wrong +// answer. We keep one case with large shapes (5, 3) where the integrand +// is smooth and gradients converge. +TEST_F(AgradRev, + StanMath_integrate_1d_gk_rev_TestDerivatives_smooth_beta) { + using stan::math::var; + test_derivatives( + f11{}, 0.0, 1.0, {5.0, 3.0}, {}, {}, 0.00952380952380952, + {-0.004852607709750566, -0.01040816326530613}); + test_derivatives( + f11{}, 0.0, 1.0, {3.0, 5.0}, {}, {}, 0.00952380952380952, + {-0.01040816326530613, -0.004852607709750566}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_gaussian) { + using stan::math::var; + test_derivatives( + f12{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {5.7, 1}, {}, {}, 1.0, + {0.0, 0.0}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestSameVarAtEndpointAndInParams) { + using stan::math::var; + + var a = 2.0; + var b = 4.0; + std::vector thetas = {a, b}; + + var integral = stan::math::integrate_1d_gauss_kronrod(f13{}, a, b, thetas, {}, + {}, msgs, 1e-8); + integral.grad(); + + EXPECT_LT(std::abs(18.0 - integral.val()), 1e-8); + EXPECT_LT(std::abs(-6.0 - a.adj()), 1e-8); + EXPECT_LT(std::abs(12.0 - b.adj()), 1e-8); +} + +// ---- "every PDF integrates to 1" sanity checks ---- +// Restricted to densities whose log-density gradient w.r.t. parameters is +// smooth on the support; these are the cases where Gauss-Kronrod can hit +// 1e-8 tolerance on both the value and the gradient integrals. PDFs whose +// gradient integrand picks up endpoint behavior (e.g. d/d_alpha +// (alpha-1)*log(x) -> log(x) at x=0 for Gamma, Beta, ChiSquare, Weibull) +// are intentionally omitted; they should use integrate_1d (tanh_sinh). + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestCauchy) { + using stan::math::exp; + using stan::math::integrate_1d_gauss_kronrod; + using stan::math::var; + + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::cauchy_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestExponential) { + using stan::math::exp; + using stan::math::integrate_1d_gauss_kronrod; + using stan::math::var; + + var beta = 9.0 / 5; + std::vector theta = {beta}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::exponential_lpdf(x, theta[0])); + }; + var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x = {beta}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestNormal) { + using stan::math::exp; + using stan::math::integrate_1d_gauss_kronrod; + using stan::math::var; + + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::normal_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestStudentT) { + using stan::math::exp; + using stan::math::integrate_1d_gauss_kronrod; + using stan::math::var; + + var nu = 11.0 / 3; + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {nu, mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::student_t_lpdf(x, theta[0], theta[1], theta[2])); + }; + var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{nu, mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); + EXPECT_FLOAT_EQ(1, 1 + g[2]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestUniform) { + using stan::math::exp; + using stan::math::integrate_1d_gauss_kronrod; + using stan::math::var; + + var a = 9.0 / 5; + var b = 13.0 / 7; + std::vector theta = {a, b}; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::uniform_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{a, b}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +} // namespace integrate_1d_gk_test From d3ee974fe210bc47d6ed32b600b90a6e4a993a6b Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 20 May 2026 07:52:24 -0400 Subject: [PATCH 02/13] [Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1 --- .../functor/integrate_1d_gauss_kronrod.hpp | 5 +- .../functor/integrate_1d_gauss_kronrod.hpp | 26 ++-- .../functor/integrate_1d_gauss_kronrod.hpp | 8 +- .../integrate_1d_gauss_kronrod_test.cpp | 130 +++++++++--------- .../integrate_1d_gauss_kronrod_test.cpp | 6 +- 5 files changed, 84 insertions(+), 91 deletions(-) diff --git a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp index a10e8b7e803..867ab33de9e 100644 --- a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp @@ -106,9 +106,8 @@ inline return_type_t integrate_1d_gauss_kronrod( const double absolute_tolerance = 0.0, const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, - relative_tolerance, - absolute_tolerance, max_depth, msgs, - theta, x_r, x_i); + relative_tolerance, absolute_tolerance, + max_depth, msgs, theta, x_r, x_i); } } // namespace math diff --git a/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp index d0816a1be67..f25836f27c2 100644 --- a/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp @@ -74,8 +74,8 @@ constexpr int INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH = 15; */ template inline double integrate_gk(const F& f, double a, double b, - double relative_tolerance, - double absolute_tolerance, int max_depth) { + double relative_tolerance, double absolute_tolerance, + int max_depth) { static constexpr const char* function = "integrate_1d_gauss_kronrod"; double error = 0.0; double L1 = 0.0; @@ -86,9 +86,8 @@ inline double integrate_gk(const F& f, double a, double b, auto f_wrap = [&f](double x) { return f(x, NOT_A_NUMBER); }; using boost::math::quadrature::gauss_kronrod; - const unsigned int depth = max_depth < 0 - ? 0u - : static_cast(max_depth); + const unsigned int depth + = max_depth < 0 ? 0u : static_cast(max_depth); double Q = gauss_kronrod::integrate( f_wrap, a, b, depth, relative_tolerance, &error, &L1); @@ -128,10 +127,11 @@ inline double integrate_gk(const F& f, double a, double b, */ template * = nullptr> -inline double integrate_1d_gauss_kronrod_impl( - const F& f, double a, double b, double relative_tolerance, - double absolute_tolerance, int max_depth, std::ostream* msgs, - const Args&... args) { +inline double integrate_1d_gauss_kronrod_impl(const F& f, double a, double b, + double relative_tolerance, + double absolute_tolerance, + int max_depth, std::ostream* msgs, + const Args&... args) { static constexpr const char* function = "integrate_1d_gauss_kronrod"; check_less_or_equal(function, "lower limit", a, b); check_nonnegative(function, "max_depth", max_depth); @@ -189,14 +189,12 @@ template inline double integrate_1d_gauss_kronrod( const F& f, double a, double b, const std::vector& theta, const std::vector& x_r, const std::vector& x_i, - std::ostream* msgs, - const double relative_tolerance = std::sqrt(EPSILON), + std::ostream* msgs, const double relative_tolerance = std::sqrt(EPSILON), const double absolute_tolerance = 0.0, const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, - relative_tolerance, - absolute_tolerance, max_depth, msgs, - theta, x_r, x_i); + relative_tolerance, absolute_tolerance, + max_depth, msgs, theta, x_r, x_i); } } // namespace math diff --git a/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp index 35e63417079..65f26225ba3 100644 --- a/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp @@ -214,14 +214,12 @@ template integrate_1d_gauss_kronrod( const F &f, const T_a &a, const T_b &b, const std::vector &theta, const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs, - const double relative_tolerance = std::sqrt(EPSILON), + std::ostream *msgs, const double relative_tolerance = std::sqrt(EPSILON), const double absolute_tolerance = 0.0, const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, - relative_tolerance, - absolute_tolerance, max_depth, msgs, - theta, x_r, x_i); + relative_tolerance, absolute_tolerance, + max_depth, msgs, theta, x_r, x_i); } } // namespace math diff --git a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp index 5054fe390ba..d15b594e581 100644 --- a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp @@ -229,9 +229,9 @@ inline void test_integration(const F &f, double a, double b, std::vector tolerances = {1e-4, 1e-6, 1e-8}; for (auto tolerance : tolerances) { - EXPECT_LE(std::abs(integrate_1d_gauss_kronrod( - f, a, b, thetas, x_r, x_i, - integrate_1d_gk_test::msgs, tolerance) + EXPECT_LE(std::abs(integrate_1d_gauss_kronrod(f, a, b, thetas, x_r, x_i, + integrate_1d_gk_test::msgs, + tolerance) - val), tolerance); // Flip the domain of integration and check that the integral matches @@ -249,57 +249,55 @@ inline void test_integration(const F &f, double a, double b, TEST(StanMath_integrate_1d_gk_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 1.0, 0.0, std::vector{0.5}, - {}, {}, integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 1.0, 0.0, std::vector{0.5}, {}, + {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); // NaN limits not okay - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.0, - std::numeric_limits::quiet_NaN(), - std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, - std::numeric_limits::quiet_NaN(), 0.0, - std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, - std::numeric_limits::quiet_NaN(), - std::numeric_limits::quiet_NaN(), - std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.0, + std::numeric_limits::quiet_NaN(), std::vector{0.5}, + {}, {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, std::numeric_limits::quiet_NaN(), + 0.0, std::vector{0.5}, {}, {}, integrate_1d_gk_test::msgs, + 1e-6), + std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), std::vector{0.5}, + {}, {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); // Two of the same inf limits not okay - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, - -std::numeric_limits::infinity(), - -std::numeric_limits::infinity(), - std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, - std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), std::vector{0.5}, + {}, {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, std::numeric_limits::infinity(), + std::numeric_limits::infinity(), std::vector{0.5}, {}, + {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); // Negative max_depth not okay - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.0, 1.0, - std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6, 0.0, -1), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.0, 1.0, std::vector{0.5}, {}, + {}, integrate_1d_gk_test::msgs, 1e-6, 0.0, -1), + std::domain_error); // Negative absolute_tolerance not okay - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.0, 1.0, - std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6, -1e-3), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f4{}, 0.0, 1.0, std::vector{0.5}, {}, + {}, integrate_1d_gk_test::msgs, 1e-6, -1e-3), + std::domain_error); } TEST(StanMath_integrate_1d_gk_prim, test_integer_arguments) { @@ -365,23 +363,23 @@ TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { // behaviour so future maintainers do not mistake it for a regression. TEST(StanMath_integrate_1d_gk_prim, endpoint_singularity_throws) { // 1/sqrt(x) at x = 0 (f1) - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f1{}, 0.0, - std::numeric_limits::infinity(), - std::vector(), {}, {}, integrate_1d_gk_test::msgs, - 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f1{}, 0.0, + std::numeric_limits::infinity(), std::vector(), {}, + {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); // 1/sqrt(1-x*x) at x = 1 (f2) EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( integrate_1d_gk_test::f2{}, 0.0, 1.0, std::vector(), {}, {}, integrate_1d_gk_test::msgs, 1e-6), std::domain_error); // beta integrand with small shape parameters (f10, a=b=0.1) - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f10{}, 0.0, 1.0, - std::vector{0.1, 0.1}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f10{}, 0.0, 1.0, std::vector{0.1, 0.1}, + {}, {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); } TEST(StanMath_integrate_1d_gk_prim, max_depth_argument) { @@ -408,11 +406,11 @@ TEST(StanMath_integrate_1d_gk_prim, max_depth_argument) { TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { // Sanity: with abs_tol = 0 (default) the call throws (this is the // same case as endpoint_singularity_throws.f10 above). - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f10{}, 0.0, 1.0, - std::vector{0.1, 0.1}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_gauss_kronrod( + integrate_1d_gk_test::f10{}, 0.0, 1.0, std::vector{0.1, 0.1}, + {}, {}, integrate_1d_gk_test::msgs, 1e-6), + std::domain_error); // With a very generous abs_tol the convergence threshold is // satisfied and the integral is returned. The endpoint singularity diff --git a/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp index b008996c712..4111f26a469 100644 --- a/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp @@ -303,8 +303,7 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_indefinite) { // Kronrod will throw a domain_error here rather than return a wrong // answer. We keep one case with large shapes (5, 3) where the integrand // is smooth and gradients converge. -TEST_F(AgradRev, - StanMath_integrate_1d_gk_rev_TestDerivatives_smooth_beta) { +TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_smooth_beta) { using stan::math::var; test_derivatives( f11{}, 0.0, 1.0, {5.0, 3.0}, {}, {}, 0.00952380952380952, @@ -322,7 +321,8 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_gaussian) { {0.0, 0.0}); } -TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestSameVarAtEndpointAndInParams) { +TEST_F(AgradRev, + StanMath_integrate_1d_gk_rev_TestSameVarAtEndpointAndInParams) { using stan::math::var; var a = 2.0; From 4773aa75f6e00e571811e5bdf788602d0079d0e9 Mon Sep 17 00:00:00 2001 From: Aki Vehtari Date: Wed, 20 May 2026 18:33:27 +0300 Subject: [PATCH 03/13] add integrate_1d_double_exponential --- stan/math/fwd/functor.hpp | 1 + .../integrate_1d_double_exponential.hpp | 113 +++ stan/math/prim/functor.hpp | 1 + .../integrate_1d_double_exponential.hpp | 288 ++++++ stan/math/rev/functor.hpp | 1 + .../integrate_1d_double_exponential.hpp | 210 ++++ .../integrate_1d_double_exponential_test.cpp | 24 + .../integrate_1d_double_exponential_test.cpp | 548 +++++++++++ .../integrate_1d_double_exponential_test.cpp | 903 ++++++++++++++++++ 9 files changed, 2089 insertions(+) create mode 100644 stan/math/fwd/functor/integrate_1d_double_exponential.hpp create mode 100644 stan/math/prim/functor/integrate_1d_double_exponential.hpp create mode 100644 stan/math/rev/functor/integrate_1d_double_exponential.hpp create mode 100644 test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp create mode 100644 test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp create mode 100644 test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp diff --git a/stan/math/fwd/functor.hpp b/stan/math/fwd/functor.hpp index 1258579d80a..39b24cfa7bf 100644 --- a/stan/math/fwd/functor.hpp +++ b/stan/math/fwd/functor.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/fwd/functor/integrate_1d_double_exponential.hpp b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp new file mode 100644 index 00000000000..916da43a4f0 --- /dev/null +++ b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp @@ -0,0 +1,113 @@ +#ifndef STAN_MATH_FWD_FUNCTOR_INTEGRATE_1D_DOUBLE_EXPONENTIAL_HPP +#define STAN_MATH_FWD_FUNCTOR_INTEGRATE_1D_DOUBLE_EXPONENTIAL_HPP + +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { +/** + * Return the integral of f from a to b using adaptive double-exponential + * quadrature, with tangents computed via finite differences over the + * integrand parameters. + * + * @tparam F Type of f + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam Args types of parameter pack arguments + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_refinements maximum refinement level passed to the Boost + * quadrature class constructor + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments to pass to f + * @return numeric integral of function f + */ +template * = nullptr> +inline return_type_t integrate_1d_double_exponential_impl( + const F &f, const T_a &a, const T_b &b, double relative_tolerance, + double absolute_tolerance, int max_refinements, std::ostream *msgs, + const Args &... args) { + using FvarT = scalar_type_t>; + + auto a_val = value_of(a); + auto b_val = value_of(b); + auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_refinements, + a_val, b_val](const auto &... args_var) { + return integrate_1d_double_exponential_impl( + f, a_val, b_val, relative_tolerance, absolute_tolerance, + max_refinements, msgs, args_var...); + }; + FvarT ret = finite_diff(func, args...); + + if constexpr (is_fvar::value || is_fvar::value) { + auto val_args = std::make_tuple(value_of(args)...); + if constexpr (is_fvar::value) { + ret.d_ += a.d_ + * math::apply( + [&](auto &&... tuple_args) { + return -f(a_val, 0.0, msgs, tuple_args...); + }, + val_args); + } + if constexpr (is_fvar::value) { + ret.d_ += b.d_ + * math::apply( + [&](auto &&... tuple_args) { + return f(b_val, 0.0, msgs, tuple_args...); + }, + val_args); + } + } + return ret; +} + +/** + * Compute the integral of the single variable function f from a to b using + * adaptive double-exponential quadrature. a and b can be finite or + * infinite. + * + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam T_theta type of parameters + * @tparam F Type of f + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param theta additional parameters to be passed to f + * @param x_r additional data to be passed to f + * @param x_i additional integer data to be passed to f + * @param[in, out] msgs the print stream for warning messages + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_refinements maximum refinement level passed to the Boost + * quadrature class constructor + * @return numeric integral of function f + */ +template * = nullptr> +inline return_type_t integrate_1d_double_exponential( + const F &f, const T_a &a, const T_b &b, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs, const double relative_tolerance, + const double absolute_tolerance = 0.0, + const int max_refinements + = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { + return integrate_1d_double_exponential_impl( + integrate_1d_adapter(f), a, b, relative_tolerance, absolute_tolerance, + max_refinements, msgs, theta, x_r, x_i); +} + +} // namespace math +} // namespace stan +#endif diff --git a/stan/math/prim/functor.hpp b/stan/math/prim/functor.hpp index 2ffa0c10dc8..7c1d0bb059d 100644 --- a/stan/math/prim/functor.hpp +++ b/stan/math/prim/functor.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/prim/functor/integrate_1d_double_exponential.hpp b/stan/math/prim/functor/integrate_1d_double_exponential.hpp new file mode 100644 index 00000000000..b1f01df4579 --- /dev/null +++ b/stan/math/prim/functor/integrate_1d_double_exponential.hpp @@ -0,0 +1,288 @@ +#ifndef STAN_MATH_PRIM_FUNCTOR_INTEGRATE_1D_DOUBLE_EXPONENTIAL_HPP +#define STAN_MATH_PRIM_FUNCTOR_INTEGRATE_1D_DOUBLE_EXPONENTIAL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { + +/** + * Default maximum refinement count used by integrate_1d_double_exponential + * when the user does not pass one explicitly. Matches Boost's tanh_sinh + * default (Boost's exp_sinh/sinh_sinh default is 9; we use 15 here for + * symmetry with integrate_1d_gauss_kronrod's max_depth = 15 and because + * tanh_sinh is by far the most-dispatched branch). + */ +constexpr int INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS = 15; + +/** + * Integrate a single variable function f from a to b using Boost's adaptive + * double-exponential quadrature, with QUADPACK-style mixed convergence + * criterion. The integration succeeds (returns the Boost estimate Q) + * whenever + * error <= max(relative_tolerance * L1, absolute_tolerance) + * on each piece, where error and L1 are Boost's quadrature-error and + * L1-norm estimates for that piece. A larger error throws + * std::domain_error. + * + * Setting absolute_tolerance to a small positive value escapes the + * pathological regime where the relative-tolerance test on its own is + * checking accumulated floating-point round-off against itself. This + * happens in particular under the zero-crossing split (see below) when + * one half of the split integrates to near-zero: the strict per-piece + * relative test on that half can fire spuriously. Setting it to zero + * (the default) reproduces the strict pure-relative-tolerance + * behaviour of integrate_1d. + * + * The signature for f should be: + * double f(double x, double xc) + * + * Unlike integrate_1d_gauss_kronrod, double-exponential quadrature + * computes a meaningful distance-to-boundary xc, and user functors may + * (and should, for accuracy near singular endpoints) use it. For + * a > 0, b > 0, xc is a - x for x closer to a, and b - x for x closer + * to b. xc is computed in a way that avoids the precision loss of + * computing a - x or b - x manually. For integrals that cross zero, xc + * can take values a - x, -x, or b - x depending on which integration + * limit is nearest. If either limit is infinite, xc is set to NaN. + * + * Depending on whether or not a is finite or negative infinity and b is + * finite or positive infinity, a different version of the 1d quadrature + * algorithm from the Boost quadrature library is chosen. + * + * Integrals that cross zero are broken into two, and the separate + * integrals are each integrated to the given tolerances. This is the + * pre-existing behaviour of integrate_1d, preserved here to maintain + * call-site compatibility. + * + * @tparam F Type of f + * @param f the function to be integrated + * @param a lower limit of integration + * @param b upper limit of integration + * @param relative_tolerance target relative tolerance passed to Boost + * quadrature + * @param absolute_tolerance absolute-error floor on the per-piece + * convergence test + * @param max_refinements maximum refinement level passed to the + * constructor of the Boost quadrature class + * @return numeric integral of function f + */ +template +inline double integrate_de(const F& f, double a, double b, + double relative_tolerance, + double absolute_tolerance, int max_refinements) { + static constexpr const char* function = "integrate_1d_double_exponential"; + double error1 = 0.0; + double error2 = 0.0; + double L1 = 0.0; + double L2 = 0.0; + size_t levels = 0; + + const size_t mr = max_refinements < 0 + ? 0u + : static_cast(max_refinements); + + auto one_integral_convergence_check = [&](double e, double rel_tol, + double abs_tol, double L) { + const double threshold = std::max(rel_tol * L, abs_tol); + if (e > threshold) { + [e]() STAN_COLD_PATH { + throw_domain_error( + function, "error estimate of integral", e, "", + " exceeds max(relative_tolerance * L1, absolute_tolerance)"); + }(); + } + }; + auto two_integral_convergence_check = [&](double e1, double e2, + double rel_tol, double abs_tol, + double La, double Lb) { + const double threshold_a = std::max(rel_tol * La, abs_tol); + const double threshold_b = std::max(rel_tol * Lb, abs_tol); + if (e1 > threshold_a) { + [e1]() STAN_COLD_PATH { + throw_domain_error( + function, "error estimate of integral below zero", e1, "", + " exceeds max(relative_tolerance * L1, absolute_tolerance) for " + "the lower piece of the split"); + }(); + } + if (e2 > threshold_b) { + [e2]() STAN_COLD_PATH { + throw_domain_error( + function, "error estimate of integral above zero", e2, "", + " exceeds max(relative_tolerance * L1, absolute_tolerance) for " + "the upper piece of the split"); + }(); + } + }; + + // If a or b is infinite, set xc to NaN (see docs above) + auto f_wrap = [&f](double x) { return f(x, NOT_A_NUMBER); }; + if (std::isinf(a) && std::isinf(b)) { + boost::math::quadrature::sinh_sinh integrator(mr); + double Q = integrator.integrate(f_wrap, relative_tolerance, &error1, &L1, + &levels); + one_integral_convergence_check(error1, relative_tolerance, + absolute_tolerance, L1); + return Q; + } else if (std::isinf(a)) { + boost::math::quadrature::exp_sinh integrator(mr); + // If the integral crosses zero, break it into two (advice from the + // Boost implementation). + if (b <= 0.0) { + double Q = integrator.integrate(f_wrap, a, b, relative_tolerance, &error1, + &L1, &levels); + one_integral_convergence_check(error1, relative_tolerance, + absolute_tolerance, L1); + return Q; + } else { + boost::math::quadrature::tanh_sinh integrator_right(mr); + double Q = integrator.integrate(f_wrap, a, 0.0, relative_tolerance, + &error1, &L1, &levels) + + integrator_right.integrate( + f_wrap, 0.0, b, relative_tolerance, &error2, &L2, &levels); + two_integral_convergence_check(error1, error2, relative_tolerance, + absolute_tolerance, L1, L2); + return Q; + } + } else if (std::isinf(b)) { + boost::math::quadrature::exp_sinh integrator(mr); + if (a >= 0.0) { + double Q = integrator.integrate(f_wrap, a, b, relative_tolerance, &error1, + &L1, &levels); + one_integral_convergence_check(error1, relative_tolerance, + absolute_tolerance, L1); + return Q; + } else { + boost::math::quadrature::tanh_sinh integrator_left(mr); + double Q = integrator_left.integrate(f_wrap, a, 0, relative_tolerance, + &error1, &L1, &levels) + + integrator.integrate(f_wrap, relative_tolerance, &error2, + &L2, &levels); + two_integral_convergence_check(error1, error2, relative_tolerance, + absolute_tolerance, L1, L2); + return Q; + } + } else { + auto f_wrap = [&f](double x, double xc) { return f(x, xc); }; + boost::math::quadrature::tanh_sinh integrator(mr); + if (a < 0.0 && b > 0.0) { + double Q = integrator.integrate(f_wrap, a, 0.0, relative_tolerance, + &error1, &L1, &levels) + + integrator.integrate(f_wrap, 0.0, b, relative_tolerance, + &error2, &L2, &levels); + two_integral_convergence_check(error1, error2, relative_tolerance, + absolute_tolerance, L1, L2); + return Q; + } else { + double Q = integrator.integrate(f_wrap, a, b, relative_tolerance, &error1, + &L1, &levels); + one_integral_convergence_check(error1, relative_tolerance, + absolute_tolerance, L1); + return Q; + } + } +} + +/** + * Compute the integral of the single variable function f from a to b using + * adaptive double-exponential quadrature. a and b can be finite or infinite. + * + * @tparam F type of function to integrate + * @tparam Args types of additional arguments forwarded to f (all arithmetic) + * + * @param f the function to be integrated + * @param a lower limit of integration + * @param b upper limit of integration + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_refinements maximum refinement level passed to the + * Boost quadrature class constructor + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments passed to f + * @return numeric integral of function f + */ +template * = nullptr> +inline double integrate_1d_double_exponential_impl( + const F& f, double a, double b, double relative_tolerance, + double absolute_tolerance, int max_refinements, std::ostream* msgs, + const Args&... args) { + static constexpr const char* function = "integrate_1d_double_exponential"; + check_less_or_equal(function, "lower limit", a, b); + check_nonnegative(function, "max_refinements", max_refinements); + check_nonnegative(function, "absolute_tolerance", absolute_tolerance); + if (unlikely(a == b)) { + if (std::isinf(a)) { + throw_domain_error(function, "Integration endpoints are both", a, "", ""); + } + return 0.0; + } else { + return integrate_de( + [&](auto&& x, auto&& xc) { return f(x, xc, msgs, args...); }, a, b, + relative_tolerance, absolute_tolerance, max_refinements); + } +} + +/** + * Compute the integral of the single variable function f from a to b using + * adaptive double-exponential quadrature. a and b can be finite or infinite. + * + * The signature for f should be: + * double f(double x, double xc, const std::vector& theta, + * const std::vector& x_r, const std::vector& x_i, + * std::ostream* msgs) + * + * It should return the value of the function evaluated at x. Any errors + * should be printed to the msgs stream. + * + * The integration algorithm terminates per piece when + * error <= max(relative_tolerance * L1, absolute_tolerance) + * where L1 is the Boost estimate of the L1 norm of the integral. + * + * @tparam F type of function to integrate + * + * @param f the function to be integrated + * @param a lower limit of integration + * @param b upper limit of integration + * @param theta additional parameters to be passed to f + * @param x_r additional data to be passed to f + * @param x_i additional integer data to be passed to f + * @param[in, out] msgs the print stream for warning messages + * @param relative_tolerance tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_refinements maximum refinement level passed to the Boost + * quadrature class constructor + * @return numeric integral of function f + */ +template +inline double integrate_1d_double_exponential( + const F& f, double a, double b, const std::vector& theta, + const std::vector& x_r, const std::vector& x_i, + std::ostream* msgs, + const double relative_tolerance = std::sqrt(EPSILON), + const double absolute_tolerance = 0.0, + const int max_refinements + = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { + return integrate_1d_double_exponential_impl( + integrate_1d_adapter(f), a, b, relative_tolerance, absolute_tolerance, + max_refinements, msgs, theta, x_r, x_i); +} + +} // namespace math +} // namespace stan + +#endif diff --git a/stan/math/rev/functor.hpp b/stan/math/rev/functor.hpp index d95bfcd8bf9..205143df554 100644 --- a/stan/math/rev/functor.hpp +++ b/stan/math/rev/functor.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/rev/functor/integrate_1d_double_exponential.hpp b/stan/math/rev/functor/integrate_1d_double_exponential.hpp new file mode 100644 index 00000000000..285da2b7a97 --- /dev/null +++ b/stan/math/rev/functor/integrate_1d_double_exponential.hpp @@ -0,0 +1,210 @@ +#ifndef STAN_MATH_REV_FUNCTOR_INTEGRATE_1D_DOUBLE_EXPONENTIAL_HPP +#define STAN_MATH_REV_FUNCTOR_INTEGRATE_1D_DOUBLE_EXPONENTIAL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace stan { +namespace math { + +/** + * Return the integral of f from a to b using adaptive double-exponential + * quadrature. + * + * @tparam F Type of f + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam Args types of parameter pack arguments + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_refinements maximum refinement level passed to the Boost + * quadrature class constructor + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments to pass to f + * @return numeric integral of function f + */ +template * = nullptr> +inline return_type_t integrate_1d_double_exponential_impl( + const F &f, const T_a &a, const T_b &b, double relative_tolerance, + double absolute_tolerance, int max_refinements, std::ostream *msgs, + const Args &... args) { + static constexpr const char *function = "integrate_1d_double_exponential"; + check_less_or_equal(function, "lower limit", a, b); + check_nonnegative(function, "max_refinements", max_refinements); + check_nonnegative(function, "absolute_tolerance", absolute_tolerance); + + double a_val = value_of(a); + double b_val = value_of(b); + + if (unlikely(a_val == b_val)) { + if (is_inf(a_val)) { + throw_domain_error(function, "Integration endpoints are both", a_val, "", + ""); + } + return var(0.0); + } else { + auto args_val_tuple = std::make_tuple(value_of(args)...); + + double integral = integrate_de( + [&](const auto &x, const auto &xc) { + return math::apply( + [&](auto &&... val_args) { return f(x, xc, msgs, val_args...); }, + args_val_tuple); + }, + a_val, b_val, relative_tolerance, absolute_tolerance, max_refinements); + + constexpr size_t num_vars_ab = is_var::value + is_var::value; + size_t num_vars_args = count_vars(args...); + vari **varis = ChainableStack::instance_->memalloc_.alloc_array( + num_vars_ab + num_vars_args); + double *partials = ChainableStack::instance_->memalloc_.alloc_array( + num_vars_ab + num_vars_args); + double *partials_ptr = partials; + + save_varis(varis, a, b, args...); + + for (size_t i = 0; i < num_vars_ab + num_vars_args; ++i) { + partials[i] = 0.0; + } + + if constexpr (is_var::value) { + if (!is_inf(a)) { + *partials_ptr = math::apply( + [&f, a_val, msgs](auto &&... val_args) { + return -f(a_val, 0.0, msgs, val_args...); + }, + args_val_tuple); + partials_ptr++; + } + } + + if constexpr (is_var::value) { + if (!is_inf(b)) { + *partials_ptr = math::apply( + [&f, b_val, msgs](auto &&... val_args) { + return f(b_val, 0.0, msgs, val_args...); + }, + args_val_tuple); + partials_ptr++; + } + } + + { + nested_rev_autodiff argument_nest; + auto args_tuple_local_copy = std::make_tuple(deep_copy_vars(args)...); + std::vector local_varis(num_vars_args); + math::apply( + [&](const auto &... args) { + save_varis(local_varis.data(), args...); + }, + args_tuple_local_copy); + + for (size_t n = 0; n < num_vars_args; ++n) { + *partials_ptr = integrate_de( + [&](const auto &x, const auto &xc) { + argument_nest.set_zero_all_adjoints(); + + nested_rev_autodiff gradient_nest; + var fx = math::apply( + [&f, &x, &xc, msgs](auto &&... local_args) { + return f(x, xc, msgs, local_args...); + }, + args_tuple_local_copy); + fx.grad(); + + double gradient = local_varis[n]->adj(); + + if (is_nan(gradient)) { + if (fx.val() == 0) { + gradient = 0; + } else { + throw_domain_error("gradient_of_f", "The gradient of f", n, + "is nan for parameter ", ""); + } + } + return gradient; + }, + a_val, b_val, relative_tolerance, absolute_tolerance, + max_refinements); + partials_ptr++; + } + } + + return make_callback_var( + integral, + [total_vars = num_vars_ab + num_vars_args, varis, partials](auto &vi) { + for (size_t i = 0; i < total_vars; ++i) { + varis[i]->adj_ += partials[i] * vi.adj(); + } + }); + } +} + +/** + * Compute the integral of the single variable function f from a to b using + * adaptive double-exponential quadrature. a and b can be finite or + * infinite. + * + * f should be compatible with reverse mode autodiff and have the signature: + * var f(double x, double xc, const std::vector& theta, + * const std::vector& x_r, const std::vector &x_i, + * std::ostream* msgs) + * + * Gradients of f that evaluate to NaN when the function evaluates to zero + * are set to zero themselves. + * + * @tparam T_a type of first limit + * @tparam T_b type of second limit + * @tparam T_theta type of parameters + * @tparam F Type of f + * + * @param f the functor to integrate + * @param a lower limit of integration + * @param b upper limit of integration + * @param theta additional parameters to be passed to f + * @param x_r additional data to be passed to f + * @param x_i additional integer data to be passed to f + * @param[in, out] msgs the print stream for warning messages + * @param relative_tolerance relative tolerance passed to Boost quadrature + * @param absolute_tolerance absolute-error floor on the convergence test + * @param max_refinements maximum refinement level passed to the Boost + * quadrature class constructor + * @return numeric integral of function f + */ +template > +inline return_type_t integrate_1d_double_exponential( + const F &f, const T_a &a, const T_b &b, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs, + const double relative_tolerance = std::sqrt(EPSILON), + const double absolute_tolerance = 0.0, + const int max_refinements + = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { + return integrate_1d_double_exponential_impl( + integrate_1d_adapter(f), a, b, relative_tolerance, absolute_tolerance, + max_refinements, msgs, theta, x_r, x_i); +} + +} // namespace math +} // namespace stan + +#endif diff --git a/test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp new file mode 100644 index 00000000000..f520e36f6f9 --- /dev/null +++ b/test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp @@ -0,0 +1,24 @@ +#include + +TEST(mixFunctor, integrate1DDoubleExponential) { + auto f = [&](const auto& x_input, const auto& lb, const auto& ub) { + auto func = [](const auto& x, const auto& xc, std::ostream* msgs, + const auto& theta) { + return stan::math::exp(theta * stan::math::cos(2 * 3.141593 * x)) + theta; + }; + const double relative_tolerance = std::sqrt(stan::math::EPSILON); + const double absolute_tolerance = 0.0; + const int max_refinements = 15; + std::ostringstream* msgs = nullptr; + return stan::math::integrate_1d_double_exponential_impl( + func, lb, ub, relative_tolerance, absolute_tolerance, max_refinements, + msgs, x_input); + }; + stan::test::expect_ad(f, 0.75, 0, 1); + stan::test::expect_ad(f, 0.2, 0.2, 0.7); + // The NOT_A_NUMBER case is included in the upstream integrate_1d mix + // test. We keep it here too because tanh_sinh's behaviour on + // NaN-everywhere integrands matches what integrate_1d_double_exponential + // inherits (the upstream test passes; this is a port of the same). + stan::test::expect_ad(f, stan::math::NOT_A_NUMBER, 0, 1); +} diff --git a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp new file mode 100644 index 00000000000..3e732410411 --- /dev/null +++ b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp @@ -0,0 +1,548 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace integrate_1d_de_test { + +std::ostringstream *msgs = nullptr; + +struct f1 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(-x) / sqrt(x); + } +}; + +struct f2 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + if (x <= 0.5) { + return sqrt(x) / sqrt(1 - x * x); + } else { + return sqrt(x / ((x + 1) * (xc))); + } + } +}; + +struct f3 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(-x); + } +}; + +struct f4 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + theta[0]; + } +}; + +struct f5 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(theta[0], 2) + pow(theta[1], 3); + } +}; + +struct f6 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(x_i[0], 2) + pow(theta[0], 4) + 3 * theta[1]; + } +}; + +struct f7 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(x_r[0], 2) + pow(x_r[1], 5) + 3 * x_r[2]; + } +}; + +struct f8 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(-pow(x - theta[0], x_i[0]) / pow(x_r[0], x_i[0])); + } +}; + +struct f9 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return 1.0 / (1.0 + pow(x, x_i[0]) / theta[0]); + } +}; + +struct f10 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return pow(x, theta[0] - 1.0) + * pow((x > 0.5) ? xc : (1 - x), theta[1] - 1.0); + } +}; + +struct f11 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return (std::isnan(xc)) ? xc : 0.0; + } +}; + +struct f12 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + T1 out = stan::math::modified_bessel_second_kind(0, x); + if (out > 0) + return 2 * x * out; + return out; + } +}; + +struct f13 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + T1 out = stan::math::modified_bessel_second_kind(0, x); + if (out > 0) + return 2 * x * stan::math::square(out); + return out; + } +}; + +struct f14 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) * stan::math::inv_sqrt(x > 0.5 ? xc : 1 - x); + } +}; + +struct f15 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + T1 x2 = x * x; + T1 numer = x2 * log(x); + T1 denom = x < 0.5 ? (x + 1) * (x - 1) : (x + 1) * -xc; + denom *= x2 * x2 + 1; + return numer / denom; + } +}; + +struct f16 { + template + inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + return x * sin(x) / (1 + stan::math::square(cos(x))); + } +}; + +struct f17 { + inline double operator()(const double &x, const double &xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *msgs) const { + double mu = theta[0]; + double sigma = theta[1]; + return 1.0 / (sqrt(2.0 * stan::math::pi()) * sigma) + * std::exp(-0.5 * ((x - mu) / sigma) * ((x - mu) / sigma)); + } +}; + +inline double lbaX_pdf(double X, double t, double A, double v, double s, + std::ostream *pstream__) { + double b_A_tv_ts; + double b_tv_ts; + double term_1; + double term_2; + double pdf; + + b_A_tv_ts = (((X - A) - (t * v)) / (t * s)); + b_tv_ts = ((X - (t * v)) / (t * s)); + term_1 = stan::math::Phi(b_A_tv_ts); + term_2 = stan::math::Phi(b_tv_ts); + pdf = ((1 / A) * (-term_1 + term_2)); + return pdf; +} + +inline double lbaX_cdf(double X, double t, double A, double v, double s, + std::ostream *pstream__) { + double b_A_tv; + double b_tv; + double ts; + double term_1; + double term_2; + double term_3; + double term_4; + double cdf; + + b_A_tv = ((X - A) - (t * v)); + b_tv = (X - (t * v)); + ts = (t * s); + term_1 = (b_A_tv * stan::math::Phi((b_A_tv / ts))); + term_2 = (b_tv * stan::math::Phi((b_tv / ts))); + term_3 = (ts + * stan::math::exp( + stan::math::normal_lpdf((b_A_tv / ts), 0, 1))); + term_4 + = (ts + * stan::math::exp(stan::math::normal_lpdf((b_tv / ts), 0, 1))); + cdf = ((1 / A) * (((-term_1 + term_2) - term_3) + term_4)); + return cdf; +} + +inline double rank_density(double x, double xc, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i, + std::ostream *pstream__) { + double t = theta[0]; + double A = theta[1]; + double v1 = theta[2]; + double v2 = theta[3]; + double s = theta[4]; + double v = (lbaX_pdf(x, t, A, v1, s, pstream__) + * lbaX_cdf(x, t, A, v2, s, pstream__)); + return v; +} + +struct rank_density_functor__ { + double operator()(double x, double xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *pstream__) const { + return rank_density(x, xc, theta, x_r, x_i, pstream__); + } +}; + +inline double order(double down, double up, const std::vector &theta, + const std::vector &x_r, std::ostream *pstream__) { + std::vector x_i; + + double v; + + v = stan::math::integrate_1d_double_exponential(rank_density_functor__(), down, up, theta, x_r, + x_i, pstream__, 1e-8); + return v; +} +} // namespace integrate_1d_de_test +/* + * test_integration is a helper function to make it easy to test the + * integrate_1d function. + * + * It takes in a callable function object, integration limits, parameters, real + * and integer data. It integrates the provided function and compares the + * computed integral against the provided integral (val). + * + * The prototype for f is: + * struct f10 { + * inline double operator()(const double& x, const double& xc, const + * std::vector& theta, const std::vector& x_r, const + * std::vector& x_i, std::ostream& msgs) const { + * } + * }; + * + * @tparam F Type of f + * @param f a functor with signature given above + * @param a lower limit of integration + * @param b upper limit of integration + * @param param parameters to be passed to f (should be + * std::vector) + * @param x_r real data to be passed to f (should be std::vector) + * @param x_i integer data to be passed to f (should be std::vector) + * @param val correct value of integral + */ +template +inline void test_integration(const F &f, double a, double b, + std::vector thetas, + const std::vector &x_r, + const std::vector &x_i, double val) { + using stan::math::integrate_1d_double_exponential; + + std::vector tolerances = {1e-4, 1e-6, 1e-8}; + + for (auto tolerance : tolerances) { + EXPECT_LE(std::abs(integrate_1d_double_exponential(f, a, b, thetas, x_r, x_i, + integrate_1d_de_test::msgs, tolerance) + - val), + tolerance); + // Flip the domain of integration and check that the integral is working + auto flipped = + [&](const double &x, const double &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) { return f(-x, -xc, theta, x_r, x_i, msgs); }; + EXPECT_LE(std::abs(integrate_1d_double_exponential(flipped, -b, -a, thetas, x_r, x_i, + integrate_1d_de_test::msgs, tolerance) + - val), + tolerance); + } +} + +TEST(StanMath_integrate_1d_de_prim, TestThrows) { + // Left limit of integration must be less than or equal to right limit + EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 1.0, 0.0, + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + // NaN limits not okay + EXPECT_THROW( + stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0.0, + std::numeric_limits::quiet_NaN(), + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, std::numeric_limits::quiet_NaN(), + 0.0, std::vector(), {}, {}, integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), std::vector(), {}, + {}, integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + // Two of the same inf limits not okay + EXPECT_THROW( + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), std::vector(), {}, + {}, integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + + EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, + std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + // xc should be nan if there are infinite limits + EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, 0.0, + std::numeric_limits::infinity(), + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6), + std::runtime_error); + EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, + std::numeric_limits::infinity(), + 0.0, std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, + std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + // But not otherwise + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, 0.0, 1.0, + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); +} + +TEST(StanMath_integrate_1d_de_prim, test_integer_arguments) { + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0, 1, + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0.0, 1, + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0, 1.0, + std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); +} + +TEST(StanMath_integrate_1d_de_prim, test1) { + // Tricky integral from Boost docs + limit at infinity + test_integration(integrate_1d_de_test::f1{}, 0.0, + std::numeric_limits::infinity(), {}, {}, {}, + 1.772453850905516); + // Tricky integral from Boost 1d integration docs + test_integration(integrate_1d_de_test::f2{}, 0.0, 1.0, {}, {}, {}, + 1.198140234735592); + // Tricky integral from Boost 1d integration docs + test_integration(integrate_1d_de_test::f2{}, 0, 1, {}, {}, {}, + 1.198140234735592); + // Zero crossing integral + limit at infinity + test_integration(integrate_1d_de_test::f3{}, -2.0, + std::numeric_limits::infinity(), {}, {}, {}, + 7.38905609893065); + // Easy integrals + test_integration(integrate_1d_de_test::f4{}, 0.2, 0.7, {0.5}, {}, {}, + 1.0423499493102901); + test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, {0.4, 0.4}, {}, {}, + 1.396621954392482); + test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, {0.5}, {}, {}, 0.0); + test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, {}, {}, 0.0); + // Test x_i + test_integration(integrate_1d_de_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, + 4131.985414616364); + // Test x_r + test_integration(integrate_1d_de_test::f7{}, -0.2, 2.9, {}, {4.0, 6.0, 5.1}, {}, + 24219.985414616367); + // Both limits at infinity + test x_r/x_i + test_integration(integrate_1d_de_test::f8{}, + -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {5.0}, {1.7}, {2}, + 3.013171546539377); + // Both limits at infinity + test x_i + test_integration(integrate_1d_de_test::f9{}, + -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {1.3}, {}, {4}, + 2.372032924895055); + // Various integrals of beta function + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.1}, {}, {}, + 19.71463948905016); + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.5}, {}, {}, + 11.32308697521577); + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.5, 0.1}, {}, {}, + 11.32308697521577); + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {5.0, 3.0}, {}, {}, + 0.00952380952380952); + + // Integrals from + // http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf + test_integration(integrate_1d_de_test::f12{}, 0.0, + std::numeric_limits::infinity(), {}, {}, {}, 2.0); + test_integration(integrate_1d_de_test::f13{}, 0.0, + std::numeric_limits::infinity(), {}, {}, {}, 1.0); + test_integration(integrate_1d_de_test::f14{}, 0.0, 1.0, {}, {}, {}, + exp(1) * sqrt(stan::math::pi()) * stan::math::erf(1.0)); + + // Integrals from http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/quadrature.pdf + // works normally but not to tolerance when limits of integration are flipped + // test_integration(f15{}, 0.0, 1.0, {}, {}, {}, + // stan::math::square(stan::math::pi()) * (2 - sqrt(2.0)) / + // 32); + test_integration(integrate_1d_de_test::f16{}, 0.0, stan::math::pi(), {}, {}, {}, + stan::math::square(stan::math::pi()) / 4); + + // Make sure bounds working right + test_integration(integrate_1d_de_test::f17{}, + -std::numeric_limits::infinity(), -1.5, {0.0, 1.0}, + {}, {}, 0.066807201268858071); +} + +TEST(StanMath_integrate_1d_de_prim, TestTolerance) { + std::ostringstream *msgs = nullptr; + + double t = 0.5; + double A = 0.5; + double v1 = 1.0; + double v2 = 1.0; + double s = 1.0; + + std::vector theta = {t, A, v1, v2, s}; + std::vector x_r; + + EXPECT_NO_THROW(integrate_1d_de_test::order(-10, 0.67, theta, x_r, msgs)); +} + +// Smoke test: explicit absolute_tolerance is accepted and does not change +// the value on a well-converged integrand. +TEST(StanMath_integrate_1d_de_prim, abs_tol_argument_smoke) { + using stan::math::integrate_1d_double_exponential; + std::ostringstream *msgs = nullptr; + double Q_strict = integrate_1d_double_exponential( + integrate_1d_de_test::f4{}, 0.2, 0.7, std::vector{0.5}, {}, {}, + msgs, 1e-8, 0.0); + double Q_lenient = integrate_1d_double_exponential( + integrate_1d_de_test::f4{}, 0.2, 0.7, std::vector{0.5}, {}, {}, + msgs, 1e-8, 1e-12); + EXPECT_NEAR(Q_strict, 1.0423499493102901, 1e-8); + EXPECT_NEAR(Q_lenient, Q_strict, 1e-12); +} + +// Smoke test: explicit max_refinements is accepted and produces a sensible +// result. Argument order is (rel_tol, abs_tol, max_refinements). +TEST(StanMath_integrate_1d_de_prim, max_refinements_argument) { + using stan::math::integrate_1d_double_exponential; + std::ostringstream *msgs = nullptr; + double Q = integrate_1d_double_exponential( + integrate_1d_de_test::f4{}, 0.2, 0.7, std::vector{0.5}, {}, {}, + msgs, 1e-8, 0.0, 20); + EXPECT_NEAR(Q, 1.0423499493102901, 1e-8); +} + +// Negative max_refinements not okay. +TEST(StanMath_integrate_1d_de_prim, negative_max_refinements_throws) { + using stan::math::integrate_1d_double_exponential; + std::ostringstream *msgs = nullptr; + EXPECT_THROW(integrate_1d_double_exponential( + integrate_1d_de_test::f4{}, 0.2, 0.7, + std::vector{0.5}, {}, {}, msgs, 1e-6, 0.0, -1), + std::domain_error); +} + +// Negative absolute_tolerance not okay. +TEST(StanMath_integrate_1d_de_prim, negative_abs_tol_throws) { + using stan::math::integrate_1d_double_exponential; + std::ostringstream *msgs = nullptr; + EXPECT_THROW(integrate_1d_double_exponential( + integrate_1d_de_test::f4{}, 0.2, 0.7, + std::vector{0.5}, {}, {}, msgs, 1e-6, -1e-3), + std::domain_error); +} diff --git a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp new file mode 100644 index 00000000000..d2836b978c8 --- /dev/null +++ b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp @@ -0,0 +1,903 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace integrate_1d_de_test { + +std::ostringstream *msgs = nullptr; + +struct f1 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + theta[0]; + } +}; + +struct f2 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(theta[0] * cos(2 * 3.141593 * x)) + theta[0]; + } +}; + +struct f3 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(x) + pow(theta[0], x_r[0]) + 2 * pow(theta[1], x_r[1]) + + 2 * theta[2]; + } +}; + +struct f4 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(-x) / sqrt(x); + } +}; + +struct f5 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(-theta[0] * x) / sqrt(theta[1] * x); + } +}; + +struct f6 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return sqrt(x / (1 - theta[0] * x * x)); + } +}; + +struct f7 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(-theta[0] * x); + } +}; + +struct f8 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return exp(theta[0] * x); + } +}; + +struct f10 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return 1 / (1 + pow(x, x_i[0]) / x_r[0]); + } +}; + +struct f11 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return pow(x, theta[0] - 1.0) + * pow((x > 0.5) ? xc : (1 - x), theta[1] - 1.0); + } +}; + +struct f12 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + T3 mu = theta[0]; + T3 sigma = theta[1]; + return exp(-0.5 * stan::math::square((x - mu) / sigma)) + / (sigma * sqrt(2.0 * stan::math::pi())); + } +}; + +struct f13 { + template + inline stan::return_type_t operator()( + const T1 &x, const T2 &xc, const std::vector &theta, + const std::vector &x_r, const std::vector &x_i, + std::ostream *msgs) const { + return x + theta[0] + theta[1]; + } +}; + +/* + * Return the adjoint if the argument is a var + * + * @param v variable + * @return adjoint of var + */ +inline double get_adjoint_if_var(stan::math::var v) { return v.adj(); } + +/* + * If the argument is not a var, return a NaN + * + * @param v variable + * @return NaN + */ +inline double get_adjoint_if_var(double v) { + return std::numeric_limits::quiet_NaN(); +} + +/* + * test_derivatives is a helper function to make it easy to test the + * integrate_1d function. + * + * It takes in a callable function object, integration limits, parameters, real + * and integer data. It integrates the provided function and compares the + * computed integral and gradients against the provided integral value (val) and + * gradients (grad, d_a, d_b). + * + * The first three template arguments specify how the left limit, right limit, + * and parameters are tested. If the template arguments are vars, then the + * gradients are checked against the provided gradients, otherwise the provided + * gradients aren't used. + * + * The prototype for f is: + * struct f10 { + * template + * inline stan::return_type_t operator()( + * const T1& x, const T2& xc, const std::vector& theta, const + * std::vector& x_r, const std::vector& x_i, std::ostream& msgs) + * const { + * } + * }; + * + * @tparam T_a Type of integration left limit + * @tparam T_b Type of integration right limit + * @tparam T_theta Type of parameters + * @tparam F Type of f + * @param f a functor with signature given above + * @param a lower limit of integration + * @param b upper limit of integration + * @param param parameters to be passed to f (should be + * std::vector) + * @param x_r real data to be passed to f (should be std::vector) + * @param x_i integer data to be passed to f (should be std::vector) + * @param val correct value of integral + * @param grad correct value of gradients (not used if T_theta is not var) + * @param d_a correct value of gradient of integral with respect to left hand + * limit (not used if T_a is not var) + * @param d_b correct value of gradient of integral with respect to right hand + * limit (not used if T_b is not var) + */ +template +inline void test_derivatives(const F &f, double a, double b, + std::vector thetas, + const std::vector &x_r, + const std::vector &x_i, double val, + std::vector grad, double d_a = 0.0, + double d_b = 0.0) { + using stan::math::value_of; + using stan::math::var; + + std::vector tolerances = {1e-4, 1e-6, 1e-8}; + + for (auto tolerance : tolerances) { + // Reset autodiff stack + stan::math::recover_memory(); + // Convert endpoints into given template types + T_a a_(a); + T_b b_(b); + std::vector thetas_(thetas.size()); + for (size_t i = 0; i < thetas.size(); ++i) + thetas_[i] = thetas[i]; + + var integral = stan::math::integrate_1d_double_exponential(f, a_, b_, thetas_, x_r, x_i, msgs, + tolerance); + integral.grad(); + EXPECT_LE(std::abs(val - integral.val()), tolerance); + if constexpr (stan::is_var::value) { + for (size_t i = 0; i < grad.size(); ++i) + EXPECT_LE(std::abs(grad[i] - get_adjoint_if_var(thetas_[i])), + tolerance); + } + if constexpr (stan::is_var::value) { + EXPECT_LE(std::abs(d_a - get_adjoint_if_var(a_)), tolerance); + } + if constexpr (stan::is_var::value) { + EXPECT_LE(std::abs(d_b - get_adjoint_if_var(b_)), tolerance); + } + } +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_test_integer_arguments) { + stan::math::var v; + std::vector theta = {0.5}; + EXPECT_NO_THROW( + v = stan::math::integrate_1d_double_exponential(f2{}, 0, 1, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW( + v = stan::math::integrate_1d_double_exponential(f2{}, 0.0, 1, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW( + v = stan::math::integrate_1d_double_exponential(f2{}, 0, 1.0, theta, {}, {}, msgs, 1e-6)); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_easy) { + // Easy integrals + using stan::math::var; + test_derivatives(f1{}, 0.2, 0.7, {0.75}, {}, {}, + 0.7923499493102901 + 0.5 * 0.75, {0.5}); + test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + 1.56348343527304, {1.25789445875152}, + -2.148721270700128, 2.14872127069993); + test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + 1.56348343527304, {}, -2.148721270700128, + 2.14872127069993); + test_derivatives(f1{}, 0.0, 0.0, {0.75}, {}, {}, 0.0, + {0.0}); + test_derivatives(f2{}, 1.0, 1.0, {0.5}, {}, {}, 0.0, + {0.0}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing) { + // Zero crossing integral + test x_r + vars at endpoints + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, {2.5, 3.0}, + {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {5 * pow(0.5, 1.5), 12 * 1.75 * 1.75, 4.0}, + -19.06340613646808, 21.41380852375568); +} + +TEST_F( + AgradRev, + StanMath_integrate_1d_de_rev_TestDerivatives_var_right_endpoint_var_params) { + // Zero crossing integral + test x_r + vars at right endpoint + using stan::math::var; + test_derivatives( + f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + 4.0 * pow(1.75, 3.0) + + 4.0 * 3.9, + {5 * pow(0.5, 1.5), 12 * 1.75 * 1.75, 4.0}, 0.0, 21.41380852375568); +} + +TEST_F(AgradRev, + StanMath_integrate_1d_de_rev_TestDerivatives_var_left_endpoint_var_params) { + // Zero crossing integral + test x_r + var at left endpoint + using stan::math::var; + test_derivatives( + f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + 4.0 * pow(1.75, 3.0) + + 4.0 * 3.9, + {5 * pow(0.5, 1.5), 12 * 1.75 * 1.75, 4.0}, -19.06340613646808, 0.0); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_no_param_vars) { + // No param vars + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, + {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {}, -19.06340613646808, 21.41380852375568); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_left_limit_var) { + // No param vars, only left limit var + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, + {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {}, -19.06340613646808, 0.0); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_right_limit_var) { + // No param vars, only right limit var + using stan::math::var; + test_derivatives(f3{}, -1.0, 1.0, {0.5, 1.75, 3.9}, + {2.5, 3.0}, {}, + 2.350402387287579 + 2.0 * pow(0.5, 2.5) + + 4.0 * pow(1.75, 3.0) + 4.0 * 3.9, + {}, 0.0, 21.41380852375568); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky1) { + // Tricky integral from Boost docs + limit at infinity + no gradients + using stan::math::var; + test_derivatives(f4{}, 0.0, + std::numeric_limits::infinity(), + {}, {}, {}, 1.772453850905516, {}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky2) { + // Tricky integral from Boost docs + limit at infinity with gradients + using stan::math::var; + test_derivatives( + f5{}, 0.0, std::numeric_limits::infinity(), {0.5, 3.0}, {}, {}, + 1.772453850905516 / sqrt(0.5 * 3.0), + {-1.772453850905516 * 3.0 / (2 * pow(0.5 * 3.0, 1.5)), + -1.772453850905516 * 0.5 / (2 * pow(0.5 * 3.0, 1.5))}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky3) { + // Tricky integral from Boost docs + using stan::math::var; + test_derivatives( + f6{}, 0.0, 1.0, {0.75}, {}, {}, 0.851926727945904, {0.4814066053874294}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing2) { + // Zero crossing integral + limit at infinity + var at left limit + using stan::math::var; + test_derivatives( + f7{}, -5.0, std::numeric_limits::infinity(), {1.5}, {}, {}, + 1205.361609637375, {5223.23364176196}, -1808.042414456063, + std::numeric_limits::quiet_NaN()); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing3) { + // Zero crossing integral + limit at negative infinity + var at right limit + using stan::math::var; + test_derivatives( + f8{}, -std::numeric_limits::infinity(), 5.0, {1.5}, {}, {}, + 1205.361609637375, {5223.23364176196}, + std::numeric_limits::quiet_NaN(), 1808.042414456063); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_indefinite) { + // Both limits at infinity + test x_r/x_i + no gradients + using stan::math::var; + test_derivatives( + f10{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {}, {1.7}, {4}, + 2.536571480364399, {}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_endpoint_precision) { + // Various integrals of beta function + using stan::math::var; + test_derivatives(f11{}, 0.0, 1.0, {0.1, 0.1}, {}, {}, + 19.71463948905016, + {-101.229055967892, -101.229055967892}); + test_derivatives( + f11{}, 0.0, 1.0, {0.5, 0.51}, {}, {}, 3.098843783331868, + {-4.346514423368675, -4.196150770134913}); + test_derivatives( + f11{}, 0.0, 1.0, {0.51, 0.5}, {}, {}, 3.098843783331868, + {-4.196150770134913, -4.346514423368675}); + test_derivatives( + f11{}, 0.0, 1.0, {5.0, 3.0}, {}, {}, 0.00952380952380952, + {-0.004852607709750566, -0.01040816326530613}); + test_derivatives( + f11{}, 0.0, 1.0, {3.0, 5.0}, {}, {}, 0.00952380952380952, + {-0.01040816326530613, -0.004852607709750566}); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_gaussian) { + // Check Gaussian integrates to 1.0 always + using stan::math::var; + test_derivatives( + f12{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {5.7, 1}, {}, {}, 1.0, + {0.0, 0.0}); +} + +TEST_F(AgradRev, + StanMath_integrate_1d_de_rev_TestDerivativesSameVarAtEndpointAndInParams) { + using stan::math::var; + + var a = 2.0; + var b = 4.0; + std::vector thetas = {a, b}; + + var integral + = stan::math::integrate_1d_double_exponential(f13{}, a, b, thetas, {}, {}, msgs, 1e-8); + integral.grad(); + + EXPECT_LT(std::abs(18.0 - integral.val()), 1e-8); + EXPECT_LT(std::abs(-6.0 - a.adj()), 1e-8); + EXPECT_LT(std::abs(12.0 - b.adj()), 1e-8); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestBeta) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var alpha = 9.0 / 5; + var beta = 13.0 / 7; + std::vector theta = {alpha, beta}; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::beta_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, 0.0, 1.0, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{alpha, beta}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestCauchy) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::cauchy_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestChiSquare) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var nu = 9.0 / 5; + std::vector theta = {nu}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::chi_square_lpdf(x, theta[0])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x = {nu}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDoubleExponential) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {mu, sigma}; + double a = -std::numeric_limits::infinity(); + double b = mu.val(); + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::double_exponential_lpdf(x, theta[0], theta[1])); + }; + // requires two subintervals to achieve numerical accuracy + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8) + + integrate_1d_double_exponential(pdf, b, -a, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestExponential) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var beta = 9.0 / 5; + std::vector theta = {beta}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::exponential_lpdf(x, theta[0])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x = {beta}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestFrechet) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var alpha = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {alpha, sigma}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::frechet_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x = {alpha, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestGamma) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var alpha = 9.0 / 5; + var beta = 13.0 / 7; + std::vector theta = {alpha, beta}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::gamma_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{alpha, beta}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestGumbel) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var beta = 13.0 / 7; + std::vector theta = {mu, beta}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::gumbel_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, beta}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestInvChiSquared) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var nu = 9.0 / 5; + std::vector theta = {nu}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::inv_chi_square_lpdf(x, theta[0])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x = {nu}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestLogistic) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::logistic_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestLogNormal) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::lognormal_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestNormal) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::normal_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestPareto) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var m = 9.0 / 5; + var alpha = 13.0 / 7; + std::vector theta = {m, alpha}; + double b = std::numeric_limits::infinity(); + var a = m; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::pareto_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{m, alpha}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestPareto2) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var lambda = 13.0 / 7; + var alpha = 11.0 / 3; + std::vector theta = {mu, lambda, alpha}; + double b = std::numeric_limits::infinity(); + var a = mu; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::pareto_type_2_lpdf(x, theta[0], theta[1], theta[2])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, lambda, alpha}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); + EXPECT_FLOAT_EQ(1, 1 + g[2]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestRayleigh) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var sigma = 13.0 / 7; + std::vector theta = {sigma}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::rayleigh_lpdf(x, theta[0])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestScaledInvChiSquare) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var nu = 9.0 / 5; + var s = 13.0 / 7; + std::vector theta = {nu, s}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::scaled_inv_chi_square_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{nu, s}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestStudentT) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var nu = 11.0 / 3; + var mu = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {nu, mu, sigma}; + double b = std::numeric_limits::infinity(); + double a = -b; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::student_t_lpdf(x, theta[0], theta[1], theta[2])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{nu, mu, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); + EXPECT_FLOAT_EQ(1, 1 + g[2]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestUniform) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var a = 9.0 / 5; + var b = 13.0 / 7; + std::vector theta = {a, b}; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::uniform_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{a, b}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestVonMises) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var mu = 9.0 / 5; + var kappa = 13.0 / 7; + std::vector theta = {mu, kappa}; + double b = stan::math::pi() * 2; + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::von_mises_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{mu, kappa}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} + +TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestWeibull) { + using stan::math::exp; + using stan::math::integrate_1d_double_exponential; + using stan::math::var; + + var alpha = 9.0 / 5; + var sigma = 13.0 / 7; + std::vector theta = {alpha, sigma}; + double b = std::numeric_limits::infinity(); + double a = 0; + auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, + std::ostream *msgs) { + return exp(stan::math::weibull_lpdf(x, theta[0], theta[1])); + }; + var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + EXPECT_FLOAT_EQ(1, I.val()); + + std::vector x{alpha, sigma}; + std::vector g; + I.grad(x, g); + EXPECT_FLOAT_EQ(1, 1 + g[0]); + EXPECT_FLOAT_EQ(1, 1 + g[1]); +} +} // namespace integrate_1d_de_test From 829c057b3bc68234447baf072e38daa076b9202f Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 20 May 2026 11:57:25 -0400 Subject: [PATCH 04/13] [Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1 --- .../integrate_1d_double_exponential.hpp | 34 +++-- .../integrate_1d_double_exponential.hpp | 3 +- .../integrate_1d_double_exponential_test.cpp | 126 +++++++++--------- .../integrate_1d_double_exponential_test.cpp | 39 +++--- 4 files changed, 104 insertions(+), 98 deletions(-) diff --git a/stan/math/prim/functor/integrate_1d_double_exponential.hpp b/stan/math/prim/functor/integrate_1d_double_exponential.hpp index b1f01df4579..db19ebc67a0 100644 --- a/stan/math/prim/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/prim/functor/integrate_1d_double_exponential.hpp @@ -82,8 +82,8 @@ constexpr int INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS = 15; */ template inline double integrate_de(const F& f, double a, double b, - double relative_tolerance, - double absolute_tolerance, int max_refinements) { + double relative_tolerance, double absolute_tolerance, + int max_refinements) { static constexpr const char* function = "integrate_1d_double_exponential"; double error1 = 0.0; double error2 = 0.0; @@ -91,21 +91,20 @@ inline double integrate_de(const F& f, double a, double b, double L2 = 0.0; size_t levels = 0; - const size_t mr = max_refinements < 0 - ? 0u - : static_cast(max_refinements); + const size_t mr + = max_refinements < 0 ? 0u : static_cast(max_refinements); - auto one_integral_convergence_check = [&](double e, double rel_tol, - double abs_tol, double L) { - const double threshold = std::max(rel_tol * L, abs_tol); - if (e > threshold) { - [e]() STAN_COLD_PATH { - throw_domain_error( - function, "error estimate of integral", e, "", - " exceeds max(relative_tolerance * L1, absolute_tolerance)"); - }(); - } - }; + auto one_integral_convergence_check + = [&](double e, double rel_tol, double abs_tol, double L) { + const double threshold = std::max(rel_tol * L, abs_tol); + if (e > threshold) { + [e]() STAN_COLD_PATH { + throw_domain_error( + function, "error estimate of integral", e, "", + " exceeds max(relative_tolerance * L1, absolute_tolerance)"); + }(); + } + }; auto two_integral_convergence_check = [&](double e1, double e2, double rel_tol, double abs_tol, double La, double Lb) { @@ -272,8 +271,7 @@ template inline double integrate_1d_double_exponential( const F& f, double a, double b, const std::vector& theta, const std::vector& x_r, const std::vector& x_i, - std::ostream* msgs, - const double relative_tolerance = std::sqrt(EPSILON), + std::ostream* msgs, const double relative_tolerance = std::sqrt(EPSILON), const double absolute_tolerance = 0.0, const int max_refinements = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { diff --git a/stan/math/rev/functor/integrate_1d_double_exponential.hpp b/stan/math/rev/functor/integrate_1d_double_exponential.hpp index 285da2b7a97..326566b12f5 100644 --- a/stan/math/rev/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/rev/functor/integrate_1d_double_exponential.hpp @@ -194,8 +194,7 @@ template integrate_1d_double_exponential( const F &f, const T_a &a, const T_b &b, const std::vector &theta, const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs, - const double relative_tolerance = std::sqrt(EPSILON), + std::ostream *msgs, const double relative_tolerance = std::sqrt(EPSILON), const double absolute_tolerance = 0.0, const int max_refinements = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { diff --git a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp index 3e732410411..aecb5d51302 100644 --- a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp @@ -285,8 +285,8 @@ inline double order(double down, double up, const std::vector &theta, double v; - v = stan::math::integrate_1d_double_exponential(rank_density_functor__(), down, up, theta, x_r, - x_i, pstream__, 1e-8); + v = stan::math::integrate_1d_double_exponential( + rank_density_functor__(), down, up, theta, x_r, x_i, pstream__, 1e-8); return v; } } // namespace integrate_1d_de_test @@ -326,8 +326,9 @@ inline void test_integration(const F &f, double a, double b, std::vector tolerances = {1e-4, 1e-6, 1e-8}; for (auto tolerance : tolerances) { - EXPECT_LE(std::abs(integrate_1d_double_exponential(f, a, b, thetas, x_r, x_i, - integrate_1d_de_test::msgs, tolerance) + EXPECT_LE(std::abs(integrate_1d_double_exponential( + f, a, b, thetas, x_r, x_i, + integrate_1d_de_test::msgs, tolerance) - val), tolerance); // Flip the domain of integration and check that the integral is working @@ -335,8 +336,9 @@ inline void test_integration(const F &f, double a, double b, [&](const double &x, const double &xc, const std::vector &theta, const std::vector &x_r, const std::vector &x_i, std::ostream *msgs) { return f(-x, -xc, theta, x_r, x_i, msgs); }; - EXPECT_LE(std::abs(integrate_1d_double_exponential(flipped, -b, -a, thetas, x_r, x_i, - integrate_1d_de_test::msgs, tolerance) + EXPECT_LE(std::abs(integrate_1d_double_exponential( + flipped, -b, -a, thetas, x_r, x_i, + integrate_1d_de_test::msgs, tolerance) - val), tolerance); } @@ -344,16 +346,16 @@ inline void test_integration(const F &f, double a, double b, TEST(StanMath_integrate_1d_de_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit - EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 1.0, 0.0, - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6), + EXPECT_THROW(stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, 1.0, 0.0, std::vector(), + {}, {}, integrate_1d_de_test::msgs, 1e-6), std::domain_error); // NaN limits not okay EXPECT_THROW( - stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0.0, - std::numeric_limits::quiet_NaN(), - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, 0.0, + std::numeric_limits::quiet_NaN(), std::vector(), {}, + {}, integrate_1d_de_test::msgs, 1e-6), std::domain_error); EXPECT_THROW( stan::math::integrate_1d_double_exponential( @@ -374,45 +376,46 @@ TEST(StanMath_integrate_1d_de_prim, TestThrows) { {}, integrate_1d_de_test::msgs, 1e-6), std::domain_error); - EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, - std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, std::numeric_limits::infinity(), + std::numeric_limits::infinity(), std::vector(), {}, + {}, integrate_1d_de_test::msgs, 1e-6), + std::domain_error); // xc should be nan if there are infinite limits - EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, 0.0, - std::numeric_limits::infinity(), - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6), - std::runtime_error); - EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, - std::numeric_limits::infinity(), - 0.0, std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6), - std::domain_error); - EXPECT_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, - std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6), - std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f11{}, 0.0, + std::numeric_limits::infinity(), std::vector(), {}, + {}, integrate_1d_de_test::msgs, 1e-6), + std::runtime_error); + EXPECT_THROW( + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f11{}, std::numeric_limits::infinity(), + 0.0, std::vector(), {}, {}, integrate_1d_de_test::msgs, 1e-6), + std::domain_error); + EXPECT_THROW( + stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f11{}, std::numeric_limits::infinity(), + std::numeric_limits::infinity(), std::vector(), {}, + {}, integrate_1d_de_test::msgs, 1e-6), + std::domain_error); // But not otherwise - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f11{}, 0.0, 1.0, - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f11{}, 0.0, 1.0, std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); } TEST(StanMath_integrate_1d_de_prim, test_integer_arguments) { - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0, 1, - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0.0, 1, - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential(integrate_1d_de_test::f2{}, 0, 1.0, - std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, 0, 1, std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, 0.0, 1, std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( + integrate_1d_de_test::f2{}, 0, 1.0, std::vector(), {}, {}, + integrate_1d_de_test::msgs, 1e-6)); } TEST(StanMath_integrate_1d_de_prim, test1) { @@ -436,13 +439,14 @@ TEST(StanMath_integrate_1d_de_prim, test1) { test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, {0.4, 0.4}, {}, {}, 1.396621954392482); test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, {0.5}, {}, {}, 0.0); - test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, {}, {}, 0.0); + test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, {}, {}, + 0.0); // Test x_i test_integration(integrate_1d_de_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, 4131.985414616364); // Test x_r - test_integration(integrate_1d_de_test::f7{}, -0.2, 2.9, {}, {4.0, 6.0, 5.1}, {}, - 24219.985414616367); + test_integration(integrate_1d_de_test::f7{}, -0.2, 2.9, {}, {4.0, 6.0, 5.1}, + {}, 24219.985414616367); // Both limits at infinity + test x_r/x_i test_integration(integrate_1d_de_test::f8{}, -std::numeric_limits::infinity(), @@ -477,8 +481,8 @@ TEST(StanMath_integrate_1d_de_prim, test1) { // test_integration(f15{}, 0.0, 1.0, {}, {}, {}, // stan::math::square(stan::math::pi()) * (2 - sqrt(2.0)) / // 32); - test_integration(integrate_1d_de_test::f16{}, 0.0, stan::math::pi(), {}, {}, {}, - stan::math::square(stan::math::pi()) / 4); + test_integration(integrate_1d_de_test::f16{}, 0.0, stan::math::pi(), {}, {}, + {}, stan::math::square(stan::math::pi()) / 4); // Make sure bounds working right test_integration(integrate_1d_de_test::f17{}, @@ -521,9 +525,9 @@ TEST(StanMath_integrate_1d_de_prim, abs_tol_argument_smoke) { TEST(StanMath_integrate_1d_de_prim, max_refinements_argument) { using stan::math::integrate_1d_double_exponential; std::ostringstream *msgs = nullptr; - double Q = integrate_1d_double_exponential( - integrate_1d_de_test::f4{}, 0.2, 0.7, std::vector{0.5}, {}, {}, - msgs, 1e-8, 0.0, 20); + double Q = integrate_1d_double_exponential(integrate_1d_de_test::f4{}, 0.2, + 0.7, std::vector{0.5}, {}, + {}, msgs, 1e-8, 0.0, 20); EXPECT_NEAR(Q, 1.0423499493102901, 1e-8); } @@ -531,9 +535,9 @@ TEST(StanMath_integrate_1d_de_prim, max_refinements_argument) { TEST(StanMath_integrate_1d_de_prim, negative_max_refinements_throws) { using stan::math::integrate_1d_double_exponential; std::ostringstream *msgs = nullptr; - EXPECT_THROW(integrate_1d_double_exponential( - integrate_1d_de_test::f4{}, 0.2, 0.7, - std::vector{0.5}, {}, {}, msgs, 1e-6, 0.0, -1), + EXPECT_THROW(integrate_1d_double_exponential(integrate_1d_de_test::f4{}, 0.2, + 0.7, std::vector{0.5}, + {}, {}, msgs, 1e-6, 0.0, -1), std::domain_error); } @@ -541,8 +545,8 @@ TEST(StanMath_integrate_1d_de_prim, negative_max_refinements_throws) { TEST(StanMath_integrate_1d_de_prim, negative_abs_tol_throws) { using stan::math::integrate_1d_double_exponential; std::ostringstream *msgs = nullptr; - EXPECT_THROW(integrate_1d_double_exponential( - integrate_1d_de_test::f4{}, 0.2, 0.7, - std::vector{0.5}, {}, {}, msgs, 1e-6, -1e-3), + EXPECT_THROW(integrate_1d_double_exponential(integrate_1d_de_test::f4{}, 0.2, + 0.7, std::vector{0.5}, + {}, {}, msgs, 1e-6, -1e-3), std::domain_error); } diff --git a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp index d2836b978c8..83c3942547f 100644 --- a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp @@ -219,8 +219,8 @@ inline void test_derivatives(const F &f, double a, double b, for (size_t i = 0; i < thetas.size(); ++i) thetas_[i] = thetas[i]; - var integral = stan::math::integrate_1d_double_exponential(f, a_, b_, thetas_, x_r, x_i, msgs, - tolerance); + var integral = stan::math::integrate_1d_double_exponential( + f, a_, b_, thetas_, x_r, x_i, msgs, tolerance); integral.grad(); EXPECT_LE(std::abs(val - integral.val()), tolerance); if constexpr (stan::is_var::value) { @@ -240,12 +240,12 @@ inline void test_derivatives(const F &f, double a, double b, TEST_F(AgradRev, StanMath_integrate_1d_de_rev_test_integer_arguments) { stan::math::var v; std::vector theta = {0.5}; - EXPECT_NO_THROW( - v = stan::math::integrate_1d_double_exponential(f2{}, 0, 1, theta, {}, {}, msgs, 1e-6)); - EXPECT_NO_THROW( - v = stan::math::integrate_1d_double_exponential(f2{}, 0.0, 1, theta, {}, {}, msgs, 1e-6)); - EXPECT_NO_THROW( - v = stan::math::integrate_1d_double_exponential(f2{}, 0, 1.0, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential( + f2{}, 0, 1, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential( + f2{}, 0.0, 1, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential( + f2{}, 0, 1.0, theta, {}, {}, msgs, 1e-6)); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_easy) { @@ -288,8 +288,9 @@ TEST_F( {5 * pow(0.5, 1.5), 12 * 1.75 * 1.75, 4.0}, 0.0, 21.41380852375568); } -TEST_F(AgradRev, - StanMath_integrate_1d_de_rev_TestDerivatives_var_left_endpoint_var_params) { +TEST_F( + AgradRev, + StanMath_integrate_1d_de_rev_TestDerivatives_var_left_endpoint_var_params) { // Zero crossing integral + test x_r + var at left endpoint using stan::math::var; test_derivatives( @@ -381,7 +382,8 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_indefinite) { 2.536571480364399, {}); } -TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_endpoint_precision) { +TEST_F(AgradRev, + StanMath_integrate_1d_de_rev_TestDerivatives_endpoint_precision) { // Various integrals of beta function using stan::math::var; test_derivatives(f11{}, 0.0, 1.0, {0.1, 0.1}, {}, {}, @@ -410,16 +412,17 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_gaussian) { {0.0, 0.0}); } -TEST_F(AgradRev, - StanMath_integrate_1d_de_rev_TestDerivativesSameVarAtEndpointAndInParams) { +TEST_F( + AgradRev, + StanMath_integrate_1d_de_rev_TestDerivativesSameVarAtEndpointAndInParams) { using stan::math::var; var a = 2.0; var b = 4.0; std::vector thetas = {a, b}; - var integral - = stan::math::integrate_1d_double_exponential(f13{}, a, b, thetas, {}, {}, msgs, 1e-8); + var integral = stan::math::integrate_1d_double_exponential( + f13{}, a, b, thetas, {}, {}, msgs, 1e-8); integral.grad(); EXPECT_LT(std::abs(18.0 - integral.val()), 1e-8); @@ -439,7 +442,8 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestBeta) { std::ostream *msgs) { return exp(stan::math::beta_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, 0.0, 1.0, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential(pdf, 0.0, 1.0, theta, {}, {}, msgs, + 1e-8); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{alpha, beta}; @@ -511,7 +515,8 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDoubleExponential) { }; // requires two subintervals to achieve numerical accuracy var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8) - + integrate_1d_double_exponential(pdf, b, -a, theta, {}, {}, msgs, 1e-8); + + integrate_1d_double_exponential(pdf, b, -a, theta, {}, {}, msgs, + 1e-8); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; From c6c89d5c449f97ecd0adabed17a7089a0afaa620 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Thu, 21 May 2026 14:50:43 -0400 Subject: [PATCH 05/13] Rename _impl to _tol, make normal signature variadic --- .../integrate_1d_double_exponential.hpp | 42 +++++++-------- .../functor/integrate_1d_gauss_kronrod.hpp | 49 ++++++++--------- .../integrate_1d_double_exponential.hpp | 37 ++++++------- .../functor/integrate_1d_gauss_kronrod.hpp | 46 +++++++--------- .../integrate_1d_double_exponential.hpp | 49 +++++++---------- .../functor/integrate_1d_gauss_kronrod.hpp | 54 +++++++------------ 6 files changed, 111 insertions(+), 166 deletions(-) diff --git a/stan/math/fwd/functor/integrate_1d_double_exponential.hpp b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp index 916da43a4f0..334aa8d59e9 100644 --- a/stan/math/fwd/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp @@ -10,6 +10,7 @@ namespace stan { namespace math { + /** * Return the integral of f from a to b using adaptive double-exponential * quadrature, with tangents computed via finite differences over the @@ -33,16 +34,16 @@ namespace math { */ template * = nullptr> -inline return_type_t integrate_1d_double_exponential_impl( +inline return_type_t integrate_1d_double_exponential_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_refinements, std::ostream *msgs, - const Args &... args) { + const Args &...args) { using FvarT = scalar_type_t>; auto a_val = value_of(a); auto b_val = value_of(b); auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_refinements, - a_val, b_val](const auto &... args_var) { + a_val, b_val](const auto &...args_var) { return integrate_1d_double_exponential_impl( f, a_val, b_val, relative_tolerance, absolute_tolerance, max_refinements, msgs, args_var...); @@ -54,7 +55,7 @@ inline return_type_t integrate_1d_double_exponential_impl( if constexpr (is_fvar::value) { ret.d_ += a.d_ * math::apply( - [&](auto &&... tuple_args) { + [&](auto &&...tuple_args) { return -f(a_val, 0.0, msgs, tuple_args...); }, val_args); @@ -62,7 +63,7 @@ inline return_type_t integrate_1d_double_exponential_impl( if constexpr (is_fvar::value) { ret.d_ += b.d_ * math::apply( - [&](auto &&... tuple_args) { + [&](auto &&...tuple_args) { return f(b_val, 0.0, msgs, tuple_args...); }, val_args); @@ -76,36 +77,31 @@ inline return_type_t integrate_1d_double_exponential_impl( * adaptive double-exponential quadrature. a and b can be finite or * infinite. * + * @tparam F Type of f * @tparam T_a type of first limit * @tparam T_b type of second limit - * @tparam T_theta type of parameters - * @tparam F Type of f + * @tparam Args types of parameter pack arguments * * @param f the functor to integrate * @param a lower limit of integration * @param b upper limit of integration - * @param theta additional parameters to be passed to f - * @param x_r additional data to be passed to f - * @param x_i additional integer data to be passed to f - * @param[in, out] msgs the print stream for warning messages * @param relative_tolerance relative tolerance passed to Boost quadrature * @param absolute_tolerance absolute-error floor on the convergence test * @param max_refinements maximum refinement level passed to the Boost * quadrature class constructor + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments to pass to f * @return numeric integral of function f */ -template * = nullptr> -inline return_type_t integrate_1d_double_exponential( - const F &f, const T_a &a, const T_b &b, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs, const double relative_tolerance, - const double absolute_tolerance = 0.0, - const int max_refinements - = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { - return integrate_1d_double_exponential_impl( - integrate_1d_adapter(f), a, b, relative_tolerance, absolute_tolerance, - max_refinements, msgs, theta, x_r, x_i); +template * = nullptr> +inline return_type_t integrate_1d_double_exponential_impl( + const F &f, const T_a &a, const T_b &b, double relative_tolerance, + double absolute_tolerance, int max_refinements, std::ostream *msgs, + const Args &...args) { + return integrate_1d_double_exponential_tol( + f, a, b, std::sqrt(EPSILON), 0.0, + INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS, msgs, args...); } } // namespace math diff --git a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp index 867ab33de9e..c6a5b49f533 100644 --- a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp @@ -10,6 +10,7 @@ namespace stan { namespace math { + /** * Return the integral of f from a to b using adaptive Gauss-Kronrod (G21,K21) * quadrature, with tangents computed via finite differences over the @@ -33,10 +34,10 @@ namespace math { */ template * = nullptr> -inline return_type_t integrate_1d_gauss_kronrod_impl( +inline return_type_t integrate_1d_gauss_kronrod_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_depth, std::ostream *msgs, - const Args &... args) { + const Args &...args) { using FvarT = scalar_type_t>; // Wrap integrate_1d_gauss_kronrod call in a functor where the input @@ -44,7 +45,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( auto a_val = value_of(a); auto b_val = value_of(b); auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_depth, - a_val, b_val](const auto &... args_var) { + a_val, b_val](const auto &...args_var) { return integrate_1d_gauss_kronrod_impl(f, a_val, b_val, relative_tolerance, absolute_tolerance, max_depth, msgs, args_var...); @@ -57,7 +58,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( if constexpr (is_fvar::value) { ret.d_ += a.d_ * math::apply( - [&](auto &&... tuple_args) { + [&](auto &&...tuple_args) { return -f(a_val, 0.0, msgs, tuple_args...); }, val_args); @@ -65,7 +66,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( if constexpr (is_fvar::value) { ret.d_ += b.d_ * math::apply( - [&](auto &&... tuple_args) { + [&](auto &&...tuple_args) { return f(b_val, 0.0, msgs, tuple_args...); }, val_args); @@ -75,39 +76,31 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( } /** - * Compute the integral of the single variable function f from a to b using - * adaptive Gauss-Kronrod (G21,K21) quadrature. a and b can be finite or - * infinite. + * Return the integral of f from a to b using adaptive Gauss-Kronrod (G21,K21) + * quadrature, with tangents computed via finite differences over the + * integrand parameters. * + * @tparam F Type of f * @tparam T_a type of first limit * @tparam T_b type of second limit - * @tparam T_theta type of parameters - * @tparam F Type of f + * @tparam Args types of parameter pack arguments * * @param f the functor to integrate * @param a lower limit of integration * @param b upper limit of integration - * @param theta additional parameters to be passed to f - * @param x_r additional data to be passed to f - * @param x_i additional integer data to be passed to f * @param[in, out] msgs the print stream for warning messages - * @param relative_tolerance relative tolerance passed to Boost quadrature - * @param absolute_tolerance absolute-error floor on the convergence test - * @param max_depth maximum recursive bisection depth passed to Boost - * quadrature + * @param args additional arguments to pass to f * @return numeric integral of function f */ -template * = nullptr> -inline return_type_t integrate_1d_gauss_kronrod( - const F &f, const T_a &a, const T_b &b, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs, const double relative_tolerance, - const double absolute_tolerance = 0.0, - const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { - return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, - relative_tolerance, absolute_tolerance, - max_depth, msgs, theta, x_r, x_i); +template * = nullptr> +inline return_type_t integrate_1d_gauss_kronrod( + const F &f, const T_a &a, const T_b &b, double relative_tolerance, + double absolute_tolerance, int max_depth, std::ostream *msgs, + const Args &...args) { + return integrate_1d_gauss_kronrod_tol(f, a, b, std::sqrt(EPSILON), 0.0, + INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH, + msgs, args...); } } // namespace math diff --git a/stan/math/prim/functor/integrate_1d_double_exponential.hpp b/stan/math/prim/functor/integrate_1d_double_exponential.hpp index db19ebc67a0..a8aa4b83e48 100644 --- a/stan/math/prim/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/prim/functor/integrate_1d_double_exponential.hpp @@ -11,10 +11,7 @@ #include #include #include -#include -#include #include -#include namespace stan { namespace math { @@ -216,7 +213,7 @@ inline double integrate_de(const F& f, double a, double b, */ template * = nullptr> -inline double integrate_1d_double_exponential_impl( +inline double integrate_1d_double_exponential_tol( const F& f, double a, double b, double relative_tolerance, double absolute_tolerance, int max_refinements, std::ostream* msgs, const Args&... args) { @@ -253,31 +250,27 @@ inline double integrate_1d_double_exponential_impl( * where L1 is the Boost estimate of the L1 norm of the integral. * * @tparam F type of function to integrate + * @tparam Args types of additional arguments forwarded to f (all arithmetic) * * @param f the function to be integrated * @param a lower limit of integration * @param b upper limit of integration - * @param theta additional parameters to be passed to f - * @param x_r additional data to be passed to f - * @param x_i additional integer data to be passed to f - * @param[in, out] msgs the print stream for warning messages - * @param relative_tolerance tolerance passed to Boost quadrature + * @param relative_tolerance relative tolerance passed to Boost quadrature * @param absolute_tolerance absolute-error floor on the convergence test - * @param max_refinements maximum refinement level passed to the Boost - * quadrature class constructor + * @param max_refinements maximum refinement level passed to the + * Boost quadrature class constructor + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments passed to f * @return numeric integral of function f */ -template -inline double integrate_1d_double_exponential( - const F& f, double a, double b, const std::vector& theta, - const std::vector& x_r, const std::vector& x_i, - std::ostream* msgs, const double relative_tolerance = std::sqrt(EPSILON), - const double absolute_tolerance = 0.0, - const int max_refinements - = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { - return integrate_1d_double_exponential_impl( - integrate_1d_adapter(f), a, b, relative_tolerance, absolute_tolerance, - max_refinements, msgs, theta, x_r, x_i); +template * = nullptr> +inline double integrate_1d_double_exponential(const F& f, double a, double b, + std::ostream* msgs, + const Args&... args) { + return integrate_1d_double_exponential_tol( + f, a, b, std::sqrt(EPSILON), 0.0, + INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS, msgs, args...); } } // namespace math diff --git a/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp index f25836f27c2..0f0013f5ee6 100644 --- a/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/prim/functor/integrate_1d_gauss_kronrod.hpp @@ -8,10 +8,7 @@ #include #include #include -#include -#include #include -#include namespace stan { namespace math { @@ -127,11 +124,11 @@ inline double integrate_gk(const F& f, double a, double b, */ template * = nullptr> -inline double integrate_1d_gauss_kronrod_impl(const F& f, double a, double b, - double relative_tolerance, - double absolute_tolerance, - int max_depth, std::ostream* msgs, - const Args&... args) { +inline double integrate_1d_gauss_kronrod_tol(const F& f, double a, double b, + double relative_tolerance, + double absolute_tolerance, + int max_depth, std::ostream* msgs, + const Args&... args) { static constexpr const char* function = "integrate_1d_gauss_kronrod"; check_less_or_equal(function, "lower limit", a, b); check_nonnegative(function, "max_depth", max_depth); @@ -154,9 +151,7 @@ inline double integrate_1d_gauss_kronrod_impl(const F& f, double a, double b, * infinite. * * The signature for f should be: - * double f(double x, double xc, const std::vector& theta, - * const std::vector& x_r, const std::vector& x_i, - * std::ostream* msgs) + * double f(double x, double xc, std::ostream* msgs, Args... args...) * * It should return the value of the function evaluated at x. Any errors * should be printed to the msgs stream. xc is unused (always NaN) here; see @@ -170,31 +165,26 @@ inline double integrate_1d_gauss_kronrod_impl(const F& f, double a, double b, * \f] * where \f$|I|\f$ is the Boost estimate of the L1 norm of the integral. * + * * @tparam F type of function to integrate + * @tparam Args types of additional arguments forwarded to f (all arithmetic) * * @param f the function to be integrated * @param a lower limit of integration * @param b upper limit of integration - * @param theta additional parameters to be passed to f - * @param x_r additional data to be passed to f - * @param x_i additional integer data to be passed to f * @param[in, out] msgs the print stream for warning messages - * @param relative_tolerance relative tolerance passed to Boost quadrature - * @param absolute_tolerance absolute-error floor on the convergence test - * @param max_depth maximum recursive bisection depth passed to Boost - * quadrature + * @param args additional arguments passed to f * @return numeric integral of function f */ -template -inline double integrate_1d_gauss_kronrod( - const F& f, double a, double b, const std::vector& theta, - const std::vector& x_r, const std::vector& x_i, - std::ostream* msgs, const double relative_tolerance = std::sqrt(EPSILON), - const double absolute_tolerance = 0.0, - const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { - return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, - relative_tolerance, absolute_tolerance, - max_depth, msgs, theta, x_r, x_i); + +template * = nullptr> +inline double integrate_1d_gauss_kronrod(const F& f, double a, double b, + std::ostream* msgs, + const Args&... args) { + return integrate_1d_gauss_kronrod_tol(f, a, b, std::sqrt(EPSILON), 0.0, + INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH, + msgs, args...); } } // namespace math diff --git a/stan/math/rev/functor/integrate_1d_double_exponential.hpp b/stan/math/rev/functor/integrate_1d_double_exponential.hpp index 326566b12f5..a9bcf334609 100644 --- a/stan/math/rev/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/rev/functor/integrate_1d_double_exponential.hpp @@ -11,10 +11,7 @@ #include #include #include -#include #include -#include -#include #include namespace stan { @@ -42,10 +39,10 @@ namespace math { */ template * = nullptr> -inline return_type_t integrate_1d_double_exponential_impl( +inline return_type_t integrate_1d_double_exponential_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_refinements, std::ostream *msgs, - const Args &... args) { + const Args &...args) { static constexpr const char *function = "integrate_1d_double_exponential"; check_less_or_equal(function, "lower limit", a, b); check_nonnegative(function, "max_refinements", max_refinements); @@ -66,7 +63,7 @@ inline return_type_t integrate_1d_double_exponential_impl( double integral = integrate_de( [&](const auto &x, const auto &xc) { return math::apply( - [&](auto &&... val_args) { return f(x, xc, msgs, val_args...); }, + [&](auto &&...val_args) { return f(x, xc, msgs, val_args...); }, args_val_tuple); }, a_val, b_val, relative_tolerance, absolute_tolerance, max_refinements); @@ -88,7 +85,7 @@ inline return_type_t integrate_1d_double_exponential_impl( if constexpr (is_var::value) { if (!is_inf(a)) { *partials_ptr = math::apply( - [&f, a_val, msgs](auto &&... val_args) { + [&f, a_val, msgs](auto &&...val_args) { return -f(a_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -99,7 +96,7 @@ inline return_type_t integrate_1d_double_exponential_impl( if constexpr (is_var::value) { if (!is_inf(b)) { *partials_ptr = math::apply( - [&f, b_val, msgs](auto &&... val_args) { + [&f, b_val, msgs](auto &&...val_args) { return f(b_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -112,9 +109,7 @@ inline return_type_t integrate_1d_double_exponential_impl( auto args_tuple_local_copy = std::make_tuple(deep_copy_vars(args)...); std::vector local_varis(num_vars_args); math::apply( - [&](const auto &... args) { - save_varis(local_varis.data(), args...); - }, + [&](const auto &...args) { save_varis(local_varis.data(), args...); }, args_tuple_local_copy); for (size_t n = 0; n < num_vars_args; ++n) { @@ -124,7 +119,7 @@ inline return_type_t integrate_1d_double_exponential_impl( nested_rev_autodiff gradient_nest; var fx = math::apply( - [&f, &x, &xc, msgs](auto &&... local_args) { + [&f, &x, &xc, msgs](auto &&...local_args) { return f(x, xc, msgs, local_args...); }, args_tuple_local_copy); @@ -171,36 +166,30 @@ inline return_type_t integrate_1d_double_exponential_impl( * Gradients of f that evaluate to NaN when the function evaluates to zero * are set to zero themselves. * + * @tparam F Type of f * @tparam T_a type of first limit * @tparam T_b type of second limit - * @tparam T_theta type of parameters - * @tparam F Type of f + * @tparam Args types of parameter pack arguments * * @param f the functor to integrate * @param a lower limit of integration * @param b upper limit of integration - * @param theta additional parameters to be passed to f - * @param x_r additional data to be passed to f - * @param x_i additional integer data to be passed to f - * @param[in, out] msgs the print stream for warning messages * @param relative_tolerance relative tolerance passed to Boost quadrature * @param absolute_tolerance absolute-error floor on the convergence test * @param max_refinements maximum refinement level passed to the Boost * quadrature class constructor + * @param[in, out] msgs the print stream for warning messages + * @param args additional arguments to pass to f * @return numeric integral of function f */ -template > -inline return_type_t integrate_1d_double_exponential( - const F &f, const T_a &a, const T_b &b, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs, const double relative_tolerance = std::sqrt(EPSILON), - const double absolute_tolerance = 0.0, - const int max_refinements - = INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS) { - return integrate_1d_double_exponential_impl( - integrate_1d_adapter(f), a, b, relative_tolerance, absolute_tolerance, - max_refinements, msgs, theta, x_r, x_i); +template * = nullptr> +inline return_type_t integrate_1d_double_exponential( + const F &f, const T_a &a, const T_b &b, std::ostream *msgs, + const Args &...args) { + return integrate_1d_double_exponential_tol( + f, a, b, std::sqrt(EPSILON), 0.0, + INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS, msgs, args...); } } // namespace math diff --git a/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp index 65f26225ba3..1221ab134d5 100644 --- a/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp @@ -11,10 +11,7 @@ #include #include #include -#include #include -#include -#include #include namespace stan { @@ -42,10 +39,10 @@ namespace math { */ template * = nullptr> -inline return_type_t integrate_1d_gauss_kronrod_impl( +inline return_type_t integrate_1d_gauss_kronrod_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_depth, std::ostream *msgs, - const Args &... args) { + const Args &...args) { static constexpr const char *function = "integrate_1d_gauss_kronrod"; check_less_or_equal(function, "lower limit", a, b); check_nonnegative(function, "max_depth", max_depth); @@ -66,7 +63,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( double integral = integrate_gk( [&](const auto &x, const auto &xc) { return math::apply( - [&](auto &&... val_args) { return f(x, xc, msgs, val_args...); }, + [&](auto &&...val_args) { return f(x, xc, msgs, val_args...); }, args_val_tuple); }, a_val, b_val, relative_tolerance, absolute_tolerance, max_depth); @@ -89,7 +86,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( if constexpr (is_var::value) { if (!is_inf(a)) { *partials_ptr = math::apply( - [&f, a_val, msgs](auto &&... val_args) { + [&f, a_val, msgs](auto &&...val_args) { return -f(a_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -100,7 +97,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( if constexpr (is_var::value) { if (!is_inf(b)) { *partials_ptr = math::apply( - [&f, b_val, msgs](auto &&... val_args) { + [&f, b_val, msgs](auto &&...val_args) { return f(b_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -117,9 +114,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( // Save the varis so it's easy to efficiently access the nth adjoint std::vector local_varis(num_vars_args); math::apply( - [&](const auto &... args) { - save_varis(local_varis.data(), args...); - }, + [&](const auto &...args) { save_varis(local_varis.data(), args...); }, args_tuple_local_copy); for (size_t n = 0; n < num_vars_args; ++n) { @@ -131,7 +126,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( nested_rev_autodiff gradient_nest; var fx = math::apply( - [&f, &x, &xc, msgs](auto &&... local_args) { + [&f, &x, &xc, msgs](auto &&...local_args) { return f(x, xc, msgs, local_args...); }, args_tuple_local_copy); @@ -173,9 +168,7 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( * infinite. * * f should be compatible with reverse mode autodiff and have the signature: - * var f(double x, double xc, const std::vector& theta, - * const std::vector& x_r, const std::vector &x_i, - * std::ostream* msgs) + * var f(double x, double xc, std::ostream* msgs, Args... args...); * * It should return the value of the function evaluated at x. Any errors * should be printed to the msgs stream. xc is unused (always NaN) here. @@ -191,35 +184,26 @@ inline return_type_t integrate_1d_gauss_kronrod_impl( * values (where the function should be zero anyway for the integral to * exist). * + * @tparam F Type of f * @tparam T_a type of first limit * @tparam T_b type of second limit - * @tparam T_theta type of parameters - * @tparam F Type of f + * @tparam Args types of parameter pack arguments * * @param f the functor to integrate * @param a lower limit of integration * @param b upper limit of integration - * @param theta additional parameters to be passed to f - * @param x_r additional data to be passed to f - * @param x_i additional integer data to be passed to f * @param[in, out] msgs the print stream for warning messages - * @param relative_tolerance relative tolerance passed to Boost quadrature - * @param absolute_tolerance absolute-error floor on the convergence test - * @param max_depth maximum recursive bisection depth passed to Boost - * quadrature + * @param args additional arguments to pass to f * @return numeric integral of function f */ -template > -inline return_type_t integrate_1d_gauss_kronrod( - const F &f, const T_a &a, const T_b &b, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs, const double relative_tolerance = std::sqrt(EPSILON), - const double absolute_tolerance = 0.0, - const int max_depth = INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH) { - return integrate_1d_gauss_kronrod_impl(integrate_1d_adapter(f), a, b, - relative_tolerance, absolute_tolerance, - max_depth, msgs, theta, x_r, x_i); +template * = nullptr> +inline return_type_t integrate_1d_gauss_kronrod( + const F &f, const T_a &a, const T_b &b, std::ostream *msgs, + const Args &...args) { + return integrate_1d_gauss_kronrod_tol(f, a, b, std::sqrt(EPSILON), 0.0, + INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH, + msgs, args...); } } // namespace math From b63170961509c552bcaf24d13b6b32605a1b3624 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Thu, 21 May 2026 14:51:58 -0400 Subject: [PATCH 06/13] [Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1 --- .../functor/integrate_1d_double_exponential.hpp | 10 +++++----- .../fwd/functor/integrate_1d_gauss_kronrod.hpp | 10 +++++----- .../functor/integrate_1d_double_exponential.hpp | 16 +++++++++------- .../rev/functor/integrate_1d_gauss_kronrod.hpp | 16 +++++++++------- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/stan/math/fwd/functor/integrate_1d_double_exponential.hpp b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp index 334aa8d59e9..8a7778a6915 100644 --- a/stan/math/fwd/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp @@ -37,13 +37,13 @@ template integrate_1d_double_exponential_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_refinements, std::ostream *msgs, - const Args &...args) { + const Args &... args) { using FvarT = scalar_type_t>; auto a_val = value_of(a); auto b_val = value_of(b); auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_refinements, - a_val, b_val](const auto &...args_var) { + a_val, b_val](const auto &... args_var) { return integrate_1d_double_exponential_impl( f, a_val, b_val, relative_tolerance, absolute_tolerance, max_refinements, msgs, args_var...); @@ -55,7 +55,7 @@ inline return_type_t integrate_1d_double_exponential_tol( if constexpr (is_fvar::value) { ret.d_ += a.d_ * math::apply( - [&](auto &&...tuple_args) { + [&](auto &&... tuple_args) { return -f(a_val, 0.0, msgs, tuple_args...); }, val_args); @@ -63,7 +63,7 @@ inline return_type_t integrate_1d_double_exponential_tol( if constexpr (is_fvar::value) { ret.d_ += b.d_ * math::apply( - [&](auto &&...tuple_args) { + [&](auto &&... tuple_args) { return f(b_val, 0.0, msgs, tuple_args...); }, val_args); @@ -98,7 +98,7 @@ template integrate_1d_double_exponential_impl( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_refinements, std::ostream *msgs, - const Args &...args) { + const Args &... args) { return integrate_1d_double_exponential_tol( f, a, b, std::sqrt(EPSILON), 0.0, INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS, msgs, args...); diff --git a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp index c6a5b49f533..1a92d91bde7 100644 --- a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp @@ -37,7 +37,7 @@ template integrate_1d_gauss_kronrod_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_depth, std::ostream *msgs, - const Args &...args) { + const Args &... args) { using FvarT = scalar_type_t>; // Wrap integrate_1d_gauss_kronrod call in a functor where the input @@ -45,7 +45,7 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( auto a_val = value_of(a); auto b_val = value_of(b); auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_depth, - a_val, b_val](const auto &...args_var) { + a_val, b_val](const auto &... args_var) { return integrate_1d_gauss_kronrod_impl(f, a_val, b_val, relative_tolerance, absolute_tolerance, max_depth, msgs, args_var...); @@ -58,7 +58,7 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( if constexpr (is_fvar::value) { ret.d_ += a.d_ * math::apply( - [&](auto &&...tuple_args) { + [&](auto &&... tuple_args) { return -f(a_val, 0.0, msgs, tuple_args...); }, val_args); @@ -66,7 +66,7 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( if constexpr (is_fvar::value) { ret.d_ += b.d_ * math::apply( - [&](auto &&...tuple_args) { + [&](auto &&... tuple_args) { return f(b_val, 0.0, msgs, tuple_args...); }, val_args); @@ -97,7 +97,7 @@ template integrate_1d_gauss_kronrod( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_depth, std::ostream *msgs, - const Args &...args) { + const Args &... args) { return integrate_1d_gauss_kronrod_tol(f, a, b, std::sqrt(EPSILON), 0.0, INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH, msgs, args...); diff --git a/stan/math/rev/functor/integrate_1d_double_exponential.hpp b/stan/math/rev/functor/integrate_1d_double_exponential.hpp index a9bcf334609..dad234e441e 100644 --- a/stan/math/rev/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/rev/functor/integrate_1d_double_exponential.hpp @@ -42,7 +42,7 @@ template integrate_1d_double_exponential_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_refinements, std::ostream *msgs, - const Args &...args) { + const Args &... args) { static constexpr const char *function = "integrate_1d_double_exponential"; check_less_or_equal(function, "lower limit", a, b); check_nonnegative(function, "max_refinements", max_refinements); @@ -63,7 +63,7 @@ inline return_type_t integrate_1d_double_exponential_tol( double integral = integrate_de( [&](const auto &x, const auto &xc) { return math::apply( - [&](auto &&...val_args) { return f(x, xc, msgs, val_args...); }, + [&](auto &&... val_args) { return f(x, xc, msgs, val_args...); }, args_val_tuple); }, a_val, b_val, relative_tolerance, absolute_tolerance, max_refinements); @@ -85,7 +85,7 @@ inline return_type_t integrate_1d_double_exponential_tol( if constexpr (is_var::value) { if (!is_inf(a)) { *partials_ptr = math::apply( - [&f, a_val, msgs](auto &&...val_args) { + [&f, a_val, msgs](auto &&... val_args) { return -f(a_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -96,7 +96,7 @@ inline return_type_t integrate_1d_double_exponential_tol( if constexpr (is_var::value) { if (!is_inf(b)) { *partials_ptr = math::apply( - [&f, b_val, msgs](auto &&...val_args) { + [&f, b_val, msgs](auto &&... val_args) { return f(b_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -109,7 +109,9 @@ inline return_type_t integrate_1d_double_exponential_tol( auto args_tuple_local_copy = std::make_tuple(deep_copy_vars(args)...); std::vector local_varis(num_vars_args); math::apply( - [&](const auto &...args) { save_varis(local_varis.data(), args...); }, + [&](const auto &... args) { + save_varis(local_varis.data(), args...); + }, args_tuple_local_copy); for (size_t n = 0; n < num_vars_args; ++n) { @@ -119,7 +121,7 @@ inline return_type_t integrate_1d_double_exponential_tol( nested_rev_autodiff gradient_nest; var fx = math::apply( - [&f, &x, &xc, msgs](auto &&...local_args) { + [&f, &x, &xc, msgs](auto &&... local_args) { return f(x, xc, msgs, local_args...); }, args_tuple_local_copy); @@ -186,7 +188,7 @@ template * = nullptr> inline return_type_t integrate_1d_double_exponential( const F &f, const T_a &a, const T_b &b, std::ostream *msgs, - const Args &...args) { + const Args &... args) { return integrate_1d_double_exponential_tol( f, a, b, std::sqrt(EPSILON), 0.0, INTEGRATE_1D_DOUBLE_EXPONENTIAL_MAX_REFINEMENTS, msgs, args...); diff --git a/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp index 1221ab134d5..699789a2db3 100644 --- a/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/rev/functor/integrate_1d_gauss_kronrod.hpp @@ -42,7 +42,7 @@ template integrate_1d_gauss_kronrod_tol( const F &f, const T_a &a, const T_b &b, double relative_tolerance, double absolute_tolerance, int max_depth, std::ostream *msgs, - const Args &...args) { + const Args &... args) { static constexpr const char *function = "integrate_1d_gauss_kronrod"; check_less_or_equal(function, "lower limit", a, b); check_nonnegative(function, "max_depth", max_depth); @@ -63,7 +63,7 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( double integral = integrate_gk( [&](const auto &x, const auto &xc) { return math::apply( - [&](auto &&...val_args) { return f(x, xc, msgs, val_args...); }, + [&](auto &&... val_args) { return f(x, xc, msgs, val_args...); }, args_val_tuple); }, a_val, b_val, relative_tolerance, absolute_tolerance, max_depth); @@ -86,7 +86,7 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( if constexpr (is_var::value) { if (!is_inf(a)) { *partials_ptr = math::apply( - [&f, a_val, msgs](auto &&...val_args) { + [&f, a_val, msgs](auto &&... val_args) { return -f(a_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -97,7 +97,7 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( if constexpr (is_var::value) { if (!is_inf(b)) { *partials_ptr = math::apply( - [&f, b_val, msgs](auto &&...val_args) { + [&f, b_val, msgs](auto &&... val_args) { return f(b_val, 0.0, msgs, val_args...); }, args_val_tuple); @@ -114,7 +114,9 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( // Save the varis so it's easy to efficiently access the nth adjoint std::vector local_varis(num_vars_args); math::apply( - [&](const auto &...args) { save_varis(local_varis.data(), args...); }, + [&](const auto &... args) { + save_varis(local_varis.data(), args...); + }, args_tuple_local_copy); for (size_t n = 0; n < num_vars_args; ++n) { @@ -126,7 +128,7 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( nested_rev_autodiff gradient_nest; var fx = math::apply( - [&f, &x, &xc, msgs](auto &&...local_args) { + [&f, &x, &xc, msgs](auto &&... local_args) { return f(x, xc, msgs, local_args...); }, args_tuple_local_copy); @@ -200,7 +202,7 @@ template * = nullptr> inline return_type_t integrate_1d_gauss_kronrod( const F &f, const T_a &a, const T_b &b, std::ostream *msgs, - const Args &...args) { + const Args &... args) { return integrate_1d_gauss_kronrod_tol(f, a, b, std::sqrt(EPSILON), 0.0, INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH, msgs, args...); From ff86bc7d522c04493726bda741aeb24a83f97a9e Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Thu, 21 May 2026 15:00:30 -0400 Subject: [PATCH 07/13] Remove accidental arguments in fwd --- stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp index 1a92d91bde7..6b1680afd47 100644 --- a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp @@ -95,9 +95,8 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( template * = nullptr> inline return_type_t integrate_1d_gauss_kronrod( - const F &f, const T_a &a, const T_b &b, double relative_tolerance, - double absolute_tolerance, int max_depth, std::ostream *msgs, - const Args &... args) { + const F &f, const T_a &a, const T_b &b, std::ostream *msgs, + const Args &...args) { return integrate_1d_gauss_kronrod_tol(f, a, b, std::sqrt(EPSILON), 0.0, INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH, msgs, args...); From 592458faf493bd7ce79fea5a1afc6e99b3c7ab13 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Thu, 21 May 2026 15:01:50 -0400 Subject: [PATCH 08/13] [Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1 --- stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp index 6b1680afd47..56d78ac6a6f 100644 --- a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp @@ -96,7 +96,7 @@ template * = nullptr> inline return_type_t integrate_1d_gauss_kronrod( const F &f, const T_a &a, const T_b &b, std::ostream *msgs, - const Args &...args) { + const Args &... args) { return integrate_1d_gauss_kronrod_tol(f, a, b, std::sqrt(EPSILON), 0.0, INTEGRATE_1D_GAUSS_KRONROD_MAX_DEPTH, msgs, args...); From 540aa6bf392754bbdf60e276ceb718c201f346bc Mon Sep 17 00:00:00 2001 From: Aki Vehtari Date: Thu, 21 May 2026 23:15:52 +0300 Subject: [PATCH 09/13] fix two cases of _impl to _tol --- stan/math/fwd/functor/integrate_1d_double_exponential.hpp | 2 +- stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stan/math/fwd/functor/integrate_1d_double_exponential.hpp b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp index 8a7778a6915..69b73a90279 100644 --- a/stan/math/fwd/functor/integrate_1d_double_exponential.hpp +++ b/stan/math/fwd/functor/integrate_1d_double_exponential.hpp @@ -44,7 +44,7 @@ inline return_type_t integrate_1d_double_exponential_tol( auto b_val = value_of(b); auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_refinements, a_val, b_val](const auto &... args_var) { - return integrate_1d_double_exponential_impl( + return integrate_1d_double_exponential_tol( f, a_val, b_val, relative_tolerance, absolute_tolerance, max_refinements, msgs, args_var...); }; diff --git a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp index 56d78ac6a6f..9598c8877af 100644 --- a/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp +++ b/stan/math/fwd/functor/integrate_1d_gauss_kronrod.hpp @@ -46,9 +46,9 @@ inline return_type_t integrate_1d_gauss_kronrod_tol( auto b_val = value_of(b); auto func = [f, msgs, relative_tolerance, absolute_tolerance, max_depth, a_val, b_val](const auto &... args_var) { - return integrate_1d_gauss_kronrod_impl(f, a_val, b_val, relative_tolerance, - absolute_tolerance, max_depth, msgs, - args_var...); + return integrate_1d_gauss_kronrod_tol(f, a_val, b_val, relative_tolerance, + absolute_tolerance, max_depth, msgs, + args_var...); }; FvarT ret = finite_diff(func, args...); From 2ba4742eb364969c239e6428c0f0f421ae0a1ef8 Mon Sep 17 00:00:00 2001 From: Aki Vehtari Date: Thu, 21 May 2026 23:17:27 +0300 Subject: [PATCH 10/13] update integrate_1d_ tests to use variadic --- .../integrate_1d_double_exponential_test.cpp | 2 +- .../integrate_1d_gauss_kronrod_test.cpp | 2 +- .../integrate_1d_double_exponential_test.cpp | 382 ++++++++++++------ .../integrate_1d_gauss_kronrod_test.cpp | 360 ++++++++++++----- .../integrate_1d_double_exponential_test.cpp | 288 +++++++------ .../integrate_1d_gauss_kronrod_test.cpp | 178 ++++---- 6 files changed, 786 insertions(+), 426 deletions(-) diff --git a/test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp index f520e36f6f9..3b87edb25fb 100644 --- a/test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/mix/functor/integrate_1d_double_exponential_test.cpp @@ -10,7 +10,7 @@ TEST(mixFunctor, integrate1DDoubleExponential) { const double absolute_tolerance = 0.0; const int max_refinements = 15; std::ostringstream* msgs = nullptr; - return stan::math::integrate_1d_double_exponential_impl( + return stan::math::integrate_1d_double_exponential_tol( func, lb, ub, relative_tolerance, absolute_tolerance, max_refinements, msgs, x_input); }; diff --git a/test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp index dbb9d3f777c..fa162ecd962 100644 --- a/test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/mix/functor/integrate_1d_gauss_kronrod_test.cpp @@ -10,7 +10,7 @@ TEST(mixFunctor, integrate1DGaussKronrod) { const double absolute_tolerance = 0.0; const int max_depth = 15; std::ostringstream* msgs = nullptr; - return stan::math::integrate_1d_gauss_kronrod_impl( + return stan::math::integrate_1d_gauss_kronrod_tol( func, lb, ub, relative_tolerance, absolute_tolerance, max_depth, msgs, x_input); }; diff --git a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp index aecb5d51302..dbbf16129fc 100644 --- a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp @@ -13,10 +13,10 @@ std::ostringstream *msgs = nullptr; struct f1 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; @@ -24,10 +24,10 @@ struct f1 { struct f2 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { if (x <= 0.5) { return sqrt(x) / sqrt(1 - x * x); } else { @@ -39,10 +39,10 @@ struct f2 { struct f3 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(-x); } }; @@ -50,10 +50,10 @@ struct f3 { struct f4 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + theta[0]; } }; @@ -61,10 +61,10 @@ struct f4 { struct f5 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + pow(theta[0], 2) + pow(theta[1], 3); } }; @@ -72,10 +72,10 @@ struct f5 { struct f6 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + pow(x_i[0], 2) + pow(theta[0], 4) + 3 * theta[1]; } }; @@ -83,10 +83,10 @@ struct f6 { struct f7 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + pow(x_r[0], 2) + pow(x_r[1], 5) + 3 * x_r[2]; } }; @@ -94,10 +94,10 @@ struct f7 { struct f8 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(-pow(x - theta[0], x_i[0]) / pow(x_r[0], x_i[0])); } }; @@ -105,10 +105,10 @@ struct f8 { struct f9 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return 1.0 / (1.0 + pow(x, x_i[0]) / theta[0]); } }; @@ -116,10 +116,10 @@ struct f9 { struct f10 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow((x > 0.5) ? xc : (1 - x), theta[1] - 1.0); } @@ -128,10 +128,10 @@ struct f10 { struct f11 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return (std::isnan(xc)) ? xc : 0.0; } }; @@ -139,10 +139,10 @@ struct f11 { struct f12 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * out; @@ -153,10 +153,10 @@ struct f12 { struct f13 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * stan::math::square(out); @@ -167,10 +167,10 @@ struct f13 { struct f14 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) * stan::math::inv_sqrt(x > 0.5 ? xc : 1 - x); } }; @@ -178,10 +178,10 @@ struct f14 { struct f15 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { T1 x2 = x * x; T1 numer = x2 * log(x); T1 denom = x < 0.5 ? (x + 1) * (x - 1) : (x + 1) * -xc; @@ -193,20 +193,20 @@ struct f15 { struct f16 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return x * sin(x) / (1 + stan::math::square(cos(x))); } }; struct f17 { inline double operator()(const double &x, const double &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { double mu = theta[0]; double sigma = theta[1]; return 1.0 / (sqrt(2.0 * stan::math::pi()) * sigma) @@ -256,11 +256,10 @@ inline double lbaX_cdf(double X, double t, double A, double v, double s, return cdf; } -inline double rank_density(double x, double xc, +inline double rank_density(double x, double xc, std::ostream *pstream__, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *pstream__) { + const std::vector &x_i) { double t = theta[0]; double A = theta[1]; double v1 = theta[2]; @@ -272,10 +271,11 @@ inline double rank_density(double x, double xc, } struct rank_density_functor__ { - double operator()(double x, double xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *pstream__) const { - return rank_density(x, xc, theta, x_r, x_i, pstream__); + double operator()(double x, double xc, std::ostream *pstream__, + const std::vector &theta, + const std::vector &x_r, + const std::vector &x_i) const { + return rank_density(x, xc, pstream__, theta, x_r, x_i); } }; @@ -285,8 +285,9 @@ inline double order(double down, double up, const std::vector &theta, double v; - v = stan::math::integrate_1d_double_exponential( - rank_density_functor__(), down, up, theta, x_r, x_i, pstream__, 1e-8); + v = stan::math::integrate_1d_double_exponential_tol( + rank_density_functor__(), down, up, 1e-8, 0.0, 15, pstream__, theta, x_r, + x_i); return v; } } // namespace integrate_1d_de_test @@ -322,23 +323,40 @@ inline void test_integration(const F &f, double a, double b, const std::vector &x_r, const std::vector &x_i, double val) { using stan::math::integrate_1d_double_exponential; + using stan::math::integrate_1d_double_exponential_tol; std::vector tolerances = {1e-4, 1e-6, 1e-8}; for (auto tolerance : tolerances) { - EXPECT_LE(std::abs(integrate_1d_double_exponential( - f, a, b, thetas, x_r, x_i, - integrate_1d_de_test::msgs, tolerance) + EXPECT_LE(std::abs(integrate_1d_double_exponential_tol(f, + a, + b, + tolerance, + 0.0, + 15, + integrate_1d_de_test::msgs, + thetas, + x_r, + x_i) - val), tolerance); // Flip the domain of integration and check that the integral is working auto flipped = - [&](const double &x, const double &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) { return f(-x, -xc, theta, x_r, x_i, msgs); }; - EXPECT_LE(std::abs(integrate_1d_double_exponential( - flipped, -b, -a, thetas, x_r, x_i, - integrate_1d_de_test::msgs, tolerance) + [&](const double &x, const double &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) { + return f(-x, -xc, msgs, theta, x_r, x_i); + }; + EXPECT_LE(std::abs(integrate_1d_double_exponential_tol(flipped, + -b, + -a, + tolerance, + 0.0, + 15, + integrate_1d_de_test::msgs, + thetas, + x_r, + x_i) - val), tolerance); } @@ -346,76 +364,161 @@ inline void test_integration(const F &f, double a, double b, TEST(StanMath_integrate_1d_de_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit - EXPECT_THROW(stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, 1.0, 0.0, std::vector(), - {}, {}, integrate_1d_de_test::msgs, 1e-6), + EXPECT_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + 1.0, + 0.0, + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); // NaN limits not okay EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, 0.0, - std::numeric_limits::quiet_NaN(), std::vector(), {}, - {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + 0.0, + std::numeric_limits::quiet_NaN(), + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, std::numeric_limits::quiet_NaN(), - 0.0, std::vector(), {}, {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + std::numeric_limits::quiet_NaN(), + 0.0, + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, std::numeric_limits::quiet_NaN(), - std::numeric_limits::quiet_NaN(), std::vector(), {}, - {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); // Two of the same inf limits not okay EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, -std::numeric_limits::infinity(), - -std::numeric_limits::infinity(), std::vector(), {}, - {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, std::numeric_limits::infinity(), - std::numeric_limits::infinity(), std::vector(), {}, - {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); // xc should be nan if there are infinite limits EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f11{}, 0.0, - std::numeric_limits::infinity(), std::vector(), {}, - {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, + 0.0, + std::numeric_limits::infinity(), + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::runtime_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f11{}, std::numeric_limits::infinity(), - 0.0, std::vector(), {}, {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, + std::numeric_limits::infinity(), + 0.0, + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f11{}, std::numeric_limits::infinity(), - std::numeric_limits::infinity(), std::vector(), {}, - {}, integrate_1d_de_test::msgs, 1e-6), + stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, + std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); // But not otherwise - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f11{}, 0.0, 1.0, std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, + 0.0, + 1.0, + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{})); } TEST(StanMath_integrate_1d_de_prim, test_integer_arguments) { - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, 0, 1, std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, 0.0, 1, std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential( - integrate_1d_de_test::f2{}, 0, 1.0, std::vector(), {}, {}, - integrate_1d_de_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + 0, + 1, + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + 0.0, + 1, + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, + 0, + 1.0, + 1e-6, + 0.0, + 15, + integrate_1d_de_test::msgs, + std::vector(), + std::vector{}, + std::vector{})); } TEST(StanMath_integrate_1d_de_prim, test1) { @@ -434,12 +537,12 @@ TEST(StanMath_integrate_1d_de_prim, test1) { std::numeric_limits::infinity(), {}, {}, {}, 7.38905609893065); // Easy integrals - test_integration(integrate_1d_de_test::f4{}, 0.2, 0.7, {0.5}, {}, {}, + test_integration(integrate_1d_de_test::f4{}, 0.2, 0.7, {0.5}, std::vector{}, std::vector{}, 1.0423499493102901); - test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, {0.4, 0.4}, {}, {}, + test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, {0.4, 0.4}, std::vector{}, std::vector{}, 1.396621954392482); - test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, {0.5}, {}, {}, 0.0); - test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, {}, {}, + test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, {0.5}, std::vector{}, std::vector{}, 0.0); + test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, std::vector{}, std::vector{}, 0.0); // Test x_i test_integration(integrate_1d_de_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, @@ -458,13 +561,13 @@ TEST(StanMath_integrate_1d_de_prim, test1) { std::numeric_limits::infinity(), {1.3}, {}, {4}, 2.372032924895055); // Various integrals of beta function - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.1}, {}, {}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.1}, std::vector{}, std::vector{}, 19.71463948905016); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.5}, {}, {}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.5}, std::vector{}, std::vector{}, 11.32308697521577); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.5, 0.1}, {}, {}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.5, 0.1}, std::vector{}, std::vector{}, 11.32308697521577); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {5.0, 3.0}, {}, {}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, 0.00952380952380952); // Integrals from @@ -509,13 +612,28 @@ TEST(StanMath_integrate_1d_de_prim, TestTolerance) { // the value on a well-converged integrand. TEST(StanMath_integrate_1d_de_prim, abs_tol_argument_smoke) { using stan::math::integrate_1d_double_exponential; + using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - double Q_strict = integrate_1d_double_exponential( - integrate_1d_de_test::f4{}, 0.2, 0.7, std::vector{0.5}, {}, {}, - msgs, 1e-8, 0.0); - double Q_lenient = integrate_1d_double_exponential( - integrate_1d_de_test::f4{}, 0.2, 0.7, std::vector{0.5}, {}, {}, - msgs, 1e-8, 1e-12); + double Q_strict = integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, + 0.2, + 0.7, + 1e-8, + 0.0, + 15, + msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}); + double Q_lenient = integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, + 0.2, + 0.7, + 1e-8, + 1e-12, + 15, + msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}); EXPECT_NEAR(Q_strict, 1.0423499493102901, 1e-8); EXPECT_NEAR(Q_lenient, Q_strict, 1e-12); } @@ -524,29 +642,53 @@ TEST(StanMath_integrate_1d_de_prim, abs_tol_argument_smoke) { // result. Argument order is (rel_tol, abs_tol, max_refinements). TEST(StanMath_integrate_1d_de_prim, max_refinements_argument) { using stan::math::integrate_1d_double_exponential; + using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - double Q = integrate_1d_double_exponential(integrate_1d_de_test::f4{}, 0.2, - 0.7, std::vector{0.5}, {}, - {}, msgs, 1e-8, 0.0, 20); + double Q = integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, + 0.2, + 0.7, + 1e-8, + 0.0, + 20, + msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}); EXPECT_NEAR(Q, 1.0423499493102901, 1e-8); } // Negative max_refinements not okay. TEST(StanMath_integrate_1d_de_prim, negative_max_refinements_throws) { using stan::math::integrate_1d_double_exponential; + using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - EXPECT_THROW(integrate_1d_double_exponential(integrate_1d_de_test::f4{}, 0.2, - 0.7, std::vector{0.5}, - {}, {}, msgs, 1e-6, 0.0, -1), + EXPECT_THROW(integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, + 0.2, + 0.7, + 1e-6, + 0.0, + -1, + msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); } // Negative absolute_tolerance not okay. TEST(StanMath_integrate_1d_de_prim, negative_abs_tol_throws) { using stan::math::integrate_1d_double_exponential; + using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - EXPECT_THROW(integrate_1d_double_exponential(integrate_1d_de_test::f4{}, 0.2, - 0.7, std::vector{0.5}, - {}, {}, msgs, 1e-6, -1e-3), + EXPECT_THROW(integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, + 0.2, + 0.7, + 1e-6, + -1e-3, + 15, + msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); } diff --git a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp index d15b594e581..f3a0d151883 100644 --- a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp @@ -35,10 +35,10 @@ std::ostringstream *msgs = nullptr; struct f1 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; @@ -47,10 +47,10 @@ struct f1 { struct f2 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { if (x <= 0.5) { return sqrt(x) / sqrt(1 - x * x); } else { @@ -62,10 +62,10 @@ struct f2 { struct f3 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(-x); } }; @@ -73,10 +73,10 @@ struct f3 { struct f4 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + theta[0]; } }; @@ -84,10 +84,10 @@ struct f4 { struct f5 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + pow(theta[0], 2) + pow(theta[1], 3); } }; @@ -95,10 +95,10 @@ struct f5 { struct f6 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + pow(x_i[0], 2) + pow(theta[0], 4) + 3 * theta[1]; } }; @@ -106,10 +106,10 @@ struct f6 { struct f7 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) + pow(x_r[0], 2) + pow(x_r[1], 5) + 3 * x_r[2]; } }; @@ -117,10 +117,10 @@ struct f7 { struct f8 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(-pow(x - theta[0], x_i[0]) / pow(x_r[0], x_i[0])); } }; @@ -128,10 +128,10 @@ struct f8 { struct f9 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return 1.0 / (1.0 + pow(x, x_i[0]) / theta[0]); } }; @@ -140,10 +140,10 @@ struct f9 { struct f10 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow(1 - x, theta[1] - 1.0); } }; @@ -151,10 +151,10 @@ struct f10 { struct f12 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * out; @@ -165,10 +165,10 @@ struct f12 { struct f13 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * stan::math::square(out); @@ -180,10 +180,10 @@ struct f13 { struct f14 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return exp(x) * stan::math::inv_sqrt(1 - x); } }; @@ -191,20 +191,20 @@ struct f14 { struct f16 { template inline stan::return_type_t operator()(const T1 &x, const T1 &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { return x * sin(x) / (1 + stan::math::square(cos(x))); } }; struct f17 { inline double operator()(const double &x, const double &xc, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, - const std::vector &x_i, - std::ostream *msgs) const { + const std::vector &x_i) const { double mu = theta[0]; double sigma = theta[1]; return 1.0 / (sqrt(2.0 * stan::math::pi()) * sigma) @@ -225,23 +225,40 @@ inline void test_integration(const F &f, double a, double b, const std::vector &x_r, const std::vector &x_i, double val) { using stan::math::integrate_1d_gauss_kronrod; + using stan::math::integrate_1d_gauss_kronrod_tol; std::vector tolerances = {1e-4, 1e-6, 1e-8}; for (auto tolerance : tolerances) { - EXPECT_LE(std::abs(integrate_1d_gauss_kronrod(f, a, b, thetas, x_r, x_i, - integrate_1d_gk_test::msgs, - tolerance) + EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol(f, + a, + b, + tolerance, + 0.0, + 15, + integrate_1d_gk_test::msgs, + thetas, + x_r, + x_i) - val), tolerance); // Flip the domain of integration and check that the integral matches auto flipped = - [&](const double &x, const double &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) { return f(-x, -xc, theta, x_r, x_i, msgs); }; - EXPECT_LE(std::abs(integrate_1d_gauss_kronrod( - flipped, -b, -a, thetas, x_r, x_i, - integrate_1d_gk_test::msgs, tolerance) + [&](const double &x, const double &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) { + return f(-x, -xc, msgs, theta, x_r, x_i); + }; + EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol(flipped, + -b, + -a, + tolerance, + 0.0, + 15, + integrate_1d_gk_test::msgs, + thetas, + x_r, + x_i) - val), tolerance); } @@ -250,68 +267,140 @@ inline void test_integration(const F &f, double a, double b, TEST(StanMath_integrate_1d_gk_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 1.0, 0.0, std::vector{0.5}, {}, - {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 1.0, + 0.0, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); // NaN limits not okay EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.0, - std::numeric_limits::quiet_NaN(), std::vector{0.5}, - {}, {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0.0, + std::numeric_limits::quiet_NaN(), + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, std::numeric_limits::quiet_NaN(), - 0.0, std::vector{0.5}, {}, {}, integrate_1d_gk_test::msgs, - 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + std::numeric_limits::quiet_NaN(), + 0.0, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, std::numeric_limits::quiet_NaN(), - std::numeric_limits::quiet_NaN(), std::vector{0.5}, - {}, {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); // Two of the same inf limits not okay EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, -std::numeric_limits::infinity(), - -std::numeric_limits::infinity(), std::vector{0.5}, - {}, {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, std::numeric_limits::infinity(), - std::numeric_limits::infinity(), std::vector{0.5}, {}, - {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); // Negative max_depth not okay EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.0, 1.0, std::vector{0.5}, {}, - {}, integrate_1d_gk_test::msgs, 1e-6, 0.0, -1), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0.0, + 1.0, + 1e-6, + 0.0, + -1, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); // Negative absolute_tolerance not okay EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.0, 1.0, std::vector{0.5}, {}, - {}, integrate_1d_gk_test::msgs, 1e-6, -1e-3), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0.0, + 1.0, + 1e-6, + -1e-3, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}), std::domain_error); } TEST(StanMath_integrate_1d_gk_prim, test_integer_arguments) { // Use a smooth integrand for the integer-bounds smoke test; f4 is exp(x)+c // and integrates cleanly under Gauss-Kronrod. - EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0, 1, std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6)); - EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.0, 1, std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6)); - EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0, 1.0, std::vector{0.5}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6)); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0, + 1, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0.0, + 1, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0, + 1.0, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{})); } TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { @@ -320,13 +409,13 @@ TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { std::numeric_limits::infinity(), {}, {}, {}, 7.38905609893065); // Easy integrals - test_integration(integrate_1d_gk_test::f4{}, 0.2, 0.7, {0.5}, {}, {}, + test_integration(integrate_1d_gk_test::f4{}, 0.2, 0.7, {0.5}, std::vector{}, std::vector{}, 1.0423499493102901); - test_integration(integrate_1d_gk_test::f5{}, -0.2, 0.7, {0.4, 0.4}, {}, {}, + test_integration(integrate_1d_gk_test::f5{}, -0.2, 0.7, {0.4, 0.4}, std::vector{}, std::vector{}, 1.396621954392482); // Zero-length intervals - test_integration(integrate_1d_gk_test::f4{}, 0.0, 0.0, {0.5}, {}, {}, 0.0); - test_integration(integrate_1d_gk_test::f5{}, 1.0, 1.0, {0.4, 0.4}, {}, {}, + test_integration(integrate_1d_gk_test::f4{}, 0.0, 0.0, {0.5}, std::vector{}, std::vector{}, 0.0); + test_integration(integrate_1d_gk_test::f5{}, 1.0, 1.0, {0.4, 0.4}, std::vector{}, std::vector{}, 0.0); // Test x_i test_integration(integrate_1d_gk_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, @@ -364,31 +453,57 @@ TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { TEST(StanMath_integrate_1d_gk_prim, endpoint_singularity_throws) { // 1/sqrt(x) at x = 0 (f1) EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f1{}, 0.0, - std::numeric_limits::infinity(), std::vector(), {}, - {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f1{}, + 0.0, + std::numeric_limits::infinity(), + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); // 1/sqrt(1-x*x) at x = 1 (f2) - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f2{}, 0.0, 1.0, std::vector(), - {}, {}, integrate_1d_gk_test::msgs, 1e-6), + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f2{}, + 0.0, + 1.0, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector(), + std::vector{}, + std::vector{}), std::domain_error); // beta integrand with small shape parameters (f10, a=b=0.1) EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f10{}, 0.0, 1.0, std::vector{0.1, 0.1}, - {}, {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f10{}, + 0.0, + 1.0, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.1, 0.1}, + std::vector{}, + std::vector{}), std::domain_error); } TEST(StanMath_integrate_1d_gk_prim, max_depth_argument) { // Smoke test: explicit max_depth is accepted and produces a sensible result. // Argument order is (rel_tol, abs_tol, max_depth). - double Q = stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.2, 0.7, std::vector{0.5}, - std::vector{}, std::vector{}, integrate_1d_gk_test::msgs, - 1e-8, 0.0, 20); + double Q = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0.2, + 0.7, + 1e-8, + 0.0, + 20, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}); EXPECT_NEAR(Q, 1.0423499493102901, 1e-8); } @@ -407,9 +522,16 @@ TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { // Sanity: with abs_tol = 0 (default) the call throws (this is the // same case as endpoint_singularity_throws.f10 above). EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f10{}, 0.0, 1.0, std::vector{0.1, 0.1}, - {}, {}, integrate_1d_gk_test::msgs, 1e-6), + stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f10{}, + 0.0, + 1.0, + 1e-6, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.1, 0.1}, + std::vector{}, + std::vector{}), std::domain_error); // With a very generous abs_tol the convergence threshold is @@ -420,10 +542,16 @@ TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { // safely above it. The true value of B(0.1, 0.1) is ~19.7, so even // an imprecise estimate should be in the right ballpark. double Q = 0.0; - EXPECT_NO_THROW(Q = stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f10{}, 0.0, 1.0, - std::vector{0.1, 0.1}, {}, {}, - integrate_1d_gk_test::msgs, 1e-6, 1e6)); + EXPECT_NO_THROW(Q = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f10{}, + 0.0, + 1.0, + 1e-6, + 1e6, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.1, 0.1}, + std::vector{}, + std::vector{})); EXPECT_GT(Q, 1.0); EXPECT_LT(Q, 1000.0); } @@ -431,14 +559,26 @@ TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { TEST(StanMath_integrate_1d_gk_prim, abs_tol_argument_smoke) { // Smoke test: explicit abs_tol on a well-converged integrand does // not change the result. Argument order is (rel_tol, abs_tol). - double Q0 = stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.2, 0.7, std::vector{0.5}, - std::vector{}, std::vector{}, integrate_1d_gk_test::msgs, - 1e-8, 0.0); - double Q1 = stan::math::integrate_1d_gauss_kronrod( - integrate_1d_gk_test::f4{}, 0.2, 0.7, std::vector{0.5}, - std::vector{}, std::vector{}, integrate_1d_gk_test::msgs, - 1e-8, 1e-12); + double Q0 = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0.2, + 0.7, + 1e-8, + 0.0, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}); + double Q1 = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, + 0.2, + 0.7, + 1e-8, + 1e-12, + 15, + integrate_1d_gk_test::msgs, + std::vector{0.5}, + std::vector{}, + std::vector{}); EXPECT_NEAR(Q0, 1.0423499493102901, 1e-8); EXPECT_NEAR(Q1, Q0, 1e-12); } diff --git a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp index 83c3942547f..d0bf6135113 100644 --- a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp @@ -15,9 +15,9 @@ std::ostringstream *msgs = nullptr; struct f1 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(x) + theta[0]; } }; @@ -25,9 +25,9 @@ struct f1 { struct f2 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(theta[0] * cos(2 * 3.141593 * x)) + theta[0]; } }; @@ -35,9 +35,9 @@ struct f2 { struct f3 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(x) + pow(theta[0], x_r[0]) + 2 * pow(theta[1], x_r[1]) + 2 * theta[2]; } @@ -46,9 +46,9 @@ struct f3 { struct f4 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; @@ -56,9 +56,9 @@ struct f4 { struct f5 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(-theta[0] * x) / sqrt(theta[1] * x); } }; @@ -66,9 +66,9 @@ struct f5 { struct f6 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return sqrt(x / (1 - theta[0] * x * x)); } }; @@ -76,9 +76,9 @@ struct f6 { struct f7 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(-theta[0] * x); } }; @@ -86,9 +86,9 @@ struct f7 { struct f8 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(theta[0] * x); } }; @@ -96,9 +96,9 @@ struct f8 { struct f10 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return 1 / (1 + pow(x, x_i[0]) / x_r[0]); } }; @@ -106,9 +106,9 @@ struct f10 { struct f11 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow((x > 0.5) ? xc : (1 - x), theta[1] - 1.0); } @@ -117,9 +117,9 @@ struct f11 { struct f12 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { T3 mu = theta[0]; T3 sigma = theta[1]; return exp(-0.5 * stan::math::square((x - mu) / sigma)) @@ -130,9 +130,9 @@ struct f12 { struct f13 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return x + theta[0] + theta[1]; } }; @@ -219,8 +219,16 @@ inline void test_derivatives(const F &f, double a, double b, for (size_t i = 0; i < thetas.size(); ++i) thetas_[i] = thetas[i]; - var integral = stan::math::integrate_1d_double_exponential( - f, a_, b_, thetas_, x_r, x_i, msgs, tolerance); + var integral = stan::math::integrate_1d_double_exponential_tol(f, + a_, + b_, + tolerance, + 0.0, + 15, + msgs, + thetas_, + x_r, + x_i); integral.grad(); EXPECT_LE(std::abs(val - integral.val()), tolerance); if constexpr (stan::is_var::value) { @@ -240,28 +248,52 @@ inline void test_derivatives(const F &f, double a, double b, TEST_F(AgradRev, StanMath_integrate_1d_de_rev_test_integer_arguments) { stan::math::var v; std::vector theta = {0.5}; - EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential( - f2{}, 0, 1, theta, {}, {}, msgs, 1e-6)); - EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential( - f2{}, 0.0, 1, theta, {}, {}, msgs, 1e-6)); - EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential( - f2{}, 0, 1.0, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol(f2{}, + 0, + 1, + 1e-6, + 0.0, + 15, + msgs, + theta, + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol(f2{}, + 0.0, + 1, + 1e-6, + 0.0, + 15, + msgs, + theta, + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol(f2{}, + 0, + 1.0, + 1e-6, + 0.0, + 15, + msgs, + theta, + std::vector{}, + std::vector{})); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_easy) { // Easy integrals using stan::math::var; - test_derivatives(f1{}, 0.2, 0.7, {0.75}, {}, {}, + test_derivatives(f1{}, 0.2, 0.7, {0.75}, std::vector{}, std::vector{}, 0.7923499493102901 + 0.5 * 0.75, {0.5}); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, 1.56348343527304, {1.25789445875152}, -2.148721270700128, 2.14872127069993); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, 1.56348343527304, {}, -2.148721270700128, 2.14872127069993); - test_derivatives(f1{}, 0.0, 0.0, {0.75}, {}, {}, 0.0, + test_derivatives(f1{}, 0.0, 0.0, {0.75}, std::vector{}, std::vector{}, 0.0, {0.0}); - test_derivatives(f2{}, 1.0, 1.0, {0.5}, {}, {}, 0.0, + test_derivatives(f2{}, 1.0, 1.0, {0.5}, std::vector{}, std::vector{}, 0.0, {0.0}); } @@ -335,14 +367,14 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky1) { using stan::math::var; test_derivatives(f4{}, 0.0, std::numeric_limits::infinity(), - {}, {}, {}, 1.772453850905516, {}); + {}, std::vector{}, std::vector{}, 1.772453850905516, {}); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky2) { // Tricky integral from Boost docs + limit at infinity with gradients using stan::math::var; test_derivatives( - f5{}, 0.0, std::numeric_limits::infinity(), {0.5, 3.0}, {}, {}, + f5{}, 0.0, std::numeric_limits::infinity(), {0.5, 3.0}, std::vector{}, std::vector{}, 1.772453850905516 / sqrt(0.5 * 3.0), {-1.772453850905516 * 3.0 / (2 * pow(0.5 * 3.0, 1.5)), -1.772453850905516 * 0.5 / (2 * pow(0.5 * 3.0, 1.5))}); @@ -352,14 +384,14 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky3) { // Tricky integral from Boost docs using stan::math::var; test_derivatives( - f6{}, 0.0, 1.0, {0.75}, {}, {}, 0.851926727945904, {0.4814066053874294}); + f6{}, 0.0, 1.0, {0.75}, std::vector{}, std::vector{}, 0.851926727945904, {0.4814066053874294}); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing2) { // Zero crossing integral + limit at infinity + var at left limit using stan::math::var; test_derivatives( - f7{}, -5.0, std::numeric_limits::infinity(), {1.5}, {}, {}, + f7{}, -5.0, std::numeric_limits::infinity(), {1.5}, std::vector{}, std::vector{}, 1205.361609637375, {5223.23364176196}, -1808.042414456063, std::numeric_limits::quiet_NaN()); } @@ -368,7 +400,7 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing3) { // Zero crossing integral + limit at negative infinity + var at right limit using stan::math::var; test_derivatives( - f8{}, -std::numeric_limits::infinity(), 5.0, {1.5}, {}, {}, + f8{}, -std::numeric_limits::infinity(), 5.0, {1.5}, std::vector{}, std::vector{}, 1205.361609637375, {5223.23364176196}, std::numeric_limits::quiet_NaN(), 1808.042414456063); } @@ -386,20 +418,20 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_endpoint_precision) { // Various integrals of beta function using stan::math::var; - test_derivatives(f11{}, 0.0, 1.0, {0.1, 0.1}, {}, {}, + test_derivatives(f11{}, 0.0, 1.0, {0.1, 0.1}, std::vector{}, std::vector{}, 19.71463948905016, {-101.229055967892, -101.229055967892}); test_derivatives( - f11{}, 0.0, 1.0, {0.5, 0.51}, {}, {}, 3.098843783331868, + f11{}, 0.0, 1.0, {0.5, 0.51}, std::vector{}, std::vector{}, 3.098843783331868, {-4.346514423368675, -4.196150770134913}); test_derivatives( - f11{}, 0.0, 1.0, {0.51, 0.5}, {}, {}, 3.098843783331868, + f11{}, 0.0, 1.0, {0.51, 0.5}, std::vector{}, std::vector{}, 3.098843783331868, {-4.196150770134913, -4.346514423368675}); test_derivatives( - f11{}, 0.0, 1.0, {5.0, 3.0}, {}, {}, 0.00952380952380952, + f11{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, 0.00952380952380952, {-0.004852607709750566, -0.01040816326530613}); test_derivatives( - f11{}, 0.0, 1.0, {3.0, 5.0}, {}, {}, 0.00952380952380952, + f11{}, 0.0, 1.0, {3.0, 5.0}, std::vector{}, std::vector{}, 0.00952380952380952, {-0.01040816326530613, -0.004852607709750566}); } @@ -408,7 +440,7 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_gaussian) { using stan::math::var; test_derivatives( f12{}, -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {5.7, 1}, {}, {}, 1.0, + std::numeric_limits::infinity(), {5.7, 1}, std::vector{}, std::vector{}, 1.0, {0.0, 0.0}); } @@ -421,8 +453,16 @@ TEST_F( var b = 4.0; std::vector thetas = {a, b}; - var integral = stan::math::integrate_1d_double_exponential( - f13{}, a, b, thetas, {}, {}, msgs, 1e-8); + var integral = stan::math::integrate_1d_double_exponential_tol(f13{}, + a, + b, + 1e-8, + 0.0, + 15, + msgs, + thetas, + std::vector{}, + std::vector{}); integral.grad(); EXPECT_LT(std::abs(18.0 - integral.val()), 1e-8); @@ -438,12 +478,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestBeta) { var alpha = 9.0 / 5; var beta = 13.0 / 7; std::vector theta = {alpha, beta}; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::beta_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, 0.0, 1.0, theta, {}, {}, msgs, - 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, 0.0, 1.0, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{alpha, beta}; @@ -463,11 +502,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestCauchy) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::cauchy_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -486,11 +525,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestChiSquare) { std::vector theta = {nu}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::chi_square_lpdf(x, theta[0])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {nu}; @@ -509,14 +548,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDoubleExponential) { std::vector theta = {mu, sigma}; double a = -std::numeric_limits::infinity(); double b = mu.val(); - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::double_exponential_lpdf(x, theta[0], theta[1])); }; // requires two subintervals to achieve numerical accuracy - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8) - + integrate_1d_double_exponential(pdf, b, -a, theta, {}, {}, msgs, - 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}) + + integrate_1d_double_exponential_tol(pdf, b, -a, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -535,11 +573,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestExponential) { std::vector theta = {beta}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::exponential_lpdf(x, theta[0])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {beta}; @@ -558,11 +596,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestFrechet) { std::vector theta = {alpha, sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::frechet_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {alpha, sigma}; @@ -582,11 +620,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestGamma) { std::vector theta = {alpha, beta}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::gamma_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{alpha, beta}; @@ -606,11 +644,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestGumbel) { std::vector theta = {mu, beta}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::gumbel_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, beta}; @@ -629,11 +667,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestInvChiSquared) { std::vector theta = {nu}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::inv_chi_square_lpdf(x, theta[0])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {nu}; @@ -652,11 +690,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestLogistic) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::logistic_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -676,11 +714,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestLogNormal) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::lognormal_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -700,11 +738,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestNormal) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::normal_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -724,11 +762,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestPareto) { std::vector theta = {m, alpha}; double b = std::numeric_limits::infinity(); var a = m; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::pareto_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{m, alpha}; @@ -749,11 +787,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestPareto2) { std::vector theta = {mu, lambda, alpha}; double b = std::numeric_limits::infinity(); var a = mu; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::pareto_type_2_lpdf(x, theta[0], theta[1], theta[2])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, lambda, alpha}; @@ -773,11 +811,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestRayleigh) { std::vector theta = {sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::rayleigh_lpdf(x, theta[0])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{sigma}; @@ -796,11 +834,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestScaledInvChiSquare) { std::vector theta = {nu, s}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::scaled_inv_chi_square_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{nu, s}; @@ -821,11 +859,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestStudentT) { std::vector theta = {nu, mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::student_t_lpdf(x, theta[0], theta[1], theta[2])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{nu, mu, sigma}; @@ -844,11 +882,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestUniform) { var a = 9.0 / 5; var b = 13.0 / 7; std::vector theta = {a, b}; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::uniform_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{a, b}; @@ -868,11 +906,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestVonMises) { std::vector theta = {mu, kappa}; double b = stan::math::pi() * 2; double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::von_mises_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, kappa}; @@ -892,11 +930,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestWeibull) { std::vector theta = {alpha, sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::weibull_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{alpha, sigma}; diff --git a/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp index 4111f26a469..87aa8c9a85e 100644 --- a/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp @@ -20,9 +20,9 @@ std::ostringstream *msgs = nullptr; struct f1 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(x) + theta[0]; } }; @@ -30,9 +30,9 @@ struct f1 { struct f2 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(theta[0] * cos(2 * 3.141593 * x)) + theta[0]; } }; @@ -40,9 +40,9 @@ struct f2 { struct f3 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(x) + pow(theta[0], x_r[0]) + 2 * pow(theta[1], x_r[1]) + 2 * theta[2]; } @@ -51,9 +51,9 @@ struct f3 { struct f4 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; @@ -61,9 +61,9 @@ struct f4 { struct f5 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(-theta[0] * x) / sqrt(theta[1] * x); } }; @@ -71,9 +71,9 @@ struct f5 { struct f6 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return sqrt(x / (1 - theta[0] * x * x)); } }; @@ -81,9 +81,9 @@ struct f6 { struct f7 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(-theta[0] * x); } }; @@ -91,9 +91,9 @@ struct f7 { struct f8 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return exp(theta[0] * x); } }; @@ -101,9 +101,9 @@ struct f8 { struct f10 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return 1 / (1 + pow(x, x_i[0]) / x_r[0]); } }; @@ -112,9 +112,9 @@ struct f10 { struct f11 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow(1 - x, theta[1] - 1.0); } }; @@ -122,9 +122,9 @@ struct f11 { struct f12 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { T3 mu = theta[0]; T3 sigma = theta[1]; return exp(-0.5 * stan::math::square((x - mu) / sigma)) @@ -135,9 +135,9 @@ struct f12 { struct f13 { template inline stan::return_type_t operator()( - const T1 &x, const T2 &xc, const std::vector &theta, - const std::vector &x_r, const std::vector &x_i, - std::ostream *msgs) const { + const T1 &x, const T2 &xc, std::ostream *msgs, + const std::vector &theta, + const std::vector &x_r, const std::vector &x_i) const { return x + theta[0] + theta[1]; } }; @@ -176,8 +176,16 @@ inline void test_derivatives(const F &f, double a, double b, for (size_t i = 0; i < thetas.size(); ++i) thetas_[i] = thetas[i]; - var integral = stan::math::integrate_1d_gauss_kronrod( - f, a_, b_, thetas_, x_r, x_i, msgs, tolerance); + var integral = stan::math::integrate_1d_gauss_kronrod_tol(f, + a_, + b_, + tolerance, + 0.0, + 15, + msgs, + thetas_, + x_r, + x_i); integral.grad(); EXPECT_LE(std::abs(val - integral.val()), tolerance); if constexpr (stan::is_var::value) { @@ -197,27 +205,51 @@ inline void test_derivatives(const F &f, double a, double b, TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_test_integer_arguments) { stan::math::var v; std::vector theta = {0.5}; - EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod( - f2{}, 0, 1, theta, {}, {}, msgs, 1e-6)); - EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod( - f2{}, 0.0, 1, theta, {}, {}, msgs, 1e-6)); - EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod( - f2{}, 0, 1.0, theta, {}, {}, msgs, 1e-6)); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol(f2{}, + 0, + 1, + 1e-6, + 0.0, + 15, + msgs, + theta, + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol(f2{}, + 0.0, + 1, + 1e-6, + 0.0, + 15, + msgs, + theta, + std::vector{}, + std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol(f2{}, + 0, + 1.0, + 1e-6, + 0.0, + 15, + msgs, + theta, + std::vector{}, + std::vector{})); } TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_easy) { using stan::math::var; - test_derivatives(f1{}, 0.2, 0.7, {0.75}, {}, {}, + test_derivatives(f1{}, 0.2, 0.7, {0.75}, std::vector{}, std::vector{}, 0.7923499493102901 + 0.5 * 0.75, {0.5}); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, 1.56348343527304, {1.25789445875152}, -2.148721270700128, 2.14872127069993); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, {}, {}, + test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, 1.56348343527304, {}, -2.148721270700128, 2.14872127069993); - test_derivatives(f1{}, 0.0, 0.0, {0.75}, {}, {}, 0.0, + test_derivatives(f1{}, 0.0, 0.0, {0.75}, std::vector{}, std::vector{}, 0.0, {0.0}); - test_derivatives(f2{}, 1.0, 1.0, {0.5}, {}, {}, 0.0, + test_derivatives(f2{}, 1.0, 1.0, {0.5}, std::vector{}, std::vector{}, 0.0, {0.0}); } @@ -306,10 +338,10 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_indefinite) { TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_smooth_beta) { using stan::math::var; test_derivatives( - f11{}, 0.0, 1.0, {5.0, 3.0}, {}, {}, 0.00952380952380952, + f11{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, 0.00952380952380952, {-0.004852607709750566, -0.01040816326530613}); test_derivatives( - f11{}, 0.0, 1.0, {3.0, 5.0}, {}, {}, 0.00952380952380952, + f11{}, 0.0, 1.0, {3.0, 5.0}, std::vector{}, std::vector{}, 0.00952380952380952, {-0.01040816326530613, -0.004852607709750566}); } @@ -317,7 +349,7 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_gaussian) { using stan::math::var; test_derivatives( f12{}, -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {5.7, 1}, {}, {}, 1.0, + std::numeric_limits::infinity(), {5.7, 1}, std::vector{}, std::vector{}, 1.0, {0.0, 0.0}); } @@ -329,8 +361,16 @@ TEST_F(AgradRev, var b = 4.0; std::vector thetas = {a, b}; - var integral = stan::math::integrate_1d_gauss_kronrod(f13{}, a, b, thetas, {}, - {}, msgs, 1e-8); + var integral = stan::math::integrate_1d_gauss_kronrod_tol(f13{}, + a, + b, + 1e-8, + 0.0, + 15, + msgs, + thetas, + std::vector{}, + std::vector{}); integral.grad(); EXPECT_LT(std::abs(18.0 - integral.val()), 1e-8); @@ -356,11 +396,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestCauchy) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::cauchy_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -379,11 +419,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestExponential) { std::vector theta = {beta}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::exponential_lpdf(x, theta[0])); }; - var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {beta}; @@ -402,11 +442,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestNormal) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::normal_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -427,11 +467,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestStudentT) { std::vector theta = {nu, mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::student_t_lpdf(x, theta[0], theta[1], theta[2])); }; - var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{nu, mu, sigma}; @@ -450,11 +490,11 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestUniform) { var a = 9.0 / 5; var b = 13.0 / 7; std::vector theta = {a, b}; - auto pdf = [](auto x, auto xc, auto theta, auto x_r, auto x_i, - std::ostream *msgs) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, + auto theta, auto x_r, auto x_i) { return exp(stan::math::uniform_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_gauss_kronrod(pdf, a, b, theta, {}, {}, msgs, 1e-8); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{a, b}; From c18272218e08475225c3de6c8f339eae4e988376 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Thu, 21 May 2026 16:18:59 -0400 Subject: [PATCH 11/13] [Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1 --- .../integrate_1d_double_exponential_test.cpp | 475 +++++++----------- .../integrate_1d_gauss_kronrod_test.cpp | 472 ++++++----------- .../integrate_1d_double_exponential_test.cpp | 363 ++++++------- .../integrate_1d_gauss_kronrod_test.cpp | 195 ++++--- 4 files changed, 610 insertions(+), 895 deletions(-) diff --git a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp index dbbf16129fc..34a7ff64cfc 100644 --- a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp @@ -12,22 +12,20 @@ std::ostringstream *msgs = nullptr; struct f1 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; struct f2 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { if (x <= 0.5) { return sqrt(x) / sqrt(1 - x * x); } else { @@ -38,88 +36,80 @@ struct f2 { struct f3 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-x); } }; struct f4 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + theta[0]; } }; struct f5 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(theta[0], 2) + pow(theta[1], 3); } }; struct f6 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(x_i[0], 2) + pow(theta[0], 4) + 3 * theta[1]; } }; struct f7 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(x_r[0], 2) + pow(x_r[1], 5) + 3 * x_r[2]; } }; struct f8 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-pow(x - theta[0], x_i[0]) / pow(x_r[0], x_i[0])); } }; struct f9 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return 1.0 / (1.0 + pow(x, x_i[0]) / theta[0]); } }; struct f10 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow((x > 0.5) ? xc : (1 - x), theta[1] - 1.0); } @@ -127,22 +117,20 @@ struct f10 { struct f11 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return (std::isnan(xc)) ? xc : 0.0; } }; struct f12 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * out; @@ -152,11 +140,10 @@ struct f12 { struct f13 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * stan::math::square(out); @@ -166,22 +153,20 @@ struct f13 { struct f14 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) * stan::math::inv_sqrt(x > 0.5 ? xc : 1 - x); } }; struct f15 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { T1 x2 = x * x; T1 numer = x2 * log(x); T1 denom = x < 0.5 ? (x + 1) * (x - 1) : (x + 1) * -xc; @@ -192,19 +177,17 @@ struct f15 { struct f16 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return x * sin(x) / (1 + stan::math::square(cos(x))); } }; struct f17 { inline double operator()(const double &x, const double &xc, - std::ostream *msgs, - const std::vector &theta, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, const std::vector &x_i) const { double mu = theta[0]; @@ -328,35 +311,21 @@ inline void test_integration(const F &f, double a, double b, std::vector tolerances = {1e-4, 1e-6, 1e-8}; for (auto tolerance : tolerances) { - EXPECT_LE(std::abs(integrate_1d_double_exponential_tol(f, - a, - b, - tolerance, - 0.0, - 15, - integrate_1d_de_test::msgs, - thetas, - x_r, - x_i) + EXPECT_LE(std::abs(integrate_1d_double_exponential_tol( + f, a, b, tolerance, 0.0, 15, + integrate_1d_de_test::msgs, thetas, x_r, x_i) - val), tolerance); // Flip the domain of integration and check that the integral is working - auto flipped = - [&](const double &x, const double &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) { - return f(-x, -xc, msgs, theta, x_r, x_i); - }; - EXPECT_LE(std::abs(integrate_1d_double_exponential_tol(flipped, - -b, - -a, - tolerance, - 0.0, - 15, - integrate_1d_de_test::msgs, - thetas, - x_r, - x_i) + auto flipped + = [&](const double &x, const double &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) { + return f(-x, -xc, msgs, theta, x_r, x_i); + }; + EXPECT_LE(std::abs(integrate_1d_double_exponential_tol( + flipped, -b, -a, tolerance, 0.0, 15, + integrate_1d_de_test::msgs, thetas, x_r, x_i) - val), tolerance); } @@ -364,161 +333,87 @@ inline void test_integration(const F &f, double a, double b, TEST(StanMath_integrate_1d_de_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit - EXPECT_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - 1.0, - 0.0, - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + EXPECT_THROW(stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, 1.0, 0.0, 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); // NaN limits not okay + EXPECT_THROW(stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, 0.0, + std::numeric_limits::quiet_NaN(), 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), + std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - 0.0, - std::numeric_limits::quiet_NaN(), - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), - std::domain_error); - EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - std::numeric_limits::quiet_NaN(), - 0.0, - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, std::numeric_limits::quiet_NaN(), + 0.0, 1e-6, 0.0, 15, integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - std::numeric_limits::quiet_NaN(), - std::numeric_limits::quiet_NaN(), - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); // Two of the same inf limits not okay EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - -std::numeric_limits::infinity(), - -std::numeric_limits::infinity(), - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, std::numeric_limits::infinity(), + std::numeric_limits::infinity(), 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); // xc should be nan if there are infinite limits + EXPECT_THROW(stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f11{}, 0.0, + std::numeric_limits::infinity(), 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), + std::runtime_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, - 0.0, - std::numeric_limits::infinity(), - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), - std::runtime_error); - EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, - std::numeric_limits::infinity(), - 0.0, - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f11{}, std::numeric_limits::infinity(), + 0.0, 1e-6, 0.0, 15, integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, - std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f11{}, std::numeric_limits::infinity(), + std::numeric_limits::infinity(), 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); // But not otherwise - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f11{}, - 0.0, - 1.0, - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f11{}, 0.0, 1.0, 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), std::vector{}, + std::vector{})); } TEST(StanMath_integrate_1d_de_prim, test_integer_arguments) { - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - 0, - 1, - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - 0.0, - 1, - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol(integrate_1d_de_test::f2{}, - 0, - 1.0, - 1e-6, - 0.0, - 15, - integrate_1d_de_test::msgs, - std::vector(), - std::vector{}, - std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, 0, 1, 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), std::vector{}, + std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, 0.0, 1, 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), std::vector{}, + std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_double_exponential_tol( + integrate_1d_de_test::f2{}, 0, 1.0, 1e-6, 0.0, 15, + integrate_1d_de_test::msgs, std::vector(), std::vector{}, + std::vector{})); } TEST(StanMath_integrate_1d_de_prim, test1) { @@ -537,13 +432,16 @@ TEST(StanMath_integrate_1d_de_prim, test1) { std::numeric_limits::infinity(), {}, {}, {}, 7.38905609893065); // Easy integrals - test_integration(integrate_1d_de_test::f4{}, 0.2, 0.7, {0.5}, std::vector{}, std::vector{}, + test_integration(integrate_1d_de_test::f4{}, 0.2, 0.7, {0.5}, + std::vector{}, std::vector{}, 1.0423499493102901); - test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, {0.4, 0.4}, std::vector{}, std::vector{}, + test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, {0.4, 0.4}, + std::vector{}, std::vector{}, 1.396621954392482); - test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, {0.5}, std::vector{}, std::vector{}, 0.0); - test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, std::vector{}, std::vector{}, - 0.0); + test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, {0.5}, + std::vector{}, std::vector{}, 0.0); + test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, + std::vector{}, std::vector{}, 0.0); // Test x_i test_integration(integrate_1d_de_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, 4131.985414616364); @@ -561,13 +459,17 @@ TEST(StanMath_integrate_1d_de_prim, test1) { std::numeric_limits::infinity(), {1.3}, {}, {4}, 2.372032924895055); // Various integrals of beta function - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.1}, std::vector{}, std::vector{}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.1}, + std::vector{}, std::vector{}, 19.71463948905016); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.5}, std::vector{}, std::vector{}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.5}, + std::vector{}, std::vector{}, 11.32308697521577); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.5, 0.1}, std::vector{}, std::vector{}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.5, 0.1}, + std::vector{}, std::vector{}, 11.32308697521577); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, + test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {5.0, 3.0}, + std::vector{}, std::vector{}, 0.00952380952380952); // Integrals from @@ -614,26 +516,12 @@ TEST(StanMath_integrate_1d_de_prim, abs_tol_argument_smoke) { using stan::math::integrate_1d_double_exponential; using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - double Q_strict = integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, - 0.2, - 0.7, - 1e-8, - 0.0, - 15, - msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}); - double Q_lenient = integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, - 0.2, - 0.7, - 1e-8, - 1e-12, - 15, - msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}); + double Q_strict = integrate_1d_double_exponential_tol( + integrate_1d_de_test::f4{}, 0.2, 0.7, 1e-8, 0.0, 15, msgs, + std::vector{0.5}, std::vector{}, std::vector{}); + double Q_lenient = integrate_1d_double_exponential_tol( + integrate_1d_de_test::f4{}, 0.2, 0.7, 1e-8, 1e-12, 15, msgs, + std::vector{0.5}, std::vector{}, std::vector{}); EXPECT_NEAR(Q_strict, 1.0423499493102901, 1e-8); EXPECT_NEAR(Q_lenient, Q_strict, 1e-12); } @@ -644,16 +532,9 @@ TEST(StanMath_integrate_1d_de_prim, max_refinements_argument) { using stan::math::integrate_1d_double_exponential; using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - double Q = integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, - 0.2, - 0.7, - 1e-8, - 0.0, - 20, - msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}); + double Q = integrate_1d_double_exponential_tol( + integrate_1d_de_test::f4{}, 0.2, 0.7, 1e-8, 0.0, 20, msgs, + std::vector{0.5}, std::vector{}, std::vector{}); EXPECT_NEAR(Q, 1.0423499493102901, 1e-8); } @@ -662,17 +543,11 @@ TEST(StanMath_integrate_1d_de_prim, negative_max_refinements_throws) { using stan::math::integrate_1d_double_exponential; using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - EXPECT_THROW(integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, - 0.2, - 0.7, - 1e-6, - 0.0, - -1, - msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW( + integrate_1d_double_exponential_tol( + integrate_1d_de_test::f4{}, 0.2, 0.7, 1e-6, 0.0, -1, msgs, + std::vector{0.5}, std::vector{}, std::vector{}), + std::domain_error); } // Negative absolute_tolerance not okay. @@ -680,15 +555,9 @@ TEST(StanMath_integrate_1d_de_prim, negative_abs_tol_throws) { using stan::math::integrate_1d_double_exponential; using stan::math::integrate_1d_double_exponential_tol; std::ostringstream *msgs = nullptr; - EXPECT_THROW(integrate_1d_double_exponential_tol(integrate_1d_de_test::f4{}, - 0.2, - 0.7, - 1e-6, - -1e-3, - 15, - msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW( + integrate_1d_double_exponential_tol( + integrate_1d_de_test::f4{}, 0.2, 0.7, 1e-6, -1e-3, 15, msgs, + std::vector{0.5}, std::vector{}, std::vector{}), + std::domain_error); } diff --git a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp index f3a0d151883..84b60971b6b 100644 --- a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp @@ -34,11 +34,10 @@ std::ostringstream *msgs = nullptr; struct f1 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; @@ -46,11 +45,10 @@ struct f1 { // Original f2 used xc near x=1; rewritten with explicit (1 - x). struct f2 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { if (x <= 0.5) { return sqrt(x) / sqrt(1 - x * x); } else { @@ -61,77 +59,70 @@ struct f2 { struct f3 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-x); } }; struct f4 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + theta[0]; } }; struct f5 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(theta[0], 2) + pow(theta[1], 3); } }; struct f6 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(x_i[0], 2) + pow(theta[0], 4) + 3 * theta[1]; } }; struct f7 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(x_r[0], 2) + pow(x_r[1], 5) + 3 * x_r[2]; } }; struct f8 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-pow(x - theta[0], x_i[0]) / pow(x_r[0], x_i[0])); } }; struct f9 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return 1.0 / (1.0 + pow(x, x_i[0]) / theta[0]); } }; @@ -139,22 +130,20 @@ struct f9 { // Original f10 used xc on the right half; rewritten with explicit (1 - x). struct f10 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow(1 - x, theta[1] - 1.0); } }; struct f12 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * out; @@ -164,11 +153,10 @@ struct f12 { struct f13 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { T1 out = stan::math::modified_bessel_second_kind(0, x); if (out > 0) return 2 * x * stan::math::square(out); @@ -179,30 +167,27 @@ struct f13 { // Original f14 used xc near x=1; rewritten with explicit (1 - x). struct f14 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) * stan::math::inv_sqrt(1 - x); } }; struct f16 { template - inline stan::return_type_t operator()(const T1 &x, const T1 &xc, - std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, - const std::vector &x_i) const { + inline stan::return_type_t operator()( + const T1 &x, const T1 &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return x * sin(x) / (1 + stan::math::square(cos(x))); } }; struct f17 { inline double operator()(const double &x, const double &xc, - std::ostream *msgs, - const std::vector &theta, + std::ostream *msgs, const std::vector &theta, const std::vector &x_r, const std::vector &x_i) const { double mu = theta[0]; @@ -230,35 +215,21 @@ inline void test_integration(const F &f, double a, double b, std::vector tolerances = {1e-4, 1e-6, 1e-8}; for (auto tolerance : tolerances) { - EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol(f, - a, - b, - tolerance, - 0.0, - 15, - integrate_1d_gk_test::msgs, - thetas, - x_r, - x_i) + EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol( + f, a, b, tolerance, 0.0, 15, + integrate_1d_gk_test::msgs, thetas, x_r, x_i) - val), tolerance); // Flip the domain of integration and check that the integral matches - auto flipped = - [&](const double &x, const double &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) { - return f(-x, -xc, msgs, theta, x_r, x_i); - }; - EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol(flipped, - -b, - -a, - tolerance, - 0.0, - 15, - integrate_1d_gk_test::msgs, - thetas, - x_r, - x_i) + auto flipped + = [&](const double &x, const double &xc, std::ostream *msgs, + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) { + return f(-x, -xc, msgs, theta, x_r, x_i); + }; + EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol( + flipped, -b, -a, tolerance, 0.0, 15, + integrate_1d_gk_test::msgs, thetas, x_r, x_i) - val), tolerance); } @@ -266,141 +237,75 @@ inline void test_integration(const F &f, double a, double b, TEST(StanMath_integrate_1d_gk_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit - EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 1.0, - 0.0, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 1.0, 0.0, 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}), + std::domain_error); // NaN limits not okay + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0.0, + std::numeric_limits::quiet_NaN(), 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}), + std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0.0, - std::numeric_limits::quiet_NaN(), - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), - std::domain_error); - EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - std::numeric_limits::quiet_NaN(), - 0.0, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), + stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, std::numeric_limits::quiet_NaN(), + 0.0, 1e-6, 0.0, 15, integrate_1d_gk_test::msgs, + std::vector{0.5}, std::vector{}, std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - std::numeric_limits::quiet_NaN(), - std::numeric_limits::quiet_NaN(), - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), + stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}), std::domain_error); // Two of the same inf limits not okay EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - -std::numeric_limits::infinity(), - -std::numeric_limits::infinity(), - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), + stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, -std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}), std::domain_error); EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), + stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, std::numeric_limits::infinity(), + std::numeric_limits::infinity(), 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}), std::domain_error); // Negative max_depth not okay - EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0.0, - 1.0, - 1e-6, - 0.0, - -1, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0.0, 1.0, 1e-6, 0.0, -1, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}), + std::domain_error); // Negative absolute_tolerance not okay - EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0.0, - 1.0, - 1e-6, - -1e-3, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0.0, 1.0, 1e-6, -1e-3, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}), + std::domain_error); } TEST(StanMath_integrate_1d_gk_prim, test_integer_arguments) { // Use a smooth integrand for the integer-bounds smoke test; f4 is exp(x)+c // and integrates cleanly under Gauss-Kronrod. - EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0, - 1, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0.0, - 1, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0, - 1.0, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0, 1, 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0.0, 1, 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{})); + EXPECT_NO_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0, 1.0, 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{})); } TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { @@ -409,14 +314,17 @@ TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { std::numeric_limits::infinity(), {}, {}, {}, 7.38905609893065); // Easy integrals - test_integration(integrate_1d_gk_test::f4{}, 0.2, 0.7, {0.5}, std::vector{}, std::vector{}, + test_integration(integrate_1d_gk_test::f4{}, 0.2, 0.7, {0.5}, + std::vector{}, std::vector{}, 1.0423499493102901); - test_integration(integrate_1d_gk_test::f5{}, -0.2, 0.7, {0.4, 0.4}, std::vector{}, std::vector{}, + test_integration(integrate_1d_gk_test::f5{}, -0.2, 0.7, {0.4, 0.4}, + std::vector{}, std::vector{}, 1.396621954392482); // Zero-length intervals - test_integration(integrate_1d_gk_test::f4{}, 0.0, 0.0, {0.5}, std::vector{}, std::vector{}, 0.0); - test_integration(integrate_1d_gk_test::f5{}, 1.0, 1.0, {0.4, 0.4}, std::vector{}, std::vector{}, - 0.0); + test_integration(integrate_1d_gk_test::f4{}, 0.0, 0.0, {0.5}, + std::vector{}, std::vector{}, 0.0); + test_integration(integrate_1d_gk_test::f5{}, 1.0, 1.0, {0.4, 0.4}, + std::vector{}, std::vector{}, 0.0); // Test x_i test_integration(integrate_1d_gk_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, 4131.985414616364); @@ -452,58 +360,33 @@ TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { // behaviour so future maintainers do not mistake it for a regression. TEST(StanMath_integrate_1d_gk_prim, endpoint_singularity_throws) { // 1/sqrt(x) at x = 0 (f1) - EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f1{}, - 0.0, - std::numeric_limits::infinity(), - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f1{}, 0.0, + std::numeric_limits::infinity(), 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector(), + std::vector{}, std::vector{}), + std::domain_error); // 1/sqrt(1-x*x) at x = 1 (f2) - EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f2{}, - 0.0, - 1.0, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector(), - std::vector{}, - std::vector{}), + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f2{}, 0.0, 1.0, 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector(), + std::vector{}, std::vector{}), std::domain_error); // beta integrand with small shape parameters (f10, a=b=0.1) - EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f10{}, - 0.0, - 1.0, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.1, 0.1}, - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f10{}, 0.0, 1.0, 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.1, 0.1}, + std::vector{}, std::vector{}), + std::domain_error); } TEST(StanMath_integrate_1d_gk_prim, max_depth_argument) { // Smoke test: explicit max_depth is accepted and produces a sensible result. // Argument order is (rel_tol, abs_tol, max_depth). - double Q = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0.2, - 0.7, - 1e-8, - 0.0, - 20, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}); + double Q = stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0.2, 0.7, 1e-8, 0.0, 20, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}); EXPECT_NEAR(Q, 1.0423499493102901, 1e-8); } @@ -521,18 +404,11 @@ TEST(StanMath_integrate_1d_gk_prim, max_depth_argument) { TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { // Sanity: with abs_tol = 0 (default) the call throws (this is the // same case as endpoint_singularity_throws.f10 above). - EXPECT_THROW( - stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f10{}, - 0.0, - 1.0, - 1e-6, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.1, 0.1}, - std::vector{}, - std::vector{}), - std::domain_error); + EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f10{}, 0.0, 1.0, 1e-6, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.1, 0.1}, + std::vector{}, std::vector{}), + std::domain_error); // With a very generous abs_tol the convergence threshold is // satisfied and the integral is returned. The endpoint singularity @@ -542,16 +418,10 @@ TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { // safely above it. The true value of B(0.1, 0.1) is ~19.7, so even // an imprecise estimate should be in the right ballpark. double Q = 0.0; - EXPECT_NO_THROW(Q = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f10{}, - 0.0, - 1.0, - 1e-6, - 1e6, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.1, 0.1}, - std::vector{}, - std::vector{})); + EXPECT_NO_THROW(Q = stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f10{}, 0.0, 1.0, 1e-6, 1e6, 15, + integrate_1d_gk_test::msgs, std::vector{0.1, 0.1}, + std::vector{}, std::vector{})); EXPECT_GT(Q, 1.0); EXPECT_LT(Q, 1000.0); } @@ -559,26 +429,14 @@ TEST(StanMath_integrate_1d_gk_prim, abs_tol_suppresses_throw) { TEST(StanMath_integrate_1d_gk_prim, abs_tol_argument_smoke) { // Smoke test: explicit abs_tol on a well-converged integrand does // not change the result. Argument order is (rel_tol, abs_tol). - double Q0 = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0.2, - 0.7, - 1e-8, - 0.0, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}); - double Q1 = stan::math::integrate_1d_gauss_kronrod_tol(integrate_1d_gk_test::f4{}, - 0.2, - 0.7, - 1e-8, - 1e-12, - 15, - integrate_1d_gk_test::msgs, - std::vector{0.5}, - std::vector{}, - std::vector{}); + double Q0 = stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0.2, 0.7, 1e-8, 0.0, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}); + double Q1 = stan::math::integrate_1d_gauss_kronrod_tol( + integrate_1d_gk_test::f4{}, 0.2, 0.7, 1e-8, 1e-12, 15, + integrate_1d_gk_test::msgs, std::vector{0.5}, + std::vector{}, std::vector{}); EXPECT_NEAR(Q0, 1.0423499493102901, 1e-8); EXPECT_NEAR(Q1, Q0, 1e-12); } diff --git a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp index d0bf6135113..748892b3b4c 100644 --- a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp @@ -16,8 +16,8 @@ struct f1 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + theta[0]; } }; @@ -26,8 +26,8 @@ struct f2 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(theta[0] * cos(2 * 3.141593 * x)) + theta[0]; } }; @@ -36,8 +36,8 @@ struct f3 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(theta[0], x_r[0]) + 2 * pow(theta[1], x_r[1]) + 2 * theta[2]; } @@ -47,8 +47,8 @@ struct f4 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; @@ -57,8 +57,8 @@ struct f5 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-theta[0] * x) / sqrt(theta[1] * x); } }; @@ -67,8 +67,8 @@ struct f6 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return sqrt(x / (1 - theta[0] * x * x)); } }; @@ -77,8 +77,8 @@ struct f7 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-theta[0] * x); } }; @@ -87,8 +87,8 @@ struct f8 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(theta[0] * x); } }; @@ -97,8 +97,8 @@ struct f10 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return 1 / (1 + pow(x, x_i[0]) / x_r[0]); } }; @@ -107,8 +107,8 @@ struct f11 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow((x > 0.5) ? xc : (1 - x), theta[1] - 1.0); } @@ -118,8 +118,8 @@ struct f12 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { T3 mu = theta[0]; T3 sigma = theta[1]; return exp(-0.5 * stan::math::square((x - mu) / sigma)) @@ -131,8 +131,8 @@ struct f13 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return x + theta[0] + theta[1]; } }; @@ -219,16 +219,8 @@ inline void test_derivatives(const F &f, double a, double b, for (size_t i = 0; i < thetas.size(); ++i) thetas_[i] = thetas[i]; - var integral = stan::math::integrate_1d_double_exponential_tol(f, - a_, - b_, - tolerance, - 0.0, - 15, - msgs, - thetas_, - x_r, - x_i); + var integral = stan::math::integrate_1d_double_exponential_tol( + f, a_, b_, tolerance, 0.0, 15, msgs, thetas_, x_r, x_i); integral.grad(); EXPECT_LE(std::abs(val - integral.val()), tolerance); if constexpr (stan::is_var::value) { @@ -248,53 +240,36 @@ inline void test_derivatives(const F &f, double a, double b, TEST_F(AgradRev, StanMath_integrate_1d_de_rev_test_integer_arguments) { stan::math::var v; std::vector theta = {0.5}; - EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol(f2{}, - 0, - 1, - 1e-6, - 0.0, - 15, - msgs, - theta, - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol(f2{}, - 0.0, - 1, - 1e-6, - 0.0, - 15, - msgs, - theta, - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol(f2{}, - 0, - 1.0, - 1e-6, - 0.0, - 15, - msgs, - theta, - std::vector{}, - std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol( + f2{}, 0, 1, 1e-6, 0.0, 15, msgs, theta, + std::vector{}, std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol( + f2{}, 0.0, 1, 1e-6, 0.0, 15, msgs, theta, + std::vector{}, std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_double_exponential_tol( + f2{}, 0, 1.0, 1e-6, 0.0, 15, msgs, theta, + std::vector{}, std::vector{})); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_easy) { // Easy integrals using stan::math::var; - test_derivatives(f1{}, 0.2, 0.7, {0.75}, std::vector{}, std::vector{}, - 0.7923499493102901 + 0.5 * 0.75, {0.5}); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, - 1.56348343527304, {1.25789445875152}, - -2.148721270700128, 2.14872127069993); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, - 1.56348343527304, {}, -2.148721270700128, - 2.14872127069993); - test_derivatives(f1{}, 0.0, 0.0, {0.75}, std::vector{}, std::vector{}, 0.0, - {0.0}); - test_derivatives(f2{}, 1.0, 1.0, {0.5}, std::vector{}, std::vector{}, 0.0, - {0.0}); + test_derivatives( + f1{}, 0.2, 0.7, {0.75}, std::vector{}, std::vector{}, + 0.7923499493102901 + 0.5 * 0.75, {0.5}); + test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, + std::vector{}, 1.56348343527304, + {1.25789445875152}, -2.148721270700128, + 2.14872127069993); + test_derivatives( + f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, + 1.56348343527304, {}, -2.148721270700128, 2.14872127069993); + test_derivatives(f1{}, 0.0, 0.0, {0.75}, + std::vector{}, + std::vector{}, 0.0, {0.0}); + test_derivatives(f2{}, 1.0, 1.0, {0.5}, + std::vector{}, + std::vector{}, 0.0, {0.0}); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing) { @@ -365,16 +340,17 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_right_limit_var) { TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky1) { // Tricky integral from Boost docs + limit at infinity + no gradients using stan::math::var; - test_derivatives(f4{}, 0.0, - std::numeric_limits::infinity(), - {}, std::vector{}, std::vector{}, 1.772453850905516, {}); + test_derivatives( + f4{}, 0.0, std::numeric_limits::infinity(), {}, + std::vector{}, std::vector{}, 1.772453850905516, {}); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky2) { // Tricky integral from Boost docs + limit at infinity with gradients using stan::math::var; test_derivatives( - f5{}, 0.0, std::numeric_limits::infinity(), {0.5, 3.0}, std::vector{}, std::vector{}, + f5{}, 0.0, std::numeric_limits::infinity(), {0.5, 3.0}, + std::vector{}, std::vector{}, 1.772453850905516 / sqrt(0.5 * 3.0), {-1.772453850905516 * 3.0 / (2 * pow(0.5 * 3.0, 1.5)), -1.772453850905516 * 0.5 / (2 * pow(0.5 * 3.0, 1.5))}); @@ -384,15 +360,17 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_tricky3) { // Tricky integral from Boost docs using stan::math::var; test_derivatives( - f6{}, 0.0, 1.0, {0.75}, std::vector{}, std::vector{}, 0.851926727945904, {0.4814066053874294}); + f6{}, 0.0, 1.0, {0.75}, std::vector{}, std::vector{}, + 0.851926727945904, {0.4814066053874294}); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing2) { // Zero crossing integral + limit at infinity + var at left limit using stan::math::var; test_derivatives( - f7{}, -5.0, std::numeric_limits::infinity(), {1.5}, std::vector{}, std::vector{}, - 1205.361609637375, {5223.23364176196}, -1808.042414456063, + f7{}, -5.0, std::numeric_limits::infinity(), {1.5}, + std::vector{}, std::vector{}, 1205.361609637375, + {5223.23364176196}, -1808.042414456063, std::numeric_limits::quiet_NaN()); } @@ -400,9 +378,10 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing3) { // Zero crossing integral + limit at negative infinity + var at right limit using stan::math::var; test_derivatives( - f8{}, -std::numeric_limits::infinity(), 5.0, {1.5}, std::vector{}, std::vector{}, - 1205.361609637375, {5223.23364176196}, - std::numeric_limits::quiet_NaN(), 1808.042414456063); + f8{}, -std::numeric_limits::infinity(), 5.0, {1.5}, + std::vector{}, std::vector{}, 1205.361609637375, + {5223.23364176196}, std::numeric_limits::quiet_NaN(), + 1808.042414456063); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_indefinite) { @@ -418,21 +397,21 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_endpoint_precision) { // Various integrals of beta function using stan::math::var; - test_derivatives(f11{}, 0.0, 1.0, {0.1, 0.1}, std::vector{}, std::vector{}, - 19.71463948905016, - {-101.229055967892, -101.229055967892}); test_derivatives( - f11{}, 0.0, 1.0, {0.5, 0.51}, std::vector{}, std::vector{}, 3.098843783331868, - {-4.346514423368675, -4.196150770134913}); + f11{}, 0.0, 1.0, {0.1, 0.1}, std::vector{}, std::vector{}, + 19.71463948905016, {-101.229055967892, -101.229055967892}); + test_derivatives( + f11{}, 0.0, 1.0, {0.5, 0.51}, std::vector{}, std::vector{}, + 3.098843783331868, {-4.346514423368675, -4.196150770134913}); test_derivatives( - f11{}, 0.0, 1.0, {0.51, 0.5}, std::vector{}, std::vector{}, 3.098843783331868, - {-4.196150770134913, -4.346514423368675}); + f11{}, 0.0, 1.0, {0.51, 0.5}, std::vector{}, std::vector{}, + 3.098843783331868, {-4.196150770134913, -4.346514423368675}); test_derivatives( - f11{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, 0.00952380952380952, - {-0.004852607709750566, -0.01040816326530613}); + f11{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, + 0.00952380952380952, {-0.004852607709750566, -0.01040816326530613}); test_derivatives( - f11{}, 0.0, 1.0, {3.0, 5.0}, std::vector{}, std::vector{}, 0.00952380952380952, - {-0.01040816326530613, -0.004852607709750566}); + f11{}, 0.0, 1.0, {3.0, 5.0}, std::vector{}, std::vector{}, + 0.00952380952380952, {-0.01040816326530613, -0.004852607709750566}); } TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_gaussian) { @@ -440,8 +419,8 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_gaussian) { using stan::math::var; test_derivatives( f12{}, -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {5.7, 1}, std::vector{}, std::vector{}, 1.0, - {0.0, 0.0}); + std::numeric_limits::infinity(), {5.7, 1}, std::vector{}, + std::vector{}, 1.0, {0.0, 0.0}); } TEST_F( @@ -453,16 +432,9 @@ TEST_F( var b = 4.0; std::vector thetas = {a, b}; - var integral = stan::math::integrate_1d_double_exponential_tol(f13{}, - a, - b, - 1e-8, - 0.0, - 15, - msgs, - thetas, - std::vector{}, - std::vector{}); + var integral = stan::math::integrate_1d_double_exponential_tol( + f13{}, a, b, 1e-8, 0.0, 15, msgs, thetas, std::vector{}, + std::vector{}); integral.grad(); EXPECT_LT(std::abs(18.0 - integral.val()), 1e-8); @@ -478,11 +450,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestBeta) { var alpha = 9.0 / 5; var beta = 13.0 / 7; std::vector theta = {alpha, beta}; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::beta_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, 0.0, 1.0, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol( + pdf, 0.0, 1.0, 1e-8, 0.0, 15, msgs, theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{alpha, beta}; @@ -502,11 +476,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestCauchy) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::cauchy_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -525,11 +501,12 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestChiSquare) { std::vector theta = {nu}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { - return exp(stan::math::chi_square_lpdf(x, theta[0])); - }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + auto pdf + = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::chi_square_lpdf(x, theta[0])); }; + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {nu}; @@ -548,13 +525,17 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDoubleExponential) { std::vector theta = {mu, sigma}; double a = -std::numeric_limits::infinity(); double b = mu.val(); - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::double_exponential_lpdf(x, theta[0], theta[1])); }; // requires two subintervals to achieve numerical accuracy - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}) - + integrate_1d_double_exponential_tol(pdf, b, -a, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}) + + integrate_1d_double_exponential_tol(pdf, b, -a, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -573,11 +554,12 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestExponential) { std::vector theta = {beta}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { - return exp(stan::math::exponential_lpdf(x, theta[0])); - }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + auto pdf + = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::exponential_lpdf(x, theta[0])); }; + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {beta}; @@ -596,11 +578,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestFrechet) { std::vector theta = {alpha, sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::frechet_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {alpha, sigma}; @@ -620,11 +604,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestGamma) { std::vector theta = {alpha, beta}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::gamma_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{alpha, beta}; @@ -644,11 +630,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestGumbel) { std::vector theta = {mu, beta}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::gumbel_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, beta}; @@ -667,11 +655,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestInvChiSquared) { std::vector theta = {nu}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::inv_chi_square_lpdf(x, theta[0])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {nu}; @@ -690,11 +680,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestLogistic) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::logistic_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -714,11 +706,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestLogNormal) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::lognormal_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -738,11 +732,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestNormal) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::normal_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -762,11 +758,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestPareto) { std::vector theta = {m, alpha}; double b = std::numeric_limits::infinity(); var a = m; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::pareto_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{m, alpha}; @@ -787,11 +785,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestPareto2) { std::vector theta = {mu, lambda, alpha}; double b = std::numeric_limits::infinity(); var a = mu; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::pareto_type_2_lpdf(x, theta[0], theta[1], theta[2])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, lambda, alpha}; @@ -811,11 +811,12 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestRayleigh) { std::vector theta = {sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { - return exp(stan::math::rayleigh_lpdf(x, theta[0])); - }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + auto pdf + = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::rayleigh_lpdf(x, theta[0])); }; + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{sigma}; @@ -834,11 +835,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestScaledInvChiSquare) { std::vector theta = {nu, s}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::scaled_inv_chi_square_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{nu, s}; @@ -859,11 +862,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestStudentT) { std::vector theta = {nu, mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::student_t_lpdf(x, theta[0], theta[1], theta[2])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{nu, mu, sigma}; @@ -882,11 +887,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestUniform) { var a = 9.0 / 5; var b = 13.0 / 7; std::vector theta = {a, b}; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::uniform_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{a, b}; @@ -906,11 +913,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestVonMises) { std::vector theta = {mu, kappa}; double b = stan::math::pi() * 2; double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::von_mises_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, kappa}; @@ -930,11 +939,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestWeibull) { std::vector theta = {alpha, sigma}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::weibull_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_double_exponential_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, + theta, std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{alpha, sigma}; diff --git a/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp index 87aa8c9a85e..7dfc53065d6 100644 --- a/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/rev/functor/integrate_1d_gauss_kronrod_test.cpp @@ -21,8 +21,8 @@ struct f1 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + theta[0]; } }; @@ -31,8 +31,8 @@ struct f2 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(theta[0] * cos(2 * 3.141593 * x)) + theta[0]; } }; @@ -41,8 +41,8 @@ struct f3 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(x) + pow(theta[0], x_r[0]) + 2 * pow(theta[1], x_r[1]) + 2 * theta[2]; } @@ -52,8 +52,8 @@ struct f4 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-x) / sqrt(x); } }; @@ -62,8 +62,8 @@ struct f5 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-theta[0] * x) / sqrt(theta[1] * x); } }; @@ -72,8 +72,8 @@ struct f6 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return sqrt(x / (1 - theta[0] * x * x)); } }; @@ -82,8 +82,8 @@ struct f7 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(-theta[0] * x); } }; @@ -92,8 +92,8 @@ struct f8 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return exp(theta[0] * x); } }; @@ -102,8 +102,8 @@ struct f10 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return 1 / (1 + pow(x, x_i[0]) / x_r[0]); } }; @@ -113,8 +113,8 @@ struct f11 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return pow(x, theta[0] - 1.0) * pow(1 - x, theta[1] - 1.0); } }; @@ -123,8 +123,8 @@ struct f12 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { T3 mu = theta[0]; T3 sigma = theta[1]; return exp(-0.5 * stan::math::square((x - mu) / sigma)) @@ -136,8 +136,8 @@ struct f13 { template inline stan::return_type_t operator()( const T1 &x, const T2 &xc, std::ostream *msgs, - const std::vector &theta, - const std::vector &x_r, const std::vector &x_i) const { + const std::vector &theta, const std::vector &x_r, + const std::vector &x_i) const { return x + theta[0] + theta[1]; } }; @@ -176,16 +176,8 @@ inline void test_derivatives(const F &f, double a, double b, for (size_t i = 0; i < thetas.size(); ++i) thetas_[i] = thetas[i]; - var integral = stan::math::integrate_1d_gauss_kronrod_tol(f, - a_, - b_, - tolerance, - 0.0, - 15, - msgs, - thetas_, - x_r, - x_i); + var integral = stan::math::integrate_1d_gauss_kronrod_tol( + f, a_, b_, tolerance, 0.0, 15, msgs, thetas_, x_r, x_i); integral.grad(); EXPECT_LE(std::abs(val - integral.val()), tolerance); if constexpr (stan::is_var::value) { @@ -205,52 +197,35 @@ inline void test_derivatives(const F &f, double a, double b, TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_test_integer_arguments) { stan::math::var v; std::vector theta = {0.5}; - EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol(f2{}, - 0, - 1, - 1e-6, - 0.0, - 15, - msgs, - theta, - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol(f2{}, - 0.0, - 1, - 1e-6, - 0.0, - 15, - msgs, - theta, - std::vector{}, - std::vector{})); - EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol(f2{}, - 0, - 1.0, - 1e-6, - 0.0, - 15, - msgs, - theta, - std::vector{}, - std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol( + f2{}, 0, 1, 1e-6, 0.0, 15, msgs, theta, + std::vector{}, std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol( + f2{}, 0.0, 1, 1e-6, 0.0, 15, msgs, theta, + std::vector{}, std::vector{})); + EXPECT_NO_THROW(v = stan::math::integrate_1d_gauss_kronrod_tol( + f2{}, 0, 1.0, 1e-6, 0.0, 15, msgs, theta, + std::vector{}, std::vector{})); } TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_easy) { using stan::math::var; - test_derivatives(f1{}, 0.2, 0.7, {0.75}, std::vector{}, std::vector{}, - 0.7923499493102901 + 0.5 * 0.75, {0.5}); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, - 1.56348343527304, {1.25789445875152}, - -2.148721270700128, 2.14872127069993); - test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, - 1.56348343527304, {}, -2.148721270700128, - 2.14872127069993); - test_derivatives(f1{}, 0.0, 0.0, {0.75}, std::vector{}, std::vector{}, 0.0, - {0.0}); - test_derivatives(f2{}, 1.0, 1.0, {0.5}, std::vector{}, std::vector{}, 0.0, - {0.0}); + test_derivatives( + f1{}, 0.2, 0.7, {0.75}, std::vector{}, std::vector{}, + 0.7923499493102901 + 0.5 * 0.75, {0.5}); + test_derivatives(f2{}, 0.0, 1.0, {0.5}, std::vector{}, + std::vector{}, 1.56348343527304, + {1.25789445875152}, -2.148721270700128, + 2.14872127069993); + test_derivatives( + f2{}, 0.0, 1.0, {0.5}, std::vector{}, std::vector{}, + 1.56348343527304, {}, -2.148721270700128, 2.14872127069993); + test_derivatives(f1{}, 0.0, 0.0, {0.75}, + std::vector{}, + std::vector{}, 0.0, {0.0}); + test_derivatives(f2{}, 1.0, 1.0, {0.5}, + std::vector{}, + std::vector{}, 0.0, {0.0}); } TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_zero_crossing) { @@ -338,19 +313,19 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_indefinite) { TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_smooth_beta) { using stan::math::var; test_derivatives( - f11{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, 0.00952380952380952, - {-0.004852607709750566, -0.01040816326530613}); + f11{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, std::vector{}, + 0.00952380952380952, {-0.004852607709750566, -0.01040816326530613}); test_derivatives( - f11{}, 0.0, 1.0, {3.0, 5.0}, std::vector{}, std::vector{}, 0.00952380952380952, - {-0.01040816326530613, -0.004852607709750566}); + f11{}, 0.0, 1.0, {3.0, 5.0}, std::vector{}, std::vector{}, + 0.00952380952380952, {-0.01040816326530613, -0.004852607709750566}); } TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestDerivatives_gaussian) { using stan::math::var; test_derivatives( f12{}, -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {5.7, 1}, std::vector{}, std::vector{}, 1.0, - {0.0, 0.0}); + std::numeric_limits::infinity(), {5.7, 1}, std::vector{}, + std::vector{}, 1.0, {0.0, 0.0}); } TEST_F(AgradRev, @@ -361,16 +336,9 @@ TEST_F(AgradRev, var b = 4.0; std::vector thetas = {a, b}; - var integral = stan::math::integrate_1d_gauss_kronrod_tol(f13{}, - a, - b, - 1e-8, - 0.0, - 15, - msgs, - thetas, - std::vector{}, - std::vector{}); + var integral = stan::math::integrate_1d_gauss_kronrod_tol( + f13{}, a, b, 1e-8, 0.0, 15, msgs, thetas, std::vector{}, + std::vector{}); integral.grad(); EXPECT_LT(std::abs(18.0 - integral.val()), 1e-8); @@ -396,11 +364,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestCauchy) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::cauchy_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, + std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -419,11 +389,12 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestExponential) { std::vector theta = {beta}; double b = std::numeric_limits::infinity(); double a = 0; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { - return exp(stan::math::exponential_lpdf(x, theta[0])); - }; - var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + auto pdf + = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::exponential_lpdf(x, theta[0])); }; + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, + std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x = {beta}; @@ -442,11 +413,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestNormal) { std::vector theta = {mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::normal_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, + std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{mu, sigma}; @@ -467,11 +440,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestStudentT) { std::vector theta = {nu, mu, sigma}; double b = std::numeric_limits::infinity(); double a = -b; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::student_t_lpdf(x, theta[0], theta[1], theta[2])); }; - var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, + std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{nu, mu, sigma}; @@ -490,11 +465,13 @@ TEST_F(AgradRev, StanMath_integrate_1d_gk_rev_TestUniform) { var a = 9.0 / 5; var b = 13.0 / 7; std::vector theta = {a, b}; - auto pdf = [](auto x, auto xc, std::ostream *msgs, - auto theta, auto x_r, auto x_i) { + auto pdf = [](auto x, auto xc, std::ostream *msgs, auto theta, auto x_r, + auto x_i) { return exp(stan::math::uniform_lpdf(x, theta[0], theta[1])); }; - var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, std::vector{}, std::vector{}); + var I = integrate_1d_gauss_kronrod_tol(pdf, a, b, 1e-8, 0.0, 15, msgs, theta, + std::vector{}, + std::vector{}); EXPECT_FLOAT_EQ(1, I.val()); std::vector x{a, b}; From 34d808a4aee9dd532e794af3cafe115e8c933030 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Thu, 21 May 2026 16:24:57 -0400 Subject: [PATCH 12/13] Shorter test name for cpplint --- .../math/rev/functor/integrate_1d_double_exponential_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp index 748892b3b4c..70b0dd2282e 100644 --- a/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/rev/functor/integrate_1d_double_exponential_test.cpp @@ -285,7 +285,7 @@ TEST_F(AgradRev, StanMath_integrate_1d_de_rev_TestDerivatives_zero_crossing) { TEST_F( AgradRev, - StanMath_integrate_1d_de_rev_TestDerivatives_var_right_endpoint_var_params) { + StanMath_integrate_1d_de_rev_TestDerivatives_var_right_endpt_var_params) { // Zero crossing integral + test x_r + vars at right endpoint using stan::math::var; test_derivatives( From f8bb190456555510e72086f77122d177114bd397 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Thu, 21 May 2026 17:04:25 -0400 Subject: [PATCH 13/13] Avoid namespace collision in testing --- .../integrate_1d_double_exponential_test.cpp | 131 ++++++++++-------- .../integrate_1d_gauss_kronrod_test.cpp | 82 +++++------ 2 files changed, 113 insertions(+), 100 deletions(-) diff --git a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp index 34a7ff64cfc..b84cadf069b 100644 --- a/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_double_exponential_test.cpp @@ -273,7 +273,7 @@ inline double order(double down, double up, const std::vector &theta, x_i); return v; } -} // namespace integrate_1d_de_test + /* * test_integration is a helper function to make it easy to test the * integrate_1d function. @@ -312,8 +312,7 @@ inline void test_integration(const F &f, double a, double b, for (auto tolerance : tolerances) { EXPECT_LE(std::abs(integrate_1d_double_exponential_tol( - f, a, b, tolerance, 0.0, 15, - integrate_1d_de_test::msgs, thetas, x_r, x_i) + f, a, b, tolerance, 0.0, 15, msgs, thetas, x_r, x_i) - val), tolerance); // Flip the domain of integration and check that the integral is working @@ -324,13 +323,15 @@ inline void test_integration(const F &f, double a, double b, return f(-x, -xc, msgs, theta, x_r, x_i); }; EXPECT_LE(std::abs(integrate_1d_double_exponential_tol( - flipped, -b, -a, tolerance, 0.0, 15, - integrate_1d_de_test::msgs, thetas, x_r, x_i) + flipped, -b, -a, tolerance, 0.0, 15, msgs, thetas, + x_r, x_i) - val), tolerance); } } +} // namespace integrate_1d_de_test + TEST(StanMath_integrate_1d_de_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit EXPECT_THROW(stan::math::integrate_1d_double_exponential_tol( @@ -418,81 +419,89 @@ TEST(StanMath_integrate_1d_de_prim, test_integer_arguments) { TEST(StanMath_integrate_1d_de_prim, test1) { // Tricky integral from Boost docs + limit at infinity - test_integration(integrate_1d_de_test::f1{}, 0.0, - std::numeric_limits::infinity(), {}, {}, {}, - 1.772453850905516); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f1{}, 0.0, std::numeric_limits::infinity(), + {}, {}, {}, 1.772453850905516); // Tricky integral from Boost 1d integration docs - test_integration(integrate_1d_de_test::f2{}, 0.0, 1.0, {}, {}, {}, - 1.198140234735592); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f2{}, 0.0, 1.0, + {}, {}, {}, 1.198140234735592); // Tricky integral from Boost 1d integration docs - test_integration(integrate_1d_de_test::f2{}, 0, 1, {}, {}, {}, - 1.198140234735592); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f2{}, 0, 1, {}, + {}, {}, 1.198140234735592); // Zero crossing integral + limit at infinity - test_integration(integrate_1d_de_test::f3{}, -2.0, - std::numeric_limits::infinity(), {}, {}, {}, - 7.38905609893065); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f3{}, -2.0, std::numeric_limits::infinity(), + {}, {}, {}, 7.38905609893065); // Easy integrals - test_integration(integrate_1d_de_test::f4{}, 0.2, 0.7, {0.5}, - std::vector{}, std::vector{}, - 1.0423499493102901); - test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, {0.4, 0.4}, - std::vector{}, std::vector{}, - 1.396621954392482); - test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, {0.5}, - std::vector{}, std::vector{}, 0.0); - test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, {0.4, 0.4}, - std::vector{}, std::vector{}, 0.0); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f4{}, 0.2, 0.7, {0.5}, std::vector{}, + std::vector{}, 1.0423499493102901); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f5{}, -0.2, 0.7, + {0.4, 0.4}, std::vector{}, + std::vector{}, 1.396621954392482); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f4{}, 0.0, 0.0, + {0.5}, std::vector{}, + std::vector{}, 0.0); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f5{}, 1.0, 1.0, + {0.4, 0.4}, std::vector{}, + std::vector{}, 0.0); // Test x_i - test_integration(integrate_1d_de_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, - 4131.985414616364); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f6{}, -0.2, 2.9, + {6.0, 5.1}, {}, {4}, + 4131.985414616364); // Test x_r - test_integration(integrate_1d_de_test::f7{}, -0.2, 2.9, {}, {4.0, 6.0, 5.1}, - {}, 24219.985414616367); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f7{}, -0.2, 2.9, + {}, {4.0, 6.0, 5.1}, {}, + 24219.985414616367); // Both limits at infinity + test x_r/x_i - test_integration(integrate_1d_de_test::f8{}, - -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {5.0}, {1.7}, {2}, - 3.013171546539377); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f8{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {5.0}, {1.7}, {2}, + 3.013171546539377); // Both limits at infinity + test x_i - test_integration(integrate_1d_de_test::f9{}, - -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {1.3}, {}, {4}, - 2.372032924895055); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f9{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {1.3}, {}, {4}, + 2.372032924895055); // Various integrals of beta function - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.1}, - std::vector{}, std::vector{}, - 19.71463948905016); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.1, 0.5}, - std::vector{}, std::vector{}, - 11.32308697521577); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {0.5, 0.1}, - std::vector{}, std::vector{}, - 11.32308697521577); - test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, {5.0, 3.0}, - std::vector{}, std::vector{}, - 0.00952380952380952); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, + {0.1, 0.1}, std::vector{}, + std::vector{}, 19.71463948905016); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, + {0.1, 0.5}, std::vector{}, + std::vector{}, 11.32308697521577); + integrate_1d_de_test::test_integration(integrate_1d_de_test::f10{}, 0.0, 1.0, + {0.5, 0.1}, std::vector{}, + std::vector{}, 11.32308697521577); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f10{}, 0.0, 1.0, {5.0, 3.0}, std::vector{}, + std::vector{}, 0.00952380952380952); // Integrals from // http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf - test_integration(integrate_1d_de_test::f12{}, 0.0, - std::numeric_limits::infinity(), {}, {}, {}, 2.0); - test_integration(integrate_1d_de_test::f13{}, 0.0, - std::numeric_limits::infinity(), {}, {}, {}, 1.0); - test_integration(integrate_1d_de_test::f14{}, 0.0, 1.0, {}, {}, {}, - exp(1) * sqrt(stan::math::pi()) * stan::math::erf(1.0)); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f12{}, 0.0, std::numeric_limits::infinity(), + {}, {}, {}, 2.0); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f13{}, 0.0, std::numeric_limits::infinity(), + {}, {}, {}, 1.0); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f14{}, 0.0, 1.0, {}, {}, {}, + exp(1) * sqrt(stan::math::pi()) * stan::math::erf(1.0)); // Integrals from http://crd-legacy.lbl.gov/~dhbailey/dhbpapers/quadrature.pdf // works normally but not to tolerance when limits of integration are flipped - // test_integration(f15{}, 0.0, 1.0, {}, {}, {}, + // integrate_1d_de_test::test_integration(f15{}, 0.0, 1.0, {}, {}, {}, // stan::math::square(stan::math::pi()) * (2 - sqrt(2.0)) / // 32); - test_integration(integrate_1d_de_test::f16{}, 0.0, stan::math::pi(), {}, {}, - {}, stan::math::square(stan::math::pi()) / 4); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f16{}, 0.0, stan::math::pi(), {}, {}, {}, + stan::math::square(stan::math::pi()) / 4); // Make sure bounds working right - test_integration(integrate_1d_de_test::f17{}, - -std::numeric_limits::infinity(), -1.5, {0.0, 1.0}, - {}, {}, 0.066807201268858071); + integrate_1d_de_test::test_integration( + integrate_1d_de_test::f17{}, -std::numeric_limits::infinity(), + -1.5, {0.0, 1.0}, {}, {}, 0.066807201268858071); } TEST(StanMath_integrate_1d_de_prim, TestTolerance) { diff --git a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp index 84b60971b6b..e49ae98e4dd 100644 --- a/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp +++ b/test/unit/math/prim/functor/integrate_1d_gauss_kronrod_test.cpp @@ -197,8 +197,6 @@ struct f17 { } }; -} // namespace integrate_1d_gk_test - /* * test_integration is a helper that integrates the provided function and * checks the computed value against val. It also exercises the flipped @@ -216,8 +214,7 @@ inline void test_integration(const F &f, double a, double b, for (auto tolerance : tolerances) { EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol( - f, a, b, tolerance, 0.0, 15, - integrate_1d_gk_test::msgs, thetas, x_r, x_i) + f, a, b, tolerance, 0.0, 15, msgs, thetas, x_r, x_i) - val), tolerance); // Flip the domain of integration and check that the integral matches @@ -227,14 +224,16 @@ inline void test_integration(const F &f, double a, double b, const std::vector &x_i) { return f(-x, -xc, msgs, theta, x_r, x_i); }; - EXPECT_LE(std::abs(integrate_1d_gauss_kronrod_tol( - flipped, -b, -a, tolerance, 0.0, 15, - integrate_1d_gk_test::msgs, thetas, x_r, x_i) - - val), - tolerance); + EXPECT_LE( + std::abs(integrate_1d_gauss_kronrod_tol(flipped, -b, -a, tolerance, 0.0, + 15, msgs, thetas, x_r, x_i) + - val), + tolerance); } } +} // namespace integrate_1d_gk_test + TEST(StanMath_integrate_1d_gk_prim, TestThrows) { // Left limit of integration must be less than or equal to right limit EXPECT_THROW(stan::math::integrate_1d_gauss_kronrod_tol( @@ -310,44 +309,49 @@ TEST(StanMath_integrate_1d_gk_prim, test_integer_arguments) { TEST(StanMath_integrate_1d_gk_prim, test1_smooth) { // Zero-crossing integral + limit at infinity (smooth exponential decay) - test_integration(integrate_1d_gk_test::f3{}, -2.0, - std::numeric_limits::infinity(), {}, {}, {}, - 7.38905609893065); + integrate_1d_gk_test::test_integration( + integrate_1d_gk_test::f3{}, -2.0, std::numeric_limits::infinity(), + {}, {}, {}, 7.38905609893065); // Easy integrals - test_integration(integrate_1d_gk_test::f4{}, 0.2, 0.7, {0.5}, - std::vector{}, std::vector{}, - 1.0423499493102901); - test_integration(integrate_1d_gk_test::f5{}, -0.2, 0.7, {0.4, 0.4}, - std::vector{}, std::vector{}, - 1.396621954392482); + integrate_1d_gk_test::test_integration( + integrate_1d_gk_test::f4{}, 0.2, 0.7, {0.5}, std::vector{}, + std::vector{}, 1.0423499493102901); + integrate_1d_gk_test::test_integration(integrate_1d_gk_test::f5{}, -0.2, 0.7, + {0.4, 0.4}, std::vector{}, + std::vector{}, 1.396621954392482); // Zero-length intervals - test_integration(integrate_1d_gk_test::f4{}, 0.0, 0.0, {0.5}, - std::vector{}, std::vector{}, 0.0); - test_integration(integrate_1d_gk_test::f5{}, 1.0, 1.0, {0.4, 0.4}, - std::vector{}, std::vector{}, 0.0); + integrate_1d_gk_test::test_integration(integrate_1d_gk_test::f4{}, 0.0, 0.0, + {0.5}, std::vector{}, + std::vector{}, 0.0); + integrate_1d_gk_test::test_integration(integrate_1d_gk_test::f5{}, 1.0, 1.0, + {0.4, 0.4}, std::vector{}, + std::vector{}, 0.0); // Test x_i - test_integration(integrate_1d_gk_test::f6{}, -0.2, 2.9, {6.0, 5.1}, {}, {4}, - 4131.985414616364); + integrate_1d_gk_test::test_integration(integrate_1d_gk_test::f6{}, -0.2, 2.9, + {6.0, 5.1}, {}, {4}, + 4131.985414616364); // Test x_r - test_integration(integrate_1d_gk_test::f7{}, -0.2, 2.9, {}, {4.0, 6.0, 5.1}, - {}, 24219.985414616367); + integrate_1d_gk_test::test_integration(integrate_1d_gk_test::f7{}, -0.2, 2.9, + {}, {4.0, 6.0, 5.1}, {}, + 24219.985414616367); // Both limits at infinity + test x_r/x_i (smooth Gaussian-shaped) - test_integration(integrate_1d_gk_test::f8{}, - -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {5.0}, {1.7}, {2}, - 3.013171546539377); + integrate_1d_gk_test::test_integration( + integrate_1d_gk_test::f8{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {5.0}, {1.7}, {2}, + 3.013171546539377); // Both limits at infinity + test x_i (smooth rational on R) - test_integration(integrate_1d_gk_test::f9{}, - -std::numeric_limits::infinity(), - std::numeric_limits::infinity(), {1.3}, {}, {4}, - 2.372032924895055); + integrate_1d_gk_test::test_integration( + integrate_1d_gk_test::f9{}, -std::numeric_limits::infinity(), + std::numeric_limits::infinity(), {1.3}, {}, {4}, + 2.372032924895055); // Smooth oscillation - test_integration(integrate_1d_gk_test::f16{}, 0.0, stan::math::pi(), {}, {}, - {}, stan::math::square(stan::math::pi()) / 4); + integrate_1d_gk_test::test_integration( + integrate_1d_gk_test::f16{}, 0.0, stan::math::pi(), {}, {}, {}, + stan::math::square(stan::math::pi()) / 4); // Bounds working right (Gaussian PDF tail integral) - test_integration(integrate_1d_gk_test::f17{}, - -std::numeric_limits::infinity(), -1.5, {0.0, 1.0}, - {}, {}, 0.066807201268858071); + integrate_1d_gk_test::test_integration( + integrate_1d_gk_test::f17{}, -std::numeric_limits::infinity(), + -1.5, {0.0, 1.0}, {}, {}, 0.066807201268858071); } // Demonstrate the known weakness of Gauss-Kronrod: integrands with algebraic