*! version 1.0.1 21aug2026

Title

gvar bconv -- Geweke's convergence diagnostic for the sampled chains

Syntax

gvar bconv [, options]

options

critical(#) two-sided normal cutoff. Default 1.96.

frac1(#) leading share of each chain. Default 0.1.

frac2(#) trailing share of each chain. Default 0.5.

byunit one row per unit.

graph bar chart of the share exceeding, by unit.

name(name) graph name.

nosummary suppress the report.

gvar bayes must have run first: this reads the retained draws
and has nothing to say about a model fitted by
gvar estimate.

Description

gvar bconv computes Geweke's (1992) convergence diagnostic for every
sampled coefficient of every unit and reports how many exceed the cutoff.

The test compares the mean of the first part of a chain with the mean of the last
part. If the draws come from the stationary distribution the two are equal, and

Z = (m1 - m2) / sqrt(s1/n1 + s2/n2)

is asymptotically standard normal, where s is the spectral density at
zero rather than the sample variance.

Options

critical(#) is the two-sided normal cutoff. 1.96 is the 5% point and is
what BGVAR's conv.diag uses.

frac1(#) and frac2(#) are the leading and trailing shares of each
chain, 10% and 50% by default, as in coda's geweke.diag. They must be
positive and sum to no more than one: overlapping windows would compare a stretch
of the chain with itself.

byunit adds a table with one row per unit. Worth reading whenever the
overall share is high, because a single badly identified country model often
accounts for all of it.

graph draws the share exceeding the cutoff for each unit, with a reference
line at 5% -- the share the null implies. Without that line there is no way to
tell an ordinary chain from a bad one by eye.

Remarks

The test can only fail to reject. A share near 5% is evidence that nothing
is visibly wrong, not evidence that the chain has converged. No diagnostic
establishes convergence; they only ever catch some of the ways it fails.

Why the spectral density at zero and not the sample variance. An MCMC chain
is autocorrelated. The variance of its sample mean is the long-run variance
divided by the number of draws, and the spectral density at zero is exactly that
long-run variance. Substituting the sample variance would understate the standard
error, inflate every Z, and reject far too often -- which a reader would
naturally blame on the sampler rather than on the diagnostic.

It slightly over-rejects, and that is inherent. The spectral density is
estimated by an AR fit chosen by AIC, and Yule-Walker coefficient estimates are
biased towards zero in finite samples. That makes 1 - sum(phi) a little too
large and the estimated density a little too small, so Z is a little too
big. Measured on this implementation: about 2.5% low on the density across
phi from -0.4 to 0.8, giving sd(Z) of roughly 1.07 and a rejection rate
near 7.5% where 5% is nominal. coda has the same property, since it is the
same estimator. Read a share of 7 or 8% as unremarkable.

Coefficients that never moved have no Z. prior(ssvs) and the shrinkage
priors hold coefficients at zero by design, and a chain with no variation has no
Geweke statistic. Those are counted separately and excluded from the
denominator, not recorded as passes -- treating them as passes would make a
heavily shrunk model look better converged the more it shrank.

Not from the BGVAR source. conv.diag delegates to
coda::geweke.diag, and coda is a dependency rather than part of the
package, so the diagnostic was written from the algorithm -- the same situation as
GIGrvg for prior(ng). It is therefore gated on its own test rather
than on a comparison with another implementation: the spectral density against
its closed form sigma^2/(1-phi)^2 for an AR(1), the size of the test on
i.i.d. chains, and the power of it on a drifting chain and on a random walk.
A diagnostic that never rejects is as useless as one that always does, and only
the last of those separates them.

One deliberate difference from BGVAR. It runs the diagnostic on
A_large, the stacked coefficients; this runs it on the per-unit draws, which
are the parameters the sampler actually moves. The stacked matrix is a
deterministic function of them, so nothing is lost, and byunit can then
name the unit responsible.

Examples

Sample, then check the chains before reading anything from them:

. gvar bayes, prior(mn) prmean(0) noeigentrim draws(4000) burnin(4000) seed(20260811)

. gvar bconv

When the share is high, find out which units are responsible:

. gvar bconv, byunit

. gvar bconv, graph

A stricter cutoff, and a longer trailing window:

. gvar bconv, critical(2.58) frac1(0.2) frac2(0.5)

Stored results

gvar bconv stores the following in r():

r(table) unit, examined, exceeding, no-Z; one row per unit

r(n) coefficients examined

r(nexceed) how many exceeded the cutoff

r(pexceed) that as a percentage

r(nskipped) coefficients with no Z

r(critical) the cutoff used

r(draws) retained draws per chain

Source

BGVAR R/helpers.R conv.diag; coda's geweke.diag and
spectrum0.ar. Geweke, J. (1992), Evaluating the accuracy of sampling-based
approaches to calculating posterior moments, Bayesian Statistics 4.

Author

Dr Merwan Roudane

merwanroudane920@gmail.com

https://github.com/merwanroudane