Source code for qarp.blocks._primitives.qpe_block

from typing import List, Optional

from .._block import AnyBlock, CompositeBlockBase, ControlledBlock
from .._primitives import QFTBlock
from .hn_block import HnBlock
from .readout_block import ReadoutBlock


[docs] class QPEBlock(CompositeBlockBase): """Quantum Phase Estimation (QPE) circuit block — Pattern B composite. Composes: ancilla Hadamards · state prep · controlled-U^(2^i) ladder · inverse QFT · (optional) ancilla measurements. """ def __init__( self, eigenstate: AnyBlock, unitary: AnyBlock, n_ancilla: int, n_state: int, measure: bool = True, target_qubits: Optional[List[int]] = None, name: str = "QPE", ): """Quantum Phase Estimation: controlled-U powers on an ancilla register followed by an inverse QFT. Args: eigenstate: Block preparing the probe state (on the state register). unitary: Block of the unitary operator. n_ancilla: Number of ancilla qubits (phase precision bits). n_state: Number of qubits in the eigenstate circuit. measure: If True, add Measure commands on the ancilla register. target_qubits, name: see ``Block``. """ self.eigenstate = eigenstate self.unitary = unitary self.n_ancilla = n_ancilla self.n_state = n_state self.measure_at_end = measure super().__init__( n_qubits=n_ancilla + n_state, target_qubits=target_qubits, name=name, )
[docs] def build_vanilla(self) -> None: # Build children: each `built` is a real qx.Block (Python wrapper). eigen_built = self.eigenstate.build() unit_built = self.unitary.build() n_q = self.n_qubits # n_ancilla + n_state ancilla_qubits = list(range(self.n_ancilla)) state_qubits = list(range(self.n_ancilla, n_q)) # 1) Hadamard layer on ancillas self.add_wired_child(HnBlock(self.n_ancilla, target_qubits=ancilla_qubits, name="AncillaH")) # 2) Eigenstate prep on state register — remap onto state qubits. eigen_built.target_qubits = state_qubits self.add_child(eigen_built) # 3) Controlled-U^(2^i) ladder. Ancilla i gets 2^i applications of # the controlled unitary → phase kickback 2^i·φ, so the ancilla # register encodes the phase as an integer (qubit 0 = LSB). # After the inverse QFT the raw outcome equals 2^n_ancilla · φ, # which qpe.py reads via ``int("".join(bitstring), 2)`` (MSB-first). for i, ancilla_q in enumerate(ancilla_qubits): for _ in range(2**i): ctrl_u = ControlledBlock( unit_built, num_controls=1, ctrl_state=[True], name=f"C-U@a{ancilla_q}", ) ctrl_u.build() ctrl_u.target_qubits = [ancilla_q] + state_qubits self.add_child(ctrl_u) # 4) Inverse QFT on ancilla register iqft = QFTBlock(self.n_ancilla).dagger().build() iqft.target_qubits = ancilla_qubits self.add_child(iqft) # 5) Optional ancilla measurements — ancilla q reads into cbit q. if self.measure_at_end: self.add_wired_child( ReadoutBlock(self.n_ancilla, target_qubits=ancilla_qubits, name="AncillaMeas") )