qiskit icon indicating copy to clipboard operation
qiskit copied to clipboard

TypeError: label expects a string or None when loading a circuit using default label

Open jiannanWang opened this issue 1 year ago • 4 comments

Environment

  • Qiskit version: 0.45.1
  • Python version: 3.10.13
  • Operating system: Ubuntu 22.04.3 LTS

What is happening?

Running the below code results in TypeError: label expects a string or None. However, I use the default label which is None.

How can we reproduce the issue?

from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library.standard_gates import CUGate

qr = QuantumRegister(3)
qc = QuantumCircuit(qr, name='qc')
qc.append(CUGate(1, 1, 1, 1).control(1), qr)

from qiskit import qpy
with open('circuit.qpy', 'wb') as fd:
    qpy.dump(qc, fd)
with open('circuit.qpy', 'rb') as fd:
    qc = qpy.load(fd)[0]  # TypeError: label expects a string or None

What should happen?

I expect the code to run without error.

Any suggestions?

No response

jiannanWang avatar Jan 31 '24 02:01 jiannanWang

@jiannanWang I learned That qpy works with QuantumCircuit Modules not with qiskit.circuit.library. So I checked the documents listed below and modify your code using

qc.cu(theta, phi, lam, gamma, control_qubit, target_qubit, label=None, ctrl_state=None)

supprted with QuantumCircuit and it works correctly with my code editor. here is the code..

from qiskit import QuantumCircuit
from qiskit import qpy
 
qc = QuantumCircuit(3, name='circuit')
qc.cu(1, 1, 1, 1, 1, 0)


with open('circuit.qpy', 'wb') as fd:
    qpy.dump(qc, fd)
 
with open('circuit.qpy', 'rb') as fd:
    new_qc = qpy.load(fd)[0]

Here is the documentation of Cu gate and qpy https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.CUGate https://docs.quantum.ibm.com/api/qiskit/qpy

I Hope It works with you as well.

Waliur003 avatar Feb 05 '24 18:02 Waliur003

Hope you are doing well! It's been a while since I submitted this issue. I wonder if there's any update? Thanks!

I revisited this issue and checked the stack track (copied below). From the traceback, it seems during loading, the qpy somehow loaded UGate instead of the CUGate. So the fourth parameter (gamma) of CUGate is passed to the fourth parameter (label) of UGate, which eventually causes this crash.

Traceback (most recent call last):
  File "/mnt/test_crash.py", line 12, in <module>
    qc = qpy.load(fd)[0]  # TypeError: label expects a string or None
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/qpy/interface.py", line 301, in load
    loader(
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 1189, in read_circuit
    _read_instruction(
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 273, in _read_instruction
    inst_obj = _parse_custom_operation(
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 396, in _parse_custom_operation
    base_gate = _read_instruction(
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 351, in _read_instruction
    gate = gate_class(*params)
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/circuit/library/standard_gates/u.py", line 81, in __init__
    super().__init__("u", 1, [theta, phi, lam], label=label, duration=duration, unit=unit)
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/circuit/gate.py", line 45, in __init__
    super().__init__(name, num_qubits, 0, params, label=label, duration=duration, unit=unit)
  File "/root/anaconda3/envs/qiskit/lib/python3.10/site-packages/qiskit/circuit/instruction.py", line 95, in __init__
    raise TypeError("label expects a string or None")
TypeError: label expects a string or None

jiannanWang avatar Feb 12 '24 01:02 jiannanWang

After experimenting with the code, the problem seems to boil down to the following. The code

gate = CUGate(1, 1, 1, 1)
print(f"{gate = }")
cgate = gate.control(1)
print(f"{cgate = }")
bgate = cgate.base_gate
print(f"{bgate = }")

outputs

gate = Instruction(name='cu', num_qubits=2, num_clbits=0, params=[1, 1, 1, 1])
cgate = Instruction(name='ccu', num_qubits=3, num_clbits=0, params=[1, 1, 1, 1])
bgate = Instruction(name='u', num_qubits=1, num_clbits=0, params=[1, 1, 1, 1])

That is, we now have a UGate with the wrong number of parameters. I believe this is due to the discrepancy in handling params for generic controlled gates vs. CUGates, see https://github.com/Qiskit/qiskit/blob/9157c04230943d5470e5bb6c31ce0f4c8dedecf6/qiskit/circuit/library/standard_gates/u.py#L148-L157

Having written this, I have no idea how to fix this, leaving the fix to the experts. :)

alexanderivrii avatar Feb 12 '24 09:02 alexanderivrii

After experimenting with the code, the problem seems to boil down to the following. The code

gate = CUGate(1, 1, 1, 1)
print(f"{gate = }")
cgate = gate.control(1)
print(f"{cgate = }")
bgate = cgate.base_gate
print(f"{bgate = }")

outputs

gate = Instruction(name='cu', num_qubits=2, num_clbits=0, params=[1, 1, 1, 1])
cgate = Instruction(name='ccu', num_qubits=3, num_clbits=0, params=[1, 1, 1, 1])
bgate = Instruction(name='u', num_qubits=1, num_clbits=0, params=[1, 1, 1, 1])

Considering this as an example, after creating the cu instruction, we create a controlled gate ccu line 240 by passing in operation.params = [1,1,1,1] (which is params of cu instruction above) and base_gate = u gate (which is the base gate of cu). During which we call the constructor of Instruction class and assign self.params = params which causes the failure. IMO, we should first check whether or not self.params is empty and then move forward The following code https://github.com/Qiskit/qiskit/blob/9157c04230943d5470e5bb6c31ce0f4c8dedecf6/qiskit/circuit/instruction.py#L108 should be

if not self.params:
   self.params = params

With this change, the output of the above code snippet is

gate = Instruction(name='cu', num_qubits=2, num_clbits=0, params=[1, 1, 1, 1])
cgate = Instruction(name='ccu', num_qubits=3, num_clbits=0, params=[1, 1, 1])
bgate = Instruction(name='u', num_qubits=1, num_clbits=0, params=[1, 1, 1])

atharva-satpute avatar May 18 '24 09:05 atharva-satpute