Skip to content

Commit b31cac4

Browse files
soroushfathipre-commit-ci[bot]burgholzerrenovate[bot]
authored
🐛 Fix native gates handling for mirror circuits (#709)
## Description Fixes #654 ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] I have added migration instructions to the upgrade guide (if needed). - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Mirror-circuit generation can optionally transpile mirrored circuits to a specified device, producing device-native gates when a target is provided and honoring a configurable optimization level. * **Bug Fixes** * Preserves original layout and measurement alignment after optional transpilation to ensure consistency with the source circuit. * **Tests** * Added validation to ensure mirror circuits use only device-native gates (allowing barriers/measures) when a target is provided. * **Chores** * Updated pre-commit tooling revision. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: burgholzer <burgholzer@me.com> Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: burgholzer <burgholzer@me.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
1 parent 8516fe2 commit b31cac4

3 files changed

Lines changed: 35 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1313

1414
- 👷 Enable testing on Python 3.14 ([#705]) ([**@denialhaag**])
1515

16+
### Fixed
17+
18+
- 🐛 Fix layout preservation and ensure native gate compliance for mirror circuit generation ([#709]) ([**@soroushfathi**], [**@burgholzer**])
19+
1620
### Removed
1721

1822
- 🔥 Drop support for Python 3.9 ([#671]) ([**@denialhaag**])
@@ -76,6 +80,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._
7680

7781
<!-- PR links -->
7882

83+
[#709]: https://github.com/munich-quantum-toolkit/bench/pull/709
7984
[#705]: https://github.com/munich-quantum-toolkit/bench/pull/705
8085
[#671]: https://github.com/munich-quantum-toolkit/bench/pull/671
8186
[#666]: https://github.com/munich-quantum-toolkit/bench/pull/666
@@ -116,6 +121,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._
116121
[**@fkiwit**]: https://github.com/fkiwit
117122
[**@CreativeBinBag**]: https://github.com/CreativeBinBag
118123
[**@denialhaag**]: https://github.com/denialhaag
124+
[**@soroushfathi**]: https://github.com/soroushfathi
119125

120126
<!-- General links -->
121127

src/mqt/bench/benchmark_generation.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,26 @@ def _get_circuit(
7575
return qc
7676

7777

78-
def _create_mirror_circuit(qc_original: QuantumCircuit, inplace: bool = False) -> QuantumCircuit:
78+
def _create_mirror_circuit(
79+
qc_original: QuantumCircuit, *, inplace: bool = False, target: Target | None = None, optimization_level: int = 2
80+
) -> QuantumCircuit:
7981
"""Generates the mirror version (qc @ qc.inverse()) of a given quantum circuit.
8082
8183
For circuits with an initial layout (e.g., mapped circuits), this function ensures
8284
that the final layout of the mirrored circuit matches the initial layout of the
8385
original circuit. While Qiskit's `inverse()` and `compose()` methods correctly track
8486
the permutation of qubits, this benchmark requires that the final qubit permutation
85-
is identical to the initial one, necessitating the explicit layout handling herein.
87+
is identical to the initial one, requiring the explicit layout handling herein.
88+
Also ensures that the mirrored circuit respects the native gate set of the target device
89+
if a target is provided.
8690
8791
All qubits are measured at the end of the mirror circuit.
8892
8993
Args:
9094
qc_original: The quantum circuit to mirror.
9195
inplace: If True, modifies the circuit in place. Otherwise, returns a new circuit.
96+
target: Target device for transpilation. If provided, ensures native gate set compliance.
97+
optimization_level: Optimization level of the transpilation.
9298
9399
Returns:
94100
The mirrored quantum circuit.
@@ -107,6 +113,20 @@ def _create_mirror_circuit(qc_original: QuantumCircuit, inplace: bool = False) -
107113
# Form the mirror circuit by composing the original circuit with its inverse.
108114
target_qc.compose(qc_inv, inplace=True)
109115

116+
# Transpile to ensure the final circuit uses only native gates while preserving the initial layout.
117+
if target is not None:
118+
layout = target_qc.layout.initial_layout if target_qc.layout is not None else None
119+
target_qc = transpile(
120+
target_qc,
121+
target=target,
122+
optimization_level=optimization_level,
123+
layout_method=None,
124+
routing_method=None,
125+
seed_transpiler=10,
126+
)
127+
if layout is not None:
128+
target_qc.layout.initial_layout = layout
129+
110130
# Add final measurements to all active qubits
111131
target_qc.barrier(active_qubits)
112132
new_creg = ClassicalRegister(len(active_qubits), "meas")
@@ -308,7 +328,7 @@ def get_benchmark_native_gates(
308328

309329
compiled_circuit = pm.run(circuit)
310330
if generate_mirror_circuit:
311-
return _create_mirror_circuit(compiled_circuit, inplace=True)
331+
return _create_mirror_circuit(compiled_circuit, inplace=True, target=target, optimization_level=opt_level)
312332
return compiled_circuit
313333

314334

@@ -374,7 +394,7 @@ def get_benchmark_mapped(
374394
seed_transpiler=10,
375395
)
376396
if generate_mirror_circuit:
377-
return _create_mirror_circuit(mapped_circuit, inplace=True)
397+
return _create_mirror_circuit(mapped_circuit, inplace=True, target=target, optimization_level=opt_level)
378398
return mapped_circuit
379399

380400

tests/test_bench.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,11 @@ def test_get_benchmark_mirror_option() -> None:
937937

938938
assert qc_mirror.num_qubits == qc_base.num_qubits
939939

940+
# Ensure the mirror circuit contains only native gates for the given target.
941+
pm = PassManager(GatesInBasis(target=target_obj))
942+
pm.run(qc_mirror)
943+
assert pm.property_set["all_gates_in_basis"]
944+
940945
# at least each logical qubit should be measured
941946
assert sum(inst.operation.name == "measure" for inst in qc_mirror.data) >= logical_circuit_size
942947

0 commit comments

Comments
 (0)