diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d1e6a495..11ea12e60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/mqt/bench/benchmarks/dynamic_qft.py b/src/mqt/bench/benchmarks/dynamic_qft.py new file mode 100644 index 000000000..848c373d0 --- /dev/null +++ b/src/mqt/bench/benchmarks/dynamic_qft.py @@ -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 diff --git a/tests/test_bench.py b/tests/test_bench.py index 0ee4eea9f..e066546e8 100644 --- a/tests/test_bench.py +++ b/tests/test_bench.py @@ -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: @@ -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. @@ -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",