Source code for qutip_qip.algorithms.qpe

import numpy as np
from qutip import Qobj
from qutip_qip.typing import IntSequence
from qutip_qip.algorithms import qft_gate_sequence
from qutip_qip.circuit import QubitCircuit
from qutip_qip.operations import get_unitary_gate, get_controlled_gate
from qutip_qip.operations.gates import H


[docs] def qpe( U: Qobj, num_counting_qubits: int, target_qubits: int | IntSequence | None = None, to_cnot: bool = False, ) -> QubitCircuit: """ Quantum Phase Estimation circuit implementation for QuTiP. Parameters ---------- U : Qobj Unitary operator whose eigenvalue we want to estimate. Should be a unitary quantum operator. num_counting_qubits : int Number of counting qubits to use for the phase estimation. More qubits provide higher precision. target_qubits : int or sequence of int, optional Index or indices of the target qubit(s) where the eigenstate is prepared. If None, target_qubits is set automatically based on U's dimension. to_cnot : bool, optional Flag to decompose controlled phase gates to CNOT gates (default: False) Returns ------- qc : :class:`.QubitCircuit` Gate sequence implementing Quantum Phase Estimation. """ if num_counting_qubits < 1: raise ValueError("Minimum value of counting qubits must be 1") # Handle target qubits specification if target_qubits is None: dim = U.shape[0] num_target_qubits = int(np.log2(dim)) if 1 << num_target_qubits != dim: raise ValueError(f"Unitary operator dimension {dim} is not a power of 2") target_qubits = list( range(num_counting_qubits, num_counting_qubits + num_target_qubits) ) elif type(target_qubits) is int: target_qubits = [target_qubits] num_target_qubits = 1 else: num_target_qubits = len(target_qubits) total_qubits = num_counting_qubits + num_target_qubits qc = QubitCircuit(total_qubits) # Apply Hadamard gates to all counting qubits for i in range(num_counting_qubits): qc.add_gate(H, targets=[i]) # Apply controlled-U gates with increasing powers for i in range(num_counting_qubits): power = 2 ** (num_counting_qubits - i - 1) # Create U^power U_power = U if power == 1 else U**power # Add controlled-U^power gate controlled_u = get_controlled_gate( gate=get_unitary_gate( gate_name=f"U^{power}", U=U_power, ), ) qc.add_gate(controlled_u, targets=target_qubits, controls=[i]) # Add inverse QFT on counting qubits qc.add_circuit( qft_gate_sequence( num_counting_qubits, swapping=True, to_cnot=to_cnot ).reverse_circuit() ) return qc