Changelog
Source:NEWS.md
bml 0.12.0
Sampling-geometry release. Multiple-membership models with weakly informed members mixed badly under the previous defaults: standard deviations routinely returned bulk ESS in the single digits and R-hat above 2 even on long runs. The causes were the model parameterization, the default scale priors, and the starting values — not the sampler settings. Refit any model whose convergence mattered; estimates from earlier versions are unaffected in expectation but were often not converged.
Non-centred random effects
- Independent
mm()andhm()random intercepts and slopes are now sampled in non-centred form (u = sigma * z,z ~ N(0, 1)) instead of the centredu ~ N(0, sigma^2). The two imply the same posterior; the non-centred form removes the funnel between an effect and its own standard deviation, which is what stalledsigmasampling. Correlated (cor = TRUE) and AR blocks are unchanged in this release.
Response-scaled default sd priors
- The default prior on every standard deviation is now a half-normal scaled to the response, replacing
dscaled.gamma(25, 1)— a half-Cauchy(0, 25) whose mass sat orders of magnitude above any realistic sd and whose tails fed the funnel above. - For an
mm()block the scale additionally undoes membership attenuation. A member effect reaches the outcome only through the weighted sumsum_k w_ik u_k, whose standard deviation issigma * sqrt(sum_k w_ik^2)— the square root of the Herfindahl index of the weights, or one over the square root of the effective number of members. A member-level sd therefore lives on a scale aboutsqrt(effN)above its footprint on the outcome, and scaling it tosd(y)alone would shrink it by that factor. - Explicit
prior(..., class = "sd")specifications are unaffected.
Dispersed starting values
- Estimated
w()andfn()parameters now start each chain from its own dispersed draw, taken on the scale of the parameter’s prior and held inside its support. Previously every chain started from one shared value, which makes R-hat anti-conservative — it assumes overdispersed starting points. - Starting values for
fn("smax", kappa = est())andfn("gmean", p = est())are kept away from zero, where those functions are singular.kappanow starts on both sides of zero rather than always at+1, so a min-like and a max-like posterior are explored symmetrically. - User-supplied
initsare applied exactly as given and are never jittered.
Adaptation is exposed and honoured in parallel
- New
n.adaptargument (default 1000, was R2jags’s 100). JAGS freezes sampler tuning when adaptation ends, so models with non-conjugate nodes benefit from a longer adaptation phase. -
cores > 1no longer routes throughR2jags::jags.parallel, which silently ignoresn.adaptand cannot give each chain its own starting values. Chains now run through a smallparLapplyrunner and are recombined identically. -
run = FALSEadditionally returnsjags.initsfor inspection.
bml 0.11.0
Compact posterior storage
-
monitoris now capability-based and defaults to"summary", which retains estimates and convergence diagnostics without retaining posterior draws. - Draws requested through
"parameters","random_effects","weights","fitted","predictive", or"log_lik"are stored once in a compactposterior::draws_array."full"selects every family-supported capability. -
monitor = "jags"is the explicit escape hatch for retaining the complete raw R2jags object. Logicalmonitorvalues are no longer accepted. - Summary-only fits retain rank-normalized R-hat, bulk/tail ESS, and Monte Carlo standard errors for every reported parameter.
summary(),tidy(),glance(), andbmlCompare()expose compact convergence information. - Post-estimation methods now report the exact posterior capability needed when a fit did not retain the required draws.
bml 0.10.0
This release redesigns the model-specification grammar around the micro-macro framework (weights, aggregation functions, effects) and aligns the package API with brms. All breaking changes are clean breaks — old spellings error with a migration message; there are no deprecation shims.
The aggregation function fn() (emergent terms)
- Every
mm()block now selects an aggregation functionfn()— thefin the framework’stheta^{micro,f}(M_it).fn("sum")(default) is the additive weighted mean; new emergent types read properties of the whole member set:-
fn("var", moment = p)— weighted central moments (distributional shape) -
fn("hhi"),fn("effn"),fn("entropy")— concentration of the weights -
fn("threshold", c =, kappa =)— smooth critical-mass terms; the cutpoint can be estimated:c = est() -
fn("smax", kappa =)— smooth max/min;kappa = est()makes the aggregation function itself the estimand (min <-> mean <-> max) -
fn("gmean", p = est())— power/CES mean (substitutes vs complements) -
fn("cov")— weighted covariance of two member attributes
-
-
Expression DSL:
fn(~ E((x - E(x))^2))— user-written reductions with the expectation operatorE()over the normalized member weights; whitelisted operations transpile to JAGS. Free symbols become estimable shape parameters. - Blocks stack: combine mean + variance + concentration features in one model.
Weights: fn() -> w()
- Weights moved to their own component:
w(~ 1/n),w(~ importance, scale = TRUE),w(~ ilogit(b0 + b1*q)). The oldfn(w ~ ...)spelling and thec =normalization flag were removed (c->scale, matching brms). -
One parameter rule: any symbol in a
w()orfn()formula that is not a data column is a free parameter; the build messagesEstimating parameters: ...so typos surface immediately.
Named blocks and interactions
- Every block has a (auto-generated or user-set)
name =; single-feature blocks can be referenced in main-formula interactions: cross-level (Ax:education) and block x block (Ax:Vx). Bare references error.
Effects grammar: re() / fe()
-
RE = re(1 + x)— random intercepts + slopes (lme4-style LHS; grouping implied byid()). Defaultcor = FALSE(independent; a deliberate divergence from brms’s correlated default);cor = TRUEopts into the correlated 2x2 with an LKJ-style prior. -
FE = fe(1 + x, showFE =)— the no-pooling peer (unit dummies + unit-specific slopes; first unit is the reference). -
hm()carries structure only:type =andvars =were removed. Fixed effects of unit-level covariates go in the main formula (Y ~ gdp + hm(id(cid), RE = re(1 + gdp))), retiringb.hm.
brms-aligned call syntax
-
bml(formula, data, family = gaussian(), prior = ...)—datais second. -
Family functions:
gaussian(),bernoulli(),weibull(),cox(intervals =)(absorbingcox_intervals); strings still accepted. -
MCMC arguments renamed:
iter,warmup,thin,chains,cores(replacingn.iter,n.burnin,n.thin,n.chains,parallel). -
prior()system: class-targeted priors on the natural (SD) scale, translated to JAGS automatically —prior(normal(0, 5), class = "b"), classesIntercept,b,sd,cor,sigma,shape,w,fn;get_prior()lists every settable prior. Raw JAGS strings remain an escape hatch. The oldpriors =list argument was removed.
Parameter naming
- One user-facing coefficient family
b, labeled by term ((Intercept),majority,A_x,A_x:education); internal arraysb,b.fn.<k>retireb.mm/b.hm. Weight parameters areb.w.<k>(class"w");fnshape parameters arefn.<name>.<k>(class"fn") and are reported in their own “Function parameters” section, never in the coefficient table. RE variances print assd(Intercept),sd(x),cor(Intercept,x).
Post-estimation: the brms / posterior vocabulary
-
as_draws()/as_draws_df()/as_draws_matrix()/as_draws_array() -
fixef(),ranef(),coef() -
fitted(),predict(),posterior_predict()(newdata not yet supported) -
log_lik(),loo(),waic()(gaussian/bernoulli; pointwiselog_likis monitored in the generated JAGS model) -
pp_check()(via bayesplot, Suggests),conditional_effects() -
make_jagscode()/make_jagsdata()(analogues ofmake_stancode())
Autoregressive random effects: ar moved into re()
- The random-walk option is part of the effects grammar now — it is a property of the random effect, not of the block.
mm(ar = TRUE)/hm(ar = TRUE)were removed (migration error): writeRE = re(1, ar = TRUE). -
New: time-indexed walks.
RE = re(1, ar = year)orders each member’s participations by a numeric time variable and scales the step variance by the normalized time gaps,u_t ~ N(u_{t-1}, sigma^2 * gap_t / mean(gap))— long gaps allow more drift than short ones; equal spacing reproduces thear = TRUEmodel. Requires an intercept-onlyre(1); time values must be unique within each member/unit.
Temporal helpers
-
bml_delta(),bml_realloc(),bml_turnover()— aggregation over membership spells: the matched (shift-share style) decomposition of a composition change into reallocation + attribute change + entry/exit, with the adding-up identity holding exactly. Observed-panel case; the in-model spell path (estimated weights, reallocation toward high-RE members) is future work.
Vignettes
- The examples vignette gained four new examples on the coalition data: mean vs. spread (
fn("var"), stacked feature blocks), estimating the aggregation function itself (fn("smax", kappa = est())), cross-level interactions via named blocks (Afin:majority), and heterogeneous member effects (vars(x + x:z)+re(1 + x)in one block). The friendship example now adjudicates linear-in-means vs. best-friend withloo_compare()and showspp_check(); the Boston example usesprior(..., class = "w").
Bug fixes
- Canonical member-row ordering. All member-level structures (design matrices, weights, slope data, and the member-id index vectors) now share one ordering, sorted by (group, member). Previously the member-id index used a different ordering than the design/weight matrices, so models with member random effects could associate rows with the wrong member’s effect when the input data were not already sorted by member within group. Results of affected models change (even with the same seed); the refit examples vignette reflects this — most notably, the friendship example’s model comparison now favors linear-in-means over the best-friend specification.
Removed (clean breaks)
-
fn(w ~ ...)weights spelling;c =normalization flag -
hm(type =),hm(vars =),hm(name =)(nowlabels =),hm(showFE =) -
mm(ar =)/hm(ar =)— moved intore(1, ar = ...) -
bml(n.iter =, n.burnin =, n.thin =, n.chains =, parallel =, priors =, cox_intervals =);dataas last argument -
family = "Gaussian"-style capitalized strings still resolve, but the canonical spellings are lowercase / family functions
bml 0.9.0
CRAN release: 2026-02-20
Major Changes
Package renamed from
rmmtobml(Bayesian Multiple-Membership Multilevel Models)-
New syntax for weight functions: The
arparameter has been moved from thefn()specification to themm()block level for clearer API- Old:
fn(w ~ 1/n, c = TRUE, ar = FALSE) - New:
fn(w ~ 1/n, c = TRUE)withar = FALSEat themm()level
- Old:
Support for multiple mmid groups: The package now supports models with multiple membership identifiers, allowing more complex membership structures
-
Enhanced documentation: Comprehensive documentation added for the
coalgovdataset including:- Detailed variable descriptions organized by level (identifiers, government-level, country-level, party-level)
- Statistical summaries for all variables
- Clear explanation of multiple-membership structure
- Updated references and examples
New Features
Flexible weight function parameterization: Enhanced support for parameterizing weight functions with covariates and group-specific structures
Per-group random effects: Random effects can now be specified separately for different mmid groups
Improved JAGS code generation: Optimized model string generation for better performance with complex multiple-membership structures
Breaking Changes
arparameter moved: Existing code usingfn(w ~ ..., ar = TRUE)must be updated to placearin themm()block instead-
Dataset changes:
- Removed
schoolnetsdataset (includingnodedatandedgedatobjects) - Updated
coalgovdataset with enhanced documentation and additional variables
- Removed
Bug Fixes
Fixed issues with weight function constraints when using multiple
mm()blocksImproved handling of group-level indices in JAGS variable creation