Example Q8: Realistic Two Qubit Tuneup and Experiments
This example notebook shows how to use APS2/X6 ecosystem to tune up a pair of qubits.
© Raytheon BBN Technologies 2019
[ ]:
import os
os.environ['AWG_DIR'] = "./AWG"
import matplotlib
import matplotlib.pyplot as plt
import QGL.config
import auspex.config
auspex.config.AWGDir = "/home/qlab/BBN/AWG"
QGL.config.AWGDir = "/home/qlab/BBN/AWG"
auspex.config.KernelDir = "/home/qlab/BBN/Kernels"
QGL.config.KernelDir = "/home/qlab/BBN/Kernels"
%matplotlib inline
from auspex.analysis.qubit_fits import *
from auspex.analysis.helpers import *
from QGL import *
from auspex.qubit import *
#import seaborn as sns
#sns.set_style('whitegrid')
Channel Library Setup
[ ]:
# this will all be system dependent
cl = ChannelLibrary(":memory:")
q1 = cl.new_qubit("q1")
q2 = cl.new_qubit("q2")
ip_addresses = [f"192.168.4.{i}" for i in [21, 22, 23, 24, 25, 26, 28, 29]]
aps2 = cl.new_APS2_rack("Maxwell", ip_addresses, tdm_ip="192.168.4.11")
cl.set_master(aps2.px("TDM"))
dig_1 = cl.new_X6("MyX6", address=0)
dig_1.record_length = 4096
# qubit 1
AM1 = cl.new_source("AutodyneM1", "HolzworthHS9000", "HS9004A-492-1",
power=16.0, frequency=6.4762e9, reference="10MHz")
q1src = cl.new_source("q1source", "HolzworthHS9000", "HS9004A-492-2",
power=16.0, frequency=4.2e9, reference="10MHz")
cl.set_measure(q1, aps2.tx(4), dig_1.channels[1], gate=False, trig_channel=aps2.tx(6).ch("m3"), generator=AM1)
cl.set_control(q1, aps2.tx(5), generator=q1src)
cl["q1"].measure_chan.autodyne_freq = 11e6
cl["q1"].measure_chan.pulse_params = {"length": 3e-6,
"amp": 1.0,
"sigma": 1.0e-8,
"shape_fun": "tanh"}
cl["q1"].frequency = 67.0e6
cl["q1"].pulse_params = {"length": 100e-9,
"pi2Amp": 0.4,
"piAmp": 0.8,
"shape_fun": "drag",
"drag_scaling": 0.0,
"sigma": 5.0e-9}
#qubit 2
AM2 = cl.new_source("AutodyneM2", "HolzworthHS9000", "HS9004A-492-3",
power=16.0, frequency=6.4762e9, reference="10MHz")
q2src = cl.new_source("q2source", "HolzworthHS9000", "HS9004A-492-4",
power=16.0, frequency=4.2e9, reference="10MHz")
cl.set_measure(q2, aps2.tx(7), dig_1.channels[0], gate=False, trig_channel=aps2.tx(6).ch("m3"), generator=AM2)
cl.set_control(q2, aps2.tx(8), generator=q2src)
cl["q2"].measure_chan.autodyne_freq = 11e6
cl["q2"].measure_chan.pulse_params = {"length": 3e-6,
"amp": 1.0,
"sigma": 1.0e-8,
"shape_fun": "tanh"}
cl.commit()
[ ]:
# initialize all four APS2 to linear regime
for i in range(4,8):
aps2.tx(i).ch(1).I_channel_amp_factor = 0.5
aps2.tx(i).ch(1).Q_channel_amp_factor = 0.5
aps2.tx(i).ch(1).amp_factor = 1
[ ]:
pl = PipelineManager()
pl.create_default_pipeline(qubits=[q1,q2])
for ql in ['q1','q2']:
qb = cl[ql]
pl[ql].clear_pipeline()
pl[ql].stream_type = "raw"
pl[ql].create_default_pipeline(buffers=False)
pl[ql]["Demodulate"].frequency = qb.measure_chan.autodyne_freq
# only enable this filter when you're running single shot fidelity
#pl[ql].add(FidelityKernel(save_kernel=True, logistic_regression=True, set_threshold=True, label="Q1_SSF"))
pl[ql]["Demodulate"]["Integrate"].box_car_start = 3e-7
pl[ql]["Demodulate"]["Integrate"].box_car_stop = 2.3e-6
#pl[ql].add(Display(label=ql+" - Raw", plot_dims=0))
#pl[ql]["Demodulate"].add(Display(label=ql+" - Demod", plot_dims=0))
pl[ql]["Demodulate"]["Integrate"]["Average"].add(Display(label=ql+" - Final Average", plot_dims=0))
# if you want to see partial averages:
#pl[ql]["Demodulate"]["Integrate"]["Average"].add(Display(label=ql+" - Partial Average", plot_dims=0), connector_out="partial_average")
#pl[ql]["Demodulate"]["Integrate"]["Average"].add(Display(label=ql+" - Partial Average1d", plot_dims=1), connector_out="partial_average")
pl.show_pipeline()
Cavity Spectroscopy
[ ]:
pf = PulsedSpec(q1, specOn=False)
plot_pulse_files(pf)
[ ]:
exp = QubitExperiment(pf,averages=256)
#exp.add_qubit_sweep(q2, "measure", "frequency", np.linspace(6.38e9, 6.395e9, 51))
exp.add_qubit_sweep(q1, "measure", "frequency", np.linspace(6.424e9, 6.432e9, 45))
#exp.add_qubit_sweep(q1,"measure","amplitude",np.linspace(0.2,0.8,10))
exp.run_sweeps()
[ ]:
# data, desc = exp.writers[0].get_data()
# plt.plot(desc.axes[0].points, np.abs(data))
[ ]:
AM1.frequency = 6.42843e9
[ ]:
AM2.frequency = 6.3863e9
Qubit Spectroscopy
[ ]:
qb = q1
[ ]:
qb.frequency = 0.0
qb.pulse_params['length'] = 5e-6
qb.pulse_params['shape_fun'] = "tanh"
pf = PulsedSpec(qb, specOn=True)
plot_pulse_files(pf)
[ ]:
pf = PulsedSpec(qb, specOn=True)
exp = QubitExperiment(pf,averages=256)
exp.add_qubit_sweep(qb, "control", "frequency", np.linspace(5.28e9, 5.34e9, 61))
#exp.run_sweeps()
[ ]:
# data, desc = exp.writers[0].get_data()
# plt.plot(desc.axes[0].points, np.abs(data))
[ ]:
q1.frequency = -63e6
q1.pulse_params['length'] = 200e-9
q1.pulse_params['shape_fun'] = "tanh"
q1.pulse_params['piAmp'] = 0.4
q1.pulse_params['pi2Amp'] = 0.2
pf = PulsedSpec(q1, specOn=True)
#plot_pulse_files(pf)
[ ]:
fq = 5.26e9 #5.2525e9
q1src.frequency = fq - q1.frequency
q1.phys_chan.I_channel_amp_factor = 0.2
q1.phys_chan.Q_channel_amp_factor = 0.2
[ ]:
q1src.frequency
[ ]:
q2.frequency = 81e6
q2.pulse_params['length'] = 200e-9
q2.pulse_params['shape_fun'] = "tanh"
q2.pulse_params['piAmp'] = 0.4
q2.pulse_params['pi2Amp'] = 0.2
pf = PulsedSpec(q2, specOn=True)
#plot_pulse_files(pf)
[ ]:
fq2 = 5.2113e9
q2src.frequency = fq2 - q2.frequency
q2.phys_chan.I_channel_amp_factor = 0.2
q2.phys_chan.Q_channel_amp_factor = 0.2
[ ]:
q2src.frequency
Mixer calibration
[ ]:
salo = cl.new_source("salo", "HolzworthHS9000", "HS9004A-381-4",
power=10.0, frequency=6.5e9, reference="10MHz")
specAn = cl.new_spectrum_analzyer('specAn','ASRL/dev/ttyACM0',salo)
[ ]:
from auspex.instruments import enumerate_visa_instruments, SpectrumAnalyzer
[ ]:
enumerate_visa_instruments()
[ ]:
# from here out, uncomment cal.calibrate() if you want to actually run the cal
cal = MixerCalibration(q1,specAn,'control', phase_range = (-0.5, 0.5), amp_range=(0.8, 1.2))
#cal.calibrate()
[ ]:
# listed here only if manual adjustment is needed
q1.phys_chan.I_channel_offset = -0.0004
q1.phys_chan.Q_channel_offset = -0.019
q1.phys_chan.amp_factor = 1.004
q1.phys_chan.phase_skew = 0.053
[ ]:
cal = MixerCalibration(q2,specAn,'control', phase_range = (-0.5, 0.5), amp_range=(0.8, 1.2))
#cal.calibrate()
[ ]:
q2.phys_chan.I_channel_offset = -0.0004
q2.phys_chan.Q_channel_offset = -0.019
q2.phys_chan.amp_factor = 0.985
q2.phys_chan.phase_skew = 0.074
[ ]:
cal = MixerCalibration(q1,specAn,'measure', phase_range = (-0.5, 0.5), amp_range=(0.8, 1.2))
#cal.calibrate()
[ ]:
cal = MixerCalibration(q2,specAn,'measure', phase_range = (-0.5, 0.5), amp_range=(0.8, 1.2))
#cal.calibrate()
Rabi Width
[ ]:
pf = RabiWidth(q1, np.arange(20e-9, 0.602e-6, 10e-9))
exp = QubitExperiment(pf, averages=200)
plot_pulse_files(pf)
#exp.add_qubit_sweep(q1, "control", "frequency", q1src.frequency + np.linspace(-6e6, 6e6, 61))
exp.run_sweeps()
[ ]:
pf = RabiWidth(q2, np.arange(20e-9, 0.602e-6, 10e-9))
exp = QubitExperiment(pf, averages=200)
plot_pulse_files(pf)
#exp.add_qubit_sweep(q2, "control", "frequency", q2src.frequency + np.linspace(-2e6, 2e6, 21))
#exp.add_qubit_sweep(q2, "measure", "frequency", AM2.frequency + np.linspace(-2e6, 2e6, 11))
exp.run_sweeps()
Rabi Amp
[ ]:
pf = RabiAmp(q1, np.linspace(-1, 1, 101))
exp = QubitExperiment(pf, averages=128)
exp.run_sweeps()
[ ]:
cal = RabiAmpCalibration(q1,quad='imag')
#cal.calibrate()
[ ]:
q1.pulse_params['piAmp'] = 0.6179
q1.pulse_params['pi2Amp'] = q1.pulse_params['piAmp']/2
[ ]:
pf = RabiAmp(q2, np.linspace(-1, 1, 101))
exp = QubitExperiment(pf, averages=128)
exp.run_sweeps()
[ ]:
cal = RabiAmpCalibration(q2,quad='imag')
#cal.calibrate()
[ ]:
q2.pulse_params['piAmp'] = 0.743
q2.pulse_params['pi2Amp'] = q2.pulse_params['piAmp']/2
Ramsey Calibration
[ ]:
# need to be in the neighbourhood for this to work
cal = RamseyCalibration(q1,quad='imag')
#cal.calibrate()
T1
[ ]:
qb = q2
icpts = np.linspace(20e-9, 201.02e-6, 101)
pf = InversionRecovery(qb, icpts)
exp = QubitExperiment(pf, averages=400)
exp.run_sweeps()
[ ]:
data, desc = exp.writers[0].get_data()
[ ]:
from auspex.analysis.qubit_fits import T1Fit, RamseyFit
from auspex.analysis.helpers import cal_scale
[ ]:
sdata = cal_scale(data)
[ ]:
fit = T1Fit(icpts, abs(data[0:-4]))
[ ]:
fit.make_plots()
T2
[ ]:
rpts = np.linspace(20e-9, 50.02e-6, 101)
pf = Ramsey(q1, rpts ,TPPIFreq=0e3)
#exp.add_qubit_sweep(q1, "control", "frequency", q1src.frequency + np.linspace(-2e6, 2e6, 21))
exp = QubitExperiment(pf, averages=200)
exp.run_sweeps()
[ ]:
data, desc = exp.writers[0].get_data()
sdata = cal_scale(data)
[ ]:
fit = RamseyFit(rpts, abs(data[0:-4]),make_plots = True)
[ ]:
fcorrect = fit.fit_params['f']
[ ]:
fcorrect
[ ]:
#q1src.frequency -= fcorrect
[ ]:
pf = Ramsey(q2, rpts,TPPIFreq=0e3)
exp = QubitExperiment(pf, averages=200)
exp.run_sweeps()
[ ]:
data, desc = exp.writers[0].get_data()
sdata = cal_scale(data)
[ ]:
fit = RamseyFit(rpts, abs(data[0:-4]),make_plots = True)
fcorrect = fit.fit_params['f']
[ ]:
fcorrect*1e-6
[ ]:
#q2src.frequency += fcorrect
Echo experiments
[ ]:
exp = QubitExperiment(HahnEcho(q2, np.linspace(20e-9, 80.02e-6, 81), periods=5), averages=512)
exp.run_sweeps()
[ ]:
data, desc = exp.writers[0].get_data()
cdata = cal_scale(np.real(data))
fit = RamseyFit(desc.axes[0].points[:-4], cdata, make_plots=True)
fit.fit_params
Single Qubit Cals
[ ]:
RabiAmpCalibration(q1, quad="imag").calibrate()
[ ]:
PiCalibration(q1, quad="imag", num_pulses=7).calibrate()
[ ]:
Pi2Calibration(q1, quad="imag", num_pulses=7).calibrate()
Single-Qubit RB
[ ]:
from auspex.analysis.qubit_fits import *
from auspex.analysis.helpers import *
[ ]:
rb_lens = [2, 4, 8, 16, 32, 128, 256, 512]
[ ]:
rb_seqs = create_RB_seqs(1, rb_lens)
pf = SingleQubitRB(q1, rb_seqs)
[ ]:
exp = QubitExperiment(pf, averages=256)
exp.run_sweeps()
[ ]:
data, desc = exp.writers[0].get_data()
[ ]:
lens = [int(l) for l in desc.axes[0].points[:-4]]
[ ]:
SingleQubitRBFit(lens, cal_scale(np.imag(data)), make_plots=True)
Fancier things
Maybe you want to see how \(T_1\) varies with repeated measurement…
[ ]:
from auspex.parameter import IntParameter
import time
[ ]:
N = 1000
lengths = np.linspace(20e-9, 500.02e-6, 101)
[ ]:
T1seq = [[X(q2), Id(q2, length=d), MEAS(q2)] for d in lengths]
T1seq += create_cal_seqs((q2,), 2)
[ ]:
wait_param = IntParameter(default=0)
wait_param.name = "Repeat"
#wait = lambda x: print(f"{x}")
def wait(x):
print(f"{x}")
time.sleep(2)
wait_param.assign_method(wait)
mf = compile_to_hardware(T1seq, "T1/T1")
exp = QubitExperiment(mf, averages=512)
exp.wait_param = wait_param
# with these params each shot is roughly 22 secs apart
exp.add_sweep(exp.wait_param, list(range(N))) # repeat T1 scan 1000 times
exp.run_sweeps()
[ ]:
# load data
# get T1s
T1s = []
T1_error = []
#data, desc = exp.writers[0].get_data()
for i in range(N):
cdata = cal_scale(np.imag(data[i,:]))
fit = T1Fit(lengths, cdata, make_plots=False)
T1s.append(fit.fit_params["T1"])
T1_error.append(fit.fit_errors["T1"])
[ ]:
plt.figure(figsize=(8,6))
#plt.errorbar(range(1000),np.array(T1s)*1e6, yerr=np.array(T1_error)*1e6, fmt='+', elinewidth=0.5, barsabove=True, capsize=0.7)
plt.plot(range(1000),np.array(T1s)*1e6, '+')
plt.title(r'Q2 $T_1$ Variability')
plt.xlabel('N repeats')
plt.ylabel(r'$T_1$ (us)')
#plt.savefig('T1vsTime.png', dpi=300, bbox_inches='tight')
2Q RB
[ ]:
lengths = [2,4,6,8,10] # range(2,10) #[2**n for n in range(1,6)]
seqs = create_RB_seqs(2, lengths=lengths)
exp = qef.create(TwoQubitRB(q1, q2, seqs=seqs), expname='Q2Q1RB')
exp.run_sweeps()
2Q Gates
[ ]:
edge12 = cl.new_edge(q1,q2)
cl.set_control(edge12, aps2.tx(5), generator=q1src)
[ ]:
q1_10 = q1.phys_chan.generator.frequency + q1.frequency
q2_10 = q2.phys_chan.generator.frequency + q2.frequency
[ ]:
edge12.frequency = q2_10 - q1.phys_chan.generator.frequency
[ ]:
edge12.pulse_params = {'length': 400e-9, 'amp': 0.8, 'shape_fun': 'tanh', 'sigma':10e-9}
[ ]:
q1.measure_chan.pulse_params['amp'] = 1.0
q2.measure_chan.pulse_params['amp'] = 1.0
CR length cal
[ ]:
crlens = np.arange(100e-9,2.1e-6,100e-9)
pf = EchoCRLen(q1,q2,lengths=crlens)
plot_pulse_files(pf)
[ ]:
exp = QubitExperiment(pf, averages=512)
exp.run_sweeps()
Above just runs the experiment used by the calibration routine. Here is the actual calibration:
[ ]:
crlens = np.arange(100e-9,2.1e-6,100e-9)
# phase, amp and rise_fall have defaults but you can overwrite them
pce = CRLenCalibration(cl["q1->q2"], lengths=lengths, phase = 0, amp = 0.8, rise_fall = 40e-9,
do_plotting=True, sample_name="CRLen", averages=512)
pec.calibrate()
CR phase cal
[ ]:
phases = np.arange(0,np.pi*2,np.pi/20)
pf = EchoCRPhase(q1,q2,phases,length=1000e-9)
plot_pulse_files(pf)
[ ]:
exp = QubitExperiment(pf, averages=512)
exp.run_sweeps()
[ ]:
phases = np.linspace(0, 2*np.pi, 21)
pce = CRPhaseCalibration(edge12, phases = phases, amp = 0.8, rise_fall = 40e-9,
do_plotting=True, sample_name="CRPhase", averages=512)
pec.calibrate()
CR amp cal
[ ]:
amps = np.arange(0.7,0.9,0.1)
pf = EchoCRAmp(q1,q2,amps,length=1000e-9)
plot_pulse_files(pf)
[ ]:
exp = QubitExperiment(pf, averages=512)
exp.run_sweeps()
[ ]:
pce = CRAmpCalibration(cl["q1->q2"], amp_range = 0.2, rise_fall = 40e-9,
do_plotting=True, sample_name="CRAmp", averages=512)
pec.calibrate()