Source code for qdk_chemistry.algorithms.hadamard_test.hadamard_test

"""QDK/Chemistry Hadamard test circuit generator abstractions and utilities."""

# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from enum import Enum
from typing import Any

from qdk_chemistry.algorithms.base import Algorithm, AlgorithmFactory
from qdk_chemistry.data import (
    AlgorithmRef,
    Circuit,
    CircuitExecutorData,
    Settings,
    UnitaryRepresentation,
)

__all__: list[str] = [
    "HadamardTest",
    "HadamardTestBasis",
    "HadamardTestFactory",
    "basis_to_qsharp_pauli",
]


[docs] class HadamardTestBasis(Enum): """Measurement bases supported by the Hadamard test control qubit.""" X = "X" Y = "Y" def __str__(self) -> str: """Return the string label ("X" or "Y") for this basis.""" return str(self.value)
[docs] def basis_to_qsharp_pauli(basis: HadamardTestBasis) -> Any: """Map a ``HadamardTestBasis`` to ``qsharp.Pauli`` for Q# interop.""" from qdk import qsharp as _qsharp # noqa: PLC0415 return getattr(_qsharp.Pauli, basis.value)
class HadamardTestSettings(Settings): """Settings for the Hadamard test algorithm.""" def __init__(self): """Initialize the settings for the Hadamard test. Includes optional nested algorithm references for the circuit executor, controlled circuit mapper, and the measurement basis for the control qubit. """ super().__init__() self._set_default( "controlled_circuit_mapper", "algorithm_ref", AlgorithmRef("controlled_circuit_mapper", "pauli_sequence"), ) self._set_default( "circuit_executor", "algorithm_ref", AlgorithmRef("circuit_executor", "qdk_full_state_simulator"), ) self._set_default( "test_basis", "string", HadamardTestBasis.X.value, "Measurement basis for the control qubit ('X' or 'Y').", [basis.value for basis in HadamardTestBasis], )
[docs] class HadamardTest(Algorithm): """Hadamard test generator. Orchestrates the backend-agnostic Hadamard test workflow: it validates the inputs, delegates circuit construction (including mapping the target unitary into a controlled evolution circuit) to ``hadamard_test_circuit_builder`` (currently fixed to ``qdk``), and executes the resulting circuit with the nested ``circuit_executor``. """
[docs] def __init__( self, test_basis: HadamardTestBasis = HadamardTestBasis.X, circuit_executor: AlgorithmRef | None = None, controlled_circuit_mapper: AlgorithmRef | None = None, ): """Initialize a Hadamard test generator. Args: test_basis: Measurement basis for the control qubit. circuit_executor: Optional algorithm reference for circuit execution. controlled_circuit_mapper: Optional algorithm reference for controlled circuit mapping. """ super().__init__() self._settings = HadamardTestSettings() if isinstance(test_basis, HadamardTestBasis): test_basis_value = test_basis.value elif isinstance(test_basis, str): try: test_basis_value = HadamardTestBasis(test_basis).value except ValueError as err: raise ValueError("test_basis must be one of {'X', 'Y'}.") from err else: raise TypeError("test_basis must be an instance of HadamardTestBasis or a string.") self._settings.set("test_basis", test_basis_value) if circuit_executor is not None: if not isinstance(circuit_executor, AlgorithmRef): raise TypeError("circuit_executor must be an instance of AlgorithmRef.") self._settings.set("circuit_executor", circuit_executor) if controlled_circuit_mapper is not None: if not isinstance(controlled_circuit_mapper, AlgorithmRef): raise TypeError("controlled_circuit_mapper must be an instance of AlgorithmRef.") self._settings.set("controlled_circuit_mapper", controlled_circuit_mapper)
[docs] def type_name(self) -> str: """Return the algorithm type name as hadamard_test.""" return "hadamard_test"
def _run_impl( self, state_preparation_circuit: Circuit, unitary: UnitaryRepresentation, shots: int, ) -> CircuitExecutorData: r"""Run the Hadamard test by building and executing a backend-specific circuit. The measurement basis is read from this algorithm's settings (``test_basis``). Args: state_preparation_circuit: Circuit that prepares the trial state on system qubits. unitary: Unitary representation :math:`U` (e.g. a time-evolution unitary built with the desired power). shots: Number of shots to execute the circuit. Returns: CircuitExecutorData returned directly by the given simulator. """ if not isinstance(state_preparation_circuit, Circuit): raise TypeError("state_preparation_circuit must be an instance of Circuit.") if not isinstance(unitary, UnitaryRepresentation): raise TypeError("unitary must be an instance of UnitaryRepresentation.") if not isinstance(shots, int): raise TypeError("shots must be an integer.") if shots <= 0: raise ValueError("shots must be a positive integer.") test_basis = HadamardTestBasis(self._settings.get("test_basis")) from qdk_chemistry.algorithms import create # noqa: PLC0415 circuit_builder = create("hadamard_test_circuit_builder", "qdk") circuit_builder.settings().set("test_basis", test_basis.value) controlled_circuit_mapper = self._settings.get("controlled_circuit_mapper") circuit_builder.settings().set("controlled_circuit_mapper", controlled_circuit_mapper) circuit = circuit_builder.run( state_preparation_circuit, unitary, ) circuit_executor_ref = self._settings.get("circuit_executor") if circuit_executor_ref.algorithm_type != "circuit_executor": raise ValueError( "circuit_executor must reference algorithm type 'circuit_executor', " f"got '{circuit_executor_ref.algorithm_type}'." ) circuit_executor = self._create_nested("circuit_executor") return circuit_executor.run(circuit, shots=shots)
[docs] def name(self) -> str: """Return the name of the Hadamard test algorithm.""" return "qdk"
[docs] class HadamardTestFactory(AlgorithmFactory): """Factory class for creating Hadamard test generator instances."""
[docs] def __init__(self): """Initialize the HadamardTestFactory.""" super().__init__()
[docs] def algorithm_type_name(self) -> str: """Return the algorithm type name as hadamard_test.""" return "hadamard_test"
[docs] def default_algorithm_name(self) -> str: """Return 'qdk' as the default algorithm name.""" return "qdk"