Repository navigation
Conversation
|
@avehtari can you confirm that the failures in cmdstan are expected? The existence of stan-dev/cmdstan#1363 seems to imply so, but wanted to be sure |
|
Yes. With this PR, 3 of the 21 tests in CSV output ( CmdStan For example, the MCSE of The console output shows 2 significant digits, so Console output, CmdStan CmdStan |
WardBrian
left a comment
There was a problem hiding this comment.
The code here looks good but I ask that you don't merge until the cmdstan PR is also approved to prevent there being a period of time where CI is broken. Thanks!
Submission Checklist
./runTests.py src/test/unit/analyze/mcmc,src/test/unit/mcmc/chainset_test.cpp,src/test/unit/mcmc/chains_test.cppmake cpplintSummary
Fixes #3453. Four commits; the first fixes the issue, the others remove the remaining differences from the R package
posterior. Each commit has its own tests.Commit 1: constant chains (#3453)
Current behavior (
develop):autocorrelation()divides by the lag-0 autocovariance, which is 0 for a constant chain. The NaN reachesess(), the guard!std::isnan(rho_hat_even + rho_hat_odd)stops the Geyer loop at once, andess()returns exactly N/2. This happens for every matrix that has a constant column, and for a constant matrix. Insplit_rank_normalized_ess(), the tail indicator of a split chain that never enters a tail is constant, so the tail-ESS of chains in different modes is N/2. Separately,is_finite_and_varies()returns false if one (split) chain is constant, so R-hat and ESS are NaN when one chain does not move.New behavior:
autocovariance()returns 0 at every lag for a constant sequence, asposterior::autocovariance()does. A constant chain then contributes zero within-chain variance and zero autocovariance, andess()gives a finite value.ess()returns NaN if all draws are equal or a draw is not finite (var_plusis not positive).is_finite_and_varies()returns false only if a value is not finite or every chain is constant.posteriorreturns NA only if all draws are equal. This commit also returns NaN when every chain is constant at different values. That case includesstepsize__, which is constant in each chain and different between chains, sostansummaryoutput forstepsize__does not change.Commit 2: end of the Geyer sum
Current behavior (
develop): the loop over pairs of autocorrelations stops at the first negative pair or at the lag limitN - 4. At a negative pair,ess()does not store the pair, and the result agrees withposteriorto 1e-14. At the lag limit the last pair is positive and stored.rho_hat_t.head(max_t)includes its even lag, andrho_hat_t(max_t + 1)adds that lag again, so it counts three times instead of once. The loop reaches the lag limit when the autocorrelations stay positive, which happens for chains that do not mix and for short chains. The ESS then differs fromposterior(it is lower in 1342 of 1847 random cases), and the relative difference is about 1/N (median over random AR(1) inputs: 0.13 at N = 10, 0.020 at N = 50, 0.0018 at N = 500).New behavior: the truncation lag is the even lag of the last pair computed, both at a negative pair and at the lag limit, as in
posterior. At the lag limit that lag counts once. The result at a negative pair does not change.Commit 3: tail-ESS when one tail is NaN
Current behavior (
develop): the tail indicators arex <= q05andx >= q95. If more than 95% of the draws are at one value, the indicator of one tail is constant and its ESS is NaN.split_rank_normalized_ess()then reports the ESS of the other tail as the tail-ESS.New behavior: the tail-ESS is NaN if the ESS of either tail is NaN, as
posterior::ess_tail()does. The indicators do not change.In addition, stan-dev/posterior#NNNN changes the upper-tail indicator of
posteriorfromx <= q95tox >= q95, as in Stan. Both then give the same tail-ESS for discretexand-x, and a finite tail-ESS for discrete draws with more than 5% at the maximum.Commit 4: MCSE
Current behavior (
develop):mcse_mean()uses the ESS of the unsplit chains, whileposterior(ess_mean()) and every other diagnostic in this namespace use split chains.mcse_sd()uses the ESS of the split chains of|x - mean|, as an oldposteriorversion did, and computes the variance of the variance asmean(d^4) - mean(d^2)^2.New behavior:
mcse_mean()usesess(split_chains(x)).mcse_sd()uses the ESS of the split chains ofd2 = (x - mean)^2, the quantity in the variance estimate, asposteriornow does. It computes the variance of the variance asmean((d2 - mean(d2))^2), which avoids a cancellation.Intended Effect
Example of #3453, 4 chains of 1000 draws, chains 1 to 3 iid N(0, 1):
posteriorAfter the four commits, R-hat, bulk-ESS, tail-ESS and both MCSEs equal the values of
posterior(master plus stan-dev/posterior#NNNN) to the printed digits for all test inputs, except when every chain is constant (NaN here, Inf R-hat inposterior).How to Verify
./runTests.py src/test/unit/analyze/mcmcand./runTests.py src/test/unit/mcmc. The reference values in the new tests are fromposterior. The inputs are thebern*.csvtheta draws with a change to chain 4: constant, second half constant, or shifted by 1 so that it never enters either 5% tail.autocovariance_test,check_chains_test,ess_basic_test,split_rank_normalized_ess_testandsplit_rank_normalized_rhat_test. With thedevelopheaders, 9 tests (32 expectations) fail. Three ESS values at the lag limit have a tolerance of 1% in this commit.mcse_testreference values fromposteriorwith a tolerance of 1e-10 (it was 1e-4), and a constant-chain test. With the headers of commit 3, 2 tests (6 expectations) fail.Every commit passes all of
src/test/unit/analyze/mcmc,src/test/unit/mcmc/chainset_testandsrc/test/unit/mcmc/chains_test(13 suites, 86 tests after commit 4). Tested withdevelopc3db9b995and Stan Math579253050, g++ 14.3.0, Linux x86_64.Side Effects
CmdStan
stansummaryoutput changes:stansummary_testvalues that this PR changes: the MCSE oflp__,theta,theta[6]andtheta[7]incheck_csv_output,check_csv_output_no_percentilesandcheck_csv_output_include_param. With CmdStandevelop, these 3 tests fail with this PR; with that CmdStan branch, all 21stansummary_testtests pass.The deprecated
compute_effective_sample_size()andcompute_split_effective_sample_size()also callautocovariance(), so their results change for inputs with a constant chain; their tests pass unchanged.Documentation
Doc comments of
ess(),autocovariance(),is_finite_and_varies(),mcse_mean()andmcse_sd().Copyright and Licensing
Please list the copyright holder for the work you are submitting (this will be you or your assignee, such as a university or company):
Aalto University
By submitting this pull request, the copyright holder is agreeing to license the submitted work under the following licenses: