Multiplexed Readout

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

Goals and Requirements

This tutorial will show users 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 the 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,


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,

  • prepare an empty python script,

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


Users can use the Python program below to perform multiplexed readout for 8 qubits, and each step is explained as the following.

  1. Connect the SHFQA to a host computer

    Open a daq-session to the dev-instrument using this code from the tutorial "Connecting to the instrument" and replace devXXXXX with the ID of the SHFQA instrument, e.g. dev12024).

  2. Import packages

    There a few packages are used in this tutorial. The numpy is the fundamental package for scientific computing with Python. The shf_utils is the Zurich Instruments LabOne Python API utility class for the SHF-series instrument. The helper_qubit_readout includes helper functions for qubit readout examples. The helper_commons includes common helper functions for SHFQA examples. The ziDAQServer is used to create an API session (connect to a Data Server).

    import numpy as np
    import shf_utils
    import helper_qubit_readout as helper
    import helper_commons
    from zhinst.ziPython import ziDAQServer
  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, which is corresponding to channel 1 in 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, the valid setting is from 1 GHz to 8 GHz with a granularity of 100 MHz. The input and output power are set to 0 dBm and -5 dBm, respectively. The input power range is from -50 dBm to +10 dBm and the output power range is from -30 dBm to +10 dBm. The granularity of the power range for both input and output is 5 dB. There are two integration modes, one is the spectroscopy mode which uses digital oscillators to generate the modulation, and it is generally used for resonator and qubit spectroscopy, another is the readout mode which uses the Readout Pulse Generator to generate readout sequence for standard readout with known qubit readout frequencies. The readout mode is used in this tutorial. The input and output parameters are uploaded to the instrument using function configure_channel.

    # define parameters
    channel_index = 0
    num_qubits = 8
    num_readouts = 100
    # configure inputs and outputs
  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 sum of the amplitude factor of all readout waveforms does not exceed 1, a scaling factor is used. 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.

    # generate and upload waveforms
    scaling = 0.9 / num_qubits
    readout_pulses = helper_commons.generate_flat_top_gaussian(
        frequencies=np.linspace(32e6, 230e6, num_qubits),
    shfqa.write_to_waveform_memory(channel_index, waveforms=readout_pulses)
  5. Configure integration weights and the QA result logger

    Integration weights are used to optimize readout SNR. Users can refer to the tutorial of the Measure Integration Weights to measure the integration weights. The 8 integration weights are generated from the readout pulses directly and 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 1.

    # configure result logger and weighted integration
    weights = helper.generate_integration_weights(readout_pulses)
        # compensation for the delay between generator output and input of the integration unit
    shfqa.configure_result_logger(channel_index, 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. Here, a "software_trigger0" is used, 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 waveform memory, the QA_GEN_ALL means the readout pulse is generated by the sum of all waveforms saved in the waveform memory, and all integration units are enabled by the argument QA_INT_ALL. The measurement is repeated 100 times.

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

    The SeqC program is then uploaded to the Readout Pulse Generator 1.

    # generate and upload SeqC program
    shfqa.configure_sequencer_triggering(channel_index, aux_trigger="software_trigger0")
    seqc_program = helper.generate_sequencer_program(
        num_measurements=num_readouts, task="dig_trigger_play_all"
    shfqa.load_sequencer_program(channel_index, sequencer_program=seqc_program)
  7. Run the experiment and plot readout results

    To run the experiment, the QA result logger 1 is enabled first, so it is ready to receive data, then sequencer 1, i.e., the Read Pulse Generator 1, is enabled and ready to be triggered. The measurement starts once the software triggered is enabled. The software trigger repeats 100 times with a duration 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 helper function users can plot the results as shown in Figure 1. The results will also be displayed on the LabOne GUI QA Result Logger tab.

    # run experiment
    shfqa.start_continuous_sw_trigger(num_triggers=num_readouts, wait_time=2e-3)
    # get and plot results
    readout_results = shfqa.get_result_logger_data(channel_index)
    readout results
    Figure 1. Readout results of 8 qubits after weighted integration.