NEWS
psychonetrics 0.15.17
- BlumeCapel() now gives an error (instead of a warning) when fewer than
three response options are supplied. With only two responses the
quadratic 'delta' term is not identifiable (x^2 is constant across the
two states), so it is confounded with the thresholds; use Ising() for
binary data.
psychonetrics 0.15.16
- New function allequal(): constrains all free elements of a model
matrix to be equal within each group (a single shared parameter per
group), separately per group (not across groups; use groupequal() in
addition for that). Useful e.g. to model all thresholds (tau) or all
Blume-Capel quadratic potentials (delta) equal, or to fit an
equal-edge-weight network (omega). Optional 'row', 'col' and 'group'
arguments restrict which elements/groups are affected.
psychonetrics 0.15.15
- New model: BlumeCapel(). The Ising distribution has been generalized
to the 'Spin' distribution, which adds a per-node quadratic potential
delta:
P(X = x) propto exp( sum_i tau_i x_i - sum_i delta_i x_i^2
+ sum_{i<j} omega_ij x_i x_j ).
Two models are built on this distribution: the Ising model (delta
fixed to zero, exactly reproducing the previous Ising behavior) and
the new BlumeCapel model (delta freely estimated). BlumeCapel()
shares the Ising() engine, fit, gradient, expected Hessian and
standard errors. For the ternary -1/0/1 coding this is the usual
Blume-Capel model; any ordered response set is allowed (a warning is
raised when the coding departs from the conventional one).
- Ising() now warns when 'responses' is not -1/1 or 0/1, since the
behavior is then not exactly that of the classical Ising model.
- Internally, the model distribution previously labelled "Ising" is now
labelled "Spin"; both the Ising and BlumeCapel models use it. This is
transparent to users.
psychonetrics 0.15.14
- Ising() no longer requires the response options to be integers. Any
set of two or more distinct ordered response values is accepted in
'responses' (or detected from the data), integer or not.
psychonetrics 0.15.13
- The Ising model has been generalized to more than two response
options. Ising() now accepts any number of ordered integer response
options in 'responses' (e.g., c(-1,0,1) or seq(-5,5)), encoded
identically across all variables and detected automatically from the
data when not supplied. The parameterization is unchanged (one
threshold per variable in 'tau' and a pairwise network in 'omega');
only the partition function, expected values and (expected) Hessian
now sum over all length(responses)^nNode response patterns. With two
response options the results are identical to before. Only
non-integer responses raise an error.
- New argument 'maxStates' to Ising() (default 2^maxNodes) caps the
number of response patterns the exact ML estimator may enumerate,
since this cost grows as length(responses)^nNode. For binary data
the default reproduces the historical 'maxNodes' limit; raise it to
fit larger multi-state models when feasible.
psychonetrics 0.15.12
- The random intercept cross-lagged panel model (RI-CLPM) framework
has been finalized. ri_clpm() now accepts the same data input as
the dlvm1 family: wide-format data with a design matrix, or
long-format data via 'idvar' (and optionally 'beepvar'), with the
format auto-detected. A 'standardize' argument ("z", "z_per_wave"
or "quantile") was added. ri_clpm() models now also build an
independence baseline so that incremental fit indices (CFI/TLI)
are available.
- New function ri_clpm_search() runs a sequential stationarity
search: starting from the unconstrained model it adds, one at a
time, stationarity of intercepts, temporal effects, contemporaneous
relations and innovation variances, and finally the panelVAR model
(wave 1 endogenous), retaining each constraint only if it does not
worsen fit according to AIC, BIC, or a chi-square difference test.
- Fix: parequal() constrained parameters incorrectly (a misplaced
parenthesis passed the indices as unlist()'s 'recursive'
argument), which broke the temporal / contemporaneous / innovation
branches of ri_clpm_stationary().
psychonetrics 0.15.11
- New convenience setters update_baseline() and update_saturated()
overwrite the @baseline_saturated$baseline / $saturated slots of
a psychonetrics model. Together with 'baseline_saturated = FALSE'
on the model constructor they make it straightforward to attach
hand-built reference models before calling runmodel().
psychonetrics 0.15.10
- Fix: the internal saturated reference model no longer inherits
cross-group equality from the target. Previously the saturated
builders in 10 model files (varcov / ggm, lvm, ml_lvm, var1,
dlvm1, tsdlvm1, Ising, plus changedata() / bootstrap()) passed
'equal = equal' into the saturated, so a multi-group model fit
with e.g. 'equal = "omega"' produced a saturated whose covariance
structure was constrained equal across groups too. The saturated
is meant to be a fully-unrestricted per-group reference, so this
inflated chi-square pvalues and could produce nonsensical
negative df (the target was no longer nested in the saturated:
e.g. ggm(equal="omega") on 4 vars / 2 groups gave df = -4).
The recommended pipeline of imposing cross-group equality via
groupequal() after model construction was unaffected by this bug;
only the 'equal = ...' shortcut at construction time triggered it.
After the fix, both pipelines produce sensible chi-square tests
and incremental fit indices.
- Fix (issue #51, jwe4ec): baseline.df was not adjusted for the
case where the saturated reference model has fewer free
parameters than nobs. The target df already had this adjustment;
baseline.df now mirrors it. With the saturated-equal fix above
this correction is usually zero, but it now also handles the
case where the user supplies a custom non-fully-saturated
saturated via @baseline_saturated$saturated. Affects the chi-
square reference for the baseline test and any incremental fit
indices that depend on baseline.df (CFI, TLI, NFI, RFI, IFI,
RNI, NNFI).
psychonetrics 0.15.9
- Performance: the expected Fisher information for Ising models is
now computed entirely in C++ in a single 2^N state-enumeration pass
(psychonetrics_FisherInformation_cpp -> expected_hessian_Ising_full_cpp).
The previous path made an R callback to expected_hessian_Ising_group,
which itself called IsingSampler::IsingLikelihood (one full state
enumeration plus materialization of a 2^N x N states matrix) and
then expHessianCpp (which made three more passes through all
2^N states: expHcpp, expH2cpp, and the main moment-accumulation
loop). The new single-pass implementation also fills only the
canonical lower-tri shapes of the 3rd- and 4th-order moment
tensors that the Hessian assembly actually reads, eliminating the
O(2^N N^4) dense fill in favour of O(2^N N^4 / 8).
Per-call benchmarks (multi-group Ising):
N=10 G=2 : 11.8x
N=12 G=2 : 21.2x
N=14 G=2 : 28.2x
End-to-end partialprune() at N=15 G=2 (sample 800/group) drops
from ~700s (0.15.7) to ~43s. Output is bit-identical (max abs
diff in the @information slot: 6.7e-16, machine epsilon).
The R-level expected_hessian_Ising / expected_hessian_Ising_group
functions and the Rcpp::export expHessianCpp are kept as-is for
callers outside the cpp Fisher-information path.
psychonetrics 0.15.8
- partialprune() gains an 'identify' argument (default TRUE) that is
threaded through prune(), unionmodel(), intersectionmodel(),
groupequal(), and groupfree(). Setting identify = FALSE prevents
identify() from being called on the intermediate models, so user-
fixed parameters (e.g. fixpar(\"beta\", 1, 1, value = 1) in a multi-
group Ising) are not re-freed when omega equality constraints are
introduced. Default behavior (identify = TRUE) is unchanged.
psychonetrics 0.15.7
- Performance: partialprune() inner refits skip addMIs(). The
repeat-loop only consumes the BIC and the joint score-test
statistic mi_free_joint, so addMIs's three Fisher-information
builds (type = normal / free / equal) are wasted on every
iteration. mi_free_joint is repopulated directly via
.equalityScoreTestInner(joint_only = TRUE) on each candidate fit
so the next selection still has the data it needs.
addSEs and addInformation are kept enabled because the post-loop
prune() reads p-values (from addSEs) and @information (from
addInformation) on the final curMod; disabling them changed the
post-loop prune behavior on multi-group cases where the inner
loop iterated more than once.
For useMIs = "simple" we fall back to a full addMIs() call inside
the loop so the legacy per-parameter mi_free column is still
populated (the simple branch reads it).
Final partialprune() output is unchanged: the user-facing returned
model is produced by the regular post-loop runmodel() / prune()
chain, which uses default flags and populates MIs / SEs / @information.
psychonetrics 0.15.6
- Performance: addMIs() now skips the univariate score-test loop in
.equalityScoreTestInner() (added in 0.15.4), which was computed on
every runmodel() call but never consumed -- only the joint score-test
statistic populates the parameter table. The univariate output is
still produced on demand when equalityScoreTest() or MIs(type="free")
are called. partialprune() no longer pays this cost on every inner
refit. Speedup scales with the number of groups: ~1.2x for G=2,
~1.5x for G=3, ~2.5x for G=4 on a small Ising example. Final
partialprune() output is bit-identical to 0.15.5.
psychonetrics 0.15.5
- partialprune() gains a new argument release_model = c("pruned",
"saturated"), defaulting to "pruned". This changes the alternative
model used in the stepwise BIC comparison when deciding whether to
release an equality constraint: with "pruned" the just-released
parameter is immediately fixed to zero in any group where the
initial per-group prune (Step 1) had already removed it, before
refitting and comparing BIC. The equality-constrained model is
therefore tested against the asymmetric structure suggested by the
initial per-group prune rather than against a fully saturated
per-group alternative, which improves specificity at low sample
sizes. Set release_model = "saturated" to reproduce the 0.15.4
behavior.
psychonetrics 0.15.4
- New function equalityScoreTest() that computes the score (Lagrange
multiplier) test for cross-group equality constraints in a multi-group
model. Returns both per-group univariate score statistics and a joint
multivariate score statistic (one per equality-constrained parameter,
df = G - 1). With the default method = "jacobian" the test
reproduces lavaan::lavTestScore exactly (to numerical precision)
for both balanced and unbalanced multi-group samples. A faster
method = "schur" alternative is available for complex models; it
inverts only the smaller "currently free" block of the information
matrix and matches lavaan in balanced samples.
- The parameter table now carries three new columns: mi_free_joint,
pmi_free_joint, and df_free_joint, populated whenever modification
indices of type "free" are computed.
- MIs() now prints two tables for type = "free": the existing summed
per-group univariate statistics (header updated to "Top N summed
equality-free modification indices") and a new "Top N joint
equality-free modification indices" table based on the multivariate
score test.
- partialprune() gains a new argument useMIs = c("joint","simple"). The
default "joint" ranks candidate equality releases by the joint score
test (correctly accounting for cross-group covariance for G >= 3).
Set useMIs = "simple" to reproduce the psychonetrics <= 0.15.3
behavior of summing per-group univariate MIs. The two options are
equivalent for two-group models.
psychonetrics 0.15.3
- Fixed meta_ggm() crashing with "non-numeric argument to mathematical
function" when using Vestimation = "per_study". The SRMR computation
in addfit() assumed sigma was a numeric matrix, but for meta_varcov
models with per-study estimation it is a list of per-study matrices.
SRMR now gracefully returns NA for such models.
- Fixed stepup() breaking equality constraints set by groupequal() in
multi-group models (GitHub issue #50). stepup() previously relied on
the @equal slot to detect cross-group equality constraints, but
groupequal() never populated it, so stepup() always freed parameters
for a single group - silently breaking the constraint. stepup() now
infers equality constraints directly from the parameters table
(shared non-zero par indices), which is the canonical representation.
groupequal() and groupfree() also now maintain the @equal slot for
backwards compatibility with model reconstruction code.
- stepup(greedy = TRUE) now falls back to the non-greedy path when
any searched matrix has cross-group equality constraints. The greedy
branch previously bypassed freepar()/groupequal() and assigned unique
par indices to each added parameter, which would silently break
equality constraints. Single-group models and multi-group models
without equality constraints take the fast greedy path as before.
psychonetrics 0.15.2
- Fixed incorrect (negative) chi-square and fit indices for FIML
estimation with high-dimensional panel data. By default, the saturated
model is still estimated numerically (using nlminb for robustness), but
if the optimizer fails (saturated LL < model LL), the saturated
log-likelihood is now automatically computed analytically from
pattern-specific sample statistics, following the approach used by
lavaan and Mplus.
- Added 'saturated' argument to runmodel() with options "default"
(model-based with automatic analytical fallback), "analytic" (skip
optimization, use analytical formula directly -- recommended for
high-dimensional panel data), and "model" (force numerical optimization
with no fallback).
psychonetrics 0.15.1
- panelgvar() and panelvar() now accept both wide-format (design matrix)
and long-format (character vector of variable names) data, matching the
flexibility of the underlying dlvm1(). Previously these convenience
wrappers only accepted wide-format data. For long-format usage, pass
vars as a character vector and provide idvar (and optionally beepvar)
via ... (GitHub issue #48).
- Updated dlvm1 family documentation to reflect long-format support in
panelgvar() and panelvar(), with examples.
- Replaced all Rf_error() calls in C++ code with Rcpp::stop() for proper
C++ stack unwinding and destructor calls, avoiding potential memory
leaks (GitHub issue #49).
psychonetrics 0.15 (2026-02-27)
- The package has gone through a large overhaul in the 0.14.x series of
package versions. The most important changes are listed below.
- Major C++ optimization of the estimation pipeline, resulting in ~45%
cumulative speedup. This includes direct parameter-to-matrix
mapping, OptimWorkspace caching of constant data across iterations,
cache-aware prepareModel to skip redundant computations, and the new
LBFGS++ pure C++ optimizer.
- Added ordinal data support and WLSMV estimation for the lvm framework.
lvm() now accepts ordinal variables via the 'ordered' argument, with
WLS, DWLS, and ULS estimators. The WLSMV scaled test statistic
(Asparouhov & Muthen, 2010) provides scaled chi-square, scaled RMSEA,
and scaled incremental fit indices matching lavaan output.
- Restructured dlvm1() to accept both wide-format and long-format data,
unifying the interface for panel data models. Deprecated ml_tsdlvm1(),
ml_gvar(), ml_var(), and related wrappers in favor of dlvm1() and
panelvar() / panelgvar().
- Penalized maximum likelihood (PML) and penalized FIML (PFIML) estimation
with automatic lambda selection via EBIC-based grid search. Includes
beta_min thresholding, warm starts across the lambda path, and refit()
for refitting parameters without regularization.
- New meta_lvm() model family for single-stage meta-analytic latent variable
modeling (extending the MAGNA framework to CFA/SEM), and new meta_var1()
/ meta_gvar() for meta-analytic VAR(1) network models pooling temporal
and contemporaneous structures across studies.
- New write_psychonetrics() function for exporting comprehensive
Mplus/LISREL-style output to a text file, intended for supplementary
materials in publications.
- Standardized input arguments across all model families: new 'cors',
'groupvar', and 'corinput' arguments with consistent validation via
standardize_input(). Meta-analytic models now accept raw data input.
- Added SRMR (Standardized Root Mean Square Residual) fit measure using
Bentler (1995) standardization. Ising models now also compute
model-implied means and covariances enabling SRMR computation.
- Improved starting values: OLS-based beta starting values for SEM models,
fixed the 'equal' argument in multi-group lvm() models.
- New loop_psychonetrics() convenience function for parallel bootstrapping
and simulations with automatic variable export and progress bars.
- Changed default missing data handling to missing = "auto", automatically
switching to FIML/PFIML when missing data is detected.
- Automatic fallback to approximate standard errors when the Fisher
information matrix cannot be inverted.
- lvm() now accepts lambda = "full" to specify a full loading matrix where
all cross-loadings are free. Requires the 'latents' argument to be
specified. FA-based starting values use promax-rotated loadings directly
(no permutation matching needed for full lambda).
- penalize() now accepts a matrix for the 'lambda' argument, enabling
per-element penalty specification: 0 = don't penalize, NA = auto-select
via EBIC grid search, numeric > 0 = fixed penalty strength. Parameters
that are fixed at zero in the model are automatically freed when a
non-zero penalty is specified, combining freepar() and penalize() in
one step. Works for both symmetric (e.g., sigma_epsilon) and
non-symmetric (e.g., lambda) matrices.
- Various bugfixes for Ising Fisher information, dlvm1 Jacobian
construction, dplyr/ggplot2 deprecation warnings, and CRAN compliance.
psychonetrics 0.14.28
- Ising models now compute model-implied means (mu) and covariances (sigma)
from the exact Ising expectations after optimization. This enables SRMR
and other residual-based fit measures for Ising models (previously returned
NA). The computation uses the existing isingExpectation() C++ function:
mu = E[X] and sigma = E[XX'] - E[X]E[X]'.
psychonetrics 0.14.27
- Removed roptim-based C++ optimizers (cpp_L-BFGS-B, cpp_BFGS, cpp_CG,
cpp_SANN, cpp_Nelder-Mead) from the public interface. These optimizers
are no longer available via setoptimizer() or documented in help pages.
Users should use "nlminb" (default), "ucminf", "nloptr_TNEWTON", or
"LBFGS++" instead.
- Replaced deprecated ggplot2::aes_string() calls with
ggplot2::aes(.data[[]]) in CIplot() and ESA plot methods.
- Fixed delta matrix starting values when using delta_* = "zero":
diagonal elements fixed at zero now correctly show est = 0 in the
parameter table instead of data-derived starting values.
- Added SRMR (Standardized Root Mean Square Residual) fit measure.
Uses Bentler (1995) standardization, matching lavaan's default
srmr_bentler. Includes mean residuals when a mean structure is
present. Under FIML, uses the saturated model's EM-estimated
covariance as the observed statistic (matching lavaan's h1 approach).
Multi-group models use an N-weighted average across groups.
- The 'beta_min' argument in find_penalized_lambda() and runmodel() now
accepts "numerical" (default, uses 1e-05), "theoretical"
(uses sqrt(log(p)/n)), or a numeric value. The previous default was
the theoretical formula; the new default is the fixed numerical threshold.
- Added C++ pipeline for meta_var1/meta_gvar models. The C++ implementation
(cpp=TRUE, the default) uses OptimWorkspace caching and
formModelMatrices_direct for efficient parameter-to-matrix mapping.
- C++ implied model (implied_meta_var1_cpp) computes BetaStar, Sigma0,
Sigma1, and meta-analytic mu/sigma/kappa entirely in C++.
- C++ derivatives (d_phi_theta_meta_var1_cpp) reuse existing var1 C++
derivative helpers for the mean part and varcov helpers for the
random effects variance part.
- C++ prepare (prepare_meta_var1_cpp) follows the meta_lvm_prepare_cpp
pattern with D_c override for the ML gradient.
- Registered meta_var1 in all C++ dispatchers: prepareModel_cpp,
impliedModel_cpp, psychonetrics_gradient_cpp_inner, and
psychonetrics_FisherInformation_cpp_inner.
psychonetrics 0.14.26
- New meta_var1() model family: meta-analytic VAR(1) model for pooling
temporal and contemporaneous network structures across multiple studies.
Uses a one-stage random effects framework. Accepts either raw time-series
data (via data + studyvar) or pre-computed Toeplitz covariance matrices
(via covs + nobs).
- New meta_gvar() wrapper: convenience function that calls meta_var1()
with contemporaneous = "ggm" for graphical VAR estimation.
- meta_var1/meta_gvar model the stationary covariance (Sigma0) and lag-1
cross-covariance (Sigma1) blocks, excluding the nuisance exogenous
covariance block from the Toeplitz matrix.
- Derivatives for meta_var1 reuse existing var1 Jacobian helpers
(d_sigma0_beta_var1_cpp, etc.) for the mean part, and meta_lvm random
effects Jacobians for the variance part.
- R-only implementation (no C++ yet); model@cpp is set to FALSE.
- If studyvar is not assigned but idvar is, studyvar is set to idvar
with a warning. If both are assigned, idvar functions within each study
for collating time series.
psychonetrics 0.14.24
- Standardized input arguments across model families. All model constructors
(varcov, lvm, var1, tsdlvm1, dlvm1, meta_varcov, meta_lvm) now accept a
consistent set of input arguments:
- New 'cors' argument: supply correlation matrices directly (with 'nobs').
In varcov/meta_varcov, 'corinput' defaults to TRUE. In other families,
correlations are treated as covariances with a warning.
- New 'groupvar' argument: replaces the deprecated 'groups' argument. If both
are supplied, 'groupvar' takes precedence with a deprecation warning.
- New 'corinput' argument added to lvm, var1, tsdlvm1, and dlvm1 signatures
(errors if set to TRUE, as correlation input is only supported in varcov
and meta_varcov).
- Meta-analytic models (meta_varcov, meta_lvm) now support raw data input via
'data' + 'studyvar' arguments. Correlation/covariance matrices and sample
sizes are computed internally per study.
- meta_varcov() now also accepts covariance matrices via 'covs' argument.
- meta_lvm() now also accepts correlation matrices via 'cors' argument.
- Shared input validation consolidated into standardize_input() helper function,
providing consistent error messages for mutual exclusion violations, missing
required arguments, and unsupported combinations.
- Fixed pre-existing bug in meta_varcov() baseline/saturated model construction
where R's missing() function was passed as the 'missing' argument to varcov()
instead of a string value.
psychonetrics 0.14.23
- Strip debug symbols from compiled shared library via linker flags in Makevars,
reducing installed libs/ size on CRAN macOS builds (from ~29MB to ~2MB).
- Added C++ pipeline for meta_lvm (implied, prepare, derivatives), providing
~2.8x speedup over the R-only implementation.
- Fixed meta_lvm.Rd documentation to match actual function signature.
psychonetrics 0.14.22
- meta_lvm() now accepts covariance matrices (not just correlations). The data vector
includes diagonal elements (variances) alongside off-diagonal covariances, with free
sigma_epsilon diagonal. V matrix computation, implied model, and derivatives updated
to handle the full vech (with diagonal).
- Fixed checkJacobian() and checkFisher() to respect the model's @cpp slot: models
with cpp=FALSE (e.g., meta_lvm) now correctly use R-only fit/gradient/Fisher functions
instead of always defaulting to C++ versions.
- Fixed R CMD check --as-cran issues:
- panelvar()/panelgvar() now handle missing data argument correctly
- dlvm1() guards against missing data in auto-detect and samplestats calls
- penalty_lambda documentation updated to match code default (NA, not 0)
- Removed stray browser() calls from var1 implied and identify functions
- Fixed non-ASCII character in NA2020.Rd reference
- Removed continuous-data corinput from lvm(): correlations passed as covs are now
treated as covariances with free diagonal. Ordinal corinput retained internally.
psychonetrics 0.14.20
- Added meta_lvm() function for single-stage meta-analytic latent variable modeling.
Combines a latent variable model (CFA/SEM) for the mean structure of pooled
covariance/correlation matrices with a random-effects model for between-study
heterogeneity. Extends the MAGNA framework to latent variable models. Supports all
standard parameterizations for latent (cov/chol/prec/ggm), residual (cov/chol/prec/ggm),
and random effects (chol/cov/prec/ggm/cor) structures.
psychonetrics 0.14.18
- Standard errors now auto-fallback to approximate SEs when the Fisher information matrix
cannot be inverted (e.g., due to zero cells in crosstables in multi-group Ising models).
Previously this returned NA standard errors; now approximate SEs are computed automatically
with an informative warning
psychonetrics 0.14.17
- Fixed the 'equal' argument in multi-group lvm() models:
- The equal= argument (e.g., equal="lambda") now correctly constrains parameters across groups
- Previously, the equality flag was passed as a positional argument to fixMatrix, causing it to
be silently ignored
- Additionally fixed corrupted starting values when equal= is used: the lambda structure matrix
contains integer codes (>1) for equality constraints, but these were used as multipliers for
FA-derived starting values, producing extreme values that prevented convergence
- Both loadings and variance identification now work correctly with equal=
- Results from equal= now match those from the groupequal() workflow
- Added OLS-based starting values for the beta (structural) matrix in lvm()/sem():
- For each endogenous latent, OLS regression on FA-implied latent covariance provides initial
beta coefficients
- Disturbance covariance (sigma_zeta) is adjusted via (I-B)*Cov(eta)*(I-B)' instead of using
the raw FA factor correlations
- Guard rails for near-singular covariance, ill-conditioned predictor blocks, extreme
coefficients, and near-singular (I-B)
- Falls back to previous zero-initialization on any numerical issue
- New 'start' argument: "default" for OLS-based, "simple" for zero-initialization
- Added loop_psychonetrics() convenience function for parallel bootstrapping and simulations:
- Replaces boilerplate of makeCluster / clusterExport / parLapply / stopCluster
- Variables referenced in the expression are automatically detected and exported to workers
- Uses pbapply progress bars and safe cluster cleanup via on.exit
- Works with aggregate_bootstraps() and CIplot() for bootstrap analysis
- Added the NA2020 dataset: 8 self-report items (n=501) from Isvoranu (2020) for GGM examples
psychonetrics 0.14.16
- Added automatic lambda selection for PML/PFIML estimation via EBIC-based grid search:
- New function find_penalized_lambda() searches over a log-spaced grid of 100 lambda values
- Lambda now defaults to NA in all model constructors, signaling automatic selection
- runmodel() automatically triggers the grid search when NA lambdas are detected
- Lambda_max computed analytically from KKT conditions at the null-penalized model
- Warm starts across the lambda path for efficient computation
- Supports criterion selection: "bic", "ebic.25", "ebic.5" (default), "ebic.75", "ebic1"
- Users can still specify a fixed lambda (e.g., penalty_lambda = 0.1) for manual control
- Added beta_min thresholding for penalized estimates:
- Near-zero estimates below beta_min are set to exactly zero after convergence
- Default threshold: sqrt(log(p)/n) where p = number of penalized parameters
- Applied both during EBIC evaluation (for degree-of-freedom counting) and on the final model
- Reduces numerical noise from ISTA optimizer, improving specificity
- Exposed as user argument in runmodel() and find_penalized_lambda()
- EBIC for penalized model selection uses unpenalized log-likelihood at penalized estimates
(Convention A), with npar = unpenalized free params + nonzero penalized params after threshold
- Print and write methods now display lambda search results (selected lambda, criterion value,
beta_min) and show "auto (not yet selected)" for models with NA lambda before estimation
- NA-safe comparisons throughout the codebase for penalty_lambda column (print, write, refit,
penalize, unpenalize, C++ workspace builder)
psychonetrics 0.14.15
- Added PFIML (Penalized Full Information Maximum Likelihood) estimator:
- Combines FIML with L1/L2 penalty for penalized estimation with missing data
- Available for all Gaussian models (varcov, lvm, var1, dlvm1, tsdlvm1)
- Uses proximal gradient optimizer (same as PML)
- refit() switches PFIML back to FIML for post-selection inference
- Changed default missing data handling to missing = "auto" for all Gaussian models:
- ML automatically switches to FIML when missing data is detected
- PML automatically switches to PFIML when missing data is detected
- LS estimators (ULS, WLS, DWLS) default to listwise deletion
- Users can still override with explicit missing = "listwise" or missing = "pairwise"
- No change for Ising models (missing = "listwise" default remains)
- Penalty setup in model constructors now supports PFIML:
- estimator = "PML" and estimator = "PFIML" both trigger penalty initialization
- Baseline/saturated models use the appropriate unpenalized estimator (ML or FIML)
- Added experimental feature warnings for features introduced in the 0.14.x branch:
- PML, PFIML, LBFGS++ optimizer, ordinal data in lvm()/varcov(), WLSMV scaled test statistic
- Warnings appear once per session and only while version < 0.15
- Users are directed to report unexpected behavior to GitHub issues
psychonetrics 0.14.14
- Unified dlvm1() to accept both wide-format and long-format data:
- New 'datatype' argument with options "auto" (default), "wide", and "long"
- Auto-detects format from 'vars': matrix = wide, character vector = long
- New 'idvar' and 'beepvar' arguments for long-format data
- Long-to-wide conversion handled internally (previously required ml_tsdlvm1)
- Added 'standardize' argument to dlvm1() with options:
- "none" (default): no standardization
- "z": global z-scores per variable across all waves (does not impose stationarity)
- "quantile": quantile normalization across all waves
- "z_per_wave": independent z-scores per wave column (imposes stationarity)
- Added panellvgvar() as the canonical wrapper for panel latent variable GVAR models
- Deprecated functions (still work but emit warnings):
- ml_tsdlvm1() - use dlvm1() with long-format data instead
- ml_gvar() - use dlvm1() with within_latent="ggm", between_latent="ggm"
- ml_var() - use dlvm1() with desired within_latent and between_latent
- ml_ts_lvgvar() - use panellvgvar() instead
- panel_lvgvar() - use panellvgvar() instead
psychonetrics 0.14.13
- Added write_psychonetrics() function for exporting comprehensive model output to a text file:
- Produces Mplus/LISREL-style output with logo, sample info, model specification, parameter
estimates, fit measures, model matrices, modification indices, and logbook
- Intended for sharing as supplementary materials to papers
- Supports uncomputed models: shows starting values with clear warnings, omitting SEs, p-values,
fit measures, and modification indices
- Model matrices section shows only modeled matrices (not derived/implied quantities)
- Matrix output uses 2-digit rounding with dots for fixed zeros
- Fixed prune() error when no parameters to test in the requested matrix:
- adjust_p_values() now correctly returns a numeric vector (not the S4 model object) when nTest == 0
- Fixed backward compatibility for models saved without the WLS.Gamma slot:
- Added .hasSlot() guard to prevent "no slot of name 'WLS.Gamma'" warnings
psychonetrics 0.14.12
- Added WLSMV scaled test statistic (mean-and-variance adjusted chi-square) for WLS/DWLS/ULS estimators:
- Implements the scaled-shifted test (Asparouhov & Muthen, 2010) matching lavaan's WLSMV output
- New fit measures: chisq.scaled, df.scaled, pvalue.scaled, chisq.scaling.factor
- Scaled incremental fit indices: cfi.scaled, tli.scaled, nfi.scaled, rfi.scaled, ifi.scaled, rni.scaled, nnfi.scaled
- Scaled RMSEA: rmsea.scaled
- Baseline model also receives scaled test statistic (baseline.chisq.scaled, etc.)
- Added WLS.Gamma slot to store the full asymptotic covariance matrix (Gamma) per group
- Added LS_Gamma() helper function for computing Gamma from raw data
- Scaling factors match lavaan to 6+ decimal places for both ordinal and continuous data
- Supports single-group and multi-group models
- Changed the default estimator for ordinal data from WLS to DWLS, matching lavaan's convention
- Added "WLSMV" as an estimator alias for "DWLS" in lvm() and varcov()
- Fixed the DWLS weight matrix for ordinal data:
- Previously used diag(Gamma^{-1}) (diagonal of the full inverse), which differs from standard
- Now uses 1/diag(Gamma) (reciprocal of the diagonal), matching the lavaan convention
- Fixed (n+1)/n scaling bug in ULS fit function, gradient, and expected Hessian (R and C++):
- The weight matrix normalisation factor was (n+1)/n instead of 1, causing small systematic bias
psychonetrics 0.14.11
- Added corinput support to the lvm framework:
- lvm() now accepts a 'corinput' argument to specify that the input is a correlation matrix
- When corinput=TRUE, residual variances (diagonal of sigma_epsilon) are derived from the constraint diag(sigma) = 1
- Diagonal sigma_epsilon parameters are fixed and derived in the implied function, not freely estimated
- Supports WLS, DWLS, and ULS estimators; defaults to WLS when corinput=TRUE
- Variance identification is automatically enforced
- Both R and C++ code paths updated (implied and derivatives)
- Fixed a bug in the identity weight matrix dimension in samplestats for corinput/meanstructure combinations
- Fixed a bug in lvm Jacobian row removal when both corinput=TRUE and meanstructure=FALSE for continuous data
psychonetrics 0.14.10
- Added ordinal data support to the lvm framework (Muthen, 1984 three-stage estimator):
- lvm() now accepts an 'ordered' argument (character vector or TRUE) to specify ordinal variables
- Supports WLS, DWLS, and ULS estimators for ordinal CFA/SEM
- Polychoric correlations and thresholds are estimated from ordinal data
- Variance identification is automatically enforced for ordinal models
- Both R and C++ code paths (implied, prepare, derivatives) handle ordinal/continuous seamlessly
- Added nloptr_TNEWTON as a new optimizer, available via setoptimizer(x, "nloptr_TNEWTON"):
- Uses NLopt's preconditioned truncated Newton with restarts (NLOPT_LD_TNEWTON_PRECOND_RESTART)
- Builds a local quadratic model and solves the Newton system via preconditioned conjugate-gradient
- Supports box constraints and analytic gradients
- Added nloptr to Imports
- Fixed CRAN NOTE: commented out stray std::cout in vendored LBFGS++ header (LineSearchMoreThuente.h)
psychonetrics 0.14.9
- Tuned LBFGS++ convergence parameters for better performance on ill-conditioned models:
- Switched primary convergence criterion from gradient norm to relative function change (delta = 1e-8), matching nlminb behavior
- Increased L-BFGS history size from 6 to 10 for better Hessian approximation on larger models
- Relaxed gradient tolerance (epsilon = 1e-5) to avoid unnecessary iterations on ill-conditioned FIML problems
- ~4x faster on complex dlvm1/panel models (e.g., 8.6s -> 2.0s on 84-parameter panelgvar)
- Removed unused variable declarations to eliminate all compiler warnings from psychonetrics C++ source
psychonetrics 0.14.8
- Added LBFGS++ (LBFGSpp) as a new pure C++ optimizer, available via setoptimizer(x, "LBFGS++"):
- Vendored header-only LBFGS++ library (MIT license) into inst/include/
- Combined function+gradient evaluation exploits the prepareModel cache for efficiency
- ~1.6x faster than the existing roptim-based cpp_L-BFGS-B optimizer on benchmark models
- Added RcppEigen to LinkingTo for Eigen header support
psychonetrics 0.14.7
- Fixed R CMD check --as-cran issues:
- Guarded semPlot example with requireNamespace() since it is a Suggested package
- Replaced defunct dplyr::summarize_each_() with summarize(across()) in lvm example
- Added build artifacts and non-package files to .Rbuildignore
psychonetrics 0.14.6
- Fixed Ising model Fisher information for the beta (inverse temperature) parameter:
- Added missing beta factor in beta-threshold and beta-network cross-derivative blocks of the expected Hessian
- Fixed expectedmodel() to replace sample sufficient statistics with model-implied expectations for Ising models
psychonetrics 0.14.5
- Optimized dlvm1 Jacobian construction (d_phi_theta_dlvm1_group_cpp):
- Added dense Kronecker helpers (kronecker_I_X_dense, kronecker_X_I_dense) to avoid slow sparse element-by-element insertion when the result is used as a dense matrix
- Precompute the constant "between" Kronecker block once instead of recomputing it for every lag
- Pre-extract all grouplist values before the loop to eliminate repeated string-based hash lookups
- Fixed copy-paste bug: removed duplicate Jac.submat write for sigma_zeta_between in the lag loop
psychonetrics 0.14.4
- Fixed dplyr 1.1.0 deprecation warning about one-column matrices in filter() calls
psychonetrics 0.14.3
- Cache-aware prepareModel_cpp: skip redundant prepare when fit and gradient are called at the same parameter vector
- ~24% speedup in full model fitting (e.g., 9-variable CFA: ~155ms -> ~118ms)
psychonetrics 0.14.2
- Expanded OptimWorkspace to cache all constant S4 model/sample data (extramatrices, types, sample stats, etc.)
- All prepare_*_cpp and implied_*_cpp_core functions now read from cached workspace instead of S4 slots
- prepareModel_cpp post-processing now reads from workspace instead of S4 slots
psychonetrics 0.14.1
- Performance optimization: replaced O(nMat * nPar) parameter-to-matrix mapping with O(nFreePar) direct index mapping
- Eliminated S4 model cloning during optimization iterations by bypassing updateModel_cpp in all prepare functions
- Refactored all 8 implied_*_cpp functions into _core variants that accept pre-formed matrices
- All 8 prepare_*_cpp functions now use formModelMatrices_cpp_direct + implied_*_cpp_core pipeline
- Added OptimWorkspace: static cache for MatrixMapping, M matrix, and parameter indices across optimization iterations
- ~25% speedup in full model fitting (e.g., 9-variable CFA: ~200ms -> ~155ms)
psychonetrics 0.13.2 (2025-10-22)
- Fixed a bug in 'intersectionmodel' not properly clearing removed parameters
- Improved starting values for identity model in partialprune
- Fixed a bug in partialprune resulting in an uninformative error
- Switch from deprecated arma::is_finite() to std::isfinite()
psychonetrics 0.13.1 (2025-06-02)
- Bootstrapping now occurs before standardizing the data (thanks to Yiwei Yang and Amanda Montoya)
- Improved support for N=1 models in dlvm1 (e.g., for true ML estimation)
- Fixed a bug with missing values in the beta starting values matrix for dlvm1 models
- Changed partialprune() default for 'return' to "partialprune"; set to "best" to mimic old behavior
- Added 'final_prune' argument to partialprune() and changed the default to "saturated"; set to "partialprune" to mimic old behavior
psychonetrics 0.13 (2024-06-20)
- Added bootstrap support:
- Every model now supports 'bootstrap' (to enable bootstrap), 'boot_sub' (for subsampling) and 'boot_resample' (to resample)
- Enabling these will bootstrap the data *once* and create a psychonetrics object for the bootstrap sample
- The function 'aggregate_bootstraps' can be used to aggregate bootstraps:
- use print(...) for some general info
- use parameters(...) for the bootstrap results on the parameters
- use fit(...) for the bootstrap results on the fit indices
- use CIplot(...) for the bootstrap results on the confidence intervals (enable split0=TRUE if model selection is used)
psychonetrics 0.12 (2024-05-21)
- Updates for the dlvm1 model family:
- Changed the starting values for dlvm1 models for better convergence
- Added the 'start' argument to the dlvm1 model family. Use start = "version1" to use pre 0.12 start values
- Added the 'baseline' argument to dlvm1 model family, which now defaults to "stationary_random_intercept". Note: this will change incremental fit indices! To recover old behavior, set baseline = "independence"
- The log(det(kappa)) value used in fit function is now computed in FIML in the same way as computed in ML framework, this should lead to more stability, but might also lead to some different chi-square values
- The startreduce argument in prune() now defaults to 1
- Using "empty" for matrix specification is now deprecated; use "diag" or "zero" instead.
- getmatrix will no longer evaluate a model if it is not computed (now returns start values)
- the 'startreduce' argument in 'prune' now defaults to 1
- Added the 'fixstart' function that can aide in fixing startvalues
- The 'getmatrix' function now supports thresholded PDC matrices
- Diagnostic functions 'checkJacobian' and 'checkFischer' are now exported
- Added the transmod function, which can be used to transform one model type (e.g., using a Cholesky decomposition) to another model type (e.g., a GGM).
- Improved support for models with only one (latent) variable
- Added the 'logbook' function which can be used to more easily retrieve the psychonetrics logbook
- The 'partialprune' function has been updated and now allows for pruning specific matrices as well as returning different models
- Fixed some instances where the 'equal' argument wasn't working
- 'partialprune' now supports multiple matrices
- 'unionmodel' and 'intersectionmodel' now support the 'matrices' argument
- Changed lambda starting value choice to the following: Run an EFA, set the starting values to the loadigs of the factor that has the strongest loadings in the indicators, and set correlations between factors that use the same EFA factor for starting values to 0.8
- added warning for non computed model in parameters()
psychonetrics 0.11.5 (2023-10-03)
- Fixes to support Matrix 1.7-0
- The groupequal, groupfree, fixpar and freepar functions can now handle multiple rows/columns for symmetric matrices
- Fixed a bug in the identification function for ml_lvm models
- Implemented min_sum argument for estimating Ising models on data with a minimum sum score due to selection bias. This method is further discussed in Boot, de Ron, Haslbeck & Epskamp (in preperation). Correcting for selection bias after sum score selection in the Ising model.
- The 'warn_improper' argument in runmodel now defaults to FALSE, omitting the warning that a non-positive defnite matrix was encountered in parameter estimation.
- SEs are now set to NA if the Fischer information matrix could not be inverted normally
- Added an argument 'approximate_SEs' to runmodel to allow for SEs to be approximated using a pseudoinverse
psychonetrics 0.11 (2023-06-05)
- Optimised lambda starting value choice
- Changed warning for when a peusdoinverse was used in model estimation.
- Added 'threshold' argument to the matrix(...) function for significance thresholding
- Fixed a bug in psychonetrics:::emergencystart
psychonetrics 0.10.3
- Added a 'bootstrap' argument to var1() family to facilitate bootstrapping the data
- Fixed a bug where the saturated log-likelihood under FIML estimation could be incorrect
psychonetrics 0.10.2
- Built in a check to find NA sample covs
- added diag argument to getmatrix to obtain diagonal
- diagonal elements are now also thresholded when using getmatrix(..., threshold = ...)
- Added 'threshold' argument to getmatrix()
- Adjusting p-values is now done by default only for the tested p-values. This means that the number of tests is assumed to be the number of parameters of interest (matrices that are to be pruned), not the total number of parameters. Previous behavior can be reproduced with mode = "all" in prune(..).
- panelvar and panelgvar now support arguments within_latent and between_latent
psychonetrics 0.10.1
- Fixed a bug in 'prune' which did not allow intercept/mean vectors to be pruned
psychonetrics 0.10 (2021-10-25)
- ml_tsdlvm1 now orders data by idvar if beepvar is not supplied.
- Added 'CIplot' function
- Standardizing in ts_dlvm1 is now more stable.
- dlvm1 now uses observed variable names as latent variable names when appropriate.
- Fixed a bug with the beepvar argument in psychonetrics
psychonetrics 0.9 (2021-02-23)
- Changed 'rel.tol=1e-10' in the nlminb optimizer to be in line with lavaan
- Added 'warn_improper' argument to runmodel(..)
- The argument 'return_improper' now defaults to TRUE
- Optimizer no longer uses a spectral shift when inverting a semi-positive definite matrix
- Detection of non positive definite matrices is now done faster and should lead to less problems
- Added the 'bounded' argument to runmodel() to define if bounded estimation should be used (defaults to TRUE)
- Diagonals of symmetrical matrices can no longer be estimated to be negative (avoiding Heywood cases, although doing this with a Cholesky decomposition is nicer!)
psychonetrics 0.8.1 (2021-01-28)
- The nlminb estimator now uses more iterations.
psychonetrics 0.8 (2020-10-26)
- The log determinant is now computer better to be able to include fit measures at higher model complexities
- The 'return_improper' argument in runmodel( ) now returns improper estimates without trying different starting values
- Fixed a bug in meta_varcov when using individual estimates of the sampling variation
- Updated optimizer default control parameters
psychonetrics 0.7.6
- Model estimations that used improper estimation (pseudoinverse of variance-covariance matrix) now return with an error unless return_improper = TRUE in runmodel()
- Equality-free MIs are now also computed when all edges are included
psychonetrics 0.7.5
- Fixed a bug with nu_zeta being constrained in multigroup LGC models.
- factorscores now supports multi group models
psychonetrics 0.7.3
- Added the 'covariates_as' argument to latentgrowth() to model covariates with regressions (now default) or covariances
psychonetrics 0.7.2 (2020-06-24)
- Fixed a bug with removing diagonal elements of sigma_epsilon with single indicators
- Fixed another bug with models with only one free indicator
- Added the partialprune function for partially pruning multi-group models
psychonetrics 0.7.1 (2020-04-15)
psychonetrics 0.7 (2020-04-01)
- Major changes
- Major restructuring of underlying core code. Most vital functions have been translated to C++, leading to a large speedup!
- Added C++ based optimizers cpp_L-BFGS-B, cpp_CG, cpp_SANN, and cpp_Nelder. These are faster but slightly less stable than nlminb (now the default optimizer).
- Verbose now defaults to FALSE everywhere. This can be set with the setverbose function for a model
- Added meta-analysis model for varcov family (meta_varcov)
- The ml_tsdlvm1 function now allows for multi-kevek tsdlvm1 models (dlvm1 models) to be specified using syntax familiar to those using mlVAR and graphicalVAR
- Minor changes
- Numerous small bugfixes and improvements (e.g., better starting values)
- Added function 'fullFIML' to use true FIML computing the likelihood per row
- Changed 'WLS.V' to 'WLS.W'
- Several warning messages have been updated
psychonetrics 0.6 (2020-02-21)
- The 'dlvm1' model family now also returns the implied latent variance-covarriance matrix as 'sigma_eta_within'
- The latent lag-1 matrix is also returned as 'sigma_eta_within_lag1'
- The dlvm1 model family now no longer requires a 'lambda' matrix to be specified (will default to a panelvar model)
- Most model families now support the 'standardized' argument, which can be set to 'z' for z-scores (helps convergence) or 'quantile' for a semiparametric transformation
- Added the ml_lvm family for two-level random intercept latent variable models
- Added the simplestructure function to easily make a lambda matrix
- addalpha in modelsearch now works as expected
- addalpha and prunealpha now default to 0.01 in 'modelsearch'
- recursive now defaults to FALSE in 'prune'
psychonetrics 0.5.1 (2020-01-26)
- Fixed a bug with responses being missing when summary statistics are used in Ising()
- Fixed a bug with covtype sometimes being set to UB when corinput = TRUE
- Added DOI to description field
psychonetrics 0.5.0 (2020-01-25)
- The Ising model is now supported (only ML estimation) through the Ising() model function
- Added the covtype option to several models, controlling if maximum likelihood or unbiased estimates are used for the input covariances
- Added the function 'covML' for maximum likelihood covariance estimates
psychonetrics 0.4.1 (2019-12-18)
- Small fix for CRAN checks
psychonetrics 0.4 (2019-12-17)
- type = "cor" is now supported in varcov, with rho representing the correlations and SD the diagonal standard deviations matrix.
- The 'corr' function is now implemented as shorthand for varcov(..., type = "cor")
- The scale of the Fisher information matrix has been adjusted to portray unit information to be similar to Lavaan
- The getVCOV function has been added to obtain the estimated asymptotic var-cov matrix of the parameters.
- The meanstructure can now be ignored using meanstructure = TRUE in the following model families:
- varcov
- A correlation matrix can now be used as input (detected or set with corinput = TRUE) for the following families:
- varcov (type = "ggm" and type = "cor")
- The WLS estimator will now not investigate means when meanstructure is ignored, and variances when a correlaton matrix is used as input.
- The WLS weights matrix must be of the appropriate dimensions!
- The WLS.V matrix will no longer be adjusted for missing means.
- Added 'startEPC' argument to stepup and freepar
- Added the 'modelsearch' function for extensive stepwise model search
- Fixed several bugs and improved starting values in several models
psychonetrics 0.3.3 (2019-07-06)
- prune() now removes diagonal values of temporal effects
- psychonetrics now requires R 3.6
- Some C++ fixes for Solaris
psychonetrics 0.3.2 (2019-06-28)
- The parameters function now invisibly returns the parameter estimate data frame
- The MIs function now invisibly returns a data frame with MI estimates
- fit now invisibly returns a data frame with fit measure estimates
psychonetrics 0.3.1
- Several help-files are now updated with executable examples
psychonetrics 0.3.0
- First version to be submitted to CRAN