Download code/bifurcation/registry.py from coarse-grained-forecasting-benchmark/forecasting-models: direct link, hf CLI and curl.
- Browser
- Download file 6 kB
-
https://huggingface.co/coarse-grained-forecasting-benchmark/forecasting-models/resolve/main/code/bifurcation/registry.py
- Command line
-
hf download hf://coarse-grained-forecasting-benchmark/forecasting-models/code/bifurcation/registry.py
-
curl -L -o registry.py https://huggingface.co/coarse-grained-forecasting-benchmark/forecasting-models/resolve/main/code/bifurcation/registry.py
6 kB
| """Model and method factories shared by training, sampling and evaluation. | |
| Methods: ``direct_si``, ``cascaded_si`` and ``edm``. Models: ``unet2_5ch`` (Euler, SQG), ``unet2_5ch_coord`` | |
| (passive scalar), ``unet2_5ch_coord_aug`` (heterogeneous waves) and ``unet2_2comp`` (NLKG); each run configuration | |
| under records/<system>/runs/ names its model and method.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Callable | |
| from .methods.edm import EDMMethod | |
| from .methods.direct_si import DirectSIMethod | |
| from .methods.cascaded_si import CascadedSIMethod | |
| from .models.unet2 import Conditional2D, LocalDrift2D | |
| SEGMENT_DATA_MODES = ("segment_pairs", "fine_segment_pairs") | |
| # reserved transient key: the method's segment boundaries in physical time, injected by | |
| # build_model for backbones that declare physical times in their own YAML (see module docstring) | |
| T_VALUES_KEY = "_method_t_values" | |
| class ModelSpec: | |
| name: str | |
| endpoint_cls: type | |
| segment_cls: type | |
| needs_dataset_norm: bool = False | |
| description: str = "" | |
| MODELS: dict[str, ModelSpec] = {} | |
| METHODS: dict[str, Callable[[dict], object]] = {} | |
| def register_model(name: str, endpoint_cls: type, segment_cls: type, | |
| needs_dataset_norm: bool = False, description: str = "", | |
| overwrite: bool = False) -> ModelSpec: | |
| if name in MODELS and not overwrite: | |
| raise KeyError(f"model {name!r} already registered") | |
| spec = ModelSpec(name, endpoint_cls, segment_cls, needs_dataset_norm, description) | |
| MODELS[name] = spec | |
| return spec | |
| def register_method(name: str, factory: Callable[[dict], object], | |
| overwrite: bool = False) -> None: | |
| if name in METHODS and not overwrite: | |
| raise KeyError(f"method {name!r} already registered") | |
| METHODS[name] = factory | |
| def head_for_data_mode(data_mode: str | None) -> str: | |
| return "segment" if data_mode in SEGMENT_DATA_MODES else "endpoint" | |
| def build_model(model_cfg: dict, method_cfg: dict | None = None, | |
| data_cfg: dict | None = None): | |
| """Construct the backbone named by ``model_cfg['name']``. | |
| ``method_cfg['data_mode']`` selects the head. For models with ``needs_dataset_norm`` the dataset path is | |
| taken from ``data_cfg`` (``packaged_dataset_path``); a model configuration that carries its own copy of the | |
| key is accepted only if it agrees with the data config. | |
| """ | |
| name = model_cfg["name"] | |
| if name not in MODELS: | |
| raise ValueError(f"unknown model: {name!r} (registered: {sorted(MODELS)})") | |
| spec = MODELS[name] | |
| cfg = dict(model_cfg) | |
| if spec.needs_dataset_norm: | |
| data_path = (data_cfg or {}).get("packaged_dataset_path") | |
| model_path = cfg.get("packaged_dataset_path") | |
| if data_path is None and model_path is None: | |
| raise KeyError( | |
| f"model {name!r} needs the dataset normalization: pass data_cfg with " | |
| "'packaged_dataset_path' (model configurations do not carry dataset paths)") | |
| if data_path is not None and model_path is not None: | |
| if _norm_path(data_path) != _norm_path(model_path): | |
| raise ValueError( | |
| f"model.packaged_dataset_path ({model_path}) != data.packaged_dataset_path " | |
| f"({data_path}) β the FFT wrapper must use the training dataset's statistics") | |
| cfg["packaged_dataset_path"] = str(data_path if data_path is not None else model_path) | |
| t_values = ((method_cfg or {}).get("sampling") or {}).get("t_values") | |
| if t_values: | |
| cfg[T_VALUES_KEY] = list(t_values) | |
| data_mode = (method_cfg or {}).get("data_mode") | |
| # a model configuration that declares the horizon planes must agree with the method's time grid: | |
| # t_start = t_values[0], t_end = t_values[-1] | |
| tv = ((method_cfg or {}).get("sampling") or {}).get("t_values") | |
| if tv is not None and ("t_start" in cfg or "t_end" in cfg): | |
| t0, t1 = float(tv[0]), float(tv[-1]) | |
| if "t_start" in cfg and abs(float(cfg["t_start"]) - t0) > 1e-9: | |
| raise ValueError(f"model {name!r}: t_start {cfg['t_start']} != method t_values[0] = {t0}") | |
| if "t_end" in cfg and abs(float(cfg["t_end"]) - t1) > 1e-9: | |
| raise ValueError(f"model {name!r}: t_end {cfg['t_end']} != method horizon t_values[-1] = {t1} " | |
| f"(the model constant must equal the forecast horizon)") | |
| cls = spec.segment_cls if head_for_data_mode(data_mode) == "segment" else spec.endpoint_cls | |
| return cls.from_config(cfg) | |
| def build_method(method_cfg: dict): | |
| name = method_cfg["name"] | |
| if name not in METHODS: | |
| raise ValueError(f"unknown method: {name!r} (registered: {sorted(METHODS)})") | |
| return METHODS[name](method_cfg) | |
| def _norm_path(p: str) -> str: | |
| import os | |
| return os.path.normpath(os.path.abspath(str(p))) | |
| # ββ built-ins ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| register_model("unet2", Conditional2D, LocalDrift2D, needs_dataset_norm=True, | |
| description="FFT-wrapped 2-D circular-conv U-Net on the 32x32 coarse field " | |
| "(Conditional2D 3-ch / LocalDrift2D 5-ch)") | |
| register_method("edm", EDMMethod.from_config) | |
| register_method("direct_si", DirectSIMethod.from_config) | |
| register_method("cascaded_si", CascadedSIMethod.from_config) | |
| # the backbones register themselves on import; unet2_coord provides the coordinate planes of unet2_5ch_coord(_aug) | |
| from .models import unet2_coord as _unet2_coord # noqa: F401,E402 | |
| from .models import unet2_5ch as _unet2_5ch # noqa: F401,E402 | |
| from .models import unet2_5ch_coord as _unet2_5ch_coord # noqa: F401,E402 | |
| from .models import unet2_5ch_coord_aug as _unet2_5ch_coord_aug # noqa: F401,E402 | |
| from .models import unet2_2comp as _unet2_2comp # noqa: F401,E402 | |