Source code for specsrbench.methods

"""The nine reconstructions, their display names, and the keys they are cached under.

Three different naming schemes meet in this project and none of them can be
retired without invalidating a shipped cache:

* ``snr.npz`` is keyed by a short name -- ``LR``, ``RL``, ``MF``, ``SR2``.
* ``fit_params_cache.npz`` is keyed by the *display* name -- ``Cubic (LR)``,
  ``R-L``, ``Wiener + MF``, ``ML (SR2)``.
* The arrays themselves are separate ``.npy`` files named after the method.

Each notebook carried its own copy of all three maps, and they had drifted: three
of the six mapped ``ML (SR2)`` to the S/N key ``"ML (SR2)"``, which does not
exist in ``snr.npz``.  It never raised, because those three defined the map and
then did not use it.  One table here, with the cache's own key names checked by
``tests/test_cache_integrity.py``, is what replaces that.

Display *labels* still vary by figure -- figure 2 writes ``LR (cubic)`` and
``Wiener + TV`` where figure 3 writes ``Cubic (LR)`` and ``TV`` -- and figure 3
recolours two methods to keep them legible against its diverging colour map.
Those variations are real and are preserved by :func:`registry` overrides rather
than by nine more copies of the table.
"""
from __future__ import annotations

from dataclasses import dataclass, replace

__all__ = ["Method", "METHODS", "registry", "labels", "colors", "LINES",
           "line_keys", "ORDER", "NON_HR"]


[docs] @dataclass(frozen=True) class Method: """One reconstruction: how to find it, what to call it, how to draw it.""" key: str #: canonical short name, and the ``snr.npz`` key prefix label: str #: display name, and the ``fit_params_cache.npz`` prefix array: str | None #: file in the cache holding it, if it has one of its own color: str @property def snr_key(self) -> str: return self.key
#: Fixed display order, and the order every bar chart and panel grid uses: #: baseline -> linear -> non-linear/regularised -> line-list-aware -> learned, #: with the reference last. METHODS: tuple[Method, ...] = ( Method("LR", "Cubic (LR)", "x_low.npy", "deeppink"), Method("Wiener", "Wiener", "wiener_cache.npy", "tomato"), Method("Tikhonov", "Tikhonov", "tikhonov_cache.npy", "forestgreen"), Method("TV", "TV", "tv_cache.npy", "teal"), Method("RL", "R-L", "rl_cache.npy", "steelblue"), Method("Sparse", "Sparse", "sparse_cache.npy", "goldenrod"), Method("MF", "Wiener + MF", "mf_cache.npy", "darkorchid"), Method("SR2", "ML (SR2)", None, "darkorange"), Method("HR", "HR target", "x_high.npy", "black"), ) ORDER: tuple[str, ...] = tuple(m.key for m in METHODS) NON_HR: tuple[str, ...] = tuple(k for k in ORDER if k != "HR") #: ``(cache key, display label, rest wavelength in um)`` for the four lines the #: paper measures. The order is the one every per-line panel row uses. LINES: tuple[tuple[str, str, float], ...] = ( ("Halpha", r"H$\alpha$", 0.6563), ("OIII5007", r"[O III] $\lambda$5007", 0.5007), ("Hbeta", r"H$\beta$", 0.4861), ("OII3727", r"[O II] $\lambda$3727", 0.3727), )
[docs] def line_keys() -> list[str]: return [k for k, _, _ in LINES]
[docs] def registry(label_overrides: dict[str, str] | None = None, color_overrides: dict[str, str] | None = None, include_hr: bool = True) -> dict[str, Method]: """The method table, keyed by canonical name, with per-figure overrides. Overrides are given by canonical key, so a figure that renames ``LR`` to ``LR (cubic)`` cannot accidentally rename it to something the cache has never heard of -- the key it looks arrays up by does not change. """ out: dict[str, Method] = {} for m in METHODS: if not include_hr and m.key == "HR": continue if label_overrides and m.key in label_overrides: m = replace(m, label=label_overrides[m.key]) if color_overrides and m.key in color_overrides: m = replace(m, color=color_overrides[m.key]) out[m.key] = m return out
[docs] def labels(reg: dict[str, Method]) -> list[str]: return [reg[k].label for k in reg]
[docs] def colors(reg: dict[str, Method]) -> list[str]: return [reg[k].color for k in reg]