---
title: "Getting started with shieldR"
author: "R. Heller"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Getting started with shieldR}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
resource_files:
- figures/logo.svg
- figures/logo-dark.svg
---
```{r setup, include=FALSE}
set.seed(20260512)
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(shieldR)
```
## Your first experiment
This tutorial takes you from a neutral scenario to saved, reproducible results.
Only the installed `shieldR` package is needed for the code below. Small batch
and replication counts keep the tutorial quick; they are teaching choices and
are not a study design or a precision justification.
The package implements a fixed five-leg research model. Its teaching scenarios
use synthetic route and condition inputs. The default examples disable
exposure and item-condition hazards so that network mechanics are easy to inspect.
```{r scenario}
scenario <- example_scenario(N = 4L)
scenario$graph
run <- simulate_shield(scenario, "P0", n_rep = 3L, seed = 20260512L)
summarize_shield(run)
```
## Interpret the endpoints
The statistical unit is the batch replication. The primary endpoint,
`viable_stage2`, measures arrival viable at Stage 2 by the administrative horizon. Arrival is
recorded before unloading. Item-condition hazard ends there, while downstream
transport continues to Stage 3. Conditional arrival-time medians remain missing
when no item arrives; they are never replaced by zero.
The constrained example uses fewer vehicles and a shorter horizon. Its purpose
is to illustrate that failure to arrive is an endpoint failure even without
a loss.
```{r constrained}
limited <- example_scenario("constrained", N = 4L)
short_run <- simulate_shield(limited, n_rep = 3L)
summarize_shield(short_run)
```
## Construct an explicit scenario
The fixed topology is:
```text
Source -> Collection -> Transfer-A / Transfer-B -> Stage1-a / Stage1-b -> Stage2 -> Stage3
L1 L2 L3 L4 L5
```
Each leg offers routes A, B and C. Parallel destinations and route labels are
separate concepts: choosing a route does not itself identify the destination.
Vehicles carry one item and are shared within the documented leg pools.
To define an experiment, pass your own graph and hazard, observation and policy
settings to `shield_parameters()`. The documented graph schema fixes five legs
and three routes per leg. All event durations are whole minutes. Defaults are
synthetic teaching values, not calibrated logistics parameters.
`scenario$graph` includes derived indices. Select only the seven input columns
before reconstructing or exporting a graph for later use:
```{r explicit-inputs}
graph_columns <- c("leg", "from", "to", "route", "travel_min",
"exposure_weight", "vehicle_fraction")
graph <- scenario$graph[graph_columns]
rebuilt <- shield_parameters(graph = graph, N = 4L, horizon = 60L)
stopifnot(identical(rebuilt, scenario))
```
| Column | Contract |
|---|---|
| `leg` | One of L1 to L5, with exactly one row for each route |
| `from`, `to` | The fixed node-group names shown by `example_scenario()$graph` |
| `route` | A, B or C; 15 distinct leg-route rows in total |
| `travel_min` | Strictly positive whole minutes |
| `exposure_weight` | Finite number in the closed interval from zero to one |
| `vehicle_fraction` | Strictly positive, finite multiplier of batch size, constant within a leg |
The constructor also checks observation probabilities, class counts, hazards,
and duration constraints. Reconstruct from inputs when changing a scenario;
editing a derived field such as `V` or `leg_time` leaves related fields stale.
The following catches an intentionally invalid graph to show the diagnostic:
```{r input-error}
invalid_graph <- graph
invalid_graph$travel_min[1] <- 0
tryCatch(
shield_parameters(invalid_graph),
error = function(e) conditionMessage(e)
)
```
## Time-varying logistics-item specifications
An item has a true condition and an observed specification. These need not agree:
observations can be noisy, missing, delayed or retained from an earlier time.
The basic model updates item classes and viability each minute. The optional
`with_item_model()` extension adds a dimensionless condition index, a degradation
indicator and finite-capacity processing at Stage 2. Processing can stabilize an
item; residual degradation and other losses can continue afterwards.
| Specification | Interpretation |
|---|---|
| `fragility` | Positive class-specific weights controlling susceptibility to loss |
| `obs_condition_index` | Latest delivered, noisy condition measurement; higher is better in this synthetic model |
| `obs_degradation` | Latest observed degradation flag |
| `obs_time`, `obs_missing` | Measurement time and whether the latest observation was missing |
| `refresh`, `delivery` | Observation interval and delivery delay in minutes |
| `useful_updates` | Whether later observations are delivered; does not stop underlying condition changes |
The observed fields belong to the internal `item_specs` state table. They are
used by the decision rules; ordinary run rows contain batch-level outcomes.
The index equation and its thresholds are synthetic, dimensionless assumptions,
not a physical sensor calibration. `with_decision_controls(..., item =
"initial_observed")` freezes the information used for decisions while item
conditions continue to evolve. The default `item = "updated"` uses delivered
updates. This separates information freshness from the physical process.
The following explicit specification activates condition changes and finite
processing capacity. The coefficients are illustrative and uncalibrated.
The processing extension continues to fixed 24-hour and 30-day follow-up times;
these are model assumptions, separate from the transport horizon.
```{r changing-item-specifications}
item_model <- list(
degradation_scale = 0.2, early_other = 0.02, late_other = 0.1,
tau = 40, residual = 0.1,
prep = c(1, 2, 3), processing = c(2, 3, 4), failure = 0,
capacity = 1L, fragility = c(1.2, 1, 0.8),
sensitivity = 0.7, specificity = 0.95, persistence = 1,
missing = 0, refresh = 2L, delivery = 0L,
useful_updates = TRUE, item_age = 0
)
dynamic <- with_item_model(example_scenario(N = 4L), item_model)
updated <- with_decision_controls(dynamic, item = "updated")
initial <- with_decision_controls(dynamic, item = "initial_observed")
updated_run <- simulate_shield(updated, "P3", n_rep = 3L, seed = 20260512L)
initial_run <- simulate_shield(initial, "P3", n_rep = 3L, seed = 20260513L)
summarize_shield(updated_run)
summarize_shield(initial_run)
```
Both arms retain the same condition dynamics. Only the information used by the
allocation rule differs. A small synthetic comparison does not establish that
one information policy is better in a real logistics application.
## Compare designs explicitly
Independent policy comparisons use different seeds. `compare_policies()`
returns a Mann-Whitney U comparison, Cliff's delta and bootstrap intervals for
delta and the median difference. It does not perform a preliminary normality
test. Its intervals quantify Monte Carlo sampling uncertainty conditional on the
model. This short example explicitly reduces bootstrap resamples for speed;
study reporting uses the default 10,000.
```{r comparison}
a <- simulate_shield(scenario, "P0", n_rep = 3L, seed = 20260512L)
b <- simulate_shield(scenario, "P3", n_rep = 3L, seed = 20260512L + 1L)
compare_policies(b$viable_stage2, a$viable_stage2, "P3", "P0", B = 100L)
```
The neutral scenario is tied by construction. A point bootstrap interval on a
discrete endpoint is flagged rather than interpreted as empirical certainty.
For several comparisons, collect `p_raw_log10` values in each prespecified
family and call `adjust_fdr()` separately for that family. Very small tails
should be reported using log probabilities; the linear field is floored when
it cannot represent the computed value.
Paired disruptions-disabled interventions instead use the same seed and replication
count. The package aligns rows and checks their replication identities.
```{r paired}
on <- simulate_shield(scenario, "P3", "adaptive", n_rep = 3L,
disruptions_enabled = TRUE)
off <- simulate_shield(scenario, "P3", "adaptive", n_rep = 3L,
disruptions_enabled = FALSE)
compare_intervention(on, off, B = 100L)
```
Here exposure is zero, so the contrast is zero. In a nonzero disruption risk experiment,
the contrast includes downstream changes and horizon effects. It is not a
count of direct disruption losses or proof of a better policy.
P3 uses a known disruption-response model and an entropy-regularized decision heuristic.
Its comparisons with P1 and P2 do not isolate individual component effects.
All assumptions require scientific review for a new application.
The [study design tutorial](study-design.html) develops independent comparisons,
paired interventions, complete comparison-family adjustment, and replay in more
detail. The [workbench tutorial](interactive-exploration.html) covers the Shiny
interface.
## Save results with provenance
CSV is convenient for a table, but it does not preserve the scenario and
software attributes needed by `compare_intervention()`. Save the full run as
RDS and write an environment record alongside derived exports. The temporary
folder below makes this example self-contained; use a named output directory
inside your analysis project for a real study.
```{r save-results}
output_dir <- tempfile("shieldR-tutorial-")
dir.create(output_dir)
saveRDS(run, file.path(output_dir, "batch-run.rds"))
utils::write.csv(summarize_shield(run),
file.path(output_dir, "endpoint-summary.csv"), row.names = FALSE)
writeLines(capture.output(sessionInfo()),
file.path(output_dir, "sessionInfo.txt"))
restored <- readRDS(file.path(output_dir, "batch-run.rds"))
stopifnot(identical(run, restored))
list.files(output_dir)
```
## Troubleshooting
| Symptom | Check |
|---|---|
| Every policy has the same viability | The neutral examples have no item-condition or disruption hazards; ties are expected |
| Viability is below one without any losses | Some items may be viable but have not arrived by the administrative horizon |
| Arrival-time summaries are missing | No arrival occurred in one or more replications; retain missingness explicitly |
| Graph validation fails | Keep exactly the seven input columns and all 15 unique leg-route rows |
| A paired comparison is rejected | Keep original run attributes, matching configurations and identical replication seeds |
| A bootstrap interval is a single point | Inspect discreteness and replication counts; this does not establish empirical certainty |
| The vignette is not installed | Install from a built source archive or use `build_vignettes = TRUE` |
## Session information
```{r environment}
sessionInfo()
```