Skip to content

Commit 95a9388

Browse files
committed
Update doc, operate on LCDF scale
1 parent 0a68fdb commit 95a9388

File tree

1 file changed

+79
-50
lines changed

1 file changed

+79
-50
lines changed

stan/math/prim/prob/gaussian_copula_cholesky_lpdf.hpp

Lines changed: 79 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,92 +3,113 @@
33

44
#include <stan/math/prim/meta.hpp>
55
#include <stan/math/prim/err.hpp>
6-
#include <stan/math/prim/fun/inv_Phi.hpp>
76
#include <stan/math/prim/fun/to_vector.hpp>
87
#include <stan/math/prim/fun/rep_vector.hpp>
98
#include <stan/math/prim/fun/vector_seq_view.hpp>
109
#include <stan/math/prim/fun/size.hpp>
1110
#include <stan/math/prim/fun/size_mvt.hpp>
1211
#include <stan/math/prim/prob/multi_normal_cholesky_lpdf.hpp>
1312
#include <stan/math/prim/prob/std_normal_lpdf.hpp>
13+
#include <stan/math/prim/prob/std_normal_log_qf.hpp>
1414

1515
namespace stan {
1616
namespace math {
1717
namespace internal {
1818

1919
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(
20+
auto apply_lcdfs_elem_impl(
21+
Ty_elem&& y_elem, Ttuple&& lcdf_tuple, std::index_sequence<I...>) {
22+
auto&& lcdf_func = std::get<0>(lcdf_tuple);
23+
return lcdf_func(
2324
std::forward<Ty_elem>(y_elem),
24-
std::get<I + 1>(std::forward<Ttuple>(cdf_tuple))...
25+
std::get<I + 1>(std::forward<Ttuple>(lcdf_tuple))...
2526
);
2627
}
2728

2829

2930
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(
31+
std::vector<return_type_t<Ty, Ttuple>> apply_lcdfs_impl(
32+
Ty&& y, Ttuple&& lcdf_tuple, std::index_sequence<I...>) {
33+
// Use initializer-list expansion to return a vector with the result of
34+
// applying each LCDF functor to the respective input.
35+
// We cannot use apply_scalar_unary here as we need a constant
36+
// expression for indexing the tuple of LCDF functors
37+
return {
38+
apply_lcdfs_elem_impl(
3839
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;
40+
std::get<I>(lcdf_tuple),
41+
std::make_index_sequence<
42+
// Using size - 1 as the first element is the functor to apply
43+
std::tuple_size<
44+
std::remove_reference_t<
45+
decltype(std::get<I>(lcdf_tuple))>>{} - 1>{}
46+
)...
47+
};
4848
}
4949

5050
template <typename Ty, typename Ttuple>
51-
auto apply_cdfs(Ty&& y, Ttuple&& cdf_tuple){
52-
return apply_cdfs_impl(
51+
auto apply_lcdfs(Ty&& y, Ttuple&& lcdf_tuple){
52+
return apply_lcdfs_impl(
5353
std::forward<Ty>(y),
54-
std::forward<Ttuple>(cdf_tuple),
55-
std::make_index_sequence<
56-
std::tuple_size<std::remove_reference_t<Ttuple>>{}>{}
54+
std::forward<Ttuple>(lcdf_tuple),
55+
std::make_index_sequence<
56+
std::tuple_size<std::remove_reference_t<Ttuple>>{}>{}
5757
);
5858
}
5959
}
6060

61-
/*
62-
vectors ~ gaussian_copula_cholesky(cdf_funs_tuple, chol)
63-
(cdf_fun, ...)
64-
*/
6561

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>>
62+
/** \ingroup multivar_dists
63+
* The log of the Gaussian copula density for the given y and
64+
* a Cholesky factor L of the variance matrix.
65+
*
66+
* As the Gaussian copula requires inputs to be in the unit interval,
67+
* users are required to provide a tuple (of the same size as y) where each
68+
* element is a tuple containing a functor for calculating the LCDF and any
69+
* additional parameters required by the LCDF functor.
70+
*
71+
* This version of the function is vectorized on y and the tuple of LCDF
72+
* functor tuples.
73+
*
74+
* @param y A scalar vector
75+
* @param lcdf_fun_tuple A tuple of tuples, where each inner tuple follows the
76+
* structure {functor, param1, param2, ...}, where the functor has signature
77+
* (y[i], param1, param2, ...) and returns the log CDF for the given y[i].
78+
* @param chol The Cholesky decomposition of a variance matrix
79+
* of the multivariate normal distribution
80+
* @return The log of the gaussian copula density.
81+
* @throw std::domain_error if LL' is not square, not symmetric,
82+
* or not semi-positive definite.
83+
* @tparam T_y Type of scalar.
84+
* @tparam T_lcdf_fun_tuple Type of tuple of LCDF functor tuples.
85+
* @tparam T_chol Type of cholesky factor.
86+
*/
87+
template <bool propto, typename T_y, typename T_lcdf_fun_tuple, typename T_chol,
88+
typename T_return = return_type_t<T_y, T_lcdf_fun_tuple, T_chol>>
6889
T_return gaussian_copula_cholesky_lpdf(
69-
const T_y& y, const T_cdf_fun_tuple& cdf_fun_tuple, const T_chol chol) {
90+
const T_y& y, const T_lcdf_fun_tuple& lcdf_fun_tuple, const T_chol chol) {
7091
static constexpr const char* function = "gaussian_copula_cholesky_lpdf";
7192

7293
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>;
94+
using T_chol_ref = ref_type_t<T_chol>;
95+
using T_lcdf_ref = ref_type_t<T_lcdf_fun_tuple>;
7596

76-
check_consistent_sizes_mvt(function, "y", y, "cdf_fun_tuple", cdf_fun_tuple);
97+
check_consistent_sizes_mvt(function, "y", y, "lcdf_fun_tuple", lcdf_fun_tuple);
7798
const size_t size_mvt_y = size_mvt(y);
78-
const size_t size_mvt_cdf_tuple = size_mvt(cdf_fun_tuple);
99+
const size_t size_mvt_lcdf_tuple = size_mvt(lcdf_fun_tuple);
79100
if (size_mvt_y == 0) {
80101
return 0;
81102
}
82103
T_y_ref y_ref = y;
83-
T_chochol_ref chol_ref = chol;
84-
T_cdf_ref cdf_tuple_ref = cdf_fun_tuple;
104+
T_chol_ref chol_ref = chol;
105+
T_lcdf_ref lcdf_tuple_ref = lcdf_fun_tuple;
85106

86107
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);
108+
vector_seq_view<T_lcdf_ref> lcdf_tuple_vec(lcdf_tuple_ref);
109+
const size_t size_vec = max_size_mvt(y, lcdf_fun_tuple);
89110

90111
const int size_y = math::size(y_vec[0]);
91-
const int size_cdf_tuple = math::size(cdf_tuple_vec[0]);
112+
const int size_lcdf_tuple = math::size(lcdf_tuple_vec[0]);
92113

93114
// check size consistency of all random variables y
94115
for (size_t i = 1; i < size_mvt_y; i++) {
@@ -101,18 +122,18 @@ T_return gaussian_copula_cholesky_lpdf(
101122
size_y);
102123
}
103124
// check size consistency of all CDF tuples
104-
for (size_t i = 1; i < size_mvt_cdf_tuple; i++) {
125+
for (size_t i = 1; i < size_mvt_lcdf_tuple; i++) {
105126
check_size_match(function,
106127
"Size of one of the vectors of "
107128
"the CDF functor tuple",
108-
math::size(cdf_tuple_vec[i]),
129+
math::size(lcdf_tuple_vec[i]),
109130
"Size of the first vector of the "
110131
"location variable",
111-
size_cdf_tuple);
132+
size_lcdf_tuple);
112133
}
113134

114135
check_size_match(function, "Size of random variable", size_y,
115-
"size of CDF functor tuple", size_cdf_tuple);
136+
"size of CDF functor tuple", size_lcdf_tuple);
116137
check_size_match(function, "Size of random variable", size_y,
117138
"rows of covariance parameter", chol.rows());
118139
check_size_match(function, "Size of random variable", size_y,
@@ -127,14 +148,22 @@ T_return gaussian_copula_cholesky_lpdf(
127148
promote_scalar_t<T_return, std::vector<Eigen::VectorXd>> q(size_vec);
128149
T_return lp(0);
129150
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]);
151+
const auto& u = internal::apply_lcdfs(y_vec[i], lcdf_tuple_vec[i]);
152+
check_bounded(function, "CDF-transformed inputs", u, NEGATIVE_INFTY, 0);
153+
q[i] = to_vector(std_normal_log_qf(u));
154+
lp -= std_normal_lpdf<propto>(q[i]);
132155
}
133156
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);
157+
lp += multi_normal_cholesky_lpdf<propto>(q, zero_vec, chol_ref);
135158
return lp;
136159
}
137160

161+
template <typename T_y, typename T_lcdf_fun_tuple, typename T_chol,
162+
typename T_return = return_type_t<T_y, T_lcdf_fun_tuple, T_chol>>
163+
T_return gaussian_copula_cholesky_lpdf(
164+
const T_y& y, const T_lcdf_fun_tuple& lcdf_fun_tuple, const T_chol chol) {
165+
return gaussian_copula_cholesky_lpdf<false>(y, lcdf_fun_tuple, chol);
166+
}
138167
} // namespace math
139168
} // namespace stan
140169
#endif

0 commit comments

Comments
 (0)