Skip to content

feat(gds-psuu): sensitivity analysis framework #112

@rororowyourboat

Description

@rororowyourboat

Summary

Add a pluggable sensitivity analysis framework to gds-psuu. Sensitivity analysis answers "which parameters matter?" — a fundamentally different analytical mode from optimization ("what's optimal?"), but composing with the same primitives (ParameterSpace, KPI, Evaluator).

Motivation

PSUU is about search under uncertainty. Knowing which parameters drive variance in KPIs is as important as finding optimal values. Without sensitivity analysis, users must sweep the full space and eyeball results. Formal sensitivity indices (Sobol, Morris) provide rigorous, quantitative answers.

Design

Follows the pluggable analyzer pattern (mirrors how G-checks and SC-checks are separate registries on the same IR):

class Analyzer(ABC):
    """Base class for sensitivity analyzers."""
    def analyze(self, evaluator: Evaluator, space: ParameterSpace) -> SensitivityResult: ...

class SensitivityResult(BaseModel):
    """Per-parameter sensitivity indices."""
    indices: dict[str, dict[str, float]]  # param -> {"S1": ..., "ST": ...}
    method: str

Planned analyzers

  1. One-at-a-time (OAT) — no extra dependencies, good baseline
  2. Morris method (elementary effects) — screening method for identifying non-influential parameters
  3. Sobol indices — variance-based decomposition (optional dependency on SALib)

API sketch

from gds_psuu.sensitivity import OATAnalyzer, SensitivityResult

analyzer = OATAnalyzer(n_levels=4)
result: SensitivityResult = analyzer.analyze(evaluator, space)
result.indices["growth_rate"]  # {"mean_effect": 0.82, "std_effect": 0.15}
result.ranking("final_pop")     # ["growth_rate", "decay_rate", ...]

Structure

gds_psuu/
  sensitivity/
    __init__.py
    base.py          # Analyzer ABC + SensitivityResult
    oat.py           # One-at-a-time analyzer
    morris.py        # Morris method
    sobol.py         # Sobol indices (optional SALib dep)

Acceptance criteria

  • Analyzer ABC with analyze()SensitivityResult
  • OAT analyzer with no extra dependencies
  • Morris method analyzer
  • SensitivityResult with ranking() and to_dataframe()
  • Tests with >90% coverage on new code
  • Optional SALib integration for Sobol (behind [sensitivity] extra)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions