Integration Weights Measurement

In this tutorial, the Scope of the SHFQA is used to measure integration weights that are needed for high-fidelity single-shot readout. For this, readout signals are acquired while the qubit is in the ground and the excited state using the Python API. In specific, the readout pulse is generated by the SHFQA Readout Pulse Generator, and the signal acquisition is done by the SHFQA Scope. The integration weights are derived from the difference of the acquired readout signals.

Goals and Requirements

This tutorial you will learn how to

  • configure input and output parameters,

  • configure Scope,

  • configure readout pulses,

  • configure the SHFQA Readout Pulse Generator,

  • run the experiment and calculate the integration weights.

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

    There 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. Define measurement parameters

    In this tutorial, integration weights of 8 qubits are sequentially measured using channel 1. The readout pulses with a duration of 600 ns are repeated 100 times for each qubit, and the Scope records all readout pulses and stores 2 averaged waveforms from the first and second 50 pulses. Here, we assume that the qubits are prepared in the ground state when applying the first 50 readout pulses, and in the excited state when applying the second 50 readout pulses. Each recording is triggered by the QA Monitor configured through the setting channel0_sequencer_monitor0. The trigger delay is set to 200 ns to compensate for the delay between the Readout Pulse Generator output and the input of the Integration Unit.

    # define parameters
    channel_index = 0
    num_qubits = 8
    readout_duration = 600e-9
    num_segments = 2
    num_averages = 50
    num_measurements = num_segments * num_averages
    scope_channel = 0
  4. Configure input and output of the SHFQA channel 1

    The 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. 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.

    # configure inputs and outputs
    shfqa_utils.configure_channel(
        daq,
        device_id,
        channel_index,
        center_frequency=5e9,
        input_range=0,
        output_range=-5,
        mode="readout",
    )
  5. Configure the SHFQA Scope

    The Scope of channel 1 is configured to record readout pulses. The recorded length is 600 ns, the number of averages is 50, and 2 segments in total for each qubit. The Scope is triggered by the "sequencer_monitor0", which can be enabled using the SeqC command startQA. The trigger delay setting is to compensate for the delay between generator output and input of the integration unit.

        # configure scope
    
       shfqa_utils.configure_scope(
            daq,
            device_id,
            input_select={scope_channel: f"channel{channel_index}_signal_input"},
            num_samples=int(readout_duration * shfqa_utils.SHFQA_SAMPLING_FREQUENCY),
            trigger_input=f"channel{channel_index}_sequencer_monitor0",
            num_segments=num_segments,
            num_averages=num_averages,
            # compensation for the delay between generator output and input of the integration unit
            trigger_delay=200e-9,
        )
  6. 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.

    In this example, 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
    excitation_pulses = helper_commons.generate_flat_top_gaussian(
        frequencies=np.linspace(2e6, 32e6, num_qubits),
        pulse_duration=500e-9,
        rise_fall_time=10e-9,
        sampling_rate=shfqa_utils.SHFQA_SAMPLING_FREQUENCY,
    )
    shfqa_utils.write_to_waveform_memory(
        daq, device_id, channel_index, waveforms=excitation_pulses
    )
  7. Configure the Readout Pulse Generator and run the experiment

    A for loop is used to measure the integration weights of 8 qubits sequentially. For each qubit, a new SeqC program is generated and uploaded according to the task "dig_trigger_play_single". Depending on the index of the qubits, the SeqC program plays readout pulses from different waveform memory blocks (set by the first argument of the command startQA). The third argument of this command being true means that a monitor trigger is enabled. This trigger is then used for triggering the Scope.

    repeat({num_measurements}) {{
        waitDigTrigger(1);
        startQA(QA_GEN_{waveform_slot}, 0x0, true,  0, 0x0);
    }}

    Before the measurement begins, the scope and the Readout Pulse generator have to be enabled, so that they are ready to receive triggers. By turning on a continuous software trigger, the Readout Pulse Generator is triggered, and the measurement starts. The continuous software trigger is configured such that it generates 100 triggers with a separation of 1 ms for each loop.

    The integration weights including their real and imaginary parts are calculated from the difference of 2 averaged data acquired by the Scope. Here, the assumption is that the qubit is prepared in the ground state in the first 50 measurements and in the excited state in the second 50 measurements. With the helper function, the real and imaginary part of the readout pulse and the calculated integration weights can be plotted, as shown in Figure 2 and Figure 3.

    # configure sequencer
    shfqa_utils.configure_sequencer_triggering(
        daq, device_id, channel_index, aux_trigger="software_trigger0"
    )
    
    # enable qachannel
    path = f"/{device_id}/qachannels/{channel_index}/"
    daq.setInt(path + "input/on", 1)
    daq.setInt(path + "output/on", 1)
    
    # run experiment measurement loop
    weights = {}
    for i in range(num_qubits):
    
        print(f"Measuring qubit {i}.")
    
        # upload sequencer program
        seqc_program = helper.generate_sequencer_program(
            num_measurements=num_measurements,
            task="dig_trigger_play_single",
            waveform_slot=i,
        )
        shfqa_utils.load_sequencer_program(
            daq, device_id, channel_index, sequencer_program=seqc_program
        )
    
        # start a measurement
        shfqa_utils.enable_scope(daq, device_id, single=1)
        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_measurements, wait_time=0.001
        )
    
        # get results to calculate weights and plot data
        scope_data, *_ = shfqa_utils.get_scope_data(daq, device_id, time_out=5)
        weights[i] = helper.calculate_readout_weights(scope_data[scope_channel])
    
        helper.plot_scope_data_for_weights(
            scope_data[scope_channel],
            sampling_rate=shfqc_utils.SHFQC_SAMPLING_FREQUENCY,
        )
        helper.plot_readout_weights(
            weights[i], sampling_rate=shfqc_utils.SHFQC_SAMPLING_FREQUENCY,
        )
    scope data for weights
    Figure 2. Readout pulses acquired by the SHFQA Scope.
readout weigths
Figure 3. Integration weights calculated from Figure 2.