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:
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.
mean is exact when every component has an exact mean
method: it sums the component means.
The sampler is also exact: independent samples from each component, summed.
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:
set.seed(42)
S <- algebraic.dist::surv(sys)
S(c(1, 2, 3, 5), mc = 5000) # survival at multiple t values
#> [1] 0.9332 0.7136 0.4986 0.2104A different mc triggers a fresh draw:
S(2, mc = 5000) # uses the same cache
#> [1] 0.7136
S(2, mc = 10000) # new cache with 10000 samples
#> [1] 0.7104Because 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:
For reproducibility across separate closure constructions, seed before each one:
A cold standby of m iid Exp(rate) components has system
lifetime Gamma(shape = m, rate = rate):
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))
#> MC: 0.1522, Exact: 0.1512If you hit this case often, you can avoid the Monte Carlo by
constructing an algebraic.dist::gamma_dist directly.
Generics requiring topology or component-level
density/sup fail cleanly:
dist.structure::phi(sys, c(1, 1, 1, 1))
#> Error in UseMethod("phi"): no applicable method for 'phi' applied to an object of class "c('cold_standby_dist', 'univariate_dist', 'continuous_dist', 'dist')"dist.structure::min_paths(sys)
#> Error in UseMethod("min_paths"): no applicable method for 'min_paths' applied to an object of class "c('cold_standby_dist', 'univariate_dist', 'continuous_dist', 'dist')"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 you need the full dist.structure protocol (topology, importance, composition), cold standby is the wrong tool; use a coherent system instead.