--- title: "Non-coherent systems: cold standby" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Non-coherent systems: cold standby} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` ```{r setup} library(dist.structure) library(algebraic.dist) ``` ## Why "non-coherent" The structure-function framework assumes a monotone binary function `phi: {0, 1}^m -> {0, 1}` that maps per-component states to a system state. This captures a huge class of reliability topologies but not everything practitioners build. Cold standby is a canonical example. A **cold standby** system has m components but only one is active at a time. When the active component fails, a dormant spare instantaneously takes over. System lifetime is the sum of component lifetimes, not an order statistic. The topology is temporal succession, not a structure function on states. So cold standby: - is a valid distribution object (you can sample, compute survival, ask for mean), - is NOT a `dist_structure`: it has no `phi`, no `min_paths`, no signature, no Birnbaum importance. dist.structure provides `cold_standby_dist` for this case, with its own class chain that deliberately does not inherit `dist_structure`. ## Constructing a cold standby ```{r} sys <- cold_standby_dist(list( exponential(1), exponential(2), exponential(0.5) )) class(sys) dist.structure::is_dist_structure(sys) # FALSE -- not a coherent system ``` ## Mean and sampling: exact `mean` is exact when every component has an exact mean method: it sums the component means. ```{r} mean(sys) # 1/1 + 1/2 + 1/0.5 = 3.5 ``` The sampler is also exact: independent samples from each component, summed. ```{r} set.seed(1) x <- algebraic.dist::sampler(sys)(5000) mean(x) # empirical ~ 3.5 ``` ## Survival and CDF: Monte Carlo with caching For general components, the density of a sum-of-independent-RVs has no closed form (it's a convolution). dist.structure computes `surv` and `cdf` via Monte Carlo sampling. The closure caches the samples on the first call, so repeated evaluations at different `t` values are deterministic given the same `mc`: ```{r} set.seed(42) S <- algebraic.dist::surv(sys) S(c(1, 2, 3, 5), mc = 5000) # survival at multiple t values ``` A different `mc` triggers a fresh draw: ```{r} S(2, mc = 5000) # uses the same cache S(2, mc = 10000) # new cache with 10000 samples ``` Because the cache lives inside the closure, using the same `S` closure keeps results consistent. `cdf` has its own closure and its own cache: `cdf(sys)(t) + surv(sys)(t)` computed independently is not exactly 1. If you need that identity, use one `surv` closure and compute the CDF from it: ```{r} S <- algebraic.dist::surv(sys) F_t <- function(t) 1 - S(t) F_t(2) + S(2) # exactly 1 ``` For reproducibility across separate closure constructions, seed before each one: ```{r} set.seed(1); S1 <- algebraic.dist::surv(sys); s1 <- S1(2) set.seed(1); S2 <- algebraic.dist::surv(sys); s2 <- S2(2) all.equal(s1, s2) ``` ## Specialized closed-form case: iid exponentials A cold standby of m iid `Exp(rate)` components has system lifetime `Gamma(shape = m, rate = rate)`: ```{r} m <- 4; rate <- 2 sys <- cold_standby_dist(replicate(m, exponential(rate), simplify = FALSE)) # Monte Carlo survival at t = 3 set.seed(1) mc_est <- algebraic.dist::surv(sys)(3, mc = 10000) # Exact via Gamma exact <- pgamma(3, shape = m, rate = rate, lower.tail = FALSE) cat(sprintf("MC: %.4f, Exact: %.4f\n", mc_est, exact)) ``` If you hit this case often, you can avoid the Monte Carlo by constructing an `algebraic.dist::gamma_dist` directly. ## What cold standby does NOT support Generics requiring topology or component-level `density`/`sup` fail cleanly: ```{r, error = TRUE} dist.structure::phi(sys, c(1, 1, 1, 1)) ``` ```{r, error = TRUE} dist.structure::min_paths(sys) ``` Methods inherited from `algebraic.dist::univariate_dist` that require `density` or `sup` (notably `vcov`, `expectation`) are also not supported by default. A specialized subclass with a closed-form aggregate (e.g., iid exponential -> Gamma) can provide them by overriding. ## When to reach for cold standby - Sequential redundancy with dormant spares (perfect switching, zero standby hazard). - Modelling sum-of-lifetimes directly as a distribution. - Monte Carlo as a first-pass answer when no closed form is available. When you need the full dist.structure protocol (topology, importance, composition), cold standby is the wrong tool; use a coherent system instead.