Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
43c3eef
update the laplace line search to use a more advanced wolfe line sear…
SteveBronder Sep 29, 2025
74a92bc
Merge remote-tracking branch 'origin/develop' into fix/laplace-wolfe-…
SteveBronder Oct 6, 2025
2de7a4f
add data for roach data test
SteveBronder Oct 6, 2025
547288e
update tests
SteveBronder Oct 6, 2025
cb5e282
move wolfe to its own file
SteveBronder Oct 6, 2025
cdaf700
Merge remote-tracking branch 'origin' into fix/laplace-wolfe-line-search
SteveBronder Oct 7, 2025
6b22c85
update wolfe
SteveBronder Oct 9, 2025
a1d0906
Merge remote-tracking branch 'origin' into fix/laplace-wolfe-line-search
SteveBronder Oct 9, 2025
5b6ffff
update to use barzilai borwein step size as initial step size estimate
SteveBronder Oct 9, 2025
8eff766
seperate moto from other lpdf tests
SteveBronder Oct 13, 2025
f542cc5
update
SteveBronder Oct 13, 2025
c845944
add WolfeInfo
SteveBronder Oct 13, 2025
40f1243
use WolfeInfo for extra data
SteveBronder Oct 13, 2025
6e528d2
put everything for iterations in laplace into structs
SteveBronder Oct 14, 2025
d89eeb5
update poisson test
SteveBronder Oct 15, 2025
40d889f
add swap functions
SteveBronder Oct 15, 2025
59b7a2f
cleanup laplace_density_est to reduce repeated code
SteveBronder Oct 17, 2025
c73f5aa
update to search for a good initial alpha on a space
SteveBronder Oct 17, 2025
773d417
fix code for wolfe line search
SteveBronder Oct 17, 2025
b557dad
update tests for zoom
SteveBronder Oct 19, 2025
b18bf87
all tests pass for laplace with new wolfe
SteveBronder Oct 20, 2025
98df588
use log sum of diagonal of U matrix for solver 3 determinant
SteveBronder Oct 20, 2025
929dd47
move update_step to be a user passed function
SteveBronder Oct 20, 2025
2ebb01a
cleanup the laplace code to remove some passed by reference values to…
SteveBronder Oct 21, 2025
3bbcef3
cleanup the laplace code to remove some passed by reference values to…
SteveBronder Oct 21, 2025
66ffec9
update WolfeData with member accessors and use Eval within WolfeData
SteveBronder Oct 21, 2025
ff5bee4
update docs for wolfe
SteveBronder Oct 21, 2025
7a7415a
update logic in laplace_marginal_desntiy_est so that final updated va…
SteveBronder Oct 22, 2025
973144a
clang format
SteveBronder Oct 22, 2025
cc5d49a
change stepsize of finite difference to use 6th order instead of 2nd …
SteveBronder Oct 23, 2025
d759fdd
change moto test gradient relative error
SteveBronder Oct 23, 2025
7b4e3a1
update wolfe tests
SteveBronder Oct 23, 2025
dfba08b
allow user to set max number of line search iterations.
SteveBronder Oct 24, 2025
7df0ed1
remove extra copy is laplace_likelihood::theta_grad
SteveBronder Oct 24, 2025
0c92732
cleanup and doc
SteveBronder Oct 24, 2025
d19ee8b
clang format
SteveBronder Oct 24, 2025
82e43da
Merge commit '5e698970d52ee9cfe85630cca9794e43ec829cf2' into HEAD
yashikno Oct 24, 2025
22a2210
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Oct 24, 2025
24e2e19
update finit diff back to original
SteveBronder Oct 27, 2025
7720c7a
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Oct 27, 2025
63e1700
fix finite_diff_stepsize and lower tolerance for AD tests on inv_Phi,…
SteveBronder Oct 28, 2025
a9f17d4
Merge commit 'b82d68ced2e73c8188f3bbf287c1321033103986' into HEAD
yashikno Oct 28, 2025
28c44dd
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Oct 28, 2025
fddf54f
cpplint fixes
SteveBronder Oct 28, 2025
113e2b1
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Oct 28, 2025
88a8950
fix doxygen docs
SteveBronder Oct 28, 2025
c4fcba2
Merge remote-tracking branch 'refs/remotes/origin/fix/wolfe-zoom1' in…
SteveBronder Oct 28, 2025
521145f
update moto tests to not take the gradient with respect to y as some …
SteveBronder Oct 28, 2025
d648ee0
update laplace_latent_tol_bernoulli_logit_rng user options orderings.…
SteveBronder Oct 29, 2025
7778307
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Oct 29, 2025
04b5b2e
handle NA values for obj and grad. Allow for zero line search. allow …
SteveBronder Nov 17, 2025
4117a31
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Nov 17, 2025
affabfa
update step iter
SteveBronder Dec 1, 2025
a143355
cleanup zoom in wolfe line search
SteveBronder Dec 1, 2025
95c21d5
Merge commit '85c147ee6adbe58eb9ae1578c0478fcf3da9bf76' into HEAD
yashikno Dec 1, 2025
5ba7426
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Dec 1, 2025
475c632
update poisson_log_lpmf test to use google test parameterization
SteveBronder Dec 2, 2025
307fb0c
add throw testing in neg_binomial_log_summary
SteveBronder Dec 2, 2025
5038198
breakout the laplace tests so they print nicely
SteveBronder Dec 2, 2025
7e9af37
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Dec 2, 2025
863223e
fix cpplint
SteveBronder Dec 3, 2025
04197f2
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Dec 3, 2025
c8a1613
address partial review comments
SteveBronder Dec 17, 2025
aeb1662
Merge commit 'a5f80224b857e06dd7ca753d826e5b292ee8e73c' into HEAD
yashikno Dec 17, 2025
ea9ffe0
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Dec 17, 2025
7f7bbb2
update refactor for laplace_marginal_density_est
SteveBronder Jan 5, 2026
66e8470
update with initial shim
SteveBronder Jan 5, 2026
6acdd09
update with initial shim
SteveBronder Jan 6, 2026
9dc118e
update with initial shim
SteveBronder Jan 6, 2026
b9a493a
update with initial shim
SteveBronder Jan 6, 2026
7c886f5
update with initial shim
SteveBronder Jan 6, 2026
5eb664b
update with initial shim
SteveBronder Jan 6, 2026
1f7ff3c
update with initial shim
SteveBronder Jan 6, 2026
65914d3
update with initial shim
SteveBronder Jan 6, 2026
6b74be8
update with initial shim
SteveBronder Jan 6, 2026
e9cad2e
update
SteveBronder Jan 6, 2026
db4f677
Merge commit 'a6932f2a8ff8d1800c7020d29593b5934a20e40b' into HEAD
yashikno Jan 6, 2026
aa73b5d
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 6, 2026
cf151fa
remove extra comments
SteveBronder Jan 6, 2026
0022b41
Moves many of the functions built for laplace into seperate files for…
SteveBronder Jan 6, 2026
0e1b7f4
Merge commit '52f7e9244d5367b74e4b4274d2a1c0e9f15a23f9' into HEAD
yashikno Jan 6, 2026
d26626f
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 6, 2026
a2a0a99
add initializers to solvers for laplace
SteveBronder Jan 7, 2026
a4f0b7d
fix docs for laplace_marginal_density_est
SteveBronder Jan 7, 2026
4c884dd
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 7, 2026
e6b2d74
minor cleanup
SteveBronder Jan 8, 2026
27d2fc9
minor cleanup
SteveBronder Jan 8, 2026
f845a43
cleanup retry logic on NaN of Inf objective function values
SteveBronder Jan 8, 2026
200fd9a
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 8, 2026
d6859f1
adds fallthrough option in user opts. User ops are now a tuple the us…
SteveBronder Jan 9, 2026
d2de83d
Merge remote-tracking branch 'refs/remotes/origin/fix/wolfe-zoom1' in…
SteveBronder Jan 9, 2026
7f1a995
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 9, 2026
d86fa69
update docs
SteveBronder Jan 9, 2026
6abd9da
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 9, 2026
b30823d
remove .csv files for .hpp files
SteveBronder Jan 9, 2026
dd1e74e
Merge remote-tracking branch 'refs/remotes/origin/fix/wolfe-zoom1' in…
SteveBronder Jan 9, 2026
b292cae
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 9, 2026
ed3ce3a
make non-static so it's memory is allocated dynamically.
SteveBronder Jan 12, 2026
d9319ad
remove rev/core/set_zero_adjoints.hpp
SteveBronder Jan 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions doxygen/doxygen.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2743,15 +2743,17 @@ MSCGEN_TOOL =
MSCFILE_DIRS =

ALIASES += laplace_options="\
\param[in] theta_0 the initial guess for the Laplace approximation. \
\param[in] tolerance controls the convergence criterion when finding the mode in the Laplace approximation. \
\param[in] max_num_steps maximum number of steps before the Newton solver breaks and returns an error. \
\param[in] hessian_block_size Block size of Hessian of log likelihood w.r.t latent Gaussian variable theta. \
\param[in] solver Type of Newton solver. Each corresponds to a distinct choice of B matrix (i.e. application SWM formula): \
1. computes square-root of negative Hessian. \
2. computes square-root of covariance matrix. \
3. computes no square-root and uses LU decomposition. \
\param[in] max_steps_line_search Number of steps after which the algorithm gives up on doing a line search. If 0, no linesearch. \
\param[in] ops Options for controlling Laplace approximation. The following options are available: \
- theta_0 the initial guess for the Laplace approximation. \
- tolerance controls the convergence criterion when finding the mode in the Laplace approximation. \
- max_num_steps maximum number of steps before the Newton solver breaks and returns an error. \
- hessian_block_size Block size of Hessian of log likelihood w.r.t latent Gaussian variable theta. \
- solver Type of Newton solver. Each corresponds to a distinct choice of B matrix (i.e. application SWM formula): \
1. computes square-root of negative Hessian. \
2. computes square-root of covariance matrix. \
3. computes no square-root and uses LU decomposition. \
- max_steps_line_search Number of steps after which the algorithm gives up on doing a line search. If 0, no linesearch. \
- allow_fallthrough If true, if solver 1 fails then solver 2 is tried, and if that fails solver 3 is tried. \
"

ALIASES += laplace_common_template_args="\
Expand All @@ -2761,6 +2763,7 @@ ALIASES += laplace_common_template_args="\
should be a type inheriting from `Eigen::EigenBase` with dynamic sized \
rows and columns. \
\tparam CovarArgs A tuple of types to passed as the first arguments of `CovarFun::operator()`\
\tparam OpsTuple A tuple of laplace_options types \
"

ALIASES += laplace_common_args="\
Expand Down
124 changes: 124 additions & 0 deletions stan/math/mix/functor/barzilai_borwein_step_size.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#ifndef STAN_MATH_MIX_FUNCTOR_BARZILAI_BORWEIN_STEP_SIZE_HPP
#define STAN_MATH_MIX_FUNCTOR_BARZILAI_BORWEIN_STEP_SIZE_HPP
#include <stan/math/prim/fun/Eigen.hpp>
#include <algorithm>
#include <numeric>
#include <cmath>

namespace stan::math::internal {
/**
* @brief Curvature-aware Barzilai–Borwein (BB) step length with robust
* safeguards.
*
* Given successive parameter displacements \f$s = x_k - x_{k-1}\f$ and
* gradients \f$g_k\f$, \f$g_{k-1}\f$, this routine forms
* \f$y = g_k - g_{k-1}\f$ and computes the two classical BB candidates
*
* \f{align*}{
* \alpha_{\text{BB1}} &= \frac{\langle s,s\rangle}{\langle s,y\rangle},\\
* \alpha_{\text{BB2}} &= \frac{\langle s,y\rangle}{\langle y,y\rangle},
* \f}
*
* then chooses between them using the **spectral cosine**
* \f$r = \cos^2\!\angle(s,y) = \dfrac{\langle s,y\rangle^2}
* {\langle s,s\rangle\,\langle
* y,y\rangle}\in[0,1]\f$:
*
* - if \f$r > 0.9\f$ (well-aligned curvature) and the previous line search
* did **≤ 1** backtrack, prefer the “long” step \f$\alpha_{\text{BB1}}\f$;
* - if \f$0.1 \le r \le 0.9\f$, take the neutral geometric mean
* \f$\sqrt{\alpha_{\text{BB1}}\alpha_{\text{BB2}}}\f$;
* - otherwise default to the “short” step \f$\alpha_{\text{BB2}}\f$.
*
* All candidates are clamped into \f$[\text{min\_alpha},\,\text{max\_alpha}]\f$
* and must be finite and positive.
* If the curvature scalars are ill-posed (non-finite or too small),
* \f$\langle s,y\rangle \le \varepsilon\f$, or if `last_backtracks == 99`
* (explicitly disabling BB for this iteration), the function falls back to a
* **safe** step:
* use `prev_step` when finite and positive, otherwise \f$1.0\f$, then clamp to
* \f$[\text{min\_alpha},\,\text{max\_alpha}]\f$.
*
* @param s Displacement between consecutive iterates
* (\f$s = x_k - x_{k-1}\f$).
* @param g_curr Gradient at the current iterate \f$g_k\f$.
* @param g_prev Gradient at the previous iterate \f$g_{k-1}\f$.
* @param prev_step Previously accepted step length (used by the fallback).
* @param last_backtracks
* Number of backtracking contractions performed by the most
* recent line search; set to 99 to force the safe fallback.
* @param min_alpha Lower bound for the returned step length.
* @param max_alpha Upper bound for the returned step length.
*
* @return A finite, positive BB-style step length \f$\alpha \in
* [\text{min\_alpha},\,\text{max\_alpha}]\f$ suitable for seeding a
* line search or as a spectral preconditioner scale.
*
* @note Uses \f$\varepsilon=10^{-16}\f$ to guard against division by very
* small curvature terms, and applies `std::abs` to BB ratios to avoid
* negative steps; descent is enforced by the line search.
* @warning The vectors must have identical size. Non-finite inputs yield the
* safe fallback.
*/
inline double barzilai_borwein_step_size(const Eigen::VectorXd& s,
const Eigen::VectorXd& g_curr,
const Eigen::VectorXd& g_prev,
double prev_step, int last_backtracks,
double min_alpha, double max_alpha) {
// Fallbacks
auto safe_fallback = [&]() -> double {
double a = std::clamp(
prev_step > 0.0 && std::isfinite(prev_step) ? prev_step : 1.0,
min_alpha, max_alpha);
return a;
};

const Eigen::VectorXd y = g_curr - g_prev;
const double sty = s.dot(y);
const double sts = s.squaredNorm();
const double yty = y.squaredNorm();

// Basic validity checks
constexpr double eps = 1e-16;
if (!(std::isfinite(sty) && std::isfinite(sts) && std::isfinite(yty))
|| sts <= eps || yty <= eps || sty <= eps || last_backtracks == 99) {
return safe_fallback();
}

// BB candidates
double alpha_bb1 = std::clamp(std::abs(sts / sty), min_alpha, max_alpha);
double alpha_bb2 = std::clamp(std::abs(sty / yty), min_alpha, max_alpha);

// Safeguard candidates
if (!std::isfinite(alpha_bb1) || !std::isfinite(alpha_bb2) || alpha_bb1 <= 0.0
|| alpha_bb2 <= 0.0) {
return safe_fallback();
}

// Spectral cosine r = cos^2(angle(s, y)) in [0,1]
const double r = (sty * sty) / (sts * yty);

// Heuristic thresholds (robust defaults)
constexpr double kLoose = 0.9; // "nice" curvature
constexpr double kTight = 0.1; // "dodgy" curvature

double alpha0 = alpha_bb2; // default to short BB for robustness
if (r > kLoose && last_backtracks <= 1) {
// Spectrum looks friendly and line search was not harsh -> try long BB
alpha0 = alpha_bb1;
} else if (r >= kTight && r <= kLoose) {
// Neither clearly friendly nor clearly dodgy -> neutral middle
alpha0 = std::sqrt(alpha_bb1 * alpha_bb2);
} // else keep alpha_bb2

// Clip to user bounds
alpha0 = std::clamp(alpha0, min_alpha, max_alpha);

if (!std::isfinite(alpha0) || alpha0 <= 0.0) {
return safe_fallback();
}
return alpha0;
}

} // namespace stan::math::internal
#endif
106 changes: 106 additions & 0 deletions stan/math/mix/functor/conditional_copy_and_promote.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#ifndef STAN_MATH_MIX_FUNCTOR_CONDITIONAL_COPY_AND_PROMOTE_HPP
#define STAN_MATH_MIX_FUNCTOR_CONDITIONAL_COPY_AND_PROMOTE_HPP

#include <stan/math/mix/functor/hessian_block_diag.hpp>
#include <stan/math/prim/functor.hpp>
#include <stan/math/prim/fun.hpp>

namespace stan::math::internal {

/**
* Decide if object should be deep or shallow copied when
* using @ref conditional_copy_and_promote .
*/
enum class COPY_TYPE : uint8_t { SHALLOW = 0, DEEP = 1 };

/**
* Conditional copy and promote a type's scalar type to a `PromotedType`.
* @tparam Filter type trait with a static constexpr bool member `value`
* that is true if the type should be promoted. Otherwise, the type is
* left unchanged.
* @tparam PromotedType type to promote the scalar to.
* @tparam CopyType type of copy to perform.
* @tparam Args variadic arguments.
* @param args variadic arguments to conditionally copy and promote.
* @return a tuple where each element is either a reference to the original
* argument or a promoted copy of the argument.
*/
template <template <typename...> class Filter,
typename PromotedType = stan::math::var,
COPY_TYPE CopyType = COPY_TYPE::DEEP, typename... Args>
inline auto conditional_copy_and_promote(Args&&... args) {
return map_if<Filter>(
[](auto&& arg) {
if constexpr (is_tuple_v<decltype(arg)>) {
return stan::math::apply(
[](auto&&... inner_args) {
return make_holder_tuple(
conditional_copy_and_promote<Filter, PromotedType,
CopyType>(
std::forward<decltype(inner_args)>(inner_args))...);
},
std::forward<decltype(arg)>(arg));
} else if constexpr (is_std_vector_v<decltype(arg)>) {
std::vector<decltype(conditional_copy_and_promote<
Filter, PromotedType, CopyType>(arg[0]))>
ret;
for (std::size_t i = 0; i < arg.size(); ++i) {
ret.push_back(
conditional_copy_and_promote<Filter, PromotedType, CopyType>(
arg[i]));
}
return ret;
} else {
if constexpr (CopyType == COPY_TYPE::DEEP) {
return stan::math::eval(promote_scalar<PromotedType>(
value_of_rec(std::forward<decltype(arg)>(arg))));
} else if (CopyType == COPY_TYPE::SHALLOW) {
if constexpr (std::is_same_v<PromotedType,
scalar_type_t<decltype(arg)>>) {
return std::forward<decltype(arg)>(arg);
} else {
return stan::math::eval(promote_scalar<PromotedType>(
std::forward<decltype(arg)>(arg)));
}
}
}
},
std::forward<Args>(args)...);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: should the internal namespace end here? deep/shallow copy look much more like normal functions than conditional_copy_and_promote does

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only used in other internal functions so I think it is better to have in internal

/**
* Conditional deep copy types with a `var` scalar type to `PromotedType`.
* @tparam PromotedType type to promote the scalar to.
* @tparam Args variadic arguments.
* @param args variadic arguments to conditionally copy and promote.
* @return a tuple where each element is either a reference to the original
* argument or a promoted copy of the argument.
*/
template <typename PromotedType, typename... Args>
inline auto deep_copy_vargs(Args&&... args) {
return conditional_copy_and_promote<is_any_var_scalar, PromotedType,
COPY_TYPE::DEEP>(
std::forward<Args>(args)...);
}

/**
* Conditional shallow copy types with a `var` scalar type to `PromotedType`.
* @note This function is useful whenever you are inside of nested autodiff
* and want to allow the input arguments from an outer autodiff to be used
* in an inner autodiff without making a hard copy of the input arguments.
* @tparam PromotedType type to promote the scalar to.
* @tparam Args variadic arguments.
* @param args variadic arguments to conditionally copy and promote.
* @return a tuple where each element is either a reference to the original
* argument or a promoted copy of the argument.
*/
template <typename PromotedType, typename... Args>
inline auto shallow_copy_vargs(Args&&... args) {
return conditional_copy_and_promote<is_any_var_scalar, PromotedType,
COPY_TYPE::SHALLOW>(
std::forward<Args>(args)...);
}

} // namespace stan::math::internal

#endif
11 changes: 6 additions & 5 deletions stan/math/mix/functor/laplace_base_rng.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ inline Eigen::VectorXd laplace_base_rng(
LLFunc&& ll_fun, LLArgs&& ll_args, CovarFun&& covariance_function,
CovarArgs&& covar_args, const laplace_options<InitTheta>& options, RNG& rng,
std::ostream* msgs) {
Eigen::MatrixXd covariance_train = stan::math::apply(
[msgs, &covariance_function](auto&&... args) {
return covariance_function(std::forward<decltype(args)>(args)..., msgs);
},
std::forward<CovarArgs>(covar_args));
auto md_est = internal::laplace_marginal_density_est(
ll_fun, std::forward<LLArgs>(ll_args),
std::forward<CovarFun>(covariance_function),
to_ref(std::forward<CovarArgs>(covar_args)), options, msgs);
// Modified R&W method
auto&& covariance_train = md_est.covariance;
ll_fun, std::forward<LLArgs>(ll_args), covariance_train, options, msgs);
Eigen::VectorXd mean_train = covariance_train * md_est.theta_grad;
if (options.solver == 1 || options.solver == 2) {
Eigen::MatrixXd V_dec
Expand Down
Loading