Source code for xnn.common.models.hub.formats

"""Model file formats the hub can read, beyond xnn's own.

xnn's own format (a portable model directory, or a trainer checkpoint) is
built in. Other formats register a :class:`ModelFormat` whose ``convert``
turns a file into an xnn model plus the config that rebuilds it; the hub then
caches the result in the portable format, so a converted model loads without
the upstream package afterwards. The MACE foundation models use this through
the ``"mace-torch"`` format, registered by
:mod:`xnn.gnn.models.mace_foundation`.
"""
from __future__ import annotations

import importlib
from dataclasses import dataclass
from pathlib import Path
from typing import Callable, Optional

XNN_FORMAT = "xnn"
XNN_SUFFIXES = (".pt", ".pth", ".ckpt")
ARCHIVE_SUFFIXES = (".zip", ".tar", ".tar.gz", ".tgz", ".tar.bz2", ".tar.xz")

# formats provided by an optional family: name -> (module that registers it,
# file suffixes), so detection and the error messages work before the import
_PROVIDERS: dict[str, tuple[str, tuple[str, ...]]] = {
    "mace-torch": ("xnn.gnn.models.mace_foundation", (".model",)),
    "aimnet2": ("xnn.gnn.models.aimnet2_foundation", (".safetensors",)),
}


[docs] @dataclass(frozen=True) class ModelFormat: """A foreign model format the hub converts into xnn's own. Attributes ---------- name : str Format key used in model cards (e.g. ``"mace-torch"``). suffixes : tuple of str File suffixes recognized as this format. convert : callable ``convert(path, head) -> (model, config)``: the bare xnn potential and the :class:`~xnn.common.config.ModelConfig` that :func:`~xnn.common.models.build_model` rebuilds it from (the weights then load strictly), or a full :class:`~xnn.common.config.Config` when the model comes with more than its architecture (a ``subtracted_dispersion`` record). find_cached : callable, optional ``find_cached(card) -> path or None``: a copy of the card's file that some other cache already holds, so it is not downloaded again. requires : str What has to be installed to convert (shown in error messages). detect : callable, optional ``detect(path) -> bool``: recognizes a file or directory of this format when its suffix does not tell (a ``.pt`` artifact, a directory of weights and metadata); a path that does not exist is never one. """ name: str suffixes: tuple[str, ...] convert: Callable find_cached: Optional[Callable] = None requires: str = "" detect: Optional[Callable] = None
_FORMATS: dict[str, ModelFormat] = {}
[docs] def register_format(fmt: ModelFormat) -> ModelFormat: """Register a foreign model format. Parameters ---------- fmt : ModelFormat The format. Returns ------- ModelFormat ``fmt``. """ _FORMATS[fmt.name] = fmt return fmt
[docs] def get_format(name: str) -> ModelFormat: """Look up a foreign format, importing the family that provides it. Parameters ---------- name : str Format key. Returns ------- ModelFormat The registered format. Raises ------ KeyError For an unknown format. ImportError When the providing family cannot be imported. """ if name not in _FORMATS and name in _PROVIDERS: module = _PROVIDERS[name][0] try: importlib.import_module(module) except ImportError as e: raise ImportError( f"the {name!r} model format is provided by {module}, which " f"failed to import ({e}); the GNN family needs e3nn") from e try: return _FORMATS[name] except KeyError: known = sorted({XNN_FORMAT, *_FORMATS, *_PROVIDERS}) raise KeyError(f"unknown model format {name!r}; known: {known}") from None
[docs] def suffixes() -> tuple[str, ...]: """Every suffix that marks a model file or an archive of one.""" out = list(XNN_SUFFIXES) + list(ARCHIVE_SUFFIXES) for _, sfx in _PROVIDERS.values(): out += sfx for fmt in _FORMATS.values(): out += fmt.suffixes return tuple(dict.fromkeys(out))
[docs] def is_archive(path: Path) -> bool: """Whether ``path`` has an archive suffix.""" return str(path).lower().endswith(ARCHIVE_SUFFIXES)
[docs] def detect_format(path: Path) -> str: """Guess a file's format from its suffix, or a format's own ``detect`` (xnn's own when unknown). Parameters ---------- path : pathlib.Path A model file or directory. Returns ------- str Format key. """ path = Path(path) for fmt in _FORMATS.values(): if fmt.detect is not None and fmt.detect(path): return fmt.name if path.is_dir(): return XNN_FORMAT name = str(path).lower() for fmt in _FORMATS.values(): if name.endswith(fmt.suffixes): return fmt.name for fmt_name, (_, sfx) in _PROVIDERS.items(): if name.endswith(sfx): return fmt_name return XNN_FORMAT