qiskit icon indicating copy to clipboard operation
qiskit copied to clipboard

`ConsolidateBlocks` pass panics on consecutive self-inverse gates (e.g., `x`, `x`) with `PassManager([Collect1qRuns(), CommutativeInverseCancellation(), ConsolidateBlocks()])`

Open YaegakiY opened this issue 6 months ago • 2 comments

Environment

  • Qiskit version: 2.1.0
  • Python version: Python 3.13.2
  • Operating system: Ubuntu 20.04.6 LTS

What is happening?

When applying Collect1qRuns, CommutativeInverseCancellation, and ConsolidateBlocks together in a PassManager, the transpiler crashes with a Rust panic if the circuit contains two consecutive self-inverse gates (e.g., x, x) on the same qubit. Error message:

thread '<unnamed>' panicked at crates/transpiler/src/passes/consolidate_blocks.rs:275:33:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

pyo3_runtime.PanicException: called `Option::unwrap()` on a `None` value

However, running the same three passes separately does not cause a crash, and the output is as expected.

How can we reproduce the issue?

To reproduce:

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile
from qiskit_aer import Aer
from qiskit.transpiler.passes import *
from qiskit.transpiler import PassManager

qreg = QuantumRegister(4)
creg = ClassicalRegister(4)
qc = QuantumCircuit(qreg, creg)

qc.x(2)
qc.x(2)

qc.measure(qreg[0], creg[0])
qc.measure(qreg[1], creg[1])
qc.measure(qreg[2], creg[2])
qc.measure(qreg[3], creg[3])

simulator = Aer.get_backend("aer_simulator")

p = PassManager([Collect1qRuns(),CommutativeInverseCancellation(),ConsolidateBlocks()])
qc = p.run(qc)

compiled_circuit = transpile(qc, backend = simulator)

job = simulator.run(compiled_circuit, shots=500)
result = job.result().get_counts()
print(result)

What should happen?

The circuit should transpile and execute successfully.

Any suggestions?

Break the passes into three separate PassManager runs the circuit would work fine:

p = PassManager(Collect1qRuns())
qc = p.run(qc)

p = PassManager(CommutativeInverseCancellation())
qc = p.run(qc)

p = PassManager(ConsolidateBlocks())
qc = p.run(qc)

YaegakiY avatar Jun 19 '25 12:06 YaegakiY

ConsolidateBlocks shouldn't panic, but the main problem here is likely to be that Collect1qRuns is an analysis pass that ConsolidateBlocks uses, but putting CommutativeInverseCancellation in the middle invalidates the analysis (since it deletes various gates from the runs).

In an ideal world, the PassManager would detect that the analysis was invalidated and clear it out, though that's harder to achieve with our current infrastructure right now (but we do want to improve that story). Certainly, though, we shouldn't have the Rust code panic if its assumptions are violated.

jakelishman avatar Jun 19 '25 12:06 jakelishman

@jakelishman Thanks again for the explanation!

YaegakiY avatar Jun 19 '25 13:06 YaegakiY