Comparison, selection and sensitivity¶
comparison ¶
Scenario comparison against a baseline.
Comparison ¶
Comparison(rows: Sequence[ComparisonRow], confidence: float, adjust: AdjustMethod = 'none')
Differences scenario - baseline for each metric, with confidence intervals.
The intervals quantify simulation sampling error only. A significant
difference means the model, as specified, responds to the change; it says
nothing about whether the real system would (that depends on model
validity). With adjust="none" no multiple-comparison correction is
applied; see :func:compare for the alternatives.
compare ¶
compare(result: ExperimentResult, baseline: str = 'baseline', scenarios: Iterable[str] | None = None, metrics: Iterable[str] | None = None, *, confidence: float = 0.95, adjust: AdjustMethod = 'none') -> Comparison
Compare scenarios of one experiment with its baseline scenario.
When the experiment used common random numbers and both scenarios have the same replication seeds, a paired-t interval is used (replication i of each scenario saw the same random streams); otherwise Welch's t.
adjust corrects for testing many (metric, scenario) pairs at once:
"bonferroni" widens every interval to level 1 - alpha/m (simultaneous
coverage) and adjusts p-values; "holm" (family-wise error) and "bh"
(false discovery rate) adjust p-values only, and significant then
means p_adjusted < 1 - confidence.
compare_samples ¶
compare_samples(baseline: Mapping[str, Sequence[float]], scenario: Mapping[str, Sequence[float]], *, paired: bool = False, confidence: float = 0.95, adjust: AdjustMethod = 'none', baseline_name: str = 'baseline', scenario_name: str = 'scenario') -> Comparison
Compare two {metric: values} samples (e.g. collected outside an Experiment).
selection ¶
Ranking and selection: pick the best scenario with a statistical guarantee.
:func:select_best implements the fully sequential procedure KN of Kim and
Nelson (2001). It runs every scenario n0 times, then adds one
replication at a time to the scenarios still in contention and eliminates
any that is clearly worse than another. With probability at least
confidence the scenario selected is the true best, or within
indifference of it (the smallest difference you care about).
Replications use common random numbers, which KN exploits: it works with the variance of paired differences, which CRN makes small. Compared with a fixed number of replications per scenario, clearly inferior scenarios stop early and the budget goes to the close contenders.
select_best ¶
select_best(model: Model, scenarios: Sequence[Scenario] | Mapping[str, Mapping[str, Any]], metric: str, *, indifference: float, minimize: bool = True, confidence: float = 0.95, n0: int = 10, max_replications: int = 500, seed: int = 0) -> SelectionResult
Select the best of scenarios on metric with procedure KN.
indifference (delta) is in the metric's units: differences smaller
than this are treated as ties, and any scenario within delta of the best
counts as a correct selection. Smaller delta means more replications.
If max_replications per scenario is reached first, the scenario with
the best mean among those remaining is returned with converged=False.
sensitivity ¶
Sensitivity analysis: which inputs move which outputs, and by how much.
Methods implemented (all simple and well understood):
- :func:
one_at_a_time- change one parameter at a time around a base point, with common random numbers, and report the change in each output plus a paired confidence interval and an elasticity. - :func:
finite_difference- local derivatived output / d parameterby central (or forward) differences with common random numbers. -
:func:
correlation_sensitivity- global screening from Monte Carlo samples: Pearson, Spearman rank correlation and standardised regression coefficients (SRC, with the regression R^2 so you can tell whether a linear summary is adequate). -
:func:
sobol_indices- variance-based global sensitivity: first-order and total-effect Sobol indices (Saltelli 2010 / Jansen estimators) with bootstrap confidence intervals. Captures interactions and non-linear effects at a cost ofN * (d + 2)model evaluations. - :func:
morris_screening- Morris elementary effects: a cheap global screen (r * (d + 1)evaluations) that separates negligible inputs from important ones (mu_star) and flags non-linearity or interactions (sigma). Use it to prune many inputs before a Sobol analysis.
Correlation measures are cheap screening tools that miss interactions and non-monotonic effects; use Sobol indices when that matters.
SensitivityResult ¶
SensitivityResult(rows: Sequence[SensitivityRow], info: Mapping[str, Any] | None = None)
ranking ¶
ranking(output: str | None = None, method: str | None = None) -> list[SensitivityRow]
Rows sorted by absolute effect size, largest first.
one_at_a_time ¶
one_at_a_time(model: Model, changes: Mapping[str, Sequence[Any]], outputs: Sequence[str], *, base: Mapping[str, Any] | None = None, replications: int = 10, seed: int = 0, confidence: float = 0.95) -> SensitivityResult
Vary each parameter in changes alone; report output change vs the base point.
value is the mean change in the output; detail includes the
elasticity (dy/y) / (dx/x) when both base values are non-zero numbers.
finite_difference ¶
finite_difference(model: Model, parameters: Sequence[str], outputs: Sequence[str], *, base: Mapping[str, Any] | None = None, relative_step: float = 0.05, replications: int = 10, seed: int = 0, scheme: Literal['central', 'forward'] = 'central', confidence: float = 0.95) -> SensitivityResult
Local derivatives by finite differences with common random numbers.
Integer parameters use a step of at least 1. The CI comes from the per-replication paired differences, so it reflects simulation noise only; a step that is too small amplifies noise, too large adds bias.
correlation_sensitivity ¶
correlation_sensitivity(mc: MonteCarloResult, outputs: Sequence[str] | None = None, *, methods: Sequence[Literal['pearson', 'spearman', 'src']] = ('pearson', 'spearman', 'src')) -> SensitivityResult
Global screening from Monte Carlo samples of uncertain inputs.
sobol_indices ¶
sobol_indices(model: Callable[..., Any] | Model, parameters: Mapping[str, DistributionLike | Mapping[str, Any]], n: int = 1024, *, outputs: Sequence[str] | None = None, seed: int = 0, fixed: Mapping[str, Any] | None = None, vectorized: bool = False, confidence: float = 0.95, n_bootstrap: int = 500) -> SensitivityResult
First-order and total-effect Sobol indices with bootstrap confidence intervals.
Uses the Saltelli (2010) design: two independent sample matrices A and
B of n rows plus, for each of the d parameters, A with that
column taken from B. First-order indices use the Saltelli (2010)
estimator, total effects the Jansen (1999) estimator. Inputs are assumed
independent. For simulation models every matrix row j uses the same
replication seed (common random numbers), which keeps simulation noise
from swamping the index estimates; noise still adds some bias, so use
enough replication length or average several runs per row if outputs are
very noisy.
value is the index estimate (method = sobol-first or
sobol-total). Estimates can fall slightly outside [0, 1] with small
n; the bootstrap interval shows how precise they are.
morris_screening ¶
morris_screening(model: Callable[..., Any] | Model, ranges: Mapping[str, tuple[float, float]], r: int = 20, *, levels: int = 4, outputs: Sequence[str] | None = None, seed: int = 0, fixed: Mapping[str, Any] | None = None, confidence: float = 0.95, n_bootstrap: int = 500) -> SensitivityResult
Morris elementary-effects screening over ranges ({name: (low, high)}).
Builds r random one-at-a-time trajectories on a levels-level grid
(Morris 1991), each costing d + 1 evaluations. Effects are measured
on inputs scaled to [0, 1], so they are comparable across parameters and
in output units. Per parameter and output it reports:
morris-mu_star- mean absolute effect (Campolongo et al. 2007), the importance measure, with a bootstrap CI over trajectories;morris-mu- mean signed effect (direction; can cancel out);morris-sigma- standard deviation of effects: large relative tomu_starmeans non-linear effects or interactions.
Integer model parameters are rounded. For simulation models every point of a trajectory uses the same replication seed (common random numbers).
warmup ¶
Warm-up (initial transient) detection.
Steady-state estimates are biased by the start-up period of a run (an empty
system has short queues). :func:mser implements MSER (White 1997; MSER-5
with batches of 5, Franklin & White 2008): delete the first d
observations where d minimises the marginal standard error of the
remaining mean. :func:suggest_warmup applies it to a model: it averages a
recorded time series across replications (reducing noise, as in Welch's
method), and reports the truncation point as a simulation time.
MSER is a heuristic. If the suggested truncation is close to the maximum allowed fraction, the run is probably too short to reach steady state - the result flags this rather than returning a misleading number.
MserResult
dataclass
¶
MserResult(truncation: int, statistic: float, n: int, batch_size: int, reliable: bool, note: str = '')
mser ¶
mser(values: Sequence[float] | FloatArray, batch_size: int = 5, max_fraction: float = 0.5) -> MserResult
MSER-batch_size truncation point for one output sequence (in observation order).
suggest_warmup ¶
suggest_warmup(model: Model, params: Mapping[str, Any] | None = None, *, series: str | None = None, replications: int = 5, seed: int = 0, duration: float | None = None, bins: int = 200, batch_size: int = 5) -> WarmupAdvice
Suggest a warm-up for model from a recorded time series.
series is a key of SimulationResult.series (default: the first
resource queue_length). Each replication is run without warm-up, the
series is time-averaged into bins equal bins, averaged across
replications, and MSER-batch_size picks the truncation bin.
input_uncertainty ¶
How much of the uncertainty comes from the input data?
Simulation confidence intervals usually cover simulation noise only. But
input distributions are fitted to finite data, and a different sample of
data would give different parameters. :func:input_uncertainty estimates
that extra uncertainty by bootstrapping: resample each data set, refit its
distribution, run the model, repeat. The spread of the results across
bootstrap fits, after removing simulation noise, is the input uncertainty
(Barton and Schruben 2001; Barton 2012).
interarrival times observed at a desk, used to set the arrival rate:¶
input_uncertainty(mmc, {"arrival_rate": InputData(gaps, "exponential", lambda d: 1 / d.mean)},¶
"resource.server.wait.mean")¶
InputData
dataclass
¶
InputData(values: Sequence[float], family: str = 'exponential', transform: Callable[[Distribution[Any]], Any] | None = None)
Observed data for one model parameter.
family is the distribution family to fit ("exponential",
"gamma", ...). transform turns the fitted distribution into the
parameter value (for example lambda d: 1 / d.mean for a rate); by
default the distribution itself is passed, for distribution parameters.
InputUncertaintyResult
dataclass
¶
InputUncertaintyResult(metric: str, point_estimate: float, simulation_half_width: float, ci_low: float, ci_high: float, input_variance: float, simulation_variance: float, bootstrap_means: list[float], replications: int, confidence: float)
input_share
property
¶
input_share: float
Share of the estimator's variance caused by finite input data.
input_uncertainty ¶
input_uncertainty(model: Model, inputs: Mapping[str, InputData], metric: str, *, n_bootstrap: int = 30, replications: int = 5, seed: int = 0, fixed: Mapping[str, Any] | None = None, confidence: float = 0.95) -> InputUncertaintyResult
Bootstrap the input data to see how much it moves metric.
For each of n_bootstrap rounds every data set in inputs is
resampled with replacement and refitted; the model then runs
replications times with common seeds. The total variance of the
round means is split into simulation noise (mean within-round variance
/ replications) and input variance (the rest). The returned interval is
the bootstrap percentile interval, which includes both.
rare_events ¶
Rare events: probabilities of extreme outcomes, with honest intervals.
"How likely is it that the hospital overflows?" is a question about a small
probability. :func:rare_event_probability runs replications, counts how
often metric exceeds threshold and reports an exact (Clopper-Pearson)
interval plus the number of replications needed for a target relative
error. When the event is too rare to observe often, it also fits a
generalised Pareto distribution to the upper tail (peaks over threshold,
extreme value theory) and extrapolates the probability, with a bootstrap
interval. Extrapolation assumes the tail keeps its shape beyond the data,
so treat it as an informed estimate rather than a measurement.
gpd_tail_probability ¶
gpd_tail_probability(values: Sequence[float] | ndarray[Any, Any], threshold: float, *, tail_quantile: float = 0.9, n_bootstrap: int = 200, confidence: float = 0.95, seed: int = 0) -> TailEstimate
P(X > threshold) from a generalised Pareto fit to the values above a high quantile.
clopper_pearson ¶
clopper_pearson(hits: int, n: int, confidence: float = 0.95) -> tuple[float, float]
Exact binomial confidence interval.
rare_event_probability ¶
rare_event_probability(model: Model, metric: str, threshold: float, *, replications: int = 200, seed: int = 0, params: Mapping[str, Any] | None = None, tail: bool = True, confidence: float = 0.95) -> RareEventResult
Estimate P(metric > threshold) from replications independent runs.