Skip to content

Commit 8d20f9d

Browse files
committed
feat: add dynamic qft benchmark
Assisted-by: GPT-5 via Codex
1 parent c704c91 commit 8d20f9d

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
2+
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
3+
# All rights reserved.
4+
#
5+
# SPDX-License-Identifier: MIT
6+
#
7+
# Licensed under the MIT License
8+
9+
"""Dynamic QFT benchmark definition."""
10+
11+
from __future__ import annotations
12+
13+
import math
14+
15+
from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister
16+
17+
from ._registry import register_benchmark
18+
19+
_MAX_NUM_QUBITS = 64
20+
21+
22+
@register_benchmark("dynamic_qft", description="Dynamic QFT")
23+
def create_circuit(num_qubits: int) -> QuantumCircuit:
24+
"""Return a circuit implementing the Dynamic QFT.
25+
26+
This benchmark follows the semiclassical Quantum Fourier Transform by
27+
Griffiths and Niu. Each qubit is transformed with a Hadamard gate, measured
28+
immediately, and then its classical result controls phase rotations on the
29+
remaining qubits. This replaces the usual controlled-phase gates with
30+
mid-circuit measurements and classical feed-forward while keeping the same
31+
triangular phase-angle structure as the standard QFT.
32+
33+
Arguments:
34+
num_qubits: number of qubits of the returned quantum circuit. Must be at most 64.
35+
36+
Returns:
37+
QuantumCircuit: a quantum circuit implementing the Dynamic QFT.
38+
39+
Raises:
40+
ValueError: if more than 64 qubits are requested.
41+
"""
42+
if num_qubits > _MAX_NUM_QUBITS:
43+
msg = "Dynamic QFT supports at most 64 qubits."
44+
raise ValueError(msg)
45+
46+
q = QuantumRegister(num_qubits, "q")
47+
c = ClassicalRegister(num_qubits, "c")
48+
qc = QuantumCircuit(q, c, name="dynamic_qft")
49+
50+
for measured_qubit in range(num_qubits):
51+
qc.h(q[measured_qubit])
52+
qc.measure(q[measured_qubit], c[measured_qubit])
53+
54+
for offset in range(1, num_qubits - measured_qubit):
55+
with qc.if_test((c[measured_qubit], 1)):
56+
qc.p(math.pi / (2**offset), q[measured_qubit + offset])
57+
58+
return qc

tests/test_bench.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import datetime
1515
import functools
1616
import io
17+
import math
1718
import re
1819
from enum import Enum
1920
from importlib import metadata
@@ -252,7 +253,55 @@ def test_graphstate_seed() -> None:
252253
assert qc_no_seed.name == "graphstate"
253254

254255

255-
# Test the dynamic GHZ circuit
256+
# Test the dynamic circuits
257+
def _conditional_phase_angles(qc: QuantumCircuit) -> list[float]:
258+
"""Return phase angles applied inside conditional blocks."""
259+
angles = []
260+
for instruction in qc.data:
261+
operation = instruction.operation
262+
if isinstance(operation, IfElseOp):
263+
angles.extend(
264+
float(conditional_instruction.operation.params[0])
265+
for conditional_instruction in operation.blocks[0].data
266+
if conditional_instruction.operation.name == "p"
267+
)
268+
return angles
269+
270+
271+
def test_dynamic_qft_description() -> None:
272+
"""Verify that the Dynamic QFT benchmark is registered with its canonical name."""
273+
description = get_benchmark_description("dynamic_qft")
274+
assert "Dynamic QFT" in description
275+
assert "Dynamical" not in description
276+
277+
278+
def test_dynamic_qft_circuit_structure() -> None:
279+
"""Verify the structure of the Dynamic QFT benchmark."""
280+
qc = create_circuit("dynamic_qft", 5)
281+
282+
assert qc.name == "dynamic_qft"
283+
assert qc.num_qubits == 5
284+
assert len(qc.cregs) == 1
285+
assert qc.cregs[0].name == "c"
286+
assert qc.cregs[0].size == 5
287+
288+
ops = qc.count_ops()
289+
assert ops.get("h", 0) == 5
290+
assert ops.get("measure", 0) == 5
291+
assert ops.get("if_else", 0) == 10
292+
assert ops.get("cp", 0) == 0
293+
294+
expected_angles = sorted(round(math.pi / (2**shift), 12) for shift in range(1, 5) for _ in range(5 - shift))
295+
actual_angles = sorted(round(angle, 12) for angle in _conditional_phase_angles(qc))
296+
assert actual_angles == expected_angles
297+
298+
299+
def test_dynamic_qft_rejects_too_many_qubits() -> None:
300+
"""Verify that Dynamic QFT rejects instances with impractically small phase shifts."""
301+
with pytest.raises(ValueError, match="at most 64 qubits"):
302+
create_circuit("dynamic_qft", 65)
303+
304+
256305
@pytest.mark.parametrize("num_qubits", [1, 2, 3, 7, 10])
257306
def test_dynamic_ghz_circuit_structure(num_qubits: int) -> None:
258307
"""Verify the structure of the dynamic GHZ circuit for various qubit counts."""

0 commit comments

Comments
 (0)