Source code for pyhs3.analyses
"""
HS3 Analysis implementations.
Provides Pydantic classes for handling HS3 analysis specifications
including analysis configurations with parameters of interest and domains.
"""
from __future__ import annotations
from collections.abc import Iterator
from pydantic import BaseModel, ConfigDict, Field, RootModel
[docs]
class Analysis(BaseModel):
"""
Analysis specification defining automated analysis parameters.
Represents a complete analysis configuration that specifies which likelihood
to use, parameters of interest, parameter domains, and optional priors.
All parameters from the likelihood distributions must either be in the
referenced domain or set to 'const' in the parameter point.
Attributes:
name: Custom string identifier for the analysis
likelihood: Name referencing a likelihood in the likelihoods component
parameters_of_interest: Optional array of parameter names of interest
domains: Array of domain names from domains component
init: Optional name of initial values from parameter_points component
prior: Optional name of prior distribution from distributions component
"""
model_config = ConfigDict()
name: str = Field(..., repr=True)
likelihood: str = Field(..., repr=False)
parameters_of_interest: list[str] | None = Field(default=None, repr=False)
domains: list[str] = Field(..., repr=False)
init: str | None = Field(default=None, repr=False)
prior: str | None = Field(default=None, repr=False)
[docs]
class Analyses(RootModel[list[Analysis]]):
"""
Collection of HS3 analysis specifications.
Manages a set of analysis instances that define automated analysis
configurations with likelihoods, parameters of interest, and domains.
Provides dict-like access to analyses by name.
"""
root: list[Analysis] = Field(default_factory=list)
@property
def analysis_map(self) -> dict[str, Analysis]:
"""Mapping from analysis names to Analysis instances."""
return {analysis.name: analysis for analysis in self.root}
def __len__(self) -> int:
"""Number of analyses in this collection."""
return len(self.root)
def __contains__(self, analysis_name: str) -> bool:
"""Check if an analysis with the given name exists."""
return analysis_name in self.analysis_map
def __getitem__(self, item: str | int) -> Analysis:
"""Get an analysis by name or index."""
if isinstance(item, int):
return self.root[item]
return self.analysis_map[item]
def get(
self, analysis_name: str, default: Analysis | None = None
) -> Analysis | None:
"""Get an analysis by name, returning default if not found."""
return self.analysis_map.get(analysis_name, default)
def __iter__(self) -> Iterator[Analysis]: # type: ignore[override]
"""Iterate over the analyses."""
return iter(self.root)