`ConsolidateBlocks` pass panics on consecutive self-inverse gates (e.g., `x`, `x`) with `PassManager([Collect1qRuns(), CommutativeInverseCancellation(), ConsolidateBlocks()])`
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)
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 Thanks again for the explanation!