Skip to content

Commit 24e2e19

Browse files
committed
update finit diff back to original
1 parent 22a2210 commit 24e2e19

8 files changed

+83
-23
lines changed

stan/math/mix/functor/finite_diff_grad_hessian_auto.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ inline void finite_diff_grad_hessian_auto(
5858

5959
for (int i = 0; i < d; ++i) {
6060
double dummy_fx_eval;
61-
double epsilon = finite_diff_stepsize(x(i));
61+
double epsilon = finite_diff_stepsize<2>(x(i));
6262
hess_diff.setZero();
6363

6464
x_temp(i) = x(i) + 2 * epsilon;

stan/math/prim/fun/finite_diff_stepsize.hpp

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,82 @@
88
namespace stan {
99
namespace math {
1010

11+
#include <cmath>
12+
#include <limits>
13+
#include <algorithm>
14+
15+
namespace internal {
16+
template <std::size_t StencilOrder = 2, typename T>
17+
inline constexpr auto eps_root_calc() {
18+
constexpr T eps = std::numeric_limits<T>::epsilon();
19+
if constexpr (StencilOrder == 2) {
20+
// ε^(1/3)
21+
return std::cbrt(eps);
22+
} else if constexpr (StencilOrder == 3) {
23+
// ε^(1/4) = sqrt(sqrt(ε))
24+
return std::sqrt(std::sqrt(eps));
25+
} else if constexpr (StencilOrder == 5) {
26+
// ε^(1/6) = sqrt(cbrt(ε))
27+
return std::sqrt(std::cbrt(eps));
28+
} else {
29+
// General fallback: ε^(1/(p+1))
30+
return std::pow(eps, T(1) / T(StencilOrder + 1));
31+
}
32+
}
33+
}
1134
/**
12-
* Return the stepsize for finite difference evaluations at the
13-
* specified scalar.
35+
* @brief Compute a finite-difference step size suitable for a stencil with
36+
* leading truncation order \p StencilOrder.
37+
*
38+
* This implements the standard balance between truncation error
39+
* \f$A\,h^{p}\f$ and floating-point rounding error \f$B\,\varepsilon/h\f$,
40+
* whose minimizer is \f$h_\star \propto \varepsilon^{1/(p+1)}\f$, where
41+
* \f$p = \texttt{StencilOrder}\f$ and \f$\varepsilon\f$ is machine epsilon
42+
* for the scalar type \p T. We additionally scale by \f$\max(1, |u|)\f$ to
43+
* obtain an absolute step size near the point being perturbed.
44+
*
45+
* For the common second-order (3-point central) stencil, the function uses an
46+
* `if constexpr` branch to compute \f$\varepsilon^{1/3}\f$ via `std::cbrt`
47+
* (avoids a `pow` and is numerically tidy). For higher orders it uses
48+
* \f$\varepsilon^{1/(p+1)}\f` via `std::pow`.
49+
*
50+
* @tparam T Floating-point scalar type (e.g., float, double).
51+
* @tparam StencilOrder Leading truncation order \f$p\f$ of your stencil:
52+
* - first-derivative 3-point central → \f$p=2\f$
53+
* - first-derivative 5-point central → \f$p=4\f$
54+
* - first-derivative 6-point central → \f$p=6\f$
55+
* - “derivative of Hessian” via 5-point diff → \f$p=4\f$
56+
*
57+
* @param u The coordinate value (or local scale) at which the step will be
58+
* applied; the step is scaled by \f$\max(1, |u|)\f$.
59+
* @param c Dimensionless tuning constant multiplying the theoretically
60+
* optimal step. Default is 1.0. In practice, \f$c \in [0.5, 2]\f$
61+
* works well:
62+
* - Increase \p c (larger h) if round-off/cancellation dominates
63+
* (noisy function values, very large |f|, many subtractions).
64+
* - Decrease \p c (smaller h) if truncation dominates (very smooth
65+
* function with large higher derivatives or low curvature scale).
66+
* If your problem has a known physical scale S (not |u|), prefer
67+
* passing \p u scaled by S or modify the scaling accordingly.
1468
*
15-
* <p>The formula used is `stepsize(u) = cbrt(epsilon) * max(1,
16-
* abs(u)).`
69+
* @return A step size \f$h\f$ such that \f$u+h \neq u\f$; if the theoretical
70+
* step underflows at \p u, the function falls back to the next
71+
* representable increment.
1772
*
18-
* @param u initial value to increment
19-
* @return stepsize away from u for finite differences
73+
* @note The step computed here assumes smooth (at least \(p{+}1\) times
74+
* differentiable) behavior along the perturbed coordinate.
2075
*/
21-
inline double finite_diff_stepsize(double u) {
22-
using std::fabs;
23-
static const double eps = std::numeric_limits<double>::epsilon();
24-
static const double eps_pow
25-
= std::pow(eps, 1.0 / 7.0); // for 6th-order stencil
26-
return eps_pow * std::fmax(1.0, fabs(u));
76+
template <std::size_t StencilOrder = 2, typename T>
77+
inline T finite_diff_stepsize(T u, T c = T(1)) {
78+
const T scale = std::max(T(1), std::abs(u));
79+
const T eps_root = internal::eps_root_calc<StencilOrder, T>();
80+
const T h = c * eps_root * scale;
81+
// Ensure perturbation isn’t rounded away at u.
82+
if (u + h == u) {
83+
const T next = std::nextafter(u, std::numeric_limits<T>::infinity());
84+
return std::max(h, next - u);
85+
}
86+
return h;
2787
}
2888

2989
} // namespace math

stan/math/prim/functor/finite_diff_gradient_auto.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ inline void finite_diff_gradient_auto(const F& f, VectorT&& x, ScalarT& fx,
5959
Eigen::Map<EigT> grad_map(grad_fx.data(), grad_fx.size());
6060

6161
grad_map = EigT::NullaryExpr(x.size(), [&f, &x](Eigen::Index i) {
62-
double h = finite_diff_stepsize(value_of_rec(x[i]));
62+
double h = finite_diff_stepsize<2>(value_of_rec(x[i]));
6363
ScalarT delta_f = 0;
6464
for (int j = 0; j < 6; ++j) {
6565
auto x_temp

test/unit/math/laplace/laplace_marginal_bernoulli_logit_lpmf_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ TEST(laplace_marginal_bernoulli_logit_lpmf, phi_dim500) {
3939
constexpr double tolerance = 1e-12;
4040
constexpr int max_num_steps = 1000;
4141
constexpr stan::test::ad_tolerances tols{
42-
stan::test::ad_gradient_tols{1e-8, 1e-4}};
42+
stan::test::ad_gradient_tols{1e-8, 1e-3}};
4343
stan::math::test::run_solver_grid(
4444
[&](int solver_num, int hessian_block_size, int max_steps_line_search,
4545
auto&& theta_0) {

test/unit/math/laplace/laplace_marginal_lpdf_test.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ TEST(laplace, poisson_log_phi_dim_2) {
7474
using stan::is_var_v;
7575
using stan::scalar_type_t;
7676
constexpr stan::test::ad_tolerances tols{
77-
stan::test::ad_gradient_tols{1e-8, 1e-4}};
77+
stan::test::ad_gradient_tols{1e-8, 1e-3}};
7878
// tols.gradient_grad_ = 1e-3;
7979
stan::math::test::run_solver_grid(
8080
[&](int solver_num, int hessian_block_size, int max_steps_line_search,
@@ -180,7 +180,8 @@ TEST(laplace, bernoulli_logit_phi_dim500) {
180180
// All fail for ad check with relative tolerance ~0.002
181181
constexpr double tolerance = 1e-12;
182182
constexpr int max_num_steps = 100;
183-
constexpr stan::test::ad_tolerances tols;
183+
constexpr stan::test::ad_tolerances tols{
184+
stan::test::ad_gradient_tols{1e-8, 5e-3}};
184185
stan::math::test::run_solver_grid(
185186
[&](int solver_num, int hessian_block_size, int max_steps_line_search,
186187
auto&& theta_0) {

test/unit/math/laplace/laplace_marginal_neg_binomial_log_lpmf_test.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ TEST(laplace_marginal_neg_binomial_log_lpmf, phi_dim_2) {
3232
constexpr double tolerance = 1e-12;
3333
constexpr int max_num_steps = 1000;
3434
constexpr stan::test::ad_tolerances tols{
35-
stan::test::ad_gradient_tols{1e-8, 1e-4}};
36-
// tols.gradient_grad_ = 1e-2;
35+
stan::test::ad_gradient_tols{1e-8, 1e-2}};
3736
stan::math::test::run_solver_grid(
3837
[&](int solver_num, int hessian_block_size, int max_steps_line_search,
3938
auto&& theta_0) {

test/unit/math/laplace/laplace_marginal_poisson_log_lpmf_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ TEST(laplace_marginal_poisson_log_lpmf, log_phi_dim_2) {
9393
// stan::test::ad_tolerances tols;
9494
// tols.gradient_val_ = 1e-3;
9595
constexpr stan::test::ad_tolerances tols{
96-
stan::test::ad_gradient_tols{1e-8, 1e-4}};
96+
stan::test::ad_gradient_tols{1e-8, 1e-3}};
9797

9898
// tols.gradient_grad_ = 1e-3;
9999
Eigen::VectorXd ye(2);

test/unit/math/laplace/laplace_types_test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ TEST(laplace, poisson_log_phi_dim_2_tuple_extended) {
137137
using stan::math::test::laplace_issue;
138138
constexpr std::array known_issues{laplace_issue{0, 0, 0}};
139139
constexpr stan::test::ad_tolerances tols{
140-
stan::test::ad_gradient_tols{1e-8, 1e-4}};
140+
stan::test::ad_gradient_tols{1e-8, 1e-3}};
141141
// stan::test::ad_tolerances tols;
142142
// tols.gradient_grad_ = 1e-1;
143143
stan::math::test::run_solver_grid(
@@ -193,7 +193,7 @@ TEST(laplace, poisson_log_phi_dim_2_tuple) {
193193
using stan::math::test::laplace_issue;
194194
constexpr std::array known_issues{laplace_issue{0, 0, 0}};
195195
constexpr stan::test::ad_tolerances tols{
196-
stan::test::ad_gradient_tols{1e-8, 1e-4}};
196+
stan::test::ad_gradient_tols{1e-8, 1e-3}};
197197
// stan::test::ad_tolerances tols;
198198
// tols.gradient_grad_ = 1e-1;
199199
stan::math::test::run_solver_grid(
@@ -272,7 +272,7 @@ TEST(laplace, poisson_log_phi_dim_2_array_tuple) {
272272
using stan::math::test::laplace_issue;
273273
constexpr std::array known_issues{laplace_issue{0, 0, 0}};
274274
constexpr stan::test::ad_tolerances tols{
275-
stan::test::ad_gradient_tols{1e-8, 1e-4}};
275+
stan::test::ad_gradient_tols{1e-8, 1e-3}};
276276
// stan::test::ad_tolerances tols;
277277
// tols.gradient_grad_ = 1e-1;
278278
stan::math::test::run_solver_grid(

0 commit comments

Comments
 (0)