autoppl  v0.8
A C++ template library for probabilistic programming
ess.hpp
Go to the documentation of this file.
1 #pragma once
2 #include <Eigen/Dense>
4 
5 namespace ppl {
6 namespace math {
7 namespace details {
8 
9 template <class T>
10 using vec_cref_t = std::vector<
11  std::reference_wrapper<const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>> >;
12 
13 } // namespace details
14 
33 template <class T>
34 inline auto ess(const details::vec_cref_t<T>& samples)
35 {
36  using value_t = T;
37  using vec_t = Eigen::Matrix<value_t, Eigen::Dynamic, 1>;
38  using mat_t = Eigen::Matrix<value_t, Eigen::Dynamic, Eigen::Dynamic>;
39 
40  vec_t tau_hat;
41 
42  size_t M = samples.size(); // number of chains
43 
44  if (M == 0) return tau_hat;
45 
46  size_t dim = samples[0].get().cols(); // sample dimension
47  size_t N = samples[0].get().rows(); // number of samples
48 
49  if (N <= 1 || dim == 0) return tau_hat;
50 
51  tau_hat = vec_t::Zero(dim);
52 
53  auto var = [N](const auto& mat) {
54  return (mat.rowwise() -
55  mat.colwise().mean()).colwise()
56  .squaredNorm() / (N-1);
57  };
58 
59  // use N-1 scaling to compute variance
60  // each col is the sample variance per chain
61  mat_t sample_vars(dim, M);
62  for (size_t i = 0; i < M; ++i) {
63  sample_vars.col(i) = var(samples[i].get());
64  }
65 
66  // column vector of average of sample variances
67  vec_t W = sample_vars.rowwise().mean();
68 
69  // compute variance estimator
70  vec_t var_est = static_cast<T>(N-1) / N * W;
71 
72  // if there is more than 1 chain, then update by N * B
73  // where B is the between-chain variance
74  if (M > 1) {
75  mat_t sample_mean(dim, M);
76  for (size_t i = 0; i < M; ++i) {
77  sample_mean.col(i) = samples[i].get().colwise().mean();
78  }
79  var_est += var(sample_mean.transpose());
80  }
81 
82  // compute autocorrelation vector for each component
83  // every column vector (every component) is average AC over chains
84  mat_t acov_mean(N, dim);
85  acov_mean.setZero();
86 
87  for (size_t m = 1; m <= M; ++m) {
88  mat_t next_acov = autocorrelation(samples[m-1].get());
89  for (int j = 0; j < next_acov.cols(); ++j) {
90  next_acov.col(j) *= sample_vars(j,m-1);
91  }
92  value_t m_inv = 1./m;
93  acov_mean = m_inv * next_acov + (m-1) * m_inv * acov_mean;
94  }
95 
96  // compute rho-hat at lag t for dimension d
97  auto rho_hat = [&](size_t t, size_t d) {
98  return 1. - (W(d) - acov_mean(t,d))/var_est(d);
99  };
100 
101  // compute tau-hat directly to save memory
102  for (size_t d = 0; d < dim; ++d) {
103 
104  // first two should not be corrected for positive and monotoneness
105  value_t curr_rho_hat_even = rho_hat(0,d);
106  value_t curr_p_hat = curr_rho_hat_even + rho_hat(1,d); // current P_hat(t)
107  value_t curr_min = curr_p_hat; // current min of P_hat(t)
108  tau_hat(d) = curr_min; // update with P_hat(0)
109 
110  // only estimate up to 3 samples before the end
111  // and Geyer's positive condition holds
112  size_t t = 2;
113  for (; t < (N-3) && curr_p_hat > 0; t += 2) {
114  curr_rho_hat_even = rho_hat(t,d);
115  curr_p_hat = curr_rho_hat_even + rho_hat(t+1,d);
116 
117  // if positive condition holds, take the min
118  // of current P_hat(t) with the min of previous P_hat's
119  // to create a monotone sequence and accumulate to tau_hat
120  if (curr_p_hat >= 0) {
121  curr_min = std::min(curr_min, curr_p_hat);
122  tau_hat(d) += curr_min;
123  }
124  }
125 
126  // correct to improve estimate (see STAN's implementation)
127  value_t correction = (curr_rho_hat_even > 0) ?
128  curr_rho_hat_even : rho_hat(t,d);
129 
130  tau_hat(d) *= 2.; // 2 * sum of adjusted P_hat(t)
131  tau_hat(d) -= 1.; // -1 + 2 * sum of adjusted P_hat(t)
132  tau_hat(d) += correction;
133  }
134 
135  vec_t n_eff = N*M*(1./tau_hat.array()).min(std::log10(N)).matrix();
136 
137  return n_eff;
138 }
139 
151 template <class T>
152 inline auto ess(const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& samples)
153 {
155  v.emplace_back(samples);
156  return ess(v);
157 }
158 
159 
160 } // namespace math
161 } // namespace ppl
ppl::util::get
auto & get(T &&x)
Definition: value.hpp:52
ppl::math::details::vec_cref_t
std::vector< std::reference_wrapper< const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > > > vec_cref_t
Definition: ess.hpp:11
ppl::math::autocorrelation
auto autocorrelation(const Eigen::MatrixBase< T > &x)
Definition: autocorrelation.hpp:26
ppl::math::ess
auto ess(const details::vec_cref_t< T > &samples)
Definition: ess.hpp:34
ppl
Definition: bounded.hpp:11
ppl::mat
ad::mat mat
Definition: shape_traits.hpp:18
autocorrelation.hpp