Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4ecffad
Fix: ensure mirror circuits respect native gate set via transpilation
soroushfathi Oct 13, 2025
d884558
Merge branch 'main' into fix/mirror-native-gates
soroushfathi Oct 13, 2025
3e3030e
directly pass in a Qiskit Target
soroushfathi Oct 13, 2025
a569584
test: add mirror circuit validation with Target support in test_bench.py
soroushfathi Oct 14, 2025
c98b693
🎨 pre-commit fixes
pre-commit-ci[bot] Oct 14, 2025
e8a8cd6
Fix import
soroushfathi Oct 14, 2025
99f2093
Merge branch 'main' into fix/mirror-native-gates
soroushfathi Oct 14, 2025
d3ac5d6
Merge remote-tracking branch 'origin/fix/mirror-native-gates' into fi…
soroushfathi Oct 14, 2025
e4cb425
Fix import
soroushfathi Oct 14, 2025
7540f27
import `_create_mirror_circuit` from external module `mqt.bench.bench…
soroushfathi Oct 14, 2025
ecdd690
🎨 pre-commit fixes
pre-commit-ci[bot] Oct 14, 2025
b58b0b3
add description for optimization_level
soroushfathi Oct 14, 2025
adffc60
import `_create_mirror_circuit` from external module `mqt.bench.bench…
soroushfathi Oct 14, 2025
32121b1
🎨 pre-commit fixes
pre-commit-ci[bot] Oct 14, 2025
c231cb1
Remove unit test
soroushfathi Oct 14, 2025
f4c9b04
Merge remote-tracking branch 'origin/fix/mirror-native-gates' into fi…
soroushfathi Oct 14, 2025
4a08ff5
Merge branch 'main' into fix/mirror-native-gates
soroushfathi Oct 14, 2025
e8eaecf
Add argument description in the docstring
soroushfathi Oct 14, 2025
b56db85
Pass target parameter to _create_mirror_circuit in native gates and …
soroushfathi Oct 14, 2025
87ec363
Update opt level due to other usage.
soroushfathi Oct 14, 2025
2893c72
Fix grammar and wording in docstring.
soroushfathi Oct 14, 2025
e80c930
Add seed_transpiler for reproducibility.
soroushfathi Oct 14, 2025
31bea30
🎨 pre-commit fixes
pre-commit-ci[bot] Oct 14, 2025
b4c701c
refactoring
soroushfathi Oct 17, 2025
3b6a1b7
🎨 pre-commit fixes
pre-commit-ci[bot] Oct 17, 2025
3be8a1b
🩹preserve initial layout in mirror circuit creation
burgholzer Oct 17, 2025
bf7797d
⬆️🔒️ Lock file maintenance (#711)
renovate[bot] Oct 16, 2025
962e6f1
⬆️🪝 Update pre-commit hook henryiii/validate-pyproject-schema-store t…
renovate[bot] Oct 16, 2025
fd5561c
🐛 Fix layout preservation
soroushfathi Oct 17, 2025
d9d7320
🎨 pre-commit fixes
pre-commit-ci[bot] Oct 17, 2025
957fdf5
test(bench): add native gate validation for mirror circuits
soroushfathi Oct 17, 2025
5c19b9f
🎨 pre-commit fixes
pre-commit-ci[bot] Oct 17, 2025
6790f9f
⏪ revert unrelated changes
burgholzer Oct 17, 2025
2ccbf3e
📝 fix changelog
burgholzer Oct 17, 2025
cf2e276
🎨 simplify test condition
burgholzer Oct 17, 2025
e3b8c62
📝 streamline docstring
burgholzer Oct 17, 2025
190a41e
Merge branch 'main' into fix/mirror-native-gates
burgholzer Oct 17, 2025
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 @@ -13,6 +13,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

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

### Fixed

- 🐛 Fix layout preservation and ensure native gate compliance for mirror circuit generation ([#709]) ([**@soroushfathi**], [**@burgholzer**])

### Removed

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

<!-- PR links -->

[#709]: https://github.com/munich-quantum-toolkit/bench/pull/709
[#705]: https://github.com/munich-quantum-toolkit/bench/pull/705
[#671]: https://github.com/munich-quantum-toolkit/bench/pull/671
[#666]: https://github.com/munich-quantum-toolkit/bench/pull/666
Expand Down Expand Up @@ -116,6 +121,7 @@ _📚 Refer to the [GitHub Release Notes] for previous changelogs._
[**@fkiwit**]: https://github.com/fkiwit
[**@CreativeBinBag**]: https://github.com/CreativeBinBag
[**@denialhaag**]: https://github.com/denialhaag
[**@soroushfathi**]: https://github.com/soroushfathi

<!-- General links -->

Expand Down
28 changes: 24 additions & 4 deletions src/mqt/bench/benchmark_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,26 @@ def _get_circuit(
return qc


def _create_mirror_circuit(qc_original: QuantumCircuit, inplace: bool = False) -> QuantumCircuit:
def _create_mirror_circuit(
qc_original: QuantumCircuit, *, inplace: bool = False, target: Target | None = None, optimization_level: int = 2
) -> QuantumCircuit:
"""Generates the mirror version (qc @ qc.inverse()) of a given quantum circuit.

For circuits with an initial layout (e.g., mapped circuits), this function ensures
that the final layout of the mirrored circuit matches the initial layout of the
original circuit. While Qiskit's `inverse()` and `compose()` methods correctly track
the permutation of qubits, this benchmark requires that the final qubit permutation
is identical to the initial one, necessitating the explicit layout handling herein.
is identical to the initial one, requiring the explicit layout handling herein.
Also ensures that the mirrored circuit respects the native gate set of the target device
if a target is provided.

All qubits are measured at the end of the mirror circuit.

Args:
qc_original: The quantum circuit to mirror.
inplace: If True, modifies the circuit in place. Otherwise, returns a new circuit.
target: Target device for transpilation. If provided, ensures native gate set compliance.
optimization_level: Optimization level of the transpilation.

Returns:
The mirrored quantum circuit.
Expand All @@ -107,6 +113,20 @@ def _create_mirror_circuit(qc_original: QuantumCircuit, inplace: bool = False) -
# Form the mirror circuit by composing the original circuit with its inverse.
target_qc.compose(qc_inv, inplace=True)

# Transpile to ensure the final circuit uses only native gates while preserving the initial layout.
if target is not None:
layout = target_qc.layout.initial_layout if target_qc.layout is not None else None
target_qc = transpile(
target_qc,
target=target,
optimization_level=optimization_level,
layout_method=None,
routing_method=None,
seed_transpiler=10,
)
if layout is not None:
target_qc.layout.initial_layout = layout

# Add final measurements to all active qubits
target_qc.barrier(active_qubits)
new_creg = ClassicalRegister(len(active_qubits), "meas")
Expand Down Expand Up @@ -308,7 +328,7 @@ def get_benchmark_native_gates(

compiled_circuit = pm.run(circuit)
if generate_mirror_circuit:
return _create_mirror_circuit(compiled_circuit, inplace=True)
return _create_mirror_circuit(compiled_circuit, inplace=True, target=target, optimization_level=opt_level)
return compiled_circuit


Expand Down Expand Up @@ -374,7 +394,7 @@ def get_benchmark_mapped(
seed_transpiler=10,
)
if generate_mirror_circuit:
return _create_mirror_circuit(mapped_circuit, inplace=True)
return _create_mirror_circuit(mapped_circuit, inplace=True, target=target, optimization_level=opt_level)
return mapped_circuit


Expand Down
5 changes: 5 additions & 0 deletions tests/test_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,11 @@ def test_get_benchmark_mirror_option() -> None:

assert qc_mirror.num_qubits == qc_base.num_qubits

# Ensure the mirror circuit contains only native gates for the given target.
pm = PassManager(GatesInBasis(target=target_obj))
pm.run(qc_mirror)
assert pm.property_set["all_gates_in_basis"]

# at least each logical qubit should be measured
assert sum(inst.operation.name == "measure" for inst in qc_mirror.data) >= logical_circuit_size

Expand Down
Loading