Prevent uploading of null waveforms (QAE-1193)
Explanation of changes
If a pulse is played on a channel whose real or imaginary waveform envelopes are all zeroes, then that envelope is still uploaded to the Cluster.
This is inefficient in terms of memory usage and data transfer.
This MR changes how such "null waveforms" are compiled in the Qblox backend. The change is that null waveforms are no longer stored in the waveform dictionary and consequently are not uploaded to hardware. The emitted instructions to the target Q1ASM program are also changed to only reference waveform indices which are non-null.
The proposed change is to introduce a 4-way conditional block conditional blocks in the GenericPulseStrategy.generate_data
method by testing for non-zero maximum absolute amplitudes returned from normalize_waveform_data
, on the in-phase and quadrature output paths.
This introduces four mutually exclusive cases:
1. If both max abs. amplitudes are 0, then the pulse is functionally an IdlePulse
, and will be replaced with a wait
instruction.
2. If max abs. amplitude of the in-phase output path is positive and the max abs. amplitude of the quadrature output path is zero, then only the waveform corresponding to the former will be uploaded.
3. If max abs. amplitude of the quadrature output path is positive and the max abs. amplitude of the in-phase output path is zero, then only the waveform corresponding to the former will be uploaded.
4. If the max abs. amplitudes of both output paths are positive, then the waveform is uploaded like before.
The change is intended to introduce no semantic changes on the target Q1ASM program output of the Qblox hardware (like timings of pulses, voltage levels, etc), but the compiled schedules' Q1ASM program can look different.
Additional small changes
- Small refactoring, now
quantify_scheduler.backends.qblox.helpers.add_to_wf_dict_if_unique
does not return all the information about the added waveform: like the uuid and the wholewf_dict
, these were unnecessary. - Some attributes of the
PulseStrategyPartial
became private, because it would cause issues if those were set externally (outside of the class). - The method by which new indices are assigned to each waveform during the compilation process (again it's in
quantify_scheduler.backends.qblox.helpers.add_to_wf_dict_if_unique
) was changed slightly: now it assigns the first free index, and not just the length of the list, because that could in theory cause problems. - Tests were slightly changed and added so that they better test compilation even with this new optimization added.
Motivation of changes
The motivation for this particular change is that it works for all pulses, regardless of real or complex mode output mode.
Example
Input schedule:
q0:fl
channel before changes:
Q1ASM for See the play
instructions: we are using 4 different waveforms.
set_mrk 0 # set markers to 0
wait_sync 4
upd_param 4
wait 4 # latency correction of 4 + 0 ns
move 1024,R0 # iterator for loop with label start
start:
reset_ph
upd_param 4
wait 65532 # auto generated wait (200020 ns)
wait 65532 # auto generated wait (200020 ns)
wait 65532 # auto generated wait (200020 ns)
wait 3424 # auto generated wait (200020 ns)
set_awg_gain 8187,0 # setting gain for DRAG
# We're using indices 0,1
play 0,1,4 # play DRAG (40 ns)
wait 36 # auto generated wait (36 ns)
set_awg_gain 8184,0 # setting gain for DRAG
# We're using indices 2,3
play 2,3,4 # play DRAG (20 ns)
wait 16 # auto generated wait (16 ns)
set_awg_gain 8187,0 # setting gain for DRAG
# We're using indices 0,1
play 0,1,4 # play DRAG (40 ns)
wait 1256 # auto generated wait (1256 ns)
loop R0,@start
stop
q0:fl
channel after changes:
Q1ASM for See the play
instructions: we are using much less waveforms.
20230628-175232-610-e6d772_q0fl_cl0.baseband.json
waveform 0 of 40 ns:
[-0.00019827570185208763, 0.00019827570185208574, 0.0010007121684926326, 0.002556447088155848, 0.0054452961587442535, 0.010581010386123182, 0.01931777081176339, 0.03353171582533737, 0.05563081672384893, 0.08843517682255925, 0.13487362366535668, 0.1974704432111291, 0.2776518319775251, 0.374977566553063, 0.4864782240388489, 0.6063205795701019, 0.7260028232688699, 0.8351822593044049, 0.9230752968631896, 0.9801881010331165, 1.0, 0.9801881010331165, 0.9230752968631899, 0.8351822593044049, 0.7260028232688699, 0.6063205795701023, 0.4864782240388489, 0.374977566553063, 0.27765183197752524, 0.1974704432111291, 0.13487362366535688, 0.08843517682255929, 0.055630816723848855, 0.03353171582533742, 0.01931777081176339, 0.010581010386123182, 0.005445296158744269, 0.002556447088155848, 0.00100071216849263, 0.0001982757018520877]
waveform 1 of 20 ns:
[-0.0005997345596128277, 0.0005997345596128305, 0.005046102513759707, 0.018924145290583706, 0.0552517665066743, 0.13452637986850224, 0.2773618963942665, 0.4862721070412287, 0.7258928464809945, 0.9230444208816916, 1.0, 0.9230444208816919, 0.7258928464809945, 0.4862721070412287, 0.27736189639426667, 0.13452637986850244, 0.055251766506674224, 0.018924145290583706, 0.0050461025137597225, 0.0005997345596128279]
set_mrk 0 # set markers to 0
wait_sync 4
upd_param 4
wait 4 # latency correction of 4 + 0 ns
move 1024,R0 # iterator for loop with label start
start:
reset_ph
upd_param 4
wait 65532 # auto generated wait (200020 ns)
wait 65532 # auto generated wait (200020 ns)
wait 65532 # auto generated wait (200020 ns)
wait 3424 # auto generated wait (200020 ns)
set_awg_gain 8187,0 # setting gain for DRAG
# Now we're only using indices 0,0
play 0,0,4 # play DRAG (40 ns)
wait 36 # auto generated wait (36 ns)
set_awg_gain 8184,0 # setting gain for DRAG
# Now we're only using indices 1,1
play 1,1,4 # play DRAG (20 ns)
wait 16 # auto generated wait (16 ns)
set_awg_gain 8187,0 # setting gain for DRAG
# Now we're only using indices 0,0
play 0,0,4 # play DRAG (40 ns)
wait 1256 # auto generated wait (1256 ns)
loop R0,@start
stop
Merge checklist
See also merge request guidelines
-
Merge request has been reviewed (in-depth by a knowledgeable contributor), and is approved by a project maintainer. -
New code is covered by unit tests (or N/A). -
New code is documented and docstrings use numpydoc format (or N/A). -
New functionality: considered making private instead of extending public API (or N/A). -
Public API changed: added @deprecated
(or N/A). -
Tested on hardware (or N/A). -
CHANGELOG.md
andAUTHORS.md
have been updated (or N/A). -
Windows tests in CI pipeline pass (manually triggered by maintainers before merging). - Maintainers do not hit Auto-merge, we need to actively check as manual tests do not block pipeline
For reference, the issues workflow is described in the contribution guidelines.