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.18k stars 2.35k forks source link

`from_qasm_str` not importing `Fswap` correctly #13339

Open dberthault opened 2 hours ago

dberthault commented 2 hours ago

Environment

What is happening?

When one creates a circuit with qiskit.QuantumCircuit.from_qasm_str an Fswap gate will sometimes be mapped to swap. This seems to only happen if there is a swap before the fswap in the qasm file.

swap q[0],q[1];
fswap q[0],q[1];
fswap q[0],q[1];

gives

CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())

But

fswap q[0],q[1];
swap q[0],q[1];
fswap q[0],q[1];

gives

CircuitInstruction(operation=Instruction(name='fswap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='fswap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())

How can we reproduce the issue?

Run this code:

from qiskit import QuantumCircuit

qasm_str = """OPENQASM 2.0;

gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }
gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }
gate u1(lambda) q { U(0,0,lambda) q; }
gate rx(theta) a { u3(theta,-pi/2,pi/2) a; }
gate ry(theta) a { u3(theta,0,0) a; }
gate rz(phi) a { u1(phi) a; }
gate cx c,t { CX c,t; }

gate swap a,b { cx a,b; cx b,a; cx a,b; }
gate fswap a,b { rz(-pi/2) a; rz(-pi/2) b; rx(pi/2) a; cx a,b; rx(-pi/2) a; ry(-pi/2) b; cx a,b; rx(-pi/2) a; }

qreg q[2];

swap q[0],q[1];
fswap q[0],q[1];
fswap q[0],q[1];"""

qiskit_circuit = QuantumCircuit.from_qasm_str(qasm_str)
for gate in qiskit_circuit:
    print(gate)
qiskit_circuit.draw()

It outputs

CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())

        ┌───────┐┌───────┐
q_0: ─X─┤0      ├┤0      ├
      │ │  Swap ││  Swap │
q_1: ─X─┤1      ├┤1      ├
        └───────┘└───────┘

What should happen?

I should output

CircuitInstruction(operation=Instruction(name='swap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='fswap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())
CircuitInstruction(operation=Instruction(name='fswap', num_qubits=2, num_clbits=0, params=[]), qubits=(Qubit(QuantumRegister(2, 'q'), 0), Qubit(QuantumRegister(2, 'q'), 1)), clbits=())

        ┌────────┐┌────────┐
q_0: ─X─┤0       ├┤0       ├
      │ │  Fswap ││  Fswap │
q_1: ─X─┤1       ├┤1       ├
        └────────┘└────────┘

Any suggestions?

No response

jakelishman commented 2 hours ago

Huh, thanks for the report, this is definitely a weird one. Feels like the Rust and Python sides of the parser must be getting out-of-sync somehow, and I'm assuming that it's related to the legacy handling for instructions that the much older parser used to treat as "built in" without needing a definition.

Here's a minimal reproducer:

from qiskit import qasm2
from qiskit.circuit import library

custom = qasm2.CustomInstruction("whoops", 0, 1, library.XGate, builtin=True)
prog = """
OPENQASM 2.0;
gate whoops a { U(0, 0, 0) a; }
gate ignored a { U(pi, pi, pi) a; }

qreg q[1];
ignored q[0];
"""
qasm2.loads(prog, custom_instructions=[custom]).draw()

The output circuit only calls for gate ignored, but the name and definition that gets assigned is whoops.

jakelishman commented 1 hour ago

13340 should get this fixed.