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