Source code for qarp.blocks._primitives.amplitude_estimation_block
"""Measurement-free canonical quantum amplitude estimation."""
from copy import deepcopy
from typing import Optional
from .._block import AnyBlock, CompositeBlockBase
from .amplitude_amplification_block import (
AmplitudeAmplificationBlock,
validate_amplification_blocks,
)
from .qpe_block import QPEBlock
def validate_n_ancilla(n_ancilla: int) -> int:
"""Validate and return a positive canonical-QAE precision width."""
if isinstance(n_ancilla, bool) or not isinstance(n_ancilla, int):
raise TypeError("n_ancilla must be an integer")
if n_ancilla < 1:
raise ValueError("n_ancilla must be positive")
return n_ancilla
[docs]
class AmplitudeEstimationBlock(CompositeBlockBase):
r"""Canonical QAE circuit for a phase-exact amplification iterate.
For ``A|0> = sqrt(1-a)|psi_bad> + sqrt(a)|psi_good>`` and an oracle
implementing exactly ``O_good = I - 2 Pi_good``, the embedded
:class:`AmplitudeAmplificationBlock` has relevant eigenphases
``+/- 2 theta``, where ``sin(theta)**2 = a``. This block applies QPE to
that iterate without adding measurements.
Qubits ``0 .. n_ancilla-1`` form the estimation register and the state
register follows it. Caller-owned blocks are deep-copied and never built,
retargeted, or otherwise mutated.
The construction follows Brassard, Hoyer, Mosca, and Tapp, *Quantum
Amplitude Amplification and Estimation*, arXiv:quant-ph/0005055.
Args:
state_preparation: Unitary ``A`` preparing the initial state.
oracle: Good-state phase oracle implementing ``I - 2 Pi_good``.
n_ancilla: Positive number of estimation qubits.
target_qubits: Optional placement of the complete QAE circuit.
name: Block name.
"""
def __init__(
self,
state_preparation: AnyBlock,
oracle: AnyBlock,
n_ancilla: int,
target_qubits: Optional[list[int]] = None,
name: str = "AmplitudeEstimation",
) -> None:
self.n_state_qubits = validate_amplification_blocks(state_preparation, oracle)
self.n_ancilla = validate_n_ancilla(n_ancilla)
self.state_preparation = deepcopy(state_preparation)
self.oracle = deepcopy(oracle)
super().__init__(
n_qubits=self.n_ancilla + self.n_state_qubits,
target_qubits=target_qubits,
name=name,
)
[docs]
def build_vanilla(self) -> None:
# QPEBlock builds and retargets its eigenstate in place; the iterate copies its own.
state_preparation = deepcopy(self.state_preparation)
iterate = AmplitudeAmplificationBlock(self.state_preparation, self.oracle)
qpe = QPEBlock(
eigenstate=state_preparation,
unitary=iterate,
n_ancilla=self.n_ancilla,
n_state=self.n_state_qubits,
measure=False,
name="CanonicalQAE",
)
self.add_wired_child(qpe)