qiskit icon indicating copy to clipboard operation
qiskit copied to clipboard

`CommutativeInverseCancellation` fails with `for_loop`

Open YaegakiY opened this issue 6 months ago • 3 comments

Environment

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

What is happening?

When applying the CommutativeInverseCancellation pass to a circuit that contains a for_loop block, a CircuitError is raised due to the lack of .inverse() implementation for for_loop.

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
import numpy as np

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

qc.crx(np.pi/2, 2, 0)

with qc.for_loop(range(3)) as i:
    qc.cx(2, 0)


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(CommutativeInverseCancellation())
qc = p.run(qc)

compiled_circuit = transpile(qc, backend=simulator)


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

Error Message

qiskit.circuit.exceptions.CircuitError: 'inverse() not implemented for for_loop.'

What should happen?

The circuit should transpile and execute successfully.

Any suggestions?

The following modified version runs without error, even though it also contains a for_loop:

with qc.for_loop(range(3)) as i:
    qc.cx(2, 0)
    qr = QuantumRegister(2)
    cr = ClassicalRegister(2)
    qc.add_register(qr)
    qc.add_register(cr)
    qc.measure(qr[0], cr[0])
    qc.measure(qr[1], cr[1])

Another no error version is:

with qc.for_loop(range(3)) as i:
    qc.cx(0, 2)

YaegakiY avatar Jun 18 '25 06:06 YaegakiY

Hi @alexanderivrii , This issue looks similar to #14254 , which you had worked on before. I thought it might be related, so just wanted to bring it to your attention. Thanks!

YaegakiY avatar Jun 18 '25 06:06 YaegakiY

Thanks for reporting the issue. Indeed, not all Instructions have inverses and CommutativeInverseCancellation pass does not properly check for that. A very simple fix for the current problem would be to adjust the _skip_node function to also skip control-flow nodes. However, this still does not handle the related issue #14407. There the circuit contains an Initialize instruction.

alexanderivrii avatar Jun 18 '25 10:06 alexanderivrii

@alexanderivrii , thanks for the clarification!

I actually ran into a similar issue in #14409, which I also opened. It also involves CommutativeInverseCancellation failing due to an instruction not implementing .inverse().

If the fix you're considering here (e.g., extending _skip_node to skip control-flow ops) works well, maybe a similar strategy could help with that one too?

YaegakiY avatar Jun 18 '25 10:06 YaegakiY

Thank you for reporting this, should be fixed by #14655.

alexanderivrii avatar Jun 23 '25 08:06 alexanderivrii

Closing as the fix has been merged in #14655. Thanks for reporting!

eliarbel avatar Jul 10 '25 09:07 eliarbel