Source code for qvartools.molecules.registry

"""
registry --- Molecule registry and factory functions
====================================================

Defines a registry of standard molecular benchmarks used in quantum
chemistry.  Each entry provides a factory function that computes
molecular integrals and constructs a :class:`MolecularHamiltonian`.

The registry covers a range of system sizes from H2 (4 qubits) to
C2H4 (28 qubits), enabling systematic benchmarking of SQD/SKQD methods.

Constants
---------
MOLECULE_REGISTRY
    Dictionary mapping lowercase molecule names to factory metadata.

Functions
---------
get_molecule
    Instantiate a Hamiltonian for a named molecule.
list_molecules
    Return sorted list of registered molecule names.
"""

from __future__ import annotations

import logging
from typing import Any, Dict, List, Tuple

from qvartools.hamiltonians.molecular import (
    MolecularHamiltonian,
    compute_molecular_integrals,
)

__all__ = [
    "MOLECULE_REGISTRY",
    "get_molecule",
    "list_molecules",
]

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# Geometry definitions
# ---------------------------------------------------------------------------

_H2_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("H", (0.0, 0.0, 0.0)),
    ("H", (0.0, 0.0, 0.74)),
]

_LIH_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("Li", (0.0, 0.0, 0.0)),
    ("H", (0.0, 0.0, 1.6)),
]

_BEH2_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("Be", (0.0, 0.0, 0.0)),
    ("H", (0.0, 0.0, 1.33)),
    ("H", (0.0, 0.0, -1.33)),
]

# H2O geometry from parametric (OH=0.96 Å, angle=104.5°)
import math as _math

_H2O_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("O", (0.0, 0.0, 0.0)),
    ("H", (0.96, 0.0, 0.0)),
    ("H", (0.96 * _math.cos(_math.radians(104.5)),
           0.96 * _math.sin(_math.radians(104.5)), 0.0)),
]

_NH3_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("N", (0.0, 0.0, 0.0)),
    ("H", (0.0, -0.9377, -0.3816)),
    ("H", (0.8121, 0.4689, -0.3816)),
    ("H", (-0.8121, 0.4689, -0.3816)),
]

_N2_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("N", (0.0, 0.0, 0.0)),
    ("N", (0.0, 0.0, 1.0977)),
]

_CH4_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("C", (0.0, 0.0, 0.0)),
    ("H", (0.6276, 0.6276, 0.6276)),
    ("H", (0.6276, -0.6276, -0.6276)),
    ("H", (-0.6276, 0.6276, -0.6276)),
    ("H", (-0.6276, -0.6276, 0.6276)),
]

_C2H4_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("C", (0.0, 0.0, 0.6695)),
    ("C", (0.0, 0.0, -0.6695)),
    ("H", (0.0, 0.9289, 1.2321)),
    ("H", (0.0, -0.9289, 1.2321)),
    ("H", (0.0, 0.9289, -1.2321)),
    ("H", (0.0, -0.9289, -1.2321)),
]

_CO_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("C", (0.0, 0.0, 0.0)),
    ("O", (0.0, 0.0, 1.13)),
]

_HCN_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("H", (0.0, 0.0, 0.0)),
    ("C", (0.0, 0.0, 1.06)),
    ("N", (0.0, 0.0, 2.22)),
]

_C2H2_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("H", (0.0, 0.0, 0.0)),
    ("C", (0.0, 0.0, 1.06)),
    ("C", (0.0, 0.0, 2.26)),
    ("H", (0.0, 0.0, 3.32)),
]

_H2S_GEOMETRY: List[Tuple[str, Tuple[float, float, float]]] = [
    ("S", (0.0, 0.0, 0.0)),
    ("H", (1.34, 0.0, 0.0)),
    ("H", (-0.0497, 1.3391, 0.0)),
]


# ---------------------------------------------------------------------------
# Factory functions
# ---------------------------------------------------------------------------


def _make_h2(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create H2 Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_H2_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("H2", 4, "sto-3g", _H2_GEOMETRY, 0, 0)
    return ham, info


def _make_lih(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create LiH Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_LIH_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("LiH", 12, "sto-3g", _LIH_GEOMETRY, 0, 0)
    return ham, info


def _make_beh2(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create BeH2 Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_BEH2_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("BeH2", 14, "sto-3g", _BEH2_GEOMETRY, 0, 0)
    return ham, info


def _make_h2o(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create H2O Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_H2O_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("H2O", 14, "sto-3g", _H2O_GEOMETRY, 0, 0)
    return ham, info


def _make_nh3(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create NH3 Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_NH3_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("NH3", 16, "sto-3g", _NH3_GEOMETRY, 0, 0)
    return ham, info


def _make_n2(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create N2 Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_N2_GEOMETRY, basis="cc-pvdz", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("N2", 20, "cc-pvdz", _N2_GEOMETRY, 0, 0)
    return ham, info


def _make_ch4(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create CH4 Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_CH4_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("CH4", 18, "sto-3g", _CH4_GEOMETRY, 0, 0)
    return ham, info


def _make_c2h4(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create C2H4 (ethylene) Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_C2H4_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("C2H4", 28, "sto-3g", _C2H4_GEOMETRY, 0, 0)
    return ham, info


def _make_co(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create CO (carbon monoxide) Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_CO_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("CO", 20, "sto-3g", _CO_GEOMETRY, 0, 0)
    return ham, info


def _make_hcn(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create HCN (hydrogen cyanide) Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_HCN_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("HCN", 22, "sto-3g", _HCN_GEOMETRY, 0, 0)
    return ham, info


def _make_c2h2(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create C2H2 (acetylene) Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_C2H2_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("C2H2", 24, "sto-3g", _C2H2_GEOMETRY, 0, 0)
    return ham, info


def _make_h2s(device: str = "cpu") -> Tuple[MolecularHamiltonian, Dict[str, Any]]:
    """Create H2S (hydrogen sulfide) Hamiltonian and info dict.

    Parameters
    ----------
    device : str, optional
        Torch device (default ``"cpu"``).

    Returns
    -------
    tuple
        ``(hamiltonian, info_dict)``.
    """
    integrals = compute_molecular_integrals(
        geometry=_H2S_GEOMETRY, basis="sto-3g", charge=0, spin=0
    )
    ham = MolecularHamiltonian(integrals, device=device)
    info = _build_info("H2S", 26, "sto-3g", _H2S_GEOMETRY, 0, 0)
    return ham, info


# ---------------------------------------------------------------------------
# Helper
# ---------------------------------------------------------------------------


def _build_info(
    name: str,
    n_qubits: int,
    basis: str,
    geometry: List[Tuple[str, Tuple[float, float, float]]],
    charge: int,
    spin: int,
) -> Dict[str, Any]:
    """Build a standardised molecule info dictionary.

    Parameters
    ----------
    name : str
        Molecule name.
    n_qubits : int
        Number of qubits (spin-orbitals).
    basis : str
        Gaussian basis set.
    geometry : list
        Atomic geometry.
    charge : int
        Net molecular charge.
    spin : int
        Spin multiplicity minus one (2S).

    Returns
    -------
    dict
        Info dictionary with keys ``name``, ``n_qubits``, ``basis``,
        ``geometry``, ``charge``, ``spin``.
    """
    return {
        "name": name,
        "n_qubits": n_qubits,
        "basis": basis,
        "geometry": geometry,
        "charge": charge,
        "spin": spin,
    }


# ---------------------------------------------------------------------------
# Registry
# ---------------------------------------------------------------------------

MOLECULE_REGISTRY: Dict[str, Dict[str, Any]] = {
    "h2": {
        "factory": _make_h2,
        "n_qubits": 4,
        "description": "Hydrogen molecule (minimal basis)",
        "basis": "sto-3g",
    },
    "lih": {
        "factory": _make_lih,
        "n_qubits": 12,
        "description": "Lithium hydride",
        "basis": "sto-3g",
    },
    "beh2": {
        "factory": _make_beh2,
        "n_qubits": 14,
        "description": "Beryllium dihydride",
        "basis": "sto-3g",
    },
    "h2o": {
        "factory": _make_h2o,
        "n_qubits": 14,
        "description": "Water molecule",
        "basis": "sto-3g",
    },
    "nh3": {
        "factory": _make_nh3,
        "n_qubits": 16,
        "description": "Ammonia",
        "basis": "sto-3g",
    },
    "n2": {
        "factory": _make_n2,
        "n_qubits": 20,
        "description": "Nitrogen molecule (cc-pVDZ basis)",
        "basis": "cc-pvdz",
    },
    "ch4": {
        "factory": _make_ch4,
        "n_qubits": 18,
        "description": "Methane",
        "basis": "sto-3g",
    },
    "c2h4": {
        "factory": _make_c2h4,
        "n_qubits": 28,
        "description": "Ethylene (minimal basis)",
        "basis": "sto-3g",
    },
    "co": {
        "factory": _make_co,
        "n_qubits": 20,
        "description": "Carbon monoxide (STO-3G)",
        "basis": "sto-3g",
    },
    "hcn": {
        "factory": _make_hcn,
        "n_qubits": 22,
        "description": "Hydrogen cyanide (STO-3G)",
        "basis": "sto-3g",
    },
    "c2h2": {
        "factory": _make_c2h2,
        "n_qubits": 24,
        "description": "Acetylene (STO-3G)",
        "basis": "sto-3g",
    },
    "h2s": {
        "factory": _make_h2s,
        "n_qubits": 26,
        "description": "Hydrogen sulfide (STO-3G)",
        "basis": "sto-3g",
    },
}


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


[docs] def get_molecule( name: str, device: str = "cpu" ) -> Tuple[MolecularHamiltonian, Dict[str, Any]]: """Create a Hamiltonian and info dict for a named molecule. Looks up the molecule in :data:`MOLECULE_REGISTRY`, runs the PySCF integral computation, and constructs a :class:`MolecularHamiltonian`. Parameters ---------- name : str Molecule name (case-insensitive). Must be a key in :data:`MOLECULE_REGISTRY`. device : str, optional Torch device for the Hamiltonian (default ``"cpu"``). Returns ------- hamiltonian : MolecularHamiltonian The molecular Hamiltonian ready for diagonalisation. info : dict Metadata dictionary with keys ``name``, ``n_qubits``, ``basis``, ``geometry``, ``charge``, ``spin``. Raises ------ KeyError If ``name`` is not found in the registry. Examples -------- >>> ham, info = get_molecule("H2") >>> info["n_qubits"] 4 >>> ham.num_sites 4 """ key = name.lower().strip() if key not in MOLECULE_REGISTRY: available = ", ".join(sorted(MOLECULE_REGISTRY.keys())) raise KeyError( f"Unknown molecule {name!r}. Available: {available}" ) entry = MOLECULE_REGISTRY[key] factory = entry["factory"] logger.info( "Creating molecule %r (%d qubits, %s basis)", key, entry["n_qubits"], entry["basis"], ) return factory(device=device)
[docs] def list_molecules() -> List[str]: """Return a sorted list of available molecule names. Returns ------- list of str Registered molecule names in alphabetical order. Examples -------- >>> names = list_molecules() >>> "h2" in names True """ return sorted(MOLECULE_REGISTRY.keys())