Multiplexed Readout

In this tutorial, the SHFQA is used to perform multiplexed readout for 8 qubits.

Goals and Requirements

This tutorial will show you how to

  • configure input and output parameters,

  • configure frequency multiplexed readout pulses,

  • configure integration weights,

  • configure the SHFQA Readout Pulse Generator,

  • configure the Quantum Analyzer Setup (QA Setup) and the Quantum Analyzer Result Logger (QA Result Logger),

  • run a multiplexed readout measurement and acquire results from the QA Result Logger.

This tutorial is applicable to all SHFQA Instruments and no additional instrumentation is needed.

Users can download all LabOne API Python example files introduced in this tutorial from GitHub, https://github.com/zhinst/labone-api-examples.

Preparation

The tutorial starts with the instrument in the default configuration (e.g., after a power cycle). For an optimal tutorial experience, please follow these preparation steps:

  • ensure that the version of ziPython, LabOne and the Firmware of the SHFQA device are updated and compatible,

  • make sure that the instrument is powered on and connected by Ethernet to your local area network (LAN) where the host computer resides,

  • start LabOne and open the LabOne graphical user interface using the default web browser,

  • connect the SHFQA channel 1 output (input) to the readout input (output) line, see SHFQA connection.

shfqa tutorial loopback
Figure 1. SHFQA connection.

Tutorial

  1. Connect the SHFQA to a host computer

    Open a daq-session to the dev-instrument (see tutorial Connecting to the Instrument) and replace devXXXXX with the ID of the SHFQA instrument, e.g. dev12024.

    device_id = 'devXXXXX'
    apilevel_example = 6
    server_host = "localhost"
    server_port = 8004
    
    (daq, dev, _) = zhinst.utils.create_api_session(
        device_id, apilevel_example, server_host=server_host, server_port=server_port
    )
  2. Import packages

    A few packages are used in this tutorial. The numpy is one of the fundamental packages for scientific computing with Python. The zhinst.utils and shfqa_utils packages are the Zurich Instruments LabOne Python API utility and SHFQA utility class, respectively. The helper_qubit_readout and the helper_commons packages provide helper functions for this tutorial. The ziDAQServer package is used to create an API session (connect to a Data Server).

    import numpy as np
    import zhinst.utils
    import zhinst.deviceutils.shfqa as shfqa_utils
    import helper_qubit_readout as helper
    import helper_commons
  3. Configure input and output of the SHFQA channel 1

    The basic input and output settings, e.g., center frequency and power range, are defined in this step. Channel index 0 is used for this measurement, corresponding to channel 1 on the front panel. 8 qubits are measured by the multiplexed readout, and the readout results are averaged 100 times.

    The center frequency is set to 5 GHz. Note that the valid setting is from 1 GHz to 8 GHz with a granularity of 100 MHz. The input and output powers are set to 0 dBm and -5 dBm, respectively. The possible input power ranges are from -50 dBm to +10 dBm and the output power ranges are from -30 dBm to +10 dBm. The granularity of the power range for both input and output is 5 dB. There are two operation modes. The first one is the spectroscopy mode which uses digital oscillators to generate a modulated output signal. This mode is generally used for resonator spectroscopy. The other mode, the readout mode, uses the Readout Pulse Generator to generate readout sequences for experiments where the qubit readout frequencies are known already. This is why, in this tutorial, we will use the readout mode. The input and output parameters are uploaded to the Instrument using configure_channel.

    # define parameters
    channel_index = 0
    num_qubits = 8
    num_readouts = 100
    
    # configure inputs and outputs
    shfqa_utils.configure_channel(
        daq,
        device_id,
        channel_index,
        center_frequency=5e9,
        input_range=0,
        output_range=-5,
        mode="readout",
    )
  4. Generate and upload readout pulses

    The readout pulses can be defined sample-by-sample by users. They are uploaded to the waveform memory and then used by the Readout Pulse Generator.

    To make sure that the maximal amplitude of all readout waveforms does not exceed 1, a scaling factor is used. The factor 0.9 ensures that the interpolated waveform from 2 GSa/s to 6 GSa/s does not exceed 1 at the DAC as well. The offset frequencies are defined from 2 MHz to 32 MHz for 8 qubits. The frequency of each readout pulse is the sum of the center frequency and the offset frequency. The flat-top Gaussian readout pulses with pulse duration of 500 ns and rise and fall time of 10 ns are generated using the helper function generate_flat_top_gaussian. They are then uploaded to the waveform memory of channel 1. Enable both Signal input and output to be ready for the measurement.

    # generate and upload waveforms
    scaling = 0.9 / num_qubits
    readout_pulses = helper_commons.generate_flat_top_gaussian(
        frequencies=np.linspace(32e6, 230e6, num_qubits),
        pulse_duration=500e-9,
        rise_fall_time=10e-9,
        sampling_rate=shfqa_utils.SHFQA_SAMPLING_FREQUENCY,
        scaling=scaling,
    )
    
    shfqa_utils.write_to_waveform_memory(
        daq, device_id, channel_index, waveforms=readout_pulses
    )
    
    # enable qachannel
    path = f"/{device_id}/qachannels/{channel_index}/"
    daq.setInt(path + "input/on", 1)
    daq.setInt(path + "output/on", 1)
  5. Configure the integration weights and the QA Result Logger

    Integration weights are used to optimize readout SNR. Users can refer to the previous tutorial to learn how to measure the integration weights. Here, the 8 integration weights are defined to be identical to the readout pulses and are then uploaded to the corresponding integration units. The integration delay between generator output and input of the integration unit is set to 200 ns. The measurement results are saved in the result logger after 100 times averaging.

    The QA Result Logger is configured such that the readout results are averaged 100 times, and the averaged data is saved in the QA Result Logger of the channel.

    # configure result logger and weighted integration
    weights = helper.generate_integration_weights(readout_pulses)
    shfqa_utils.configure_weighted_integration(
        daq,
        device_id,
        channel_index,
        weights,
        # compensation for the delay between generator output and input of the integration unit
        integration_delay=200e-9,
    )
    
    shfqa_utils.configure_result_logger_for_readout(
        daq,
        device_id,
        channel_index,
        result_source="result_of_integration",
        result_length=num_readouts,
    )
  6. Configure the Readout Pulse Generator and upload the SeqC program

    In readout mode, the readout sequence is defined by a SeqC program. With the helper function a SeqC program according to task "dig_trigger_play_all" is generated. The sequence waits for a trigger to start the readout measurement which is a software trigger ("software_trigger0") in this tutorial. Of course, users can also select an external trigger. Once the sequence receives a trigger, the instrument will generate a readout pulse based on the waveform data in the called waveform memory, the QA_GEN_ALL means the readout pulse is generated as the sum of all waveforms saved in the waveform memory. Correspondingly, all integration units are enabled by the argument QA_INT_ALL. The measurement is repeated 100 times. Multiple startQA commands inside the repeat loop can be used for readout of multiple qubits sequentially.

    # SeqC program of the task "dig_trigger_play_all"
    repeat({num_measurements}) {{
        waitDigTrigger(1);
        startQA(QA_GEN_ALL, QA_INT_ALL, true, 0, 0x0);

    The SeqC program is then uploaded to the Readout Pulse Generator through

    # configure sequencer
    shfqa_utils.configure_sequencer_triggering(
        daq, device_id, channel_index, aux_trigger="software_trigger0"
    )
    seqc_program = helper.generate_sequencer_program(
        num_measurements=num_readouts, task="dig_trigger_play_all"
    )
    shfqa_utils.load_sequencer_program(
        daq, device_id, channel_index, sequencer_program=seqc_program
    )
  7. Run the experiment and plot readout results

    To run the experiment, the channels' QA Result Logger is enabled first, so it is ready to receive data. Then, the sequencer of the same channel, i.e., the Read Pulse Generator 1, is enabled and starts waiting for a first trigger. The measurement starts once the software triggered is enabled. The software trigger repeats 100 times with a separation of 2 ms.

    The results saved in the QA Result Logger can be simply transferred to a host computer using get_result_logger_data. With the helper function users can plot the results as shown in Figure 2. The results will also be displayed on the LabOne GUI QA Result Logger tab.

    # run experiment
    shfqa_utils.enable_result_logger(daq, device_id, channel_index, mode="readout")
    shfqa_utils.enable_sequencer(daq, device_id, channel_index, single=1)
    # Note: software triggering is used for illustration purposes only. Use a real
    # trigger source for actual experiments
    shfqa_utils.start_continuous_sw_trigger(
        daq, device_id, num_triggers=num_readouts, wait_time=2e-3
    )
    
    # get and plot results
    readout_results = shfqa_utils.get_result_logger_data(
        daq, device_id, channel_index, mode="readout"
    )
    helper.plot_readout_results(readout_results[:num_qubits])
    readout results
    Figure 2. Readout results of 8 qubits after weighted integration.