Predefined Sequences

Instead of uploading waveforms defined as numpy arrays, the zhinst-toolkit also provides pre-defined sequences for standard experiments. These differ between UHFQA and HDAWG and include:

  • Rabi (HDAWG)

  • T1 (HDAWG)

  • T2 Ramsey (HDAWG)

  • Trigger (HDAWG)

  • Continuous-Wave (CW) Spectroscopy (UHFQA)

  • Pulsed Spectroscopy (UHFQA)

  • Multiplexed Readout (UHFQA)

Here we show some basic examples of predefined sequences and how they can be used with the HDAWG and UHFQA together in an experiment. To ensure that the two devices use the same Data Server, we use the zhinst-toolkit’s MultiDeviceConnection and to setup and connect a Data Server and then connect the two devices to it.

[1]:
import zhinst.toolkit as tk
import numpy as np

# create a 'Multi Device Connection'
mdc = tk.MultiDeviceConnection(host="10.42.0.226")
mdc.setup()

# connect the devices
mdc.connect_device(tk.HDAWG("hdawg 1", "dev8030"))
mdc.connect_device(tk.UHFQA("uhfqa 1", "dev2266"))

# references to the instruments are held in attributes 'hdawgs' and 'uhfqas'
hdawg = mdc.hdawgs["hdawg 1"]
uhfqa = mdc.uhfqas["uhfqa 1"]
Successfully connected to data server at 10.42.0.2268004 api version: 6
Successfully connected to device DEV8030 on interface 1GBE
Successfully connected to device DEV2266 on interface 1GBE

Resonator spectroscopy

A typical first experiment in a circuit QED setup would be to find the frequency of microwave resonator used for dispersive readout. For this resonator spectroscopy we can use the predefined "Pulsed Spectroscopy" sequence on the UHFQA. It simply plays a modulated rectangular waveform of duration pulse_length. We configure one AWG Core of the HDAWG to a "Trigger" sequence to trigger the spectroscopy pulses on the UHFQA (trigger_mode="External Trigger").

[2]:
# assign AWG Cores
trigger = hdawg.awgs[0]
readout = uhfqa.awg

# common sequence parameters
period = 10e-6
repetitions = 1e3

# configure trigger AWG
trigger.set_sequence_params(
    sequence_type="Trigger",
    period=period,
    repetitions=repetitions,
)
trigger.compile()

# configure UHFQA to Pulsed Spectroscopy
readout.set_sequence_params(
    sequence_type="Pulsed Spectroscopy",
    period=period,
    repetitions=repetitions,
    trigger_mode= "External Trigger",   # UHFQA triggered by Master Trigger
)
readout.compile()
Compilation successful
hdawg 1-0: Sequencer status: ELF file uploaded
Compilation successful
uhfqa 1-0: Sequencer status: ELF file uploaded

The spectroscopy sequence enables output modulation on the UHFQA. The signals on the two output channels are modulated with the sine and cosine of the internal oscillator. Its frequency is set by the parameter uhfqa.nodetree.osc.freq. On the signal acquisition side, the integration mode: “Spectroscopy” is used, which demodulates the signal input with the same internal oscillator.

A sweep of the IF modulation frequency could be done like this:

[ ]:
# configure results to length 1000 and 1 hardware averages
uhfqa.result_source("Integration")
ufhqa.arm(length=repetitions, averages=1)
modulation_freq = uhfqa.nodetree.osc.freq

# define the sweep points and an empty array for results
frequencies = np.linspace(50e6, 100e6, 101)
results = np.array([])

for f in frequencies:
    modulation_freq(f)                               # set modulation frequency
    uhfqa.arm()                                      # reset the readout
    readout.run()                                    # start readout AWG
    trigger.run()                                    # start trigger AWG
    trigger.wait_done()                              # wait until trigger AWG has finished
    avg_result = np.mean(uhfqa.channels[0].result()) # average the result vector
    results = np.append(results, avg_result)         # append to results

Qubit spectroscopy

Once the frequency of the readout resonator is found, one of the next typical experiments would be qubit spectroscopy. A drive pulse for qubit spectroscopy can be implemented in one of two ways. Either the predefined "Rabi" sequence (see below) is used with a single amplitude point and a long pulse width. The other option would be to use the "Simple" sequence and upload the drive pulse of choice as a numpy array. For simplicity we will show the latter option and play a rectangular pulse of 5 us duration on the HDAWG. In any way, the IQ Modulation mode of the AWG Core can be used to modulate the outputs at an IF frequency such that the two channels can be used as inputs for IQ upconversion.

On the UHFQA, we use the predefined "Readout" sequence. This sequence creates a readout tone for every enabled Readout Channel of the UHFQA at their respective readout_frequency. The readout tones for all enabled channels are summed up to a readout pulse of length readout_length that can be used for multiplexed qubit readout. By enabling the readout channel we also program the integration weights of the Readout Channel to demodulate the signal at the given readout_frequency.

[8]:
# assign AWGs
drive = hdawg.awgs[0]
readout = uhfqa.awg
channel = uhfqa.channels[0]

period = 10e-6
repetitions = 100

# configure trigger AWG
drive.set_sequence_params(
    sequence_type="Simple",
    period=period,
    repetitions=repetitions,
    trigger_mode="Send Trigger",
)
drive.reset_queue()
wave = np.ones(int(2.4e9 * 5e-6))    # sampling rate x pulse duration
drive.queue_waveform(wave, wave)
drive.compile_and_upload_waveforms()

# configure UHFQA to Readout
channel.enable()
channel.readout_frequency(123e6)
readout.set_sequence_params(
    sequence_type="Readout",
    period=period,
    repetitions=repetitions,
    pulse_lenth=2e-6,
    trigger_mode="External Trigger", # UHFQA triggered by Master Trigger
)
readout.compile()
Current length of queue: 1
Compilation successful
hdawg 1-0: Sequencer status: ELF file uploaded
Upload of 1 waveforms took 0.067963 s
Compilation successful
uhfqa 1-0: Sequencer status: ELF file uploaded

To sweep the frequency of the drive signal, we want to enable modulation of the ouput signal of the qubit drive with the internal oscillator. This is done by enabling the IQ Modulation mode of the AWG Core (modulation with the internal oscillator for IQ signal generation). The frequency of the oscillator can then be swept over the desired range in a loop like this:

[10]:
# configure results to length 1000 and 1 hardware averages
uhfqa.nodetree.qa.result.source(7)
uhfqa.arm(length=repetitions, averages=1)

frequencies = np.linspace(20e6, 50e6, 101)
results = np.array([])

# enable IQ Modulation mode
drive.enable_iq_modulation()

for f in frequencies:
    drive.modulation_freq(f)
    uhfqa.arm()
    readout.run()
    drive.run()
    drive.wait_done()
    avg_result = np.mean(channel.result())
    results = np.append(results, avg_result)

Rabi sequence

The next kind of typical experiment would be to drive coherent oscillations between the ground and the excited state of the qubit, so called Rabi oscillations. The pre-defined Rabi Sequence plays a simple Gaussian pulse to drive the qubit. It is defined by the sequence parameters pulse_width and pulse_truncation. In this Rabi Sequence we sweep the amplitude of the drive pulse in order to rotate the qubit state by different amounts. The sequence parameter pulse_amplitudes expects a list or array of values for the amplitude sweep. With the IQ Modulation mode enabled, the Gaussian wave envelope is modulated at with the internal oscilaltor at a frequeny set by the paraemter modulation_freq.

A typical Rabi sequence could look like this:

[11]:
# assign AWGs
drive = hdawg.awgs[0]
readout = uhfqa.awg
channel = uhfqa.channels[0]

averages = 2**8
period = 10e-6

# define numpy array with Rabi Amplutdes
rabi_amplitudes = np.linspace(0, 1.0, 101)

# configure Rabi Amplitude Sweep
hdawg.awgs[0].enable_iq_modulation()
hdawg.awgs[0].modulation_freq(100e6)

hdawg.awgs[0].set_sequence_params(
    sequence_type="Rabi",
    pulse_width=50e-9,
    pulse_amplitudes=rabi_amplitudes,
    period=period,
    repetitions=averages,
    trigger_mode="Send Trigger",
)
hdawg.awgs[0].compile()

# configure UHFQA to Readout
channel.enable()
channel.readout_frequency(123e6)
readout.set_sequence_params(
    sequence_type="Readout",
    period=period,
    repetitions=averages*len(rabi_amplitudes), # play averages x sweep-points readout pulses
    trigger_mode= "External Trigger",          # UHFQA triggered by Master Trigger
    pulse_lenth=2e-6,
)
readout.compile()
Compilation successful
hdawg 1-0: Sequencer status: ELF file uploaded
Compilation successful
uhfqa 1-0: Sequencer status: ELF file uploaded
[14]:
uhfqa.result_mode("Cyclic")
uhfqa.arm(length=len(rabi_amplitudes), averages=averages)
readout.run()
drive.run()
drive.wait_done()
result = channel.result()

Time-domain characterization: T1 & T2 Ramsey

After the Rabi experiment, time-domain characterization of superconducting qubits is needed to extract coherence times. This can be done with predefined sequences T1 and T2 Ramsey. They work in the same way as the Rabi Sequence. However, instead of passing an array of sweep values to the parameter pulse_amplitudes, the parameter is called time_delays. This parameter defines the shifts between the drive pulse and the time origin t=0 in seconds. Positive values shift the pulse forward in time with respect to the time origin.

[22]:
# assign AWGs
drive = hdawg.awgs[0]
readout = uhfqa.awg
channel = uhfqa.channels[0]

# shared sequence parameters
period = 50e-6
averages = 2**8

# define numpy array with Delay Times
delay_times = np.linspace(0.5e-6, 20e-6, 40)

# configure Rabi Amplitude Sweep
hdawg.awgs[0].enable_iq_modulation()
hdawg.awgs[0].modulation_freq(100e6)

hdawg.awgs[0].set_sequence_params(
    sequence_type="T1",
    pulse_width=50e-9,
    delay_times=delay_times,
    period=period,
    repetitions=averages,
    trigger_mode="Send Trigger",
)
hdawg.awgs[0].compile()

# configure UHFQA to Readout
channel.enable()
channel.readout_frequency(123e6)
readout.set_sequence_params(
    sequence_type="Readout",
    period=period,
    pulse_lenth=2e-6,
    repetitions=averages*len(delay_times),  # play averages x sweep-points readout pulses
    trigger_mode= "External Trigger",            # UHFQA triggered by Master Trigger
)
readout.compile()
Compilation successful
hdawg 1-0: Sequencer status: ELF file uploaded
Compilation successful
uhfqa 1-0: Sequencer status: ELF file uploaded
[23]:
uhfqa.arm(length=len(delay_times), averages=averages)
readout.run()
drive.run()
drive.wait_done()
result = channel.result()

Multiplexed readout

The sequence type ‘Readout’ is designed for multiplexed resonator readout of superconducting qubits using the UHFQA Quantum Analyzer. It takes the parameters readout frequency and readout amplitude from all enabled Readout Channels and assembles a multi-tone readout pulse.

As an example, this could be used for a multi-qubit Rabi experiment that measures Rabi-oscillations for different qubits at the same time. A setup for this experiment with one HDAWG and one UHFQA could look like this:

              HDAWG 1
           +-----------+                    ______________________________
+----<----+   AWG 1   |  Trigger          _|                              |_________:_
|         +-----------+                    :                              :         :
+----+----->  AWG 2   |  AWGs[0]          _:________________________|XXXXX|_________:_
     |    |-----------+                    :                              :         :
     +----->  AWG 3   |  AWGs[1]          _:_____________________|XXXXXXXX|_________:_
     |    |-----------+                    :                              :         :
     +----->  AWG 4   |  AWGs[2]          _:__________________|XXXXXXXXXXX|_________:_
     |    +-----------+                    :                              :         :
     |                                     :                              :         :
     |     UHFQA                           :                              :         :
     |    +-----------+                    :                              :         :
     +----->          | Readout           _:______________________________|XXXXX|___:_
          +-----------+

One AWG Core would be used to trigger the other AWG Cores as well as the UHFQA readout. All Rabi sequences on the HDAWG could be configured to align ‘End with Trigger’, whereas the readout sequence would ‘Start with Trigger’.

Custom Sequence

A ‘Custom’ allows the user to write their own .seqC sequence program and compile it on the AWG. The parameter path has to point to the existing .seqC file in the folder ‘…:nbsphinx-math:`Documents`:nbsphinx-math:`Zurich `Instruments:nbsphinx-math:`LabOne`:nbsphinx-math:`WebServer`:nbsphinx-math:`awg`:nbsphinx-math:`src`’. An advanced feature of the custom sequence is that placeholders can be defined and replaced by sequence parameters. The placeholders are defined by a string in the .seqC file of the format ‘\:math:`paramN`’ where N should be replaced with an index (starting from 0). The placeholders are then substituted by the sequence parameter (list) custom_params.

[ ]:
awg.set_sequence_params(
   path="...\Zurich Instruments\LabOne\WebServer\awg\src\myProgram.seqC",
   custom_params=[1000, 99, 1],
)

In this example, the placeholders in the .seqC program will be substituted like

  • ‘\:math:`param0`’ : 1000

  • ‘\:math:`param1`’ : 99

  • ‘\:math:`param2`’ : 1