Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- ✨ Add Dynamic QFT benchmark ([#918]) ([**@gshaowei6**])

### Fixed

- 🐛 Make IQM Crystal device connectivity bidirectional ([#914]) ([**@flowerthrower**])
Expand Down Expand Up @@ -126,6 +130,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._

<!-- PR links -->

[#918]: https://github.com/munich-quantum-toolkit/bench/pull/918
[#914]: https://github.com/munich-quantum-toolkit/bench/pull/914
[#895]: https://github.com/munich-quantum-toolkit/bench/pull/895
[#865]: https://github.com/munich-quantum-toolkit/bench/pull/865
Expand Down Expand Up @@ -171,6 +176,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._
<!-- Contributor -->

[**@burgholzer**]: https://github.com/burgholzer
[**@gshaowei6**]: https://github.com/gshaowei6
[**@ystade**]: https://github.com/ystade
[**@simon1hofmann**]: https://github.com/simon1hofmann
[**@nquetschlich**]: https://github.com/nquetschlich
Expand Down
50 changes: 50 additions & 0 deletions src/mqt/bench/benchmarks/dynamic_qft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 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

"""Dynamic QFT benchmark definition."""

from __future__ import annotations

import math

from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Align the classical output convention with the existing qft benchmark. The measured outcomes should be written to the classical bits in the reversed order.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed measurements to write q[i] into c[num_qubits - i - 1] and kept each conditional phase controlled by that same measured classical bit. Added a structure assertion for the reversed measurement mapping.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is still a small misalignment between the regular qft benchmark and your version. I encourage you to (visually) inspect and compare the following two circuits. Both circuits should have a matching operation order on same qubit and measurement indices.

from mqt.bench import get_benchmark, BenchmarkLevel

# original qft benchmark
circ = get_benchmark("qft", BenchmarkLevel.ALG, circuit_size=3)
circ.draw("mpl", fold=-1)

# your solution
circ = get_benchmark("dynamic_qft", BenchmarkLevel.ALG, circuit_size=3)
circ.decompose().draw("mpl", fold=-1)


This benchmark follows the semiclassical Quantum Fourier Transform by
Griffiths and Niu. Each qubit is transformed with a Hadamard gate, measured
immediately, and then its classical result controls phase rotations on the
remaining qubits. This replaces the usual controlled-phase gates with
mid-circuit measurements and classical feed-forward while keeping the same
triangular phase-angle structure as the standard QFT.

Arguments:
num_qubits: number of qubits of the returned quantum circuit.

Returns:
QuantumCircuit: a quantum circuit implementing the Dynamic QFT.
"""
q = QuantumRegister(num_qubits, "q")
c = ClassicalRegister(num_qubits, "c")
qc = QuantumCircuit(q, c, name="dynamic_qft")

for measured_qubit in range(num_qubits):
measured_bit = num_qubits - measured_qubit - 1
qc.h(q[measured_qubit])
qc.measure(q[measured_qubit], c[measured_bit])

for offset in range(1, num_qubits - measured_qubit):
with qc.if_test((c[measured_bit], 1)):
qc.p(math.pi / (2**offset), q[measured_qubit + offset])

return qc
59 changes: 58 additions & 1 deletion tests/test_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import datetime
import functools
import io
import math
import re
from enum import Enum
from importlib import metadata
Expand Down Expand Up @@ -252,7 +253,63 @@ def test_graphstate_seed() -> None:
assert qc_no_seed.name == "graphstate"


# Test the dynamic GHZ circuit
# Test the dynamic circuits
def _conditional_phase_angles(qc: QuantumCircuit) -> list[float]:
"""Return phase angles applied inside conditional blocks."""
angles = []
for instruction in qc.data:
operation = instruction.operation
if isinstance(operation, IfElseOp):
angles.extend(
float(conditional_instruction.operation.params[0])
for conditional_instruction in operation.blocks[0].data
if conditional_instruction.operation.name == "p"
)
return angles


def test_dynamic_qft_description() -> None:
"""Verify that the Dynamic QFT benchmark is registered with its canonical name."""
description = get_benchmark_description("dynamic_qft")
assert "Dynamic QFT" in description
assert "Dynamical" not in description


def test_dynamic_qft_circuit_structure() -> None:
"""Verify the structure of the Dynamic QFT benchmark."""
qc = create_circuit("dynamic_qft", 5)

assert qc.name == "dynamic_qft"
assert qc.num_qubits == 5
assert len(qc.cregs) == 1
assert qc.cregs[0].name == "c"
assert qc.cregs[0].size == 5

ops = qc.count_ops()
assert ops.get("h", 0) == 5
assert ops.get("measure", 0) == 5
assert ops.get("if_else", 0) == 10
assert ops.get("cp", 0) == 0
assert [
(qc.find_bit(instruction.qubits[0]).index, qc.find_bit(instruction.clbits[0]).index)
for instruction in qc.data
if instruction.operation.name == "measure"
] == [(qubit, 4 - qubit) for qubit in range(5)]

expected_angles = sorted(round(math.pi / (2**shift), 12) for shift in range(1, 5) for _ in range(5 - shift))
actual_angles = sorted(round(angle, 12) for angle in _conditional_phase_angles(qc))
assert actual_angles == expected_angles


def test_dynamic_qft_supports_large_instances() -> None:
"""Verify that Dynamic QFT supports arbitrary benchmark sizes like the regular QFT."""
qc = create_circuit("dynamic_qft", 65)

assert qc.num_qubits == 65
assert qc.cregs[0].size == 65
assert qc.count_ops().get("measure", 0) == 65


@pytest.mark.parametrize("num_qubits", [1, 2, 3, 7, 10])
def test_dynamic_ghz_circuit_structure(num_qubits: int) -> None:
"""Verify the structure of the dynamic GHZ circuit for various qubit counts."""
Expand Down
Loading