Voltage offsets and long waveforms#
In this section we introduce how to use voltage offsets and build up long waveforms using Qblox Cluster modules.
Voltage offsets#
Qblox modules can set and hold a voltage on their outputs using the VoltageOffset
operation. The operation supports real and complex outputs, and it has effectively zero duration, meaning it takes effect at the exact moment you schedule it, and you can schedule other operations simultaneously. It can be used as follows:
1from quantify_scheduler.operations.pulse_library import VoltageOffset
2
3
4voltage_offset_real = VoltageOffset(
5 offset_path_I=0.5,
6 offset_path_Q=0.0,
7 port="q3:mw",
8 clock="q3.01",
9)
10
11voltage_offset_complex = VoltageOffset(
12 offset_path_I=0.5,
13 offset_path_Q=0.5,
14 port="q3:mw",
15 clock="q3.01",
16)
17
18sched = Schedule("offset_sched")
19sched.add_resource(ClockResource(name="q3.01", freq=9e9))
20
21ref_op = sched.add(voltage_offset_real)
22
23# It's possible to schedule a voltage offset simultaneously with a pulse
24sched.add(voltage_offset_complex, ref_op=ref_op, rel_time=1e-7)
25sched.add(SquarePulse(amp=1, duration=1e-7, port="q3:mw", clock="q3.01"))
26
27compiler = SerialCompiler(name="compiler")
28compiled_sched = compiler.compile(
29 schedule=sched, config=quantum_device.generate_compilation_config()
30)
Note that the offset will remain on the output until the schedule execution ends, or until a new voltage offset is set.
Important
While voltage offsets have effectively zero duration, the hardware does require 4 ns of “buffer” time after they are scheduled. That is, voltage offsets cannot be scheduled right at the end of the schedule, or at the end of a Control flow block (e.g., a loop).
If you do want a voltage offset at those moments, it is necessary to leave some time (at least 4 ns) by inserting an IdlePulse
.
For example, the following is not possible:
1from quantify_scheduler.operations.pulse_library import IdlePulse
2
3sched = Schedule("offset_sched")
4sched.add_resource(ClockResource(name="q3.01", freq=9e9))
5
6sched.add(
7 VoltageOffset(offset_path_I=0.5, offset_path_Q=0.0, port="q3:mw", clock="q3.01")
8)
9sched.add(SquarePulse(amp=1, duration=1e-7, port="q3:mw", clock="q3.01"))
10sched.add(
11 VoltageOffset(offset_path_I=0.0, offset_path_Q=0.0, port="q3:mw", clock="q3.01")
12)
13
14compiler = SerialCompiler(name="compiler")
15try:
16 compiled_sched = compiler.compile(
17 schedule=sched, config=quantum_device.generate_compilation_config()
18 )
19except RuntimeError as err:
20 print(err)
Parameter operation Pulse "UpdateParameters" (t0=1.0000000000000001e-07, duration=0) with start time 1.0000000000000001e-07 cannot be scheduled at the very end of a Schedule. The Schedule can be extended by adding an IdlePulse operation with a duration of at least 4 ns, or the Parameter operation can be replaced by another operation.
Instead, some time should be left at the end like so:
1sched = Schedule("offset_sched")
2sched.add_resource(ClockResource(name="q3.01", freq=9e9))
3
4sched.add(
5 VoltageOffset(offset_path_I=0.5, offset_path_Q=0.0, port="q3:mw", clock="q3.01")
6)
7sched.add(SquarePulse(amp=1, duration=1e-7, port="q3:mw", clock="q3.01"))
8sched.add(
9 VoltageOffset(offset_path_I=0.0, offset_path_Q=0.0, port="q3:mw", clock="q3.01")
10)
11sched.add(IdlePulse(4e-9))
12
13compiler = SerialCompiler(name="compiler")
14compiled_sched = compiler.compile(
15 schedule=sched, config=quantum_device.generate_compilation_config()
16)
Long waveforms via StitchedPulse#
The sequencers in Qblox modules have a waveform sample limit of MAX_SAMPLE_SIZE_WAVEFORMS
. Trying to play many (long) waveforms might cause you to exceed this limit. For certain waveforms, however, it is possible to use the available memory more efficiently. This section explains how to do this with the StitchedPulse
.
Factory functions#
For convenience, quantify-scheduler
provides helper functions for the square
(long_square_pulse()
), ramp
(long_ramp_pulse()
) and staircase
(staircase_pulse()
) waveforms for when they become too long to fit into the waveform memory of the hardware.
from quantify_scheduler.backends.qblox.operations import (
long_ramp_pulse,
long_square_pulse,
staircase_pulse,
)
sched = Schedule("Basic long pulses")
sched.add(
long_square_pulse(
amp=0.5,
duration=10e-6,
port="q0:fl",
clock=BasebandClockResource.IDENTITY,
),
)
sched.add(
long_ramp_pulse(
amp=1.0,
duration=10e-6,
port="q0:fl",
offset=-0.5,
clock=BasebandClockResource.IDENTITY,
),
rel_time=5e-7,
)
sched.add(
staircase_pulse(
start_amp=-0.5,
final_amp=0.5,
num_steps=20,
duration=10e-6,
port="q0:fl",
clock=BasebandClockResource.IDENTITY,
),
rel_time=5e-7,
)
quantum_device = QuantumDevice("quantum_device")
device_compiler = SerialCompiler("Device compiler", quantum_device)
comp_sched = device_compiler.compile(sched)
comp_sched.plot_pulse_diagram(
plot_backend="plotly", combine_waveforms_on_same_port=True
)
Tip
Add the argument combine_waveforms_on_same_port=True
to plot_pulse_diagram
to show the appearance of the final hardware output (default combine_waveforms_on_same_port=False
shows individual pulse elements).
Using these factory functions, the resulting square and staircase pulses use no waveform memory at all. The ramp pulse uses waveform memory for a short section of the waveform, which is repeated multiple times.
Builder class#
For more complicated shapes, the StitchedPulseBuilder
makes it possible to stitch together pulse shapes yourself. In the following example, we create a long soft square pulse where the constant-voltage middle part is created with a voltage offset instruction, using no waveform memory.
import numpy as np
from quantify_scheduler.operations.pulse_library import NumericalPulse
from quantify_scheduler.backends.qblox.operations import StitchedPulseBuilder
# Define a few constants
port = "q0:fl"
clock = BasebandClockResource.IDENTITY
ramp_duration = 4e-6
constant_duration = 8e-6
ramp_t = np.arange(0, round(ramp_duration * 1e9) + 1) * 1e-9
# Define the waveforms for the up and down ramps
hann_up = ramp_t / ramp_duration - 1 / 2 / np.pi * np.sin(
2 * np.pi / ramp_duration * ramp_t
)
hann_down = 1 - hann_up
# Make the stitched pulse
builder = StitchedPulseBuilder(port=port, clock=BasebandClockResource.IDENTITY)
builder.add_pulse(
NumericalPulse(samples=hann_up, t_samples=ramp_t, port=port, clock=clock)
)
builder.add_voltage_offset(path_I=1.0, path_Q=0.0, duration=constant_duration)
builder.add_pulse(
NumericalPulse(samples=hann_down, t_samples=ramp_t, port=port, clock=clock)
)
pulse = builder.build()
sched = Schedule("Long soft square pulse")
sched.add(pulse)
comp_sched = device_compiler.compile(sched)
comp_sched.plot_pulse_diagram(
plot_backend="plotly", combine_waveforms_on_same_port=True
)