Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
StatisticConfig.py
Go to the documentation of this file.
1"""High-level configuration object for the statistic workflow.
2
3``StatisticConfig`` is the Python entry point for configuring the bound C++
4statistic manager. It mirrors the fields exposed by the pybind11
5``StatisticConfig`` binding.
6"""
7
8from __future__ import annotations
9
10from dataclasses import dataclass, field
11from enum import Enum
12from typing import Dict, Optional, Tuple
13
14from pyhyperiso.phyperiso.pyhyperiso import statistic as st
15from pyhyperiso.core.Common.ParamId import ParamId
16from pyhyperiso.core.Statistic.Copula import CopulaKind
17from pyhyperiso.core.Statistic.ExperimentObs import ExperimentObs
18from pyhyperiso.core.Statistic.MarginalConfig import MarginalKind
19
20
21@dataclass(frozen=True)
23 """Immutable progress snapshot produced by the C++ statistic pipeline."""
24
25 phase: str = "idle"
26 message: str = ""
27 completed: int = 0
28 total: int = 0
29 attempts: int = 0
30 failures: int = 0
31 fraction: float = 0.0
32 elapsed_seconds: float = 0.0
33 eta_seconds: float = -1.0
34 finished: bool = False
35 sequence: int = 0
36
37
39 """Thread-safe bridge exposing C++ progress to Python frontends."""
40
41 def __init__(self):
42 self._cpp_obj = st.StatisticProgressMonitor()
43
44 def reset(
45 self, phase: str = "preparing", message: str = "Preparing statistic workflow"
46 ) -> None:
47 """Reset progress reporting and set the initial phase and message."""
48 self._cpp_obj.reset(str(phase), str(message))
49
51 self,
52 phase: str,
53 message: str,
54 fraction: float,
55 *,
56 completed: int = 0,
57 total: int = 0,
58 eta_seconds: float = -1.0,
59 finished: bool = False,
60 ) -> None:
61 """Publish a progress update to Python and graphical frontends."""
63 str(phase),
64 str(message),
65 float(fraction),
66 int(completed),
67 int(total),
68 float(eta_seconds),
69 bool(finished),
70 )
71
72 def snapshot(self) -> StatisticProgressSnapshot:
73 """Return an immutable snapshot of the latest progress event."""
74 event = self._cpp_obj.snapshot()
76 phase=str(event.phase),
77 message=str(event.message),
78 completed=int(event.completed),
79 total=int(event.total),
80 attempts=int(event.attempts),
81 failures=int(event.failures),
82 fraction=float(event.fraction),
83 elapsed_seconds=float(event.elapsed_seconds),
84 eta_seconds=float(event.eta_seconds),
85 finished=bool(event.finished),
86 sequence=int(event.sequence),
87 )
88
89
91 """Likelihood backend used by the statistic manager.
92
93 Attributes:
94 PROFILED_NUISANCE: Full likelihood backend with explicit nuisance
95 parameters, nuisance copula and experimental-observable copula.
96 CHI2_MC_COVARIANCE: Chi-square backend without explicit nuisance
97 coordinates. The covariance is estimated from Monte-Carlo theory
98 propagation and combined with the experimental covariance.
99 """
100
101 PROFILED_NUISANCE = st.StatisticLikelihoodMode.PROFILED_NUISANCE
102 CHI2_MC_COVARIANCE = st.StatisticLikelihoodMode.CHI2_MC_COVARIANCE
103
104 def to_cpp(self):
105 """Convert this Python enum wrapper to the bound C++ enum.
106
107 Returns:
108 Bound C++ ``StatisticLikelihoodMode`` value.
109 """
110 return self.value
111
112
113def _require(value, typ, name: str):
114 """Validate that a value has the expected wrapper type.
115
116 Args:
117 value: Value to validate.
118 typ: Expected Python type.
119 name: Human-readable argument name.
120
121 Returns:
122 The validated value.
123
124 Raises:
125 TypeError: If ``value`` is not an instance of ``typ``.
126 """
127 if not isinstance(value, typ):
128 raise TypeError(f"{name} must be {typ.__name__}, got {type(value).__name__}.")
129 return value
130
131
132def _cpp_param_id(pid: ParamId):
133 """Convert a Python ``ParamId`` wrapper to C++.
134
135 Args:
136 pid: Python parameter identifier.
137
138 Returns:
139 Bound C++ ``ParamId`` value.
140 """
141 return _require(pid, ParamId, "ParamId").to_cpp()
142
143
144def _cpp_marginal_kind(kind: MarginalKind):
145 """Convert a Python ``MarginalKind`` wrapper to C++.
146
147 Args:
148 kind: Python marginal kind enum.
149
150 Returns:
151 Bound C++ ``MarginalKind`` value.
152 """
153 return _require(kind, MarginalKind, "MarginalKind").to_cpp()
154
155
156def _cpp_copula_kind(kind: CopulaKind):
157 """Convert a Python ``CopulaKind`` wrapper to C++.
158
159 Args:
160 kind: Python copula kind enum.
161
162 Returns:
163 Bound C++ ``CopulaKind`` value.
164 """
165 return _require(kind, CopulaKind, "CopulaKind").to_cpp()
166
167
168def _cpp_likelihood_mode(mode: StatisticLikelihoodMode):
169 """Convert a Python likelihood mode wrapper to C++.
170
171 Args:
172 mode: Python likelihood backend enum.
173
174 Returns:
175 Bound C++ ``StatisticLikelihoodMode`` value.
176 """
177 return _require(
178 mode,
179 StatisticLikelihoodMode,
180 "StatisticLikelihoodMode",
181 ).to_cpp()
182
183
184def _cpp_experiment_obs(obs: ExperimentObs):
185 """Convert a Python ``ExperimentObs`` wrapper to C++.
186
187 Args:
188 obs: Python experimental-observable wrapper.
189
190 Returns:
191 Bound C++ ``ExperimentObs`` value.
192 """
193 return _require(obs, ExperimentObs, "ExperimentObs").to_cpp()
194
195
196@dataclass
198 """Expert configuration for fitting, nuisance pruning and covariance logic.
199
200 ``StatisticConfig`` should remain the simple user-facing object. Fields that
201 affect minimization internals, likelihood backend selection, covariance
202 regularization or nuisance-preselection live here.
203 """
204
205 override_nuisance_marginals: Dict[ParamId, MarginalKind] = field(default_factory=dict)
206 override_exp_data_marginals: Dict[ExperimentObs, MarginalKind] = field(default_factory=dict)
207
208 nuisance_copula_type: CopulaKind = CopulaKind.GAUSSIAN
209 exp_data_copula_type: CopulaKind = CopulaKind.GAUSSIAN
210
211 MLE_max_iter: int = 500
212 MLE_tol: float = 1e-8
213 MLE_strategy: int = 2
214 MLE_run_hesse: bool = True
215 MLE_request_minos: bool = False
216 MLE_verbose: bool = False
217
218 nuisance_relevance_cutoff: float = 1e-8
219 nuisance_sensitivity_pruning: bool = True
220 nuisance_sensitivity_probe_sigmas: float = 1.0
221 nuisance_sensitivity_rel_cutoff: float = 1e-6
222 nuisance_sensitivity_abs_cutoff: float = 1e-12
223 nuisance_sensitivity_scale_floor: float = 1e-3
224 nuisance_sensitivity_contexts: int = 2
225 nuisance_sensitivity_context_sigma: float = 0.35
226 nuisance_sensitivity_seed: int = 12345
227 nuisance_sensitivity_keep_on_failure: bool = True
228
229 fit_parameter_sensitivity_check: bool = True
230 fit_parameter_sensitivity_probe_fraction: float = 0.05
231 fit_parameter_sensitivity_rel_cutoff: float = 1e-10
232 fit_parameter_sensitivity_abs_cutoff: float = 1e-12
233 fit_parameter_sensitivity_keep_on_failure: bool = True
234
235 MLE_trace_first_evals: bool = False
236 MLE_trace_max_evals: int = 25
237 MLE_allow_profile_hessian_fallback: bool = True
238 MLE_profile_hessian_step_scale: float = 1.0
239 MLE_profile_hessian_eig_floor_rel: float = 1e-8
240
241 likelihood_mode: StatisticLikelihoodMode = StatisticLikelihoodMode.CHI2_MC_COVARIANCE
242 chi2_covariance_ridge_rel: float = 1e-8
243 chi2_covariance_ridge_abs: float = 1e-12
244
245 # Advanced MC/decay thread arbitration. MC_threads itself stays on
246 # StatisticConfig because it is the main user-facing parallelism knob.
247 MC_force_decay_threads_to_one: bool = True
248 MC_forced_decay_threads: int = 1
249
250 def to_cpp(self):
251 """Convert this Python config to the bound C++ ``AdvancedStatisticConfig``."""
252 cpp = st.AdvancedStatisticConfig()
253 cpp.override_nuisance_marginals = {
255 for pid, kind in self.override_nuisance_marginals.items()
256 }
257 cpp.override_exp_data_marginals = {
259 for obs, kind in self.override_exp_data_marginals.items()
260 }
261 cpp.nuisance_copula_type = _cpp_copula_kind(self.nuisance_copula_type)
262 cpp.exp_data_copula_type = _cpp_copula_kind(self.exp_data_copula_type)
263 cpp.MLE_max_iter = int(self.MLE_max_iter)
264 cpp.MLE_tol = float(self.MLE_tol)
265 cpp.MLE_strategy = int(self.MLE_strategy)
266 cpp.MLE_run_hesse = bool(self.MLE_run_hesse)
267 cpp.MLE_request_minos = bool(self.MLE_request_minos)
268 cpp.MLE_verbose = bool(self.MLE_verbose)
269 cpp.nuisance_relevance_cutoff = float(self.nuisance_relevance_cutoff)
270 cpp.nuisance_sensitivity_pruning = bool(self.nuisance_sensitivity_pruning)
271 cpp.nuisance_sensitivity_probe_sigmas = float(self.nuisance_sensitivity_probe_sigmas)
272 cpp.nuisance_sensitivity_rel_cutoff = float(self.nuisance_sensitivity_rel_cutoff)
273 cpp.nuisance_sensitivity_abs_cutoff = float(self.nuisance_sensitivity_abs_cutoff)
274 cpp.nuisance_sensitivity_scale_floor = float(self.nuisance_sensitivity_scale_floor)
275 cpp.nuisance_sensitivity_contexts = int(self.nuisance_sensitivity_contexts)
276 cpp.nuisance_sensitivity_context_sigma = float(self.nuisance_sensitivity_context_sigma)
277 cpp.nuisance_sensitivity_seed = int(self.nuisance_sensitivity_seed)
278 cpp.nuisance_sensitivity_keep_on_failure = bool(self.nuisance_sensitivity_keep_on_failure)
279 cpp.fit_parameter_sensitivity_check = bool(self.fit_parameter_sensitivity_check)
280 cpp.fit_parameter_sensitivity_probe_fraction = float(
282 )
283 cpp.fit_parameter_sensitivity_rel_cutoff = float(self.fit_parameter_sensitivity_rel_cutoff)
284 cpp.fit_parameter_sensitivity_abs_cutoff = float(self.fit_parameter_sensitivity_abs_cutoff)
285 cpp.fit_parameter_sensitivity_keep_on_failure = bool(
287 )
288 cpp.MLE_trace_first_evals = bool(self.MLE_trace_first_evals)
289 cpp.MLE_trace_max_evals = int(self.MLE_trace_max_evals)
290 cpp.MLE_allow_profile_hessian_fallback = bool(self.MLE_allow_profile_hessian_fallback)
291 cpp.MLE_profile_hessian_step_scale = float(self.MLE_profile_hessian_step_scale)
292 cpp.MLE_profile_hessian_eig_floor_rel = float(self.MLE_profile_hessian_eig_floor_rel)
293 cpp.likelihood_mode = _cpp_likelihood_mode(self.likelihood_mode)
294 cpp.chi2_covariance_ridge_rel = float(self.chi2_covariance_ridge_rel)
295 cpp.chi2_covariance_ridge_abs = float(self.chi2_covariance_ridge_abs)
296 cpp.MC_force_decay_threads_to_one = bool(self.MC_force_decay_threads_to_one)
297 cpp.MC_forced_decay_threads = int(self.MC_forced_decay_threads)
298 return cpp
299
300
301@dataclass
303 """Basic configuration of the statistic pipeline.
304
305 This is the small, common config used by examples and scripts. Advanced
306 minimization, nuisance-pruning and covariance options are grouped in
307 :class:`AdvancedStatisticConfig` under ``advanced``.
308
309 Args:
310 MC_draws: Number of accepted MC predictions used in uncertainty
311 propagation or chi-square covariance estimation.
312 MC_threads: Number of worker threads used by MC propagation.
313 MC_seed: RNG seed used for reproducible MC nuisance and experimental-data sampling.
314 skew_abs_threshold: Skewness threshold for symmetric vs split-Gaussian
315 summaries.
316 print_mc_progress: Print a compact progress bar with live ETA during MC
317 sampling.
318 print_chi2_pipeline_progress: In chi-square MC-covariance mode, print
319 the post-MC workflow stages so the MC ETA is not mistaken for the
320 full runtime.
321 print_mc_config: Print nuisance candidates and retained MC marginal
322 configuration.
323 print_fit_summary: Print high-level fit diagnostics.
324 print_scan_summary: Print likelihood-scan diagnostics.
325 print_cache_summary: Enable ``StatisticManager::print_cache`` output.
326 print_debug: Master debug flag enabling extra diagnostic output.
327 write_mc_samples_csv: Persist accepted MC observable samples.
328 mc_samples_csv_path: Output CSV path when sample writing is enabled.
329 advanced: Expert configuration object.
330 """
331
332 MC_draws: int = 100
333 MC_threads: int = 1
334 MC_seed: int = 123456
335 skew_abs_threshold: float = 0.2
336
337 print_mc_progress: bool = False
338 print_chi2_pipeline_progress: bool = False
339 print_mc_config: bool = False
340 print_fit_summary: bool = False
341 print_scan_summary: bool = False
342 print_cache_summary: bool = False
343 print_debug: bool = False
344
345 write_mc_samples_csv: bool = False
346 mc_samples_csv_path: str = "obs_samples.csv"
347 mc_progress_probe_draws: int = 5
348 mc_progress_update_every: int = 1
349
350 progress_monitor: Optional[StatisticProgressMonitor] = None
351 fit_parameter_bounds: Dict[ParamId, Tuple[float, float]] = field(default_factory=dict)
352 fit_parameter_offsets: Dict[ParamId, float] = field(default_factory=dict)
353 advanced: AdvancedStatisticConfig = field(default_factory=AdvancedStatisticConfig)
354
355 def to_cpp(self):
356 """Convert this Python config to the bound C++ ``StatisticConfig``."""
357 cpp = st.StatisticConfig()
358 cpp.MC_draws = int(self.MC_draws)
359 cpp.MC_threads = int(self.MC_threads)
360 cpp.MC_seed = int(self.MC_seed)
361 cpp.skew_abs_threshold = float(self.skew_abs_threshold)
362 cpp.print_mc_progress = bool(self.print_mc_progress)
363 cpp.print_chi2_pipeline_progress = bool(self.print_chi2_pipeline_progress)
364 cpp.print_mc_config = bool(self.print_mc_config)
365 cpp.print_fit_summary = bool(self.print_fit_summary)
366 cpp.print_scan_summary = bool(self.print_scan_summary)
367 cpp.print_cache_summary = bool(self.print_cache_summary)
368 cpp.print_debug = bool(self.print_debug)
369 cpp.write_mc_samples_csv = bool(self.write_mc_samples_csv)
370 cpp.mc_samples_csv_path = str(self.mc_samples_csv_path)
371 cpp.mc_progress_probe_draws = int(self.mc_progress_probe_draws)
372 cpp.mc_progress_update_every = int(self.mc_progress_update_every)
373 if self.progress_monitor is not None:
374 cpp.progress_monitor = self.progress_monitor._cpp_obj
375 cpp.fit_parameter_bounds = {
376 _cpp_param_id(pid): (float(bounds[0]), float(bounds[1]))
377 for pid, bounds in self.fit_parameter_bounds.items()
378 }
379 cpp.fit_parameter_offsets = {
380 _cpp_param_id(pid): float(offset) for pid, offset in self.fit_parameter_offsets.items()
381 }
382 cpp.advanced = self.advanced.to_cpp()
383 return cpp
384
385
386__all__ = [
387 "StatisticConfig",
388 "AdvancedStatisticConfig",
389 "StatisticLikelihoodMode",
390 "StatisticProgressMonitor",
391 "StatisticProgressSnapshot",
392]
AdvancedStatisticConfig advanced
StatisticProgressSnapshot snapshot(self)
None set_progress(self, str phase, str message, float fraction, *int completed=0, int total=0, float eta_seconds=-1.0, bool finished=False)
None reset(self, str phase="preparing", str message="Preparing statistic workflow")
_cpp_marginal_kind(MarginalKind kind)
_require(value, typ, str name)
_cpp_experiment_obs(ExperimentObs obs)
_cpp_param_id(ParamId pid)
_cpp_copula_kind(CopulaKind kind)
_cpp_likelihood_mode(StatisticLikelihoodMode mode)