Example-APS2-2Qubit


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


In [ ]:
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


In [ ]:
# this will all be system dependent
cl = ChannelLibrary(db_resource_name=":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()

In [ ]:
# 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

In [ ]:
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


In [ ]:
pf = PulsedSpec(q1, specOn=False)
plot_pulse_files(pf)

In [ ]:
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()

In [ ]:
# data, desc = exp.writers[0].get_data()
# plt.plot(desc.axes[0].points, np.abs(data))

In [ ]:
AM1.frequency = 6.42843e9

In [ ]:
AM2.frequency = 6.3863e9

Qubit Spectroscopy


In [ ]:
qb = q1

In [ ]:
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)

In [ ]:
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()

In [ ]:
# data, desc = exp.writers[0].get_data()
# plt.plot(desc.axes[0].points, np.abs(data))

In [ ]:
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)

In [ ]:
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

In [ ]:
q1src.frequency

In [ ]:
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)

In [ ]:
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

In [ ]:
q2src.frequency

Mixer calibration


In [ ]:
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)

In [ ]:
from auspex.instruments import enumerate_visa_instruments, SpectrumAnalyzer

In [ ]:
enumerate_visa_instruments()

In [ ]:
# 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()

In [ ]:
# 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

In [ ]:
cal = MixerCalibration(q2,specAn,'control', phase_range = (-0.5, 0.5), amp_range=(0.8, 1.2))
#cal.calibrate()

In [ ]:
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

In [ ]:
cal = MixerCalibration(q1,specAn,'measure', phase_range = (-0.5, 0.5), amp_range=(0.8, 1.2))
#cal.calibrate()

In [ ]:
cal = MixerCalibration(q2,specAn,'measure', phase_range = (-0.5, 0.5), amp_range=(0.8, 1.2))
#cal.calibrate()

Rabi Width


In [ ]:
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()

In [ ]:
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


In [ ]:
pf = RabiAmp(q1, np.linspace(-1, 1, 101))
exp = QubitExperiment(pf, averages=128)
exp.run_sweeps()

In [ ]:
cal = RabiAmpCalibration(q1,quad='imag')
#cal.calibrate()

In [ ]:
q1.pulse_params['piAmp'] = 0.6179
q1.pulse_params['pi2Amp'] = q1.pulse_params['piAmp']/2

In [ ]:
pf = RabiAmp(q2, np.linspace(-1, 1, 101))
exp = QubitExperiment(pf, averages=128)
exp.run_sweeps()

In [ ]:
cal = RabiAmpCalibration(q2,quad='imag')
#cal.calibrate()

In [ ]:
q2.pulse_params['piAmp'] = 0.743
q2.pulse_params['pi2Amp'] = q2.pulse_params['piAmp']/2

Ramsey Calibration


In [ ]:
# need to be in the neighbourhood for this to work

cal = RamseyCalibration(q1,quad='imag')
#cal.calibrate()

T1


In [ ]:
qb = q2
icpts = np.linspace(20e-9, 201.02e-6, 101)
pf = InversionRecovery(qb, icpts)
exp = QubitExperiment(pf, averages=400)
exp.run_sweeps()

In [ ]:
data, desc = exp.writers[0].get_data()

In [ ]:
from auspex.analysis.qubit_fits import T1Fit, RamseyFit
from auspex.analysis.helpers import cal_scale

In [ ]:
sdata = cal_scale(data)

In [ ]:
fit = T1Fit(icpts, abs(data[0:-4]))

In [ ]:
fit.make_plots()

T2


In [ ]:
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()

In [ ]:
data, desc = exp.writers[0].get_data()
sdata = cal_scale(data)

In [ ]:
fit = RamseyFit(rpts, abs(data[0:-4]),make_plots = True)

In [ ]:
fcorrect = fit.fit_params['f']

In [ ]:
fcorrect

In [ ]:
#q1src.frequency -= fcorrect

In [ ]:
pf = Ramsey(q2, rpts,TPPIFreq=0e3)
exp = QubitExperiment(pf, averages=200)
exp.run_sweeps()

In [ ]:
data, desc = exp.writers[0].get_data()
sdata = cal_scale(data)

In [ ]:
fit = RamseyFit(rpts, abs(data[0:-4]),make_plots = True)
fcorrect = fit.fit_params['f']

In [ ]:
fcorrect*1e-6

In [ ]:
#q2src.frequency += fcorrect

Echo experiments


In [ ]:
exp = QubitExperiment(HahnEcho(q2, np.linspace(20e-9, 80.02e-6, 81), periods=5), averages=512)
exp.run_sweeps()

In [ ]:
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


In [ ]:
RabiAmpCalibration(q1, quad="imag").calibrate()

In [ ]:
PiCalibration(q1, quad="imag", num_pulses=7).calibrate()

In [ ]:
Pi2Calibration(q1, quad="imag", num_pulses=7).calibrate()

Single-Qubit RB


In [ ]:
from auspex.analysis.qubit_fits import *
from auspex.analysis.helpers import *

In [ ]:
rb_lens = [2, 4, 8, 16, 32, 128, 256, 512]

In [ ]:
rb_seqs = create_RB_seqs(1, rb_lens)
pf = SingleQubitRB(q1, rb_seqs)

In [ ]:
exp = QubitExperiment(pf, averages=256)
exp.run_sweeps()

In [ ]:
data, desc = exp.writers[0].get_data()

In [ ]:
lens = [int(l) for l in desc.axes[0].points[:-4]]

In [ ]:
SingleQubitRBFit(lens, cal_scale(np.imag(data)), make_plots=True)

Fancier things

Maybe you want to see how $T_1$ varies with repeated measurement...


In [ ]:
from auspex.parameter import IntParameter
import time

In [ ]:
N = 1000
lengths = np.linspace(20e-9, 500.02e-6, 101)

In [ ]:
T1seq = [[X(q2), Id(q2, length=d), MEAS(q2)] for d in lengths]
T1seq += create_cal_seqs((q2,), 2)

In [ ]:
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()

In [ ]:
# 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"])

In [ ]:
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


In [ ]:
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


In [ ]:
edge12 = cl.new_edge(q1,q2)
cl.set_control(edge12, aps2.tx(5), generator=q1src)

In [ ]:
q1_10 = q1.phys_chan.generator.frequency + q1.frequency
q2_10 = q2.phys_chan.generator.frequency + q2.frequency

In [ ]:
edge12.frequency = q2_10 - q1.phys_chan.generator.frequency

In [ ]:
edge12.pulse_params = {'length': 400e-9, 'amp': 0.8, 'shape_fun': 'tanh', 'sigma':10e-9}

In [ ]:
q1.measure_chan.pulse_params['amp'] = 1.0
q2.measure_chan.pulse_params['amp'] = 1.0

CR length cal


In [ ]:
crlens = np.arange(100e-9,2.1e-6,100e-9)
pf = EchoCRLen(q1,q2,lengths=crlens)
plot_pulse_files(pf)

In [ ]:
exp = QubitExperiment(pf, averages=512)
exp.run_sweeps()

Above just runs the experiment used by the calibration routine. Here is the actual calibration:


In [ ]:
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


In [ ]:
phases = np.arange(0,np.pi*2,np.pi/20)
pf = EchoCRPhase(q1,q2,phases,length=1000e-9)
plot_pulse_files(pf)

In [ ]:
exp = QubitExperiment(pf, averages=512)
exp.run_sweeps()

In [ ]:
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


In [ ]:
amps = np.arange(0.7,0.9,0.1)
pf = EchoCRAmp(q1,q2,amps,length=1000e-9)
plot_pulse_files(pf)

In [ ]:
exp = QubitExperiment(pf, averages=512)
exp.run_sweeps()

In [ ]:
pce = CRAmpCalibration(cl["q1->q2"], amp_range = 0.2, rise_fall = 40e-9,
            do_plotting=True, sample_name="CRAmp", averages=512)
pec.calibrate()