Note
This is the documentation for the current state of the development branch of Qiskit Experiments. The documentation or APIs here can change prior to being released.
BackendTiming¶
- class BackendTiming(backend)[source]¶
Helper for calculating pulse and delay times for an experiment
The methods and properties provided by this class help with calculating delay and pulse timing that depends on the timing constraints of the backend.
When designing qubit characterization experiments, it is often necessary to deal with precise timing of pulses and delays. The fact that physical backends (i.e. not simulators) only support sampling time at intervals of
dt
complicates this process as times must be rounded. Besides the sampling time, there can be additional constraints like a minimum pulse length or a pulse granularity, which specifies the allowed increments of a pulse length in samples (i.e., for a granularity of 16, pulse lengths of 64 and 80 samples are valid but not any number in between).Here are some specific problems that can occur when dealing with timing constraints for pulses and delays:
An invalid pulse length or pulse start time could result in an error from the backend.
An invalid delay length could be rounded by the backend, and this rounding could lead to error in analysis that assumes the unrounded value.
An invalid delay length that requires rounding could trigger a new scheduling pass of a circuit during transpilation, which is a computationally expensive process. Scheduling the circuit with valid timing to start out can avoid this rescheduling.
While there are separate alignment requirements for drive (
pulse_alignment
) and for measurement (acquire_alignment
) channels, the nature of pulse and circuit instruction alignment can couple the timing of different instructions, resulting in improperly aligned instructions. For example, consider this circuit:from qiskit import QuantumCircuit qc = QuantumCircuit(1, 1) qc.x(0) qc.delay(delay, 0) qc.x(0) qc.delay(delay2, 0) qc.measure(0, 0)
Because the circuit instructions are all pushed together sequentially in time without extra delays, whether or not the
measure
instruction occurs at a valid time depends on the details of the circuit. In particular, since thex
gates typically have durations that are multiples ofacquire_alignment
(becausegranularity
usually is), themeasure
start will occur at a time consistent withacquire_alignment
whendelay + delay2
is a multiple ofacquire_alignment
. Note that in the case of IBM Quantum backends, whenacquire_alignment
is not satisfied, there is no error reported by Qiskit or by the backend. Instead the measurement pulse is misaligned relative to the start of the signal acquisition, resulting in an incorrect phase and often an incorrect state discrimination.
To help avoid these problems,
BackendTiming
provides methods for calculating pulse and delay durations. These methods work with samples and seconds as appropriate. If these methods are used for all durations in a circuit, the alignment constraints should always be satisfied.Note
For delay duration, the least common multiple of
pulse_alignment
andacquire_alignment
is used as the granularity. Thus, in the example above about the coupling betweenpulse_alignment
andacquire_alignment
,delay
anddelay2
would each be rounded to a multiple ofacquire_alignment
and so the sum would always be a multiple of each alignment value as well. This approach modifies some valid circuits (like each delay being half ofacquire_alignment
) but has the benefit of always being valid without detailed analysis of the full circuit.As an example use-case for
BackendTiming
, consider a T1 experiment where delay times are specified in seconds in aqiskit_experiments.framework.BaseExperiment.circuits()
method as follows:def circuits(self): # Pass backend to BackendTiming timing = BackendTiming(self.backend) circuits = [] # delays is a list of delay values in seconds for delay in self.experiment_options.delays: circ = QuantumCircuit(1, 1) circ.x(0) # Convert delay into appropriate units for backend and also set # those units with delay_unit circ.delay(timing.round_delay(time=delay), 0, timing.delay_unit) circ.measure(0, 0) # Use delay_time to get the actual value in seconds that was # set on the backend for the xval rather than the delay # variable's nominal value. circ.metadata = { "unit": "s", "xval": timing.delay_time(time=delay), } circuits.append(circ)
As another example, consider a time Rabi experiment where the width of a pulse in a schedule is stretched:
from qiskit import pulse from qiskit.circuit import Gate, Parameter def circuits(self): chan = pulse.DriveChannel(0) dur = Paramater("duration") with pulse.build() as sched: pulse.play(pulse.Gaussian(duration=dur, amp=1, sigma=dur / 4), chan) gate = Gate("Rabi", num_qubits=1, params=[dur]) template_circ = QuantumCircuit(1, 1) template_circ.append(gate, [0]) template_circ.measure(0, 0) template_circ.add_calibration(gate, (0,), sched) # Pass backend to BackendTiming timing = BackendTiming(self.backend) circs = [] # durations is a list of pulse durations in seconds for duration in self.experiment_options.durations: # Calculate valid sample number closest to this duration circ = template_circ.assign_parameters( {dur: timing.round_pulse(time=duration)}, inplace=False, ) # Track corresponding duration for the pulse in seconds circ.metadata = { "xval": timing.pulse_time(time=duration), "unit": "s", }
Initialize backend timing object
- Parameters:
experiment – the experiment to provide timing help for
Attributes
The delay unit for the current backend
The backend's
dt
value, copied toBackendTiming
for convenienceMethods
BackendTiming.delay_time
(*[, time, samples])The closest valid delay time in seconds to the input
BackendTiming.pulse_time
(*[, time, samples])The closest valid pulse duration to the input in seconds
BackendTiming.round_delay
(*[, time, samples])Delay duration closest to input and consistent with timing constraints
BackendTiming.round_pulse
(*[, time, samples])The number of samples giving the valid pulse duration closest to the input