Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

### Added

- ✨ Add Dynamic QFT benchmark ([#XXX]) ([**@inesalonsoo**])
- ✨ Add Iterative Quantum Phase Estimation (IQPE) benchmark ([#925]) ([**@johanneswittmann9**])

### Fixed
Expand Down
43 changes: 43 additions & 0 deletions src/mqt/bench/benchmarks/dynamic_qft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 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

"""Dynamical QFT benchmark definition."""

from __future__ import annotations

import numpy as np
from qiskit import ClassicalRegister
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library import PhaseGate

from ._registry import register_benchmark


@register_benchmark("dynamic_qft", description="Dynamic QFT")
def create_circuit(num_qubits: int) -> QuantumCircuit:
"""Return a circuit implementing the Dynamic QFT."""
if num_qubits < 1:
msg = "The number of qubits must be at least 1."
raise ValueError(msg)

q = QuantumRegister(num_qubits, "q")
c = ClassicalRegister(num_qubits, "c")
qc = QuantumCircuit(q, c, name="dynamic_qft")

# Forward loop that mirrors the gate order and qubit indices of the static QFT
for i in range(num_qubits):
qc.h(q[i])
qc.measure(q[i], c[i])

# Apply feed-forward phase corrections to all remaining qubits
for j in range(i + 1, num_qubits):
angle = np.pi / (2 ** (j - i))
p_gate = PhaseGate(angle).c_if(c[i], 1)
qc.append(p_gate, [q[j]])

return qc
16 changes: 15 additions & 1 deletion tests/test_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def test_arithmetic_circuits(benchmark_name: str, input_value: int) -> None:
("ae", 1, None, r"Number of qubits must be at least 2 \(1 evaluation \+ 1 target\)."),
("shors_nine_qubit_code", 9, None, "num_qubits must be divisible by 17."),
("seven_qubit_steane_code", 9, None, "num_qubits must be divisible by 13."),
("dynamic_qft", 0, None, "The number of qubits must be at least 1."),
],
)
def test_wrong_circuit_size(benchmark_name: str, input_value: int, kind: str | None, msg: str) -> None:
Expand Down Expand Up @@ -345,6 +346,19 @@ def test_shors_nine_qubit_code_circuit_structure(num_qubits: int) -> None:
)


@pytest.mark.parametrize("num_qubits", [1, 2, 4, 8])
def test_dynamic_qft_circuit_structure(num_qubits: int) -> None:
"""Test that the dynamic QFT allocates registers and compiles."""
qc = create_circuit("dynamic_qft", num_qubits)

assert qc.num_qubits == num_qubits
assert qc.name == "dynamic_qft"

# Verify both a quantum and classical register exist with matching sizes
assert len(qc.qregs) == 1
assert len(qc.cregs) == 1


@pytest.mark.parametrize("num_qubits", [13, 26, 39, 52])
def test_seven_qubit_steane_code_circuit_structure(num_qubits: int) -> None:
"""Test that Steane 7-qubit code circuits have the expected structure.
Expand Down Expand Up @@ -409,7 +423,7 @@ def test_seven_qubit_steane_code_circuit_structure(num_qubits: int) -> None:
get_target_for_gateset("ionq_forte", num_qubits=5),
0,
),
("qft", BenchmarkLevel.NATIVEGATES, 3, get_target_for_gateset("rigetti", 5), 2),
("dynamic_qft", BenchmarkLevel.NATIVEGATES, 3, get_target_for_gateset("rigetti", 5), 2),
# Mapped level tests
(
"ghz",
Expand Down
Loading