--- title: "Simulating uncertainty" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Simulating uncertainty} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(comment = "#>", message = FALSE, warning = FALSE) ``` When point estimates of basic-event probabilities are uncertain, you can **simulate** rates or probabilities, evaluate the top event for each draw with [`quantify(..., prob = TRUE)`](https://tidyfault.netlify.app/reference/quantify.html), and summarize the resulting distribution. This follows the IT security workflow in the **tidyfault** paper. ## Model and bundled data - Use the IT security tree as in [quantify()-ing binary outcomes](https://tidyfault.netlify.app/articles/quantify-binary). - [`it_security_probs`](https://tidyfault.netlify.app/reference/it_security_probs.html) holds point estimates at $t = 10{,}000$ hours. ```{r} library(tidyfault) library(dplyr) library(tidyr) data("it_security_nodes") data("it_security_edges") data("it_security_probs") f <- formulate(equate(curate(it_security_nodes, it_security_edges))) ``` --- ## Simulate uncertainty in failure rates Outline: 1. Convert each point probability at time $t$ to an exponential rate $\lambda = -\log(1-p)/t$. 2. Perturb $\lambda$ with a normal draw using an illustrative coefficient of variation. 3. Map back to $p(t)$ with `pexp()`. 4. Call **`quantify()` once** on the full matrix of simulated probabilities (vectorized over rows). ```{r} set.seed(7) t_horizon <- 10000 cv <- 0.2 n_draws <- 2500 batch_probs <- it_security_probs |> pivot_longer(everything(), names_to = "event", values_to = "prob") |> mutate( lambda = -log(1 - prob) / t_horizon, lambda_se = lambda * cv ) |> group_by(event) |> reframe( prob = pexp( q = t_horizon, rate = rnorm(n = n_draws, mean = lambda, sd = lambda_se) ), scenario = seq_len(n_draws) ) |> ungroup() |> pivot_wider(id_cols = scenario, names_from = event, values_from = prob) event_cols <- setdiff(names(batch_probs), "scenario") sim_probs <- batch_probs sim_probs$outcome <- quantify( f, newdata = sim_probs[, event_cols, drop = FALSE], prob = TRUE ) knitr::kable(head(sim_probs, 3), digits = 4) ``` --- ## Summarize the top-event distribution ```{r} sim_probs |> summarize( estimate = mean(outcome), lower = quantile(outcome, 0.025), upper = quantile(outcome, 0.975) ) |> knitr::kable(digits = 4) ``` --- ## Interpretation - The interval reflects **propagation** of rate uncertainty through the Boolean structure, not sampling error from field data. - Tighten or widen the simulation by changing `cv`, `n_draws`, or the distributional assumption on $\lambda$. For deterministic probability scenarios, see [quantify()-ing probabilities](https://tidyfault.netlify.app/articles/quantify-probabilities).