Automatic Differentiation
 
Loading...
Searching...
No Matches
read_corr_L.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_REV_FUN_READ_CORR_L_HPP
2#define STAN_MATH_REV_FUN_READ_CORR_L_HPP
3
8#include <cmath>
9#include <complex>
10
11namespace stan {
12namespace math {
13
35template <typename T, require_var_vector_t<T>* = nullptr>
36auto read_corr_L(const T& CPCs, size_t K) { // on (-1, 1)
37 using ret_type = var_value<Eigen::MatrixXd>;
38
39 if (K == 0) {
40 return ret_type(Eigen::MatrixXd());
41 }
42 if (K == 1) {
43 return ret_type(Eigen::MatrixXd::Identity(1, 1));
44 }
45
46 using std::sqrt;
47 arena_t<Eigen::ArrayXd> arena_acc = Eigen::ArrayXd::Ones(K - 1);
48 // Cholesky factor of correlation matrix
49 arena_t<Eigen::MatrixXd> L = Eigen::MatrixXd::Zero(K, K);
50
51 size_t pos = 0;
52 size_t pull = K - 1;
53
54 L(0, 0) = 1.0;
55 L.col(0).tail(pull) = CPCs.val().head(pull);
56 arena_acc.tail(pull) = 1.0 - CPCs.val().head(pull).array().square();
57 for (size_t i = 1; i < (K - 1); i++) {
58 pos += pull;
59 pull = K - 1 - i;
60
61 const auto& cpc_seg = CPCs.val().array().segment(pos, pull);
62 L.coeffRef(i, i) = sqrt(arena_acc.coeff(i - 1));
63 L.col(i).tail(pull) = cpc_seg * arena_acc.tail(pull).sqrt();
64 arena_acc.tail(pull) = arena_acc.tail(pull) * (1.0 - cpc_seg.square());
65 }
66
67 L.coeffRef(K - 1, K - 1) = sqrt(arena_acc.coeff(K - 2));
68
69 arena_t<ret_type> L_res = L;
70
71 reverse_pass_callback([CPCs, arena_acc, K, L_res]() mutable {
72 Eigen::ArrayXd acc_val = arena_acc;
73 Eigen::ArrayXd acc_adj = Eigen::ArrayXd::Zero(K - 1);
74
75 acc_adj.coeffRef(K - 2) += 0.5 * L_res.adj().coeff(K - 1, K - 1)
76 / L_res.val().coeff(K - 1, K - 1);
77
78 int pos = CPCs.size() - 1;
79 for (size_t i = K - 2; i > 0; --i) {
80 int pull = K - 1 - i;
81
82 const auto& cpc_seg_val = CPCs.val().array().segment(pos, pull);
83 auto cpc_seg_adj = CPCs.adj().array().segment(pos, pull);
84 acc_val.tail(pull) /= 1.0 - cpc_seg_val.square();
85 cpc_seg_adj
86 -= 2.0 * acc_adj.tail(pull) * acc_val.tail(pull) * cpc_seg_val;
87 acc_adj.tail(pull)
88 = (acc_adj.tail(pull).array() * (1.0 - cpc_seg_val.square())).eval();
89 cpc_seg_adj
90 += L_res.adj().array().col(i).tail(pull) * acc_val.tail(pull).sqrt();
91 acc_adj.tail(pull) += 0.5 * L_res.adj().array().col(i).tail(pull)
92 * cpc_seg_val / acc_val.tail(pull).sqrt();
93 acc_adj.coeffRef(i - 1)
94 += 0.5 * L_res.adj().coeff(i, i) / L_res.val().coeff(i, i);
95
96 pos -= pull + 1;
97 }
98
99 int pull = K - 1;
100 CPCs.adj().array().head(pull)
101 -= 2.0 * acc_adj.tail(pull) * CPCs.val().array().head(pull);
102 CPCs.adj().head(pull) += L_res.adj().col(0).tail(pull);
103 });
104
105 return ret_type(L_res);
106}
107
133template <typename T1, typename T2, require_var_vector_t<T1>* = nullptr,
134 require_stan_scalar_t<T2>* = nullptr>
135auto read_corr_L(const T1& CPCs, size_t K, T2& log_prob) {
136 using ret_val_type = Eigen::MatrixXd;
137 using ret_type = var_value<Eigen::MatrixXd>;
138
139 if (K == 0) {
140 return ret_type(ret_val_type());
141 }
142 if (K == 1) {
143 return ret_type(Eigen::MatrixXd::Identity(1, 1));
144 }
145
146 size_t pos = 0;
147 double acc_val = 0;
148 // no need to abs() because this Jacobian determinant
149 // is strictly positive (and triangular)
150 // see inverse of Jacobian in equation 11 of LKJ paper
151 for (size_t k = 1; k <= (K - 2); k++) {
152 for (size_t i = k + 1; i <= K; i++) {
153 acc_val += (K - k - 1) * log1m(square(CPCs.val().coeff(pos)));
154 pos++;
155 }
156 }
157
158 log_prob += 0.5 * acc_val;
159
160 reverse_pass_callback([K, CPCs, log_prob]() mutable {
161 double acc_adj = log_prob.adj() * 0.5;
162
163 size_t pos = CPCs.size() - 2;
164 for (size_t k = K - 2; k >= 1; --k) {
165 for (size_t i = K; i >= k + 1; --i) {
166 CPCs.adj().coeffRef(pos) -= 2.0 * acc_adj * (K - k - 1)
167 * CPCs.val().coeff(pos)
168 / (1.0 - square(CPCs.val().coeff(pos)));
169 pos--;
170 }
171 }
172 });
173
174 return read_corr_L(CPCs, K);
175}
176
177} // namespace math
178} // namespace stan
179#endif
void reverse_pass_callback(F &&functor)
Puts a callback on the autodiff stack to be called in reverse pass.
fvar< T > sqrt(const fvar< T > &x)
Definition sqrt.hpp:17
fvar< T > log1m(const fvar< T > &x)
Definition log1m.hpp:12
Eigen::Matrix< value_type_t< T >, Eigen::Dynamic, Eigen::Dynamic > read_corr_L(const T &CPCs, size_t K)
Return the Cholesky factor of the correlation matrix of the specified dimensionality corresponding to...
fvar< T > square(const fvar< T > &x)
Definition square.hpp:12
typename internal::arena_type_impl< std::decay_t< T > >::type arena_t
Determines a type that can be used in place of T that does any dynamic allocations on the AD stack.
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...