Source code for qbraid.programs.alias_manager

# Copyright (C) 2024 qBraid
#
# This file is part of the qBraid-SDK
#
# The qBraid-SDK is free software released under the GNU General Public License v3
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
#
# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.

"""
Module for managing and retrieving custom program type aliases

"""
from typing import TYPE_CHECKING, Optional, Type

from openqasm3.parser import QASM3ParsingError, parse

from .exceptions import ProgramTypeError, QasmError
from .registry import QPROGRAM_REGISTRY, QPROGRAM_TYPES

if TYPE_CHECKING:
    import qbraid.programs


def find_str_type_alias(registry: dict[str, Type] = QPROGRAM_REGISTRY) -> Optional[str]:
    """Find additional keys with type 'str' in the registry."""
    str_keys = [k for k, v in registry.items() if v == str and k not in ("qasm2", "qasm3")]

    if len(str_keys) == 0:
        return None
    if len(str_keys) == 1:
        return str_keys[0]
    raise ValueError(f"Multiple additional keys with type 'str' found: {str_keys}")


def parse_qasm_type_alias(qasm: str) -> str:
    """Gets OpenQASM program version, either qasm2 or qasm3.

    Args:
        qasm_str: An OpenQASM program string

    Returns:
        QASM version from list :obj:`~qbraid.programs.QPROGRAM_ALIASES`

    Raises:
        :class:`~qbraid.programs.QasmError`: If string does not represent a valid OpenQASAM program.

    """
    qasm = qasm.replace("opaque", "// opaque")

    try:
        program = parse(qasm)
    except QASM3ParsingError as err:
        raise QasmError("Failed to parse OpenQASM program.") from err

    verion = int(float(program.version))
    return f"qasm{verion}"


def _get_program_type_alias(program: "qbraid.programs.QPROGRAM") -> str:
    """
    Get the type alias of a quantum program from registry.

    Args:
        program (qbraid.programs.QPROGRAM): The quantum program to get the type of.

    Returns:
        str: The type of the quantum program.

    Raises:
        ProgramTypeError: If the program type does not match any registered program types.
    """
    if isinstance(program, type):
        raise ProgramTypeError(message="Expected an instance of a quantum program, not a type.")

    if isinstance(program, str):
        try:
            return parse_qasm_type_alias(program)
        except QasmError as err:
            package = find_str_type_alias()
            if package is not None:
                return package
            raise ProgramTypeError(
                message=(
                    "Input of program of type string does not represent a valid OpenQASM program, "
                    "and no additional string type aliases are registered."
                )
            ) from err

    matched = []
    for alias, program_type in QPROGRAM_REGISTRY.items():
        if isinstance(program, (program_type, type(program_type))):
            matched.append(alias)

    if len(matched) == 1:
        return matched[0]

    if len(matched) > 1:
        raise ProgramTypeError(
            message=(
                f"Program of type '{type(program)}' matches multiple registered program types: "
                f"{matched}."
            )
        )

    raise ProgramTypeError(
        message=(
            f"Program of type '{type(program)}' does not match any registered "
            f"program types. Registered program types are: {QPROGRAM_TYPES}."
        )
    )


[docs] def get_program_type_alias( program: "qbraid.programs.QPROGRAM", safe: bool = False ) -> Optional[str]: """ Get the type alias of a quantum program from registry. Args: program (qbraid.programs.QPROGRAM): The quantum program to get the type of. safe (bool): If True, return None if the program type does not match any registered program types. Defaults to False. Returns: str: The type of the quantum program. None: If the program type does not match any registered program types and safe is True. Raises: ProgramTypeError: If the program type does not match any registered program types. """ try: return _get_program_type_alias(program) except ProgramTypeError as err: if safe: return None raise err