INLAvaan now fits two-level models with random slopes, which
lavaan specifies with the rv() modifier. They work with
compare(), loo(), waic(),
logLik(), predict(), simulate()
and sampling(), and summary() marks the rows
that carry a random slope. fitted(),
residuals() and the standardised estimates use the implied
moments averaged over the covariates, and
per_cluster = TRUE gives those of each cluster. The
Bayesian fit indices are scaled against an unrestricted
random-coefficient model. The posterior predictive p-value is not
available and is dropped from test. A random slope on a
latent or split covariate uses lavaan’s quadrature, which is slower, and
integration.ngh sets its number of nodes. Such fits have no
simulate(), casewise values,
predict(type = "yhat"), per_cluster = TRUE or
Bayesian fit indices. Composites cannot be combined with random slopes
yet. See the multilevel article.
INLAvaan now fits composites, which lavaan specifies with the
<~ operator, with continuous data, in one or more groups
or at either level of a two-level model. They work with the fit
measures, loo(), compare(),
predict() and sampling(), and
summary() gives posterior summaries for the variance and
intercept of each composite. Free weights have the new default prior
wmat = "normal(0,10)" in priors_for(). Unlike
lavaan, INLAvaan estimates the (co)variances of the indicators by
default (composites.cov = "free");
composites.cov = "fixed" fixes them at their sample values.
See the new article on composites.
Composite models that INLAvaan cannot fit yet stop with an error:
ordinal data, and composites.cov = "fixed" in two-level
models. So do composite specifications that cannot be estimated as
written, such as a free latent mean that only composites measure (as in
agrowth() on composites).
The posterior predictive p-value of two-level models was often
close to 0 for models that fit. It now follows blavaan’s two-level PPP:
each posterior draw generates replicate data, which are scored against
the saturated model. The new arguments ppp_method and
ppp_nsamp set how the data are scored (by default one
Fisher-scoring step towards the saturated fit) and how many draws are
used (250). The two-level PPP is experimental.
simulate() on a two-level fit now keeps the clusters
of the data, with their labels and sizes, and no longer warns on every
data set.
BCFI was -Inf or NaN for
posterior draws whose independence model fits within its degrees of
freedom. Such draws now follow lavaan’s CFI convention.
standardisedsolution() ignored its
nsamp argument and summarised as many posterior draws as
the fit itself had used.
Multigroup models with missing = "ml" gave wrong
posteriors.
loo() and waic() gave wrong values for
complete-data two-level models in which the two levels list the
variables in different orders, for example when a regression appears at
one level only.
compare() failed when a model was given as a long
call.
sampling(prior = TRUE) with
type = "observed" or type = "all" rejected
every draw in models with an observed outcome or covariate.
The marginal log-likelihood was too low by about half the number
of free parameters when vb_correction = TRUE (the default).
As a result, Bayes factors from compare() favoured models
with fewer parameters.
A defined parameter (:=) can now use other defined
parameters, in any order, as in lavaan. Previously the fit failed with
an “object not found” error.
Defined parameters that are constant, or that cannot be computed
for some posterior draws (such as log(b) when
b can be negative), no longer stop the fit. Their summaries
use the draws where they can be computed, and a single warning gives the
share left out, in place of R’s repeated “NaNs produced”
warnings.
summary() showed the wrong SD, credible interval and
prior for defined parameters, and for some rows after them, when the
model had equality constraints (shared labels or
group.equal).
Equality constraints written as a == b or
a == <number> are now honoured. Previously they were
ignored, along with shared labels and group.equal in the
same model. Constraints INLAvaan cannot fit (such as
a == 2*b, a > 0,
effect.coding, or a loading held equal to a variance) now
give an error.
Covariances held equal (by shared labels or
group.equal) now fit correctly. Previously their posterior
was biased, or the fit failed.
sampling() and simulate() now work for
models with equality constraints, and simulate() for
multigroup models.
Parameter names now match coef(). Previously
plot(fit, params = ) could not find multigroup
:= parameters, and labels such as lag1 were
mangled. A := parameter that reuses a parameter label, and
multigroup two-level models, now give an error.
Data with ordered-factor columns are now fitted as ordinal data,
as with ordered =. Previously the fit failed.
ordered = naming variables outside the model no longer
widens the posterior, and a name that matches no model variable gives an
error.
predict(newdata = ) for ordinal models now codes the
categories as in the fit. Previously factor columns gave an error and
0/1 codes gave wrong factor scores.
Chained modifiers in the model syntax are now all kept.
Previously only one survived, so prior("normal(0,1)")*a*x2
lost its prior, 0.5*a*x2 was left free and
NA*a*x1 stayed fixed.
A prior() on any of several parameters held equal is
now used. Previously only a prior on the first of them counted.
Models where only one variance enters a covariance (for example, with the other variance fixed) no longer fail.
predict() now works for models with observed
covariates or observed outcomes, including with
conditional.x = TRUE. Factor scores and fitted values for
models with latent regressions now match lavaan.
Two-level models with named levels (level: within /
level: between) now fit, and their parameter names follow
coef().
vcov() now matches coef() in length,
order and names for models with equality constraints, and
lavInspect(fit, "vcov") works on such fits.
predict() no longer fails when a variance is zero,
for example a residual variance fixed to zero, or a latent variable
fully determined by such an indicator.
loo() and waic() no longer fail on
two-level models fitted with missing = "ML" that have
between-level variables, such as cluster-level covariates.
loo() and waic() now score two-level
fixed.x = TRUE models correctly when they mix covariates at
both levels with within-only ones.
fit_skew_normal_samp(), and with it the summaries of
covariances and defined parameters (:=), missed the skew of
draws with a small SD (below about 0.2). Posterior modes are also more
accurate for parameters on a small scale.
loo() and waic() on FIML fits with
fixed.x covariates now score the outcomes given the
covariates, as for complete data. Previously two-level FIML scores
included the covariates’ density, so they could not be compared with
listwise fits or across covariate sets.
loo() and waic() no longer fail on FIML
fits with singleton or two-row clusters, or with clusters or rows that
have no observed data. Units with nothing to predict are left out, with
a message. In two-level type = "loso", rows with no
within-level data no longer each count the whole cluster.
predict(type = "ypred") for two-level models now
adds residual noise to observed outcomes, not only to factor
indicators.
predict() for two-level models now draws latent
variables and missing values from their posterior given all of the
cluster’s data, as for single-level models. Previously it returned
factor scores without their uncertainty, and imputed each row without
the rest of its cluster.
With fixed.x = TRUE (the default), the marginal
likelihood and DIC now condition on the covariates. Previously a
covariate with no effect changed Bayes factors from
compare(), by a lot in two-level models where it varies at
both levels.
fitMeasures() overstated the chi-square of models
without a mean structure, and with it BRMSEA and the other Bayesian fit
indices.
The PPP no longer fails for conditional.x = TRUE
fits.
The PPP no longer overstates the fit of models with fixed
covariates (fixed.x = TRUE, the default). Its replicates
varied the covariates, which the model does not predict.
simulate() and predict(type = "ymis")
now work for conditional.x = TRUE fits. Previously
simulate() rejected every draw and imputation
failed.
sampling() for conditional.x = TRUE
fits now draws the covariates too, as for other fits. Previously it left
out their effects.
Models with a free covariance above 1 at its starting value, such
as covariances between covariates under fixed.x = FALSE, no
longer fail.
Models without a mean structure fit much faster. The gradient of
their marginalised log-likelihood is now lavaan’s ML gradient for
n - 1 observations and the unbiased sample covariance,
where INLAvaan used to rebuild lavaan’s Jacobian of the implied
covariance at every call. The posterior is unchanged, and a
256-parameter factor model fits about ten times faster. Such a model
with sampling weights now gives an error when a group’s weights sum to 1
or less, where the marginalised likelihood is unbounded.
likelihood = "wishart", also the default under
mimic = "EQS" or "LISREL", is now ignored with
a message. lavaan then divides the sample covariance by
n - 1, but INLAvaan’s likelihood assumes the divisor
n, so such fits overstated the sample covariance by
n / (n - 1). Without a mean structure, integrating out the
saturated means already gives the Wishart likelihood.
Parameter types missing from dp take their default
priors, and a dp without names gives an error.
A Hessian that is not positive definite at the posterior mode gives a clear error.
plot() explains that fixed and derived parameters
have no posterior density.
compare() warns when fits differ in
vb_correction.
predict(newdata = ) warns about ordinal values not
seen when fitting.
fitted(), residuals(), the standardised
estimates and the R-square of random-slope fits also work with lavaan
versions after 0.7-3.
Sigma argument of loo() is now
Omega. The new name agrees with the notation for the
posterior covariance in the documentation. You can still use
Sigma, but you get a deprecation warning. If you supply the
two names, you get an error.Single-level fits with missing = "ml" gave incorrect
posterior summaries with the default skew-normal marginals. The loadings
and variances were far from the FIML estimates, and the scan-endpoint
check flagged almost all parameters. The cause was a shortcut that
treats the free intercepts as separate from the covariance parameters.
This is correct for complete data, but not under FIML. INLAvaan no
longer uses the shortcut under FIML. These results were already
correct:
marginal_method = "marggaus", or with
marginal_correction = "hessian" or
"none".The posterior predictive p-value (PPP) was incorrect for data with missing values. For example, a correct model with 20% missing cells got a PPP of 0.000. The cause was that the PPP used a sample covariance that is not correct when values are missing. The PPP now uses the saturated (h1) covariance from lavaan:
Single-level fits with complete data do not change. Two-level fits with complete data get slightly different PPP values.
The Bayesian fit indices had two errors:
BRMSEA was 0, BGammaHat was 1 and
BTLI was more than 1. Two-level fits with
missing = "ml" gave NA.BRMSEA and adjBGammaHat used slightly
incorrect degrees of freedom for models with an observed predictor and
fixed.x = TRUE.INLAvaan now gets the two values from lavaan. Single-level fits without covariates do not change.
The incremental fit indices BCFI, BTLI
and BNFI used an incorrect baseline. compare()
used its first model as the baseline, without a warning. Thus the first
model got a score against itself, which is always zero, and the other
models got a score against the first model, not against a null model.
fitMeasures(), bfit_indices() and
compare() now fit the independence model automatically, as
lavaan does. In this model, each observed variable has its variance and
intercept, and no variables correlate. The fit uses the same data and
options as the model, and takes less than one second, also for 64 items.
compare() fits it one time for all models. Other
changes:
baseline.model to supply a different baseline, or
baseline.model = FALSE to skip the incremental
indices.baseline.model has the same free parameters as the
model, you get a warning.BTLI is now NA, not -Inf,
when the ratio of the baseline is 1.The absolute indices (BRMSEA, BGammaHat,
adjBGammaHat, BMc) do not change.
For two-level models, sampling() gave only the
within-level part. All three types now draw from the full two-level
model:
type = "latent" now includes the between-level latent
variables.type = "observed" now includes the between-level part
and the between-only variables.type = "implied" now gives the within-level and
between-level covariances, not one covariance.With marginal_method = "marggaus", the
Mean and SD were incorrect for parameters on a
transformed scale, such as variances and correlations. The
Mean was the posterior median, and the SD came
from the delta method. INLAvaan now calculates the two values as the
moments of the transformed Gaussian marginal, with Gauss-Hermite
quadrature. The quantiles, modes and densities do not change.
predict() did not draw its parameter sample in the
same way as the rest of the package. It did not use the NORTA
correlation adjustment, and it ignored the samp_copula
setting of the fit. Thus the factor scores and predicted values had a
different dependence structure from the posterior draws of the fit.
predict() now uses the stored correlation matrix and the
samp_copula setting of the fit.
timing() gave a total that was too large. It added
the lavaan setup time two times, because this time is already part of
init. The segments now do not overlap, and their sum agrees
with system.time(). timing() no longer shows
the start_time stamp as a duration. The documentation now
includes the loo and waic segments.
loo() removed the units without a second-order term
from elpd_loo, p_loo and their standard
errors. Thus the sum had fewer units, and the model looked better than
it is. loo() now keeps all the units:
k_max >= 1), loo() gives all estimates at
first order, for all units. Thus each estimate uses one order only.
loo() and fitmeasures() give a warning that
names these units.lpd term of a unit does not exist at second
order, the unit adds its first-order difference to p_loo.
elpd_loo and looic do not change. This case is
usual in SEM fits and the error is small, thus there is no warning. The
printed result shows a note, and n_lpd_ok gives the
count.This changes elpd_loo and looic for fits
with a unit at k_max >= 1. Without such a unit, the
other data and the prior cannot identify some combination of the
parameters. Thus, examine such units.
compare() calculated se_diff from
per-unit values that did not agree with elpd_diff. The
paired variance did not include the units without a second-order term,
but the ELPD totals included them. The two values now use the same
per-unit values.
compare(loo = TRUE) now uses the same Taylor order
for all models. This is the highest order that all the models can
supply. Before, it could compare a model at second order with a model at
first order. Thus part of elpd_diff came from the change of
order, not from the models. The table shows the order.
test = "loo" stored the LOO but not the WAIC, but
the documentation said that it stores the two. A request for the LOO or
the WAIC now stores the two (see the test entry under New
features).
New vb_method argument to inlavaan(),
acfa(), asem() and agrowth() sets
the integration rule for the VB mean correction:
"sobol" (the default) uses the scrambled Sobol rule
with n_qmc nodes, as before."gauss_hermite" uses a deterministic three-point
Gauss-Hermite rule along each principal axis of the Laplace covariance.
This is 2m + 1 nodes for m free
parameters.The Gauss-Hermite rule gives the same shift on each run. On the
benchmark models, it was more accurate than the default 64-node rule. It
is faster than the default for fewer than approximately 30 free
parameters, and slower for more. It gives no quadrature error, thus
vb_mcse_sigma is NA. This feature is
experimental.
diagnostics() has new values:
vb_shift_max, the largest VB mean correction in
posterior-SD units, and scan_end_mass_max, the largest
scan_end_mass.scan_end_mass (see the next entry),
and alpha, the shape of the fitted skew-normal.New diagnostic scan_end_mass. It is the probability
that the fitted skew-normal marginal puts outside the scan window, which
is four posterior SDs on each side of the mode. INLAvaan fits the
marginal only inside this window, thus the mass outside it is an
extrapolation. A large value shows that the credible limits of the
parameter are not reliable.
NA unless
marginal_method = "skewnorm".New samp_norta argument to inlavaan(),
acfa(), asem() and agrowth()
enables or disables the NORTA correlation adjustment of the skew-normal
copula. It is independent of samp_copula. The default is
FALSE, because the adjustment does not change the marginals
and has a very small effect: on the benchmark models, it changed no
correlation by more than 0.01. predict() uses the same
setting as the fit.
The test argument of inlavaan(),
acfa(), asem() and agrowth() now
gives the set of post-estimation values to calculate:
"ppp", "dic",
"loo" and "waic"."standard" (or "default") is
c("ppp", "dic"). "full" is all four.
"none" is nothing.test = c("standard", "loo")."satorra.bentler". Before, lavaan
ignored these values without a warning.You can now request the PPP and the DIC separately. For example,
test = "dic" gives the DIC without the PPP.
The default no longer calculates the LOO and the
WAIC. Before, the default calculated the LOO if its predicted
time was less than 10 seconds. Thus the fit time was difficult to
predict. INLAvaan now calculates the LOO and the WAIC only on request
("loo", "waic" or "full"). If the
model does not support them (PML or ordinal data,
conditional.x = TRUE, multigroup two-level models), you get
a warning and the fit continues without them.
To get elpd_loo in fitmeasures(fit) as
before, use test = "full" or add_loo(fit).
add_loo() now stores the LOO and the WAIC (before, it
stored only the LOO). loo(fit) and waic(fit)
still calculate on request.
cores > 1 now works in all front ends. Before,
the parallel stages of inlavaan() and loo()
used mclapply() to fork worker processes. Forks are not
available on Windows. They are also not safe in threaded IDE sessions,
such as RStudio and Positron, where the child processes can stop without
a message. INLAvaan now uses a PSOCK cluster (separate R processes) when
forks are not safe.
New cov_as_cor argument to inlavaan().
INLAvaan always estimates the residual and latent covariances
(theta_cov, psi_cov) on the correlation scale.
By default, it then reports them on the covariance scale from a
posterior sample, as lavaan and blavaan do. With
cov_as_cor = TRUE, INLAvaan reports the correlation-scale
marginals directly, as theta_cor and psi_cor.
Use this option to compare the marginals with a reference on the
correlation scale. The estimation does not change.
waic() is now deterministic. It calculates the two
WAIC terms in closed form from the Laplace summary, with the same
per-unit Taylor values as loo(). It no longer uses
posterior draws. This changes the estimand, thus
p_waic and waic change for all existing fits.
The change is largest at small N. Other changes:
nsamp argument is removed. If you supply it, you
get a warning.second_order argument works as in
loo(). At first order, the WAIC and the LOO are
identical.p_waic > 0.4 rule is removed. It was an
empirical threshold for the variation of the old estimator, with no
theory to support it.lpd term
exists for all units (k_min > -1). If not,
waic() gives all estimates at first order, with a
warning.per_unit table of loo() has two new
columns: k_ssq, which is part of the WAIC penalty, and
k_min, the existence check for the lpd
term.loo() now gives two curvature diagnostics for each
unit, k_max and k_sum. It calculates them in
closed form from the Laplace summary, not from posterior draws.
k_max is the fraction of the posterior precision that the
unit has along its worst direction. The second-order term of the unit
exists only if k_max < 1. k_sum is the
total leverage of the unit. loo() applies no threshold to
these values.
loo() no longer uses the name “effective number of
parameters” for two different values:
p_loo has the same definition as in the
loo package. It stays in the estimates
table.k_sum is now
pd_trace, the trace form of the DIC pD.The printed result also shows a new curvature check. It compares the
total gap between the first-order and second-order estimates
(elpd_gap) with half of pD. A large excess
shows that the second-order expansion is not stable. The documentation
now also tells you to use second_order = FALSE only for
diagnostics or to decrease cost. A first-order score is too high by
approximately half of pD, thus it cannot compare models of
different sizes.
loo() and waic() results now print
under a cli rule that shows the number of units, the number
of groups and the Taylor order. This rule replaces the “Computed from …”
line. The notes now fit the width of the console. summary()
on these results is the same as print().
INLAvaan now requires lavaan >= 0.7-2. Thus the compatibility layer for older lavaan versions is removed. The warning about the two-level FIML gradient for cases with all within-level values missing is also removed, because lavaan >= 0.7-1.2707 corrects this problem.
The default "nlminb" optimiser now uses
iter.max = 1000 and eval.max = 2000. The
nlminb() defaults (150 and 200) were too small for some
complex models. When the optimiser reached these limits, only
diagnostics() or a fit-time warning showed it. Values that
you supply in control still have priority.
timing() function did not return the correct total
time due to a breaking name change in lavaan.loo() computes leave-one-out cross-validation from a
single fit without refitting nor sampling, via a Taylor approximation of
the case-deletion posterior: per-subject (LOSO) for single-level models,
per-cluster (LOCO) for two-level models. Reports first- and second-order
estimates and pointwise contributions, with opt-in parallelism
(cores) and theta/Sigma overrides
for scoring conditioned posterior summaries in user-built model-search
workflows.waic() computes the widely applicable information
criterion from posterior draws, with pointwise contributions and
reliability warnings.fixed.x = FALSE) or conditionally on them
(fixed.x = TRUE, the lavaan default; exact, no additional
approximation), for any covariate placement, including cluster-level and
within-level covariates in two-level models. The two flavours are never
comparable, as conditional comparisons may differ in their covariate
sets, which enables covariate selection.loo() and waic() support multigroup
models. Groups are independent, so each unit is scored against its own
group’s implied moments, under either mean treatment and either
covariate flavour, with cross-group equality constraints
(group.equal) flowing through automatically. Units are
identified by case number and carry a group column, so
results keep their identity across fits that stack groups differently.
Multigroup two-level models are not supported yet.loo() and waic() support fits estimated by
full-information maximum likelihood (missing = "ml").
Single-level units are scored on the entries they actually have – the
observed-data predictive log p(y_i,obs | D_-i) – with
casewise kernels evaluated per missing pattern, so a unit with fewer
observed entries self-weights in the elpd. Two-level fits are scored per
cluster (LOCO), each cluster on its observed-data marginal likelihood
via lavaan’s raw-data cluster kernels (no per-cluster sufficient
statistics, since LOCO deletes whole clusters). This shares the
missing-at-random assumption of the FIML fit itself. Multigroup
two-level models remain unsupported under missingness.loo() and waic() gain
type = "loso", scoring the conditional predictive
(leave-one-unit-out: a new observation within an observed cluster)
instead of the default marginal predictive
(type = "loco", leave-one-cluster-out: a new cluster).
These are the two estimands of Merkle, Furr & Rabe-Hesketh (2019);
they answer different questions and are easily conflated, so the
marginal is the default and the conditional warns. loo()
uses the Taylor expansion and waic() the posterior draws,
computing the same estimand two ways; both work with and without missing
data. (waic() previously had no type.)fitmeasures() gains elpd_loo,
se_loo, p_loo, looic and
elpd_waic, se_waic, p_waic,
waic: included in "all" when stored with the
fit, computed on demand when requested by name.compare() gains loo = TRUE. Models sorted
by descending ELPD, with p_loo and ELPD differences with
paired standard errors (mixed-flavour comparisons are refused). Pairing
matches units by id rather than row order, so a pooled fit can be
compared against a multigroup fit of the same data, and the
measurement-invariance ladder (configural, metric, scalar) is compared
on a proper predictive scale.test = "standard" does so automatically for
supported models with a mean structure. The WAIC reuses the fit’s own
posterior draws (when nsamp >= 100), and the LOO runs
when its predicted serial cost is within a 10-second budget.
test = "loo" forces the LOO regardless of the budget,
test = "none" skips everything, and
fit <- add_loo(fit) stores it post hoc. Stored results
are reused by loo(), waic(),
fitmeasures(), and compare().fitted() (and fitted.values()) return the
model-implied moments of an INLAvaan fit, evaluated at the
posterior means, matching the lavaan and blavaan output structure.
type = "ov" gives casewise predicted values.predict() gains a summary argument;
summary = TRUE collapses the posterior draws and returns
point estimates directly, equivalent to
summary(predict(...)) in one call. Default
FALSE, so existing code is unaffected.residuals() (and resid()) return the
observed-minus-fitted moments of an INLAvaan fit, matching
the lavaan and blavaan output structure and supporting all lavaan
residual types (raw, cor,
cor.bentler, normalized,
standardized) plus type = "casewise".anova() on an INLAvaan fit now errors,
pointing to compare(). Unlike
fitted()/residuals()/predict(),
this is a deliberate departure from blavaan (which silently inherits
lavaan’s frequentist likelihood-ratio test): there is no direct Bayesian
analogue of that test, and compare() already provides the
appropriate tools (Bayes factors, DIC/pD, LOO/WAIC).logLik() returns the Laplace-approximated marginal
log-likelihood (log evidence) by default, printed with a note that it is
not comparable to a classical log-likelihood;
type = "plugin" instead returns the classical
log-likelihood at the posterior mean, with
df/nobs attributes so it supports
AIC()/BIC() at the point estimate.deviance() is new for INLAvaan fits
(lavaan has no deviance() at all). Follows the
BUGS/JAGS/Stan convention: type = "mean" (default) returns
the posterior mean deviance with pD/DIC
attached as attributes; type = "plugin" returns the
deviance at the posterior mean (matching
-2 * logLik(type = "plugin")). Both require
test != "none".AIC()/BIC() on an INLAvaan
fit now error, documented alongside logLik(). Both are
large-sample asymptotic approximations to quantities INLAvaan already
computes directly – AIC approximates predictive accuracy
(loo()/waic()), BIC approximates
-2 * log(marginal likelihood) (logLik()) – so reporting
them at the posterior mean would be a cruder proxy for numbers already
available. The point estimate remains available for reporting-convention
purposes via AIC(logLik(object, type = "plugin")) /
BIC(...).inlavaan() warns
once, at the end of the fit, if the optimiser did not converge, the
gradient at the reported mode is materially non-zero (Newton step >
0.1 posterior SD), a skew-normal marginal fits poorly (NMAD > 0.1),
the VB correction shifted a posterior mean by more than 1 posterior SD,
or the Hessian is near-singular – naming the offending parameters. A
healthy fit stays silent; suppress via the
inlavaan_diagnostics_warning condition class.
diagnostics() gains the scale-free
mode_shift_max (global) and mode_shift_sigma
(per-parameter) measures backing the gradient check. (#18)inlavaan() fit calls.standardisedsolution() and
summary(standardized = TRUE) no longer silently drop their
arguments under lavaan >= 0.7-1, which renamed several exported
arguments (e.g. cov.std to cov_std,
GLIST to glist). INLAvaan now resolves the
spelling the installed lavaan expects once per session at load, working
across lavaan versions.loo()/waic() scores are now
correct for clusters containing a case fully missing on the within-level
variables. lavaan retains such cases but its analytic gradient kernel
mishandles the zero-observed pattern; INLAvaan drops these rows before
the cluster kernels (exact for the marginal likelihood). Two-level FIML
fitting also inherits the upstream gradient issue (fixed in lavaan PR
#581), so inlavaan() warns when such cases are present on
lavaan versions before the fix.meanstructure = FALSE now use a
proper Bayesian likelihood. See “Mean structures” vignette for details,
including when model comparisons across the two mean treatments are
meaningful.
loo() and waic() score such fits on the
exact exchangeable case-deletion conditionals. The previous zero-mean
fallback and its warning are gone, and absolute ELPD values are
meaningful and comparable with meanstructure = TRUE
fits.meanstructure = FALSE for a two-level model
now warns and fits with meanstructure = TRUE (the mean
structure is required there).fixed.x = TRUE) flavour — the default
for SEM with exogenous covariates — is fully supported: the mean
marginalisation factorises blockwise, so each unit is scored by the
difference of two exchangeable conditionals, with the frozen-covariate
term entering as an exact constant.predict() now centres the conditioning data on the
model-implied means (or the saturated sample means when the model has no
mean structure) when drawing factor scores and predicted observed
variables. Previously the kernels conditioned on raw data, offsetting
every factor score by a constant that grows with the variable
means.sampling() and simulate() draws of
observed variables from models without a mean structure now include the
saturated (sample) means, so posterior predictive replicates live on the
data scale instead of being centred at zero.sampling() and simulate() no longer error
for models with a single latent variable, and their saturated-mean
recovery is now robust to missing data (replicate columns were
previously NA under
missing = "pairwise").coef() (and the merged parameter table, fitted values,
and implied moments) now reports covariance parameters on the covariance
scale. Previously these slots carried the posterior-mean
correlation, while summary() showed the correct
sample-based covariance; the discrepancy is visible whenever the
relevant standard deviations are far from 1.compare_mcmc() for
density normalisation, overlap, and KL divergence computations.compare_mcmc() and diagnostics() are now
robust to NA values in density and diagnostic
computations.dp argument of inlavaan() and friends
is now documented in terms of priors_for().bfit_indices() computes per-sample Bayesian fit index
vectors (BRMSEA, BCFI, BTLI, BNFI), with summary() and
print() methods. Summary statistics are also available via
fitmeasures().compare() compares two or more fitted models side by
side, reporting marginal log-likelihood, Bayes factors, and DIC, with
optional fit measures from fitmeasures().diagnostics() computes global and per-parameter
convergence and approximation-quality diagnostics for fitted
models.get_inlavaan_internal() is now exported and documented,
providing access to the internal list stored in a fitted
INLAvaan object.predict() generates predictions for observed data and
missing data imputation, respecting multilevel structure if
present.sampling() draws from the posterior (or prior) SEM
generative model, returning parameter vectors, latent variables, or
observed variables.simulate() generates complete replicate datasets from a
fitted model, useful for simulation-based calibration and posterior
predictive checks.timing() extracts wall-clock timings for individual
computation stages of a fitted model.solve() for covariance and log-determinant
calculations.samp_copula = TRUE),
ensuring posterior samples have correct skew-normal marginals and
correct Pearson correlations.{qrng} for larger sequences. QMC sample
size now scales with model dimension.acfa(), asem(), and agrowth()
gain a vb_correction argument.{ggplot2} is now optional; plots fall back to base R
graphics when it is not installed.inlavaan() gains an sn_fit_ngrid argument
to control the number of grid points per dimension when fitting
skew-normal marginals (default 21).inlavaan() now supports
sn_fit_sample = TRUE for defined parameters, fitting a
skew-normal approximation to their posterior marginals based on drawn
samples.plot() method gains improved visualisation
options.priors_for() now supports the [prec] scale
qualifier for variance parameters (theta,
psi), placing the prior on the precision scale with
automatic Jacobian adjustment.sampling() and simulate() gain a
silent argument to suppress informational messages.summary() now includes 25th and 75th percentile
columns.vcov() now returns the covariance matrix of the
lavaan-side parameters and supports a type argument for
choosing between sample and Laplace covariance.marginal_correction = "shortcut" no longer produces
incorrect volume corrections.qsnorm_fast() no longer incorrectly handles sign
symmetries.missing = "ML" to handle FIML for
missing data.rgeneric functionality of R-INLA to implement a
basic SEM framework.