Automatic Differentiation
 
Loading...
Searching...
No Matches
gaussian_dlm_obs_lpdf.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_PRIM_PROB_GAUSSIAN_DLM_OBS_LPDF_HPP
2#define STAN_MATH_PRIM_PROB_GAUSSIAN_DLM_OBS_LPDF_HPP
3
21#include <cmath>
22
23/*
24 TODO: time-varying system matrices
25 TODO: use sequential processing even for non-diagonal obs
26 covariance.
27 TODO: add constant terms in observation.
28*/
29namespace stan {
30namespace math {
65template <bool propto, typename T_y, typename T_F, typename T_G, typename T_V,
66 typename T_W, typename T_m0, typename T_C0,
67 require_all_eigen_matrix_dynamic_t<T_y, T_F, T_G, T_V, T_W,
68 T_C0>* = nullptr,
69 require_eigen_col_vector_t<T_m0>* = nullptr>
71 const T_y& y, const T_F& F, const T_G& G, const T_V& V, const T_W& W,
72 const T_m0& m0, const T_C0& C0) {
74 using std::pow;
75 static constexpr const char* function = "gaussian_dlm_obs_lpdf";
76 check_size_match(function, "columns of F", F.cols(), "rows of y", y.rows());
77 check_size_match(function, "rows of F", F.rows(), "rows of G", G.rows());
78 check_size_match(function, "rows of V", V.rows(), "rows of y", y.rows());
79 check_size_match(function, "rows of W", W.rows(), "rows of G", G.rows());
80 check_size_match(function, "size of m0", m0.size(), "rows of G", G.rows());
81 check_size_match(function, "rows of C0", C0.rows(), "rows of G", G.rows());
82 check_square(function, "G", G);
83
84 const auto& y_ref = to_ref(y);
85 const auto& F_ref = to_ref(F);
86 const auto& G_ref = to_ref(G);
87 const auto& V_ref = to_ref(V);
88 const auto& W_ref = to_ref(W);
89 const auto& m0_ref = to_ref(m0);
90 const auto& C0_ref = to_ref(C0);
91
92 check_finite(function, "y", y_ref);
93 check_finite(function, "F", F_ref);
94 check_finite(function, "G", G_ref);
95 // TODO(anyone): incorporate support for infinite V
96 check_finite(function, "V", V_ref);
97 check_pos_semidefinite(function, "V", V_ref);
98 // TODO(anyone): incorporate support for infinite W
99 check_finite(function, "W", W_ref);
100 check_pos_semidefinite(function, "W", W_ref);
101 check_finite(function, "m0", m0_ref);
102 check_pos_semidefinite(function, "C0", C0_ref);
103 check_finite(function, "C0", C0_ref);
104
105 if (size_zero(y)) {
106 return 0;
107 }
108
109 int r = y.rows(); // number of variables
110 int n = G.rows(); // number of states
111
112 T_lp lp(0);
113 if constexpr (include_summand<propto>::value) {
114 lp -= HALF_LOG_TWO_PI * r * y.cols();
115 }
116
117 if constexpr (include_summand<propto, T_y, T_F, T_G, T_V, T_W, T_m0,
118 T_C0>::value) {
119 Eigen::Matrix<T_lp, Eigen::Dynamic, 1> m{m0_ref};
120 Eigen::Matrix<T_lp, Eigen::Dynamic, Eigen::Dynamic> C{C0_ref};
121 Eigen::Matrix<T_lp, Eigen::Dynamic, 1> a(n);
122 Eigen::Matrix<T_lp, Eigen::Dynamic, Eigen::Dynamic> R(n, n);
123 Eigen::Matrix<T_lp, Eigen::Dynamic, 1> f(r);
124 Eigen::Matrix<T_lp, Eigen::Dynamic, Eigen::Dynamic> Q(r, r);
125 Eigen::Matrix<T_lp, Eigen::Dynamic, Eigen::Dynamic> Q_inv(r, r);
126 Eigen::Matrix<T_lp, Eigen::Dynamic, 1> e(r);
127 Eigen::Matrix<T_lp, Eigen::Dynamic, Eigen::Dynamic> A(n, r);
128
129 for (int i = 0; i < y.cols(); i++) {
130 // // Predict state
131 // a_t = G_t m_{t-1}
132 a = multiply(G_ref, m);
133 // R_t = G_t C_{t-1} G_t' + W_t
134 R = quad_form_sym(C, transpose(G_ref)) + W_ref;
135 // // predict observation
136 // f_t = F_t' a_t
137 f = multiply(transpose(F_ref), a);
138 // Q_t = F'_t R_t F_t + V_t
139 Q = quad_form_sym(R, F_ref) + V_ref;
140 Q_inv = inverse_spd(Q);
141 // // filtered state
142 // e_t = y_t - f_t
143 e = y_ref.col(i) - f;
144 // A_t = R_t F_t Q^{-1}_t
145 A = multiply(multiply(R, F_ref), Q_inv);
146 // m_t = a_t + A_t e_t
147 m = a + multiply(A, e);
148 // C = R_t - A_t Q_t A_t'
149 C = R - quad_form_sym(Q, transpose(A));
150 lp -= 0.5 * (log_determinant_spd(Q) + trace_quad_form(Q_inv, e));
151 }
152 }
153 return lp;
154}
155
191template <
192 bool propto, typename T_y, typename T_F, typename T_G, typename T_V,
193 typename T_W, typename T_m0, typename T_C0,
197 const T_y& y, const T_F& F, const T_G& G, const T_V& V, const T_W& W,
198 const T_m0& m0, const T_C0& C0) {
200 using std::log;
201 static constexpr const char* function = "gaussian_dlm_obs_lpdf";
202 check_size_match(function, "columns of F", F.cols(), "rows of y", y.rows());
203 check_size_match(function, "rows of F", F.rows(), "rows of G", G.rows());
204 check_size_match(function, "rows of G", G.rows(), "columns of G", G.cols());
205 check_size_match(function, "size of V", V.size(), "rows of y", y.rows());
206 check_size_match(function, "rows of W", W.rows(), "rows of G", G.rows());
207 check_size_match(function, "size of m0", m0.size(), "rows of G", G.rows());
208 check_size_match(function, "rows of C0", C0.rows(), "rows of G", G.rows());
209
210 const auto& y_ref = to_ref(y);
211 const auto& F_ref = to_ref(F);
212 const auto& G_ref = to_ref(G);
213 const auto& V_ref = to_ref(V);
214 const auto& W_ref = to_ref(W);
215 const auto& m0_ref = to_ref(m0);
216 const auto& C0_ref = to_ref(C0);
217
218 check_finite(function, "y", y_ref);
219 check_finite(function, "F", F_ref);
220 check_finite(function, "G", G_ref);
221 check_nonnegative(function, "V", V_ref);
222 // TODO(anyone): support infinite V
223 check_finite(function, "V", V_ref);
224 check_pos_semidefinite(function, "W", W_ref);
225 // TODO(anyone): support infinite W
226 check_finite(function, "W", W_ref);
227 check_finite(function, "m0", m0_ref);
228 check_pos_semidefinite(function, "C0", C0_ref);
229 check_finite(function, "C0", C0_ref);
230
231 if (y.cols() == 0 || y.rows() == 0) {
232 return 0;
233 }
234
235 int r = y.rows(); // number of variables
236 int n = G.rows(); // number of states
237
238 T_lp lp(0);
239 if constexpr (include_summand<propto>::value) {
240 lp -= HALF_LOG_TWO_PI * r * y.cols();
241 }
242
243 if constexpr (include_summand<propto, T_y, T_F, T_G, T_V, T_W, T_m0,
244 T_C0>::value) {
245 T_lp f;
246 T_lp Q;
247 T_lp Q_inv;
248 T_lp e;
249 Eigen::Matrix<T_lp, Eigen::Dynamic, 1> A(n);
250 Eigen::Matrix<T_lp, Eigen::Dynamic, 1> Fj(n);
251 Eigen::Matrix<T_lp, Eigen::Dynamic, 1> m{m0_ref};
252 Eigen::Matrix<T_lp, Eigen::Dynamic, Eigen::Dynamic> C{C0_ref};
253
254 for (int i = 0; i < y.cols(); i++) {
255 // Predict state
256 // reuse m and C instead of using a and R
257 m = multiply(G_ref, m);
258 C = quad_form_sym(C, transpose(G_ref)) + W_ref;
259 for (int j = 0; j < y.rows(); ++j) {
260 // predict observation
261 // dim Fj = (n, 1)
262 const auto& Fj = F_ref.col(j);
263 // f_{t, i} = F_{t, i}' m_{t, i-1}
264 f = dot_product(Fj, m);
265 Q = trace_quad_form(C, Fj) + V_ref.coeff(j);
266 if (i == 0)
267 check_positive(function, "Q0", Q);
268 Q_inv = 1.0 / Q;
269 // filtered observation
270 // e_{t, i} = y_{t, i} - f_{t, i}
271 e = y_ref.coeff(j, i) - f;
272 // A_{t, i} = C_{t, i-1} F_{t, i} Q_{t, i}^{-1}
273 A = multiply(multiply(C, Fj), Q_inv);
274 // m_{t, i} = m_{t, i-1} + A_{t, i} e_{t, i}
275 m += multiply(A, e);
276 // c_{t, i} = C_{t, i-1} - Q_{t, i} A_{t, i} A_{t, i}'
277 // tcrossprod throws an error (ambiguous)
278 // C = subtract(C, multiply(Q, tcrossprod(A)));
279 C -= multiply(Q, multiply(A, transpose(A)));
280 C = 0.5 * (C + transpose(C));
281 lp -= 0.5 * (log(Q) + square(e) * Q_inv);
282 }
283 }
284 }
285 return lp;
286}
287
288template <typename T_y, typename T_F, typename T_G, typename T_V, typename T_W,
289 typename T_m0, typename T_C0>
291 const T_y& y, const T_F& F, const T_G& G, const T_V& V, const T_W& W,
292 const T_m0& m0, const T_C0& C0) {
293 return gaussian_dlm_obs_lpdf<false>(y, F, G, V, W, m0, C0);
294}
295
296} // namespace math
297} // namespace stan
298#endif
require_all_t< is_eigen_col_vector< std::decay_t< Types > >... > require_all_eigen_col_vector_t
Require all of the types satisfy is_eigen_col_vector.
require_all_t< is_eigen_matrix_dynamic< std::decay_t< Types > >... > require_all_eigen_matrix_dynamic_t
Require all of the types satisfy is_eigen_matrix_dynamic.
return_type_t< T_y, T_F, T_G, T_V, T_W, T_m0, T_C0 > gaussian_dlm_obs_lpdf(const T_y &y, const T_F &F, const T_G &G, const T_V &V, const T_W &W, const T_m0 &m0, const T_C0 &C0)
The log of a Gaussian dynamic linear model (GDLM).
auto transpose(Arg &&a)
Transposes a kernel generator expression.
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
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.
bool size_zero(const T &x)
Returns 1 if input is of length 0, returns 0 otherwise.
Definition size_zero.hpp:19
void check_pos_semidefinite(const char *function, const char *name, const EigMat &y)
Check if the specified matrix is positive definite.
static constexpr double e()
Return the base of the natural logarithm.
Definition constants.hpp:20
value_type_t< EigMat > log_determinant_spd(const EigMat &m)
Returns the log absolute determinant of the specified square matrix.
fvar< T > log(const fvar< T > &x)
Definition log.hpp:18
auto multiply(const Mat1 &m1, const Mat2 &m2)
Return the product of the specified matrices.
Definition multiply.hpp:19
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
promote_scalar_t< return_type_t< EigMat1, EigMat2 >, EigMat2 > quad_form_sym(const EigMat1 &A, const EigMat2 &B)
Return the quadratic form of a symmetric matrix.
void check_positive(const char *function, const char *name, const T_y &y)
Check if y is positive.
Eigen::Matrix< value_type_t< EigMat >, Eigen::Dynamic, Eigen::Dynamic > inverse_spd(const EigMat &m)
Returns the inverse of the specified symmetric, pos/neg-definite matrix.
return_type_t< EigMat1, EigMat2 > trace_quad_form(const EigMat1 &A, const EigMat2 &B)
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
Definition to_ref.hpp:18
static constexpr double HALF_LOG_TWO_PI
The value of half the natural logarithm , .
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.
fvar< T > square(const fvar< T > &x)
Definition square.hpp:12
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...