Source code for everwillow._src.inference.hypotest.results
"""Result containers for hypothesis testing.
These modules hold the results of hypothesis tests.
"""
from __future__ import annotations
import typing as tp
import equinox as eqx
from jaxtyping import Array
__all__ = [
"BandValues",
"ExpectedBands",
"HypoTestResult",
"TestStatResult",
"ToyResult",
]
[docs]
class TestStatResult(eqx.Module):
r"""Result of computing a test statistic.
Attributes:
value: Test statistic value.
test: POI value being tested (:math:`\mu`).
q_asimov: Test statistic evaluated on Asimov data. None if not computed.
extras: Arbitrary additional data (e.g., fits, mu_hat).
"""
value: Array
test: Array
q_asimov: Array | None = None
extras: dict[str, tp.Any] = eqx.field(default_factory=dict)
[docs]
class ToyResult(eqx.Module):
"""Raw output from toy generation.
Contains the test statistic arrays under both hypotheses,
decoupled from any particular p-value computation method.
Attributes:
q_null: Test statistic values under the tested hypothesis (poi_test).
q_alt: Test statistic values under the alternative hypothesis (poi_alt).
None if poi_alt was not provided to the ToyGenerator.
"""
q_null: Array
q_alt: Array | None = None
[docs]
class BandValues(eqx.Module):
r"""Scalar values at standard :math:`\pm N\sigma` fluctuation bands.
Supports iteration via ``for name, value in bv``, indexing via
``bv["median"]``, and ``len(bv) == 5``. ``dict(bv)`` produces a
``{name: value}`` mapping, and ``BandValues(**dict(bv))`` roundtrips.
Attributes:
minus_2sigma: Value at :math:`-2\sigma` fluctuation.
minus_1sigma: Value at :math:`-1\sigma` fluctuation.
median: Value at median (:math:`0\sigma`).
plus_1sigma: Value at :math:`+1\sigma` fluctuation.
plus_2sigma: Value at :math:`+2\sigma` fluctuation.
"""
_NAMES: tp.ClassVar[tuple[str, ...]] = (
"minus_2sigma",
"minus_1sigma",
"median",
"plus_1sigma",
"plus_2sigma",
)
minus_2sigma: Array
minus_1sigma: Array
median: Array
plus_1sigma: Array
plus_2sigma: Array
def __iter__(self) -> tp.Iterator[tuple[str, Array]]:
for name in self._NAMES:
yield name, getattr(self, name)
def __getitem__(self, key: str) -> Array:
if key not in self._NAMES:
raise KeyError(key)
return getattr(self, key)
def __len__(self) -> int:
return 5
[docs]
class ExpectedBands(eqx.Module):
r"""Expected quantities at standard sigma bands.
All derived quantities (CLs, significance) are eagerly computed
at construction time so access is a simple attribute lookup.
Attributes:
null_pvalue: p-value under null hypothesis (:math:`p_\mu`) at each band.
alt_pvalue: p-value under alternative hypothesis (:math:`\text{CL}_b`) at each band.
cl_s: :math:`\text{CL}_s = p_\text{null}/p_\text{alt}` at each band.
null_sig: Null significance :math:`\Phi^{-1}(1 - p_\text{null})` at each band.
alt_sig: Alternative significance :math:`\Phi^{-1}(1 - p_\text{alt})` at each band.
"""
null_pvalue: BandValues
alt_pvalue: BandValues
cl_s: BandValues
null_sig: BandValues
alt_sig: BandValues
[docs]
class HypoTestResult(eqx.Module):
"""Result of a hypothesis test.
Attributes:
q_obs: Observed test statistic value.
pnull: p-value under the tested hypothesis (poi_test).
palt: p-value under the alternative hypothesis (poi_alt / background-only).
test_stat_result: Full test statistic result with fit information.
"""
q_obs: Array
pnull: Array | None
palt: Array | None
test_stat_result: TestStatResult