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
3 changes: 3 additions & 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 ([#798]) ([**@kris70lesgo**])
- ✨ Add Iterative Quantum Phase Estimation (IQPE) benchmark ([#925]) ([**@johanneswittmann9**])

### Fixed
Expand Down Expand Up @@ -130,6 +131,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._

<!-- PR links -->

[#798]: https://github.com/munich-quantum-toolkit/bench/issues/798
[#925]: https://github.com/munich-quantum-toolkit/bench/pull/925
[#914]: https://github.com/munich-quantum-toolkit/bench/pull/914
[#895]: https://github.com/munich-quantum-toolkit/bench/pull/895
Expand Down Expand Up @@ -187,6 +189,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._
[**@TomasVF**]: https://github.com/TomasVF
[**@flowerthrower**]: https://github.com/flowerthrower
[**@johanneswittmann9**]: https://github.com/johanneswittmann9
[**@kris70lesgo**]: https://github.com/kris70lesgo

<!-- General links -->

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:
"""Returns a quantum circuit implementing the dynamic Quantum Fourier Transform.

The circuit implements the semiclassical QFT followed by measurement from Griffiths and Niu
and the dynamic QFT construction described in Phys. Rev. Lett. 133, 150602 (2024). It mirrors
the operation order of the regular QFT benchmark, but replaces controlled phase gates from
already measured qubits by classically controlled phase rotations.

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

Returns:
QuantumCircuit: a quantum circuit implementing the dynamic Quantum Fourier Transform.
"""
q = QuantumRegister(num_qubits, "q")
c = ClassicalRegister(num_qubits, "c")
qc = QuantumCircuit(q, c, name="dynamic_qft")

for qubit in reversed(range(num_qubits)):
measured_bit = num_qubits - qubit - 1

for control_qubit in range(num_qubits - 1, qubit, -1):
control_bit = num_qubits - control_qubit - 1
with qc.if_test((c[control_bit], 1)):
qc.p(math.pi / (1 << (control_qubit - qubit)), q[qubit])

qc.h(q[qubit])
qc.measure(q[qubit], c[measured_bit])

return qc
75 changes: 75 additions & 0 deletions tests/test_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,81 @@ def test_graphstate_seed() -> None:
assert qc_no_seed.name == "graphstate"


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

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

ops = qc.count_ops()
assert ops.get("h", 0) == num_qubits
assert ops.get("measure", 0) == num_qubits
assert ops.get("if_else", 0) == num_qubits * (num_qubits - 1) // 2
assert ops.get("cp", 0) == 0

measurements = [
(qc.find_bit(instruction.qubits[0]).index, qc.find_bit(instruction.clbits[0]).index)
for instruction in qc.data
if instruction.operation.name == "measure"
]
assert measurements == [(qubit, num_qubits - qubit - 1) for qubit in reversed(range(num_qubits))]


@pytest.mark.parametrize("num_qubits", [1, 3, 4])
def test_dynamic_qft_matches_qft_operation_order(num_qubits: int) -> None:
"""Verify that Dynamic QFT mirrors QFT phase and Hadamard ordering."""
qft = create_circuit("qft", num_qubits).decompose()
dynamic_qft = create_circuit("dynamic_qft", num_qubits)

qft_ops = [
(
instruction.operation.name,
tuple(qft.find_bit(qubit).index for qubit in instruction.qubits),
tuple(round(float(parameter), 12) for parameter in instruction.operation.params),
)
for instruction in qft.data
if instruction.operation.name in {"cp", "h"}
]

dynamic_ops = []
for instruction in dynamic_qft.data:
operation = instruction.operation
if isinstance(operation, IfElseOp):
condition_bit = dynamic_qft.find_bit(instruction.clbits[0]).index
conditional_instruction = operation.blocks[0].data[0]
dynamic_ops.append((
"cp",
(
num_qubits - condition_bit - 1,
dynamic_qft.find_bit(conditional_instruction.qubits[0]).index,
),
(round(float(conditional_instruction.operation.params[0]), 12),),
))
elif operation.name == "h":
dynamic_ops.append(("h", (dynamic_qft.find_bit(instruction.qubits[0]).index,), ()))

assert dynamic_ops == qft_ops


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.num_clbits == 65
assert qc.count_ops().get("measure", 0) == 65


# Test the dynamic GHZ circuit
@pytest.mark.parametrize("num_qubits", [1, 2, 3, 7, 10])
def test_dynamic_ghz_circuit_structure(num_qubits: int) -> None:
Expand Down
Loading