1
- #ifndef STAN_MATH_PRIM_FUNCTOR_integrate_1d_HPP
2
- #define STAN_MATH_PRIM_FUNCTOR_integrate_1d_HPP
1
+ #ifndef STAN_MATH_PRIM_FUNCTOR_INTEGRATE_1D_HPP
2
+ #define STAN_MATH_PRIM_FUNCTOR_INTEGRATE_1D_HPP
3
3
4
4
#include < stan/math/prim/meta.hpp>
5
5
#include < stan/math/prim/err.hpp>
6
6
#include < stan/math/prim/fun/constants.hpp>
7
+ #include < stan/math/prim/functor/integrate_1d_adapter.hpp>
7
8
#include < boost/math/quadrature/exp_sinh.hpp>
8
9
#include < boost/math/quadrature/sinh_sinh.hpp>
9
10
#include < boost/math/quadrature/tanh_sinh.hpp>
@@ -50,17 +51,53 @@ namespace math {
50
51
template <typename F>
51
52
inline double integrate (const F& f, double a, double b,
52
53
double relative_tolerance) {
54
+ static constexpr const char * function = " integrate" ;
53
55
double error1 = 0.0 ;
54
56
double error2 = 0.0 ;
55
57
double L1 = 0.0 ;
56
58
double L2 = 0.0 ;
57
- bool used_two_integrals = false ;
58
59
size_t levels;
59
- double Q = 0.0 ;
60
- auto f_wrap = [&](double x) { return f (x, NOT_A_NUMBER); };
60
+
61
+ auto one_integral_convergence_check = [](auto & error1, auto & rel_tol,
62
+ auto & L1) {
63
+ if (error1 > rel_tol * L1) {
64
+ [error1]() STAN_COLD_PATH {
65
+ throw_domain_error (
66
+ function, " error estimate of integral" , error1, " " ,
67
+ " exceeds the given relative tolerance times norm of integral" );
68
+ }();
69
+ }
70
+ };
71
+
72
+ auto two_integral_convergence_check
73
+ = [](auto & error1, auto & error2, auto & rel_tol, auto & L1, auto & L2) {
74
+ if (error1 > rel_tol * L1) {
75
+ [error1]() STAN_COLD_PATH {
76
+ throw_domain_error (
77
+ function, " error estimate of integral below zero" , error1, " " ,
78
+ " exceeds the given relative tolerance times norm of "
79
+ " integral below zero" );
80
+ }();
81
+ }
82
+ if (error2 > rel_tol * L2) {
83
+ [error2]() STAN_COLD_PATH {
84
+ throw_domain_error (
85
+ function, " error estimate of integral above zero" , error2, " " ,
86
+ " exceeds the given relative tolerance times norm of "
87
+ " integral above zero" );
88
+ }();
89
+ }
90
+ };
91
+
92
+ // if a or b is infinite, set xc argument to NaN (see docs above for user
93
+ // function for xc info)
94
+ auto f_wrap = [&f](double x) { return f (x, NOT_A_NUMBER); };
61
95
if (std::isinf (a) && std::isinf (b)) {
62
96
boost::math::quadrature::sinh_sinh<double > integrator;
63
- Q = integrator.integrate (f_wrap, relative_tolerance, &error1, &L1, &levels);
97
+ double Q = integrator.integrate (f_wrap, relative_tolerance, &error1, &L1,
98
+ &levels);
99
+ one_integral_convergence_check (error1, relative_tolerance, L1);
100
+ return Q;
64
101
} else if (std::isinf (a)) {
65
102
boost::math::quadrature::exp_sinh<double > integrator;
66
103
/* *
@@ -69,66 +106,88 @@ inline double integrate(const F& f, double a, double b,
69
106
* https://www.boost.org/doc/libs/1_66_0/libs/math/doc/html/math_toolkit/double_exponential/de_caveats.html)
70
107
*/
71
108
if (b <= 0.0 ) {
72
- Q = integrator.integrate (f_wrap, a, b, relative_tolerance, &error1, &L1,
73
- &levels);
109
+ double Q = integrator.integrate (f_wrap, a, b, relative_tolerance, &error1,
110
+ &L1, &levels);
111
+ one_integral_convergence_check (error1, relative_tolerance, L1);
112
+ return Q;
74
113
} else {
75
114
boost::math::quadrature::tanh_sinh<double > integrator_right;
76
- Q = integrator.integrate (f_wrap, a, 0.0 , relative_tolerance, &error1, &L1,
77
- &levels)
78
- + integrator_right.integrate (f_wrap, 0.0 , b, relative_tolerance,
79
- &error2, &L2, &levels);
80
- used_two_integrals = true ;
115
+ double Q
116
+ = integrator.integrate (f_wrap, a, 0.0 , relative_tolerance, &error1,
117
+ &L1, &levels)
118
+ + integrator_right.integrate (f_wrap, 0.0 , b, relative_tolerance,
119
+ &error2, &L2, &levels);
120
+ two_integral_convergence_check (error1, error2, relative_tolerance, L1,
121
+ L2);
122
+ return Q;
81
123
}
82
124
} else if (std::isinf (b)) {
83
125
boost::math::quadrature::exp_sinh<double > integrator;
84
126
if (a >= 0.0 ) {
85
- Q = integrator.integrate (f_wrap, a, b, relative_tolerance, &error1, &L1,
86
- &levels);
127
+ double Q = integrator.integrate (f_wrap, a, b, relative_tolerance, &error1,
128
+ &L1, &levels);
129
+ one_integral_convergence_check (error1, relative_tolerance, L1);
130
+ return Q;
87
131
} else {
88
132
boost::math::quadrature::tanh_sinh<double > integrator_left;
89
- Q = integrator_left.integrate (f_wrap, a, 0 , relative_tolerance, &error1,
90
- &L1, &levels)
91
- + integrator.integrate (f_wrap, relative_tolerance, &error2, &L2,
92
- &levels);
93
- used_two_integrals = true ;
133
+ double Q = integrator_left.integrate (f_wrap, a, 0 , relative_tolerance,
134
+ &error1, &L1, &levels)
135
+ + integrator.integrate (f_wrap, relative_tolerance, &error2,
136
+ &L2, &levels);
137
+ two_integral_convergence_check (error1, error2, relative_tolerance, L1,
138
+ L2);
139
+ return Q;
94
140
}
95
141
} else {
96
- auto f_wrap = [&](double x, double xc) { return f (x, xc); };
142
+ auto f_wrap = [&f ](double x, double xc) { return f (x, xc); };
97
143
boost::math::quadrature::tanh_sinh<double > integrator;
98
144
if (a < 0.0 && b > 0.0 ) {
99
- Q = integrator.integrate (f_wrap, a, 0.0 , relative_tolerance, &error1, &L1,
100
- &levels)
101
- + integrator.integrate (f_wrap, 0.0 , b, relative_tolerance, &error2,
102
- &L2, &levels);
103
- used_two_integrals = true ;
145
+ double Q = integrator.integrate (f_wrap, a, 0.0 , relative_tolerance,
146
+ &error1, &L1, &levels)
147
+ + integrator.integrate (f_wrap, 0.0 , b, relative_tolerance,
148
+ &error2, &L2, &levels);
149
+ two_integral_convergence_check (error1, error2, relative_tolerance, L1,
150
+ L2);
151
+ return Q;
104
152
} else {
105
- Q = integrator.integrate (f_wrap, a, b, relative_tolerance, &error1, &L1,
106
- &levels);
153
+ double Q = integrator.integrate (f_wrap, a, b, relative_tolerance, &error1,
154
+ &L1, &levels);
155
+ one_integral_convergence_check (error1, relative_tolerance, L1);
156
+ return Q;
107
157
}
108
158
}
159
+ }
109
160
110
- static const char * function = " integrate" ;
111
- if (used_two_integrals) {
112
- if (error1 > relative_tolerance * L1) {
113
- throw_domain_error (function, " error estimate of integral below zero" ,
114
- error1, " " ,
115
- " exceeds the given relative tolerance times norm of "
116
- " integral below zero" );
117
- }
118
- if (error2 > relative_tolerance * L2) {
119
- throw_domain_error (function, " error estimate of integral above zero" ,
120
- error2, " " ,
121
- " exceeds the given relative tolerance times norm of "
122
- " integral above zero" );
161
+ /* *
162
+ * Compute the integral of the single variable function f from a to b to within
163
+ * a specified relative tolerance. a and b can be finite or infinite.
164
+ *
165
+ * @tparam T Type of f
166
+ * @param f the function to be integrated
167
+ * @param a lower limit of integration
168
+ * @param b upper limit of integration
169
+ * @param relative_tolerance tolerance passed to Boost quadrature
170
+ * @param[in, out] msgs the print stream for warning messages
171
+ * @param args additional arguments passed to f
172
+ * @return numeric integral of function f
173
+ */
174
+ template <typename F, typename ... Args,
175
+ require_all_not_st_var<Args...>* = nullptr >
176
+ inline double integrate_1d_impl (const F& f, double a, double b,
177
+ double relative_tolerance, std::ostream* msgs,
178
+ const Args&... args) {
179
+ static constexpr const char * function = " integrate_1d" ;
180
+ check_less_or_equal (function, " lower limit" , a, b);
181
+ if (unlikely (a == b)) {
182
+ if (std::isinf (a)) {
183
+ throw_domain_error (function, " Integration endpoints are both" , a, " " , " " );
123
184
}
185
+ return 0.0 ;
124
186
} else {
125
- if (error1 > relative_tolerance * L1) {
126
- throw_domain_error (
127
- function, " error estimate of integral" , error1, " " ,
128
- " exceeds the given relative tolerance times norm of integral" );
129
- }
187
+ return integrate (
188
+ [&](const auto & x, const auto & xc) { return f (x, xc, msgs, args...); },
189
+ a, b, relative_tolerance);
130
190
}
131
- return Q;
132
191
}
133
192
134
193
/* *
@@ -178,26 +237,14 @@ inline double integrate(const F& f, double a, double b,
178
237
* @return numeric integral of function f
179
238
*/
180
239
template <typename F>
181
- inline double integrate_1d (const F& f, const double a, const double b,
240
+ inline double integrate_1d (const F& f, double a, double b,
182
241
const std::vector<double >& theta,
183
242
const std::vector<double >& x_r,
184
243
const std::vector<int >& x_i, std::ostream* msgs,
185
244
const double relative_tolerance
186
245
= std::sqrt(EPSILON)) {
187
- static const char * function = " integrate_1d" ;
188
- check_less_or_equal (function, " lower limit" , a, b);
189
-
190
- if (a == b) {
191
- if (std::isinf (a)) {
192
- throw_domain_error (function, " Integration endpoints are both" , a, " " , " " );
193
- }
194
- return 0.0 ;
195
- } else {
196
- return integrate (
197
- std::bind<double >(f, std::placeholders::_1, std::placeholders::_2,
198
- theta, x_r, x_i, msgs),
199
- a, b, relative_tolerance);
200
- }
246
+ return integrate_1d_impl (integrate_1d_adapter<F>(f), a, b, relative_tolerance,
247
+ msgs, theta, x_r, x_i);
201
248
}
202
249
203
250
} // namespace math
0 commit comments