1#ifndef STAN_MATH_MIX_FUNCTOR_LAPLACE_MARGINAL_DENSITY_HPP
2#define STAN_MATH_MIX_FUNCTOR_LAPLACE_MARGINAL_DENSITY_HPP
13#include <unsupported/Eigen/MatrixFunctions>
50template <
bool HasInitTheta>
59 Eigen::VectorXd theta_0{0};
66template <
typename Covar,
typename ThetaVec,
typename WR,
typename L_t,
67 typename A_vec,
typename ThetaGrad,
typename LU_t,
typename KRoot>
70 double lmd{std::numeric_limits<double>::infinity()};
88 WR&& W_r_, L_t&& L_, A_vec&& a_,
89 ThetaGrad&& theta_grad_, LU_t&& LU_,
109template <
typename WRootMat>
111 const Eigen::SparseMatrix<double>& W,
112 const Eigen::Index block_size) {
113 int n_block = W.cols() / block_size;
114 Eigen::MatrixXd local_block(block_size, block_size);
115 Eigen::MatrixXd local_block_sqrt(block_size, block_size);
116 Eigen::MatrixXd sqrt_t_mat = Eigen::MatrixXd::Zero(block_size, block_size);
119 for (
int i = 0; i < n_block; i++) {
120 sqrt_t_mat.setZero();
122 = W.block(i * block_size, i * block_size, block_size, block_size);
123 if (Eigen::isnan(local_block.array()).any()) {
124 throw std::domain_error(
125 std::string(
"Error in block_matrix_sqrt: "
126 "NaNs detected in block diagonal starting at (")
127 + std::to_string(i) +
", " + std::to_string(i) +
")");
130 Eigen::RealSchur<Eigen::MatrixXd> schurOfA(local_block);
132 const auto& t_mat = schurOfA.matrixT();
133 const auto& u_mat = schurOfA.matrixU();
135 if ((t_mat.diagonal().array() < 0).any()) {
136 throw std::domain_error(
137 std::string(
"Error in block_matrix_sqrt: "
138 "values less than 0 detected in block diagonal's schur "
139 "decomposition starting at (")
140 + std::to_string(i) +
", " + std::to_string(i) +
")");
144 Eigen::matrix_sqrt_quasi_triangular(t_mat, sqrt_t_mat);
146 local_block_sqrt = u_mat * sqrt_t_mat * u_mat.adjoint();
147 }
catch (
const std::exception&
e) {
148 throw std::domain_error(
149 "Error in block_matrix_sqrt: "
150 "The matrix is not positive definite");
152 for (
int k = 0; k < block_size; k++) {
153 for (
int j = 0; j < block_size; j++) {
154 W_root.coeffRef(i * block_size + j, i * block_size + k)
155 = local_block_sqrt(j, k);
168template <
typename WRootMat>
170 const Eigen::SparseMatrix<double>& W,
171 const Eigen::Index block_size) {
172 int n_block = W.cols() / block_size;
173 Eigen::MatrixXd local_block(block_size, block_size);
174 Eigen::MatrixXd local_block_sqrt(block_size, block_size);
175 Eigen::MatrixXd sqrt_t_mat = Eigen::MatrixXd::Zero(block_size, block_size);
178 for (
int i = 0; i < n_block; i++) {
179 sqrt_t_mat.setZero();
181 = W.block(i * block_size, i * block_size, block_size, block_size);
182 if (Eigen::isnan(local_block.array()).any()) {
183 throw std::domain_error(
184 std::string(
"Error in block_matrix_sqrt: "
185 "NaNs detected in block diagonal starting at (")
186 + std::to_string(i) +
", " + std::to_string(i) +
")");
190 Eigen::LLT<Eigen::Ref<Eigen::MatrixXd>> llt(local_block);
191 if (llt.info() != Eigen::Success) {
192 throw std::runtime_error(
"Cholesky failed on block "
193 + std::to_string(i));
195 const auto Lb = llt.matrixL();
196 for (
int k = 0; k < block_size; k++) {
197 for (
int j = k; j < block_size; j++) {
198 W_root.coeffRef(i * block_size + j, i * block_size + k) = Lb(j, k);
201 }
catch (
const std::exception&
e) {
204 = W.block(i * block_size, i * block_size, block_size, block_size);
206 Eigen::RealSchur<Eigen::MatrixXd> schurOfA(local_block);
208 const auto& t_mat = schurOfA.matrixT();
209 const auto& u_mat = schurOfA.matrixU();
211 if ((t_mat.diagonal().array() < 0).any()) {
212 throw std::domain_error(
213 std::string(
"Error in block_matrix_sqrt: "
214 "values less than 0 detected in block diagonal's schur "
215 "decomposition starting at (")
216 + std::to_string(i) +
", " + std::to_string(i) +
")");
220 Eigen::matrix_sqrt_quasi_triangular(t_mat, sqrt_t_mat);
222 local_block_sqrt.noalias() = u_mat * sqrt_t_mat * u_mat.adjoint();
223 }
catch (
const std::exception&
e) {
224 throw std::domain_error(
225 "Error in block_matrix_sqrt: "
226 "The matrix is not positive definite");
228 for (
int k = 0; k < block_size; k++) {
229 for (
int j = 0; j < block_size; j++) {
230 W_root.coeffRef(i * block_size + j, i * block_size + k)
231 = local_block_sqrt(j, k);
234 throw std::domain_error(
235 "Error in block_matrix_sqrt: "
236 "The matrix is not positive definite");
273template <
typename AVec,
typename APrev,
typename ThetaVec,
typename LLFun,
274 typename LLArgs,
typename Covar,
typename Msgs>
275inline void line_search(
double& objective_new, AVec& a, ThetaVec& theta,
276 APrev& a_prev, LLFun&& ll_fun, LLArgs&& ll_args,
277 Covar&& covariance,
const int max_steps_line_search,
278 const double objective_old,
double tolerance,
280 Eigen::VectorXd a_tmp(a.size());
281 double objective_new_tmp = 0.0;
282 double objective_old_tmp = objective_old;
283 Eigen::VectorXd theta_tmp(covariance.rows());
285 j < max_steps_line_search && (objective_new < objective_old_tmp); ++j) {
286 a_tmp.noalias() = a_prev + 0.5 * (a - a_prev);
287 theta_tmp.noalias() = covariance * a_tmp;
288 if (!theta_tmp.allFinite()) {
291 objective_new_tmp = -0.5 * a_tmp.dot(theta_tmp)
293 ll_fun, theta_tmp, ll_args, msgs);
294 if (objective_new_tmp < objective_new) {
297 theta.swap(theta_tmp);
298 objective_old_tmp = objective_new;
299 objective_new = objective_new_tmp;
310template <
typename Output>
312 if constexpr (is_all_arithmetic_scalar_v<Output>) {
316 [](
auto&& output_i) {
317 using output_i_t = std::decay_t<
decltype(output_i)>;
318 if constexpr (is_all_arithmetic_scalar_v<output_i_t>) {
321 for (Eigen::Index i = 0; i < output_i.size(); ++i) {
322 output_i[i].adj() = 0;
324 }
else if constexpr (is_eigen_v<output_i_t>) {
325 output_i.adj().setZero();
326 }
else if constexpr (is_stan_scalar_v<output_i_t>) {
330 sizeof(std::decay_t<output_i_t>*) == 0,
331 "INTERNAL ERROR:(laplace_marginal_lpdf) set_zero_adjoints was "
332 "not able to deduce the actions needed for the given type. "
333 "This is an internal error, please report it: "
334 "https://github.com/stan-dev/math/issues");
337 std::forward<Output>(output));
349template <
bool ZeroInput =
false,
typename Output,
typename Input,
354 [](
auto&& output_i,
auto&& input_i) {
355 using output_i_t = std::decay_t<
decltype(output_i)>;
356 if constexpr (is_std_vector_v<output_i_t>) {
357 Eigen::Map<Eigen::Matrix<double, -1, 1>> output_map(output_i.data(),
359 Eigen::Map<Eigen::Matrix<
var, -1, 1>> input_map(input_i.data(),
361 output_map.array() += input_map.adj().array();
362 if constexpr (ZeroInput) {
363 input_map.adj().setZero();
365 }
else if constexpr (is_eigen_v<output_i_t>) {
366 output_i.array() += input_i.adj().array();
367 if constexpr (ZeroInput) {
368 input_i.adj().setZero();
370 }
else if constexpr (is_stan_scalar_v<output_i_t>) {
371 output_i += input_i.adj();
372 if constexpr (ZeroInput) {
377 sizeof(std::decay_t<output_i_t>*) == 0,
378 "INTERNAL ERROR:(laplace_marginal_lpdf) set_zero_adjoints was "
379 "not able to deduce the actions needed for the given type. "
380 "This is an internal error, please report it: "
381 "https://github.com/stan-dev/math/issues");
384 std::forward<Output>(output), std::forward<Input>(input));
396template <
typename NameStr,
typename ParamStr,
typename Param>
399 std::string msg = std::string(
"Error in ") + name_str +
": "
400 + std::string(param_str) +
" contains NaN values";
401 if ((Eigen::isnan(param.array()) || Eigen::isinf(param.array())).all()) {
402 msg +=
" for all values.";
403 throw std::domain_error(msg);
405 msg +=
" at indices [";
406 for (
int i = 0; i < param.size(); ++i) {
408 msg += std::to_string(i) +
", ";
414 throw std::domain_error(msg);
465template <
typename LLFun,
typename LLTupleArgs,
typename CovarFun,
466 typename CovarArgs,
bool InitTheta,
469 LLFun&& ll_fun, LLTupleArgs&& ll_args, CovarFun&& covariance_function,
471 std::ostream* msgs) {
472 using Eigen::MatrixXd;
473 using Eigen::SparseMatrix;
474 using Eigen::VectorXd;
475 if constexpr (InitTheta) {
477 check_finite(
"laplace_marginal",
"initial guess", options.theta_0);
480 check_positive(
"laplace_marginal",
"max_num_steps", options.max_num_steps);
482 options.hessian_block_size);
484 options.max_steps_line_search);
487 [msgs, &covariance_function](
auto&&... args) {
488 return covariance_function(args..., msgs);
491 check_square(
"laplace_marginal",
"covariance", covariance);
493 const Eigen::Index theta_size = covariance.rows();
495 if (
unlikely(theta_size % options.hessian_block_size != 0)) {
497 std::stringstream msg;
498 msg <<
"laplace_marginal_density: The hessian size (" << theta_size
499 <<
", " << theta_size
500 <<
") is not divisible by the hessian block size ("
501 << options.hessian_block_size
503 ". Try a hessian block size such as [1, ";
504 for (
int i = 2; i < 12; ++i) {
505 if (theta_size % i == 0) {
509 msg.str().pop_back();
510 msg.str().pop_back();
512 throw std::domain_error(msg.str());
516 auto throw_overstep = [](
const auto max_num_steps)
STAN_COLD_PATH {
517 throw std::domain_error(
518 std::string(
"laplace_marginal_density: max number of iterations: ")
519 + std::to_string(max_num_steps) +
" exceeded.");
521 auto ll_args_vals =
value_of(ll_args);
522 Eigen::VectorXd theta = [theta_size, &options]() {
523 if constexpr (InitTheta) {
524 return options.theta_0;
526 return Eigen::VectorXd::Zero(theta_size);
529 double objective_old = std::numeric_limits<double>::lowest();
530 double objective_new = std::numeric_limits<double>::lowest() + 1;
531 Eigen::VectorXd a_prev = Eigen::VectorXd::Zero(theta_size);
532 Eigen::MatrixXd B(theta_size, theta_size);
533 Eigen::VectorXd a(theta_size);
534 Eigen::VectorXd b(theta_size);
535 if (options.solver == 1) {
536 if (options.hessian_block_size == 1) {
537 for (Eigen::Index i = 0; i <= options.max_num_steps; i++) {
539 ll_fun, theta, options.hessian_block_size, ll_args, msgs);
540 Eigen::VectorXd W_r(W.rows());
543 for (Eigen::Index i = 0; i < W.rows(); i++) {
544 if (W.coeff(i, i) < 0) {
545 throw std::domain_error(
546 "laplace_marginal_density: Hessian matrix is not positive "
549 W_r.coeffRef(i) = std::sqrt(W.coeff(i, i));
552 B.noalias() = MatrixXd::Identity(theta_size, theta_size)
553 + W_r.asDiagonal() * covariance * W_r.asDiagonal();
554 Eigen::LLT<Eigen::Ref<Eigen::MatrixXd>> llt_B(B);
555 auto L = llt_B.matrixL();
556 auto LT = llt_B.matrixU();
557 b.noalias() = W.diagonal().cwiseProduct(theta) + theta_grad;
561 * LT.solve(L.solve(W_r.cwiseProduct(covariance * b)));
563 theta.noalias() = covariance * a;
564 objective_old = objective_new;
566 (Eigen::isinf(theta.array()) || Eigen::isnan(theta.array()))
568 throw_nan(
"laplace_marginal_density",
"theta", theta);
570 objective_new = -0.5 * a.dot(theta)
572 ll_fun, theta, ll_args_vals, msgs);
573 if (options.max_steps_line_search) {
574 line_search(objective_new, a, theta, a_prev, ll_fun, ll_args_vals,
575 covariance, options.max_steps_line_search, objective_old,
576 options.tolerance, msgs);
579 if (
abs(objective_new - objective_old) < options.tolerance) {
580 const double B_log_determinant
581 = 2.0 * llt_B.matrixLLT().diagonal().array().log().sum();
585 objective_new - 0.5 * B_log_determinant,
586 std::move(covariance),
591 std::move(theta_grad),
592 Eigen::PartialPivLU<Eigen::MatrixXd>{},
593 Eigen::MatrixXd(0, 0)};
595 a_prev = std::move(a);
600 Eigen::SparseMatrix<double> W_r(theta_size, theta_size);
601 Eigen::Index block_size = options.hessian_block_size;
602 W_r.reserve(Eigen::VectorXi::Constant(W_r.cols(), block_size));
603 const Eigen::Index n_block = W_r.cols() / block_size;
605 for (Eigen::Index i = 0; i < n_block; i++) {
606 for (Eigen::Index k = 0; k < block_size; k++) {
607 for (Eigen::Index j = 0; j < block_size; j++) {
608 W_r.insert(i * block_size + j, i * block_size + k) = 1.0;
612 W_r.makeCompressed();
613 for (Eigen::Index i = 0; i <= options.max_num_steps; i++) {
615 ll_fun, theta, options.hessian_block_size, ll_args, msgs);
616 for (Eigen::Index i = 0; i < W.rows(); i++) {
617 if (W.coeff(i, i) < 0) {
618 throw std::domain_error(
619 "laplace_marginal_density: Hessian matrix is not positive "
624 B.noalias() = MatrixXd::Identity(theta_size, theta_size)
625 + W_r * (covariance * W_r);
626 Eigen::LLT<Eigen::Ref<Eigen::MatrixXd>> llt_B(B);
627 auto L = llt_B.matrixL();
628 auto LT = llt_B.matrixU();
629 b.noalias() = W * theta + theta_grad;
630 a.noalias() = b - W_r * LT.solve(L.solve(W_r * (covariance * b)));
632 theta.noalias() = covariance * a;
633 objective_old = objective_new;
635 (Eigen::isinf(theta.array()) || Eigen::isnan(theta.array()))
637 throw_nan(
"laplace_marginal_density",
"theta", theta);
639 objective_new = -0.5 * a.dot(
value_of(theta))
641 ll_fun,
value_of(theta), ll_args_vals, msgs);
642 if (options.max_steps_line_search > 0) {
643 line_search(objective_new, a, theta, a_prev, ll_fun, ll_args_vals,
644 covariance, options.max_steps_line_search, objective_old,
645 options.tolerance, msgs);
648 if (
abs(objective_new - objective_old) < options.tolerance) {
649 const double B_log_determinant
650 = 2.0 * llt_B.matrixLLT().diagonal().array().log().sum();
652 objective_new - 0.5 * B_log_determinant,
653 std::move(covariance),
658 std::move(theta_grad),
659 Eigen::PartialPivLU<Eigen::MatrixXd>{},
660 Eigen::MatrixXd(0, 0)};
667 throw_overstep(options.max_num_steps);
668 }
else if (options.solver == 2) {
669 Eigen::MatrixXd K_root
670 = covariance.template selfadjointView<Eigen::Lower>().llt().matrixL();
671 for (Eigen::Index i = 0; i <= options.max_num_steps; i++) {
673 ll_fun, theta, options.hessian_block_size, ll_args, msgs);
674 B.noalias() = MatrixXd::Identity(theta_size, theta_size)
675 + K_root.transpose() * W * K_root;
676 Eigen::LLT<Eigen::Ref<Eigen::MatrixXd>> llt_B(B);
677 auto L = llt_B.matrixL();
678 auto LT = llt_B.matrixU();
679 b.noalias() = W * theta + theta_grad;
681 = K_root.transpose().template triangularView<Eigen::Upper>().solve(
682 LT.solve(L.solve(K_root.transpose() * b)));
684 theta.noalias() = covariance * a;
685 objective_old = objective_new;
686 if (
unlikely((Eigen::isinf(theta.array()) || Eigen::isnan(theta.array()))
688 throw_nan(
"laplace_marginal_density",
"theta", theta);
690 objective_new = -0.5 * a.dot(theta)
694 if (options.max_steps_line_search > 0) {
695 line_search(objective_new, a, theta, a_prev, ll_fun, ll_args_vals,
696 covariance, options.max_steps_line_search, objective_old,
697 options.tolerance, msgs);
700 if (
abs(objective_new - objective_old) < options.tolerance) {
701 const double B_log_determinant
702 = 2.0 * llt_B.matrixLLT().diagonal().array().log().sum();
704 objective_new - 0.5 * B_log_determinant,
705 std::move(covariance),
708 std::move(Eigen::MatrixXd(L)),
710 std::move(theta_grad),
711 Eigen::PartialPivLU<Eigen::MatrixXd>{},
718 throw_overstep(options.max_num_steps);
719 }
else if (options.solver == 3) {
720 for (Eigen::Index i = 0; i <= options.max_num_steps; i++) {
722 ll_fun, theta, options.hessian_block_size, ll_args, msgs);
723 Eigen::PartialPivLU<Eigen::MatrixXd> LU(
724 MatrixXd::Identity(theta_size, theta_size) + covariance * W);
726 b.noalias() = W * theta + theta_grad;
727 a.noalias() = b - W * LU.solve(covariance * b);
729 theta.noalias() = covariance * a;
730 objective_old = objective_new;
731 if (((Eigen::isinf(theta.array()) || Eigen::isnan(theta.array()))
733 throw_nan(
"laplace_marginal_density",
"theta", theta);
735 objective_new = -0.5 * a.dot(
value_of(theta))
737 ll_fun,
value_of(theta), ll_args_vals, msgs);
739 if (options.max_steps_line_search > 0) {
740 line_search(objective_new, a, theta, a_prev, ll_fun, ll_args_vals,
741 covariance, options.max_steps_line_search, objective_old,
742 options.tolerance, msgs);
744 if (
abs(objective_new - objective_old) < options.tolerance) {
746 const double B_log_determinant =
log(LU.determinant());
748 objective_new - 0.5 * B_log_determinant,
749 std::move(covariance),
752 Eigen::MatrixXd(0, 0),
754 std::move(theta_grad),
756 Eigen::MatrixXd(0, 0)};
762 throw_overstep(options.max_num_steps);
764 throw std::domain_error(
765 std::string(
"You chose a solver (") + std::to_string(options.solver)
766 +
") that is not valid. Please choose either 1, 2, or 3.");
797 typename LLFun,
typename LLTupleArgs,
typename CovarFun,
typename CovarArgs,
801 LLFun&& ll_fun, LLTupleArgs&& ll_args, CovarFun&& covariance_function,
803 std::ostream* msgs) {
805 std::forward<LLFun>(ll_fun), std::forward<LLTupleArgs>(ll_args),
806 std::forward<CovarFun>(covariance_function),
807 std::forward<CovarArgs>(covar_args), options, msgs)
820template <
typename Output,
typename Input,
825 [](
auto&& output_i,
auto&& input_i) {
826 using output_i_t = std::decay_t<
decltype(output_i)>;
827 if constexpr (is_std_vector_v<output_i_t>) {
828 Eigen::Map<Eigen::Matrix<double, -1, 1>> output_map(output_i.data(),
830 Eigen::Map<Eigen::Matrix<double, -1, 1>> input_map(input_i.data(),
832 output_map.array() += input_map.array();
833 }
else if constexpr (is_eigen_v<output_i_t>) {
834 output_i.array() += input_i.array();
835 }
else if constexpr (is_stan_scalar_v<output_i_t>) {
839 sizeof(std::decay_t<output_i_t>*) == 0,
840 "INTERNAL ERROR:(laplace_marginal_lpdf) set_zero_adjoints was "
841 "not able to deduce the actions needed for the given type. "
842 "This is an internal error, please report it: "
843 "https://github.com/stan-dev/math/issues");
846 std::forward<Output>(output), std::forward<Input>(input));
851template <
bool ZeroInput = false>
853 const std::tuple<>& input)
noexcept {}
863template <
bool ZeroInput =
false,
typename Output,
typename Input,
868 [](
auto&& output_i,
auto&& input_i) {
869 using output_i_t = std::decay_t<
decltype(output_i)>;
870 if constexpr (is_std_vector_v<output_i_t>) {
871 Eigen::Map<Eigen::Matrix<double, -1, 1>> output_map(output_i.data(),
873 Eigen::Map<Eigen::Matrix<
var, -1, 1>> input_map(input_i.data(),
875 output_map.array() += 0.5 * input_map.adj().array();
876 if constexpr (ZeroInput) {
877 input_map.adj().setZero();
879 }
else if constexpr (is_eigen_v<output_i_t>) {
880 output_i.array() += 0.5 * input_i.adj().array();
881 if constexpr (ZeroInput) {
882 input_i.adj().setZero();
884 }
else if constexpr (is_stan_scalar_v<output_i_t>) {
885 output_i += (0.5 * input_i.adj());
886 if constexpr (ZeroInput) {
891 sizeof(std::decay_t<output_i_t>*) == 0,
892 "INTERNAL ERROR:(laplace_marginal_lpdf) set_zero_adjoints was "
893 "not able to deduce the actions needed for the given type. "
894 "This is an internal error, please report it: "
895 "https://github.com/stan-dev/math/issues");
898 std::forward<Output>(output), std::forward<Input>(input));
903 return stan::math::filter_map<is_any_var_scalar>(
904 [](
auto&&
arg) ->
decltype(
auto) {
905 return std::forward<decltype(arg)>(
arg);
914template <
typename Input>
916 if constexpr (is_tuple_v<Input>) {
917 return stan::math::filter_map<is_any_var_scalar>(
919 }
else if constexpr (is_std_vector_v<Input>) {
920 if constexpr (!is_var_v<value_type_t<Input>>) {
921 const auto output_size = input.size();
923 ret.reserve(output_size);
924 for (Eigen::Index i = 0; i < output_size; ++i) {
931 }
else if constexpr (is_eigen_v<Input>) {
936 return static_cast<double>(0.0);
948template <
typename Output,
typename Input>
950 if constexpr (is_tuple_v<Output>) {
951 static_assert(
sizeof(std::decay_t<Output>*) == 0,
952 "INTERNAL ERROR:(laplace_marginal_lpdf) "
953 "Accumulate Adjoints called on a tuple, but tuples cannot be "
954 "on the reverse mode stack! "
955 "This is an internal error, please report it: "
956 "https://github.com/stan-dev/math/issues");
957 }
else if constexpr (is_std_vector_v<Output>) {
958 if constexpr (!is_var_v<value_type_t<Output>>) {
959 const auto output_size = output.size();
960 for (std::size_t i = 0; i < output_size; ++i) {
964 Eigen::Map<Eigen::Matrix<
var, -1, 1>> output_map(output.data(),
966 Eigen::Map<
const Eigen::Matrix<double, -1, 1>> input_map(input.data(),
968 output_map.array().adj() += ret->adj_ * input_map.array();
970 }
else if constexpr (is_eigen_v<Output>) {
971 output.adj().array() += ret->adj_ * input.array();
972 }
else if constexpr (is_var_v<Output>) {
973 output.adj() += ret->adj_ * input;
987template <
typename Output,
typename Input>
990 if constexpr (is_tuple_v<Output>) {
992 [ret](
auto&& inner_arg,
auto&& inner_input)
mutable {
994 ret, std::forward<
decltype(inner_arg)>(inner_arg),
995 std::forward<
decltype(inner_input)>(inner_input));
997 std::forward<Output>(output), std::forward<Input>(input));
998 }
else if constexpr (is_std_vector_containing_tuple_v<Output>) {
999 for (std::size_t i = 0; i < output.size(); ++i) {
1004 [vi = ret.vi_, arg_arena =
to_arena(std::forward<Output>(output)),
1005 input_arena =
to_arena(std::forward<Input>(input))]()
mutable {
1038template <
typename LLFun,
typename LLTupleArgs,
typename CovarFun,
1039 typename CovarArgs,
bool InitTheta,
1042 CovarFun&& covariance_function,
1043 CovarArgs&& covar_args,
1045 std::ostream* msgs) {
1046 auto covar_args_refs =
to_ref(std::forward<CovarArgs>(covar_args));
1047 auto ll_args_refs =
to_ref(std::forward<LLTupleArgs>(ll_args));
1060 = conditional_copy_and_promote<is_any_var_scalar, var, COPY_TYPE::DEEP>(
1064 ll_fun, ll_args_copy, covariance_function,
value_of(covar_args_refs),
1077 [](
auto&& output_i,
auto&& ll_arg_i) {
1079 internal::collect_adjoints<true>(output_i, ll_arg_i);
1082 partial_parm, ll_args_filter);
1083 if (options.solver == 1) {
1084 if (options.hessian_block_size == 1) {
1087 = md_est.L.template triangularView<Eigen::Lower>().solve(
1088 md_est.W_r.toDense());
1089 R = tmp.transpose() * tmp;
1091 = md_est.L.template triangularView<Eigen::Lower>().solve(
1092 md_est.W_r * md_est.covariance);
1093 if constexpr (!ll_args_contain_var) {
1096 * (md_est.covariance.diagonal() - (C.transpose() * C).diagonal())
1098 ll_fun, md_est.theta,
value_of(ll_args_copy), msgs))));
1102 ll_fun, md_est.theta, A, options.hessian_block_size, ll_args_copy,
1104 s2.deep_copy(s2_tmp);
1105 internal::copy_compute_s2<true>(partial_parm, ll_args_filter);
1110 = md_est.L.template triangularView<Eigen::Lower>().solve(
1111 md_est.W_r.toDense());
1112 R = tmp.transpose() * tmp;
1114 = md_est.L.template triangularView<Eigen::Lower>().solve(
1115 md_est.W_r * md_est.covariance);
1118 options.hessian_block_size,
1119 ll_args_copy, msgs);
1120 s2.deep_copy(s2_tmp);
1121 internal::copy_compute_s2<true>(partial_parm, ll_args_filter);
1123 }
else if (options.solver == 2) {
1125 - md_est.W_r * md_est.K_root
1126 * md_est.L.transpose()
1127 .template triangularView<Eigen::Upper>()
1129 md_est.L.template triangularView<Eigen::Lower>()
1130 .solve(md_est.K_root.transpose() * md_est.W_r));
1133 = md_est.L.template triangularView<Eigen::Lower>().solve(
1134 md_est.K_root.transpose());
1136 ll_fun, md_est.theta, (C.transpose() * C).eval(),
1137 options.hessian_block_size, ll_args_copy, msgs);
1138 s2.deep_copy(s2_tmp);
1139 internal::copy_compute_s2<true>(partial_parm, ll_args_filter);
1141 LU_solve_covariance = md_est.LU.solve(md_est.covariance);
1142 R = md_est.W_r - md_est.W_r * LU_solve_covariance * md_est.W_r;
1145 - md_est.covariance * md_est.W_r * LU_solve_covariance;
1147 options.hessian_block_size,
1148 ll_args_copy, msgs);
1149 s2.deep_copy(s2_tmp);
1150 internal::copy_compute_s2<true>(partial_parm, ll_args_filter);
1153 if constexpr (is_any_var_scalar_v<scalar_type_t<CovarArgs>>) {
1154 [&covar_args_refs, &covar_args_adj, &md_est, &R, &s2,
1155 &covariance_function, &msgs]()
mutable {
1157 auto covar_args_copy
1163 [&covariance_function, &msgs](
auto&&... args) {
1164 return covariance_function(args..., msgs);
1168 = 0.5 * md_est.a * md_est.a.transpose() - 0.5 * R
1169 + s2 * md_est.theta_grad.transpose()
1170 - (R * (K_var.val() * s2)) * md_est.theta_grad.transpose();
1172 K_var.adj().array() += vi.adj() * K_adj_arena.array();
1175 auto covar_args_filter
1180 if constexpr (ll_args_contain_var) {
1182 if (options.solver == 1 || options.solver == 2) {
1183 v = md_est.covariance * s2
1184 - md_est.covariance * R * md_est.covariance * s2;
1186 v = LU_solve_covariance * s2;
1189 ll_args_copy, msgs);
1190 internal::collect_adjoints<true>(partial_parm, ll_args_filter);
1194 if constexpr (is_any_var_scalar_v<CovarArgs>) {
1199 if constexpr (ll_args_contain_var) {
A class following the RAII idiom to start and recover nested autodiff scopes.
void throw_nan(NameStr &&name_str, ParamStr &¶m_str, Param &¶m)
Throws an error if the parameter contains NaN or Inf values.
void set_zero_adjoint(Output &&output)
Set all adjoints of the output to zero.
constexpr decltype(auto) filter_var_scalar_types(T &&t)
void reverse_pass_collect_adjoints(var ret, Output &&output, Input &&input)
Collects adjoints from a tuple or std::vector of tuples.
void block_matrix_chol_L(WRootMat &W_root, const Eigen::SparseMatrix< double > &W, const Eigen::Index block_size)
Performs a Cholesky decomposition on a block diagonal matrix.
void line_search(double &objective_new, AVec &a, ThetaVec &theta, APrev &a_prev, LLFun &&ll_fun, LLArgs &&ll_args, Covar &&covariance, const int max_steps_line_search, const double objective_old, double tolerance, Msgs *msgs)
Performs a simple line search.
void constexpr copy_compute_s2(const std::tuple<> &output, const std::tuple<> &input) noexcept
Base case for zero sized tuples.
constexpr auto make_zeroed_arena(Input &&input)
Creates an arena type from the input with initialized with zeros.
auto laplace_marginal_density_est(LLFun &&ll_fun, LLTupleArgs &&ll_args, CovarFun &&covariance_function, CovarArgs &&covar_args, const laplace_options< InitTheta > &options, std::ostream *msgs)
For a latent Gaussian model with hyperparameters phi and latent variables theta, and observations y,...
void collect_adjoints(Output &output, Input &&input)
Collect the adjoints from the input and add them to the output.
void block_matrix_sqrt(WRootMat &W_root, const Eigen::SparseMatrix< double > &W, const Eigen::Index block_size)
Returns the principal square root of a block diagonal matrix.
auto conditional_copy_and_promote(Args &&... args)
Conditional copy and promote a type's scalar type to a PromotedType.
COPY_TYPE
Decide if object should be deep or shallow copied when using conditional_copy_and_promote .
auto compute_s2(F &&f, Theta &&theta, AMat &&A, int hessian_block_size, TupleArgs &&ll_args, Stream *msgs)
A wrapper that accepts a tuple as arguments.
auto diff(F &&f, Theta &&theta, const Eigen::Index hessian_block_size, TupleArgs &&ll_tuple, Stream *msgs)
A wrapper that accepts a tuple as arguments.
auto diff_eta_implicit(F &&f, V_t &&v, Theta &&theta, TupleArgs &&ll_args, Stream *msgs)
A wrapper that accepts a tuple as arguments.
auto log_likelihood(F &&f, Theta &&theta, TupleArgs &&ll_tup, Stream *msgs)
A wrapper that accepts a tuple as arguments.
Eigen::VectorXd third_diff(F &&f, Theta &&theta, TupleArgs &&ll_args, Stream *msgs)
A wrapper that accepts a tuple as arguments.
fvar< T > abs(const fvar< T > &x)
void iter_tuple_nested(F &&f, Types &&... args)
Iterate and nest into a tuple or std::vector to apply f to each matrix or scalar type.
void check_square(const char *function, const char *name, const T_y &y)
Check if the specified matrix is square.
void check_nonnegative(const char *function, const char *name, const T_y &y)
Check if y is non-negative.
static constexpr double e()
Return the base of the natural logarithm.
var_value< plain_type_t< T > > make_callback_var(T &&value, F &&functor)
Creates a new var initialized with a callback_vari with a given value and reverse-pass callback funct...
void reverse_pass_callback(F &&functor)
Puts a callback on the autodiff stack to be called in reverse pass.
fvar< T > arg(const std::complex< fvar< T > > &z)
Return the phase angle of the complex argument.
constexpr void for_each(F &&f, const std::tuple<> &)
Apply a function to each element of a tuple.
T value_of(const fvar< T > &v)
Return the value of the specified variable.
fvar< T > log(const fvar< T > &x)
arena_t< T > to_arena(const T &a)
Converts given argument into a type that either has any dynamic allocation on AD stack or schedules i...
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
var_value< Eigen::Matrix< double, T::RowsAtCompileTime, T::ColsAtCompileTime > > to_var_value(const T &a)
Converts an Eigen matrix (or vector or row_vector) or expression of vars into var_value.
void check_nonzero_size(const char *function, const char *name, const T_y &y)
Check if the specified matrix/vector is of non-zero size.
void check_positive(const char *function, const char *name, const T_y &y)
Check if y is positive.
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
static void grad()
Compute the gradient for all variables starting from the end of the AD tape.
constexpr decltype(auto) apply(F &&f, Tuple &&t, PreArgs &&... pre_args)
double laplace_marginal_density(LLFun &&ll_fun, LLTupleArgs &&ll_args, CovarFun &&covariance_function, CovarArgs &&covar_args, const laplace_options< InitTheta > &options, std::ostream *msgs)
For a latent Gaussian model with global parameters phi, latent variables theta, and observations y,...
typename plain_type< std::decay_t< T > >::type plain_type_t
constexpr bool is_any_var_scalar_v
typename internal::arena_type_impl< std::decay_t< T > >::type arena_t
Determines a type that can be used in place of T that does any dynamic allocations on the AD stack.
std::enable_if_t< Check::value > require_t
If condition is true, template is enabled.
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
bool isnan(const stan::math::var &a)
Checks if the given number is NaN.
bool isinf(const stan::math::var &a)
Return 1 if the specified argument is positive infinity or negative infinity and 0 otherwise.
Base implementation for checking if type is std vector.
Defines a static member named value which is defined to be false as the primitive scalar types cannot...
laplace_density_estimates(double lmd_, Covar &&covariance_, ThetaVec &&theta_, WR &&W_r_, L_t &&L_, A_vec &&a_, ThetaGrad &&theta_grad_, LU_t &&LU_, KRoot &&K_root_)
int solver
Which Newton solver to use: (B matrix in equation 1 of https://arxiv.org/pdf/2306....
int max_steps_line_search
Options for the laplace sampler.