Skip to content

Commit 8055bb4

Browse files
authored
Merge pull request #3286 from stan-dev/fix/laplace-args
update laplace api to have hessian_block_size as a non-optional argument
2 parents 8fa404b + d9ae584 commit 8055bb4

24 files changed

Lines changed: 345 additions & 196 deletions

doxygen/doxygen.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2747,7 +2747,6 @@ ALIASES += laplace_options="\
27472747
- theta_0 the initial guess for the Laplace approximation. \
27482748
- tolerance controls the convergence criterion when finding the mode in the Laplace approximation. \
27492749
- max_num_steps maximum number of steps before the Newton solver breaks and returns an error. \
2750-
- hessian_block_size Block size of Hessian of log likelihood w.r.t latent Gaussian variable theta. \
27512750
- solver Type of Newton solver. Each corresponds to a distinct choice of B matrix (i.e. application SWM formula): \
27522751
1. computes square-root of negative Hessian. \
27532752
2. computes square-root of covariance matrix. \

stan/math/mix/functor/laplace_marginal_density_estimator.hpp

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef STAN_MATH_MIX_FUNCTOR_LAPLACE_MARGINAL_DENSITY_ESTIMATOR_HPP
22
#define STAN_MATH_MIX_FUNCTOR_LAPLACE_MARGINAL_DENSITY_ESTIMATOR_HPP
33
#include <stan/math/prim/fun/Eigen.hpp>
4+
#include <stan/math/prim/fun/generate_laplace_options.hpp>
45
#include <stan/math/mix/functor/laplace_likelihood.hpp>
56
#include <stan/math/mix/functor/wolfe_line_search.hpp>
67
#include <stan/math/rev/meta.hpp>
@@ -10,6 +11,7 @@
1011
#include <stan/math/mix/functor/barzilai_borwein_step_size.hpp>
1112
#include <stan/math/prim/fun/to_ref.hpp>
1213
#include <stan/math/prim/fun/quad_form_diag.hpp>
14+
#include <stan/math/prim/fun/value_of.hpp>
1315
#include <stan/math/prim/functor/iter_tuple_nested.hpp>
1416
#include <unsupported/Eigen/MatrixFunctions>
1517
#include <cmath>
@@ -30,7 +32,7 @@ namespace math {
3032
*/
3133
struct laplace_options_base {
3234
/* Size of the blocks in block diagonal hessian*/
33-
int hessian_block_size{1}; // 0
35+
int hessian_block_size{internal::laplace_default_hessian_block_size}; // 0
3436
/**
3537
* Which linear solver to use inside the Newton step.
3638
*
@@ -46,19 +48,20 @@ struct laplace_options_base {
4648
* `Sigma = K_root * K_root^T` and form `B = I + K_root^T * W * K_root`.
4749
* 3. General LU: form `B = I + Sigma * W` and factorize with LU.
4850
*/
49-
int solver{1}; // 1
51+
int solver{internal::laplace_default_solver}; // 1
5052
/**
5153
* Iterations end when the absolute change in the optimization objective
5254
* is less than this tolerance.
5355
*
5456
* Note: the objective used for convergence is the one optimized by the
5557
* Newton/Wolfe loop (not the final Laplace-corrected log marginal density).
5658
*/
57-
double tolerance{1.49012e-08}; // 2
59+
double tolerance{internal::laplace_default_tolerance}; // 2
5860
/* Maximum number of steps*/
59-
int max_num_steps{500}; // 3
60-
int allow_fallthrough{true}; // 4
61-
laplace_line_search_options line_search; // 5
61+
int max_num_steps{internal::laplace_default_max_num_steps}; // 3
62+
int allow_fallthrough{internal::laplace_default_allow_fallthrough}; // 4
63+
laplace_line_search_options line_search{
64+
internal::laplace_default_max_steps_line_search}; // 5
6265
laplace_options_base() = default;
6366
laplace_options_base(int hessian_block_size_, int solver_, double tolerance_,
6467
int max_num_steps_, bool allow_fallthrough_,
@@ -75,7 +78,13 @@ template <bool HasInitTheta>
7578
struct laplace_options;
7679

7780
template <>
78-
struct laplace_options<false> : public laplace_options_base {};
81+
struct laplace_options<false> : public laplace_options_base {
82+
laplace_options() = default;
83+
84+
explicit laplace_options(int hessian_block_size_) {
85+
hessian_block_size = hessian_block_size_;
86+
}
87+
};
7988

8089
template <>
8190
struct laplace_options<true> : public laplace_options_base {
@@ -89,25 +98,12 @@ struct laplace_options<true> : public laplace_options_base {
8998
: laplace_options_base(hessian_block_size_, solver_, tolerance_,
9099
max_num_steps_, allow_fallthrough_,
91100
max_steps_line_search_),
92-
theta_0(std::forward<ThetaVec>(theta_0_)) {}
101+
theta_0(value_of(std::forward<ThetaVec>(theta_0_))) {}
93102
};
94103

95104
using laplace_options_default = laplace_options<false>;
96105
using laplace_options_user_supplied = laplace_options<true>;
97106

98-
/**
99-
* User function for generating laplace options tuple
100-
* @param theta_0_size Size of user supplied initial theta
101-
* @return tuple representing laplace options exposed to user.
102-
*/
103-
inline auto generate_laplace_options(int theta_0_size) {
104-
auto ops = laplace_options_default{};
105-
return std::make_tuple(
106-
Eigen::VectorXd::Zero(theta_0_size).eval(), // 0 -> 6
107-
ops.tolerance, ops.max_num_steps, ops.hessian_block_size, ops.solver,
108-
ops.line_search.max_iterations, static_cast<int>(ops.allow_fallthrough));
109-
}
110-
111107
namespace internal {
112108

113109
template <typename Options>
@@ -135,41 +131,36 @@ inline constexpr auto tuple_to_laplace_options(Options&& ops) {
135131
"the laplace approximation.");
136132
}
137133
if constexpr (!stan::is_inner_tuple_type_v<3, Ops, int>) {
138-
static_assert(
139-
sizeof(std::decay_t<Ops>*) == 0,
140-
"ERROR:(laplace_marginal_lpdf) The fifth laplace argument is "
141-
"expected to be an int representing the hessian block size.");
142-
}
143-
if constexpr (!stan::is_inner_tuple_type_v<4, Ops, int>) {
144134
static_assert(
145135
sizeof(std::decay_t<Ops>*) == 0,
146136
"ERROR:(laplace_marginal_lpdf) The fourth laplace argument is "
147137
"expected to be an int representing the solver.");
148138
}
149-
if constexpr (!stan::is_inner_tuple_type_v<5, Ops, int>) {
139+
if constexpr (!stan::is_inner_tuple_type_v<4, Ops, int>) {
150140
static_assert(
151141
sizeof(std::decay_t<Ops>*) == 0,
152-
"ERROR:(laplace_marginal_lpdf) The sixth laplace argument is "
142+
"ERROR:(laplace_marginal_lpdf) The fifth laplace argument is "
153143
"expected to be an int representing the max steps for the laplace "
154144
"approximaton's wolfe line search.");
155145
}
156146
constexpr bool is_fallthrough
157147
= stan::is_inner_tuple_type_v<
158-
6, Ops, int> || stan::is_inner_tuple_type_v<6, Ops, bool>;
148+
5, Ops, int> || stan::is_inner_tuple_type_v<5, Ops, bool>;
159149
if constexpr (!is_fallthrough) {
160150
static_assert(
161151
sizeof(std::decay_t<Ops>*) == 0,
162-
"ERROR:(laplace_marginal_lpdf) The seventh laplace argument is "
152+
"ERROR:(laplace_marginal_lpdf) The sixth laplace argument is "
163153
"expected to be an int representing allow fallthrough (0/1).");
164154
}
155+
auto defaults = laplace_options_default{};
165156
return laplace_options_user_supplied{
166157
value_of(std::get<0>(std::forward<Ops>(ops))),
167158
std::get<1>(ops),
168159
std::get<2>(ops),
160+
defaults.hessian_block_size,
169161
std::get<3>(ops),
170162
std::get<4>(ops),
171-
std::get<5>(ops),
172-
(std::get<6>(ops) > 0) ? true : false,
163+
(std::get<5>(ops) > 0) ? true : false,
173164
};
174165
} else {
175166
return std::forward<Ops>(ops);

stan/math/mix/prob/laplace_latent_bernoulli_logit_rng.hpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ namespace math {
2424
* @param[in] n_samples Vector of number of trials.
2525
* @param[in] mean the mean of the latent normal variable.
2626
* \laplace_common_args
27+
* @param[in] hessian_block_size Block size for the Hessian approximation with
28+
* respect to the latent gaussian variable theta.
2729
* \laplace_options
2830
* \rng_arg
2931
* \msg_arg
@@ -32,15 +34,16 @@ template <typename Mean, typename CovarFun, typename CovarArgs,
3234
typename OpsTuple, typename RNG>
3335
inline Eigen::VectorXd laplace_latent_tol_bernoulli_logit_rng(
3436
const std::vector<int>& y, const std::vector<int>& n_samples, Mean&& mean,
35-
CovarFun&& covariance_function, CovarArgs&& covar_args, OpsTuple&& ops,
36-
RNG& rng, std::ostream* msgs) {
37+
int hessian_block_size, CovarFun&& covariance_function,
38+
CovarArgs&& covar_args, OpsTuple&& ops, RNG& rng, std::ostream* msgs) {
39+
auto options
40+
= internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops));
41+
options.hessian_block_size = hessian_block_size;
3742
return laplace_base_rng(
3843
bernoulli_logit_likelihood{},
3944
std::forward_as_tuple(to_vector(y), n_samples, std::forward<Mean>(mean)),
4045
std::forward<CovarFun>(covariance_function),
41-
std::forward<CovarArgs>(covar_args),
42-
internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops)), rng,
43-
msgs);
46+
std::forward<CovarArgs>(covar_args), std::move(options), rng, msgs);
4447
}
4548

4649
/**
@@ -59,20 +62,22 @@ inline Eigen::VectorXd laplace_latent_tol_bernoulli_logit_rng(
5962
* @param[in] n_samples Vector of number of trials.
6063
* @param[in] mean the mean of the latent normal variable.
6164
* \laplace_common_args
65+
* @param[in] hessian_block_size Block size for the Hessian approximation with
66+
* respect to the latent gaussian variable theta.
6267
* \rng_arg
6368
* \msg_arg
6469
*/
6570
template <typename Mean, typename CovarFun, typename CovarArgs, typename RNG>
6671
inline Eigen::VectorXd laplace_latent_bernoulli_logit_rng(
6772
const std::vector<int>& y, const std::vector<int>& n_samples, Mean&& mean,
68-
CovarFun&& covariance_function, CovarArgs&& covar_args, RNG& rng,
69-
std::ostream* msgs) {
73+
int hessian_block_size, CovarFun&& covariance_function,
74+
CovarArgs&& covar_args, RNG& rng, std::ostream* msgs) {
75+
auto options = laplace_options_default{hessian_block_size};
7076
return laplace_base_rng(
7177
bernoulli_logit_likelihood{},
7278
std::forward_as_tuple(to_vector(y), n_samples, std::forward<Mean>(mean)),
7379
std::forward<CovarFun>(covariance_function),
74-
std::forward<CovarArgs>(covar_args), laplace_options_default{}, rng,
75-
msgs);
80+
std::forward<CovarArgs>(covar_args), options, rng, msgs);
7681
}
7782

7883
} // namespace math

stan/math/mix/prob/laplace_latent_neg_binomial_2_log_rng.hpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ namespace math {
3131
* @param[in] eta Overdisperison parameter.
3232
* @param[in] mean The mean of the latent normal variable.
3333
* \laplace_common_args
34+
* @param[in] hessian_block_size Block size for the Hessian approximation with
35+
* respect to the latent gaussian variable theta.
3436
* \laplace_options
3537
* \rng_arg
3638
* \msg_arg
@@ -39,16 +41,17 @@ template <typename Eta, typename Mean, typename CovarFun, typename CovarArgs,
3941
typename OpsTuple, typename RNG>
4042
inline Eigen::VectorXd laplace_latent_tol_neg_binomial_2_log_rng(
4143
const std::vector<int>& y, const std::vector<int>& y_index, Eta&& eta,
42-
Mean&& mean, CovarFun&& covariance_function, CovarArgs&& covar_args,
43-
OpsTuple&& ops, RNG& rng, std::ostream* msgs) {
44+
Mean&& mean, int hessian_block_size, CovarFun&& covariance_function,
45+
CovarArgs&& covar_args, OpsTuple&& ops, RNG& rng, std::ostream* msgs) {
46+
auto options
47+
= internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops));
48+
options.hessian_block_size = hessian_block_size;
4449
return laplace_base_rng(
4550
neg_binomial_2_log_likelihood{},
4651
std::forward_as_tuple(std::forward<Eta>(eta), y, y_index,
4752
std::forward<Mean>(mean)),
4853
std::forward<CovarFun>(covariance_function),
49-
std::forward<CovarArgs>(covar_args),
50-
internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops)), rng,
51-
msgs);
54+
std::forward<CovarArgs>(covar_args), std::move(options), rng, msgs);
5255
}
5356

5457
/**
@@ -72,22 +75,24 @@ inline Eigen::VectorXd laplace_latent_tol_neg_binomial_2_log_rng(
7275
* @param[in] eta Overdisperison parameter.
7376
* @param[in] mean The mean of the latent normal variable.
7477
* \laplace_common_args
78+
* @param[in] hessian_block_size Block size for the Hessian approximation with
79+
* respect to the latent gaussian variable theta.
7580
* \rng_arg
7681
* \msg_arg
7782
*/
7883
template <typename Eta, typename Mean, typename CovarFun, typename CovarArgs,
7984
typename RNG>
8085
inline Eigen::VectorXd laplace_latent_neg_binomial_2_log_rng(
8186
const std::vector<int>& y, const std::vector<int>& y_index, Eta&& eta,
82-
Mean&& mean, CovarFun&& covariance_function, CovarArgs&& covar_args,
83-
RNG& rng, std::ostream* msgs) {
87+
Mean&& mean, int hessian_block_size, CovarFun&& covariance_function,
88+
CovarArgs&& covar_args, RNG& rng, std::ostream* msgs) {
89+
auto options = laplace_options_default{hessian_block_size};
8490
return laplace_base_rng(
8591
neg_binomial_2_log_likelihood{},
8692
std::forward_as_tuple(std::forward<Eta>(eta), y, y_index,
8793
std::forward<Mean>(mean)),
8894
std::forward<CovarFun>(covariance_function),
89-
std::forward<CovarArgs>(covar_args), laplace_options_default{}, rng,
90-
msgs);
95+
std::forward<CovarArgs>(covar_args), options, rng, msgs);
9196
}
9297

9398
} // namespace math

stan/math/mix/prob/laplace_latent_poisson_log_rng.hpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ namespace math {
2626
* @param[in] y_index Index indicating which group each observation belongs to.
2727
* @param[in] mean The mean of the latent normal variable.
2828
* \laplace_common_args
29+
* @param[in] hessian_block_size Block size for the Hessian approximation with
30+
* respect to the latent gaussian variable theta.
2931
* \laplace_options
3032
* \rng_arg
3133
* \msg_arg
@@ -34,15 +36,16 @@ template <typename Mean, typename CovarFun, typename CovarArgs,
3436
typename OpsTuple, typename RNG>
3537
inline Eigen::VectorXd laplace_latent_tol_poisson_log_rng(
3638
const std::vector<int>& y, const std::vector<int>& y_index, Mean&& mean,
37-
CovarFun&& covariance_function, CovarArgs&& covar_args, OpsTuple&& ops,
38-
RNG& rng, std::ostream* msgs) {
39+
int hessian_block_size, CovarFun&& covariance_function,
40+
CovarArgs&& covar_args, OpsTuple&& ops, RNG& rng, std::ostream* msgs) {
41+
auto options
42+
= internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops));
43+
options.hessian_block_size = hessian_block_size;
3944
return laplace_base_rng(
4045
poisson_log_likelihood{},
4146
std::forward_as_tuple(y, y_index, std::forward<Mean>(mean)),
4247
std::forward<CovarFun>(covariance_function),
43-
std::forward<CovarArgs>(covar_args),
44-
internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops)), rng,
45-
msgs);
48+
std::forward<CovarArgs>(covar_args), std::move(options), rng, msgs);
4649
}
4750

4851
/**
@@ -62,20 +65,22 @@ inline Eigen::VectorXd laplace_latent_tol_poisson_log_rng(
6265
* @param[in] y_index Index indicating which group each observation belongs to.
6366
* @param[in] mean The mean of the latent normal variable.
6467
* \laplace_common_args
68+
* @param[in] hessian_block_size Block size for the Hessian approximation with
69+
* respect to the latent gaussian variable theta.
6570
* \rng_arg
6671
* \msg_arg
6772
*/
6873
template <typename CovarFun, typename CovarArgs, typename RNG, typename Mean>
6974
inline Eigen::VectorXd laplace_latent_poisson_log_rng(
7075
const std::vector<int>& y, const std::vector<int>& y_index, Mean&& mean,
71-
CovarFun&& covariance_function, CovarArgs&& covar_args, RNG& rng,
72-
std::ostream* msgs) {
76+
int hessian_block_size, CovarFun&& covariance_function,
77+
CovarArgs&& covar_args, RNG& rng, std::ostream* msgs) {
78+
auto options = laplace_options_default{hessian_block_size};
7379
return laplace_base_rng(
7480
poisson_log_likelihood{},
7581
std::forward_as_tuple(y, y_index, std::forward<Mean>(mean)),
7682
std::forward<CovarFun>(covariance_function),
77-
std::forward<CovarArgs>(covar_args), laplace_options_default{}, rng,
78-
msgs);
83+
std::forward<CovarArgs>(covar_args), options, rng, msgs);
7984
}
8085

8186
} // namespace math

stan/math/mix/prob/laplace_latent_rng.hpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,26 @@ namespace math {
2525
* @param[in] L_f Function that returns log likelihood.
2626
* @param[in] ll_args Arguments for likelihood function.
2727
* \laplace_common_args
28+
* @param[in] hessian_block_size Block size for the Hessian approximation with
29+
* respect to the latent gaussian variable theta.
2830
* \laplace_options
2931
* \rng_arg
3032
* \msg_arg
3133
*/
3234
template <typename LLFunc, typename LLArgs, typename CovarFun,
3335
typename CovarArgs, typename OpsTuple, typename RNG>
3436
inline auto laplace_latent_tol_rng(LLFunc&& L_f, LLArgs&& ll_args,
37+
int hessian_block_size,
3538
CovarFun&& covariance_function,
3639
CovarArgs&& covar_args, OpsTuple&& ops,
3740
RNG& rng, std::ostream* msgs) {
41+
auto options
42+
= internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops));
43+
options.hessian_block_size = hessian_block_size;
3844
return laplace_base_rng(
3945
std::forward<LLFunc>(L_f), std::forward<LLArgs>(ll_args),
4046
std::forward<CovarFun>(covariance_function),
41-
std::forward<CovarArgs>(covar_args),
42-
internal::tuple_to_laplace_options(std::forward<OpsTuple>(ops)), rng,
43-
msgs);
47+
std::forward<CovarArgs>(covar_args), std::move(options), rng, msgs);
4448
}
4549

4650
/**
@@ -58,20 +62,23 @@ inline auto laplace_latent_tol_rng(LLFunc&& L_f, LLArgs&& ll_args,
5862
* @param[in] L_f Function that returns log likelihood.
5963
* @param[in] ll_args Arguments for likelihood function.
6064
* \laplace_common_args
65+
* @param[in] hessian_block_size Block size for the Hessian approximation with
66+
* respect to the latent gaussian variable theta.
6167
* \rng_arg
6268
* \msg_arg
6369
*/
6470
template <typename LLFunc, typename LLArgs, typename CovarFun,
6571
typename CovarArgs, typename RNG>
6672
inline auto laplace_latent_rng(LLFunc&& L_f, LLArgs&& ll_args,
73+
int hessian_block_size,
6774
CovarFun&& covariance_function,
6875
CovarArgs&& covar_args, RNG& rng,
6976
std::ostream* msgs) {
70-
return laplace_base_rng(std::forward<LLFunc>(L_f),
71-
std::forward<LLArgs>(ll_args),
72-
std::forward<CovarFun>(covariance_function),
73-
std::forward<CovarArgs>(covar_args),
74-
laplace_options_default{}, rng, msgs);
77+
auto options = laplace_options_default{hessian_block_size};
78+
return laplace_base_rng(
79+
std::forward<LLFunc>(L_f), std::forward<LLArgs>(ll_args),
80+
std::forward<CovarFun>(covariance_function),
81+
std::forward<CovarArgs>(covar_args), options, rng, msgs);
7582
}
7683

7784
} // namespace math

0 commit comments

Comments
 (0)