11#ifndef STAN_MATH_MIX_FUNCTOR_LAPLACE_MARGINAL_DENSITY_HPP
22#define STAN_MATH_MIX_FUNCTOR_LAPLACE_MARGINAL_DENSITY_HPP
3+ #include < stan/math/prim/fun/Eigen.hpp>
34#include < stan/math/mix/functor/laplace_likelihood.hpp>
45#include < stan/math/rev/meta.hpp>
56#include < stan/math/rev/core.hpp>
89#include < stan/math/rev/functor.hpp>
910#include < stan/math/prim/fun/to_ref.hpp>
1011#include < stan/math/prim/fun/quad_form_diag.hpp>
11- #include < Eigen/Sparse>
1212#include < Eigen/LU>
1313#include < unsupported/Eigen/MatrixFunctions>
1414
@@ -83,66 +83,6 @@ struct laplace_density_estimates {
8383 K_root(std::move(K_root_)) {}
8484};
8585
86- /* *
87- * Function to compute the pseudo target, $\tilde Z$,
88- * with a custom derivative method
89- * NOTE: we actually don't need to compute the pseudo-target, only its
90- * derivative
91- * @tparam Kmat Type inheriting from `Eigen::EigenBase` with dynamic rows and
92- * columns
93- * @tparam AVec Type of matrix of initial tangents
94- * @tparam RMat Type of the stable R matrix
95- * @tparam LGradVec Type of the gradient of the log likelihood
96- * @tparam S2Vec Type of the s2 vector
97- */
98- template <
99- typename KMat, typename AVec, typename RMat, typename LGradVec,
100- typename S2Vec,
101- require_eigen_matrix_dynamic_vt<std::is_floating_point, KMat>* = nullptr >
102- inline constexpr double laplace_pseudo_target (KMat&& /* K */ , AVec&& /* a */ ,
103- RMat&& /* R */ ,
104- LGradVec&& /* l_grad */ ,
105- S2Vec&& /* s2 */ ) {
106- return static_cast <double >(0.0 );
107- }
108-
109- /* *
110- * Overload function for case where K is passed as a matrix of var
111- * @tparam Kmat Type inheriting from `Eigen::EigenBase` with dynamic rows and
112- * columns
113- * @tparam AVec Type inheriting from `Eigen::EigenBase` with dynamic columns and
114- * a single row
115- * @tparam RMat Type inheriting from `Eigen::EigenBase` with dynamic rows and
116- * columns
117- * @tparam LGradVec Type inheriting from `Eigen::EigenBase` with dynamic rows
118- * and a single column
119- * @tparam S2Vec Type of s2 vector
120- * @param K Covariance matrix
121- * @param a Saved a vector from Newton solver
122- * @param R Stable R matrix
123- * @param l_grad Saved gradient of log likelihood
124- * @param s2 Gradient of log determinant w.r.t latent Gaussian variable
125- */
126- template <typename KMat, typename AVec, typename RMat, typename LGradVec,
127- typename S2Vec,
128- require_eigen_matrix_dynamic_vt<is_var, KMat>* = nullptr >
129- inline auto laplace_pseudo_target (KMat&& K, AVec&& a, RMat&& R,
130- LGradVec&& l_grad, S2Vec&& s2) {
131- const Eigen::Index dim_theta = K.rows ();
132- auto K_arena = to_arena (std::forward<KMat>(K));
133- auto && a_ref = to_ref (std::forward<AVec>(a));
134- auto && R_ref = to_ref (std::forward<RMat>(R));
135- auto && s2_ref = to_ref (std::forward<S2Vec>(s2));
136- auto && l_grad_ref = to_ref (std::forward<LGradVec>(l_grad));
137- arena_matrix<Eigen::MatrixXd> K_adj_arena
138- = 0.5 * a_ref * a_ref.transpose () - 0.5 * R_ref
139- + s2_ref * l_grad_ref.transpose ()
140- - (R_ref * (value_of (K_arena) * s2_ref)) * l_grad_ref.transpose ();
141- return make_callback_var (0.0 , [K_arena, K_adj_arena](auto && vi) mutable {
142- K_arena.adj ().array () += vi.adj () * K_adj_arena.array ();
143- });
144- }
145-
14686template <typename WRootMat>
14787inline void block_matrix_sqrt (WRootMat& W_root,
14888 const Eigen::SparseMatrix<double >& W,
@@ -194,39 +134,67 @@ inline void block_matrix_sqrt(WRootMat& W_root,
194134 }
195135 }
196136}
197- template <typename AVec, typename APrev, typename ThetaVec, typename LLFun,
198- typename LLArgs, typename Covar, typename Msgs>
199- inline auto line_search (double & objective_new, AVec&& a, APrev& a_prev,
200- ThetaVec&& theta, LLFun&& ll_fun, LLArgs&& ll_args,
201- Covar&& covariance, const int max_steps_line_search,
202- const double objective_old, Msgs* msgs) {
203- Eigen::VectorXd a_tmp (a.size ());
204- double objective_new_tmp = 0.0 ;
205- double objective_old_tmp = objective_old;
206- Eigen::VectorXd theta_tmp (covariance.rows ());
207- int j = 0 ;
208- for (; j < max_steps_line_search && (objective_new < objective_old_tmp);
209- ++j) {
210- a_tmp.noalias () = a_prev + 0.5 * (a - a_prev);
211- theta_tmp.noalias () = covariance * a_tmp;
212- if (!theta_tmp.allFinite ()) {
213- break ;
214- } else {
215- objective_new_tmp = -0.5 * a_tmp.dot (theta_tmp)
216- + laplace_likelihood::log_likelihood (
217- ll_fun, theta_tmp, ll_args, msgs);
218- if (objective_new_tmp < objective_new) {
219- a_prev.swap (a);
220- a.swap (a_tmp);
221- theta.swap (theta_tmp);
222- objective_old_tmp = objective_new;
223- objective_new = objective_new_tmp;
224- } else {
225- break ;
226- }
227- }
228- }
229- return std::make_tuple (objective_new, std::move (a), std::move (theta));
137+
138+ /* *
139+ * @brief Performs a simple line search
140+ *
141+ * @tparam AVec Type of the parameter update vector (`a`), e.g. Eigen::VectorXd.
142+ * @tparam APrev Type of the previous parameter vector (`a_prev`), same shape as AVec.
143+ * @tparam ThetaVec Type of the transformed vector (`theta`), e.g. Σ·a.
144+ * @tparam LLFun Functor type for computing the log‐likelihood.
145+ * @tparam LLArgs Tuple or pack type forwarded to `ll_fun`.
146+ * @tparam Covar Matrix type for the covariance Σ, e.g. Eigen::MatrixXd.
147+ * @tparam Msgs Diagnostics container type for capturing warnings/errors.
148+ *
149+ * @param[in,out] objective_new On entry: objective at the full‐step `a` (must satisfy objective_new < objective_old). On exit: best objective found.
150+ * @param[in,out] a On entry: candidate parameter vector. On exit: updated to the step achieving the lowest objective.
151+ * @param[in,out] theta On entry: Σ·a for the initial candidate. On exit: Σ·a for the accepted best step.
152+ * @param[in,out] a_prev On entry: previous parameter vector, with objective `objective_old`. On exit: rolled forward to each newly accepted step.
153+ * @param[in] ll_fun Callable that computes the log‐likelihood given `(theta, ll_args, msgs)`.
154+ * @param[in] ll_args Arguments forwarded to `ll_fun` at each evaluation.
155+ * @param[in] covariance Covariance matrix Σ used to compute `theta = Σ·a`.
156+ * @param[in] max_steps_line_search Maximum number of iterations.
157+ * @param[in] objective_old Objective value at the initial `a_prev` (used as f₀ for the first pass).
158+ * @param[in,out] msgs Pointer to a diagnostics container; may be used by `ll_fun` to record warnings.
159+ */
160+ template <typename AVec, typename APrev, typename ThetaVec,
161+ typename LLFun, typename LLArgs, typename Covar, typename Msgs>
162+ inline void line_search (double & objective_new,
163+ AVec& a,
164+ ThetaVec& theta,
165+ APrev& a_prev,
166+ LLFun&& ll_fun,
167+ LLArgs&& ll_args,
168+ Covar&& covariance,
169+ const int max_steps_line_search,
170+ const double objective_old,
171+ double tolerance,
172+ Msgs* msgs) {
173+ Eigen::VectorXd a_tmp (a.size ());
174+ double objective_new_tmp = 0.0 ;
175+ double objective_old_tmp = objective_old;
176+ Eigen::VectorXd theta_tmp (covariance.rows ());
177+ for (int j = 0 ; j < max_steps_line_search && (objective_new < objective_old_tmp);
178+ ++j) {
179+ a_tmp.noalias () = a_prev + 0.5 * (a - a_prev);
180+ theta_tmp.noalias () = covariance * a_tmp;
181+ if (!theta_tmp.allFinite ()) {
182+ break ;
183+ } else {
184+ objective_new_tmp = -0.5 * a_tmp.dot (theta_tmp)
185+ + laplace_likelihood::log_likelihood (
186+ ll_fun, theta_tmp, ll_args, msgs);
187+ if (objective_new_tmp < objective_new) {
188+ a_prev.swap (a);
189+ a.swap (a_tmp);
190+ theta.swap (theta_tmp);
191+ objective_old_tmp = objective_new;
192+ objective_new = objective_new_tmp;
193+ } else {
194+ break ;
195+ }
196+ }
197+ }
230198}
231199
232200// iter_tuple_n
@@ -479,10 +447,9 @@ inline auto laplace_marginal_density_est(LLFun&& ll_fun, LLTupleArgs&& ll_args,
479447 + laplace_likelihood::log_likelihood (ll_fun, theta,
480448 ll_args_vals, msgs);
481449 if (options.max_steps_line_search ) {
482- std::tie (objective_new, a, theta)
483- = line_search (objective_new, std::move (a), a_prev, std::move (theta),
450+ line_search (objective_new, a, theta, a_prev,
484451 ll_fun, ll_args_vals, covariance,
485- options.max_steps_line_search , objective_old, msgs);
452+ options.max_steps_line_search , objective_old, options. tolerance , msgs);
486453 }
487454 // Check for convergence
488455 if (abs (objective_new - objective_old) < options.tolerance ) {
@@ -547,10 +514,9 @@ inline auto laplace_marginal_density_est(LLFun&& ll_fun, LLTupleArgs&& ll_args,
547514 + laplace_likelihood::log_likelihood (
548515 ll_fun, value_of (theta), ll_args_vals, msgs);
549516 if (options.max_steps_line_search > 0 ) {
550- std::tie (objective_new, a, theta)
551- = line_search (objective_new, std::move (a), a_prev, std::move (theta),
517+ line_search (objective_new, a, theta, a_prev,
552518 ll_fun, ll_args_vals, covariance,
553- options.max_steps_line_search , objective_old, msgs);
519+ options.max_steps_line_search , objective_old, options. tolerance , msgs);
554520 }
555521 // Check for convergence
556522 if (abs (objective_new - objective_old) < options.tolerance ) {
@@ -600,10 +566,9 @@ inline auto laplace_marginal_density_est(LLFun&& ll_fun, LLTupleArgs&& ll_args,
600566 ll_args_vals, msgs);
601567 // linesearch
602568 if (options.max_steps_line_search > 0 ) {
603- std::tie (objective_new, a, theta)
604- = line_search (objective_new, std::move (a), a_prev, std::move (theta),
569+ line_search (objective_new, a, theta, a_prev,
605570 ll_fun, ll_args_vals, covariance,
606- options.max_steps_line_search , objective_old, msgs);
571+ options.max_steps_line_search , objective_old, options. tolerance , msgs);
607572 }
608573 // Check for convergence
609574 if (abs (objective_new - objective_old) < options.tolerance ) {
@@ -633,7 +598,6 @@ inline auto laplace_marginal_density_est(LLFun&& ll_fun, LLTupleArgs&& ll_args,
633598 MatrixXd::Identity (theta_size, theta_size) + covariance * W);
634599 // L on upper and U on lower triangular
635600 b.noalias () = W * theta + theta_grad;
636-
637601 a.noalias () = b - W * LU .solve (covariance * b);
638602 // Simple Newton step
639603 theta.noalias () = covariance * a;
@@ -647,13 +611,10 @@ inline auto laplace_marginal_density_est(LLFun&& ll_fun, LLTupleArgs&& ll_args,
647611 ll_fun, value_of (theta), ll_args_vals, msgs);
648612
649613 // TODO(Charles): How do we handle NA values in theta?
650- // linesearch
651- // CHECK -- does linesearch work for options.solver 2?
652614 if (options.max_steps_line_search > 0 ) {
653- std::tie (objective_new, a, theta)
654- = line_search (objective_new, std::move (a), a_prev, std::move (theta),
615+ line_search (objective_new, a, theta, a_prev,
655616 ll_fun, ll_args_vals, covariance,
656- options.max_steps_line_search , objective_old, msgs);
617+ options.max_steps_line_search , objective_old, options. tolerance , msgs);
657618 }
658619 if (abs (objective_new - objective_old) < options.tolerance ) {
659620 // TODO(Charles): There has to be a simple trick for this
@@ -1046,8 +1007,6 @@ inline auto laplace_marginal_density(const LLFun& ll_fun, LLTupleArgs&& ll_args,
10461007 return covariance_function (args..., msgs);
10471008 },
10481009 covar_args_copy));
1049- // var Z = laplace_pseudo_target(K_var, md_est.a, R,
1050- // md_est.theta_grad, s2);
10511010 arena_t <Eigen::MatrixXd> K_adj_arena
10521011 = 0.5 * md_est.a * md_est.a .transpose () - 0.5 * R
10531012 + s2 * md_est.theta_grad .transpose ()
0 commit comments