| Title: | Fast (Generalized) Linear Mixed Models via the 'glmm' Rust Kernel |
|---|---|
| Description: | Fits linear models, generalized linear models, and (generalized) linear mixed models with a compiled Rust backend (the 'glmm' crate): REML linear mixed models, and Laplace or adaptive Gauss-Hermite quadrature for binomial and Poisson mixed models, using 'lme4'-style formulas. Estimates are validated against 'lme4' and 'MixedModels.jl'. Deliberately scoped to fast fitting - fixed effects, Wald standard errors, and variance components; anything the backend cannot compute honestly (random-effect predictions, likelihood-based criteria, profiling) is a documented error, never a silently different answer. |
| Authors: | Paweł Lenartowicz [aut, cre] (ORCID: <https://orcid.org/0000-0002-6906-7217>) |
| Maintainer: | Paweł Lenartowicz <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.1.0 |
| Built: | 2026-07-17 03:42:30 UTC |
| Source: | https://github.com/pawlenartowicz/GLMM |
Normal-approximation intervals off the full Wald covariance matrix
(vcov()). method = "profile" and method = "boot" are not
available: the engine has no profiling machinery, and bootstrap needs
refitting infrastructure not built yet.
## S3 method for class 'fastglmm' confint(object, parm, level = 0.95, method = "Wald", ...)## S3 method for class 'fastglmm' confint(object, parm, level = 0.95, method = "Wald", ...)
object |
a fastglmm fit. |
parm |
coefficients to include (names or indices; default all). |
level |
confidence level. |
method |
only |
... |
unused. |
A matrix with one row per coefficient.
One entry point for the whole glmm engine, dispatching on family the way
the kernel itself does: OLS (gaussian without random effects), GLM
(binomial/poisson/gamma/negative-binomial without random effects), REML LMM
(gaussian with random effects), and GLMM (Laplace or adaptive
Gauss-Hermite) - there is no lmer/glmer split.
fastglmm( formula, data, family = gaussian(), weights = NULL, subset = NULL, na.action = getOption("na.action"), nAGQ = 1L, start = NULL, wald.se = c("hessian", "rx"), dispersion = NULL, init.theta = NULL, ... )fastglmm( formula, data, family = gaussian(), weights = NULL, subset = NULL, na.action = getOption("na.action"), nAGQ = 1L, start = NULL, wald.se = c("hessian", "rx"), dispersion = NULL, init.theta = NULL, ... )
formula |
an lme4-style model formula (or a string), e.g.
|
data |
a |
family |
a family object, family function, or string: one of
|
weights |
optional per-row prior (case) weights - |
subset |
optional row filter, evaluated in |
na.action |
how to handle |
nAGQ |
adaptive Gauss-Hermite node count: an odd integer in
|
start |
optional warm start, lme4's name and shape:
|
wald.se |
Wald standard-error mode: |
dispersion |
Gamma dispersion directive: |
init.theta |
negative-binomial shape seed, named for
|
... |
intercepted, never silently swallowed: known lme4 arguments
( |
The formula is parsed by the same Rust parser the Python port uses, which
accepts bare column names only: no I(), poly(), log(x),
cbind(), offset(), no ., and no term removal (- 1 / 0 +). Compute
transformed columns first and pass them by name. Fixed and random effects
support +, :, *, A/B nesting, and (1 + x | g) random-effect terms
with a full correlation structure - (x || g) and intercept-free RE
terms are not fittable by the kernel and raise an error. Contrasts are
always treatment coding with the first factor level as base; to change
the base, relevel() the factor (a contrasts argument is deliberately
absent). Character columns are converted to factors with lexicographic
level order (as factor() does); a factor's declared level order is
honored.
Gamma() link trap: R's Gamma() family object defaults to
link = "inverse", and a family object is honored as given - R semantics
win. The string form family = "gamma" uses the glmm default
link = "log" instead. The two forms therefore fit different models;
choose deliberately.
nAGQ fallback (louder than lme4): nAGQ > 1 is honored on binomial
and Poisson mixed models with a single grouping factor and at most 3 random
effects per group. Any other shape warns and falls back to Laplace
(nAGQ = 1) instead of erroring the way lme4::glmer does - the fit you
get is a Laplace fit, and the warning is the only notice. This mirrors the
Python port so the two ports agree.
Anything the engine cannot do is an error naming the reason - never a
silently different model. That includes REML = FALSE (the LMM path is
REML-only by design), offset(), control=/verbose=,
family = inverse.gaussian() and link = "cloglog" (approved for GLMM
0.1.1, not yet in the kernel), and quasi-likelihood dispersion= on
binomial/poisson.
An object of class "fastglmm": fixed effects (fixef), Wald
covariance (vcov()), variance components on the SD/correlation scale
(VarCorr), converged and singular flags (isSingular), plus
print(), summary(), confint()
(Wald), nobs(), formula() (returns the formula
string), family(), and model.frame(). Engine-blocked accessors
(ranef, predict, fitted, residuals, coef, logLik/AIC,
terms) error with the reason.
set.seed(1) n_g <- 30L g <- factor(rep(seq_len(n_g), each = 10)) t <- rep(seq(0, 0.9, by = 0.1), n_g) d <- rbinom(n_g * 10L, 1L, 0.4) u <- rnorm(n_g, sd = 0.8)[as.integer(g)] y <- rbinom(n_g * 10L, 1L, plogis(-0.5 + 0.7 * t + 0.4 * d + u)) fit <- fastglmm(y ~ t + d + t:d + (1 | g), data.frame(y, t, d, g), family = binomial()) fixef(fit) VarCorr(fit)set.seed(1) n_g <- 30L g <- factor(rep(seq_len(n_g), each = 10)) t <- rep(seq(0, 0.9, by = 0.1), n_g) d <- rbinom(n_g * 10L, 1L, 0.4) u <- rnorm(n_g, sd = 0.8)[as.integer(g)] y <- rbinom(n_g * 10L, 1L, plogis(-0.5 + 0.7 * t + 0.4 * d + u)) fit <- fastglmm(y ~ t + d + t:d + (1 | g), data.frame(y, t, d, g), family = binomial()) fixef(fit) VarCorr(fit)
Generic + method. The generic is defined here (masking lme4::fixef when
both are attached is harmless - S3 dispatch finds the same method).
fixef(object, ...) ## S3 method for class 'fastglmm' fixef(object, ...)fixef(object, ...) ## S3 method for class 'fastglmm' fixef(object, ...)
object |
a fastglmm fit. |
... |
unused. |
Named numeric vector of fixed-effect estimates; aliased
(rank-deficient) coefficients are NA, mirroring lm/lme4.
formula() returns the formula string as given: the R side never
builds a terms object (the parser is Rust-side, R-port spec section 3), and
synthesizing one would disagree with how the model was actually built -
which is also why terms() errors.
## S3 method for class 'fastglmm' formula(x, ...) ## S3 method for class 'fastglmm' family(object, ...) ## S3 method for class 'fastglmm' model.frame(formula, ...)## S3 method for class 'fastglmm' formula(x, ...) ## S3 method for class 'fastglmm' family(object, ...) ## S3 method for class 'fastglmm' model.frame(formula, ...)
x, formula, object
|
a fastglmm fit. |
... |
unused. |
TRUE iff the fit converged onto the variance-component boundary - the
same condition lme4::isSingular reports (the kernel computes it; see
glmm::Fit::singular).
isSingular(x, ...) ## S3 method for class 'fastglmm' isSingular(x, ...)isSingular(x, ...) ## S3 method for class 'fastglmm' isSingular(x, ...)
x |
a fastglmm fit. |
... |
unused. |
Errors: conditional modes are computed inside the kernel but not surfaced
on glmm::Fit yet. The engine spec
2026-07-15-engine-loglik-diagnostics is what lifts this.
ranef(object, ...) ## S3 method for class 'fastglmm' ranef(object, ...)ranef(object, ...) ## S3 method for class 'fastglmm' ranef(object, ...)
object |
a fastglmm fit. |
... |
unused. |
The residual standard deviation for gaussian fits (sqrt of the
kernel's dispersion: RSS/(n-p) for a linear model, matching
summary.lm's sigma; the REML residual variance for a mixed model,
matching lme4::sigma), sqrt(phi) for Gamma, and 1 for
binomial/poisson/negative-binomial (the scale is fixed, as in lme4).
## S3 method for class 'fastglmm' sigma(object, ...)## S3 method for class 'fastglmm' sigma(object, ...)
object |
a fastglmm fit. |
... |
unused. |
The coefficient table carries Wald z statistics and normal-based p-values
for all families (the kernel surfaces no residual df, so there is no t).
There is deliberately no AIC BIC logLik deviance header line: the
engine does not surface a comparable log-likelihood yet, and printing a
fake one would be worse than omitting it.
## S3 method for class 'fastglmm' summary(object, ...)## S3 method for class 'fastglmm' summary(object, ...)
object |
a fastglmm fit. |
... |
unused. |
An object of class "summary.fastglmm" with a coefficients
matrix (Estimate, Std. Error, z value, Pr(>|z|)).
Returns one covariance matrix per grouping (declaration order), each with
"stddev" and "correlation" attributes, printed lme4-shaped. This is the
load-bearing accessor for parameter-recovery use: attr(vc$g, "stddev")
are the tau estimates and attr(vc$g, "correlation") the rho estimates,
straight from the kernel's varcorr (validated against lme4's VarCorr).
VarCorr(x, ...) ## S3 method for class 'fastglmm' VarCorr(x, ...)VarCorr(x, ...) ## S3 method for class 'fastglmm' VarCorr(x, ...)
x |
a fastglmm fit. |
... |
unused. |
For a gaussian mixed fit a Residual row is printed after the group
components, as in lme4, carrying sigma() (the REML residual standard
deviation the kernel reports as dispersion).
A list of class "VarCorr.fastglmm", one named element per
grouping.