---
title: "Extending mixpower with custom backends"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Extending mixpower with custom backends}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

This vignette describes the **backend contract** used by `mp_power()` so you can plug in custom simulators, fitters, and tests without modifying the core loop.

## The three-function contract

An `mp_scenario` stores an `engine` with:

1. **`simulate_fun(scenario, seed = NULL)`** — returns a `data.frame` of simulated data.  
   `mp_power()` passes `seed` when the function accepts it (see `?mp_power`).

2. **`fit_fun(data, scenario)`** — returns a fitted model object.

3. **`test_fun(fit, scenario)`** — returns `list(p_value = <scalar>)`.  
   Use `NA_real_` for `p_value` when the test cannot be computed.

`mp_power()` records `fit_ok`, optional `singular` from `attr(fit, "singular")`, and aggregates power using `alpha` and `failure_policy`.

## Building a validated backend with `mp_backend()`

Use `mp_backend()` to wrap the three functions with a name and optional metadata.  
`validate_mp_backend()` checks formal argument names before you run simulations:

```{r backend-toy}
library(mixpower)

sim_fun <- function(scenario, seed = NULL) {
  n <- scenario$design$clusters$subject
  x <- stats::rbinom(n, 1, 0.5)
  y <- scenario$assumptions$fixed_effects$condition * x +
    stats::rnorm(n, sd = scenario$assumptions$residual_sd)
  data.frame(y = y, condition = x)
}
fit_fun <- function(data, scenario) stats::lm(scenario$formula, data = data)
test_fun <- function(fit, scenario) {
  sm <- summary(fit)
  p <- sm$coefficients["condition", "Pr(>|t|)"]
  list(p_value = as.numeric(p))
}

eng <- mp_backend(sim_fun, fit_fun, test_fun, name = "toy_lm")
eng
```

## Using `mp_scenario()` and `mp_power()`

```{r run}
d <- mp_design(list(subject = 25), trials_per_cell = 1)
a <- mp_assumptions(list(`(Intercept)` = 0, condition = 0.2), residual_sd = 1)
scn <- mp_scenario(
  y ~ condition, d, a,
  test = "custom",
  simulate_fun = eng$simulate_fun,
  fit_fun = eng$fit_fun,
  test_fun = eng$test_fun
)
mp_power(scn, nsim = 12, seed = 1)
```

## Built-in backends

- **lme4**: `mp_backend_lme4()`, `mp_backend_lme4_binomial()`, etc., return `mp_backend` objects.  
  Scenario helpers (`mp_scenario_lme4()`, …) copy the three functions into the scenario.

- **glmmTMB** (optional): `mp_backend_glmmtmb()` when package `glmmTMB` is installed — see `?mp_backend_glmmtmb`.

## Parallel grids

Sensitivity and power-curve grids can be evaluated in parallel **outside** `mp_power()` (e.g. `mp_sensitivity_parallel()`, `mp_power_curve_parallel()`) using per-cell seeds `seed + cell_index - 1L` for reproducibility.

## Further reading

- `?mp_backend`, `?validate_mp_backend`, `?mp_scenario`, `?mp_power`
- Vignette `mixpower-intro` for Wald vs LRT and design-first workflows.
