Automatic Differentiation
 
Loading...
Searching...
No Matches
multi_normal_lpdf.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_PRIM_PROB_MULTI_NORMAL_LPDF_HPP
2#define STAN_MATH_PRIM_PROB_MULTI_NORMAL_LPDF_HPP
3
20
21namespace stan {
22namespace math {
23
24template <bool propto, typename T_y, typename T_loc, typename T_covar,
25 require_any_not_vector_vt<is_stan_scalar, T_y, T_loc>* = nullptr,
27 T_y, T_loc, T_covar>* = nullptr>
29 const T_y& y, const T_loc& mu, const T_covar& Sigma) {
30 using T_covar_elem = typename scalar_type<T_covar>::type;
32 using T_partials_return = partials_return_t<T_y, T_loc, T_covar>;
33 using matrix_partials_t
34 = Eigen::Matrix<T_partials_return, Eigen::Dynamic, Eigen::Dynamic>;
35 using vector_partials_t = Eigen::Matrix<T_partials_return, Eigen::Dynamic, 1>;
36 using T_y_ref = ref_type_t<T_y>;
37 using T_mu_ref = ref_type_t<T_loc>;
38 using T_Sigma_ref = ref_type_t<T_covar>;
39
40 static const char* function = "multi_normal_lpdf";
41 check_positive(function, "Covariance matrix rows", Sigma.rows());
42
43 check_consistent_sizes_mvt(function, "y", y, "mu", mu);
44 size_t number_of_y = size_mvt(y);
45 size_t number_of_mu = size_mvt(mu);
46 if (number_of_y == 0 || number_of_mu == 0) {
47 return 0.0;
48 }
49
50 T_y_ref y_ref = y;
51 T_mu_ref mu_ref = mu;
52 T_Sigma_ref Sigma_ref = Sigma;
53 vector_seq_view<T_y_ref> y_vec(y_ref);
54 vector_seq_view<T_mu_ref> mu_vec(mu_ref);
55 const size_t size_vec = max_size_mvt(y, mu);
56 const int K = Sigma.rows();
57
58 int size_y = y_vec[0].size();
59 int size_mu = mu_vec[0].size();
60
61 // check size consistency of all random variables y
62 for (size_t i = 1, size_mvt_y = size_mvt(y); i < size_mvt_y; i++) {
63 check_size_match(function,
64 "Size of one of the vectors of "
65 "the random variable",
66 y_vec[i].size(),
67 "Size of the first vector of the "
68 "random variable",
69 size_y);
70 }
71 // check size consistency of all means mu
72 for (size_t i = 1, size_mvt_mu = size_mvt(mu); i < size_mvt_mu; i++) {
73 check_size_match(function,
74 "Size of one of the vectors of "
75 "the location variable",
76 mu_vec[i].size(),
77 "Size of the first vector of the "
78 "location variable",
79 size_mu);
80 }
81
82 check_size_match(function, "Size of random variable", size_y,
83 "size of location parameter", size_mu);
84 check_size_match(function, "Size of random variable", size_y,
85 "rows of covariance parameter", Sigma.rows());
86 check_size_match(function, "Size of random variable", size_y,
87 "columns of covariance parameter", Sigma.cols());
88
89 for (size_t i = 0; i < size_vec; i++) {
90 check_finite(function, "Location parameter", mu_vec[i]);
91 check_not_nan(function, "Random variable", y_vec[i]);
92 }
93 check_symmetric(function, "Covariance matrix", Sigma_ref);
94
95 auto ldlt_Sigma = make_ldlt_factor(value_of(Sigma_ref));
96
97 check_ldlt_factor(function, "LDLT_Factor of covariance parameter",
98 ldlt_Sigma);
99
100 if (unlikely(size_y == 0)) {
101 return T_return(0);
102 }
103
104 auto ops_partials = make_partials_propagator(y_ref, mu_ref, Sigma_ref);
105
106 T_partials_return logp(0);
107
108 if constexpr (include_summand<propto>::value) {
109 logp += NEG_LOG_SQRT_TWO_PI * size_y * size_vec;
110 }
111
113 vector_partials_t half(size_vec);
114 vector_partials_t y_val_minus_mu_val(size_vec);
115
116 T_partials_return sum_lp_vec(0.0);
117 for (size_t i = 0; i < size_vec; i++) {
118 const auto& y_val = as_value_column_vector_or_scalar(y_vec[i]);
119 const auto& mu_val = as_value_column_vector_or_scalar(mu_vec[i]);
120 y_val_minus_mu_val = eval(y_val - mu_val);
121 half = mdivide_left_ldlt(ldlt_Sigma, y_val_minus_mu_val);
122
123 sum_lp_vec += dot_product(y_val_minus_mu_val, half);
124
125 if constexpr (is_autodiff_v<T_y>) {
126 partials_vec<0>(ops_partials)[i] += -half;
127 }
128 if constexpr (is_autodiff_v<T_loc>) {
129 partials_vec<1>(ops_partials)[i] += half;
130 }
131 if constexpr (is_autodiff_v<T_covar_elem>) {
132 partials_vec<2>(ops_partials)[i] += 0.5 * half * half.transpose();
133 }
134 }
135
136 logp += -0.5 * sum_lp_vec;
137
138 // If the covariance is not autodiff, we can avoid computing a matrix
139 // inverse
140 if constexpr (is_constant_v<T_covar_elem>) {
141 if constexpr (include_summand<propto>::value) {
142 logp += -0.5 * log_determinant_ldlt(ldlt_Sigma) * size_vec;
143 }
144 } else {
145 matrix_partials_t inv_Sigma
146 = mdivide_left_ldlt(ldlt_Sigma, Eigen::MatrixXd::Identity(K, K));
147
148 logp += -0.5 * log_determinant_ldlt(ldlt_Sigma) * size_vec;
149
150 partials<2>(ops_partials) += -0.5 * size_vec * inv_Sigma;
151 }
152 }
153
154 return ops_partials.build(logp);
155}
156
157template <bool propto, typename T_y, typename T_loc, typename T_covar,
160 T_y, T_loc, T_covar>* = nullptr>
162 const T_y& y, const T_loc& mu, const T_covar& Sigma) {
163 using T_covar_elem = typename scalar_type<T_covar>::type;
164 using T_return = return_type_t<T_y, T_loc, T_covar>;
165 using T_partials_return = partials_return_t<T_y, T_loc, T_covar>;
166 using matrix_partials_t
167 = Eigen::Matrix<T_partials_return, Eigen::Dynamic, Eigen::Dynamic>;
168 using vector_partials_t = Eigen::Matrix<T_partials_return, Eigen::Dynamic, 1>;
169 using T_y_ref = ref_type_t<T_y>;
170 using T_mu_ref = ref_type_t<T_loc>;
171 using T_Sigma_ref = ref_type_t<T_covar>;
172
173 static const char* function = "multi_normal_lpdf";
174 check_positive(function, "Covariance matrix rows", Sigma.rows());
175
176 T_y_ref y_ref = y;
177 T_mu_ref mu_ref = mu;
178 T_Sigma_ref Sigma_ref = Sigma;
179
180 decltype(auto) y_val = as_value_column_vector_or_scalar(y_ref);
181 decltype(auto) mu_val = as_value_column_vector_or_scalar(mu_ref);
182
183 const int size_y = y_ref.size();
184 const int size_mu = mu_ref.size();
185 const unsigned int K = Sigma.rows();
186
187 check_finite(function, "Location parameter", mu_val);
188 check_not_nan(function, "Random variable", y_val);
189
190 check_size_match(function, "Size of random variable", size_y,
191 "size of location parameter", size_mu);
192 check_size_match(function, "Size of random variable", size_y,
193 "rows of covariance parameter", Sigma.rows());
194 check_size_match(function, "Size of random variable", size_y,
195 "columns of covariance parameter", Sigma.cols());
196
197 check_symmetric(function, "Covariance matrix", Sigma_ref);
198
199 auto ldlt_Sigma = make_ldlt_factor(value_of(Sigma_ref));
200 check_ldlt_factor(function, "LDLT_Factor of covariance parameter",
201 ldlt_Sigma);
202
203 if (unlikely(size_y == 0)) {
204 return T_return(0);
205 }
206
207 auto ops_partials = make_partials_propagator(y_ref, mu_ref, Sigma_ref);
208
209 T_partials_return logp(0);
210
211 if constexpr (include_summand<propto>::value) {
212 logp += NEG_LOG_SQRT_TWO_PI * size_y;
213 }
214
215 if constexpr (include_summand<propto, T_y, T_loc, T_covar_elem>::value) {
216 vector_partials_t half(size_y);
217 vector_partials_t y_val_minus_mu_val = eval(y_val - mu_val);
218
219 // If the covariance is not autodiff, we can avoid computing a matrix
220 // inverse
221 if constexpr (is_constant_v<T_covar_elem>) {
222 half = mdivide_left_ldlt(ldlt_Sigma, y_val_minus_mu_val);
223
224 if constexpr (include_summand<propto>::value) {
225 logp += -0.5 * log_determinant_ldlt(ldlt_Sigma);
226 }
227 } else {
228 matrix_partials_t inv_Sigma
229 = mdivide_left_ldlt(ldlt_Sigma, Eigen::MatrixXd::Identity(K, K));
230
231 half.noalias() = inv_Sigma * y_val_minus_mu_val;
232
233 logp += -0.5 * log_determinant_ldlt(ldlt_Sigma);
234
235 edge<2>(ops_partials).partials_
236 += 0.5 * (half * half.transpose() - inv_Sigma);
237 }
238
239 logp += -0.5 * dot_product(y_val_minus_mu_val, half);
240
241 if constexpr (is_autodiff_v<T_y>) {
242 partials<0>(ops_partials) += -half;
243 }
244 if constexpr (is_autodiff_v<T_loc>) {
245 partials<1>(ops_partials) += half;
246 }
247 }
248
249 return ops_partials.build(logp);
250}
251
252template <typename T_y, typename T_loc, typename T_covar>
254 const T_y& y, const T_loc& mu, const T_covar& Sigma) {
255 return multi_normal_lpdf<false>(y, mu, Sigma);
256}
257
258} // namespace math
259} // namespace stan
260#endif
This class provides a low-cost wrapper for situations where you either need an Eigen Vector or RowVec...
#define unlikely(x)
void check_symmetric(const char *function, const char *name, const matrix_cl< T > &y)
Check if the matrix_cl is symmetric.
require_all_not_t< is_nonscalar_prim_or_rev_kernel_expression< std::decay_t< Types > >... > require_all_not_nonscalar_prim_or_rev_kernel_expression_t
Require none of the types satisfy is_nonscalar_prim_or_rev_kernel_expression.
int64_t size_mvt(const ScalarT &)
Provides the size of a multivariate argument.
Definition size_mvt.hpp:25
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
int64_t size(const T &m)
Returns the size (number of the elements) of a matrix_cl or var_value<matrix_cl<T>>.
Definition size.hpp:19
require_all_t< container_type_check_base< is_vector, value_type_t, TypeCheck, Check >... > require_all_vector_vt
Require all of the types satisfy is_vector.
value_type_t< T > log_determinant_ldlt(LDLT_factor< T > &A)
Returns log(abs(det(A))) given a LDLT_factor of A.
auto make_ldlt_factor(const T &A)
Make an LDLT_factor with matrix type plain_type_t<T>
return_type_t< T_y, T_loc, T_covar > multi_normal_lpdf(const T_y &y, const T_loc &mu, const T_covar &Sigma)
void check_consistent_sizes_mvt(const char *)
Trivial no input case, this function is a no-op.
T eval(T &&arg)
Inputs which have a plain_type equal to the own time are forwarded unmodified (for Eigen expressions ...
Definition eval.hpp:20
T value_of(const fvar< T > &v)
Return the value of the specified variable.
Definition value_of.hpp:18
const double NEG_LOG_SQRT_TWO_PI
The value of minus the natural logarithm of the square root of , .
int64_t max_size_mvt(const T1 &x1, const Ts &... xs)
Calculate the size of the largest multivariate input.
Eigen::Matrix< value_type_t< EigMat >, Eigen::Dynamic, EigMat::ColsAtCompileTime > mdivide_left_ldlt(LDLT_factor< T > &A, const EigMat &b)
Returns the solution of the system Ax=b given an LDLT_factor of A.
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
void check_ldlt_factor(const char *function, const char *name, LDLT_factor< T > &A)
Raise domain error if the specified LDLT factor is invalid.
void check_not_nan(const char *function, const char *name, const T_y &y)
Check if y is not NaN.
void check_positive(const char *function, const char *name, const T_y &y)
Check if y is positive.
void check_size_match(const char *function, const char *name_i, T_size1 i, const char *name_j, T_size2 j)
Check if the provided sizes match.
auto dot_product(const T_a &a, const T_b &b)
Returns the dot product of the specified vectors.
auto as_value_column_vector_or_scalar(T &&a)
Extract values from input argument and transform to a column vector.
auto make_partials_propagator(Ops &&... ops)
Construct an partials_propagator.
typename ref_type_if< true, T >::type ref_type_t
Definition ref_type.hpp:56
typename partials_return_type< Args... >::type partials_return_t
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
Template metaprogram to calculate whether a summand needs to be included in a proportional (log) prob...
std::decay_t< T > type