1"""Monte-Carlo result wrappers for the statistic module.
3The C++ statistic layer can sample nuisance parameters, propagate them through
4the observable model, summarize the resulting observable samples and estimate an
5observable covariance matrix. This module exposes Python dataclasses mirroring
6those C++ result structures.
9from __future__
import annotations
11from dataclasses
import dataclass, field
12from typing
import Dict, List, Mapping, Sequence
14from pyhyperiso.phyperiso.pyhyperiso
import statistic
as _cpp_stat
17from pyhyperiso.core.Math.RealMatrix
import Matrix
18from pyhyperiso.core.Statistic.GaussianSummary
import GaussianSummary
21ObsSample = Dict[BinnedObservableId, float]
22"""One Monte-Carlo observable vector indexed by binned observable id."""
24ObsSamples = List[ObsSample]
25"""Collection of Monte-Carlo observable vectors."""
27NuisanceSample = Dict[ParamId, float]
28"""One sampled nuisance-parameter vector indexed by parameter id."""
30NuisanceSamples = List[NuisanceSample]
31"""Collection of nuisance-parameter samples."""
35 """Validate a wrapper argument type and return the original value."""
36 if not isinstance(value, typ):
37 raise TypeError(f
"{name} must be {typ.__name__}, received {type(value)!r}.")
42 """Convert a Python binned observable id to C++."""
43 return _require(obs, BinnedObservableId,
"BinnedObservableId").to_cpp()
47 """Convert a Python parameter id to C++."""
48 return _require(pid, ParamId,
"ParamId").to_cpp()
52 """Convert one C++ observable sample row to a Python mapping."""
53 return {BinnedObservableId.from_cpp(k): float(v)
for k, v
in dict(row).items()}
57 """Convert one Python observable sample row to a C++ mapping."""
62 """Convert one C++ nuisance sample row to a Python mapping."""
63 return {ParamId.from_cpp(k): float(v)
for k, v
in dict(row).items()}
67 """Convert one Python nuisance sample row to a C++ mapping."""
73 """Options controlling Monte-Carlo uncertainty propagation.
76 draws: Number of accepted Monte-Carlo predictions to generate.
77 skew_abs_threshold: Absolute skewness threshold below which an
78 observable distribution is treated as symmetric.
79 covariance_ridge_rel: Relative diagonal ridge added before covariance
81 covariance_ridge_abs: Absolute minimum diagonal ridge.
82 retry_failed_predictions: Whether failed model predictions should be
84 max_prediction_failures: Maximum number of rejected predictions before
85 the backend propagates the failure.
89 skew_abs_threshold: float = 0.2
90 covariance_ridge_rel: float = 1e-8
91 covariance_ridge_abs: float = 1e-12
92 retry_failed_predictions: bool =
True
93 max_prediction_failures: int = 20000
97 """Create Python options from a bound C++ ``MCConfig``."""
99 draws=int(cpp_obj.draws),
100 skew_abs_threshold=float(cpp_obj.skew_abs_threshold),
101 covariance_ridge_rel=float(cpp_obj.covariance_ridge_rel),
102 covariance_ridge_abs=float(cpp_obj.covariance_ridge_abs),
103 retry_failed_predictions=bool(cpp_obj.retry_failed_predictions),
104 max_prediction_failures=int(cpp_obj.max_prediction_failures),
108 """Convert this config to a bound C++ ``MCConfig`` object."""
109 cpp = _cpp_stat.MCConfig()
110 cpp.draws = int(self.
draws)
121 """Observable covariance estimated from Monte-Carlo samples.
124 ids: Observable ordering used by ``mean``, ``covariance`` and
126 mean: Sample mean for each observable in ``ids`` order.
127 covariance: Regularized sample covariance matrix.
128 covariance_inv: Inverse covariance matrix used by the chi-square backend.
131 ids: List[BinnedObservableId] = field(default_factory=list)
132 mean: List[float] = field(default_factory=list)
133 covariance: Matrix |
None =
None
134 covariance_inv: Matrix |
None =
None
137 def from_cpp(cls, cpp_obj) -> "MCObservableCovariance":
138 """Create a Python covariance object from C++ output."""
140 ids=[BinnedObservableId.from_cpp(x)
for x
in cpp_obj.ids],
141 mean=[float(x)
for x
in cpp_obj.mean],
142 covariance=Matrix.from_cpp(cpp_obj.covariance),
143 covariance_inv=Matrix.from_cpp(cpp_obj.covariance_inv),
147 """Convert this covariance wrapper to a bound C++ object."""
148 cpp = _cpp_stat.MCObservableCovariance()
150 cpp.mean = [float(x)
for x
in self.
mean]
160 """Raw Monte-Carlo realization.
163 sampled_obss: Accepted observable predictions, one mapping per draw.
164 sampled_params: Nuisance-parameter values used for each accepted draw.
167 sampled_obss: ObsSamples = field(default_factory=list)
168 sampled_params: NuisanceSamples = field(default_factory=list)
172 """Create a Python realization from a bound C++ ``MCRealization``."""
179 """Convert this realization to the bound C++ representation."""
180 cpp = _cpp_stat.MCRealization()
188 """Complete Monte-Carlo uncertainty-propagation result.
191 mc_real: Raw accepted samples and nuisance draws.
192 summary: Per-observable Gaussian or split-Gaussian summaries.
193 covariance: Observable covariance estimate and inverse.
196 mc_real: MCRealization = field(default_factory=MCRealization)
197 summary: List[GaussianSummary] = field(default_factory=list)
198 covariance: MCObservableCovariance = field(default_factory=MCObservableCovariance)
202 """Create a Python result from a bound C++ ``MCResult``."""
204 mc_real=MCRealization.from_cpp(cpp_obj.mc_real),
205 summary=[GaussianSummary.from_cpp(gs)
for gs
in cpp_obj.summary],
206 covariance=MCObservableCovariance.from_cpp(cpp_obj.covariance),
210 """Convert this result to the bound C++ representation."""
211 cpp = _cpp_stat.MCResult()
213 cpp.summary = [gs.to_cpp()
for gs
in self.
summary]
219 samples: Sequence[Mapping[BinnedObservableId, float]],
220) -> List[BinnedObservableId]:
221 """Return the observable ordering inferred from the first sample.
224 samples: Sequence of observable sample mappings.
227 Observable identifiers in the backend order inferred from the first row.
230 Exception: Propagates backend errors, for example when ``samples`` is
235 BinnedObservableId.from_cpp(x)
236 for x
in _cpp_stat.covariance_ids_from_first_sample(cpp_samples)
241 samples: Sequence[Mapping[BinnedObservableId, float]],
242 ids: Sequence[BinnedObservableId],
243 ridge_rel: float = 1e-8,
244 ridge_abs: float = 1e-12,
245) -> MCObservableCovariance:
246 """Estimate an observable covariance matrix from Monte-Carlo samples.
249 samples: Observable samples produced by Monte-Carlo propagation.
250 ids: Observable ordering to use for the covariance matrix.
251 ridge_rel: Relative diagonal ridge added before matrix inversion.
252 ridge_abs: Absolute minimum diagonal ridge.
255 A ``MCObservableCovariance`` containing the mean, covariance and inverse
259 >>> ids = covariance_ids_from_first_sample(samples)
260 >>> cov = covariance_from_obs_samples(samples, ids)
261 >>> len(cov.ids) == len(cov.mean)
266 return MCObservableCovariance.from_cpp(
267 _cpp_stat.covariance_from_obs_samples(
268 cpp_samples, cpp_ids, float(ridge_rel), float(ridge_abs)
279 "MCObservableCovariance",
282 "covariance_ids_from_first_sample",
283 "covariance_from_obs_samples",
bool retry_failed_predictions
float covariance_ridge_rel
"MCConfig" from_cpp(cls, cpp_obj)
float covariance_ridge_abs
int max_prediction_failures
"MCObservableCovariance" from_cpp(cls, cpp_obj)
"MCRealization" from_cpp(cls, cpp_obj)
NuisanceSamples sampled_params
MCObservableCovariance covariance
"MCResult" from_cpp(cls, cpp_obj)
MCObservableCovariance covariance_from_obs_samples(Sequence[Mapping[BinnedObservableId, float]] samples, Sequence[BinnedObservableId] ids, float ridge_rel=1e-8, float ridge_abs=1e-12)
NuisanceSample _nuisance_sample_from_cpp(row)
_cpp_binned_observable_id(BinnedObservableId obs)
ObsSample _obs_sample_from_cpp(row)
_cpp_param_id(ParamId pid)
List[BinnedObservableId] covariance_ids_from_first_sample(Sequence[Mapping[BinnedObservableId, float]] samples)
_require(value, typ, str name)
_nuisance_sample_to_cpp(Mapping[ParamId, float] row)
_obs_sample_to_cpp(Mapping[BinnedObservableId, float] row)