Source code for ofex.sampling_simulation.hadamard_test

from numbers import Number
from typing import Tuple, Optional

import numpy as np
from openfermion import QubitOperator
from openfermion.config import EQ_TOLERANCE

from ofex.linalg.sparse_tools import transition_amplitude, state_dot
from ofex.sampling_simulation.sampling_base import ProbDist
from ofex.state.types import State

__all__ = ["hadamard_test_general", "hadamard_test_qubit_operator", "hadamard_test_fermion_operator"]

[docs] def hadamard_test_general(overlap: complex, imaginary: bool, coeff: Number = 1.0,) \ -> ProbDist: r""" Computes the probability distribution for a Hadamard Test. This function generates the probability distribution for a Hadamard Test, where the expectation value is computed as :math:``c \braket{\phi_1 | \phi_2}``. It supports computation for both the real and imaginary components of the overlap. Args: overlap (complex): The overlap value :math:``\braket{\phi_1 | \phi_2}``, provided as a complex number. imaginary (bool): If True, computes the probability distribution for the imaginary component of the overlap. If False, computes the real component. coeff (Number, optional): A scaling coefficient :math:``c`` applied to the probabilities. Defaults to 1.0. Returns: ProbDist: The probability distribution for the chosen component (real or imaginary) of the expectation value. Raises: ValueError: If the absolute value of the overlap exceeds 1.0, which indicates a non-unitary operator. """ ab_overlap = abs(overlap) if not (np.isclose(ab_overlap, 1.0, atol=EQ_TOLERANCE) or ab_overlap < 1.0): raise ValueError(f"Operator seems non-unitary (abs(overlap)={ab_overlap} exceeds 1.0).") if not imaginary: p0 = 0.5 * (overlap.real + 1) else: p0 = 0.5 * (overlap.imag + 1) probdist = ProbDist({coeff: p0, -coeff: 1 - p0}) return probdist
[docs] def hadamard_test_qubit_operator(ref_state_1: State, ref_state_2: State, unitary: Optional[QubitOperator] = None, coeff=1.0, sparse_1: bool = False, sparse_2: bool = False,) \ -> Tuple[ProbDist, ProbDist]: r""" Computes probability distributions for the Hadamard Test expectation value of a qubit operator. This function calculates the real and imaginary components of the expectation value: .. math:: c \braket{\phi_1 | U | \phi_2}, where :math:`U` is an optional unitary operator applied to the second reference state :math:`\ket{ \phi_2 }`. If the scaling coefficient :math:`c` is zero, the function returns trivial distributions. Args: ref_state_1 (State): The first reference state :math:`\ket{ \phi_1 }`. ref_state_2 (State): The second reference state :math:`\ket{ \phi_2 }`. unitary (Optional[QubitOperator]): An optional unitary operator :math:`U` applied to :math:`\ket{ \phi_2 }`. If not provided, no unitary is applied. coeff (float, optional): A scaling coefficient applied to the probabilities. Defaults to 1.0. sparse_1 (bool, optional): A flag indicating if `ref_state_1` is in sparse format. Defaults to False. sparse_2 (bool, optional): A flag indicating if `ref_state_2` is in sparse format. Defaults to False. Returns: Tuple[ProbDist, ProbDist]: A tuple containing two probability distributions: - The first distribution corresponds to the real component of the expectation value. - The second distribution corresponds to the imaginary component of the expectation value. Raises: ValueError: If the scaling coefficient :math:`\text{coeff}` is not a finite number. """ if np.isclose(coeff, 0, atol=EQ_TOLERANCE): probdist_real = ProbDist({coeff: 1}) probdist_imag = ProbDist({coeff: 1}) return probdist_real, probdist_imag if unitary is not None: overlap = transition_amplitude(unitary, ref_state_1, ref_state_2, sparse_1, sparse_2) else: overlap = state_dot(ref_state_1, ref_state_2) prob_re = hadamard_test_general(overlap, False, coeff) prob_im = hadamard_test_general(overlap, True, coeff) return prob_re, prob_im
def hadamard_test_fermion_operator(ref_state_1: State, ref_state_2: State, unitary: Optional[QubitOperator] = None, coeff=1.0) \ -> Tuple[ProbDist, ProbDist]: raise NotImplementedError