Skip to content

Commit 7a47143

Browse files
committed
cleanup tests and address review comments
1 parent cd363fe commit 7a47143

10 files changed

Lines changed: 218 additions & 400 deletions

stan/math/mix/functor/laplace_marginal_density_estimator.hpp

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ inline void llt_with_jitter(LLT& llt_B, B_t& B, double min_jitter = 1e-10,
444444
}
445445
if (llt_B.info() != Eigen::Success) {
446446
throw std::domain_error(
447-
"laplace_marginal_density: Cholesky (Diag) failed");
447+
"laplace_marginal_density: Cholesky failed after adding jitter up to " + std::to_string(jitter_try));
448448
}
449449
}
450450
}
@@ -942,16 +942,12 @@ inline auto run_newton_loop(SolverPolicy& solver, NewtonStateT& state,
942942
scratch.alpha() = 1.0;
943943
update_fun(scratch, state.curr(), state.prev(), scratch.eval_,
944944
state.wolfe_info.p_);
945-
bool run_convergence_check = true;
946945
if (scratch.alpha() <= options.line_search.min_alpha) {
947946
state.wolfe_status.accept_ = false;
948947
finish_update = true;
949-
run_convergence_check = false;
950948
} else if (options.line_search.max_iterations == 0) {
951949
state.curr().update(scratch);
952950
state.wolfe_status.accept_ = true;
953-
finish_update = false;
954-
run_convergence_check = false;
955951
} else {
956952
Eigen::VectorXd s = scratch.a() - state.prev().a();
957953
auto full_step_grad
@@ -964,16 +960,15 @@ inline auto run_newton_loop(SolverPolicy& solver, NewtonStateT& state,
964960
state.wolfe_status = internal::wolfe_line_search(
965961
state.wolfe_info, update_fun, options.line_search, msgs);
966962
}
967-
if (run_convergence_check) {
968-
/**
969-
* Stop when objective change is small, or when a rejected Wolfe step
970-
* fails to improve; finish_update then exits the Newton loop.
971-
*/
972-
finish_update = std::abs(state.curr().obj() - state.prev().obj())
973-
< options.tolerance
974-
|| (!state.wolfe_status.accept_
975-
&& state.curr().obj() <= state.prev().obj());
976-
}
963+
/**
964+
* Stop when objective change is small, or when a rejected Wolfe step
965+
* fails to improve; finish_update then exits the Newton loop.
966+
*/
967+
bool objective_converged = std::abs(state.curr().obj() - state.prev().obj())
968+
< options.tolerance;
969+
bool search_failed = (!state.wolfe_status.accept_
970+
&& state.curr().obj() <= state.prev().obj());
971+
finish_update = objective_converged || search_failed;
977972
}
978973
if (finish_update) {
979974
if (!state.final_loop && state.wolfe_status.accept_) {
@@ -1039,6 +1034,52 @@ inline decltype(auto) theta_init_impl(Eigen::Index theta_size, Opts&& options) {
10391034
}
10401035
}
10411036

1037+
/**
1038+
* @brief Create the update function for the line search, capturing necessary
1039+
* references.
1040+
* @tparam ObjFun Callable type for the objective function (accepting (a, theta))
1041+
* @tparam ThetaGradFun Callable type for the theta gradient function (accepting theta)
1042+
* @tparam Covariance Type of the covariance matrix
1043+
* @tparam Options Type of the options struct containing line search parameters
1044+
* @param[in] obj_fun Objective function functor
1045+
* @param[in] theta_grad_f Theta gradient functor
1046+
* @param[in] covariance Prior covariance matrix Sigma
1047+
* @param[in] options Options struct containing line search parameters
1048+
* @return A callable update function for the line search, with signature:
1049+
* ```
1050+
* bool update_fun(proposal, curr, prev, eval_in, p)
1051+
* ```
1052+
*/
1053+
template <typename ObjFun, typename ThetaGradFun, typename Covariance, typename Options>
1054+
inline auto create_update_fun(ObjFun&& obj_fun, ThetaGradFun&& theta_grad_f,
1055+
Covariance&& covariance, Options&& options) {
1056+
auto update_step = [&covariance, &obj_fun, &theta_grad_f](
1057+
auto& proposal, auto&& /* curr */, auto&& prev,
1058+
auto& eval_in, auto&& p) {
1059+
try {
1060+
proposal.a() = prev.a() + eval_in.alpha() * p;
1061+
proposal.theta().noalias() = covariance * proposal.a();
1062+
proposal.theta_grad() = theta_grad_f(proposal.theta());
1063+
eval_in.obj() = obj_fun(proposal.a(), proposal.theta());
1064+
eval_in.dir()
1065+
= (-covariance * proposal.a() + covariance * proposal.theta_grad())
1066+
.dot(p);
1067+
return std::isfinite(eval_in.obj()) && std::isfinite(eval_in.dir());
1068+
} catch (const std::exception&) {
1069+
return false;
1070+
}
1071+
};
1072+
auto backoff = [&options](auto& eval) {
1073+
eval.alpha() *= options.line_search.tau;
1074+
return eval.alpha() > options.line_search.min_alpha;
1075+
};
1076+
return [update_step_ = std::move(update_step), backoff_ = std::move(backoff)](
1077+
auto& proposal, auto&& curr, auto&& prev, auto& eval_in, auto&& p) {
1078+
return internal::retry_evaluate(update_step_, proposal, curr, prev,
1079+
eval_in, p, backoff_);
1080+
};
1081+
}
1082+
10421083
/**
10431084
* For a latent Gaussian model with hyperparameters phi and
10441085
* latent variables theta, and observations y, this function computes
@@ -1098,9 +1139,7 @@ inline auto laplace_marginal_density_est(
10981139
const laplace_options<InitTheta>& options, std::ostream* msgs) {
10991140
internal::validate_laplace_options("laplace_marginal_density", options,
11001141
covariance);
1101-
11021142
const Eigen::Index theta_size = covariance.rows();
1103-
11041143
// Wolfe optimizes over the latent 'a' space
11051144
auto obj_fun = [&ll_fun, &ll_args, &msgs](const Eigen::VectorXd& a_val,
11061145
auto&& theta_val) -> double {
@@ -1113,36 +1152,8 @@ inline auto laplace_marginal_density_est(
11131152
};
11141153
decltype(auto) theta_init = theta_init_impl<InitTheta>(theta_size, options);
11151154
internal::NewtonState state(theta_size, obj_fun, theta_grad_f, theta_init);
1116-
// 'a' gradient
1117-
auto update_fun = [&covariance, &obj_fun, &theta_grad_f, &options]() {
1118-
auto update_step = [&covariance, &obj_fun, &theta_grad_f](
1119-
auto& proposal, auto&& /* curr */, auto&& prev,
1120-
auto& eval_in, auto&& p) {
1121-
try {
1122-
proposal.a() = prev.a() + eval_in.alpha() * p;
1123-
proposal.theta().noalias() = covariance * proposal.a();
1124-
proposal.theta_grad() = theta_grad_f(proposal.theta());
1125-
eval_in.obj() = obj_fun(proposal.a(), proposal.theta());
1126-
eval_in.dir()
1127-
= (-covariance * proposal.a() + covariance * proposal.theta_grad())
1128-
.dot(p);
1129-
return std::isfinite(eval_in.obj()) && std::isfinite(eval_in.dir());
1130-
} catch (const std::exception&) {
1131-
return false;
1132-
}
1133-
};
1134-
auto backoff = [&options](auto& eval) {
1135-
eval.alpha() *= options.line_search.tau;
1136-
return eval.alpha() > options.line_search.min_alpha;
1137-
};
1138-
return
1139-
[update_step_ = std::move(update_step), backoff_ = std::move(backoff)](
1140-
auto& proposal, auto&& curr, auto&& prev, auto& eval_in, auto&& p) {
1141-
return internal::retry_evaluate(update_step_, proposal, curr, prev,
1142-
eval_in, p, backoff_);
1143-
};
1144-
}();
11451155
// Start with safe step size
1156+
auto update_fun = create_update_fun(std::move(obj_fun), std::move(theta_grad_f), covariance, options);
11461157
Eigen::Index step_iter = 0;
11471158
try {
11481159
if (options.solver == 1) {

0 commit comments

Comments
 (0)