---
title: "Getting Started with `semrulesid`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{semrulesid-quickstart}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(semrulesid)
```

## Introduction

`semrulesid` allows the user to check a structural equation model (SEM)
written in [`lavaan`](https://lavaan.ugent.be/) (Rosseel, 2012) syntax, or
supplied as a `lavaan` parameter table or fit object, against a number of
identification rules from the literature. Rules are specified as being necessary
and/or sufficient and specific reasons are given when a rule is not satisfied or
not applicable. Users should not treat the package output as the sole
determinant of model identification. Instead, `semrulesid` should be used as a
quick check for potential identification issues and outstanding
model-specification concerns.


## Basic workflow

The primary function is `id()`. Supply a model in `lavaan` syntax and specify
the `lavaan` function whose defaults you intend to use through `lav_fun`.

```{r basic-id}
model <- '
  L1 =~ Y1 + Y2 + Y3
  L2 =~ Y4 + Y5 + Y6
  L2 ~ L1
'

id(model, lav_fun = "sem")
```

The output table reports whether each implemented rule passes and whether the
rule is necessary and/or sufficient (or neither) for the relevant model class, which is
printed at the top of the output. Rules not applicable to the model type are printed
directly below the table.

### Interpreting messages

The `id()` function reports rules applicable to the specified model in the
rule-check table. Rules that are not applicable to the detected model type are
listed below the table.

When a rule has an associated message, the table's `Message` column gives the
number of the corresponding message. Messages are grouped into the following
sections:

- **Identification failure**: A necessary condition was not met. Under the
  assumptions of the relevant rule, this indicates that the model is not
  identified and that re-specification may be necessary.

- **Sufficient condition not satisfied**: A sufficient condition was not met.
  This does not establish that the model is underidentified; rather, the rule
  cannot be used to establish identification for the model.

- **Rule not applicable to this model specification**: The rule does not apply
  to the specified model. This is *not* an identification failure.

Thus, a rule with `Pass = No` should be interpreted together with its
`Necessary` and `Sufficient` columns and any corresponding message. A failed
*necessary* condition indicates an identification problem, whereas an unmet
*sufficient* condition means only that the corresponding sufficient rule cannot
certify identification.

## Choosing `lav_fun`

When a model string is supplied, `lav_fun` determines which `lavaan` defaults
are used when the model is converted to a parameter table. Current options are:

- `"lavaan"`
- `"sem"`
- `"cfa"`

For example, use `"cfa"` for a confirmatory factor analysis model:

```{r cfa, eval=FALSE}
cfa_model <- '
  L1 =~ Y1 + Y2 + Y3
  L2 =~ Y4 + Y5 + Y6
  L1 ~~ L2
'

id(cfa_model, lav_fun = "cfa")
```

When a parameter table or fit `lavaan` object is supplied, `lav_fun` is
ignored since the model defaults have already been applied.

## Checking latent-variable scaling

Use `scaling()` to inspect whether each latent variable is scaled via a
method used in the literature.

```{r scaling}
scaling(model, lav_fun = "sem")
```

For models without a mean structure, the package checks whether each latent
variable has assigned units through a fixed, nonzero loading (scaling indicator)
or a fixed positive latent-variable variance.

For models with a mean structure, the package also checks whether the latent
variable has an assigned origin, such as through a fixed latent-variable mean or
a fixed intercept for a scaling indicator.

## Working with a fit `lavaan` model

If a model has already been fit with `lavaan`, pass the fit object
directly to `id()` or `scaling()`. Set `lav_fun = NA` to avoid warnings when
`lav_fun` does not match the fitting function used to estimate the model.

```{r fit-model, eval = FALSE}
library(lavaan)

fit <- sem(model, data = my_data)

id(fit, lav_fun = NA)
scaling(fit, lav_fun = NA)
```

## Piping identification and scaling checks

`semrulesid` supports the use of a pipe operator to chain together
identification and scaling checks, e.g., using the base R pipe `|>`
or the pipe from the `magrittr` package:

```{r piping, eval=FALSE}
id(model, lav_fun = "sem") |> scaling()
# or
library(magrittr)
id(model, lav_fun = "sem") %>% scaling
```

The reverse order is also supported:

```{r reverse-piping,eval=FALSE}
scaling(model, lav_fun = "sem") |> id()
# or
library(magrittr)
scaling(model, lav_fun = "sem") %>% id
```

## The two-step rule

For supported full SEMs (latent variables plus structural paths), `id2()`
applies the two-step rule of identification (see Bollen, 2026). It first transforms
the model into a confirmatory factor analysis (CFA) model and evaluates it for
identification. It then transforms the model into a simultaneous equations
model, and evaluates *it* for identification. If both steps are identified, the
original model is identified. The outputs of both steps are printed via the
`id2()` function.

```{r two-step}
id2(model, lav_fun = "sem")
```


## Next steps

For details on individual rules, see:

```{r help, eval = FALSE}
?id
?scaling
?get_rules
```

You can retrieve implemented rule functions with `get_rules()`:

```{r get-rules}
cfa_rules <- get_rules(rule = "*", model_type = "cfa")
names(cfa_rules)
```

For further theoretical background, see Ken Bollen's book *Elements of
Structural Equation Models* (2026).
