Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 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
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
2 changes: 1 addition & 1 deletion stan/math/mix/functor/finite_diff_grad_hessian_auto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ inline void finite_diff_grad_hessian_auto(

for (int i = 0; i < d; ++i) {
double dummy_fx_eval;
double epsilon = finite_diff_stepsize(x(i));
double epsilon = finite_diff_stepsize<2>(x(i));
hess_diff.setZero();

x_temp(i) = x(i) + 2 * epsilon;
Expand Down
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
231 changes: 231 additions & 0 deletions stan/math/mix/functor/laplace_likelihood.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,49 @@
namespace stan {
namespace math {

namespace internal {
/**
* Set all adjoints of the output to zero.
*/
template <typename Output>
inline void set_zero_adjoint(Output&& output) {
if constexpr (is_all_arithmetic_scalar_v<Output>) {
return;
} else {
return iter_tuple_nested(
[](auto&& output_i) {
using output_i_t = std::decay_t<decltype(output_i)>;
if constexpr (is_all_arithmetic_scalar_v<output_i_t>) {
return;
} else if constexpr (is_std_vector<output_i_t>::value) {
for (Eigen::Index i = 0; i < output_i.size(); ++i) {
output_i[i].adj() = 0;
}
} else if constexpr (is_eigen_v<output_i_t>) {
output_i.adj().setZero();
} else if constexpr (is_stan_scalar_v<output_i_t>) {
output_i.adj() = 0;
} else {
static_assert(
sizeof(std::decay_t<output_i_t>*) == 0,
"INTERNAL ERROR:(laplace_marginal_lpdf) set_zero_adjoints was "
"not able to deduce the actions needed for the given type. "
"This is an internal error, please report it: "
"https://github.com/stan-dev/math/issues");
}
},
std::forward<Output>(output));
}
}

} // namespace internal

/**
* functions to compute the log density, first, second,
* and third-order derivatives for a likelihoood specified by the user.
*/
namespace laplace_likelihood {

namespace internal {
/**
* @tparam F A functor with `opertor()(Args&&...)` returning a scalar
Expand Down Expand Up @@ -106,6 +144,126 @@ inline auto shallow_copy_vargs(Args&&... args) {
std::forward<Args>(args)...);
}

/**
* Computes theta gradient `f` wrt `theta` and `args...`
* @note If `Args` contains \ref var types then their adjoints will be
* calculated as a side effect.
* @tparam F A functor with `opertor()(Args&&...)` returning a scalar
* @tparam Theta A class assignable to an Eigen vector type
* @tparam Stream Type of stream for messages.
* @tparam Args Type of variadic arguments.
* @param f Log likelihood function.
* @param theta Latent Gaussian model.
* @param msgs Stream for messages.
* @param args Variadic arguments for the likelihood function.
*/
template <typename F, typename Theta, typename Stream, typename... Args,
require_eigen_vector_vt<std::is_arithmetic, Theta>* = nullptr>
inline auto theta_grad(F&& f, Theta&& theta, Stream* msgs, Args&&... args) {
using Eigen::Dynamic;
using Eigen::Matrix;
nested_rev_autodiff nested;
Matrix<var, Dynamic, 1> theta_var = theta;
var f_var = f(theta_var, args..., msgs);
grad(f_var.vi_);
return theta_var.adj().eval();
}

/**
* Computes likelihood argument gradient of `f`
* @note If `Args` contains \ref var types then their adjoints will be
* calculated as a side effect.
* @tparam F A functor with `opertor()(Args&&...)` returning a scalar
* @tparam Theta A class assignable to an Eigen vector type
* @tparam Stream Type of stream for messages.
* @tparam Args Type of variadic arguments.
* @param f Log likelihood function.
* @param theta Latent Gaussian model.
* @param msgs Stream for messages.
* @param args Variadic arguments for the likelihood function.
*/
template <typename F, typename Theta, typename Stream, typename... Args,
require_eigen_vector_vt<std::is_arithmetic, Theta>* = nullptr>
inline void ll_arg_grad(F&& f, Theta&& theta, Stream* msgs, Args&&... args) {
using Eigen::Dynamic;
using Eigen::Matrix;
nested_rev_autodiff nested;
var f_var = f(theta, args..., msgs);
grad(f_var.vi_);
}

/**
* Computes negative block diagonal Hessian of `f` wrt`theta` and `args...`
* @note If `Args` contains \ref var types then their adjoints will be
* calculated as a side effect.
* @tparam F A functor with `opertor()(Args&&...)` returning a scalar
* @tparam Theta A class assignable to an Eigen vector type
* @tparam Stream Type of stream for messages.
* @tparam Args Type of variadic arguments.
* @param f Log likelihood function.
* @param theta Latent Gaussian model.
* @param hessian_block_size If the Hessian of the log likelihood function w.r.t
* the latent Gaussian variable is block-diagonal,
* size of each block.
* @param msgs Stream for messages.
* @param args Variadic arguments for the likelihood function.
*/
template <typename F, typename Theta, typename Stream, typename... Args,
require_eigen_vector_vt<std::is_arithmetic, Theta>* = nullptr>
inline auto diagonal_hessian(F&& f, Theta&& theta, Stream* msgs,
Args&&... args) {
using Eigen::Dynamic;
using Eigen::Matrix;
const Eigen::Index theta_size = theta.size();
auto v = Eigen::VectorXd::Ones(theta_size);
Eigen::VectorXd hessian_v = Eigen::VectorXd::Zero(theta_size);
hessian_times_vector(f, hessian_v, std::forward<Theta>(theta), std::move(v),
value_of(args)..., msgs);
return (-hessian_v).eval();
}

/**
* Computes negative block diagonal Hessian of `f` wrt`theta` and `args...`
* @note If `Args` contains \ref var types then their adjoints will be
* calculated as a side effect.
* @tparam F A functor with `opertor()(Args&&...)` returning a scalar
* @tparam Theta A class assignable to an Eigen vector type
* @tparam Stream Type of stream for messages.
* @tparam Args Type of variadic arguments.
* @param f Log likelihood function.
* @param theta Latent Gaussian model.
* @param hessian_block_size If the Hessian of the log likelihood function w.r.t
* the latent Gaussian variable is block-diagonal,
* size of each block.
* @param msgs Stream for messages.
* @param args Variadic arguments for the likelihood function.
*/
template <typename F, typename Theta, typename Stream, typename... Args,
require_eigen_vector_vt<std::is_arithmetic, Theta>* = nullptr>
inline auto block_hessian(F&& f, Theta&& theta,
const Eigen::Index hessian_block_size, Stream* msgs,
Args&&... args) {
using Eigen::Dynamic;
using Eigen::Matrix;
const Eigen::Index theta_size = theta.size();
if (hessian_block_size == 1) {
auto v = Eigen::VectorXd::Ones(theta_size);
Eigen::VectorXd hessian_v = Eigen::VectorXd::Zero(theta_size);
hessian_times_vector(f, hessian_v, std::forward<Theta>(theta), std::move(v),
value_of(args)..., msgs);
Eigen::SparseMatrix<double> hessian_theta(theta_size, theta_size);
hessian_theta.reserve(Eigen::VectorXi::Constant(theta_size, 1));
for (Eigen::Index i = 0; i < theta_size; i++) {
hessian_theta.insert(i, i) = hessian_v(i);
}
return (-hessian_theta).eval();
} else {
return (-hessian_block_diag(f, std::forward<Theta>(theta),
hessian_block_size, value_of(args)..., msgs))
.eval();
}
}

/**
* Computes theta gradient and negative block diagonal Hessian of `f` wrt
* `theta` and `args...`
Expand Down Expand Up @@ -301,6 +459,79 @@ inline auto diff_eta_implicit(F&& f, V_t&& v, Theta&& theta, Stream* msgs,

} // namespace internal

/**
* A wrapper that accepts a tuple as arguments.
* @tparam F A functor with `opertor()(Args&&...)` returning a scalar
* @tparam Theta A class assignable to an Eigen vector type
* @tparam TupleArgs Type of arguments for covariance function.
* @tparam Stream Type of stream for messages.
* @param f Log likelihood function.
* @param theta Latent Gaussian model.
* @param ll_tup Arguments for likelihood function
* @param msgs stream messages.
*/
template <typename F, typename Theta, typename TupleArgs, typename Stream,
require_eigen_vector_t<Theta>* = nullptr,
require_tuple_t<TupleArgs>* = nullptr>
inline auto theta_grad(F&& f, Theta&& theta, TupleArgs&& ll_tup,
Stream* msgs = nullptr) {
return apply(
[](auto&& f, auto&& theta, auto&& msgs, auto&&... args) {
return internal::theta_grad(std::forward<decltype(f)>(f),
std::forward<decltype(theta)>(theta), msgs,
std::forward<decltype(args)>(args)...);
},
std::forward<TupleArgs>(ll_tup), std::forward<F>(f),
std::forward<Theta>(theta), msgs);
}

template <typename F, typename Theta, typename TupleArgs, typename Stream,
require_eigen_vector_t<Theta>* = nullptr,
require_tuple_t<TupleArgs>* = nullptr>
inline auto ll_arg_grad(F&& f, Theta&& theta, TupleArgs&& ll_tup,
Stream* msgs = nullptr) {
return apply(
[](auto&& f, auto&& theta, auto&& msgs, auto&&... args) {
return internal::ll_arg_grad(std::forward<decltype(f)>(f),
std::forward<decltype(theta)>(theta), msgs,
std::forward<decltype(args)>(args)...);
},
std::forward<TupleArgs>(ll_tup), std::forward<F>(f),
std::forward<Theta>(theta), msgs);
}

template <typename F, typename Theta, typename TupleArgs, typename Stream,
require_eigen_vector_t<Theta>* = nullptr,
require_tuple_t<TupleArgs>* = nullptr>
inline auto diagonal_hessian(F&& f, Theta&& theta, TupleArgs&& ll_tuple,
Stream* msgs) {
return apply(
[](auto&& f, auto&& theta, auto* msgs, auto&&... args) {
return internal::diagonal_hessian(
std::forward<decltype(f)>(f), std::forward<decltype(theta)>(theta),
msgs, std::forward<decltype(args)>(args)...);
},
std::forward<TupleArgs>(ll_tuple), std::forward<F>(f),
std::forward<Theta>(theta), msgs);
}

template <typename F, typename Theta, typename TupleArgs, typename Stream,
require_eigen_vector_t<Theta>* = nullptr,
require_tuple_t<TupleArgs>* = nullptr>
inline auto block_hessian(F&& f, Theta&& theta,
const Eigen::Index hessian_block_size,
TupleArgs&& ll_tuple, Stream* msgs) {
return apply(
[](auto&& f, auto&& theta, auto hessian_block_size, auto* msgs,
auto&&... args) {
return internal::block_hessian(
std::forward<decltype(f)>(f), std::forward<decltype(theta)>(theta),
hessian_block_size, msgs, std::forward<decltype(args)>(args)...);
},
std::forward<TupleArgs>(ll_tuple), std::forward<F>(f),
std::forward<Theta>(theta), hessian_block_size, msgs);
}

/**
* A wrapper that accepts a tuple as arguments.
* @tparam F A functor with `opertor()(Args&&...)` returning a scalar
Expand Down
Loading