---
title: "interaction effects in random intercept cross-lagged panel models (RI-CLPMs)"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{interaction effects in random intercept cross-lagged panel models}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

```{r setup}
library(modsem)
```
Here we show an example of a random intercept cross-lagged panel model with
a within $\times$ within interaction effect. The example below is based on
[Testing for *Within $\times$ Within and Between $\times$ Within* Moderation Using Random Intercept Cross-Lagged Panel Models](https://www.tandfonline.com/doi/full/10.1080/10705511.2022.2096613).
In particular, it is based on Section *8.4. Within $\times$ Within Interaction Using the Within-Person Component of a Time-Varying Moderator*,
and the corresponding M*plus* output files on [OSF](https://osf.io/tjxrd/).
In the original article, the Bayes estimator was used, but here we use LMS.
The LMS approach in M*plus* integrates along four dimensions, making estimation
quite impractical for this model. The LMS estimator in `modsem`, however, only
has to integrate along two dimensions, making estimation viable.

## Simulate Data
```{r}
library(mvtnorm)
set.seed(235910)

N <- 10000

# Simulate between-person random intercepts
phi <- matrix(c(
  0.806, 0.481, 0.438,
  0.481, 0.758, 0.469,
  0.438, 0.469, 1.290
), nrow = 3, byrow = TRUE)

Xi <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = phi
)

RIemo <- Xi[, 1]
RIper <- Xi[, 2]
RIcon <- Xi[, 3]


# Time 3
psi3 <- matrix(c(
  1.234, 0.328, 0.478,
  0.328, 1.614, 0.427,
  0.478, 0.427, 2.743
), nrow = 3, byrow = TRUE)

zeta3 <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = psi3
)

wemo_3 <- zeta3[, 1]
wper_3 <- zeta3[, 2]
wcon_3 <- zeta3[, 3]


# Time 5
psi5 <- matrix(c(
  1.489, 0.397, 0.301,
  0.397, 1.143, 0.202,
  0.301, 0.202, 0.764
), nrow = 3, byrow = TRUE)

zeta5 <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = psi5
)

wemo_5 <- 0.142 * wemo_3 + 0.071 * wper_3 + 0.086 * wcon_3 - 0.010 * (wcon_3 * wper_3) + zeta5[,1]
wper_5 <- 0.018 * wemo_3 + 0.100 * wper_3 + 0.050 * wcon_3 + zeta5[,2]
wcon_5 <- -0.008 * wemo_3 + 0.006 * wper_3 + 0.105 * wcon_3 + zeta5[,3]

# Time 7
psi7 <- matrix(c(
  1.808, 0.521, 0.475,
  0.521, 1.287, 0.311,
  0.475, 0.311, 0.881
), nrow = 3, byrow = TRUE)

zeta7 <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = psi7
)

wemo_7 <- 0.361 * wemo_5 + 0.097 * wper_5 + 0.147 * wcon_5 + 0.105 * (wcon_5 * wper_5) + zeta7[,1]
wper_7 <- 0.030 * wemo_5 + 0.348 * wper_5 + 0.123 * wcon_5 + zeta7[,2]
wcon_7 <- 0.074 * wemo_5 + 0.056 * wper_5 + 0.086 * wcon_5 + zeta7[,3]

# Indicators/Observed Variables
data <- data.frame(
  emo_3 = 1.385 + RIemo + wemo_3 + rnorm(N, 0, sqrt(.200)),
  emo_5 = 1.407 + RIemo + wemo_5 + rnorm(N, 0, sqrt(.200)),
  emo_7 = 1.528 + RIemo + wemo_7 + rnorm(N, 0, sqrt(.200)),

  per_3 = 1.563 + RIper + wper_3 + rnorm(N, 0, sqrt(.200)),
  per_5 = 1.176 + RIper + wper_5 + rnorm(N, 0, sqrt(.200)),
  per_7 = 1.242 + RIper + wper_7 + rnorm(N, 0, sqrt(.200)),

  con_3 = 2.827 + RIcon + wcon_3 + rnorm(N, 0, sqrt(.200)),
  con_5 = 1.517 + RIcon + wcon_5 + rnorm(N, 0, sqrt(.200)),
  con_7 = 1.402 + RIcon + wcon_7 + rnorm(N, 0, sqrt(.200))
)
```

## Fit The Model

```{r}
model.inp <- '
  # Create between components (random intercepts)
  RIemo =~ 1 * emo_3 + 1 * emo_5 + 1 * emo_7;
  RIcon =~ 1 * con_3 + 1 * con_5 + 1 * con_7;

  # Create within-person centered variables
  wemo_3 =~ 1 * emo_3;
  wemo_5 =~ 1 * emo_5;
  wemo_7 =~ 1 * emo_7;

  wcon_3 =~ 1 * con_3;
  wcon_5 =~ 1 * con_5;
  wcon_7 =~ 1 * con_7;

  # Moderator also needs to be decomposed into within and between parts
  RIper =~ 1 * per_3 + 1 * per_5 + 1 * per_7;

  wper_3 =~ 1 * per_3;
  wper_5 =~ 1 * per_5;
  wper_7 =~ 1 * per_7;

  # Constrain the measurement error variances close to zero
  # to allow for reasonable imputation times
  con_3 ~~ 0.2 * con_3
  con_5 ~~ 0.2 * con_5
  con_7 ~~ 0.2 * con_7
  emo_3 ~~ 0.2 * emo_3
  emo_5 ~~ 0.2 * emo_5
  emo_7 ~~ 0.2 * emo_7
  per_3 ~~ 0.2 * per_3
  per_5 ~~ 0.2 * per_5
  per_7 ~~ 0.2 * per_7

  # Estimate the covariance between the random intercepts
  RIemo ~~ RIper
  RIemo ~~ RIcon
  RIper ~~ RIcon

  # Estimate the lagged effects between
  # the within-person centered variables
  wemo_7 ~ wemo_5 + wper_5 + wcon_5
  wper_7 ~ wemo_5 + wper_5 + wcon_5
  wcon_7 ~ wemo_5 + wper_5 + wcon_5

  wemo_5 ~ wemo_3 + wper_3 + wcon_3
  wper_5 ~ wemo_3 + wper_3 + wcon_3
  wcon_5 ~ wemo_3 + wper_3 + wcon_3

  # Specify interaction terms between within-person centred
  # conduct problems and the within-person centered peer problems
  # predict within-person centred emotional problems with interaction
  wemo_7 ~ wcon_5:wper_5
  wemo_5 ~ wcon_3:wper_3

  # Estimate the covariance between the within-person
  # components at the first wave
  wemo_3 ~~ wper_3
  wemo_3 ~~ wcon_3
  wper_3 ~~ wcon_3

  # Estimate the covariances between the residuals of
  # the within-person components (the innovations)
  wemo_5 ~~ wper_5
  wemo_5 ~~ wcon_5
  wper_5 ~~ wcon_5

  wemo_7 ~~ wper_7
  wemo_7 ~~ wcon_7
  wper_7 ~~ wcon_7
'

fit.lms <- modsem(
  model.syntax = model.inp,
  data              = data,
  method            = "lms",
  nodes             = 32,
  optimize          = FALSE, # we're currently unable to optimize starting parameters here
  orthogonal.x      = TRUE,  # make sure the model is identifiable
  orthogonal.y      = TRUE,  # not strictly necessary for this model in particular
  auto.split.syntax = TRUE   # allow eta x eta interactions
)

summary(fit.lms)
```
