Skip to content

Regression: Formative indicators #576

Description

@mronkko

In lavaan version 0.6-20 and above, specifying a composite construct (using the <~ operator) that also has reflective indicators (using the =~ operator) crashes during model compilation with the following error:

Error in solve.default(wtw) : 'a' is 0-diml

This model compiles and fits successfully in lavaan 0.6-19 (where <~ was translated internally to regressions) but fails in newer versions due to the newly introduced native composite variable LISREL representation code.


Minimal Reproducible Example (MRE)

library(lavaan)

# Minimal syntax with both reflective and formative indicators
model <- '
  # Reflective indicators
  A =~ v3 + v4

  # Formative/Composite indicators
  A <~ v1 + v2
'

# This call crashes in 0.6-20 and 0.6-21
fit <- sem(model)

Analysis by Analysis by Google Antigravity and Gemini 3.5 Flash.

The crash occurs during the model-implied covariance matrix calculation:

Error in solve.default(wtw) : 'a' is 0-diml
Calls: sem ... lav_model_sigma -> lav_lisrel_sigma -> solve -> solve.default

Root Cause Analysis

The bug resides in the internal function lav_lisrel_sigma (in R/lav_model_sigma.R).

When a model utilizes composites (<~), lavaan populates the weight matrix WMAT in MLIST, directing the LISREL representation builder down the else branch of lav_lisrel_sigma:

cov.idx <- which(apply(LAMBDA, 1L, function(x) sum(x == 0) == ncol(LAMBDA)))
clv.idx <- which(apply(LAMBDA, 2L, function(x) sum(x == 0) == nrow(LAMBDA)))
rlv.idx <- seq_len(ncol(LAMBDA))[-clv.idx]
LW <- LAMBDA + WMAT
Tmat <- diag(nrow(LAMBDA))
Tmat[cov.idx, cov.idx] <- THETA[cov.idx, cov.idx]
wtw <- t(LW[, clv.idx, drop = FALSE]) %*% Tmat %*% LW[, clv.idx, drop = FALSE]
wtw.inv <- solve(wtw)

Analysis of the Bug:

  1. clv.idx is populated by identifying the columns of LAMBDA that contain only zeroes (i.e. latent variables that have no reflective indicators).
  2. If all composite constructs in the model are identified by at least one reflective indicator (a very common scenario like the $2$-step MIMIC model structure where A =~ v3 + v4 and A <~ v1 + v2), then LAMBDA has no columns that are entirely zero.
  3. Consequently, clv.idx is empty (integer(0)).
  4. Because clv.idx is empty, the matrix subset LW[, clv.idx, drop = FALSE] has zero columns, resulting in wtw being a $0 \times 0$ matrix.
  5. Calling solve(wtw) on a $0 \times 0$ matrix triggers Error in solve.default(wtw) : 'a' is 0-diml.

Suggested Code Fix

In lav_lisrel_sigma (in R/lav_model_sigma.R), a guard check should be added to ensure that clv.idx is not empty before attempting to calculate and invert the weight matrix wtw:

# Proposed correction in R/lav_model_sigma.R
WTW.inv <- diag(ncol(LAMBDA))
if (length(clv.idx) > 0L) {
    wtw <- t(LW[, clv.idx, drop = FALSE]) %*% Tmat %*% LW[, clv.idx, drop = FALSE]
    wtw.inv <- solve(wtw)
    WTW.inv[clv.idx, clv.idx] <- wtw.inv
}

This fix prevents attempting matrix operations on empty subsets and correctly falls back to treating the latent variables appropriately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions