1 #ifndef STAN_MATH_PRIM_FUN_COV_MATRIX_CONSTRAIN_HPP
2 #define STAN_MATH_PRIM_FUN_COV_MATRIX_CONSTRAIN_HPP
3
4 #include <stan/math/prim/meta.hpp>
5 #include <stan/math/prim/err.hpp>
6 #include <stan/math/prim/fun/constants.hpp>
7 #include <stan/math/prim/fun/Eigen.hpp>
8 #include <stan/math/prim/fun/exp.hpp>
9 #include <stan/math/prim/fun/log.hpp>
10 #include <stan/math/prim/fun/multiply_lower_tri_self_transpose.hpp>
11 #include <cmath>
12
13 namespace stan {
14 namespace math {
15
16 /**
17 * Return the symmetric, positive-definite matrix of dimensions K
18 * by K resulting from transforming the specified finite vector of
19 * size K plus (K choose 2).
20 *
21 * <p>See <code>cov_matrix_free()</code> for the inverse transform.
22 *
23 * @tparam T type of the vector (must be derived from \c Eigen::MatrixBase and
24 * have one compile-time dimension equal to 1)
25 * @param x The vector to convert to a covariance matrix.
26 * @param K The number of rows and columns of the resulting
27 * covariance matrix.
28 * @throws std::invalid_argument if (x.size() != K + (K choose 2)).
29 */
30 template <typename T, require_eigen_col_vector_t<T>* = nullptr>
31 inline Eigen::Matrix<value_type_t<T>, Eigen::Dynamic, Eigen::Dynamic>
cov_matrix_constrain(const T & x,Eigen::Index K)32 cov_matrix_constrain(const T& x, Eigen::Index K) {
33 using Eigen::Dynamic;
34 using Eigen::Matrix;
35 using std::exp;
36
37 Matrix<value_type_t<T>, Dynamic, Dynamic> L(K, K);
38 check_size_match("cov_matrix_constrain", "x.size()", x.size(),
39 "K + (K choose 2)", (K * (K + 1)) / 2);
40 const auto& x_ref = to_ref(x);
41 int i = 0;
42 for (Eigen::Index m = 0; m < K; ++m) {
43 L.row(m).head(m) = x_ref.segment(i, m);
44 i += m;
45 L.coeffRef(m, m) = exp(x_ref.coeff(i++));
46 L.row(m).tail(K - m - 1).setZero();
47 }
48 return multiply_lower_tri_self_transpose(L);
49 }
50
51 /**
52 * Return the symmetric, positive-definite matrix of dimensions K
53 * by K resulting from transforming the specified finite vector of
54 * size K plus (K choose 2).
55 *
56 * @tparam T type of the vector (must be derived from \c Eigen::MatrixBase and
57 * have one compile-time dimension equal to 1)
58 * @param x The vector to convert to a covariance matrix.
59 * @param K The dimensions of the resulting covariance matrix.
60 * @param lp Reference
61 * @throws std::domain_error if (x.size() != K + (K choose 2)).
62 */
63 template <typename T, require_eigen_col_vector_t<T>* = nullptr>
64 inline Eigen::Matrix<value_type_t<T>, Eigen::Dynamic, Eigen::Dynamic>
cov_matrix_constrain(const T & x,Eigen::Index K,return_type_t<T> & lp)65 cov_matrix_constrain(const T& x, Eigen::Index K, return_type_t<T>& lp) {
66 using Eigen::Dynamic;
67 using Eigen::Matrix;
68 using std::exp;
69 using std::log;
70 check_size_match("cov_matrix_constrain", "x.size()", x.size(),
71 "K + (K choose 2)", (K * (K + 1)) / 2);
72 Matrix<value_type_t<T>, Dynamic, Dynamic> L(K, K);
73 const auto& x_ref = to_ref(x);
74 int i = 0;
75 for (Eigen::Index m = 0; m < K; ++m) {
76 L.row(m).head(m) = x_ref.segment(i, m);
77 i += m;
78 L.coeffRef(m, m) = exp(x_ref.coeff(i++));
79 L.row(m).tail(K - m - 1).setZero();
80 }
81 // Jacobian for complete transform, including exp() above
82 lp += (K * LOG_TWO); // needless constant; want propto
83 for (Eigen::Index k = 0; k < K; ++k) {
84 lp += (K - k + 1) * log(L.coeff(k, k)); // only +1 because index from 0
85 }
86 return multiply_lower_tri_self_transpose(L);
87 }
88
89 /**
90 * Return the symmetric, positive-definite matrix of dimensions K by K resulting
91 * from transforming the specified finite vector of size K plus (K choose 2). If
92 * the `Jacobian` parameter is `true`, the log density accumulator is
93 * incremented with the log absolute Jacobian determinant of the transform. All
94 * of the transforms are specified with their Jacobians in the *Stan Reference
95 * Manual* chapter Constraint Transforms.
96 *
97 * @tparam Jacobian if `true`, increment log density accumulator with log
98 * absolute Jacobian determinant of constraining transform
99 * @tparam T A type inheriting from `Eigen::DenseBase` or a `var_value` with
100 * inner type inheriting from `Eigen::DenseBase` with compile time dynamic rows
101 * and 1 column
102 * @param x The vector to convert to a covariance matrix
103 * @param K The dimensions of the resulting covariance matrix
104 * @param[in, out] lp log density accumulator
105 * @throws std::domain_error if (x.size() != K + (K choose 2)).
106 */
107 template <bool Jacobian, typename T, require_not_std_vector_t<T>* = nullptr>
cov_matrix_constrain(const T & x,Eigen::Index K,return_type_t<T> & lp)108 inline auto cov_matrix_constrain(const T& x, Eigen::Index K,
109 return_type_t<T>& lp) {
110 if (Jacobian) {
111 return cov_matrix_constrain(x, K, lp);
112 } else {
113 return cov_matrix_constrain(x, K);
114 }
115 }
116
117 /**
118 * Return the symmetric, positive-definite matrix of dimensions K by K resulting
119 * from transforming the specified finite vector of size K plus (K choose 2). If
120 * the `Jacobian` parameter is `true`, the log density accumulator is
121 * incremented with the log absolute Jacobian determinant of the transform. All
122 * of the transforms are specified with their Jacobians in the *Stan Reference
123 * Manual* chapter Constraint Transforms.
124 *
125 * @tparam Jacobian if `true`, increment log density accumulator with log
126 * absolute Jacobian determinant of constraining transform
127 * @tparam T A standard vector with inner type inheriting from
128 * `Eigen::DenseBase` or a `var_value` with inner type inheriting from
129 * `Eigen::DenseBase` with compile time dynamic rows and 1 column
130 * @param x The vector to convert to a covariance matrix
131 * @param K The dimensions of the resulting covariance matrix
132 * @param[in, out] lp log density accumulator
133 * @throws std::domain_error if (x.size() != K + (K choose 2)).
134 */
135 template <bool Jacobian, typename T, require_std_vector_t<T>* = nullptr>
cov_matrix_constrain(const T & x,Eigen::Index K,return_type_t<T> & lp)136 inline auto cov_matrix_constrain(const T& x, Eigen::Index K,
137 return_type_t<T>& lp) {
138 return apply_vector_unary<T>::apply(x, [&lp, K](auto&& v) {
139 return cov_matrix_constrain<Jacobian>(v, K, lp);
140 });
141 }
142
143 } // namespace math
144 } // namespace stan
145
146 #endif
147