Output Simulator¶
LabOne Q can simulate the output of each channel in a sample-precise way. This feature can be used to check experiments even before they are executed on hardware. Combine it with the pulse sheet viewer to get a multi-scale overview over pulses and sequences.
This notebook will use an amplitude Rabi experiment to demonstrate some use cases of the output simulator.
For more examples also have a look at the demo video on our Youtube channel here
# LabOne Q:
from laboneq.simple import *
# Helpers:
from laboneq.contrib.example_helpers.plotting.plot_helpers import plot_simulation
from laboneq.contrib.example_helpers.example_notebook_helper import create_device_setup
import numpy as np
import matplotlib.pyplot as plt
# create device setup
generation = 2
device_setup = create_device_setup(generation=generation)
use_emulation = True
# qubit drive pulse
x90 = pulse_library.gaussian(uid="x90", length=100e-9, amplitude=1.0)
# flux
flux = pulse_library.const(uid="flux", length=150e-9, amplitude=1.0)
# readout drive pulse
readout_pulse = pulse_library.const(uid="readout_pulse", length=400e-9, amplitude=1.0)
# readout weights for integration
readout_weighting_function = pulse_library.const(
uid="readout_weighting_function", length=400e-9, amplitude=1.0
)
Define Amplitude Sweep and Experiment¶
amp_sweep = LinearSweepParameter(uid="amp", start=1.0, stop=0.1, count=11)
# define number of averages
average_exponent = 2 # used for 2^n averages, n=average_exponent, maximum: n = 17
# Create Experiment - no explicit mapping to qubit lines
exp = Experiment(
uid="Amplitude Rabi",
signals=[
ExperimentSignal("q0_drive"),
ExperimentSignal("q0_flux"),
ExperimentSignal("q0_measure"),
ExperimentSignal("q0_acquire"),
],
)
shot_repetition_time = 1400e-9
## experimental pulse sequence
with exp.acquire_loop_rt(
uid="shots",
count=pow(2, average_exponent),
acquisition_type=AcquisitionType.INTEGRATION,
repetition_mode=RepetitionMode.CONSTANT,
repetition_time=shot_repetition_time,
):
with exp.sweep(parameter=amp_sweep):
# qubit excitation pulse
with exp.section(uid="qubit_excitation", alignment=SectionAlignment.RIGHT):
exp.play(
signal="q0_drive", pulse=x90, amplitude=amp_sweep, phase=2 * np.pi / 3
)
exp.play(signal="q0_flux", pulse=flux)
# readout and data acquisition
with exp.section(uid="qubit_readout_and_relax", play_after="qubit_excitation"):
# play readout pulse
exp.play(signal="q0_measure", pulse=readout_pulse)
# signal data acquisition
exp.acquire(
signal="q0_acquire",
handle="ac_0",
kernel=readout_weighting_function,
)
qb = device_setup.logical_signal_groups["q0"].logical_signals
# define signal map
map_q0 = {
"q0_drive": qb["drive_line"],
"q0_flux": qb["flux_line"],
"q0_measure": qb["measure_line"],
"q0_acquire": qb["acquire_line"],
}
# set signal map
exp.set_signal_map(map_q0)
# create a session
session = Session(device_setup)
# connect to session
session.connect(do_emulation=use_emulation)
# Compile experiment
compiled_experiment = session.compile(exp)
Check the Pulse Sheet¶
interactive = False
# Uncomment to show Pulse Sheet in interactive mode with integrated output simulator.
# Click on a pulse to see its actual shape in the 'Signals' tab.
# interactive = True
show_pulse_sheet("Amplitude Rabi", compiled_experiment, interactive=interactive)
Output Simulation¶
You can easily plot the signals with the aid of one of our helper functions.
For more customized plotting, follow four simple steps to retrieve and plot the simulated output signals:
- Initialize the output simulator with the
CompiledExperiment
object - Retrieve the physical channels that you are interested in.
- Query snippets from the signals played on these channels
- Plot everything
Initialize the OutputSimulator
and plot everything with a helper function¶
# Simulate experiment
# Plot simulated output signals with helper function
plot_simulation(
compiled_experiment,
start_time=0,
length=5e-6,
plot_width=10,
plot_height=3,
)
Retrieve the physical channels of interest¶
# Get physical channel references via the logical signals
drive_iq_port = qb["drive_line"].physical_channel
flux_rf_port = qb["flux_line"].physical_channel
measure_iq_port = qb["measure_line"].physical_channel
acquire_port = qb["acquire_line"].physical_channel
Query snippets from the signals played on the respective channels¶
# Get waveform snippets from the simulation
simulation = OutputSimulator(compiled_experiment)
drive_snippet = simulation.get_snippet(drive_iq_port, start=0, output_length=250e-9)
flux_snippet = simulation.get_snippet(flux_rf_port, start=0e-9, output_length=250e-9)
measure_snippet = simulation.get_snippet(
measure_iq_port, start=200e-9, output_length=400e-9
)
acquire_snippet = simulation.get_snippet(
acquire_port, start=200e-9, output_length=400e-9
)
Plot everything¶
For each snippet, you can retrieve the time axis, together with real and imaginary part of the wave.
fig = plt.figure(figsize=(15, 5))
plt.plot(drive_snippet.time, drive_snippet.wave.real, label="drive I")
plt.plot(drive_snippet.time, drive_snippet.wave.imag, label="drive Q")
plt.plot(flux_snippet.time, flux_snippet.wave.real, label="flux")
plt.plot(measure_snippet.time, measure_snippet.wave.real, label="measure I")
plt.plot(measure_snippet.time, measure_snippet.wave.imag, label="measure Q")
plt.plot(acquire_snippet.time, acquire_snippet.wave.real, label="acquire start")
plt.legend()
Advanced Plotting: Overlay Shots¶
You can easily visualize parameter sweeps by overlaying subsequent shots. As a prerequisite, you need to know the shot repetition rate. An easy way is to fix it by setting the repetition rate of the acquire loop to constant, as it is done in the experiment definition above.
for step in range(amp_sweep.count):
start = shot_repetition_time * step # + 100e-9
drive_snippet = simulation.get_snippet(
drive_iq_port, start=start, output_length=200e-9
)
measure_snippet = simulation.get_snippet(
measure_iq_port, start=start, output_length=150e-9
)
plt.plot(drive_snippet.time - start, drive_snippet.wave.real, color="tab:blue")
plt.plot(
measure_snippet.time - start, measure_snippet.wave.real, color="tab:orange"
)