Amplitude amplification
The AmplitudeAmplification
algorithm increases the probability of measuring a state in a chosen subspace.
It takes two Circuit objects:
state_prep_oracleprepares the initial state.good_state_oracleflips a flag qubit for the good subspace.
The circuit first prepares the initial state. Each round then flags the good subspace and reflects about the prepared state. If the initial probability of the marked subspace is \(a\), the probability after \(k\) rounds is
This gives the \(O(1/\sqrt{a})\) query scaling. More rounds are not
always better: after the first maximum, additional rounds reduce the success
probability. Choose rounds from an estimate of state overlap \(a\).
Using amplitude amplification
Note
This algorithm is currently available only in the Python API.
Creating an amplitude amplification algorithm
from qdk_chemistry.algorithms import create
# Number of Grover iterates. Choose it from an estimate of the overlap a,
# the success probability after k rounds is sin^2((2k+1) arcsin(sqrt(a))).
amplitude_amplification = create("amplitude_amplification", "qdk_base", rounds=2)
run takes the two oracles and reads the register width from a resource estimate of
the state preparation. The returned circuit measures the whole register. It also carries
the same amplification without measurement as a Q# callable, so a caller can append its
own measurement instead. See below for an example of amplifying a measurement-free QPE circuit.
Amplitude amplified QPE
Build a measurement-free QPE circuit, mark the target phase bin, and amplify.
phase_marking_oracle() reads the
register layout from the QPE circuit, so only the target bins have to be given:
import math
import numpy as np
from qdk_chemistry.algorithms import create, phase_marking_oracle
from qdk_chemistry.data import AlgorithmRef, Circuit, QubitOperator
from qdk_chemistry.data.circuit import QsharpFactoryData
from qdk_chemistry.utils.qsharp import QSHARP_UTILS
# 1. A two-qubit Hamiltonian
qubit_hamiltonian = QubitOperator(
pauli_strings=["ZI", "IZ"], coefficients=np.array([math.pi / 4.0, math.pi / 4.0])
)
# 2. A guiding state with 0.3 amplitude on the target eigenvector |11>
state_vector = [0.0, 0.0, 0.0, 0.0]
state_vector[3] = 0.3
state_vector[0] = math.sqrt(1.0 - 0.3**2)
prep_parameters = {
"rowMap": [1, 0],
"stateVector": state_vector,
"expansionOps": [],
"numQubits": 2,
}
state_preparation = Circuit(
qsharp_factory=QsharpFactoryData(
program=QSHARP_UTILS.StatePreparation.MakeStatePreparationCircuit,
parameter=prep_parameters,
),
qsharp_op=QSHARP_UTILS.StatePreparation.MakeStatePreparationOp(prep_parameters),
)
# 3. Build a measurement-free QPE circuit. This whole circuit is the preparation that
# gets amplified, so the phase register and its ancillas stay inside the amplified
# register and every round reflects about the full prepared state.
num_bits = 4
builder = create(
"qpe_circuit_builder",
"qdk_standard",
num_bits=num_bits,
controlled_circuit_mapper=AlgorithmRef(
"controlled_circuit_mapper", "prepare_select_prepare"
),
unitary_builder=AlgorithmRef(
"hamiltonian_unitary_builder", "lcu", quantum_walk=True
),
measure_phase=False,
)
state_prep_oracle = builder.run(
state_preparation=state_preparation, qubit_hamiltonian=qubit_hamiltonian
)[0]
# 4. Mark the phase bins holding the target eigenvalue. QPE writes the phase phi of
# the eigenvalue exp(2 pi i phi) into bin round(phi * 2**num_bits), so the half-open
# window (8, 9) accepts bin 8 alone, that is phi = 0.5.
target_phase_bins = (8, 9)
good_state_oracle = phase_marking_oracle(state_prep_oracle, target_phase_bins)
# The same window can be named by energy instead. The walk maps E to
# phi = arccos(E / lambda) / 2 pi, with lambda the L1 norm of the Hamiltonian, and
# marks both signs of that phase. Here it selects bin 8 again.
good_state_oracle = phase_marking_oracle(
state_prep_oracle,
target_energy_range=(-np.inf, -0.99 * qubit_hamiltonian.schatten_norm),
qubit_hamiltonian=qubit_hamiltonian,
)
# 5. Amplify, then execute
amplitude_amplification = create("amplitude_amplification", "qdk_base", rounds=2)
circuit = amplitude_amplification.run(state_prep_oracle, good_state_oracle)
executor = create("circuit_executor", "qdk_sparse_state_simulator")
shots = 400
counts = executor.run(circuit, shots=shots).bitstring_counts
The target can also be named as an energy window. This only applies to a QPE circuit built on a qubitization walk, whose eigenvalues are \(e^{\pm i\arccos(E/\lambda)}\) for \(\lambda\) the L1 norm of the Hamiltonian.
Alternatively, the marked phase window can be replaced by a reflection onto the target eigenspace built with quantum signal processing on a block encoding of the Hamiltonian, amplifying an initial state preparation directly. See Lin and Tong, arXiv:2002.12508, and its use in arXiv:2510.07273, Section 2.
Settings
Setting |
Type |
Description |
|---|---|---|
|
|
Number of Grover iterates (default 1). Must be non-negative. |
Further Reading
PhaseEstimation: the un-amplified algorithm.
QpeCircuitBuilder: builds the coherent preparation this algorithm amplifies.
Lin, L. Lecture Notes on Quantum Algorithms for Scientific Computation, arXiv:2201.08309, Chapter 2.
Brassard, G., Høyer, P., Mosca, M., and Tapp, A. Quantum Amplitude Amplification and Estimation, arXiv:quant-ph/0005055.
Lin, L. and Tong, Y. Near-optimal ground state preparation, arXiv:2002.12508: the signal-processing eigenspace reflection.