Calibrating Qubits using OpenPulse

Contents

  1. Introduction
  2. Finding our qubit
  3. Rabi experiment
  4. 0 vs 1
  5. Measuring T1
  6. Ramsey experiment
  7. Measuring T2
  8. Dynamical Decoupling

1. Introduction

In [1]:
%matplotlib inline
import qiskit.pulse as pulse
import qiskit.pulse.pulse_lib as pulse_lib
from qiskit.compiler import assemble

import qiskit
qiskit.__qiskit_version__
Out[1]:
{'qiskit': '0.11.1',
 'qiskit-terra': '0.8.2',
 'qiskit-ignis': '0.1.1',
 'qiskit-aer': '0.2.3',
 'qiskit-ibmq-provider': '0.3.1',
 'qiskit-aqua': '0.5.3'}
In [2]:
from qiskit import IBMQ
IBMQ.load_account()
provider = IBMQ.get_provider(hub='your-hub-name') # change to your hub name
backend = provider.get_backend('ibmq_poughkeepsie')

backend_config = backend.configuration()
In [3]:
from qiskit.tools.jupyter import backend_overview, backend_monitor
%qiskit_backend_monitor backend

The superconducting devices at IBM are routinely calibrated to determine the properties of each qubit. The calibration procedure determines the qubit frequency, coherence and energy relaxation times, and pulse parameters, among other things. In this notebook, we show how these parameters can be determined at the microwave level using Terra.Pulse.

For an introduction to the experiments, please see this paper or this paper or this paper.

Note: Pulse is a fairly new component of Qiskit. Please contact [email protected] if you find that something in this notebook is suddenly broken.

In [4]:
backend_defaults = backend.defaults()
backend_devicespec = pulse.DeviceSpecification.create_from(backend)
dt = backend_config.dt

2. Finding our qubit

In [ ]:
# choose device to work on
from qiskit import IBMQ
IBMQ.load_account()

Define the frequency range that will be swept in search of the qubit.

In [6]:
qubit = 1
center_frequency_GHz = backend_defaults.qubit_freq_est[qubit]
# define frequencies to do VNA sweep
import numpy as np
frequency_span_kHz = 20000
frequency_step_kHz = 1000

frequency_min = center_frequency_GHz - frequency_span_kHz/2.e6
frequency_max = center_frequency_GHz + frequency_span_kHz/2.e6
frequencies_GHz = np.arange(frequency_min, frequency_max, frequency_step_kHz/1e6)
print(frequencies_GHz)
[4.82198758 4.82298758 4.82398758 4.82498758 4.82598758 4.82698758
 4.82798758 4.82898758 4.82998758 4.83098758 4.83198758 4.83298758
 4.83398758 4.83498758 4.83598758 4.83698758 4.83798758 4.83898758
 4.83998758 4.84098758]

Define drive and measurement pulse parameters for the experiment

In [7]:
# drive pulse parameters
drive_power = 0.01
drive_samples = 128
drive_sigma = 16

# creating drive pulse
drive_pulse = pulse_lib.gaussian(duration=drive_samples, amp=drive_power,
                                 sigma=drive_sigma, name='mydrivepulse')
drive_pulse_qubit = drive_pulse(backend_devicespec.q[qubit].drive)

# measurement pulse parameters
meas_amp = 0.05
meas_samples = 1200
meas_sigma = 4
meas_risefall = 25

# creating measurement pulse
meas_pulse = pulse_lib.gaussian_square(duration=meas_samples, amp=meas_amp,
                                       sigma=meas_sigma, risefall=meas_risefall, 
                                       name='mymeasurepulse')
meas_pulse_qubit = meas_pulse(backend_devicespec.q[qubit].measure)

# create acquire pulse
acq_cmd=pulse.Acquire(duration=meas_samples)
acq_cmd_qubit = acq_cmd(backend_devicespec.q, backend_devicespec.mem)

# combined measure and acquire pulse
measure_and_acquire_qubit = meas_pulse_qubit | acq_cmd_qubit

# scalefactor for received data
scale_factor = 1e-10

Once the pulse parameters have been defined, we can create the pulse schedules corresponding to each frequency in the sweep.

In [8]:
# schedules
schedules = []
schedule_LOs = []

num_shots_per_frequency = 256
for jj, drive_frequency in enumerate(frequencies_GHz):
    # start an empty schedule with a label
    this_schedule = pulse.Schedule(name="Frequency = {}".format(drive_frequency))
    this_schedule += drive_pulse_qubit
    this_schedule += measure_and_acquire_qubit << this_schedule.duration

    schedules.append(this_schedule)
    thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: drive_frequency})
    schedule_LOs.append(thisLO)
        
VNASweep_experiment_qobj = assemble(schedules, backend = backend, 
                            meas_level=1, meas_return='single', 
                            shots=num_shots_per_frequency,
                            schedule_los = schedule_LOs
                            )
In [9]:
schedules[-1].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[9]:
In [10]:
job = backend.run(VNASweep_experiment_qobj)
In [ ]:
from qiskit.tools.monitor import job_monitor
print(job.job_id())
job_monitor(job, monitor_async='True')
In [12]:
job = backend.retrieve_job('5d2e228e15ce0100196d8c22')
VNASweep_results = job.result(timeout=3600)
In [13]:
plot_X = frequencies_GHz
plot_Y = []
for kk, drive_frequency in enumerate(frequencies_GHz):
    thisfrequency_results = VNASweep_results.get_memory(kk)*scale_factor
    plot_Y.append(  np.mean(thisfrequency_results[:, qubit])  )

import matplotlib.pyplot as plotter
plotter.plot(plot_X, plot_Y)

Out[13]:
[<matplotlib.lines.Line2D at 0x12e2fd320>]
In [14]:
rough_frequency_qubit = frequencies_GHz [
    np.where( plot_Y == np.max(plot_Y))[0]
                ].tolist()[0]
rough_frequency_qubit = round(rough_frequency_qubit, 5)
print(rough_frequency_qubit)
4.83199

3. Rabi experiment

Once we know the frequency of our qubit, the next step is to determine the strength of a $\pi$ pulse.

In [15]:
# Rabi experiment parameters
num_Rabi_points = 64
num_shots_per_point = 256

# drive parameters
drive_power_min = 0
drive_power_max = 0.1
drive_powers = np.linspace(drive_power_min, drive_power_max, num_Rabi_points)
drive_samples = 128
drive_sigma = 16
In [16]:
# create schedules for Rabi experiment 
Rabi_schedules = []
Rabi_schedule_LOs = []
for ii, drive_power in enumerate(drive_powers):
    rabi_pulse = pulse_lib.gaussian(duration=drive_samples, amp=drive_power, 
                                    sigma=drive_sigma, name='rabi_pulse_{}'.format(ii))
    rabi_pulse_qubit = rabi_pulse(backend_devicespec.q[qubit].drive)
    # start an empty schedule with a label
    this_schedule = pulse.Schedule(name="Rabi drive = {}".format(drive_power))
    this_schedule += rabi_pulse_qubit
    this_schedule += measure_and_acquire_qubit << this_schedule.duration
    
    Rabi_schedules.append(this_schedule)
    thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: rough_frequency_qubit})
    Rabi_schedule_LOs.append(thisLO)
In [17]:
Rabi_schedules[-1].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[17]:
In [18]:
rabi_experiment_qobj = assemble (Rabi_schedules, backend = backend, 
                            meas_level=1, meas_return='avg',
                            shots=num_shots_per_point,
                            schedule_los = Rabi_schedule_LOs
                                )
In [ ]:
job = backend.run(rabi_experiment_qobj)
print(job.job_id())
job_monitor(job, monitor_async=True)
In [20]:
job = backend.retrieve_job('5d2e2a0099a509001888ab02')
Rabi_results = job.result(timeout=3600)
In [21]:
plot_X = drive_powers
plot_Y = []
for jj, drive_power in enumerate(drive_powers):
    thispower_results = Rabi_results.get_memory(jj)*scale_factor
    plot_Y.append( thispower_results[qubit] )

import matplotlib.pyplot as plotter
plot_Y = plot_Y - np.mean(plot_Y)
plotter.plot(plot_X, plot_Y)
Out[21]:
[<matplotlib.lines.Line2D at 0x130045940>]
In [22]:
from scipy.optimize import curve_fit

fit_func = lambda x,A,B,T,phi: (A*np.cos(2*np.pi*x/T+phi)+B)

#Fit the data
fitparams, conv = curve_fit(fit_func, plot_X, plot_Y, [3.0  ,0.0  ,0.04  ,0])

#get the pi amplitude
first_peak = abs(np.pi-fitparams[3])*fitparams[2]/(2*np.pi)
pi_amp = abs(fitparams[2]/2)

plotter.scatter(plot_X, plot_Y)
plotter.plot(plot_X, fit_func(plot_X, *fitparams), color='red')
plotter.axvline(first_peak, color='black', linestyle='dashed')
plotter.axvline(first_peak + pi_amp, color='black', linestyle='dashed')
plotter.xlabel('Pulse amplitude, a.u.', fontsize=20)
plotter.ylabel('Signal, a.u.', fontsize=20)
plotter.title('Rough Pi Amplitude Calibration', fontsize=20)

print('Pi Amplitude %f'%(pi_amp))
Pi Amplitude 0.021406

4. 0 vs 1

One our $\pi$ pulses have been calibrated, we can now create the state $\vert1\rangle$ with reasonably probability. We can use this to find out what the states $\vert0\rangle$ and $\vert1\rangle$ look like in our measurements.

In [23]:
# Rabi experiment parameters
num_shots_gndexc = 512

# drive parameters
drive_power = pi_amp
print(drive_power)
0.021406169328870545
In [24]:
# create schedules for Rabi experiment 
gndexc_schedules = []
gndexc_schedule_LOs = []

pi_pulse = pulse_lib.gaussian(duration=drive_samples, amp=pi_amp, 
                                sigma=drive_sigma, name='pi_pulse'.format(ii))
pi_pulse_qubit = pi_pulse(backend_devicespec.q[qubit].drive)
    
# ground state schedule
gnd_schedule = pulse.Schedule(name="ground state")
gnd_schedule += measure_and_acquire_qubit << gnd_schedule.duration
thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: rough_frequency_qubit})
    
# excited state schedule
exc_schedule = pulse.Schedule(name="excited state")
exc_schedule += pi_pulse_qubit
exc_schedule += measure_and_acquire_qubit << exc_schedule.duration
thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: rough_frequency_qubit})

gndexc_schedules.append(gnd_schedule)
gndexc_schedules.append(exc_schedule)
gndexc_schedule_LOs.append(thisLO)
gndexc_schedule_LOs.append(thisLO)
In [25]:
gndexc_schedules[0].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[25]:
In [26]:
gndexc_schedules[1].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[26]:
In [27]:
gndexc_experiment_qobj = assemble (gndexc_schedules, backend = backend, 
                            meas_level=1, meas_return='single',
                            shots=num_shots_gndexc,
                            schedule_los = gndexc_schedule_LOs
                                )
In [ ]:
job = backend.run(gndexc_experiment_qobj)
print(job.job_id())
job_monitor(job, monitor_async=True)
In [29]:
job = backend.retrieve_job('5d2e2c3a61157a0018e22440')
gndexc_results = job.result(timeout=3600)
In [30]:
gnd_results = gndexc_results.get_memory(0)[:, qubit]*scale_factor
exc_results = gndexc_results.get_memory(1)[:, qubit]*scale_factor


plotter.scatter(np.real(gnd_results), np.imag(gnd_results), 
                s=5, cmap='viridis',c='blue',alpha=0.5, label='state_0')
plotter.scatter(np.real(exc_results), np.imag(exc_results), 
                s=5, cmap='viridis',c='red',alpha=0.5, label='state_1')
mean_gnd = np.mean(gnd_results) # takes mean of both real and imaginary parts
mean_exc = np.mean(exc_results)
plotter.scatter(np.real(mean_gnd), np.imag(mean_gnd), 
                s=200, cmap='viridis',c='blue',alpha=1.0, label='state_0_mean')
plotter.scatter(np.real(mean_exc), np.imag(mean_exc), 
                s=200, cmap='viridis',c='red',alpha=1.0, label='state_1_mean')

plotter.xlabel('I (a.u.)')
plotter.xlabel('Q (a.u.)')
Out[30]:
Text(0.5, 0, 'Q (a.u.)')
In [31]:
def get_01(IQ_data):
    dist_0 = np.linalg.norm(np.array([
                        np.real(IQ_data) - np.real(mean_gnd),
                        np.imag(IQ_data) - np.imag(mean_gnd)
                        ]))
    dist_1 = np.linalg.norm(np.array([
                        np.real(IQ_data) - np.real(mean_exc),
                        np.imag(IQ_data) - np.imag(mean_exc)
                        ]))
    
    if dist_1 <= dist_0:
        return 1
    else:
        return 0

    
print(get_01(mean_gnd), get_01(mean_exc))
0 1

5. Measuring T1

In [32]:
# T1 experiment parameters
time_max_us = 500
time_step_us = 2
times_us = np.arange(1, time_max_us, time_step_us)
num_shots_per_point = 512

# drive parameters
drive_power = pi_amp
print(drive_power)
0.021406169328870545
In [33]:
# create schedules for Ramsey experiment 
T1_schedules = []
T1_schedule_LOs = []

T1_pulse = pulse_lib.gaussian(duration=drive_samples, amp=drive_power, 
                                sigma=drive_sigma, name='T1_pulse')
T1_pulse_qubit = T1_pulse(backend_devicespec.q[qubit].drive)
thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: rough_frequency_qubit})
for ii, delay_time_us in enumerate(times_us):
    # start an empty schedule with a label
    this_schedule = pulse.Schedule(name="T1 delay = {} us".format(delay_time_us))
    this_schedule += T1_pulse_qubit
    this_schedule |= (measure_and_acquire_qubit << int(delay_time_us*1000/dt))
    
    T1_schedules.append(this_schedule)
    T1_schedule_LOs.append(thisLO)
In [34]:
T1_schedules[0].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[34]:
In [35]:
T1_experiment_qobj = assemble (T1_schedules, backend = backend, 
                            meas_level=1, meas_return='avg',
                            shots=num_shots_per_point,
                            schedule_los = T1_schedule_LOs
                                )
In [ ]:
job = backend.run(T1_experiment_qobj)
print(job.job_id())
job_monitor(job, monitor_async=True)
In [37]:
job = backend.retrieve_job('5d2e79ad99a509001888ab09')
T1_results = job.result(timeout=3600)
In [38]:
plot_X = times_us
plot_Y = []
for jj, delay_time_us in enumerate(times_us):
    thisdelay_results = T1_results.get_memory(jj)*scale_factor
    plot_Y.append( thisdelay_results[qubit] )

plotter.plot(plot_X, plot_Y)
Out[38]:
[<matplotlib.lines.Line2D at 0x13161aba8>]
In [39]:
from scipy.optimize import curve_fit

fit_func2 = lambda x,A,B: (A*np.exp(-x/59.8)+B)

#Fit the data
fitparams2, conv2 = curve_fit(fit_func2, plot_X,
                                      plot_Y,
                                      [-1.0,-11])

print(f"T1 from backend = {backend.properties().qubits[qubit][0].value} us")

plotter.scatter(plot_X, plot_Y)
plotter.plot(plot_X, fit_func2(plot_X, *fitparams2), color='black')
plotter.xlim(0, np.max(plot_X))
plotter.xlabel('Delay before measurement, ($\mu$s)', fontsize=20)
plotter.ylabel('Measured signal, a.u.', fontsize=20)
T1 = 69.32438949917486 us
T1 from backend = 85.19798054150876 us
Out[39]:
Text(0, 0.5, 'Measured signal, a.u.')

6. Ramsey experiment

Now, we determine both $T_2$ and the qubit frequency to better precision. This is done using a Ramsey pulse sequence.

In this pulse sequence, we first apply a $\pi/2$ pulse, wait some time $\Delta t$, and then apply another $\pi/2$ pulse.

In [40]:
# Ramsey experiment parameters
time_max_us = 100
time_step_us = 0.25
times_us = np.arange(1, time_max_us, time_step_us)
num_shots_per_point = 256

# drive parameters
drive_power = pi_amp/2
print(drive_power)
0.010703084664435272
In [41]:
# create schedules for Ramsey experiment 
Ramsey_schedules = []
Ramsey_schedule_LOs = []
ramsey_pulse = pulse_lib.gaussian(duration=drive_samples, amp=drive_power, 
                                sigma=drive_sigma, name='ramsey_pulse')
ramsey_pulse_qubit = ramsey_pulse(backend_devicespec.q[qubit].drive)
thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: rough_frequency_qubit})
for ii, delay_time_us in enumerate(times_us):
    # start an empty schedule with a label
    this_schedule = pulse.Schedule(name="Ramsey delay = {} us".format(delay_time_us))
    this_schedule += ramsey_pulse_qubit
    this_schedule |= (ramsey_pulse_qubit << int(this_schedule.duration+delay_time_us*1000/dt))
    this_schedule |= (measure_and_acquire_qubit << this_schedule.duration)
    
    Ramsey_schedules.append(this_schedule)
    Ramsey_schedule_LOs.append(thisLO)
In [42]:
Ramsey_schedules[-1].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[42]:
In [43]:
ramsey_experiment_qobj = assemble (Ramsey_schedules, backend = backend, 
                            meas_level=1, meas_return='avg',
                            shots=num_shots_per_point,
                            schedule_los = Ramsey_schedule_LOs
                                )
In [ ]:
job = backend.run(ramsey_experiment_qobj)
print(job.job_id())
job_monitor(job, monitor_async=True)
In [45]:
job = backend.retrieve_job('5d2e75dc137af400181be14a')
Ramsey_results = job.result(timeout=3600)
In [46]:
plot_X = times_us
plot_Y = []

for jj, delay_time_us in enumerate(times_us):
    thisdelay_results = Ramsey_results.get_memory(jj)[qubit]*scale_factor
    plot_Y.append(np.mean(thisdelay_results))

plotter.plot(plot_X, (plot_Y))
Out[46]:
[<matplotlib.lines.Line2D at 0x132f05ac8>]
In [47]:
from scipy.optimize import curve_fit

fit_func = lambda x,A,T,phi,T2p,B: (A*np.exp(-x/T2p)*(np.sin(2*np.pi*x/T+phi))+B)

#Fit the data
fitparams, conv = curve_fit(fit_func, plot_X,
                                      plot_Y,
                                      [1.0,10,0,4,34])

#off-resonance component
delT = fitparams[1]
delf_MHz = 1./(delT)
print(f"df = {delf_MHz} MHz")
first_peak = (np.pi-fitparams[2])*delT/(2*np.pi) + delT/4
second_peak = first_peak + delT
print(f"T2p = {fitparams[3]} us")
print(f"T2 from backend = {backend.properties().qubits[qubit][1].value} us")
#get the pi amplitude
plotter.scatter(plot_X, plot_Y)
plotter.plot(plot_X, fit_func(plot_X, *fitparams), color='red')
plotter.axvline(first_peak, color='black', linestyle='dashed')
plotter.axvline(second_peak, color='red', linestyle='dashed')
plotter.xlim(0, np.max(plot_X))
plotter.xlabel('Ramsey delay, ($\mu$s)', fontsize=20)
plotter.ylabel('Ramsey signal, a.u.', fontsize=20)
plotter.title('Rough $\Delta$f Calibration', fontsize=20)
df = 0.04256591254904121 MHz
T2p = 18.247325010242665 us
T2 from backend = 91.09718741658209 us
Out[47]:
Text(0.5, 1.0, 'Rough $\\Delta$f Calibration')
In [48]:
precise_frequency_qubit_plus = round(rough_frequency_qubit + delf_MHz/1e3, 5)
precise_frequency_qubit_minus = round(rough_frequency_qubit - delf_MHz/1e3, 5)
print(f"{rough_frequency_qubit}->{precise_frequency_qubit_plus} or {precise_frequency_qubit_minus}")
4.83199->4.83203 or 4.83195

7. Measuring T2

In [49]:
# T2 experiment parameters
time_max_us = 125
time_step_us = 0.5
times_us = np.arange(1, time_max_us, time_step_us)
num_shots_per_point = 512

# drive parameters
drive_power_1 = pi_amp/2
drive_power_2 = pi_amp
print(drive_power_1)
print(drive_power_2)
0.010703084664435272
0.021406169328870545
In [50]:
# create schedules for Ramsey experiment 
T2_schedules = []
T2_schedule_LOs = []

T2_pulse_pio2 = pulse_lib.gaussian(duration=drive_samples, amp=drive_power_1, 
                                sigma=drive_sigma, name='T2_pio2_pulse')
T2_pulse_pio2_qubit = T2_pulse_pio2(backend_devicespec.q[qubit].drive)
T2_pulse_pi = pulse_lib.gaussian(duration=drive_samples, amp=drive_power_2, 
                                sigma=drive_sigma, name='T2_pi_pulse')
T2_pulse_pi_qubit = T2_pulse_pi(backend_devicespec.q[qubit].drive)
thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: precise_frequency_qubit_minus})
for ii, delay_time_us in enumerate(times_us):
    # start an empty schedule with a label
    this_schedule = pulse.Schedule(name="T2 delay = {} us".format(delay_time_us))
    this_schedule |= T2_pulse_pio2_qubit
    this_schedule |= (T2_pulse_pi_qubit << int(this_schedule.duration + 
                                               delay_time_us*1000/dt))
    this_schedule |= (T2_pulse_pio2_qubit << int(this_schedule.duration + 
                                               delay_time_us*1000/dt))
    this_schedule |= (measure_and_acquire_qubit << int(this_schedule.duration))
    
    T2_schedules.append(this_schedule)
    T2_schedule_LOs.append(thisLO)
In [51]:
T2_schedules[0].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[51]:
In [52]:
T2_experiment_qobj = assemble (T2_schedules, backend = backend, 
                            meas_level=1, meas_return='avg',
                            shots=num_shots_per_point,
                            schedule_los = T2_schedule_LOs
                                )
In [53]:
job = backend.run(T2_experiment_qobj)
print(job.job_id())
job_monitor(job, monitor_async=True)
5d4d187b507dc70011cc1ca0
In [54]:
T2job = backend.retrieve_job('5d2f6c0ae741150012334c44') 
T2_results = T2job.result(timeout=3600)
In [55]:
plot_X = 2.*times_us
plot_Y = []
for jj, delay_time_us in enumerate(times_us):
    thisdelay_results = T2_results.get_memory(jj)*scale_factor
    plot_Y.append( thisdelay_results[qubit] )

plotter.plot(plot_X, plot_Y)
T2y_echo = plot_Y
T2x_echo = plot_X
In [56]:
from scipy.optimize import curve_fit

T2guess = backend.properties().qubits[qubit][1].value

fit_func2 = lambda x,A,B: (A*np.exp(-x/T2guess)+B)
#Fit the data
fitparams2, conv2 = curve_fit(fit_func2, plot_X,
                                      plot_Y,
                                      [-2.0,1.0])

print(f"T2 from backend = {backend.properties().qubits[qubit][1].value} us")

plotter.scatter(plot_X, plot_Y)
plotter.plot(plot_X, fit_func2(plot_X, *fitparams2), color='black')
plotter.xlim(0, np.max(plot_X))
plotter.xlabel('Total time, ($\mu$s)', fontsize=20)
plotter.ylabel('Measured signal, a.u.', fontsize=20)
T2 = 23.492976894313635 us
T2 from backend = 91.09718741658209 us
Out[56]:
Text(0, 0.5, 'Measured signal, a.u.')
In [57]:
# measurement pulse parameters
meas_amp = 0.1
meas_samples = 1200
meas_sigma = 4
meas_risefall = 25

# creating measurement pulse
meas_pulse = pulse_lib.gaussian_square(duration=meas_samples, amp=meas_amp,
                                       sigma=meas_sigma, risefall=meas_risefall, 
                                       name='mymeasurepulse')
meas_pulse_qubit = meas_pulse(backend_devicespec.q[qubit].measure)

# create acquire pulse
acq_cmd=pulse.Acquire(duration=meas_samples)
acq_cmd_qubit = acq_cmd(backend_devicespec.q, backend_devicespec.mem)

# combined measure and acquire pulse
measure_and_acquire_qubit = meas_pulse_qubit | acq_cmd_qubit

# scalefactor for received data
scale_factor = 1e-10

8. Doing CPMG

In [58]:
# T2 experiment parameters
tau_us_min = 1
tau_us_max = 30
tau_step_us = 0.1
taus_us = np.arange(tau_us_min, tau_us_max, tau_step_us)
num_shots_per_point = 512
ncpmg = 10

# drive parameters
drive_power_1 = pi_amp/2
drive_power_2 = pi_amp
print(f"Total time ranges from {2.*ncpmg*taus_us[0]} to {2.*ncpmg*taus_us[-1]} us")
0.010703084664435272
0.021406169328870545
Total time ranges from 20.0 to 598.0000000000006 us
In [59]:
# create schedules for Ramsey experiment 
T2cpmg_schedules = []
T2cpmg_schedule_LOs = []

T2cpmg_pulse_pio2 = pulse_lib.gaussian(duration=drive_samples, amp=drive_power_1, 
                                sigma=drive_sigma, name='T2cpmg_pio2_pulse')
T2cpmg_pulse_pio2_qubit = T2cpmg_pulse_pio2(backend_devicespec.q[qubit].drive)
T2cpmg_pulse_pi = pulse_lib.gaussian(duration=drive_samples, amp=drive_power_2, 
                                sigma=drive_sigma, name='T2cpmg_pi_pulse')
T2cpmg_pulse_pi_qubit = T2cpmg_pulse_pi(backend_devicespec.q[qubit].drive)
thisLO = pulse.LoConfig({backend_devicespec.q[qubit].drive: precise_frequency_qubit_minus})
for ii, delay_time_us in enumerate(taus_us):
    # start an empty schedule with a label
    this_schedule = pulse.Schedule(name="T2cpmg delay = {} us".format(delay_time_us))
    this_schedule |= T2cpmg_pulse_pio2_qubit
    this_schedule |= (T2cpmg_pulse_pi_qubit << int(this_schedule.duration + 
                                               delay_time_us*1000/dt))
    for _ in range(ncpmg-1):
        this_schedule |= (T2cpmg_pulse_pi_qubit << int(this_schedule.duration + 
                                                   2*delay_time_us*1000/dt))
    this_schedule |= (T2cpmg_pulse_pio2_qubit << int(this_schedule.duration + 
                                               delay_time_us*1000/dt))
    
    this_schedule |= (measure_and_acquire_qubit << int(this_schedule.duration))
    
    T2cpmg_schedules.append(this_schedule)
    T2cpmg_schedule_LOs.append(thisLO)
In [60]:
T2cpmg_schedules[0].draw(channels_to_plot=[backend_devicespec.q[qubit].measure, 
                                           backend_devicespec.q[qubit].drive,
                                           #backend_devicespec.q[qubit].acquire,
                                         ],
                                         scaling=10.0)
Out[60]:
In [61]:
T2cpmg_experiment_qobj = assemble (T2cpmg_schedules, backend = backend, 
                            meas_level=1, meas_return='avg',
                            shots=num_shots_per_point,
                            schedule_los = T2cpmg_schedule_LOs
                                )
In [ ]:
job = backend.run(T2cpmg_experiment_qobj)
print(job.job_id())
job_monitor(job, monitor_async=True)
In [63]:
T2cpmgjob = backend.retrieve_job('5d2f6e1aca4ad70012795340')
T2cpmg_results = T2cpmgjob.result(timeout=3600)
In [64]:
plot_X = 2.*ncpmg*taus_us
plot_Y = []
for jj, delay_time_us in enumerate(taus_us):
    thisdelay_results = T2cpmg_results.get_memory(jj)*scale_factor
    plot_Y.append( thisdelay_results[qubit] )

plotter.plot(plot_X, plot_Y)
T2y_cpmg = plot_Y
T2x_cpmg = plot_X
In [65]:
from scipy.optimize import curve_fit

T2guess = backend.properties().qubits[qubit][1].value
fit_func2 = lambda x,A,B: (A*np.exp(-x/T2guess)+B)
#Fit the data
fitparams2, conv2 = curve_fit(fit_func2, plot_X,
                                      plot_Y,
                                      [-2.0,1.0])

print(f"T2 from backend = {T2guess} us")

plotter.scatter(plot_X, plot_Y)
plotter.plot(plot_X, fit_func2(plot_X, *fitparams2), color='black')
plotter.xlim(0, np.max(plot_X))
plotter.xlabel('Total time, ($\mu$s)', fontsize=20)
plotter.ylabel('Measured signal, a.u.', fontsize=20)
T2 from backend = 91.09718741658209 us
Out[65]:
Text(0, 0.5, 'Measured signal, a.u.')