|
| 1 | +#ifndef STAN_MATH_PRIM_PROB_GAUSSIAN_COPULA_CHOLESKY_LPDF_HPP |
| 2 | +#define STAN_MATH_PRIM_PROB_GAUSSIAN_COPULA_CHOLESKY_LPDF_HPP |
| 3 | + |
| 4 | +#include <stan/math/prim/meta.hpp> |
| 5 | +#include <stan/math/prim/err.hpp> |
| 6 | +#include <stan/math/prim/fun/inv_Phi.hpp> |
| 7 | +#include <stan/math/prim/fun/to_vector.hpp> |
| 8 | +#include <stan/math/prim/fun/rep_vector.hpp> |
| 9 | +#include <stan/math/prim/fun/vector_seq_view.hpp> |
| 10 | +#include <stan/math/prim/fun/size.hpp> |
| 11 | +#include <stan/math/prim/fun/size_mvt.hpp> |
| 12 | +#include <stan/math/prim/prob/multi_normal_cholesky_lpdf.hpp> |
| 13 | +#include <stan/math/prim/prob/std_normal_lpdf.hpp> |
| 14 | + |
| 15 | +namespace stan { |
| 16 | +namespace math { |
| 17 | +namespace internal { |
| 18 | + |
| 19 | +template <typename Ty_elem, typename Ttuple, std::size_t... I> |
| 20 | +auto apply_cdfs_elem_impl(Ty_elem&& y_elem, Ttuple&& cdf_tuple, std::index_sequence<I...>){ |
| 21 | + auto&& cdf_func = std::get<0>(cdf_tuple); |
| 22 | + return cdf_func( |
| 23 | + std::forward<Ty_elem>(y_elem), |
| 24 | + std::get<I + 1>(std::forward<Ttuple>(cdf_tuple))... |
| 25 | + ); |
| 26 | +} |
| 27 | + |
| 28 | + |
| 29 | +template <typename Ty, typename Ttuple, std::size_t... I> |
| 30 | +auto apply_cdfs_impl(Ty&& y, Ttuple&& cdf_tuple, std::index_sequence<I...>){ |
| 31 | + plain_type_t<Ty> res(y.size()); |
| 32 | + |
| 33 | + // Use initializer-list expansion to assign the result of each CDF |
| 34 | + // to the respective element in the result vector, as we need a constant |
| 35 | + // expression for indexing the tuple of CDF functors |
| 36 | + static_cast<void>(std::initializer_list<int>{( |
| 37 | + res[I] = apply_cdfs_elem_impl( |
| 38 | + std::forward<decltype(y[I])>(y[I]), |
| 39 | + std::get<I>(cdf_tuple), |
| 40 | + std::make_index_sequence< |
| 41 | + // Using size - 1 as the first element is the functor to apply |
| 42 | + std::tuple_size< |
| 43 | + std::remove_reference_t< |
| 44 | + decltype(std::get<I>(cdf_tuple))>>{} - 1>{}), |
| 45 | + 0)...}); |
| 46 | + |
| 47 | + return res; |
| 48 | +} |
| 49 | + |
| 50 | +template <typename Ty, typename Ttuple> |
| 51 | +auto apply_cdfs(Ty&& y, Ttuple&& cdf_tuple){ |
| 52 | + return apply_cdfs_impl( |
| 53 | + std::forward<Ty>(y), |
| 54 | + std::forward<Ttuple>(cdf_tuple), |
| 55 | + std::make_index_sequence< |
| 56 | + std::tuple_size<std::remove_reference_t<Ttuple>>{}>{} |
| 57 | + ); |
| 58 | +} |
| 59 | +} |
| 60 | + |
| 61 | +/* |
| 62 | + vectors ~ gaussian_copula_cholesky(cdf_funs_tuple, chol) |
| 63 | + (cdf_fun, ...) |
| 64 | +*/ |
| 65 | + |
| 66 | +template <typename T_y, typename T_cdf_fun_tuple, typename T_chol, |
| 67 | + typename T_return = return_type_t<T_y, T_cdf_fun_tuple, T_chol>> |
| 68 | +T_return gaussian_copula_cholesky_lpdf( |
| 69 | + const T_y& y, const T_cdf_fun_tuple& cdf_fun_tuple, const T_chol chol) { |
| 70 | + static constexpr const char* function = "gaussian_copula_cholesky_lpdf"; |
| 71 | + |
| 72 | + using T_y_ref = ref_type_t<T_y>; |
| 73 | + using T_chochol_ref = ref_type_t<T_chol>; |
| 74 | + using T_cdf_ref = ref_type_t<T_cdf_fun_tuple>; |
| 75 | + |
| 76 | + check_consistent_sizes_mvt(function, "y", y, "cdf_fun_tuple", cdf_fun_tuple); |
| 77 | + const size_t size_mvt_y = size_mvt(y); |
| 78 | + const size_t size_mvt_cdf_tuple = size_mvt(cdf_fun_tuple); |
| 79 | + if (size_mvt_y == 0) { |
| 80 | + return 0; |
| 81 | + } |
| 82 | + T_y_ref y_ref = y; |
| 83 | + T_chochol_ref chol_ref = chol; |
| 84 | + T_cdf_ref cdf_tuple_ref = cdf_fun_tuple; |
| 85 | + |
| 86 | + vector_seq_view<T_y_ref> y_vec(y_ref); |
| 87 | + vector_seq_view<T_cdf_ref> cdf_tuple_vec(cdf_tuple_ref); |
| 88 | + const size_t size_vec = max_size_mvt(y, cdf_fun_tuple); |
| 89 | + |
| 90 | + const int size_y = math::size(y_vec[0]); |
| 91 | + const int size_cdf_tuple = math::size(cdf_tuple_vec[0]); |
| 92 | + |
| 93 | + // check size consistency of all random variables y |
| 94 | + for (size_t i = 1; i < size_mvt_y; i++) { |
| 95 | + check_size_match(function, |
| 96 | + "Size of one of the vectors of " |
| 97 | + "the random variable", |
| 98 | + math::size(y_vec[i]), |
| 99 | + "Size of the first vector of the " |
| 100 | + "random variable", |
| 101 | + size_y); |
| 102 | + } |
| 103 | + // check size consistency of all CDF tuples |
| 104 | + for (size_t i = 1; i < size_mvt_cdf_tuple; i++) { |
| 105 | + check_size_match(function, |
| 106 | + "Size of one of the vectors of " |
| 107 | + "the CDF functor tuple", |
| 108 | + math::size(cdf_tuple_vec[i]), |
| 109 | + "Size of the first vector of the " |
| 110 | + "location variable", |
| 111 | + size_cdf_tuple); |
| 112 | + } |
| 113 | + |
| 114 | + check_size_match(function, "Size of random variable", size_y, |
| 115 | + "size of CDF functor tuple", size_cdf_tuple); |
| 116 | + check_size_match(function, "Size of random variable", size_y, |
| 117 | + "rows of covariance parameter", chol.rows()); |
| 118 | + check_size_match(function, "Size of random variable", size_y, |
| 119 | + "columns of covariance parameter", chol.cols()); |
| 120 | + |
| 121 | + for (size_t i = 0; i < size_vec; i++) { |
| 122 | + check_not_nan(function, "Random variable", y_vec[i]); |
| 123 | + } |
| 124 | + check_cholesky_factor(function, "Cholesky decomposition of a variance matrix", |
| 125 | + chol_ref); |
| 126 | + |
| 127 | + promote_scalar_t<T_return, std::vector<Eigen::VectorXd>> q(size_vec); |
| 128 | + T_return lp(0); |
| 129 | + for (size_t i = 0; i < size_vec; i++) { |
| 130 | + q[i] = to_vector(inv_Phi(internal::apply_cdfs(y_vec[i], cdf_tuple_vec[i]))); |
| 131 | + lp -= std_normal_lpdf(q[i]); |
| 132 | + } |
| 133 | + const std::vector<Eigen::VectorXd> zero_vec(size_vec, rep_vector(0, size_y)); |
| 134 | + lp += multi_normal_cholesky_lpdf(q, zero_vec, chol_ref); |
| 135 | + return lp; |
| 136 | +} |
| 137 | + |
| 138 | +} // namespace math |
| 139 | +} // namespace stan |
| 140 | +#endif |
0 commit comments