registry.py 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. """Analysis registry.
  2. An *analysis* is a pure function of the loaded evaluation datasets that writes
  3. artifacts into ``<work_dir>/analysis/``. Analyses declare their data
  4. requirements so the runner can skip them with a clear message instead of failing
  5. when a working directory lacks, say, a noise sweep.
  6. Adding an analysis is: one new module under ``analysis/plots/`` containing a
  7. ``@register``-decorated function, plus an import in ``analysis/plots/__init__``.
  8. """
  9. from dataclasses import dataclass, field
  10. from typing import Callable, Dict, List, TYPE_CHECKING
  11. if TYPE_CHECKING: # avoid a circular import at runtime
  12. from analysis.context import AnalysisContext
  13. AnalysisFn = Callable[["AnalysisContext"], None]
  14. @dataclass(frozen=True)
  15. class AnalysisSpec:
  16. """Metadata describing one registered analysis."""
  17. name: str
  18. title: str
  19. fn: AnalysisFn
  20. #: Requires an evaluation with more than one noise level (a noise sweep).
  21. requires_noise: bool = False
  22. #: Free-form notes shown in logs (e.g. "scaffold").
  23. tags: List[str] = field(default_factory=list)
  24. #: Registered analyses, in registration order (which is the run order).
  25. ANALYSES: Dict[str, AnalysisSpec] = {}
  26. def register(
  27. name: str,
  28. *,
  29. title: str,
  30. requires_noise: bool = False,
  31. tags: List[str] | None = None,
  32. ) -> Callable[[AnalysisFn], AnalysisFn]:
  33. """Decorator registering an analysis function under ``name``.
  34. Args:
  35. name: Unique id; also the log label and typical output filename stem.
  36. title: Human-readable description.
  37. requires_noise: Skip this analysis when no noise-sweep file is available.
  38. tags: Optional markers (e.g. ``["scaffold"]``).
  39. """
  40. def _decorate(fn: AnalysisFn) -> AnalysisFn:
  41. if name in ANALYSES:
  42. raise ValueError(f"Analysis {name!r} is already registered.")
  43. ANALYSES[name] = AnalysisSpec(
  44. name=name,
  45. title=title,
  46. fn=fn,
  47. requires_noise=requires_noise,
  48. tags=list(tags or []),
  49. )
  50. return fn
  51. return _decorate