Flexible Bayesian Mixed Models with ASReml and brms-Style Syntax
flexyBayes is a multi-backend Bayesian mixed-model framework with
cross-engine posterior triangulation exposed as a first-class
robustness diagnostic. The package routes one model specification to
greta (MCMC), INLA (integrated nested Laplace approximation), or brms
(Stan passthrough), and tells you — with evidence — when the engines
agree, when they differ, and why a backend was refused.
Development release (v0.8.3). All exports are at the experimental
lifecyclestage and the API may change within the 0.x series. Not on CRAN. The full multi-environment-trial model (genotype-by-environment interaction random effects together with a heteroscedastic per-environment residual) is not yet supported by any backend. Readsystem.file("KNOWN_ISSUES.md", package = "flexyBayes")before relying on results.
| If you have… | Use | Notes |
|---|---|---|
ASReml syntax (fixed / random / rcov) |
fb() / flexybayes() |
Variance-component / agricultural workflows. |
brms or lme4 syntax (y ~ x + (1 | g)) |
fb() / flexybayes() |
The grammar is detected from the call; syntax = forces it. |
| A pre-built greta model | fb() |
Pass the greta::model() object (or its fb_from_greta() representation) straight in. |
| To force one engine | fb_greta() / fb_inla() / fb_brms() |
Single-engine pins; a conflicting backend is refused. |
| Two fits you want to compare across engines | triangulate() |
Auto-resolves canonical parameter names. |
fb() is the short alias for flexybayes(); either name is the
universal entry that spans every backend.
| Verb | greta |
inla |
brms (Stan) |
auto |
|---|---|---|---|---|
fb() / flexybayes() |
✓ | ✓ | ✓ | ✓ (greta or INLA via lgm_gate()) |
fb_greta() |
✓ | – | – | – |
fb_inla() |
– | ✓ | – | – |
fb_brms() |
– | – | ✓ | – |
The universal entry reaches any backend: name one with backend =, or
let backend = "auto" choose. Each fb_<engine>() pin fits exactly one
engine and refuses a conflicting backend. backend = "auto" runs the
LGM feasibility gate and routes to INLA on acceptance, otherwise to
greta; it never routes to Stan, because brms's first-call Stan compile
(typically 30–60 s) is incompatible with the auto-fast-path promise.
Reach Stan explicitly with fb_brms() or fb(..., backend = "brms").
flexyBayes is standalone-functional with any one backend installed, and the planner (see Quick start) needs none at all. The backends differ in install burden and in what they offer.
| Backend | On CRAN? | Install burden | Inference | In flexyBayes |
|---|---|---|---|---|
| INLA | No (own repository) | Moderate — binary, no compiler | Approximate (nested Laplace) | Supported |
| brms (Stan) | Yes | Heavy — first-call Stan compile (~30–60 s) | MCMC (sampling error only) | Supported |
| greta | No (greta-dev R-universe) | Heavy — Python + TensorFlow stack | MCMC (sampling error only) | Supported |
| gretaR | No (opt-in) | Heavy — torch | MCMC (out-of-process NUTS) | Dormant, opt-in |
All exports are at the experimental lifecycle stage; see
API_STABILITY.md in the source repository for what that guarantees. The fastest way to explore the
package without any backend is the planner; for a worked fit with production
sampling settings and honest diagnostics, follow the getting started
vignette.
What each backend does, by model class, reflecting the 2026-06 empirical validation. "Supported" means the syntax emits and the model is tested; it does not promise that every fit converges at small budgets -- convergence is model-specific and is always reported, so treat a high R-hat badge as a diagnostic, not a result.
| Model class | greta | INLA | brms |
|---|---|---|---|
| Gaussian LMM, simple random intercepts | validated | validated | validated |
| GLMM (binomial / Poisson / NB), simple RE | validated | validated | validated |
| Random slopes, structured covariance, GBLUP, pedigree, separable spatial, splines | supported | supported | supported |
| Interaction / nested random effects (GxE) | does not converge | refused | refused |
Heteroscedastic / per-stratum residual (dsum) |
does not converge | refused | refused |
| Interaction fixed effects, binomial path | supported | known bug | supported |
Capability boundary (read before relying on flexyBayes for METs). The full multi-environment-trial model -- genotype-by-environment interaction random effects together with a heteroscedastic per-environment residual -- is not fittable by any flexyBayes backend in this release. ASReml and
lme4fit it; flexyBayes does not yet. This is the package's central open problem. Seeinst/KNOWN_ISSUES.md(system.file("KNOWN_ISSUES.md", package = "flexyBayes")) for the full status, the per-backend reasons, and the intended route to a fix. Until then, flexyBayes is an honest multi-backend orchestration and triangulation tool for the model classes above -- not yet a stand-alone Bayesian replacement for full ASReml-style MET analysis.
Breeder MET summaries. fb_met_summary() (overall performance, stability,
GxE BLUPs, factor loadings, environment genetic correlations) requires a
greta factor-analytic (fa(env, k):gen) fit — it is computed from the
identified realised effects, which fit on greta. On an INLA or brms fit it
refuses with a pointer to the right path; the scalable INLA MET route gives
variance components via summary() / fb_structured_cov().
# greta (R-native MCMC backend) — archived from CRAN, served via the
# greta-dev R-universe
install.packages("greta",
repos = c("https://greta-dev.r-universe.dev", getOption("repos")))
greta::install_greta_deps()
# INLA (approximate-inference backend) — not on CRAN
install.packages("INLA",
repos = c(getOption("repos"),
INLA = "https://inla.r-inla-download.org/R/stable"))
# brms (Stan passthrough) — on CRAN
install.packages("brms")
# flexyBayes itself (not yet on CRAN) -- install from the repository:
# install.packages("remotes")
remotes::install_github("AAGI-AUS/flexyBayes")flexyBayes degrades gracefully when an optional engine is missing:
each backend is detected at run time, and a model sent to an
unavailable engine is refused with a clear message naming what to
install rather than failing obscurely.
The planner needs no inference backend and is the fastest way to see what flexyBayes will do with a model: it builds the intermediate representation, chooses a backend, and reports the plan without fitting.
library(flexyBayes)
data(sleepstudy, package = "lme4")
# Inspect the routing decision and representation plan -- no backend needed
plan <- flexybayes(
fixed = Reaction ~ Days,
random = ~ Subject,
data = sleepstudy,
plan = TRUE
)
planTo fit, install at least one backend (see Backend support above). The following uses production sampling settings; the getting started vignette walks through the same fit with its convergence diagnostics.
fit <- flexybayes(
fixed = Reaction ~ Days,
random = ~ Subject,
data = sleepstudy,
n_samples = 2000, warmup = 5000, chains = 4
)
# Standard R output
summary(fit)
coef(fit)
confint(fit)
# emmeans + marginaleffects
emmeans::emmeans(fit, ~ Days, at = list(Days = c(0, 5)))
marginaleffects::avg_slopes(fit)Fit the same model on two backends and compare:
fit_g <- flexybayes(Reaction ~ Days, random = ~ Subject,
data = sleepstudy, backend = "greta")
fit_i <- flexybayes(Reaction ~ Days, random = ~ Subject,
data = sleepstudy, backend = "inla")
triangulate(fit_g, fit_i)
#> Common parameters: 3
#> # A tibble: 3 × 5
#> parameter mean_diff sd_ratio tail_drift wasserstein_1
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 (Intercept) -0.04 0.997 0.012 0.04
#> 2 Days 0.001 1.001 0.003 0.001
#> 3 sigma 0.12 1.004 0.020 0.12Add a third engine via brms / Stan:
fit_s <- fb_brms(Reaction ~ Days + (1 | Subject), data = sleepstudy)
triangulate(fit_g, fit_s)
triangulate(fit_i, fit_s)canonical_names() does the work of aligning backend-native parameter
names (greta's sigma_g, INLA's Precision for g on the precision
scale, brms's sd_g__Intercept) to a single canonical name with the
correct scale transform — no name_map argument needed in standard
cases.
| Accessor | Returns |
|---|---|
backend_decision(fit) |
The captured dispatch trace: backend, path, lgm_gate checks, reason. |
prior_summary(fit) |
The resolved prior — auto-default (weakly-informative bounded uniform on SD; half-Cauchy advised for small group counts), user-supplied fb_prior(), or legacy scalar bridge. |
canonical_names(fit) |
The backend-native ↔ canonical-name table with per-row scale transforms. |
review_code = TRUE on flexybayes() / fb_brms() |
Inspect-before-fit workflow; cat_code(rev) prints the generated backend code; proceed(rev) advances into the fit. Supported on the formula-entry verbs only. fb_greta() raises a documented refusal: the user's model is already greta-side, so there is no flexyBayes-generated code string to inspect (model-graph pretty-printer reserved for a future release). |
Every fit carries three (or four) top-level slots:
fit$glm # GLM-compatible shim — works with summary(), emmeans,
# marginaleffects, effectsize, broom
fit$greta # native greta output (when backend = "greta")
fit$inla # native INLA output (when backend = "inla")
fit$brms # live brmsfit (when backend = "brms")
fit$extras # BLUPs, variance components, convergence diagnostics,
# generated code, parsed IR, run time, captured call# Fixed effects
yield ~ env # fixed factor
yield ~ env + x_cov # factor + covariate
yield ~ 0 + env # means model (no intercept)
yield ~ env + I(x^2) # expression terms
# Random effects
random = ~ geno # simple iid
random = ~ block:rep # nested
random = ~ vm(geno, Gmat) # GBLUP (dense V; both backends)
random = ~ vm(geno, chol = L) # user-supplied Cholesky (greta only; v0.3.7+)
random = ~ vm(geno, precision = Q) # user-supplied sparse precision (INLA preferred; v0.3.7+)
random = ~ ped(animal, Amat) # pedigree (animal model)
random = ~ ped(animal, A_inv,
use_sparse_precision = TRUE) # sparse pedigree precision (v0.3.7+)
random = ~ at(env):geno # diagonal GxE
random = ~ us(env):id(geno) # unstructured GxE
random = ~ fa(env, 2):id(geno) # factor-analytic GxE
random = ~ ar1(row):id(col) # spatial AR1
random = ~ spl(x_cov) # P-spline
# Residual
rcov = ~ units # iid residuals (default)
rcov = ~ at(env):units # heterogeneous by environment
# Families
family = "gaussian" | "binomial" | "poisson" | "negative_binomial" |
"gamma" | "beta"Sixteen numbered vignettes ship with the package:
- Getting started
- ASReml-shaped formulas
- Foundational regression
- Hierarchical models
- Structured covariance
- Priors and regularisation
- METs and genomic selection
- Downstream analysis
- Spatio-temporal models
- Cross-engine triangulation
- LGM feasibility filter
- Backend internals
- LGM feasibility and memory
- Choosing an engine: the universal entry and the engine pins
- Architecture: the backend registry and how a new engine joins
- Big-data streaming
Heavy MCMC vignettes use a .Rmd.orig precompile pattern; the .Rmd
that ships in the package tarball is the pre-evaluated static output.
Browse them with browseVignettes("flexyBayes") after a full install —
R CMD build then R CMD INSTALL the tarball, or
devtools::install(build_vignettes = TRUE). A plain install_github() or
source-directory install does not build the vignettes into inst/doc.
Several reference vignettes (02, 04, 05, 07) fit at small sampling budgets and print diagnostics that do not meet the convergence thresholds; each opens with an "illustration of output shape, not inference" disclaimer. For a convergence-clean workflow see the getting started and cross-engine triangulation vignettes.
- Indexing, not model matrices: parameters are sized by number of
levels (
p), not observations (N), giving O(p) memory for random effects. - Non-centred parameterisation: all random effects use NCP for efficient MCMC sampling.
- Cholesky decomposition: known covariance matrices (G, A) are decomposed once.
- Triangulation as first-class output: the package is built around the claim that evidence of cross-engine agreement is the signature flexyBayes deliverable, not "one more Bayesian mixed- model frontend".
flexyBayes ships an extensive testthat suite (devtools::test()) covering
the paths most likely to hide errors: fixed-effect and factor models against
lm() / lme4::lmer() references, random intercepts and the asreml-route
random slopes, structured-covariance terms, streaming-aggregation equivalence
to the per-row fit, weights, offsets, missing-response handling, backend
routing and the structured refusal taxonomy, prior translation, and
cross-engine triangulate() agreement. The cross-engine triangulation and
per-family vignettes show clean reproducible checks; the structured-covariance
reference vignettes are explicitly marked non-inferential where a model does
not mix at vignette-scale budgets.
Continuous integration validates the INLA, brms, and engine-independent
surface (the ASReml / brms parsers, the intermediate representation,
lgm_gate(), the dispatch policy table, the refusal registry, the prior DSL,
and the triangulate() metrics). The greta integration path (Python +
TensorFlow) is not installed on the standard CI matrix, so a green CI run
does not include live greta fits; greta-dependent tests gate via
skip_if_no_greta() and are run locally / via a manual greta-integration check. Run the
full suite locally with devtools::test() after greta::install_greta_deps().
flexyBayes refuses what it cannot yet fit rather than fitting it silently. The current release does not cover the following; each is a roadmap deferral, and a request that needs one is met with a structured refusal naming the gap, not a quiet wrong answer.
- Full multi-environment-trial (MET) models: genotype-by-environment
interaction random effects (
gen:loc,gen:loc:yearf) and heteroscedastic per-environment residuals (dsum(~ units | env)) are not fittable by any trustworthy backend in this release -- INLA and brms refuse them, greta expresses but does not converge on them. This is the package's central open problem; seeinst/KNOWN_ISSUES.mdfor the full status and the intended route to a fix. Use ASReml orlme4for the full MET model today. - Spatial structure: only separable AR1 (
ar1(row):id(col)) is supported. Intrinsic CAR and BYM2 areal models are not implemented. You can express a custom spatial precision by passing your own matrix tovm(g, precision = Q), but there is no BYM2 helper. - Smooth terms: univariate penalised splines (
s(x),spl(x)) are supported. Multivariate and tensor-product smooths (te(),ti(),t2()) are refused and deferred to a later release. - Hidden-Markov, multi-state, and survival models: not supported. Survival / time-to-event families are refused at the family gate. A NIMBLE backend covering these is on the roadmap with no fixed release target.
- Missing data: flexyBayes does not impute. Greta and the brms
(Stan) passthrough require complete cases for the model variables;
INLA treats an
NAresponse as a prediction target. Resolve missingness (drop or impute) before fitting, or use the prediction path deliberately.
- R ≥ 4.1.0
- greta ≥ 0.4.0 (Python + TensorFlow + TensorFlow Probability)
- INLA (optional, via Additional_repositories)
- brms (optional, for the Stan passthrough)
Contributions are welcome. See CONTRIBUTING.md for the development workflow
(fork, usethis::pr_init(), devtools::check() must pass, and a NEWS.md
bullet for any user-facing change) and CODE_OF_CONDUCT.md (Contributor
Covenant). Please report bugs and request features at
https://github.com/AAGI-AUS/flexyBayes/issues. The package's architecture
decisions are indexed in DESIGN_DECISIONS.md.
If you use flexyBayes in your research, please cite:
@software{flexyBayes,
title = {flexyBayes: Flexible Bayesian Mixed Models with ASReml
and brms-Style Syntax},
author = {Moldovan, Max and Tanaka, Emi and Hui, Francis K.C. and
Forte Deltell, Anabel},
year = {2026},
url = {https://github.com/AAGI-AUS/flexyBayes}
}
GPL (≥ 3)