In [ ]:
import numpy as np
import matplotlib.pyplot as plt

import qkit
qkit.start()

from qkit.measure import samples_class as sample # Sample class
from qkit.measure.timedomain import sequence_library as sl # Sequence library for standard experiments
from qkit.measure.timedomain import pulse_sequence as ps # Pulse sequence class to build sequences of your own
from qkit.measure.timedomain import VirtualAWG as VirtAWG # virtual awg for managing your sequence objects

Initializing test sample


In [ ]:
testsample = sample.Sample()
testsample.readout_tone_length = 200e-9 # length of the readout tone
testsample.clock = 1e9 # sample rate of your physical awg/pulse generator
testsample.tpi = 100e-9 # duration of a pi-pulse
testsample.tpi2 = 50e-9 # duration of a pi/2-pulse
testsample.iq_frequency = 20e6 # iq_frequency for iq mixing (set to 0 for homodyne measurements)

#testsample.awg = my_awg #<- qkit instrument (your actual awg)

Building sequences

Sequences are python objects, encoding the experiment you want to run on your pulse generator. They are built from pulses (another type of object), wait times, and the readout.

The pulse object

Pulse objects are initialized with:

mypulse = ps.Pulse(length, pulse-shape, name, amplitude, phase, iq_frequency, iq_dc_offset, iq_angle)
length is the pulse length in seconds
pulse-shape is the shape of the pulse. Currently rect (square pulse, this is the default) and gaussian shapes are implemented. To use gaussian shape, write shape = ps.ShapeLib.gauss
name is the name you want to give your pulse. This is not mandatory and only used for plotting.
amplitude: relative amplitude of your pulse.
phase: relative phase of your pulse in degree.
iq_frequency: If iq_frequency is 0, homodyne mixing is used.
iq_dc_offset, iq_angle are currently not in use, but will be used in the near future to enable a calibration of the mixers.


In [ ]:
#example:
pi = ps.Pulse(50e-9, name = "pi-pulse", shape = ps.ShapeLib.gauss, iq_frequency=50e6)
#this creates a 50ns gaussian pulse with name "pi-pulse" at an iq_frequency of 50MHz.
Building sequences from pulses:

Sequences are built from pulse objects in an intuitive way: You just start adding pulses to your sequence, which are then appended. In the example below, a simple T1 measurement sequence is built, using our recently defined pi-pulse.


In [ ]:
my_sequence = ps.PulseSequence(testsample) # create sequence object
my_sequence.add(pi) # add pi pulse, as defined in the example above
my_sequence.add_wait(lambda t: t) # add a variable wait time with length t
my_sequence.add_readout() # add the readout
my_sequence.plot() # show SCHEMATIC plot of the pulse sequence

As you can see, wait times can be added with the add_wait command. In this case the wait time is given by a lambda function. This enable the implementation of variable time steps. This can also be used to add pulses with variable lengths.

Of course, there are also pre-built sequences for standard experiments, which you can find in the sequence_library class (here imported as sl).
Currently, this includes sequences for Rabi, T1, Ramsey, spin-echo and spin-locking experiments.

In [ ]:
spinecho = sl.spinecho(testsample, n_pi = 2) # spinecho with 2 pi-pulses
spinecho.plot()

Single channel virtual AWG


In [ ]:
vawg = VirtAWG.VirtualAWG(testsample) # by default, the virtual awg is initialized with a single channel
time = np.arange(0, 500e-9, 50e-9) # time t for the sequence
vawg.set_sequence(my_sequence, time) # set_sequence deletes all previously stored sequences in a channel
vawg.add_sequence(spinecho, time * 2) # add_sequence appends the next sequence to the sequences stored in the channel
# Note, this enables you to run multiple experiments, such as T1-measurement and spin-echo in parallel!#
vawg.plot()

# In the plot, the time starts at 0 together with the readout. 
# The position of the readout is also used as a phase reference for all pulses.
Please note, that this plot always displays the amplitude of your signal (not I or Q).
We refrained from displaying the pulses with their iq_frequency to prevent confusion.
Similarly, it is not necessary to initialize the virtual awg with a channel for each I and Q. Whether two physical channels are needed to generate the desired output is determined automatically by the load script (see below).

In [ ]:
# If you do not want the experiments to run consecutively, but to interleave them instead:
vawg.set_interleave(True)
# This also works for more than 2 sequences.
vawg.plot()

If you are satisfied with the results, load the sequences onto your physical device with:
vawg.load()
Currently, this is only enabled for the tabor awg.

Multi-channel virtual AWG

This feature enables the user to run multiple sequences on different channels at the same time.
The readout of each sequence is used to synchronize the channels (i.e. it is expected that the readout happens simultaneously).


In [ ]:
vawg = VirtAWG.VirtualAWG(testsample, channels = 2) #Initialize with two channels channel (number is arbitrary)
vawg.set_sequence(my_sequence, time, channel = 1) # set my_sequence (T1 measurement) on channel 1
vawg.set_sequence(spinecho, time, channel = 2) # set spinecho on channel 2
vawg.plot()