# Rabi Drive & Ramsey Experiment (by nick brønn)

Cahyati Supriyati Sangaji (My Note)

# Treat the transmon as a qubit for simplicity

Then we can describe the dynamics of the qubit with the Pauli Matrices:

They obey the commutator relations

On its own, the qubit Hamiltonian is

Ground state of the qubit (|0⟩): points in the +𝑧̂ direction of the Bloch sphere
Excited state of the qubit (|1⟩): points in the −𝑧̂ direction of the Bloch sphere

from qiskit.quantum_info import Statevectorfrom qiskit.visualization import plot_bloch_multivectorexcited = Statevector.from_int(1, 2)plot_bloch_multivector(excited.data)

The Pauli matrices also let us define raising and lowering operators

They raise and lower qubit states

# Electric Dipole Interaction

The qubit behaves as an electric dipole

The drive behaves as an electric field

The drive Hamiltonian is then

And now, some math…

# Rotating Wave Approximation

Move the Hamiltonian to the interaction picture

with

Calculate the operator terms

The transformed Hamiltonian is

# Rotating Wave Approximation

Transform Hamiltonian back to the Schrödinger picture

And the total qubit Hamiltonian is

# Qubit Drive Example

In a similar calculation to earlier, the effective Hamiltonian is

# Import Necessary Libraries

from qiskit.tools.jupyter import *from qiskit import IBMQIBMQ.save_account('<Your Token>')IBMQ.load_account()provider = IBMQ.get_provider(hub='ibm-q', group='open', project='main')backend = provider.get_backend('ibmq_armonk')

# Verify Backend is Pulse-enabled

backend_config = backend.configuration()assert backend_config.open_pulse, "Backend doesn't support Pulse"

# Qiskit Pulse: On-resonant Drive (Rabi)

Take care of some other things

dt = backend_config.dtprint(f"Sampling time: {dt*1e9} ns")backend_defaults = backend.defaults()

Out:

Sampling time: 0.2222222222222222 ns

# Qiskit Pulse: On-resonant Drive (Rabi)

import numpy as np# unit conversion factors -> all backend properties # returned in SI (Hz, sec, etc)GHz = 1.0e9 # GigahertzMHz = 1.0e6 # Megahertzus = 1.0e-6 # Microsecondsns = 1.0e-9 # Nanoseconds# We will find the qubit frequency for the following qubit.qubit = 0# The Rabi sweep will be at the given qubit frequency.# The default frequency is given in Hzcenter_frequency_Hz = backend_defaults.qubit_freq_est[qubit]        # warning: this will change in a future releaseprint(f"Qubit {qubit} has an estimated frequency of {center_frequency_Hz / GHz} GHz.")

Out:

Qubit 0 has an estimated frequency of 4.974445862780936 GHz.

# Qiskit Pulse: On-resonant Drive (Rabi)

# This is where we access all of our Pulse features!from qiskit import pulse, assemble            from qiskit.pulse import Play # This Pulse module helps us build sampled  # pulses for common pulse shapesfrom qiskit.pulse import pulse_lib### Collect the necessary channelsdrive_chan = pulse.DriveChannel(qubit)meas_chan = pulse.MeasureChannel(qubit)acq_chan = pulse.AcquireChannel(qubit)inst_sched_map = backend_defaults.instruction_schedule_mapmeasure = inst_sched_map.get('measure', qubits=)

# Qiskit Pulse: On-resonant Drive (Rabi)

# Rabi experiment parameters# Drive amplitude values to iterate over: # 50 amplitudes evenly spaced from 0 to 0.75num_rabi_points = 50drive_amp_min = 0drive_amp_max = 0.75drive_amps = np.linspace(drive_amp_min, drive_amp_max, num_rabi_points)# drive waveforms mush be in units of 16drive_sigma = 80 # in dtdrive_samples = 8*drive_sigma # in dt

# Qiskit Pulse: On-resonant Drive (Rabi)

# Build the Rabi experiments:# A drive pulse at the qubit frequency, followed by a measurement,# where we vary the drive amplitude each time.rabi_schedules = []for drive_amp in drive_amps:    rabi_pulse = pulse_lib.gaussian(duration=drive_samples, amp=drive_amp,                                     sigma=drive_sigma, name=f"Rabi drive amplitude = {drive_amp}")    this_schedule = pulse.Schedule(name=f"Rabi drive amplitude = {drive_amp}")    this_schedule += Play(rabi_pulse, drive_chan)        # The left shift << is special syntax meaning to     # shift the start time of the schedule by some duration    this_schedule += measure << this_schedule.duration    rabi_schedules.append(this_schedule)rabi_schedules[-1].draw(label=True, scaling=1.0)

# Qiskit Pulse: On-resonant Drive (Rabi)

# assemble the schedules into a Qobjnum_shots_per_point = 1024rabi_experiment_program = assemble(rabi_schedules,                                   backend=backend,                                   meas_level=1,                                   meas_return='avg',                                   shots=num_shots_per_point,                                   schedule_los=[{drive_chan:                                                center_frequency_Hz}]                                                * num_rabi_points)

Sintax:

# RUN the job on a real devicejob = backend.run(rabi_experiment_program)print(job.job_id())from qiskit.tools.monitor import job_monitorjob_monitor(job)# OR retreive result from previous runjob = backend.retrieve_job("<Job ID>")

Out:

<Your Job ID>Job Status: job has successfully run

# Qiskit Pulse: On-resonant Drive (Rabi)

rabi_results = job.result()import matplotlib.pyplot as pltplt.style.use('dark_background')scale_factor = 1e-14# center data around 0def baseline_remove(values):    return np.array(values) - np.mean(values)

# Qiskit Pulse: On-resonant Drive (Rabi)

rabi_values = []for i in range(num_rabi_points):    # Get the results for qubit from the ith experiment    rabi_values.append(rabi_results.get_memory(i)[qubit]*scale_factor)rabi_values = np.real(baseline_remove(rabi_values))plt.xlabel("Drive amp [a.u.]")plt.ylabel("Measured signal [a.u.]")plt.scatter(drive_amps, rabi_values, color='white') # plot real part of Rabi valuesplt.show()

# Qiskit Pulse: On-resonant Drive (Rabi)

Define Rabi curve-fitting function

from scipy.optimize import curve_fitdef fit_function(x_values, y_values, function, init_params):    fitparams, conv = curve_fit(function, x_values, y_values, init_params)    y_fit = function(x_values, *fitparams)        return fitparams, y_fitfit_params, y_fit = fit_function(drive_amps,                                 rabi_values,                                  lambda x, A, B, drive_period, phi:                                  (A*np.cos(2*np.pi*x/drive_period -                                   phi) + B),                                 [10, 0.1, 0.6, 0])

# Qiskit Pulse: On-resonant Drive (Rabi)

plt.scatter(drive_amps, rabi_values, color='white')plt.plot(drive_amps, y_fit, color='red')drive_period = fit_params # get period of rabi oscillationplt.axvline(drive_period/2, color='red', linestyle='--')plt.axvline(drive_period, color='red', linestyle='--')plt.annotate("", xy=(drive_period, 0), xytext=(drive_period/2,0), arrowprops=dict(arrowstyle="<->", color='red'))plt.xlabel("Drive amp [a.u.]", fontsize=15)plt.ylabel("Measured signal [a.u.]", fontsize=15)plt.show()

# Save 𝜋/2 pulse for later

pi_amp = abs(drive_period / 2)print(f"Pi Amplitude = {pi_amp}")

Out:

Pi Amplitude = 0.9344596890678515

Sintax:

# Drive parameters# The drive amplitude for pi/2 is simply half the amplitude of the pi pulsedrive_amp = pi_amp / 2# x_90 is a concise way to say pi_over_2; i.e., an X rotation of 90 degreesx90_pulse = pulse_lib.gaussian(duration=drive_samples,                               amp=drive_amp,                                sigma=drive_sigma,                               name='x90_pulse')

# Qiskit Pulse: Off-resonant Drive (Ramsey)

# Ramsey experiment parameterstime_max_us = 1.8time_step_us = 0.025times_us = np.arange(0.1, time_max_us, time_step_us)# Convert to units of dtdelay_times_dt = times_us * us / dt# create schedules for Ramsey experiment ramsey_schedules = []for delay in delay_times_dt:    this_schedule = pulse.Schedule(name=f"Ramsey delay = {delay * dt / us} us")    this_schedule += Play(x90_pulse, drive_chan)    this_schedule += Play(x90_pulse, drive_chan) << this_schedule.duration + int(delay)    this_schedule += measure << this_schedule.duration    ramsey_schedules.append(this_schedule)ramsey_schedules[-1].draw(label=True, scaling=1.0)

# Qiskit Pulse: Off-resonant Drive (Ramsey)

# Execution settingsnum_shots = 256detuning_MHz = 2 ramsey_frequency = round(center_frequency_Hz + detuning_MHz * MHz, 6) # need ramsey freq in Hzramsey_program = assemble(ramsey_schedules,                             backend=backend,                             meas_level=1,                             meas_return='avg',                             shots=num_shots,                             schedule_los=[{drive_chan: ramsey_frequency}]*len(ramsey_schedules)                            )# RUN the job on a real devicejob = backend.run(ramsey_program)print(job.job_id())from qiskit.tools.monitor import job_monitorjob_monitor(job)# OR retreive job from previous runjob = backend.retrieve_job('<Job ID>')

Out:

<Your Job>Job Status: job has successfully run

# Qiskit Pulse: Off-resonant Drive (Ramsey)

Ramsey curve-fitting function

ramsey_results = job.result()ramsey_values = []for i in range(len(times_us)):    ramsey_values.append(ramsey_results.get_memory(i)[qubit]*scale_factor)fit_params, y_fit = fit_function(times_us, np.real(ramsey_values),                                 lambda x, A, del_f_MHz, C, B: (                                          A * np.cos(2*np.pi*                                           del_f_MHz*x - C) + B                                         ),                                 [5, 1./0.4, 0, 0.25]                                )

# Qiskit Pulse: Off-resonant Drive (Ramsey)

# Off-resonance component_, del_f_MHz, _, _, = fit_params # freq is MHz since times in usplt.scatter(times_us, np.real(ramsey_values), color='white')plt.plot(times_us, y_fit, color='red', label=f"df = {del_f_MHz:.2f} MHz")plt.xlim(0, np.max(times_us))plt.xlabel('Delay between X90 pulses [$\mu$s]', fontsize=15)plt.ylabel('Measured Signal [a.u.]', fontsize=15)plt.title('Ramsey Experiment', fontsize=15)plt.legend()plt.show()

References:

Qiskit (Global Summer School), Introduction to Quantum Computing and Quantum Hardware — Lab 6.