1 #ifndef STAN_MATH_PRIM_FUN_CHOLESKY_CORR_CONSTRAIN_HPP
2 #define STAN_MATH_PRIM_FUN_CHOLESKY_CORR_CONSTRAIN_HPP
3 
4 #include <stan/math/prim/err.hpp>
5 #include <stan/math/prim/fun/Eigen.hpp>
6 #include <stan/math/prim/fun/log1m.hpp>
7 #include <stan/math/prim/fun/sqrt.hpp>
8 #include <stan/math/prim/fun/square.hpp>
9 #include <stan/math/prim/fun/corr_constrain.hpp>
10 #include <cmath>
11 
12 namespace stan {
13 namespace math {
14 
15 template <typename EigVec, require_eigen_col_vector_t<EigVec>* = nullptr>
16 inline Eigen::Matrix<value_type_t<EigVec>, Eigen::Dynamic, Eigen::Dynamic>
cholesky_corr_constrain(const EigVec & y,int K)17 cholesky_corr_constrain(const EigVec& y, int K) {
18   using Eigen::Dynamic;
19   using Eigen::Matrix;
20   using std::sqrt;
21   using T_scalar = value_type_t<EigVec>;
22   int k_choose_2 = (K * (K - 1)) / 2;
23   check_size_match("cholesky_corr_constrain", "constrain size", y.size(),
24                    "k_choose_2", k_choose_2);
25   Matrix<T_scalar, Dynamic, 1> z = corr_constrain(y);
26   Matrix<T_scalar, Dynamic, Dynamic> x(K, K);
27   if (K == 0) {
28     return x;
29   }
30   x.setZero();
31   x.coeffRef(0, 0) = 1;
32   int k = 0;
33   for (int i = 1; i < K; ++i) {
34     x.coeffRef(i, 0) = z.coeff(k++);
35     T_scalar sum_sqs = square(x.coeff(i, 0));
36     for (int j = 1; j < i; ++j) {
37       x.coeffRef(i, j) = z.coeff(k++) * sqrt(1.0 - sum_sqs);
38       sum_sqs += square(x.coeff(i, j));
39     }
40     x.coeffRef(i, i) = sqrt(1.0 - sum_sqs);
41   }
42   return x;
43 }
44 
45 // FIXME to match above after debugged
46 template <typename EigVec, require_eigen_vector_t<EigVec>* = nullptr>
47 inline Eigen::Matrix<value_type_t<EigVec>, Eigen::Dynamic, Eigen::Dynamic>
cholesky_corr_constrain(const EigVec & y,int K,return_type_t<EigVec> & lp)48 cholesky_corr_constrain(const EigVec& y, int K, return_type_t<EigVec>& lp) {
49   using Eigen::Dynamic;
50   using Eigen::Matrix;
51   using std::sqrt;
52   using T_scalar = value_type_t<EigVec>;
53   int k_choose_2 = (K * (K - 1)) / 2;
54   check_size_match("cholesky_corr_constrain", "y.size()", y.size(),
55                    "k_choose_2", k_choose_2);
56   Matrix<T_scalar, Dynamic, 1> z = corr_constrain(y, lp);
57   Matrix<T_scalar, Dynamic, Dynamic> x(K, K);
58   if (K == 0) {
59     return x;
60   }
61   x.setZero();
62   x.coeffRef(0, 0) = 1;
63   int k = 0;
64   for (int i = 1; i < K; ++i) {
65     x.coeffRef(i, 0) = z.coeff(k++);
66     T_scalar sum_sqs = square(x.coeff(i, 0));
67     for (int j = 1; j < i; ++j) {
68       lp += 0.5 * log1m(sum_sqs);
69       x.coeffRef(i, j) = z.coeff(k++) * sqrt(1.0 - sum_sqs);
70       sum_sqs += square(x.coeff(i, j));
71     }
72     x.coeffRef(i, i) = sqrt(1.0 - sum_sqs);
73   }
74   return x;
75 }
76 
77 /**
78  * Return The cholesky of a `KxK` correlation matrix. If the `Jacobian`
79  * parameter is `true`, the log density accumulator is incremented with the log
80  * absolute Jacobian determinant of the transform.  All of the transforms are
81  * specified with their Jacobians in the *Stan Reference Manual* chapter
82  * Constraint Transforms.
83  * @tparam Jacobian if `true`, increment log density accumulator with log
84  * absolute Jacobian determinant of constraining transform
85  * @tparam T A type inheriting from `Eigen::DenseBase` or a `var_value` with
86  *  inner type inheriting from `Eigen::DenseBase` with compile time dynamic rows
87  *  and 1 column
88  * @param y Linearly Serialized vector of size `(K * (K - 1))/2` holding the
89  *  column major order elements of the lower triangurlar
90  * @param K The size of the matrix to return
91  * @param[in,out] lp log density accumulator
92  */
93 template <bool Jacobian, typename T, require_not_std_vector_t<T>* = nullptr>
cholesky_corr_constrain(const T & y,int K,return_type_t<T> & lp)94 inline auto cholesky_corr_constrain(const T& y, int K, return_type_t<T>& lp) {
95   if (Jacobian) {
96     return cholesky_corr_constrain(y, K, lp);
97   } else {
98     return cholesky_corr_constrain(y, K);
99   }
100 }
101 
102 /**
103  * Return The cholesky of a `KxK` correlation matrix. If the `Jacobian`
104  * parameter is `true`, the log density accumulator is incremented with the log
105  * absolute Jacobian determinant of the transform.  All of the transforms are
106  * specified with their Jacobians in the *Stan Reference Manual* chapter
107  * Constraint Transforms.
108  * @tparam Jacobian if `true`, increment log density accumulator with log
109  * absolute Jacobian determinant of constraining transform
110  * @tparam T A standard vector with inner type inheriting from
111  * `Eigen::DenseBase` or a `var_value` with inner type inheriting from
112  * `Eigen::DenseBase` with compile time dynamic rows and 1 column
113  * @param y Linearly Serialized vector of size `(K * (K - 1))/2` holding the
114  *  column major order elements of the lower triangurlar
115  * @param K The size of the matrix to return
116  * @param[in,out] lp log density accumulator
117  */
118 template <bool Jacobian, typename T, require_std_vector_t<T>* = nullptr>
cholesky_corr_constrain(const T & y,int K,return_type_t<T> & lp)119 inline auto cholesky_corr_constrain(const T& y, int K, return_type_t<T>& lp) {
120   return apply_vector_unary<T>::apply(y, [&lp, K](auto&& v) {
121     return cholesky_corr_constrain<Jacobian>(v, K, lp);
122   });
123 }
124 
125 }  // namespace math
126 }  // namespace stan
127 #endif
128