Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
5.3k stars 2.37k forks source link

Transpiler sometimes produces circuit that causes `AerError` #13162

Open IlanIwumbwe opened 2 months ago

IlanIwumbwe commented 2 months ago

Environment

Benny and I found this bug

What is happening?

Unknown instruction appears in transpiled circuit sometimes. Run the code below a few times and it should fail with:

qiskit_aer.aererror.AerError: 'unknown instruction: rccx'

We tried to investigate this a bit, by printing out all the instructions and comparing from the version that works vs the one that doesn't. This is because we guessed that maybe some decomposition isn't being done.

The output below shows what we got. Blocks surrounded by "======" are the same, blocks left alone are different.

Working instructions:

=================================================================
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['if_0_true'])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['if_0_else'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['if_0_true'])
Instruction(name='id', num_qubits=1, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['if_0_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['if_0_else'])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_0'])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_1'])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['switch_1_0'])
Instruction(name='h', num_qubits=1, num_clbits=0, params=[])
==================================================================

Instruction(name='h', num_qubits=1, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='h', num_qubits=1, num_clbits=0, params=[])

============================================================
Instruction(name='h', num_qubits=1, num_clbits=0, params=[])
============================================================

===============================================================
Instruction(name='mcphase', num_qubits=4, num_clbits=0, params=[0.925])
Instruction(name='mcphase', num_qubits=4, num_clbits=0, params=[-0.428])
===============================================================

Instruction(name='h', num_qubits=1, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='p', num_qubits=1, num_clbits=0, params=[-0.39269908169872414])
Instruction(name='cx', num_qubits=2, num_clbits=0, params=[])
Instruction(name='h', num_qubits=1, num_clbits=0, params=[])

==================================================================
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['switch_1_1'])
Instruction(name='x', num_qubits=1, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['if_0_end'])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
===================================================================

Not working instructions:

=================================================================
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['if_0_true'])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['if_0_else'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['if_0_true'])
Instruction(name='id', num_qubits=1, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['if_0_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['if_0_else'])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_0'])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_1'])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['switch_1_0'])
Instruction(name='h', num_qubits=1, num_clbits=0, params=[])
==================================================================

Instruction(name='rccx', num_qubits=3, num_clbits=0, params=[])
Instruction(name='ccx', num_qubits=3, num_clbits=0, params=[])

============================================================
Instruction(name='h', num_qubits=1, num_clbits=0, params=[])***
============================================================

(Seems to map to nothing?)
Instruction(name='rccx', num_qubits=3, num_clbits=0, params=[])

===============================================================
Instruction(name='mcphase', num_qubits=4, num_clbits=0, params=[0.925])
Instruction(name='mcphase', num_qubits=4, num_clbits=0, params=[-0.428])
===============================================================

Instruction(name='rccx', num_qubits=3, num_clbits=0, params=[])
Instruction(name='ccx', num_qubits=3, num_clbits=0, params=[])

(Seems to map to nothing?)
Instruction(name='rccx', num_qubits=3, num_clbits=0, params=[])

==================================================================
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['switch_1_1'])
Instruction(name='x', num_qubits=1, num_clbits=0, params=[])
Instruction(name='jump', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['switch_1_end'])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='mark', num_qubits=5, num_clbits=0, params=['if_0_end'])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='barrier', num_qubits=5, num_clbits=0, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
Instruction(name='measure', num_qubits=1, num_clbits=1, params=[])
===================================================================

How can we reproduce the issue?

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit import Parameter, ParameterVector
from helpers.qiskit_helpers import compare_statevectors, run_on_simulator, run_routing_simulation
from pathlib import Path
from math import pi
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

subcirc4 = QuantumCircuit(0)
# Adding qregs 
qreg_0 = QuantumRegister(1)
subcirc4.add_register(qreg_0)
qreg_1 = QuantumRegister(1)
subcirc4.add_register(qreg_1)
# Adding creg resources 
subcirc4.z(qreg_1[0])
subcirc4.u(0,0,0.925000, qreg_1[0])
subcirc4.u(0,0,-0.428000, qreg_1[0])
subcirc4.x(qreg_1[0])
subcirc4 = subcirc4.to_gate().control(3)

main_circ = QuantumCircuit(1)
# Adding qregs 
qreg_0 = QuantumRegister(1)
main_circ.add_register(qreg_0)
qreg_1 = QuantumRegister(1)
main_circ.add_register(qreg_1)
qreg_2 = QuantumRegister(1)
main_circ.add_register(qreg_2)
qreg_3 = QuantumRegister(1)
main_circ.add_register(qreg_3)
# Adding creg resources 
creg_0 = ClassicalRegister(1)
main_circ.add_register(creg_0)
creg_1 = ClassicalRegister(2)
main_circ.add_register(creg_1)
creg_3 = ClassicalRegister(1)
main_circ.add_register(creg_3)

main_circ.measure(qreg_2[0], creg_0[0])
with main_circ.if_test((creg_0[0],0)) as else_2:
    main_circ.id(qreg_0[0])
with else_2:
    main_circ.measure(qreg_0[0], creg_0[0])
    with main_circ.switch(creg_0[0]) as case_1:
        with case_1(0):
            main_circ.append(subcirc4,[qreg_3[0],qreg_1[0],qreg_0[0],qreg_2[0],0])
        with case_1(1):
            main_circ.x(qreg_1[0])

main_circ.measure_active()

s = AerSimulator()

qc1 = transpile(main_circ, s, optimization_level=0)

c1 = s.run(qc1, shots=1).result().get_counts()

What should happen?

Circuit should be runnable all the time

Any suggestions?

No response

jakelishman commented 2 months ago

Thanks for the report - this seems to be a rather long-standing bug that I was able to reproduce at least as far back as 1.1.0, and probably appeared before that. It's quite possibly somewhere in the BasisTranslator around nested multi-block control flow. Here's a more minimal reproducer:

from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

qc = QuantumCircuit(3, 1)

with qc.if_test((0, False)) as else_:
    pass
with else_:
    with qc.if_test((0, False)) as else2:
        qc.rccx(0, 1, 2)
    with else2:
        pass

transpiled = transpile(qc, AerSimulator())

and if we do transpiled.draw(), we can see that the rccx gate survived basis translation, despite GatesInBasis recognising that it ought to have been rewritten.

The structure of the control flow seems to matter; on my cursory testing, it seems to require at least two nested structures, both of which have more than one branch, but I don't know the cause.

IlanIwumbwe commented 2 months ago

Thanks for the quick response!