Source code for mqt.qcec.verify_compilation_flow

"""Verify compilation flow results."""

from __future__ import annotations

import sys
import warnings
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing_extensions import Unpack

    from .configuration import ConfigurationOptions

if TYPE_CHECKING or sys.version_info < (3, 10, 0):
    import importlib_resources as resources
else:
    from importlib import resources

from qiskit import QuantumCircuit
from qiskit.transpiler.passes import ContainsInstruction

from . import ApplicationScheme, Configuration, EquivalenceCheckingManager
from .compilation_flow_profiles import AncillaMode, generate_profile_name
from .configuration import augment_config_from_kwargs
from .verify import verify


def __check_if_circuit_contains_measurements(circuit: QuantumCircuit) -> None:
    """Check if the circuit contains measurements and emits a warning if it does not.

    Args:
        circuit: The circuit to check.
    """
    analysis_pass = ContainsInstruction("measure")
    analysis_pass(circuit)
    if not analysis_pass.property_set["contains_measure"]:
        warnings.warn(
            UserWarning(
                "One of the circuits does not contain any measurements. "
                "This may lead to unexpected results since the measurements are used "
                "to infer the output qubit permutation at the end of the circuit. "
                "Please consider adding measurements to the circuit _before_ compilation."
            ),
            stacklevel=2,
        )


[docs] def verify_compilation( original_circuit: QuantumCircuit | str, compiled_circuit: QuantumCircuit | str, optimization_level: int = 1, ancilla_mode: AncillaMode = AncillaMode.NO_ANCILLA, configuration: Configuration | None = None, **kwargs: Unpack[ConfigurationOptions], ) -> EquivalenceCheckingManager.Results: """Verify compilation flow results. Similar to :func:`verify <.verify>`, but uses a dedicated compilation flow profile to guide the equivalence checking process. The compilation flow profile is determined by the ``optimization_level`` and ``ancilla_mode`` arguments. There are two (non-exclusive) ways of configuring the equivalence checking process: 1. Pass a :class:`Configuration <.Configuration>` instance as the ``configuration`` argument. 2. Pass keyword arguments to this function. These are directly incorporated into the :class:`Configuration <.Configuration>`. Any existing configuration is overridden by keyword arguments. Args: original_circuit: The original circuit. compiled_circuit: The compiled circuit. optimization_level: The optimization level used for compiling the circuit (0, 1, 2, or 3). Defaults to 1. ancilla_mode: The `ancilla mode <.AncillaMode>` used for realizing multi-controlled Toffoli gates, as available in Qiskit. Defaults to :attr:`.AncillaMode.NO_ANCILLA`. configuration: The configuration to use for the equivalence checking process. **kwargs: Keyword arguments to configure the equivalence checking process. Returns: The results of the equivalence checking process. """ if configuration is None: configuration = Configuration() # prepare the configuration augment_config_from_kwargs(configuration, kwargs) if isinstance(original_circuit, QuantumCircuit): __check_if_circuit_contains_measurements(original_circuit) if isinstance(compiled_circuit, QuantumCircuit): __check_if_circuit_contains_measurements(compiled_circuit) # use the gate_cost scheme for the verification configuration.application.construction_scheme = ApplicationScheme.gate_cost configuration.application.simulation_scheme = ApplicationScheme.gate_cost configuration.application.alternating_scheme = ApplicationScheme.gate_cost # get the pre-defined profile for the gate_cost scheme profile_name = generate_profile_name(optimization_level=optimization_level, mode=ancilla_mode) ref = resources.files("mqt.qcec") / "profiles" / profile_name with resources.as_file(ref) as path: configuration.application.profile = str(path) return verify(original_circuit, compiled_circuit, configuration=configuration)