Skip to content

feat: add sensitivity analysis - Morris screening and Saltelli Sobol indices#20

Merged
m2papierz merged 21 commits into
masterfrom
feat/sensitivity
Jun 14, 2026
Merged

feat: add sensitivity analysis - Morris screening and Saltelli Sobol indices#20
m2papierz merged 21 commits into
masterfrom
feat/sensitivity

Conversation

@m2papierz

Copy link
Copy Markdown
Contributor

What

New crate pirx-sensitivity implementing global sensitivity analysis: parameter space definition, hardware model mutation, Morris elementary effects screening, and Saltelli's algorithm for first-order and total-order Sobol indices with bootstrap confidence intervals.

Why

Researchers cannot answer "which hardware parameter should I optimize?" without systematic sensitivity analysis. Aggregate resource estimators give point estimates; Pirx gives temporal profiles — but without quantifying which parameters drive that profile, users are guessing. Morris screening identifies the 3-4 parameters that matter in 30 seconds. Sobol indices then decompose output variance into per-parameter contributions with confidence intervals, revealing both direct effects (S₁) and parameter interactions (Sₜ − S₁).

How

Crate architecture. pirx-sensitivity depends on pirx-core, pirx-hw, pirx-ir. Nothing depends on it. The crate wraps the engine — it never touches DES internals, only calls Engine::new / run through evaluate_point.

Parameter space. ParameterSpace maps a unit hypercube [0,1]^P to physical hardware parameters. Three kinds: Continuous (linear interpolation), Integer (nearest), OddInteger (uniform mapping to odd integer set — no rounding bias). 20 known sweepable parameters covering all mutable HardwareModel fields. Mutation via apply_single (no validation) + mutate_hw_multi (validate once after all overrides).

Evaluation. evaluate_point maps unit point → physical params → mutated HW → engine run → metric extraction. Supports single-run and Monte Carlo modes (configurable mc_replicas for noise reduction on stochastic models). Seed space is partitioned: point N uses seeds [base + N×replicas, base + (N+1)×replicas).

Morris screening. Campolongo (2007) OAT trajectories with restricted base points ([Δ, 1−Δ] per dimension — no boundary clamping artifacts). levels validated as even ≥ 4 for grid alignment. Elementary effects extracted from parallel-evaluated trajectory points. Aggregation: μ, μ* (primary importance), σ (nonlinearity/interaction indicator) with Bessel-corrected sample std.

Sobol indices. Joe-Kuo (2010) Sobol sequence generator (~150 lines, direction numbers for 30 dimensions). Saltelli matrix construction: A, B, P×AB_i from 2N quasi-random points. Jansen estimator for S₁ and Sₜ. All N×(P+2) evaluations parallelized via rayon. Allocation-free variance computation. Bootstrap CIs: paired resampling with deterministic RNG (reproducible independent of thread scheduling).

pirx-core change. extract_summary exposed as pub fn trace_summary for single-run metric extraction without Monte Carlo overhead.

CLI. pirx sensitivity morris and pirx sensitivity sobol subcommands. TOML sweep configuration. Human-readable summary to stderr with category annotations.

Testing

  • make ci passes locally (fmt + clippy + test + audit)
  • New behavior has tests

Checklist

  • PR description explains why, not just what
  • No new unwrap()/expect() in production code
  • No new allocations in the simulation hot loop
  • Crate boundaries respected — pirx-sensitivity depends on pirx-core/pirx-hw/pirx-ir, nothing depends on it

New crate for parameter sensitivity analysis (Morris/Sobol) over
hardware model parameter space. Workspace member registered, module
stubs in place, SensitivityError enum covers parameter validation,
hardware/engine/monte-carlo propagation, and method-specific checks.
…ping

ParameterDef/ParameterKind/ParameterSpace types define the sweepable
parameter dimensions. Validated construction rejects empty spaces,
duplicates, invalid ranges, and kind-specific bound violations.
map_unit_to_physical supports continuous, integer, and uniform
odd-integer discrete mappings. validate_against_hw checks factory and
routing type compatibility against KNOWN_PARAMS registry. 17 integration
tests cover mapping correctness, clamping, and all rejection paths.
Bridge between unit-hypercube sample points and concrete HardwareModel
instances. mutate_hw applies a single override, mutate_hw_multi applies
all parameters from a ParameterSpace, both validate after mutation.
Add a public wrapper around the private extract_summary function
so pirx-sensitivity can extract ReplicaSummary from a single engine
run without going through the full Monte Carlo pipeline.
OutputMetric enum maps ReplicaSummary fields to f64 for sensitivity
analysis. evaluate_point is the workhorse: unit-hypercube → physical
params → HW mutation → engine run → metric extraction, with
deterministic seed partitioning across points and single-run / MC modes.
SensitivityConfig with SweepConfig, MorrisConfig, and SobolConfig
structs. Parses metric, mc_replicas, base_seed, parameter definitions
with kind validation, and optional method-specific sections with
serde defaults for confidence and bootstrap_resamples.
@codspeed-hq

codspeed-hq Bot commented Jun 14, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 15 untouched benchmarks


Comparing feat/sensitivity (2c61a6f) with master (d674fd6)

Open in CodSpeed

m2papierz added 15 commits June 14, 2026 11:55
…and trajectory generation

Add MorrisConfig::validate(), MorrisResult/MorrisParameterResult types,
delta_value() step-size function, and generate_trajectories() with
adaptive sign selection to guarantee bounds without clamping.
Implement evaluate_trajectories (parallel via rayon), extract_elementary_effects,
aggregate_morris (Bessel-corrected σ), and morris_screening entry point.
Eight synthetic-function tests verify EE correctness, ranking, sign invariants,
and Bessel's correction without requiring engine runs.
Integration tests run full morris_screening through the real engine with
pirx-testkit circuits. Property tests (proptest, 30 cases) verify triangle
inequality, sigma non-negativity, and evaluation count invariants.
Ignored-by-default test comparing raw engine runs against evaluate_point
to verify parameter mapping and HW mutation overhead stays under 1%.
Wire Morris screening into the CLI as `pirx sensitivity morris`
with sweep TOML config, human-readable stderr summary (bar chart,
category classification, Sobol focus suggestions), and JSON output.
Export SensitivityConfig and parse_sensitivity_config from
pirx-sensitivity for CLI consumption.
Joe-Kuo (2010) direction numbers for dimensions 2..31, Gray code
construction with van der Corput for dimension 1. Returns fallible
Result for non-power-of-2 N or out-of-range dimensions.
…uation

SobolConfig validation, A/B/AB_i matrix construction from Sobol
sequences, and batched parallel point evaluation via rayon for
variance-based sensitivity analysis.
Implement variance-based index estimation (S₁, Sₜ) via the Jansen
estimator, fix Saltelli matrix construction to use column-split for
independent A/B matrices, and add synthetic-function validation tests.
…lidation

Paired resampling preserves Saltelli correlation structure. Bootstrap RNG
seeded deterministically from base_seed for reproducibility independent of
evaluation parallelism. Ishigami test validates full pipeline against
analytical indices at N=4096.
Wire up the full Sobol variance-based sensitivity pipeline: public
sobol_analysis() orchestrating Saltelli matrices, parallel evaluation,
Jansen indices, and bootstrap CIs. Add `pirx sensitivity sobol` CLI
subcommand with human-readable summary (ranked parameters, S1/ST with
CIs, interaction labels, additivity check). Integration tests validate
dominance ordering, determinism, evaluation count, and zero-variance
edge case. Property tests cover ST >= S1, index bounds, and CI ordering.
…laky test

- Add #![forbid(unsafe_code)] to pirx-sensitivity crate
- Remove unnecessary #![allow(dead_code)] from morris module
- Add InsufficientSamples error variant for n_samples < 64 (was
  misreporting as NotPowerOfTwo for valid powers of two)
- Fix SobolConfig confidence validation to reject 0.0 and NaN
- Widen Sobol property test tolerance for N=128 Jansen estimator noise
- Add missing test for negative parameter bound rejection
…I surface

Dual-strategy Saltelli matrix construction: column-split for dim≤15
(quasi-independent via Sobol cross-dimension), LHS-based row-split for
dim 16–30 (Latin Hypercube provides stratified uniformity with true
independence from A matrix, required by Jansen estimator).

- Make MAX_DIM pub(crate), add TooManyParameters error variant
- Early rejection for dim > 30 in sobol_analysis entry point
- Change validate() to take &self (Copy types, no consumption needed)
- Add #[must_use] to SobolResult, MorrisResult and ranking methods
- Tighten property test tolerances with N=1024 for stable estimates
- Add integration test for 16-parameter row-split with determinism check
…clean up Morris grid

Restrict sobol_sequence and KNOWN_PARAMS to pub(crate) — these are
implementation details not needed by consumers. Add dimension check to
map_point() returning DimensionMismatch instead of silently misaligning.
Replace float-accumulation loop in Morris trajectory generation with
integer-indexed grid construction. Correct Sobol docstring to attribute
S₁ estimator to Saltelli (2010) and Sₜ to Jansen (1999).
…ests

Test that map_point rejects wrong-length input with DimensionMismatch.
Add unit test asserting every KNOWN_PARAMS entry is handled by
apply_single — catches drift between the parameter list and mutate logic.
Add pirx-sensitivity entries to CHANGELOG under [Unreleased]: Morris
screening, Sobol analysis, parameter space, sweep config, CLI
subcommands, and trace_summary public API. Update README to move
sensitivity analysis from planned to working, add the crate to the
architecture listing, and bump crate count from six to seven.
@m2papierz m2papierz merged commit 9974222 into master Jun 14, 2026
11 of 12 checks passed
@m2papierz m2papierz deleted the feat/sensitivity branch June 14, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant