Source code for uniqc.backend_adapter.task.optional_deps

"""Optional dependency management with clear error messages.

This module provides utilities for handling optional dependencies across
different quantum cloud platforms. When a dependency is not installed,
users receive clear instructions on how to install it.

Usage::

    # Check if dependency is available
    from uniqc.backend_adapter.task.optional_deps import QUAFU_AVAILABLE
    if QUAFU_AVAILABLE:
        from uniqc.backend_adapter.task.adapters.quafu_adapter import QuafuAdapter

    # Require dependency with error message
    from uniqc.backend_adapter.task.optional_deps import require
    quafu = require("quafu", "quafu")  # Raises MissingDependencyError if not installed
"""

from __future__ import annotations

import importlib

from uniqc.exceptions import MissingDependencyError  # noqa: F401 — re-export

__all__ = [
    "MissingDependencyError",
    "require",
    "check_quafu",
    "check_quark",
    "check_quarkcircuit",
    "check_qiskit",
    "check_pyqpanda3",
    "check_uniqc_cpp",
    "check_qutip",
    "check_simulation",
    "QUAFU_AVAILABLE",
    "QUARK_AVAILABLE",
    "QUARKCIRCUIT_AVAILABLE",
    "QISKIT_AVAILABLE",
    "PYQPANDA3_AVAILABLE",
    "UNIQC_CPP_AVAILABLE",
    "QUTIP_AVAILABLE",
    "SIMULATION_AVAILABLE",
]


[docs] def require(name: str, extra: str, install_hint: str | None = None): """Import an optional module with a clear error message if missing. Args: name: The module name to import (e.g., 'quafu', 'qiskit'). extra: The pip extras name for installation (e.g., 'qiskit'). Note: as of the current release, ``qiskit`` is a core dependency (no extra) and ``quafu`` no longer has an extra — see ``install_hint``. install_hint: Optional explicit install / recovery hint that overrides the default ``unified-quantum[{extra}]`` message. Used for deprecated paths (e.g., quafu) where there is no extras name. Returns: The imported module. Raises: MissingDependencyError: If the module cannot be imported. """ # Hard-coded deprecated path: there's no [quafu] extra anymore. if install_hint is None and extra == "quafu": install_hint = ( "The Quafu adapter is deprecated and is no longer installable via " "a `[quafu]` extra. Install pyquafu directly if you still need it: " "`pip install pyquafu` (warning: pulls numpy<2)." ) try: return importlib.import_module(name) except ImportError as e: if install_hint is not None: raise MissingDependencyError(name, install_hint=install_hint) from e raise MissingDependencyError(name, extra) from e except Exception as e: if install_hint is not None: raise MissingDependencyError( name, install_hint=(f"The package is installed but failed to import cleanly: {e!r}. {install_hint}"), ) from e raise MissingDependencyError( name, extra, install_hint=( f"The package is installed but failed to import cleanly: {e!r}. " f"Upgrade or reinstall with: pip install --upgrade unified-quantum[{extra}]" ), ) from e
def _can_import(*names: str) -> bool: """Return ``True`` only if all optional modules import cleanly.""" try: for name in names: importlib.import_module(name) return True except Exception: return False
[docs] def check_quafu() -> bool: """Check if the quafu package is available. Returns: True if quafu can be imported, False otherwise. """ return _can_import("quafu")
[docs] def check_quark() -> bool: """Check if the QuarkStudio package is available. Returns: True if ``from quark import Task`` succeeds, False otherwise. """ try: module = importlib.import_module("quark") _ = module.Task return True except Exception: return False
[docs] def check_quarkcircuit() -> bool: """Check if QuarkStudio's circuit metadata module is available.""" return _can_import("quark.circuit.backend")
[docs] def check_qiskit() -> bool: """Check if the qiskit and qiskit_ibm_runtime packages are available. Returns: True if both packages can be imported, False otherwise. """ return _can_import("qiskit", "qiskit_ibm_runtime")
[docs] def check_pyqpanda3() -> bool: """Check if the pyqpanda3 package is available. Returns: True if pyqpanda3 can be imported, False otherwise. """ return _can_import("pyqpanda3")
[docs] def check_uniqc_cpp() -> bool: """Check if the uniqc_cpp C++ simulator extension is available. Returns: True if uniqc_cpp can be imported, False otherwise. """ return _can_import("uniqc_cpp")
[docs] def check_qutip() -> bool: """Check if the QuTiP-based simulation stack is available. Returns: True if qutip and qutip_qip can be imported, False otherwise. """ return _can_import("qutip", "qutip_qip")
[docs] def check_simulation(target: str = "cpp") -> bool: """Check simulation support for a specific backend family. Args: target: Which simulation capability to check. - ``"cpp"``: built-in C++ simulator extension (default) - ``"qutip"``: QuTiP-based simulation stack - ``"all"``: both C++ simulator and QuTiP stack Returns: True if the requested simulation target is available, False otherwise. Raises: ValueError: If ``target`` is not one of ``"cpp"``, ``"qutip"``, or ``"all"``. """ if target == "cpp": return check_uniqc_cpp() if target == "qutip": return check_qutip() if target == "all": return check_uniqc_cpp() and check_qutip() raise ValueError(f"Unsupported simulation target: {target}")
# Pre-computed availability flags (evaluated at module load time) QUAFU_AVAILABLE = check_quafu() QUARK_AVAILABLE = check_quark() QUARKCIRCUIT_AVAILABLE = check_quarkcircuit() QISKIT_AVAILABLE = check_qiskit() PYQPANDA3_AVAILABLE = check_pyqpanda3() UNIQC_CPP_AVAILABLE = check_uniqc_cpp() QUTIP_AVAILABLE = check_qutip() SIMULATION_AVAILABLE = UNIQC_CPP_AVAILABLE