-
-
Notifications
You must be signed in to change notification settings - Fork 29
Add IQPE benchmark #806
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add IQPE benchmark #806
Changes from all commits
17490ad
ca383f2
87789d5
4a7bfbd
8438b0d
97e019e
187de1a
f3d3131
b177dcf
2706122
79374b4
d0b186d
27082ba
86f7c39
3e1bfaf
6d5d2c8
aab071e
3d5b92c
1724210
9202b77
c7b7e01
fb0604b
d68cbbc
f799bb5
78e84f4
a96e74a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is only covering the |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # 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 | ||
|
|
||
| """Iterative Quantum Phase Estimation (IQPE).""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import random | ||
| from fractions import Fraction | ||
|
|
||
| import numpy as np | ||
| from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister | ||
|
|
||
| from ._registry import register_benchmark | ||
|
|
||
|
|
||
| @register_benchmark("iqpe_exact", description="Iterative Quantum Phase Estimation (IQPE)") | ||
| def create_circuit(num_qubits: int) -> QuantumCircuit: | ||
| """Returns a dynamic quantum circuit implementing the Iterative Quantum Phase Estimation algorithm. | ||
|
|
||
| Arguments: | ||
| num_qubits: total number of qubits. | ||
|
|
||
| Returns: | ||
| QuantumCircuit: a dynamic quantum circuit implementing the Iterative Quantum Phase Estimation algorithm for a phase which can be exactly estimated | ||
|
|
||
| """ | ||
| # We just use 2 quantum registers: q[1] is the main register where | ||
| # the eigenstate will be encoded, q[0] is the ancillary qubit where the phase | ||
| # will be encoded. Only the ancillary qubit is measured, and the result will | ||
| # be stored in "c", the classical register. | ||
| if num_qubits < 2: | ||
| msg = "num_qubits must be >= 2 (1 ancilla + at least 1 phase bit)" | ||
| raise ValueError(msg) | ||
| num_bits = num_qubits - 1 # because of ancilla qubit | ||
| q = QuantumRegister(num_qubits, "q") | ||
| c = ClassicalRegister(num_bits, "c") | ||
| qc = QuantumCircuit(q, c, name="iqpe_exact") | ||
|
|
||
| # Generate a random n-bit integer as a target phase "theta". The phase can be exactly | ||
| # estimated | ||
| rng = random.Random(10) | ||
| theta = 0 | ||
| while theta == 0: | ||
| theta = rng.getrandbits(num_bits) | ||
| lam = Fraction(0, 1) | ||
| for i in range(num_bits): | ||
| if theta & (1 << (num_bits - i - 1)): | ||
| lam += Fraction(1, (1 << i)) | ||
|
|
||
| # We apply an X gate to the q[1] qubit, to prepare the target qubit in the | ||
| # |1> state | ||
| qc.x(q[1]) | ||
|
|
||
| for k in range(num_bits): | ||
| index = num_bits - 1 - k | ||
| # We reset the ancillary qubit in order to reuse in each iteration | ||
| qc.measure(q[0], c[0]) | ||
| with qc.if_test((c[0], 1)): | ||
| qc.x(q[0]) | ||
|
Comment on lines
+63
to
+65
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Qiskit has a dedicated |
||
| qc.h(q[0]) | ||
|
|
||
| # Controlled unitary. The angle is normalized from | ||
| # [0, 2] to [-1, 1], which minimize the errors because uses shortest rotations | ||
| angle = float((lam * (1 << index)) % 2) | ||
| if angle > 1: | ||
| angle -= 2 | ||
|
|
||
| # We use pi convention for simplicity | ||
| qc.cp(angle * np.pi, q[0], q[1]) | ||
|
|
||
| # We apply phase corrections based on previous measurements. | ||
| for i in range(k): | ||
| m_index = num_bits - 1 - i | ||
| true_angle = -1.0 / (1 << (k - i)) | ||
|
|
||
| with qc.if_test((c[m_index], 1)): | ||
| qc.p(true_angle * np.pi, q[0]) | ||
|
|
||
| qc.h(q[0]) | ||
| # We measure and store the result for future corrections | ||
| qc.measure(q[0], c[index]) | ||
|
|
||
| return qc | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -145,6 +145,34 @@ def test_adder_circuits(benchmark_name: str, input_value: int, kind: str) -> Non | |
| assert qc.num_qubits == input_value | ||
|
|
||
|
|
||
| def test_iqpe_exact_structure() -> None: | ||
| """Test that the Iterative Quantum Phase Estimation code circuit has the expected structure. | ||
|
|
||
| Verifies (for a 5-qubit input): | ||
| - Quantum registers: 4 target qubits and 1 ancillary qubit | ||
| - Classical register: num_qubits - 1 (4 bits) | ||
| - 8 measurements (4 resets + 4 final measurements) | ||
| - 10 conditional operations (4 reset conditionals + 6 correction conditionals) | ||
| """ | ||
| qc = create_circuit("iqpe_exact", 5) | ||
|
|
||
| assert qc.num_qubits == 5 | ||
| assert qc.num_clbits == 4 | ||
|
|
||
| ops = qc.count_ops() | ||
| ops = dict(ops) # type: dict[str, int] | ||
carlareciofdz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assert ops.get("measure") == 8 | ||
|
|
||
| if_else_count = sum(1 for inst in qc.data if inst.operation.name == "if_else") | ||
| assert if_else_count == 10, f"Expected 10 conditional operations, found {if_else_count}" | ||
|
Comment on lines
+148
to
+167
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels a bit hard-coded to be honest. Can we generalize this slightly to cover a few more cases? |
||
|
|
||
|
|
||
| def test_iqpe_exact_invalid_qubit_number() -> None: | ||
| """Test that the Iterative Quantum Phase Estimation code circuit raises an error for invalid qubit numbers.""" | ||
| with pytest.raises(ValueError, match=r"num_qubits must be >= 2"): | ||
| create_circuit("iqpe_exact", 1) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| ("benchmark_name", "input_value", "kind", "msg"), | ||
| [ | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This algorithm will probably need a safeguard to limit it to something like 60 qubits because otherwise the shift operations are no longer well defined and will cause issues.
A corresponding check should be added, the docstrings should mention this limitation, and a test is probably also in order.