Source code for bornsim.ensemble_sampling

"""Reproducible independent-realization sampling configurations."""

from dataclasses import dataclass
from collections.abc import Sequence
from .units import _integer


[docs] @dataclass(frozen=True, kw_only=True, init=False) class EnsembleSampling: """Choose consecutive seeds or an explicit ordered set of distinct seeds. realizations defaults to four and seed to zero when seeds is omitted. Explicit seeds cannot be combined with realizations or seed. Seeds range from zero to 2**32-1; there are 1 to 32 independent realizations. Duplicate seeds are rejected because they do not provide independent uncertainty. """ realizations: int seed: int seeds: tuple[int, ...] def __init__( self, *, realizations: int | None = None, seed: int | None = None, seeds: Sequence[int] | None = None, ) -> None: if seeds is not None: if realizations is not None or seed is not None: raise ValueError("Supply seeds or realizations/seed, not both.") values = tuple(seeds) _integer(value=len(values), name="realizations", low=1, high=32) seeds = tuple(_integer(value=value, name="seed", low=0, high=2**32 - 1) for value in values) if len(set(seeds)) != len(seeds): raise ValueError("seeds must be distinct for independent realizations.") else: count = _integer(value=4 if realizations is None else realizations, name="realizations", low=1, high=32) first = _integer(value=0 if seed is None else seed, name="seed", low=0, high=2**32 - 1) if first + count - 1 > 2**32 - 1: raise ValueError("seed range exceeds the supported maximum.") seeds = tuple(range(first, first + count)) object.__setattr__(self, "seeds", seeds) object.__setattr__(self, "realizations", len(seeds)) object.__setattr__(self, "seed", seeds[0]) @property def metadata(self): """Return exact ordered seeds for reproducibility.""" return {"realizations": self.realizations, "seeds": list(getattr(self, "seeds"))} @classmethod def _resolve(cls, *, ensemble_sampling: "EnsembleSampling | None" = None) -> "EnsembleSampling": if ensemble_sampling is None: return cls() if not isinstance(ensemble_sampling, cls): raise TypeError("ensemble_sampling must be an EnsembleSampling.") return ensemble_sampling