-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Description
Environment
- Qiskit Terra version: 0.24.0
- Python version: 3.10
- Operating system: Fedora Linux 38
What is happening?
At a high level, some jobs using parameterized pulse gates are failing when using parameters with many digits of precision. The error for the jobs is "unsupported instruction" for the parameterized pulse gate.
The cause of the error is that the pulse gate instruction is becoming decoupled from the circuit calibration after the circuit is submitted to be run, in particular during the qpy serialization / deserialization process.
How can we reproduce the issue?
Here is code to build a problematic circuit:
from qiskit import QuantumCircuit, pulse
from qiskit.circuit import Gate, Parameter
from qiskit.pulse.library import Gaussian
amp = Parameter("amp")
circ = QuantumCircuit(1, 1)
custom_gate = Gate("my_custom_gate", 1, [amp])
circ.append(custom_gate, [0])
with pulse.build() as my_schedule:
pulse.play(Gaussian(duration=64, amp=amp, sigma=8), pulse.DriveChannel(0))
circ.add_calibration(custom_gate, [0], my_schedule)
circ.assign_parameters([0.3333333333333333], inplace=True)
Here is code to show the discrepancy in parameter value:
from io import BytesIO
from qiskit.qpy import dump, load
buff = BytesIO()
dump(circ, buff)
buff.seek(0)
qpy_circ = load(buff)[0]
instr_param_value = float(qpy_circ.data[0].operation.params[0])
cal_param_value = next(iter(next(iter(qpy_circ.calibrations.values()))))[1][0]
instr_param_value == cal_param_value
What should happen?
The above code should give True
for the final expression, but it gives False
.
Any suggestions?
The way the current system works is that a circuit instruction can have a name, a tuple of qubits it operates on, and a list of parameters (which can be floats). Then in the calibrations
attribute of the circuit there is a nested dictionary structure with keys <instruction_name>:<qubit_tuple>:<parameter_tuple>
mapping to schedule definitions. When the backend encounters a custom instruction, it tries to look up a schedule definition in the calibrations
attribute using these keys, so there must be an exact match. Floats as dictionary keys are not great, and I could see an argument for a different system, but maybe for now we should just make the current system work.
The cause of the discrepancy is that QuantumCircuit.assign_parameters
handles the circuit instruction and calibration substitutions differently. For the circuit instruction, assign_parameters
binds the float value into a parameter expression:
as that is what assign()
does:
For the calibration parameter, assign_parameters
calls _assign_calibration_parameters
which substitutes scalar parameters with floats:
The reason this discrepancy matters for high precision floats is that ParameterExpression
gets serialized using sympy:
while the float in the calibrations key gets written directly as a float (struct.pack("!d", val)
) by write_value()
:
One solution could be for the assignment of a float to a circuit instruction parameter to also directly replace it with the float as happens now with the calibration parameter. Other options could include improving the precision of the parameter so that it deserializes to something that matches the deserialized float or doing a more significant rework of how we keep track of parameterized calibrations.