# Integration Weights Measurement

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

## Goals and Requirements

The goal of this tutorial is to demonstrate how to use the SHFQA Scope to measure integration weights that are needed for high-fidelity single-shot readout using Zurich Instruments Toolkit API.

For qubit control with the SHFSG Signal Generator, the SHFQC Qubit Controller and the HDAWG Arbitrary Wave Generator, and instrument synchronization and feedback with the PQSC Programmable Quantum System Controller, see tutorials in the Online Documentation.

 Users can download all zhinst-toolkit example files introduced in this tutorial from GitHub, https://github.com/zhinst/zhinst-toolkit/tree/main/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 the LabOne Python API, LabOne and the Firmware of the SHFQA device, zhinst-toolkit (pip install zhinst-toolkit) and Python (3.7 or newer) 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 or by USB (Maintenance port) to the host computer,

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

Figure 1. SHFQA connection.

## Tutorial

In the tutorial, readout signals are acquired while the qubit is in ground and excited state. 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.

1. Connect the instrument

Create a toolkit session to the data server and connect the device with the device ID, e.g. 'DEV12001', see Connecting to the Instrument.

# Load the LabOne API and other necessary packages
from zhinst.toolkit import Session
from scipy.signal import gaussian
import numpy as np

DEVICE_ID = 'DEVXXXXX'
SERVER_HOST = 'localhost'

session = Session(SERVER_HOST)              ## connect to data server
device = session.connect_device(DEVICE_ID)  ## connect to device

Generate a flat-top Gaussian readout pulses with 8 offset frequencies, see Multiplexed Qubit Readout.

NUM_QUBITS = 8
SAMPLING_FREQUENCY = 2e9
RISE_FALL_TIME = 10e-9
PULSE_DURATION = 500e-9

rise_fall_len = int(RISE_FALL_TIME * SAMPLING_FREQUENCY)
pulse_len = int(PULSE_DURATION * SAMPLING_FREQUENCY)
std_dev = rise_fall_len // 10

gauss = gaussian(2 * rise_fall_len, std_dev)
flat_top_gaussian = np.ones(pulse_len)
flat_top_gaussian[0:rise_fall_len] = gauss[0:rise_fall_len]
flat_top_gaussian[-rise_fall_len:] = gauss[-rise_fall_len:]
# Scaling
flat_top_gaussian *= 0.9

time_vec = np.linspace(0, PULSE_DURATION, pulse_len)

for i, f in enumerate(np.linspace(2e6, 32e6, NUM_QUBITS)):
slot=i,
wave1=flat_top_gaussian * np.exp(2j * np.pi * f * time_vec)
)
3. Configure the channel

Configure the channel to run in Readout mode, upload and send 100 readout pulses in which 50 of them to readout qubit in excited state and the rest for qubit in ground state, see Multiplexed Qubit Readout.

# configure inputs and outputs
CHANNEL_INDEX = 0 # physical Channel 1

device.qachannels[CHANNEL_INDEX].configure_channel(
center_frequency=5e9, # in units of Hz
input_range=0, # in units of dBm
output_range=-5, # in units of dBm
)
device.qachannels[CHANNEL_INDEX].input.on(1)
device.qachannels[CHANNEL_INDEX].output.on(1)

# configure Generator
device.qachannels[CHANNEL_INDEX].generator.configure_sequencer_triggering(
aux_trigger="software_trigger0",# chanNtriginM, chanNseqtrigM, chanNrod
play_pulse_delay=0, # 0s delay between startQA trigger and the readout pulse
)
4. Configure the Scope

Configure the Scope to record 2 segments of data which are averaged 50 times. The trigger of the Scope is the Channel 1 Sequencer monitor Trigger which is enabled by the startQA command.

SCOPE_CHANNEL = 0
RECORD_DURATION = 600e-9 # in units of second
NUM_SEGMENTS = 2
NUM_AVERAGES = 50
NUM_MEASUREMENTS = NUM_SEGMENTS * NUM_AVERAGES
SAMPLING_FREQUENCY = 2e9 # in units of Hz

device.scopes[SCOPE_CHANNEL].configure(
input_select={SCOPE_CHANNEL: f"channel{CHANNEL_INDEX}_signal_input"},
num_samples=int(RECORD_DURATION * SAMPLING_FREQUENCY),
trigger_input=f"channel{CHANNEL_INDEX}_sequencer_monitor0", # Sequencer 1 monitor trigger
num_segments=NUM_SEGMENTS,
num_averages=NUM_AVERAGES,
trigger_delay=200e-9, # in units of second, record the data 200 ns later after receiving a trigger
)
5. Run the measurement and calculate the integration weights

The integration weights for different qubits are measured sequentially with the for loop. In each loop, the Generator sends 100 pulses to readout 1 of the qubits prepared in ground and excited state, and the Scope acquires 2 segments of data which are averaged 50 times (see Figure 2, and the integration weights are calculated and plotted from the downloaded data (see Figure 3).

results = []

for i in range(NUM_QUBITS):
qubit_result = {
'weights': None,
'ground_states': None,
'excited_states': None
}
print(f"Measuring qubit {i}.")

seqc_program = f"""
repeat({NUM_MEASUREMENTS}) {{
waitDigTrigger(1);
startQA(QA_GEN_{i}, 0x0, true,  0, 0x0); // only QA_GEN_{i} matters for this measurement
}}
"""

# Start a measurement
device.scopes[SCOPE_CHANNEL].run(single=True)
device.qachannels[CHANNEL_INDEX].generator.enable_sequencer(single=True)
device.start_continuous_sw_trigger(
num_triggers=NUM_MEASUREMENTS, wait_time=RECORD_DURATION
)

# get results to calculate weights and plot data
results.append(qubit_result)