Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/3299 improve ess mcse #3305

Closed
wants to merge 90 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
b2e2a34
Merge branch 'develop' of https://github.com/stan-dev/stan into develop
mitzimorris Jul 11, 2024
9d60cc7
Merge branch 'develop' of https://github.com/stan-dev/stan into develop
mitzimorris Jul 16, 2024
986fcfc
code readibility
mitzimorris Jul 17, 2024
487e4ab
code readibility
mitzimorris Jul 17, 2024
90e0b1f
code cleanup
mitzimorris Jul 17, 2024
2ae8f73
code cleanup
mitzimorris Jul 17, 2024
f1af73d
code cleanup
mitzimorris Jul 17, 2024
447c809
checkpointing
mitzimorris Jul 19, 2024
f299899
checkpointing
mitzimorris Jul 23, 2024
1dfa589
Merge branch 'develop' of https://github.com/stan-dev/stan into develop
mitzimorris Jul 23, 2024
ce88185
unit tests for added logic
mitzimorris Jul 23, 2024
eef026d
checks and unit tests
mitzimorris Jul 23, 2024
a201612
lint fix
mitzimorris Jul 23, 2024
9730bdc
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jul 23, 2024
2226ae3
Merge branch 'bugfix/3301-stan-csv-reader-skip-warmup' of https://git…
mitzimorris Jul 23, 2024
d7aeddd
adding test file
mitzimorris Jul 23, 2024
c33e954
fix divide by zero error
mitzimorris Jul 23, 2024
d7c0f05
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jul 23, 2024
b1d815f
Merge branch 'bugfix/3301-stan-csv-reader-skip-warmup' of https://git…
mitzimorris Jul 23, 2024
82aa58d
refactoring, checkpointing
mitzimorris Jul 24, 2024
d732c02
Merge branch 'develop' of https://github.com/stan-dev/stan into featu…
mitzimorris Jul 26, 2024
9b91b62
checkpointing
mitzimorris Jul 26, 2024
7a30adc
checkpointing
mitzimorris Jul 30, 2024
5caba03
checkpointing; unit tests pass
mitzimorris Jul 31, 2024
09170ed
checkpointing; unit tests pass
mitzimorris Jul 31, 2024
988e2b1
checkpointing
mitzimorris Jul 31, 2024
203dcea
checkpointing
mitzimorris Jul 31, 2024
8cc5259
Merge commit '01f592383cf29f2483451b9d5b4718666a50b3a8' into HEAD
yashikno Jul 31, 2024
5cc34b3
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jul 31, 2024
d42ef25
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Jul 31, 2024
fb44712
checkpointing; disagreement w/ CmdStanR
mitzimorris Jul 31, 2024
85fdef4
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jul 31, 2024
091b10f
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Jul 31, 2024
aaef4f4
add new function file
mitzimorris Jul 31, 2024
195d183
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jul 31, 2024
1d8f066
unit tests passing
mitzimorris Aug 1, 2024
c2e2778
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 1, 2024
3134e2b
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 1, 2024
1cef19d
checkpointing, added quantiles, mean, sd, mad, need unit tests
mitzimorris Aug 2, 2024
07f52a4
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 2, 2024
f144ba3
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 2, 2024
2e06bd3
checkpointing
mitzimorris Aug 2, 2024
6f68766
merge fix
mitzimorris Aug 2, 2024
c24e986
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 2, 2024
882f0f2
more unit tests
mitzimorris Aug 2, 2024
0bc2bc1
merge fix
mitzimorris Aug 2, 2024
db29c88
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 2, 2024
f86a766
minimal unit tests in place
mitzimorris Aug 2, 2024
6980f44
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 2, 2024
fe56771
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 2, 2024
0b1b192
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 2, 2024
4799ccb
header fix
mitzimorris Aug 2, 2024
0f133b4
remove old tests
mitzimorris Aug 2, 2024
fd2e2e0
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 2, 2024
ed3d41e
revert old compute ess code
mitzimorris Aug 2, 2024
f2e6cb5
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 2, 2024
b9f0979
fix stan_csv_reader logic
mitzimorris Aug 2, 2024
2cd6941
parse metadata, check for string true for bool values
mitzimorris Aug 2, 2024
94a914a
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 2, 2024
dc5dfb9
commented all chainset methods
mitzimorris Aug 3, 2024
a63b1ca
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 3, 2024
f8a4c37
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 3, 2024
4205136
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 3, 2024
1c59808
test throws
mitzimorris Aug 3, 2024
e8d96a1
more unit tests
mitzimorris Aug 3, 2024
51c0502
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 3, 2024
f6c70bb
fix cut-paste error in doc comments
mitzimorris Aug 4, 2024
c96500a
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 4, 2024
70abd86
merge fix
mitzimorris Aug 4, 2024
9d3f80b
stan_csv_reader - logic to parse fixed_param output
mitzimorris Aug 4, 2024
246cb52
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 4, 2024
db6eb32
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 4, 2024
7993837
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 4, 2024
6ab986e
stan_csv_reader - doc comments
mitzimorris Aug 4, 2024
3d94edc
adding autocovariance function, used by stansummary
mitzimorris Aug 5, 2024
1c6804d
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Aug 5, 2024
f746b43
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 5, 2024
bf03afd
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Aug 5, 2024
e515fd1
remove empty template parameter for chainset obj
mitzimorris Sep 23, 2024
300809c
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Sep 23, 2024
94a7e3b
stan_math fix?
mitzimorris Sep 24, 2024
52c7bbf
Merge branch 'develop' of https://github.com/stan-dev/stan into featu…
mitzimorris Sep 24, 2024
4af0913
changes per code review
mitzimorris Sep 24, 2024
e6d53c9
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Sep 25, 2024
6f4f661
harmonize get_stats from chainset and parsing of csv files
mitzimorris Sep 25, 2024
097883f
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Sep 25, 2024
c3178b9
Merge branch 'feature/3299-improve-ESS-MCSE' of https://github.com/st…
mitzimorris Sep 25, 2024
60495e7
unit test fix
mitzimorris Sep 25, 2024
340f0d9
changes per code review
mitzimorris Sep 26, 2024
fbf45ab
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/stan_math
43 changes: 43 additions & 0 deletions src/stan/analyze/mcmc/check_chains.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#ifndef STAN_ANALYZE_MCMC_CHECK_CHAINS_HPP
#define STAN_ANALYZE_MCMC_CHECK_CHAINS_HPP

#include <stan/math/prim.hpp>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <utility>
#include <vector>

namespace stan {
namespace analyze {

/**
* Checks that values across all matrix columns finite and non-identical.
*
* @param chains matrix of draws, one column per chain
WardBrian marked this conversation as resolved.
Show resolved Hide resolved
* @return bool true if OK, false otherwise
*/
inline bool is_finite_and_varies(const Eigen::MatrixXd chains) {
size_t num_chains = chains.cols();
size_t num_samples = chains.rows();
Eigen::VectorXd first_draws = Eigen::VectorXd::Zero(num_chains);
for (std::size_t i = 0; i < num_chains; ++i) {
first_draws(i) = chains.col(i)(0);
for (int j = 0; j < num_samples; ++j) {
if (!std::isfinite(chains.col(i)(j)))
return false;
}
if (chains.col(i).isApproxToConstant(first_draws(i))) {
return false;
}
}
if (num_chains > 1 && first_draws.isApproxToConstant(first_draws(0))) {
return false;
}
return true;
}

} // namespace analyze
} // namespace stan

#endif
236 changes: 0 additions & 236 deletions src/stan/analyze/mcmc/compute_potential_scale_reduction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,156 +17,6 @@
namespace stan {
namespace analyze {

/**
* Computes normalized average ranks for draws. Transforming them to normal
* scores using inverse normal transformation and a fractional offset. Based on
* paper https://arxiv.org/abs/1903.08008
* @param chains stores chains in columns
* @return normal scores for average ranks of draws
*/
inline Eigen::MatrixXd rank_transform(const Eigen::MatrixXd& chains) {
const Eigen::Index rows = chains.rows();
const Eigen::Index cols = chains.cols();
const Eigen::Index size = rows * cols;

std::vector<std::pair<double, int>> value_with_index(size);

for (Eigen::Index i = 0; i < size; ++i) {
value_with_index[i] = {chains(i), i};
}

std::sort(value_with_index.begin(), value_with_index.end());

Eigen::MatrixXd rank_matrix = Eigen::MatrixXd::Zero(rows, cols);

// Assigning average ranks
for (Eigen::Index i = 0; i < size; ++i) {
// Handle ties by averaging ranks
Eigen::Index j = i + 1;
double sum_ranks = j;
Eigen::Index count = 1;

while (j < size && value_with_index[j].first == value_with_index[i].first) {
sum_ranks += j + 1; // Rank starts from 1
++j;
++count;
}
double avg_rank = sum_ranks / count;
boost::math::normal_distribution<double> dist;
for (std::size_t k = i; k < j; ++k) {
double p = (avg_rank - 3.0 / 8.0) / (size - 2.0 * 3.0 / 8.0 + 1.0);
const Eigen::Index index = value_with_index[k].second;
rank_matrix(index) = boost::math::quantile(dist, p);
}
i = j - 1; // Skip over tied elements
}
return rank_matrix;
}

/**
* Computes square root of marginal posterior variance of the estimand by the
* weigted average of within-chain variance W and between-chain variance B.
*
* @param chains stores chains in columns
* @return square root of ((N-1)/N)W + B/N
*/
inline double rhat(const Eigen::MatrixXd& chains) {
mitzimorris marked this conversation as resolved.
Show resolved Hide resolved
const Eigen::Index num_chains = chains.cols();
const Eigen::Index num_draws = chains.rows();

Eigen::RowVectorXd within_chain_means = chains.colwise().mean();
double across_chain_mean = within_chain_means.mean();
double between_variance
= num_draws
* (within_chain_means.array() - across_chain_mean).square().sum()
/ (num_chains - 1);
double within_variance =
// Divide each row by chains and get sum of squares for each chain
// (getting a vector back)
((chains.rowwise() - within_chain_means)
.array()
.square()
.colwise()
// divide each sum of square by num_draws, sum the sum of squares,
// and divide by num chains
.sum()
/ (num_draws - 1.0))
.sum()
/ num_chains;

return sqrt((between_variance / within_variance + num_draws - 1) / num_draws);
}

/**
* Computes the potential scale reduction (Rhat) using rank based diagnostic for
* the specified parameter across all kept samples. Based on paper
* https://arxiv.org/abs/1903.08008
*
* Current implementation assumes draws are stored in contiguous
* blocks of memory. Chains are trimmed from the back to match the
* length of the shortest chain.
*
* @param chain_begins stores pointers to arrays of chains
* @param chain_sizes stores sizes of chains
* @return potential scale reduction for the specified parameter
*/
inline std::pair<double, double> compute_potential_scale_reduction_rank(
const std::vector<const double*>& chain_begins,
const std::vector<size_t>& chain_sizes) {
std::vector<const double*> nonzero_chain_begins;
std::vector<std::size_t> nonzero_chain_sizes;
nonzero_chain_begins.reserve(chain_begins.size());
nonzero_chain_sizes.reserve(chain_sizes.size());
for (size_t i = 0; i < chain_sizes.size(); ++i) {
if (chain_sizes[i]) {
nonzero_chain_begins.push_back(chain_begins[i]);
nonzero_chain_sizes.push_back(chain_sizes[i]);
}
}
if (!nonzero_chain_sizes.size()) {
return {std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN()};
}
std::size_t num_nonzero_chains = nonzero_chain_sizes.size();
std::size_t min_num_draws = nonzero_chain_sizes[0];
for (std::size_t chain = 1; chain < num_nonzero_chains; ++chain) {
min_num_draws = std::min(min_num_draws, nonzero_chain_sizes[chain]);
}

// check if chains are constant; all equal to first draw's value
bool are_all_const = false;
Eigen::VectorXd init_draw = Eigen::VectorXd::Zero(num_nonzero_chains);
Eigen::MatrixXd draws_matrix(min_num_draws, num_nonzero_chains);

for (std::size_t chain = 0; chain < num_nonzero_chains; chain++) {
Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, 1>> draws(
nonzero_chain_begins[chain], nonzero_chain_sizes[chain]);

for (std::size_t n = 0; n < min_num_draws; n++) {
if (!std::isfinite(draws(n))) {
return {std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN()};
}
draws_matrix(n, chain) = draws(n);
}

init_draw(chain) = draws(0);
are_all_const |= !draws.isApproxToConstant(draws(0));
}
// If all chains are constant then return NaN
if (are_all_const && init_draw.isApproxToConstant(init_draw(0))) {
return {std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN()};
}

double rhat_bulk = rhat(rank_transform(draws_matrix));
double rhat_tail = rhat(rank_transform(
(draws_matrix.array() - math::quantile(draws_matrix.reshaped(), 0.5))
.abs()));

return std::make_pair(rhat_bulk, rhat_tail);
}

/**
* Computes the potential scale reduction (Rhat) for the specified
* parameter across all kept samples.
Expand Down Expand Up @@ -248,33 +98,9 @@ inline double compute_potential_scale_reduction(
* num_chains / (num_chains - 1);
double var_within = chain_var.mean();

// rewrote [(n-1)*W/n + B/n]/W as (n-1+ B/W)/n
return sqrt((var_between / var_within + num_draws - 1) / num_draws);
}

/**
* Computes the potential scale reduction (Rhat) using rank based diagnostic for
* the specified parameter across all kept samples. Based on paper
* https://arxiv.org/abs/1903.08008
*
* See more details in Stan reference manual section "Potential
* Scale Reduction". http://mc-stan.org/users/documentation
*
* Current implementation assumes draws are stored in contiguous
* blocks of memory. Chains are trimmed from the back to match the
* length of the shortest chain. Argument size will be broadcast to
* same length as draws.
*
* @param chain_begins stores pointers to arrays of chains
* @param size stores sizes of chains
* @return potential scale reduction for the specified parameter
*/
inline std::pair<double, double> compute_potential_scale_reduction_rank(
const std::vector<const double*>& chain_begins, size_t size) {
std::vector<size_t> sizes(chain_begins.size(), size);
return compute_potential_scale_reduction_rank(chain_begins, sizes);
}

/**
* Computes the potential scale reduction (Rhat) for the specified
* parameter across all kept samples.
Expand All @@ -298,42 +124,6 @@ inline double compute_potential_scale_reduction(
return compute_potential_scale_reduction(draws, sizes);
}

/**
* Computes the potential scale reduction (Rhat) using rank based diagnostic for
* the specified parameter across all kept samples. Based on paper
* https://arxiv.org/abs/1903.08008
*
* When the number of total draws N is odd, the (N+1)/2th draw is ignored.
*
* See more details in Stan reference manual section "Potential
* Scale Reduction". http://mc-stan.org/users/documentation
*
* Current implementation assumes draws are stored in contiguous
* blocks of memory. Chains are trimmed from the back to match the
* length of the shortest chain.
*
* @param chain_begins stores pointers to arrays of chains
* @param chain_sizes stores sizes of chains
* @return potential scale reduction for the specified parameter
*/
inline std::pair<double, double> compute_split_potential_scale_reduction_rank(
const std::vector<const double*>& chain_begins,
const std::vector<size_t>& chain_sizes) {
size_t num_chains = chain_sizes.size();
size_t num_draws = chain_sizes[0];
for (size_t chain = 1; chain < num_chains; ++chain) {
num_draws = std::min(num_draws, chain_sizes[chain]);
}

std::vector<const double*> split_draws
= split_chains(chain_begins, chain_sizes);

size_t half = std::floor(num_draws / 2.0);
std::vector<size_t> half_sizes(2 * num_chains, half);

return compute_potential_scale_reduction_rank(split_draws, half_sizes);
}

/**
* Computes the split potential scale reduction (Rhat) for the
* specified parameter across all kept samples. When the number of
Expand Down Expand Up @@ -366,32 +156,6 @@ inline double compute_split_potential_scale_reduction(
return compute_potential_scale_reduction(split_draws, half_sizes);
}

/**
* Computes the potential scale reduction (Rhat) using rank based diagnostic for
* the specified parameter across all kept samples. Based on paper
* https://arxiv.org/abs/1903.08008
*
* When the number of total draws N is odd, the (N+1)/2th draw is ignored.
*
* See more details in Stan reference manual section "Potential
* Scale Reduction". http://mc-stan.org/users/documentation
*
* Current implementation assumes draws are stored in contiguous
* blocks of memory. Chains are trimmed from the back to match the
* length of the shortest chain. Argument size will be broadcast to
* same length as draws.
*
* @param chain_begins stores pointers to arrays of chains
* @param size stores sizes of chains
* @return potential scale reduction for the specified parameter
*/
inline std::pair<double, double> compute_split_potential_scale_reduction_rank(
const std::vector<const double*>& chain_begins, size_t size) {
size_t num_chains = chain_begins.size();
std::vector<size_t> sizes(num_chains, size);
return compute_split_potential_scale_reduction_rank(chain_begins, sizes);
}

/**
* Computes the split potential scale reduction (Rhat) for the
* specified parameter across all kept samples. When the number of
Expand Down
61 changes: 61 additions & 0 deletions src/stan/analyze/mcmc/rank_normalization.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#ifndef STAN_ANALYZE_MCMC_RANK_NORMALIZATION_HPP
#define STAN_ANALYZE_MCMC_RANK_NORMALIZATION_HPP

#include <stan/math/prim.hpp>
#include <boost/math/distributions/normal.hpp>
#include <algorithm>
#include <cmath>
#include <vector>
#include <limits>

namespace stan {
namespace analyze {

/**
* Computes normalized average ranks for pooled draws. Normal scores computed
* using inverse normal transformation and a fractional offset. Based on paper
* https://arxiv.org/abs/1903.08008
*
* @param chains matrix of draws, one column per chain
* @return normal scores for average ranks of draws
*/
inline Eigen::MatrixXd rank_transform(const Eigen::MatrixXd& chains) {
const Eigen::Index rows = chains.rows();
const Eigen::Index cols = chains.cols();
const Eigen::Index size = rows * cols;

std::vector<std::pair<double, int>> value_with_index(size);
for (Eigen::Index i = 0; i < size; ++i) {
value_with_index[i] = {chains(i), i};
}

std::sort(value_with_index.begin(), value_with_index.end());
Eigen::MatrixXd rank_matrix = Eigen::MatrixXd::Zero(rows, cols);
// Assigning average ranks
for (Eigen::Index i = 0; i < size; ++i) {
// Handle ties by averaging ranks
Eigen::Index j = i + 1;
double sum_ranks = j;
Eigen::Index count = 1;

while (j < size && value_with_index[j].first == value_with_index[i].first) {
sum_ranks += j + 1; // Rank starts from 1
++j;
++count;
}
double avg_rank = sum_ranks / count;
boost::math::normal_distribution<double> dist;
for (std::size_t k = i; k < j; ++k) {
double p = (avg_rank - 0.375) / (size + 0.25);
const Eigen::Index index = value_with_index[k].second;
rank_matrix(index) = boost::math::quantile(dist, p);
}
i = j - 1; // Skip over tied elements
}
return rank_matrix;
}

} // namespace analyze
} // namespace stan

#endif
Loading
Loading