Ramsey¶
This notebook shows you how to perform a Ramsey experiment. You'll sweep the delay between two slightly detuned pi/2 pulses to determine the qubit dephasing time as well as fine tune the calibration of the qubit excited state frequency.
0. LabOne Q Imports¶
You'll begin by importing laboneq.simple
and some extra helper functions to run the examples.
# LabOne Q:
from laboneq.simple import *
from laboneq.analysis.fitting import oscillatory_decay
from laboneq.contrib.example_helpers.plotting.plot_helpers import (
plot_simulation,
plot_results,
)
from laboneq.contrib.example_helpers.generate_example_datastore import (
generate_example_datastore,
get_first_named_entry,
)
from pathlib import Path
import time
import numpy as np
import matplotlib.pyplot as plt
# Build an in-memory data store with device setup and qubit parameters for the
# example notebooks
setup_db = generate_example_datastore(in_memory=True)
1. Device Setup¶
Below, you'll create a device setup and specify to run in an emulated mode or on hardware, emulate = True/False
respectively.
If you run on your hardware, the descriptor called by create_device_setup
should be replaced by one of your own, generally stored as a YAML file. Once you have this descriptor, it can be reused for all your experiments.
# load a calibrated device setup from the dummy database
device_setup = get_first_named_entry(
db=setup_db, name="6_qubit_setup_shfsg_shfqa_hdawg_pqsc_calibrated"
)
emulate = True
# create and connect to a session
session = Session(device_setup=device_setup)
session.connect(do_emulation=emulate)
2. Experiment Parameters¶
Now you'll define the amplitude sweep parameters and pulses to use in your experiment.
# define number of averages
# used for 2^num_averages, maximum: num_averages = 17
num_averages = 4
# pulse parameters and definitions
envelope_duration = 2.0e-6
sigma = 0.2
flat_duration = 1.0e-6
def create_readout_pulse(
qubit, length=envelope_duration, amplitude=0.9, width=flat_duration, sigma=sigma
):
readout_pulse = pulse_library.gaussian_square(
uid=f"readout_pulse_{qubit}",
length=length,
amplitude=amplitude,
width=width,
sigma=sigma,
)
return readout_pulse
def create_ramsey_drive_pulse(qubit, length=1e-6, amplitude=0.9):
return pulse_library.gaussian(
uid=f"gaussian_drive_q{qubit}", length=length, amplitude=amplitude
)
# define delay sweep
n_steps = 201
start_delay = 0e-6
stop_delay = 15e-6
def create_delay_sweep(
start=start_delay, stop=stop_delay, count=n_steps, axis_name="Time [s]"
):
time_sweep = LinearSweepParameter(
uid="time_sweep_param", start=start, stop=stop, count=count, axis_name=axis_name
)
return time_sweep
3. Experiment Definition¶
You'll now create a function which generates an experiment. In this experiment, you'll pass a sweep parameter which changes delay time between two pulses as an argument to the sweep section. Within the sweeep section, you'll create another section containing your drive pulses with play
commands separated by a delay.
As the length of the Ramsey sweep section containing the drive pulses changes, using a right-aligned sweep section and the automatic repetition time makes sure that the experiment is run as efficiently as possible on the Zurich Instruments hardware.
You'll also make a readout section containing play
and acquire
commands to perform readout.
Within the real-time acquisition section, you'll set use INTEGRATION
as your acquisition type and set the repetition mode to AUTO
.
# function that returns an amplitude Rabi experiment
def ramsey(drive_pulse, readout_pulse, time_sweep):
exp_ramsey = Experiment(
uid="Ramsey Experiment",
signals=[
ExperimentSignal("drive"),
ExperimentSignal("measure"),
ExperimentSignal("acquire"),
],
)
## define Rabi experiment pulse sequence
# outer loop - real-time, cyclic averaging
with exp_ramsey.acquire_loop_rt(
uid="ramsey_shots",
count=2**num_averages,
averaging_mode=AveragingMode.CYCLIC,
acquisition_type=AcquisitionType.INTEGRATION,
repetition_mode=RepetitionMode.AUTO,
):
# inner loop - real time sweep of Ramsey time delays
with exp_ramsey.sweep(
uid="ramsey_sweep", parameter=time_sweep, alignment=SectionAlignment.RIGHT
):
# play qubit excitation pulse - pulse amplitude is swept
with exp_ramsey.section(
uid="qubit_excitation", alignment=SectionAlignment.RIGHT
):
exp_ramsey.play(signal="drive", pulse=drive_pulse)
exp_ramsey.delay(signal="drive", time=time_sweep)
exp_ramsey.play(signal="drive", pulse=drive_pulse)
# readout pulse and data acquisition
with exp_ramsey.section(
uid="readout_section", play_after="qubit_excitation"
):
# play readout pulse on measure line
exp_ramsey.play(signal="measure", pulse=readout_pulse)
# trigger signal data acquisition
exp_ramsey.acquire(
signal="acquire",
handle="ramsey",
kernel=readout_pulse,
)
with exp_ramsey.section(uid="delay", length=1e-6):
# relax time after readout - for qubit relaxation to groundstate and signal processing
exp_ramsey.reserve(signal="measure")
return exp_ramsey
3.1 Create Experiment and Signal Map¶
Before running the experiment, you'll define and set the mapping between the experimental and logical lines.
# define pulses and create experiment
readout_pulse = create_readout_pulse("q0")
drive_pulse = create_ramsey_drive_pulse("q0")
time_sweep = create_delay_sweep(start=0, stop=15e-6, count=51)
ramsey_exp = ramsey(
drive_pulse=drive_pulse, readout_pulse=readout_pulse, time_sweep=time_sweep
)
# signal map for qubit 0
def signal_map_default(qubit):
signal_map = {
"drive": device_setup.logical_signal_groups[f"{qubit}"].logical_signals[
"drive_line"
],
"measure": device_setup.logical_signal_groups[f"{qubit}"].logical_signals[
"measure_line"
],
"acquire": device_setup.logical_signal_groups[f"{qubit}"].logical_signals[
"acquire_line"
],
}
return signal_map
# run the experiment on qubit 0
ramsey_exp.set_signal_map(signal_map_default("q0"))
3.2 Compile, Generate Pulse Sheet, and Plot Simulated Signals¶
Now, you'll compile the experiment and generate a pulse sheet.
# compile the experiment on the open instrument session
compiled_ramsey = session.compile(ramsey_exp)
Path("Pulse_Sheets").mkdir(parents=True, exist_ok=True)
# generate a pulse sheet to inspect experiment before runtime
show_pulse_sheet("Pulse_Sheets/Ramsey", compiled_ramsey)
In addition to creating a pulse sheet to inspect the timing of pulses, you can simulate physical output of the channels.
plot_simulation(compiled_ramsey, start_time=0, length=200e-6, plot_width=10)
3.3 Run, Save, and Plot Results¶
Finally, you'll run the experiment, save, and plot the results.
# run the compiled experiemnt
ramsey_results = session.run()
timestamp = time.strftime("%Y%m%dT%H%M%S")
Path("Results").mkdir(parents=True, exist_ok=True)
session.save_results(f"Results/{timestamp}_ramsey_results.json")
print(f"File saved as Results/{timestamp}_ramsey_results.json")
# plot_result_2d(ramsey_results, list(ramsey_results.acquired_results.keys())[0])
plot_results(ramsey_results)
4. Fitting Results¶
You can also fit your results. The below script fits some emulated Rabi data when running in emulation mode.
# get measurement data returned by the instruments
ramsey_res = ramsey_results.get_data("ramsey")
# define time axis from qubit parameters
ramsey_delay = ramsey_results.get_axis("ramsey")[0]
if emulate:
# create dummy data if running in emulation mode
ramsey_res = oscillatory_decay(
ramsey_delay, 1e6, 0, 1 / 10e-6, amplitude=0.5, offset=0.5
) + 0.12 * np.random.rand(len(ramsey_delay))
# plot measurement results
fig = plt.figure()
plt.plot(ramsey_delay, ramsey_res, ".k")
plt.ylabel("A (a.u.)")
plt.xlabel("delay (s)")
# increase number of plot points for smooth plotting of fit results
delay_plot = np.linspace(ramsey_delay[0], ramsey_delay[-1], 5 * len(ramsey_delay))
## fit measurement data to decaying sinusoidal oscillatio
popt, pcov = oscillatory_decay.fit(
ramsey_delay,
ramsey_res,
1e6,
0,
2 / 1 / 10e-6,
0.5,
0.5,
plot=False,
bounds=[
[0.01e6, -np.pi / 2, 0.1 / 1 / 10e-6, 0.2, 0.2],
[15e6, np.pi / 2, 10 / 1 / 10e-6, 2, 2],
],
)
print(f"Fitted parameters: {popt}")
# plot fit results together with experimental data
plt.plot(delay_plot, oscillatory_decay(delay_plot, *popt), "-r");