Skip to content

Commit 0a68fdb

Browse files
committed
Initial version of gaussian copula lpdf
1 parent bdd5b3e commit 0a68fdb

File tree

5 files changed

+161
-3
lines changed

5 files changed

+161
-3
lines changed

stan/math/prim/fun/size.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ inline int64_t size(const T& m) {
3434
return m.size();
3535
}
3636

37+
38+
template <typename T, require_tuple_t<T>* = nullptr>
39+
inline int64_t size(const T& /*t*/) {
40+
return std::tuple_size<std::remove_reference_t<T>>{};
41+
}
42+
3743
} // namespace math
3844
} // namespace stan
3945
#endif

stan/math/prim/fun/size_mvt.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,21 @@ int64_t size_mvt(const MatrixT& /* unused */) {
3636
return 1;
3737
}
3838

39+
template <typename TupleT, require_tuple_t<TupleT>* = nullptr>
40+
int64_t size_mvt(const TupleT& /* unused */) {
41+
return 1;
42+
}
43+
3944
template <typename MatrixT, require_matrix_t<MatrixT>* = nullptr>
4045
int64_t size_mvt(const std::vector<MatrixT>& x) {
4146
return x.size();
4247
}
4348

49+
template <typename TupleT, require_tuple_t<TupleT>* = nullptr>
50+
int64_t size_mvt(const std::vector<TupleT>& x) {
51+
return x.size();
52+
}
53+
4454
template <typename StdVectorT, require_std_vector_t<StdVectorT>* = nullptr>
4555
int64_t size_mvt(const std::vector<StdVectorT>& x) {
4656
return x.size();

stan/math/prim/fun/vector_seq_view.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
namespace stan {
99
namespace internal {
1010
template <typename T>
11-
using is_matrix_or_std_vector
12-
= math::disjunction<is_matrix<T>, is_std_vector<T>>;
11+
using is_matrix_or_std_vector_or_tuple
12+
= math::disjunction<is_matrix<T>, is_std_vector<T>, math::is_tuple<T>>;
1313

1414
template <typename T>
1515
using is_scalar_container = math::disjunction<
1616
is_matrix<T>,
17+
math::is_tuple<T>,
1718
math::conjunction<is_std_vector<T>, is_stan_scalar<value_type_t<T>>>>;
1819
} // namespace internal
1920

@@ -76,7 +77,7 @@ class vector_seq_view<T, require_t<internal::is_scalar_container<T>>> {
7677
*/
7778
template <typename T>
7879
class vector_seq_view<
79-
T, require_std_vector_vt<internal::is_matrix_or_std_vector, T>> {
80+
T, require_std_vector_vt<internal::is_matrix_or_std_vector_or_tuple, T>> {
8081
public:
8182
explicit vector_seq_view(const T& v) noexcept : v_(v) {}
8283
inline auto size() const noexcept { return v_.size(); }

stan/math/prim/prob.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
#include <stan/math/prim/prob/gamma_lcdf.hpp>
112112
#include <stan/math/prim/prob/gamma_lpdf.hpp>
113113
#include <stan/math/prim/prob/gamma_rng.hpp>
114+
#include <stan/math/prim/prob/gaussian_copula_cholesky_lpdf.hpp>
114115
#include <stan/math/prim/prob/gaussian_dlm_obs_lpdf.hpp>
115116
#include <stan/math/prim/prob/gaussian_dlm_obs_rng.hpp>
116117
#include <stan/math/prim/prob/gumbel_ccdf_log.hpp>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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

Comments
 (0)