Source code for mqt.bench.benchmarks.iqpe
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License
"""IQPE benchmark definition."""
from __future__ import annotations
import random
from fractions import Fraction
import numpy as np
from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister
from ._registry import register_benchmark
[docs]
@register_benchmark("iqpe", description="Iterative Quantum Phase Estimation (IQPE)")
def create_circuit(
num_qubits: int, exact: bool = True, rotation_threshold: float = 1e-15, seed: int = 10
) -> QuantumCircuit:
"""Returns a quantum circuit implementing the Iterative Quantum Phase Estimation algorithm.
In contrast to the regular QPE algorithm, the @p num_qubits argument does not influence the final number of qubits of the circuit.
The circuit will always use exactly two qubits, but `num_qubits-1` iterations of the phase estimation loop.
This way, transforming the IQPE benchmark circuit to a straight-line circuit (by replacing reset operations with new qubits and applying the deferred measurement principle) results in the equivalent QPE benchmark circuit configured with the same value for `num_qubits`.
The name `num_qubits` is kept for consistency with the other benchmark generation functions.
Arguments:
num_qubits: Controls the precision of the phase estimation algorithm. Must be at least 2.
exact: Whether to use a target phase that is exactly representable with the chosen precision.
rotation_threshold: Threshold for adding rotation gates in the feedback step. Rotation gates with smaller angles will be omitted to reduce circuit complexity.
seed: Seed for the random number generator.
Returns:
QuantumCircuit: A quantum circuit implementing the Iterative Quantum Phase Estimation algorithm.
"""
if num_qubits <= 1:
msg = "Number of qubits must be at least 2 for IQPE."
raise ValueError(msg)
num_iterations = num_qubits - 1 # one ancilla qubit, rest for iterations
ancilla = QuantumRegister(1, "q")
psi = QuantumRegister(1, "psi")
c = ClassicalRegister(num_iterations, "c")
qc = QuantumCircuit(ancilla, psi, c, name="iqpe")
# get random n-bit string as target phase
theta = 0
random.seed(seed)
if exact:
while theta == 0:
theta = random.getrandbits(num_iterations)
lam = Fraction(theta, 1 << num_iterations) # phase to be estimated
elif not exact:
while theta == 0 or (theta & 1) == 0: # Ensure an odd theta so phase cannot be exactly represented with n bits
theta = random.getrandbits(num_iterations)
lam = Fraction(theta, 1 << (num_iterations + 1)) # phase to be estimated, with extra bit
qc.x(psi) # prepare |1> state
for i in range(num_iterations, 0, -1): # start from n
qc.h(ancilla[0]) # put ancilla in superposition
frac = (lam * (1 << (i - 1))) % 1 # exact Fraction in [0, 1)
angle = 2 * np.pi * frac # in [0, 2*pi)
qc.cp(angle, psi, ancilla[0])
for meas_idx in range(i, num_iterations): # bits already measured this run
with qc.if_test((c[meas_idx], 1)):
exponent = meas_idx - (i - 1) + 1
rotation_angle = -2 * np.pi * Fraction(1, 1 << exponent)
if abs(rotation_angle) > rotation_threshold: # avoid adding negligible gates
qc.rz(rotation_angle, ancilla[0])
qc.h(ancilla[0])
qc.measure(ancilla[0], c[i - 1])
with qc.if_test((c[i - 1], 1)):
qc.x(ancilla[0]) # reset ancilla for next iteration
return qc