---
title: "Unified Model Graphs: An Introduction"
author: "Hsiu-Ting Yu"
date: "2026-06-12"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Unified Model Graphs: An Introduction}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>",
                      fig.width = 7, fig.height = 5)
library(umg)
```

## The grammar in one paragraph

A Unified Model Graph (UMG) types every vertex on three dimensions:
observability (fill: shaded = observed), support (shape: circle =
continuous, square = categorical), and inferential role (border and
dedicated shapes: diamond = fixed unknown parameter, triangle = known
constant, double border = deterministic). Edges distinguish stochastic
dependence, symmetric covariance, deterministic assignment, and
mixture selection. Plates encode replication; nesting encodes
hierarchy. Every well-formed diagram corresponds to a likelihood
factorization, so the diagram is the model.

## Building a one-factor model by hand

```{r cfa-by-hand}
m <- umg_model(
  nodes = list(
    umg_node("eta", "$\\eta_i$", observed = FALSE, dist = "N(0, psi)"),
    umg_node("y1", "$y_{1i}$", observed = TRUE),
    umg_node("y2", "$y_{2i}$", observed = TRUE),
    umg_node("y3", "$y_{3i}$", observed = TRUE)
  ),
  edges = list(
    umg_edge("eta", "y1", "dep", fixed = 1),
    umg_edge("eta", "y2", "dep", label = "$\\lambda_2$"),
    umg_edge("eta", "y3", "dep", label = "$\\lambda_3$")
  ),
  plates = list(
    umg_plate("person", c("eta", "y1", "y2", "y3"), "i = 1, ..., N")
  )
)
m
plot(m)
```

## From a lavaan model

```{r lavaan, eval = requireNamespace("lavaan", quietly = TRUE)}
model_syntax <- '
  visual  =~ x1 + x2 + x3
  textual =~ x4 + x5 + x6
  speed   =~ x7 + x8 + x9
'
g <- umg_from_lavaan(model_syntax)
plot(g)
```

## From an lme4 formula

```{r lmer, eval = requireNamespace("lme4", quietly = TRUE)}
g2 <- umg_from_lmer(Reaction ~ Days + (Days | Subject))
plot(g2)
```

## TikZ export

`umg_to_tikz()` emits TikZ code in the dialect of the umg-style.tex
lexicon shipped in `inst/tikz/`, so programmatic and hand-drawn
diagrams are stylistically identical.

```{r tikz}
cat(head(umg_to_tikz(m), 12), sep = "\n")
```

## Model-data duality

Each UMG motif induces a canonical exploratory display. With the
sleepstudy data, the plate and random-coefficient motifs induce
trellis and spaghetti displays:

```{r duality, eval = requireNamespace("lme4", quietly = TRUE) && requireNamespace("ggplot2", quietly = TRUE)}
data(sleepstudy, package = "lme4")
plots <- umg_eda_scaffold(g2, sleepstudy, id = "Subject", time = "Days")
names(plots)
plots$spaghetti
```

## Inspecting a diagram as data

A diagram can be read out as ordinary data frames, which is convenient
for tabulating a model in a manuscript or editing it programmatically
before re-assembly with `umg_model()`.

```{r inspect}
m_sem <- umg_sem(
  measurement = list(F1 = paste0("y", 1:3), F2 = paste0("y", 4:6)),
  structural  = list(c("F1", "F2"))
)
as.data.frame(m_sem)                    # edges
as.data.frame(m_sem, what = "vertices") # vertices
summary(m_sem)
```

## Round-tripping to lavaan

`umg_to_lavaan()` is the inverse of `umg_from_lavaan()`: it emits
lavaan model syntax from a diagram. A directed edge from a latent to an
observed vertex becomes a measurement loading (`=~`); other directed
edges become regressions (`~`); covariance edges become `~~`. The
correspondence between a diagram and a fitted model is thus operational
in both directions.

```{r to-lavaan}
cat(umg_to_lavaan(m_sem))
```

```{r roundtrip, eval = requireNamespace("lavaan", quietly = TRUE)}
syntax  <- "visual =~ x1 + x2 + x3\ntextual =~ x4 + x5 + x6"
g       <- umg_from_lavaan(syntax)   # syntax  -> diagram
back    <- umg_to_lavaan(g)          # diagram -> syntax
cat(back)
```
