Cluster
In this section we introduce how to configure Qblox Clusters and the options available for them via Quantify. For information about their lower-level functionality, you can consult the Qblox Instruments documentation. If you are not familiar with how to compile hardware configuration in general, see Compiling to Hardware.
General hardware compilation config structure, example
We start by looking at an example config for a single Cluster. The hardware compilation configuration specifies which modules are used ("hardware_descriptions"
) and how they are connected to the quantum device ("connectivity"
), along with some (optional) "hardware_options"
, like modulation frequencies, gains and attenuations, or mixer corrections. The general structure of this configuration file is described in the Hardware compilation configuration section of the User guide.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {
4 "cluster0": {
5 "hardware_type": "Qblox",
6 "instrument_type": "Cluster",
7 "ref": "internal",
8 "modules": {
9 "1": {
10 "module_type": "QCM"
11 },
12 "2": {
13 "module_type": "QCM_RF"
14 },
15 }
16 },
17 "lo0": {
18 "hardware_type": "LocalOscillator",
19 "power": 20
20 },
21 },
22 "hardware_options": {
23 "modulation_frequencies": {
24 "q4:mw-q4.01": {
25 "interm_freq": 200000000.0
26 },
27 "q5:mw-q5.01": {
28 "interm_freq": 50000000.0,
29 },
30 },
31 "mixer_corrections": {
32 "q4:mw-q4.01": {
33 "amp_ratio": 0.9999,
34 "phase_error": -4.2
35 }
36 },
37 },
38 "connectivity": {
39 "cluster0": {
40 "cluster0_module1": {
41 "complex_output_0": {
42 "lo_name": "lo0",
43 "portclock_configs": [
44 {
45 "clock": "q4.01",
46 "port": "q4:mw",
47 },
48 ]
49 },
50 },
51 "cluster0_module2": {
52 "complex_output_0": {
53 "portclock_configs": [
54 {
55 "clock": "q5.01",
56 "port": "q5:mw"
57 }
58 ]
59 },
60 },
61 },
62 },
63}
Notice the "quantify_scheduler.backends.qblox_backend.hardware_compile"
backend is used.
In the example, the Cluster is specified using an instrument with "instrument_type": "Cluster"
. In the backend, the Cluster instrument functions as a collection of modules.
The only instrument types that can be at the top level are:
"Cluster"
,"LocalOscillator"
.
Hardware description
To compile to a Cluster, one should include a valid ClusterDescription
in the "hardware_description"
part of the hardware compilation config.
The name of the Cluster (the key of the structure, "cluster0"
in the example) can be chosen freely.
- class ClusterDescription(**data)[source]
Information needed to specify a Cluster in the
CompilationConfig
.- __init__(**data)
Create a new model by parsing and validating input data from keyword arguments.
Raises ValidationError if the input data cannot be parsed to form a valid model.
-
hardware_type:
Literal
['Qblox'
][source] The hardware type, used to select this datastructure when parsing a
CompilationConfig
.
-
instrument_type:
Literal
['Cluster'
][source] The instrument type, used to select this datastructure when parsing a
CompilationConfig
.
-
modules:
Dict
[int
,ClusterModuleDescription
][source] Description of the modules of this Cluster, using slot index as key.
Here the modules are described by:
- class ClusterModuleDescription(**data)[source]
Information needed to specify a Cluster module in the
CompilationConfig
.- __init__(**data)
Create a new model by parsing and validating input data from keyword arguments.
Raises ValidationError if the input data cannot be parsed to form a valid model.
Write sequencer program to files
It is possible to optionally set "sequence_to_file"
key to True
or False
. If it’s not set Quantify will behave the same way as if it was set to True
. If it is True
, a file will be created for each sequencer with the program that’s uploaded to the sequencer with the filename <data_dir>/schedules/<year><month><day>-<hour><minute><seconds>-<milliseconds>-<random>_<port>_<clock>.json
in a JSON format, where <random>
is 6 random characters in the range 0-9
, a-f
.
It is also possible to set this parameter per module via its module configuration.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {
4 "cluster0": {
5 "hardware_type": "Qblox",
6 "instrument_type": "Cluster",
7 "ref": "internal",
8 "sequence_to_file": True,
9 "modules": {...}
10 }
11 },
12 "hardware_options": {...},
13 "connectivity": {...}
14}
Local Oscillator description
Local oscillator instrument can be added and then used for baseband modules. You can then reference the local oscillator instrument at the output with "lo_name"
.
The two mandatory parameters are the "hardware_type"
(which should be "LocalOscillator"
), and "power"
. The local oscillator frequency is controlled through the "modulation_frequencies"
hardware option (see Modulation frequencies).
Note that it is possible to add "generic_icc_name"
as an optional parameter to the local oscillator hardware description, but only the default name "generic"
is supported currently with the Qblox backend.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {
4 "cluster0": {...},
5 "lo1": {
6 "hardware_type": "LocalOscillator",
7 "power": 20
8 },
9 },
10 "hardware_options": {
11 "modulation_frequencies": {
12 "q1:mw-q1.01": {
13 "lo_freq": 5e9
14 }
15 }
16 },
17 "connectivity": {
18 "cluster0": {
19 "cluster0_module1": {
20 "complex_output_1": {
21 "lo_name": "lo1",
22 "portclock_configs": [
23 {
24 "clock": "q1.01",
25 "port": "q1:mw"
26 }
27 ]
28 },
29 },
30 },
31 }
32}
Connectivity
The Connectivity
describes how the inputs/outputs of the Cluster modules are connected to ports on the QuantumDevice
.
Note
The Connectivity
datastructure is currently under development. Information on the connectivity between port-clock combinations on the quantum device and ports on the control hardware is currently included in the old-style hardware configuration file, which should be included in the "connectivity"
field of the HardwareCompilationConfig
.
The possible inputs/outputs are
for
"QCM"
:"complex_output_{0,1}"
,"real_output_{0,1,2,3}"
,for
"QRM"
:"complex_{output,input}_0"
,"real_{output,input}_{0,1}"
.for
"QCM_RF"
:"complex_output_{0,1}"
,for
"QRM_RF"
:"complex_{output,input}_0"
.
Under these "{complex,real}_{x}"
keys, we specify the port-clock combinations an output may target
(see the Ports and clocks for more information on the role of ports and clocks within quantify-scheduler
).
For RF hardware, if an output is unused, the output will be turned off (this is to ensure that unused local oscillators do not interfere with used outputs).
Frequency multiplexing
It is possible to do frequency multiplexing of the signals by adding multiple port-clock configurations to the same output.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {
4 "cluster0": {
5 "hardware_type": "Qblox",
6 "instrument_type": "Cluster",
7 "ref": "internal",
8 "modules": {
9 "1": {
10 "module_type": "QCM"
11 },
12 }
13 },
14 },
15 "connectivity": {
16 "cluster0": {
17 "cluster0_module1": {
18 "complex_output_0": {
19 "portclock_configs": [
20 {
21 "port": "q0:mw",
22 "clock": "q0.01",
23 },
24 {
25 "port": "q0:mw",
26 "clock": "some_other_clock",
27 }
28 ]
29 },
30 "complex_output_1": {
31 "portclock_configs": [
32 {
33 "port": "q1:mw",
34 "clock": "q1.01",
35 }
36 ]
37 }
38 },
39 }
40 }
41}
In the given example, we added a second port-clock configuration to output 0. Now any signal on port "q0:mw"
with clock "some_other_clock"
will be added digitally to the signal with the same port but clock "q0.01"
.
The Qblox modules have six sequencers available, which sets the upper limit to our multiplexing capabilities.
Note
We note that it is a requirement of the backend that each combination of a port and a clock is unique, i.e. it is possible to use the same port or clock multiple times in the hardware config but the combination of a port with a certain clock can only occur once.
Real mode
Note
This setting will soon move to a different place in the HardwareCompilationConfig
.
To use real mode, the output/input name must start with "real_"
. When using real outputs, the backend automatically maps the signals to the correct output paths. We note that for real outputs, it is not allowed to use any pulses that have an imaginary component i.e. only real valued pulses are allowed. If you were to use a complex pulse, the backend will produce an error, e.g. square and ramp pulses are allowed but DRAG pulses not.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {...},
4 "hardware_options": {...},
5 "connectivity": {
6 "cluster0": {
7 "cluster0_module1": {
8 "real_output_0": {
9 "portclock_configs": [
10 {
11 "port": "q0:mw",
12 "clock": "q0.01",
13 }
14 ]
15 },
16 "real_output_1": {
17 "portclock_configs": [
18 {
19 "port": "q1:mw",
20 "clock": "q1.01",
21 }
22 ]
23 },
24 "real_output_2": {
25 "portclock_configs": [
26 {
27 "port": "q2:mw",
28 "clock": "q2.01",
29 }
30 ]
31 }
32 }
33 },
34 }
35}
Digital mode
Note
This setting will soon move to a different place in the HardwareCompilationConfig
.
The markers can be controlled by defining a digital I/O, and adding a MarkerPulse
on this I/O.
A digital I/O is defined by adding a "digital_output_n"
to the module configuration. n
is the number of the digital output port.
For a digital I/O only a port is required, no clocks or other parameters are needed.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {...},
4 "hardware_options": {...},
5 "connectivity": {
6 "cluster0": {
7 "cluster0_module1": {
8 "digital_output_0": {
9 "portclock_configs": [
10 {
11 "port": "q0:switch",
12 },
13 ],
14 },
15 },
16 },
17 }
18}
The MarkerPulse
is defined by adding a MarkerPulse
to the sequence in question. It takes the same parameters as any other pulse.
schedule.add(MarkerPulse(duration=52e-9, port="q0:switch"))
Marker configuration
Note
This setting will soon move to a different place in the HardwareCompilationConfig
.
The markers can be configured by adding a "marker_debug_mode_enable"
key to I/O configurations. If the value is set to True, the operations defined for this I/O will be accompanied by a 4 ns trigger pulse on the marker located next to the I/O port.
The marker will be pulled high at the same time as the module starts playing or acquiring.
"complex_output_0": {
"marker_debug_mode_enable": True,
...
}
Portclock configuration
Note
These settings will soon move to a different place in the HardwareCompilationConfig
.
Each module can have at most 6 port-clock combinations defined, and the name for each "port"
and "clock"
combination must be unique. Each of these port-clock combinations is associated with one sequencer in the Qblox hardware.
Note
We note that it is a requirement of the backend that each combination of a port and a clock is unique, i.e. it is possible to use the same port or clock multiple times in the hardware config but the combination of a port with a certain clock can only occur once.
Note
If you use gate-level operations, you have to follow strict rules for each kind of operation on which port name you can use (what’s the naming convention for each port resource).
"<device element name>:mw"
forRxy
operation (and its derived operations),"<device element name>:res"
for any measure operation,"<device element name>:fl"
for the flux port.
The only required keys are the "port"
and "clock"
which are needed to be defined.
The following parameters are available.
"ttl_acq_threshold"
,"init_offset_awg_path_0"
by default0.0
, must be between-1.0
and1.0
,"init_offset_awg_path_1"
by default0.0
, must be between-1.0
and1.0
,"init_gain_awg_path_0"
by default1.0
, must be between-1.0
and1.0
,"init_gain_awg_path_1"
by default1.0
, must be between-1.0
and1.0
,"qasm_hook_func"
, see QASM hook,"instruction_generated_pulses_enabled"
, see Instruction generated pulses (deprecated).
QASM hook
It is possible to inject custom qasm instructions for each portclock (sequencer) after the compiler inserts the footer and the stop instruction in the generated qasm program. See the following example to insert a NOP (no operation) at the beginning of the program at line 0.
1def _func_for_hook_test(qasm: QASMProgram):
2 qasm.instructions.insert(
3 0, QASMProgram.get_instruction_as_list(q1asm_instructions.NOP)
4 )
5
6hardware_compilation_cfg = {
7 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
8 "hardware_description": {...},
9 "hardware_options": {...},
10 "connectivity": {
11 "cluster0_module1": {
12 "complex_output_0": {
13 "portclock_configs": [
14 {
15 "port": "q0:mw",
16 "clock": "q0.01",
17 "qasm_hook_func": _func_for_hook_test,
18 }
19 ]
20 }
21 }
22 }
23}
Instruction generated pulses
Warning
The instruction_generated_pulses_enabled
option is deprecated and will be removed in a future version. Long square pulses and staircase pulses can be generated with the newly introduced StitchedPulseBuilder
. More information can be found in Long waveform support.
The Qblox backend contains some intelligence that allows it to generate certain specific waveforms from the pulse library using a more complicated series of sequencer instructions, which helps conserve waveform memory. Though in order to keep the backend fully transparent, all such advanced capabilities are disabled by default.
In order to enable the advanced capabilities we need to add line "instruction_generated_pulses_enabled": True
to the port-clock configuration.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {...},
4 "hardware_options": {...},
5 "connectivity": {
6 "cluster0": {
7 "cluster0_module1": {
8 "complex_output_0": {
9 "portclock_configs": [
10 {
11 "port": "q0:mw",
12 "clock": "q0.01",
13 "instruction_generated_pulses_enabled": True,
14 }
15 ]
16 }
17 }
18 }
19 }
20}
Currently, this has the following effects:
Long square pulses get broken up into separate pulses with durations <= 1 us, which allows the modules to play square pulses longer than the waveform memory normally allows.
Staircase pulses are generated using offset instructions instead of using waveform memory
Hardware options
The Hardware Options provide a way of specifying some specific settings on the Cluster.
Mixer corrections
The backend also supports setting the parameters that are used by the hardware to correct for mixer imperfections in real-time.
We configure this by adding the "mixer_corrections"
to the hardware options for a specific port-clock combination. See the following example.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {...},
4 "connectivity": {...},
5 "hardware_options": {
6 "mixer_corrections": {
7 "q4:mw-q4.01": {
8 "dc_offset_i": -0.054,
9 "dc_offset_q": -0.034,
10 "amp_ratio": 0.9997,
11 "phase_error": -4.0,
12 }
13 }
14 }
15}
Gain and attenuation
For QRM, QRM-RF and QCM-RF modules you can set the gain and attenuation parameters in dB by adding the "power_scaling"
option for the corresponding port-clock combination in the "hardware_options"
.
Gain configuration
Note, these parameters only affect the QRM modules. For complex inputs you have to specify a tuple (for the I and Q inputs), and for real inputs a scalar value.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {
4 "cluster0": {
5 "hardware_type": "Qblox",
6 "instrument_type": "Cluster",
7 "ref": "internal",
8 "modules": {
9 "1": {
10 "module_type": "QRM"
11 },
12 "2": {
13 "module_type": "QRM"
14 },
15 }
16 },
17 },
18 "hardware_options": {
19 "power_scaling": {
20 "q0:res-q0.ro": {
21 "input_gain": (2,3)
22 },
23 "q0:fl-cl0.baseband": {
24 "input_gain": 2
25 }
26 },
27 },
28 "connectivity": {
29 "cluster0": {
30 "cluster0_module1": {
31 "complex_input_0": {
32 "portclock_configs": [
33 {
34 "clock": "q0.ro",
35 "port": "q0:res",
36 },
37 ]
38 },
39 },
40 "cluster0_module2": {
41 "real_input_0": {
42 "portclock_configs": [
43 {
44 "clock": "cl0.baseband",
45 "port": "q0:fl"
46 }
47 ]
48 },
49 },
50 }
51 }
52}
Attenuation configuration
The parameters
"output_att"
and"input_att"
for QRM-RF correspond to the qcodes parameters out0_att and in0_att respectively.The parameter
"output_att"
for QCM-RF correspond to the qcodes parameters out0_att and out1_att.
Note, that these parameters only affect RF modules. See Qblox Instruments: QCM-QRM documentation for allowed values.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {
4 "cluster0": {
5 "hardware_type": "Qblox",
6 "instrument_type": "Cluster",
7 "ref": "internal",
8 "modules": {
9 "1": {
10 "module_type": "QRM_RF"
11 },
12 "2": {
13 "module_type": "QCM_RF"
14 },
15 }
16 },
17 },
18 "hardware_options": {
19 "power_scaling": {
20 "q0:res-q0.ro": {
21 "output_att": 12,
22 "input_att": 10
23 },
24 "q0:mw-q0.01": {
25 "output_att": 4
26 }
27 },
28 },
29 "connectivity": {
30 "cluster0": {
31 "cluster0_module1": {
32 "complex_output_0": {
33 "portclock_configs": [
34 {
35 "clock": "q0.res",
36 "port": "q0:ro",
37 },
38 ]
39 },
40 },
41 "cluster0_module2": {
42 "complex_output_0": {
43 "portclock_configs": [
44 {
45 "clock": "q0.01",
46 "port": "q0:mw"
47 }
48 ]
49 },
50 },
51 }
52 }
53}
Maximum AWG output voltage
Note
This subsection on max_awg_output_voltage
is still under construction.
Modulation frequencies
The aim of quantify-scheduler
is to only specify the final RF frequency when the signal arrives at the chip, rather than any parameters related to I/Q modulation. However, you still need to provide some parameters for the up/downconversion.
The backend assumes that upconversion happens according to the relation
These frequencies are specified for each port-clock combination in the "modulation_frequencies"
in the "hardware_options"
.
You can specify \(f_{RF}\) in multiple ways. You can specify it when you add a ClockResource
with freq
argument to your Schedule
, or when you specify the BasicTransmonElement.clock_freqs
.
Note
If you use gate-level operations, you have to follow strict rules for the naming of the clock resource, for each kind of operation:
"<transmon name>.01"
forRxy
operation (and its derived operations),"<transmon name>.ro"
for any measure operation,"<transmon name>.12"
for the \(|1\rangle \rightarrow |2\rangle\) transition.
Then:
For baseband modules, you can optionally specify a local oscillator by its name using the
"lo_name"
key in the Connectivity. If you specify it, the"lo_freq"
key in the"modulation_frequencies"
(see the example below) specifies \(f_{LO}\) of this local oscillator. Otherwise, \(f_{LO} = 0\) and \(f_{RF} = f_{IF}\). \(f_{RF} = f_{IF}\) can also be set in the hardware options explicitly with the"interm_freq"
key in the"modulation_frequencies"
.For RF modules, you can specify \(f_{IF}\) through the
"interm_freq"
key, and/or you can specify the local oscillator frequency for the output used for the port-clock combination with the"lo_freq"
, because they have internal local oscillators. Note, if you specify both, the relationship between these frequencies should hold, otherwise you get an error message. It’s important to note, that fast frequency sweeps only work when \(f_{LO}\) is fixed, and \(f_{IF}\) is unspecified. Because of this, it is generally advised to specify \(f_{LO}\) only.
In the following example:
For the baseband modules,
"complex_output_0"
’s \(f_{IF}\) is the same as the"q0.01"
clock resource’s frequency, and"complex_output_1"
’s \(f_{IF}\) is calculated using the frequency of"lo1"
(specified in"modulation_frequencies"
under"q1:mw-q1.01"
) and"q1.01"
.For the RF modules,
"complex_output_0"
’s \(f_{IF}\) is calculated using the provided"lo_freq"
for"q2:mw-q2.01"
and the frequency of"q2.01"
, and for"complex_output_1"
, the \(f_{LO}\) is calculated using the provided"interm_freq"
for"q3:mw-q3.01"
and the frequency of"q3.01"
.
1from quantify_scheduler import Schedule
2from quantify_scheduler.backends.graph_compilation import SerialCompiler
3from quantify_scheduler.device_under_test.quantum_device import QuantumDevice
4from quantify_scheduler.operations.pulse_library import SquarePulse
5from quantify_scheduler.resources import ClockResource
6
7hardware_compilation_cfg = {
8 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
9 "hardware_description": {
10 "cluster0": {
11 "hardware_type": "Qblox",
12 "instrument_type": "Cluster",
13 "ref": "internal",
14 "modules": {
15 "1": {
16 "module_type": "QCM"
17 },
18 "2": {
19 "module_type": "QCM_RF"
20 },
21 }
22 },
23 "lo1": {"hardware_type": "LocalOscillator", "power": 20},
24 },
25 "hardware_options": {
26 "modulation_frequencies": {
27 "q1:mw-q1.01": {
28 "lo_freq": 5e9
29 },
30 "q2:mw-q2.01": {
31 "lo_freq": 7e9
32 },
33 "q3:mw-q3.01": {
34 "interm_freq": 50e6
35 },
36 },
37 },
38 "connectivity": {
39 "cluster0": {
40 "cluster0_module1": {
41 "complex_output_0": {
42 "portclock_configs": [
43 {
44 "clock": "q0.01",
45 "port": "q0:mw"
46 }
47 ]
48 },
49 "complex_output_1": {
50 "lo_name": "lo1",
51 "portclock_configs": [
52 {
53 "clock": "q1.01",
54 "port": "q1:mw"
55 }
56 ]
57 },
58 },
59 "cluster0_module2": {
60 "complex_output_0": {
61 "portclock_configs": [
62 {
63 "clock": "q2.01",
64 "port": "q2:mw"
65 }
66 ]
67 },
68 "complex_output_1": {
69 "portclock_configs": [
70 {
71 "clock": "q3.01",
72 "port": "q3:mw"
73 }
74 ]
75 },
76 },
77 },
78 },
79}
80
81test_sched = Schedule("test_sched")
82test_sched.add_resource(ClockResource(name="q0.01", freq=8e9))
83test_sched.add_resource(ClockResource(name="q1.01", freq=9e9))
84test_sched.add_resource(ClockResource(name="q2.01", freq=8e9))
85test_sched.add_resource(ClockResource(name="q3.01", freq=9e9))
86
87test_sched.add(SquarePulse(amp=1, duration=1e-6, port="q0:mw", clock="q0.01"))
88test_sched.add(SquarePulse(amp=0.25, duration=1e-6, port="q1:mw", clock="q1.01"))
89test_sched.add(SquarePulse(amp=0.25, duration=1e-6, port="q2:mw", clock="q2.01"))
90test_sched.add(SquarePulse(amp=0.25, duration=1e-6, port="q3:mw", clock="q3.01"))
91
92quantum_device = QuantumDevice("DUT")
93quantum_device.hardware_config(hardware_compilation_cfg)
94compiler = SerialCompiler(name="compiler")
95_ = compiler.compile(
96 schedule=test_sched, config=quantum_device.generate_compilation_config()
97)
Downconverter
Note
This setting will soon move to a different place in the HardwareCompilationConfig
.
Note
This section is only relevant for users with custom qblox downconverter hardware.
Some users may have a custom Qblox downconverter module. In order to use it with this backend, we should specify a "downconverter_freq"
entry in the outputs that are connected to this module, as exemplified below.
The result is that the clock frequency is downconverted such that the signal reaching the target port is at the desired clock frequency, i.e. \(f_\mathrm{out} = f_\mathrm{downconverter} - f_\mathrm{in}\).
For baseband modules, downconversion will not happen if "mix_lo"
is not True
and there is no external LO specified ("mix_lo"
is True
by default). For RF modules, the "mix_lo"
setting is not used (effectively, always True
). Also see helper function determine_clock_lo_interm_freqs()
.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {
4 "cluster0": {
5 "hardware_type": "Qblox",
6 "instrument_type": "Cluster",
7 "ref": "internal",
8 "modules": {
9 "1": {
10 "module_type": "QCM"
11 },
12 "2": {
13 "module_type": "QCM_RF"
14 },
15 }
16 },
17 "lo1": {"hardware_type": "LocalOscillator", "power": 20},
18 },
19 "hardware_options": {
20 "modulation_frequencies": {
21 "q0:mw-q0.01": {
22 "interm_freq": 50000000.0
23 },
24 },
25 },
26 "connectivity": {
27 "cluster0": {
28 "cluster0_module1": {
29 "complex_output_0": {
30 "downconverter_freq": 9000000000,
31 "mix_lo": True,
32 "portclock_configs": [
33 {
34 "clock": "q0.01",
35 "port": "q0:mw"
36 }
37 ]
38 },
39 },
40 "cluster0_module2": {
41 "complex_output_0": {
42 "downconverter_freq": 9000000000,
43 "portclock_configs": [
44 {
45 "clock": "q0.01",
46 "port": "q0:mw"
47 }
48 ]
49 },
50 },
51 },
52 },
53}
54
55quantum_device.hardware_config(hardware_compilation_cfg)
56compiler = SerialCompiler(name="compiler")
57_ = compiler.compile(
58 schedule=test_sched, config=quantum_device.generate_compilation_config()
59)
Latency corrections
Latency corrections is a dict
containing the delays for each port-clock combination. It is possible to specify them under the key "latency_corrections"
in the hardware options. See the following example.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {...},
4 "connectivity": {...},
5 "hardware_options": {
6 "latency_corrections": {
7 "q4:mw-q4.01": 8e-9,
8 "q5:mw-q5.01": 4e-9
9 }
10 }
11}
Each correction is in nanoseconds. For each specified port-clock, the program start will be delayed by this amount of time. Note, the delay still has to be a multiple of the grid time.
Distortion corrections
Distortion corrections apply a function on the pulses which are in the schedule. Note, that this will not be applied to outputs generated by modifying the offset and gain/attenuation. The "distortion_corrections"
is an optional key in the hardware options. See the following example.
1hardware_compilation_cfg = {
2 "backend": "quantify_scheduler.backends.qblox_backend.hardware_compile",
3 "hardware_description": {...},
4 "connectivity": {...},
5 "hardware_options": {
6 "distortion_corrections": {
7 "q0:fl-cl0.baseband": {
8 "filter_func": "scipy.signal.lfilter",
9 "input_var_name": "x",
10 "kwargs": {
11 "b": [0.0, 0.5, 1.0],
12 "a": [1]
13 },
14 "clipping_values": [-2.5, 2.5]
15 }
16 }
17 }
If "distortion_corrections"
are set, then "filter_func"
, "input_var_name"
and "kwargs"
are required. If "clipping_values"
are set, its value must be a list with exactly 2 floats.
Clipping values are the boundaries to which the corrected pulses will be clipped, upon exceeding, these are optional to supply.
The "filter_func"
is a python function that we apply with "kwargs"
arguments. The waveform to be modified will be passed to this function in the argument name specified by "input_var_name"
. The waveform will be passed as a np.ndarray
.
Long waveform support
It is possible to play waveforms that are too long to fit in the waveform memory of Qblox modules. For a few standard waveforms, the square pulse, ramp pulse and staircase pulse, the following helper functions create operations that can readily be added to schedules:
1from quantify_scheduler.operations.pulse_factories import (
2 long_ramp_pulse,
3 long_square_pulse,
4 staircase_pulse,
5)
6
7ramp_pulse = long_ramp_pulse(amp=0.5, duration=1e-3, port="q0:mw")
8square_pulse = long_square_pulse(amp=0.5, duration=1e-3, port="q0:mw")
9staircase_pulse = staircase_pulse(
10 start_amp=0.0, final_amp=1.0, num_steps=20, duration=1e-4, port="q0:mw"
11)
More complex waveforms can be created from the StitchedPulseBuilder
. This class allows you to construct complex waveforms by stitching together available pulses, and adding voltage offsets in between. Voltage offsets can be specified with or without a duration. In the latter case, the offset will hold until the last operation in the StitchedPulse
ends. For example,
1from quantify_scheduler.operations.pulse_library import RampPulse
2from quantify_scheduler.operations.stitched_pulse import StitchedPulseBuilder
3
4trapezoid_pulse = (
5 StitchedPulseBuilder(port="q0:mw", clock="q0.01")
6 .add_pulse(RampPulse(amp=0.5, duration=1e-8, port="q0:mw"))
7 .add_voltage_offset(path_0=0.5, path_1=0.0, duration=1e-7)
8 .add_pulse(RampPulse(amp=-0.5, offset=0.5, duration=1e-8, port="q0:mw"))
9 .build()
10)
11
12repeat_pulse_with_offset = (
13 StitchedPulseBuilder(port="q0:mw", clock="q0.01")
14 .add_pulse(RampPulse(amp=0.2, duration=8e-6, port="q0:mw"))
15 .add_voltage_offset(path_0=0.4, path_1=0.0)
16 .add_pulse(RampPulse(amp=0.2, duration=8e-6, port="q0:mw"))
17 .build()
18)
Pulses and offsets are appended to the end of the last added operation by default. By specifying the append=False
keyword argument in the add_pulse
and add_voltage_offset
methods, in combination with the rel_time
argument, you can insert an operation at the specified time relative to the start of the StitchedPulse
. The example below uses this to generate a series of square pulses of various durations and amplitudes.
1from quantify_scheduler.operations.stitched_pulse import StitchedPulseBuilder
2
3offsets = [0.3, 0.4, 0.5]
4durations = [1e-6, 2e-6, 1e-6]
5start_times = [0.0, 2e-6, 6e-6]
6
7builder = StitchedPulseBuilder(port="q0:mw", clock="q0.01")
8
9for offset, duration, t_start in zip(offsets, durations, start_times):
10 builder.add_voltage_offset(
11 path_0=offset, path_1=0.0, duration=duration, append=False, rel_time=t_start
12 )
13
14pulse = builder.build()