Source code for qarp.blocks._primitives.swap_test_block

from typing import List, Optional

import qarpx as qx

from .._block import AnyBlock, CompositeBlockBase, MeasureBlock, SimpleBlock


[docs] class SWAPTestBlock(CompositeBlockBase): """SWAP test for the overlap ``|⟨bra|ket⟩|²`` using one ancilla qubit. Pattern B (composite). Layout: qubit 0 is the ancilla; qubits ``1 .. n_state`` hold the ket register; qubits ``1 + n_state .. 1 + 2*n_state - 1`` hold the bra register. The circuit: 1. ``H`` on ancilla. 2. ``ket`` preparation on the ket register. 3. ``bra`` preparation on the bra register. 4. ``CSWAP(ancilla, ket[i], bra[i])`` for each ``i``. 5. ``H`` on ancilla. 6. (optional) ``Measure`` ancilla into cbit 0. The resulting ancilla expectation ``P(0) - P(1) = |⟨bra|ket⟩|²``. """ def __init__( self, bra: AnyBlock, ket: AnyBlock, measure: bool = False, target_qubits: Optional[List[int]] = None, name: str = "SWAPTest", ): """Build the SWAP test circuit. Args: bra: The block corresponding to the bra preparation unitary. ket: The block corresponding to the ket preparation unitary. Must have the same ``n_qubits`` as ``bra``. measure: If True, measure the ancilla into cbit 0. target_qubits: standard Block kwarg. """ if not isinstance(bra, qx.Block): raise TypeError("bra must be a Block instance") if not isinstance(ket, qx.Block): raise TypeError("ket must be a Block instance") if bra.n_qubits != ket.n_qubits: raise ValueError( f"Qubit count mismatch: bra has {bra.n_qubits} qubits, " f"ket has {ket.n_qubits} qubits" ) n_state = bra.n_qubits super().__init__( n_qubits=1 + 2 * n_state, target_qubits=target_qubits, name=name, ) self.bra = bra self.ket = ket self.measure_at_end = measure
[docs] def build_vanilla(self) -> None: from copy import deepcopy ancilla = 0 n_state = self.bra.n_qubits ket_qubits = list(range(1, 1 + n_state)) bra_qubits = list(range(1 + n_state, 1 + 2 * n_state)) # 1. H on ancilla h_pre = SimpleBlock(1, name="AncillaH_pre") h_pre.h(0) h_pre.target_qubits = [ancilla] self.add_wired_child(h_pre) # 2. ket preparation # 3. bra preparation # Always deepcopy both ``bra`` and ``ket`` builds before mutating # ``target_qubits``: the same Python block can appear as both bra # and ket *or* nested inside one (e.g. ``InterferometricTest`` wraps # ``ket`` into a ``ket+operator`` composite while passing the inner # ``ket`` directly as ``bra``). Mutating ``target_qubits`` in place # on a shared instance corrupts every other reference. ket_built = deepcopy(self.ket.build()) ket_built.target_qubits = ket_qubits self.add_child(ket_built) bra_built = deepcopy(self.bra.build()) bra_built.target_qubits = bra_qubits self.add_child(bra_built) # 4. CSWAP(ancilla, ket[i], bra[i]) for each i cswap_block = SimpleBlock(self.n_qubits, name="CSWAPs") for i in range(n_state): cswap_block.cswap(ancilla, ket_qubits[i], bra_qubits[i]) self.add_wired_child(cswap_block) # 5. H on ancilla h_post = SimpleBlock(1, name="AncillaH_post") h_post.h(0) h_post.target_qubits = [ancilla] self.add_wired_child(h_post) # 6. Optional measurement if self.measure_at_end: self.add_child(MeasureBlock(ancilla, 0))