Source code for adaptive.learner.triangulation_backend

"""Select the triangulation backend used by the learners.

If the optional Rust-accelerated `adaptive-triangulation
<https://github.com/python-adaptive/adaptive-triangulation>`_ package is
installed (``pip install "adaptive[rust]"``), it is used automatically as a
drop-in replacement for the pure-Python implementation in
`adaptive.learner.triangulation`, which makes `~adaptive.LearnerND`
significantly faster.

The selection can be overridden with the ``ADAPTIVE_TRIANGULATION_BACKEND``
environment variable:

- ``auto`` (default): use the Rust backend if available, else pure Python
- ``python``: always use the pure-Python implementation
- ``rust``: require the Rust backend, raising `ImportError` if it is missing

The active backend is exposed as the string ``TRIANGULATION_BACKEND``
(``"python"`` or ``"rust"``).

Note that the pure-Python implementation in `adaptive.learner.triangulation`
is always importable under its own name, regardless of the selected backend,
so pickles that reference it keep working.
"""

from __future__ import annotations

import os

# Minimal version that is a complete drop-in for the learners
# (incl. ``get_opposing_vertices`` and pickle/deepcopy support).
_MIN_RUST_VERSION = (0, 2, 1)


def _rust_version() -> tuple[int, ...] | None:
    """Return the installed ``adaptive_triangulation`` version, or None."""
    try:
        import adaptive_triangulation
    except ImportError:
        return None
    version = adaptive_triangulation.__version__
    return tuple(int(part) for part in version.split(".")[:3] if part.isdigit())


def _import_rust_triangulation():
    """Import the Rust `Triangulation`, raising a helpful `ImportError`."""
    version = _rust_version()
    if version is None:
        raise ImportError(
            "The 'rust' triangulation backend was requested but the "
            "'adaptive-triangulation' package is not installed. "
            'Install it with: pip install "adaptive[rust]"'
        )
    if version < _MIN_RUST_VERSION:
        raise ImportError(
            "The 'rust' triangulation backend requires "
            f"adaptive-triangulation >= {'.'.join(map(str, _MIN_RUST_VERSION))}, "
            f"found {'.'.join(map(str, version))}. Upgrade it with: "
            'pip install -U "adaptive[rust]"'
        )
    from adaptive_triangulation import Triangulation

    return Triangulation


def _select_backend() -> str:
    backend = os.environ.get("ADAPTIVE_TRIANGULATION_BACKEND", "auto").lower()
    if backend not in ("auto", "python", "rust"):
        raise ValueError(
            f"ADAPTIVE_TRIANGULATION_BACKEND={backend!r} is invalid, "
            "use 'auto', 'python', or 'rust'."
        )
    if backend == "auto":
        version = _rust_version()
        return (
            "rust" if version is not None and version >= _MIN_RUST_VERSION else "python"
        )
    if backend == "rust":
        _import_rust_triangulation()  # raise with guidance if unusable
    return backend


[docs]def resolve_triangulation_class(backend="auto"): """Return the `Triangulation` class to use for *backend*. Parameters ---------- backend : str or type ``"auto"`` (the module-level default backend, which prefers the Rust implementation when available), ``"python"``, ``"rust"``, or a `Triangulation`-compatible class. """ if isinstance(backend, type): return backend if backend == "auto": return Triangulation if backend == "python": from adaptive.learner.triangulation import Triangulation as tri_class return tri_class if backend == "rust": return _import_rust_triangulation() raise ValueError( f"Invalid triangulation backend {backend!r}, use 'auto', 'python', " "'rust', or a Triangulation-compatible class." )
TRIANGULATION_BACKEND: str = _select_backend() if TRIANGULATION_BACKEND == "rust": from adaptive_triangulation import ( Triangulation, circumsphere, fast_2d_circumcircle, fast_2d_point_in_simplex, fast_3d_circumcircle, fast_norm, orientation, point_in_simplex, simplex_volume_in_embedding, ) else: from adaptive.learner.triangulation import ( Triangulation, circumsphere, fast_2d_circumcircle, fast_2d_point_in_simplex, fast_3d_circumcircle, fast_norm, orientation, point_in_simplex, simplex_volume_in_embedding, ) __all__ = [ "TRIANGULATION_BACKEND", "Triangulation", "resolve_triangulation_class", "circumsphere", "fast_2d_circumcircle", "fast_2d_point_in_simplex", "fast_3d_circumcircle", "fast_norm", "orientation", "point_in_simplex", "simplex_volume_in_embedding", ]