Pulsed Acquisition with HDAWG and UHFLI¶
This notebook shows you how to perform a pulsed acquisition experiment using a HDAWG and an UHFLI. On the HDAWG, a Ramsey sequence is played. In each iteration a trigger from the HDAWG is sent to the UHFLI DAQ module, to trigger the data acquisition. A second trigger is sent to the UHFLI demodulator, to gate the data transfer and enable fast measurements. In each iteration (pulse/shot) of the experiment, a time trace is acquired with the UHFLI. To imitate the readout signal, a Gaussian pulse is played and acquired with the UHFLI. You might use this notebook if you are interested in acquiring short time traces with the UHFLI, e.g. for RF reflectometry.
Connections:
- HDAWG SigOut 4 to UHFLI Input 1
- HDAWG Mark 4 to UHFLI TrigIn 1 (frontpanel)
- HDAWG Mark 5 to UHFLI TrigIn 3 (backpanel)
- synchronize RefClk of both instruments
0. General Imports¶
In [ ]:
Copied!
%config IPCompleter.greedy=True
import matplotlib.pyplot as plt
import numpy as np
import time
from laboneq.simple import *
# additional import for the purpose of demonstration
import logging
mylogger = logging.getLogger("user_func")
%config IPCompleter.greedy=True
import matplotlib.pyplot as plt
import numpy as np
import time
from laboneq.simple import *
# additional import for the purpose of demonstration
import logging
mylogger = logging.getLogger("user_func")
1. Device Setup¶
1.1 Calibration¶
In [ ]:
Copied!
def calibrate_devices(device_setup):
device_setup.logical_signal_groups["q0"].logical_signals[
"drive_line"
].calibration = SignalCalibration(
oscillator=Oscillator(
uid="drive_q0_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.0, 0.0],
correction_matrix=[
[1.0, 0.0],
[0.0, 1.0],
],
),
)
def calibrate_devices(device_setup):
device_setup.logical_signal_groups["q0"].logical_signals[
"drive_line"
].calibration = SignalCalibration(
oscillator=Oscillator(
uid="drive_q0_osc", frequency=1e8, modulation_type=ModulationType.HARDWARE
),
mixer_calibration=MixerCalibration(
voltage_offsets=[0.0, 0.0],
correction_matrix=[
[1.0, 0.0],
[0.0, 1.0],
],
),
)
1.2 Create device setup¶
In [ ]:
Copied!
descriptor = f"""\
instruments:
HDAWG:
- address: DEV8434
uid: device_hdawg
interface: usb
UHFLI:
- address: DEV2283
uid: device_uhfli
# interface: usb
connections:
device_hdawg:
- iq_signal: q0/drive_line
ports: [SIGOUTS/4, SIGOUTS/5]
- rf_signal: q0/coulomb_line_1
ports: [SIGOUTS/0]
- rf_signal: q0/coulomb_line_2
ports: [SIGOUTS/1]
"""
device_setup = DeviceSetup.from_descriptor(
descriptor,
server_host="your_ip_address",
server_port=8004,
setup_name="MySetup",
)
calibrate_devices(device_setup)
descriptor = f"""\
instruments:
HDAWG:
- address: DEV8434
uid: device_hdawg
interface: usb
UHFLI:
- address: DEV2283
uid: device_uhfli
# interface: usb
connections:
device_hdawg:
- iq_signal: q0/drive_line
ports: [SIGOUTS/4, SIGOUTS/5]
- rf_signal: q0/coulomb_line_1
ports: [SIGOUTS/0]
- rf_signal: q0/coulomb_line_2
ports: [SIGOUTS/1]
"""
device_setup = DeviceSetup.from_descriptor(
descriptor,
server_host="your_ip_address",
server_port=8004,
setup_name="MySetup",
)
calibrate_devices(device_setup)
2. UHFLI example¶
2.1 Connect to instrument in zhinst-toolkit session¶
In [ ]:
Copied!
# create and connect to session
session = Session(device_setup=device_setup)
session.connect(do_emulation=True)
# create and connect to session
session = Session(device_setup=device_setup)
session.connect(do_emulation=True)
In [ ]:
Copied!
# shortcut for the used MFLI in the setup
uhfli = session.devices["device_uhfli"]
# shortcut for the used MFLI in the setup
uhfli = session.devices["device_uhfli"]
2.2 Experiment (Ramsey with marker sent to UHFLI)¶
In [ ]:
Copied!
## constant definition
LEN_COULOMB_CYCLE = 4e-6
TAU_X90_TIME = 100e-9 # [s]
LEN_READOUT = 5e-6
# define three stages of gate pulses
coulomb_pulse = pulse_library.const(
uid="coulomb_manipulate", length=LEN_COULOMB_CYCLE / 2, amplitude=0.5
)
coulomb_readout = pulse_library.const(
uid="coulomb_readout", length=LEN_READOUT, amplitude=1
)
# define drive pulse
drive_pulse = pulse_library.const(uid="pihalf", length=TAU_X90_TIME, amplitude=1)
START = 0
STOP = 50e-9
STEPS = 4
NUM_REP = 5
sweep_delay = LinearSweepParameter(
uid="Ramsey_delay", start=START, stop=STOP, count=STEPS
)
imitate_readout = LinearSweepParameter(
uid="readout_imitation", start=0.2, stop=1, count=STEPS
)
readout_parameter = LinearSweepParameter(uid="readoutparam", start=0, stop=1, count=1)
## Create Experiment
exp = Experiment(
"UHFLI experiment",
signals=[
ExperimentSignal("drive"),
ExperimentSignal("coulomb_1"),
ExperimentSignal("coulomb_2"),
],
)
# define experiment
with exp.sweep(uid="readout_loop", parameter=readout_parameter):
# use user function to arm the acquisition in the DAQ module
exp.call("arm_UHFLI")
with exp.acquire_loop_rt(
uid="shots", count=NUM_REP, averaging_mode=AveragingMode.CYCLIC
):
with exp.sweep(uid="sweep", parameter=[sweep_delay, imitate_readout]):
with exp.section(
uid=("empty"),
length=LEN_COULOMB_CYCLE / 2,
alignment=SectionAlignment.RIGHT,
):
exp.play(signal="coulomb_1", pulse=coulomb_pulse, amplitude=0.5)
exp.play(signal="coulomb_2", pulse=coulomb_pulse, amplitude=0.5)
with exp.section(
uid=("manipulation"),
length=LEN_COULOMB_CYCLE / 2,
alignment=SectionAlignment.RIGHT,
):
exp.play(signal="coulomb_1", pulse=coulomb_pulse, amplitude=0.75)
exp.play(signal="coulomb_2", pulse=coulomb_pulse, amplitude=0.75)
exp.play(signal="drive", pulse=drive_pulse)
exp.delay(signal="drive", time=sweep_delay)
exp.play(signal="drive", pulse=drive_pulse)
with exp.section(
uid="outer_trigger",
length=LEN_READOUT,
trigger={"drive": {"state": 2}},
alignment=SectionAlignment.RIGHT,
):
with exp.section(
uid="inner_trigger",
length=LEN_READOUT - 500e-9,
trigger={"drive": {"state": 3}},
):
exp.delay(signal="drive", time=LEN_READOUT / 4)
exp.play(
signal="drive",
pulse=pulse_library.gaussian(
uid="dummy_readout", length=LEN_READOUT / 3
),
amplitude=imitate_readout,
)
with exp.section(uid="qubit_readout", length=LEN_READOUT):
exp.play(signal="coulomb_1", pulse=coulomb_readout, amplitude=0.3)
exp.play(signal="coulomb_2", pulse=coulomb_readout, amplitude=0.3)
# use user function to read out the results from the DAQ module
exp.call("read_UHFLI")
## constant definition
LEN_COULOMB_CYCLE = 4e-6
TAU_X90_TIME = 100e-9 # [s]
LEN_READOUT = 5e-6
# define three stages of gate pulses
coulomb_pulse = pulse_library.const(
uid="coulomb_manipulate", length=LEN_COULOMB_CYCLE / 2, amplitude=0.5
)
coulomb_readout = pulse_library.const(
uid="coulomb_readout", length=LEN_READOUT, amplitude=1
)
# define drive pulse
drive_pulse = pulse_library.const(uid="pihalf", length=TAU_X90_TIME, amplitude=1)
START = 0
STOP = 50e-9
STEPS = 4
NUM_REP = 5
sweep_delay = LinearSweepParameter(
uid="Ramsey_delay", start=START, stop=STOP, count=STEPS
)
imitate_readout = LinearSweepParameter(
uid="readout_imitation", start=0.2, stop=1, count=STEPS
)
readout_parameter = LinearSweepParameter(uid="readoutparam", start=0, stop=1, count=1)
## Create Experiment
exp = Experiment(
"UHFLI experiment",
signals=[
ExperimentSignal("drive"),
ExperimentSignal("coulomb_1"),
ExperimentSignal("coulomb_2"),
],
)
# define experiment
with exp.sweep(uid="readout_loop", parameter=readout_parameter):
# use user function to arm the acquisition in the DAQ module
exp.call("arm_UHFLI")
with exp.acquire_loop_rt(
uid="shots", count=NUM_REP, averaging_mode=AveragingMode.CYCLIC
):
with exp.sweep(uid="sweep", parameter=[sweep_delay, imitate_readout]):
with exp.section(
uid=("empty"),
length=LEN_COULOMB_CYCLE / 2,
alignment=SectionAlignment.RIGHT,
):
exp.play(signal="coulomb_1", pulse=coulomb_pulse, amplitude=0.5)
exp.play(signal="coulomb_2", pulse=coulomb_pulse, amplitude=0.5)
with exp.section(
uid=("manipulation"),
length=LEN_COULOMB_CYCLE / 2,
alignment=SectionAlignment.RIGHT,
):
exp.play(signal="coulomb_1", pulse=coulomb_pulse, amplitude=0.75)
exp.play(signal="coulomb_2", pulse=coulomb_pulse, amplitude=0.75)
exp.play(signal="drive", pulse=drive_pulse)
exp.delay(signal="drive", time=sweep_delay)
exp.play(signal="drive", pulse=drive_pulse)
with exp.section(
uid="outer_trigger",
length=LEN_READOUT,
trigger={"drive": {"state": 2}},
alignment=SectionAlignment.RIGHT,
):
with exp.section(
uid="inner_trigger",
length=LEN_READOUT - 500e-9,
trigger={"drive": {"state": 3}},
):
exp.delay(signal="drive", time=LEN_READOUT / 4)
exp.play(
signal="drive",
pulse=pulse_library.gaussian(
uid="dummy_readout", length=LEN_READOUT / 3
),
amplitude=imitate_readout,
)
with exp.section(uid="qubit_readout", length=LEN_READOUT):
exp.play(signal="coulomb_1", pulse=coulomb_readout, amplitude=0.3)
exp.play(signal="coulomb_2", pulse=coulomb_readout, amplitude=0.3)
# use user function to read out the results from the DAQ module
exp.call("read_UHFLI")
2.3 Configure UHFLI and DAQ module¶
In [ ]:
Copied!
# configure UHFLI
demod = uhfli.demods[3] # which demodulator to use (depends on UHF-MF option)
with uhfli.set_transaction():
uhfli.demods["*"].enable(False)
uhfli.oscs[0].freq(100e6)
demod.order(1)
demod.rate(14e6)
demod.trigger("trigin2_high")
demod.timeconstant(10e-9)
demod.enable(True)
# Parameters
DEMOD_RATE_UHFLI = demod.rate() # read the value from the instrument
NUM_COLS = int(
np.ceil(DEMOD_RATE_UHFLI * LEN_READOUT)
) # Number of samples per burst. Corresponds to length of time trace in units of sampling rate.
# Nodes to read
sample_nodes = [demod.sample.r.avg]
# Module creation
daq_module = uhfli._session.modules.daq
daq_module.device(uhfli)
daq_module.type("hardware_trigger")
for node in sample_nodes:
daq_module.subscribe(node)
# Shape of my grid
daq_module.grid.mode(
4
) # Specify how the acquired data is sampled onto the matrix’s horizontal axis
daq_module.count(1)
daq_module.grid.cols(NUM_COLS)
daq_module.grid.rows(STEPS)
daq_module.grid.repetitions(NUM_REP)
daq_module.grid.rowrepetition(
False
) # True: First average each row, then fill the next row -> sequential averaging
# False: First fill each row, then average the rows -> cyclic averaging
# Acquisition using Digital Triggering
node_trigger = demod.sample.TrigIn1
daq_module.triggernode(node_trigger)
daq_module.edge("rising")
daq_module.bandwidth(0)
daq_module.level(0.5)
daq_module.delay(500e-9)
daq_module.holdoff.time(0)
daq_module.holdoff.count(0)
daq_module.clearhistory(1)
# print(f"Columns: {daq_module.grid.cols()}")
# print(f"Rows: {daq_module.grid.rows()}")
# print(f"Repetitions: {daq_module.grid.repetitions()}")
# print(f"Holdoff: {daq_module.holdoff.time()}")
# print(f"Delay: {daq_module.delay()}")
# configure UHFLI
demod = uhfli.demods[3] # which demodulator to use (depends on UHF-MF option)
with uhfli.set_transaction():
uhfli.demods["*"].enable(False)
uhfli.oscs[0].freq(100e6)
demod.order(1)
demod.rate(14e6)
demod.trigger("trigin2_high")
demod.timeconstant(10e-9)
demod.enable(True)
# Parameters
DEMOD_RATE_UHFLI = demod.rate() # read the value from the instrument
NUM_COLS = int(
np.ceil(DEMOD_RATE_UHFLI * LEN_READOUT)
) # Number of samples per burst. Corresponds to length of time trace in units of sampling rate.
# Nodes to read
sample_nodes = [demod.sample.r.avg]
# Module creation
daq_module = uhfli._session.modules.daq
daq_module.device(uhfli)
daq_module.type("hardware_trigger")
for node in sample_nodes:
daq_module.subscribe(node)
# Shape of my grid
daq_module.grid.mode(
4
) # Specify how the acquired data is sampled onto the matrix’s horizontal axis
daq_module.count(1)
daq_module.grid.cols(NUM_COLS)
daq_module.grid.rows(STEPS)
daq_module.grid.repetitions(NUM_REP)
daq_module.grid.rowrepetition(
False
) # True: First average each row, then fill the next row -> sequential averaging
# False: First fill each row, then average the rows -> cyclic averaging
# Acquisition using Digital Triggering
node_trigger = demod.sample.TrigIn1
daq_module.triggernode(node_trigger)
daq_module.edge("rising")
daq_module.bandwidth(0)
daq_module.level(0.5)
daq_module.delay(500e-9)
daq_module.holdoff.time(0)
daq_module.holdoff.count(0)
daq_module.clearhistory(1)
# print(f"Columns: {daq_module.grid.cols()}")
# print(f"Rows: {daq_module.grid.rows()}")
# print(f"Repetitions: {daq_module.grid.repetitions()}")
# print(f"Holdoff: {daq_module.holdoff.time()}")
# print(f"Delay: {daq_module.delay()}")
2.4 Define user functions for arming UHFLI and reading results¶
In [ ]:
Copied!
def armUHFLI(session):
# device_uhfli.armDAQmodule()
daq_module.execute()
def readUHFLI(session):
# device_uhfli.readout()
clockbase = uhfli.clockbase()
timeout = 1 # s
# Retrieve data from UHFLI DAQ module
start_time = time.time()
while time.time() - start_time < timeout:
time.sleep(LEN_READOUT)
if daq_module.raw_module.finished() == True:
progress = daq_module.raw_module.finished()
mylogger.info(f"Progress of data acquisition: {100 * progress:.2f}%.")
break
progress = daq_module.raw_module.finished()
# Get data
daq_data = daq_module.read(raw=False, clk_rate=clockbase)
return daq_data
def armUHFLI(session):
# device_uhfli.armDAQmodule()
daq_module.execute()
def readUHFLI(session):
# device_uhfli.readout()
clockbase = uhfli.clockbase()
timeout = 1 # s
# Retrieve data from UHFLI DAQ module
start_time = time.time()
while time.time() - start_time < timeout:
time.sleep(LEN_READOUT)
if daq_module.raw_module.finished() == True:
progress = daq_module.raw_module.finished()
mylogger.info(f"Progress of data acquisition: {100 * progress:.2f}%.")
break
progress = daq_module.raw_module.finished()
# Get data
daq_data = daq_module.read(raw=False, clk_rate=clockbase)
return daq_data
2.5 Signal mapping¶
In [ ]:
Copied!
# define signal maps for different qubits
map_q0 = {
"drive": "/logical_signal_groups/q0/drive_line",
"coulomb_1": "/logical_signal_groups/q0/coulomb_line_1",
"coulomb_2": "/logical_signal_groups/q0/coulomb_line_2",
}
# calibration for qubit 0
calib_q0 = Calibration()
calib_q0["drive"] = SignalCalibration(
oscillator=Oscillator(
frequency=100e6,
modulation_type=ModulationType.HARDWARE,
)
)
# define signal maps for different qubits
map_q0 = {
"drive": "/logical_signal_groups/q0/drive_line",
"coulomb_1": "/logical_signal_groups/q0/coulomb_line_1",
"coulomb_2": "/logical_signal_groups/q0/coulomb_line_2",
}
# calibration for qubit 0
calib_q0 = Calibration()
calib_q0["drive"] = SignalCalibration(
oscillator=Oscillator(
frequency=100e6,
modulation_type=ModulationType.HARDWARE,
)
)
2.6 Register user functions¶
In [ ]:
Copied!
# set experiment calibration and signal map
exp.set_calibration(calib_q0)
exp.set_signal_map(map_q0)
# register user functions
session.register_user_function(armUHFLI, "arm_UHFLI")
session.register_user_function(readUHFLI, "read_UHFLI")
# set experiment calibration and signal map
exp.set_calibration(calib_q0)
exp.set_signal_map(map_q0)
# register user functions
session.register_user_function(armUHFLI, "arm_UHFLI")
session.register_user_function(readUHFLI, "read_UHFLI")
In [ ]:
Copied!
if not session.connection_state.emulated:
instrument_serial = device_setup.instrument_by_uid("device_hdawg").address
device = session.devices[instrument_serial]
device.triggers.out[2].delay(23.9e-9)
device.triggers.out[3].delay(23.9e-9)
if not session.connection_state.emulated:
instrument_serial = device_setup.instrument_by_uid("device_hdawg").address
device = session.devices[instrument_serial]
device.triggers.out[2].delay(23.9e-9)
device.triggers.out[3].delay(23.9e-9)
2.7 Run experiment¶
In [ ]:
Copied!
session.run(exp)
session.run(exp)
3. Plot results¶
In [ ]:
Copied!
results = []
ts0 = np.nan
plt.figure()
plt.xlabel("Time [s]")
plt.ylabel(str(node))
clockbase = uhfli.clockbase()
for node in sample_nodes:
for sig_burst in session.results.user_func_results["read_UHFLI"][0][node]:
results.append(sig_burst.value) # Results
if np.any(np.isnan(ts0)):
ts0 = sig_burst.header["createdtimestamp"][0] / clockbase
# Convert from device ticks to time in seconds.
t0_burst = sig_burst.header["createdtimestamp"][0] / clockbase
t = (sig_burst.time + t0_burst) - ts0
for ii, value in enumerate(results[0]):
plt.plot(t, value, label="readout step " + str(ii + 1))
plt.legend(loc="upper right", fontsize=8)
plt.title("CYCLIC averaging")
# plt.xlim([0,10e-6])
# if SEQUENTIAL: plt.title("Sequential averaging")
# if not SEQUENTIAL: plt.title("Cyclic averaging")
results = []
ts0 = np.nan
plt.figure()
plt.xlabel("Time [s]")
plt.ylabel(str(node))
clockbase = uhfli.clockbase()
for node in sample_nodes:
for sig_burst in session.results.user_func_results["read_UHFLI"][0][node]:
results.append(sig_burst.value) # Results
if np.any(np.isnan(ts0)):
ts0 = sig_burst.header["createdtimestamp"][0] / clockbase
# Convert from device ticks to time in seconds.
t0_burst = sig_burst.header["createdtimestamp"][0] / clockbase
t = (sig_burst.time + t0_burst) - ts0
for ii, value in enumerate(results[0]):
plt.plot(t, value, label="readout step " + str(ii + 1))
plt.legend(loc="upper right", fontsize=8)
plt.title("CYCLIC averaging")
# plt.xlim([0,10e-6])
# if SEQUENTIAL: plt.title("Sequential averaging")
# if not SEQUENTIAL: plt.title("Cyclic averaging")
In [ ]:
Copied!
show_pulse_sheet("UHFLI integration", session.compiled_experiment)
show_pulse_sheet("UHFLI integration", session.compiled_experiment)