Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3d Storage Cavity coupled to a Transmon #241

Merged
merged 13 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""
SELECTIVE PULSE POWER RABI
The sequence consists in playing the qubit pulse (x180_long) and measuring the state of the resonator
for different qubit pulse amplitudes.
The experimental results are then analyzed to find the qubit pulse amplitude for the chosen duration.

Prerequisites:
- Having found the resonance frequency of the resonator coupled to the qubit under study (resonator_spectroscopy).
- Having calibrated the IQ mixer connected to the qubit drive line (external mixer or Octave port)
- Having found the qubit frequency (qubit spectroscopy or time_rabi).
- Specifing the long pi pulse duration.
- Set the qubit frequency and desired pi pulse duration (x180_len_long) in the configuration.

Next steps before going to the next node:
- Update the qubit pulse amplitude (x180_amp_long) in the configuration.
"""

from qm.qua import *
from qm import QuantumMachinesManager
from qm import SimulationConfig
from configuration import *
from qualang_tools.results import progress_counter, fetching_tool
from qualang_tools.plot import interrupt_on_close
from qualang_tools.loops import from_array
import matplotlib.pyplot as plt

###################
# The QUA program #
###################

n_avg = 1000 # The number of averages
# Pulse amplitude sweep (as a pre-factor of the qubit pulse amplitude) - must be within [-2; 2)
a_min = 0
a_max = 1.0
n_a = 101
amplitudes = np.linspace(a_min, a_max, n_a)

with program() as power_rabi:
n = declare(int) # QUA variable for the averaging loop
a = declare(fixed) # QUA variable for the qubit drive amplitude pre-factor
I = declare(fixed) # QUA variable for the measured 'I' quadrature
Q = declare(fixed) # QUA variable for the measured 'Q' quadrature
I_st = declare_stream() # Stream for the 'I' quadrature
Q_st = declare_stream() # Stream for the 'Q' quadrature
n_st = declare_stream() # Stream for the averaging iteration 'n'

with for_(n, 0, n < n_avg, n + 1): # QUA for_ loop for averaging
with for_(*from_array(a, amplitudes)): # QUA for_ loop for sweeping the pulse amplitude pre-factor
# Play the qubit pulse with a variable amplitude (pre-factor to the pulse amplitude defined in the config)
play("x180_long" * amp(a), "qubit")
# Align the two elements to measure after playing the qubit pulse.
align("qubit", "resonator")
# Measure the state of the resonator
# The integration weights have changed to maximize the SNR after having calibrated the IQ blobs.
measure(
"readout",
"resonator",
None,
dual_demod.full("rotated_cos", "out1", "rotated_sin", "out2", I),
dual_demod.full("rotated_minus_sin", "out1", "rotated_cos", "out2", Q),
)
# Wait for the qubit to decay to the ground state
wait(thermalization_time * u.ns, "resonator")
# Save the 'I' & 'Q' quadratures to their respective streams
save(I, I_st)
save(Q, Q_st)
# Save the averaging iteration to get the progress bar
save(n, n_st)

with stream_processing():
# Cast the data into a 1D vector, average the 1D vectors together and store the results on the OPX processor
I_st.buffer(len(amplitudes)).average().save("I")
Q_st.buffer(len(amplitudes)).average().save("Q")
n_st.save("iteration")

#####################################
# Open Communication with the QOP #
#####################################
qmm = QuantumMachinesManager(host=qop_ip, port=qop_port, cluster_name=cluster_name, octave=octave_config)

###########################
# Run or Simulate Program #
###########################
simulate = True

if simulate:
# Simulates the QUA program for the specified duration
simulation_config = SimulationConfig(duration=10_000) # In clock cycles = 4ns
job = qmm.simulate(config, power_rabi, simulation_config)
job.get_simulated_samples().con1.plot()
plt.show()

else:
# Open the quantum machine
qm = qmm.open_qm(config)
# Send the QUA program to the OPX, which compiles and executes it
job = qm.execute(power_rabi)
# Get results from QUA program
results = fetching_tool(job, data_list=["I", "Q", "iteration"], mode="live")
# Live plotting
fig = plt.figure()
interrupt_on_close(fig, job) # Interrupts the job when closing the figure
while results.is_processing():
# Fetch results
I, Q, iteration = results.fetch_all()
# Convert the results into Volts
I, Q = u.demod2volts(I, readout_len), u.demod2volts(Q, readout_len)
# Progress bar
progress_counter(iteration, n_avg, start_time=results.get_start_time())
# Plot results
plt.suptitle("Power Rabi")
plt.subplot(211)
plt.cla()
plt.plot(amplitudes * x180_amp_long, I, ".")
plt.ylabel("I quadrature [V]")
plt.subplot(212)
plt.cla()
plt.plot(amplitudes * x180_amp_long, Q, ".")
plt.xlabel("Rabi pulse amplitude [V]")
plt.ylabel("Q quadrature [V]")
plt.pause(1)
plt.tight_layout()
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"""
STORAGE CAVITY SPECTROSCOPY
This sequence involves sending a displacement pulse to the storage, followed by a
selective pi-pulse (x180_long) to qubit and measure across various storage drive intermediate dfs.

The data is post-processed to determine the storage resonance frequency, which can then be used to adjust
the storage intermediate frequency in the configuration under "storage_IF".

Note that the pi-pulse should be long enough such that it will apply a pi-pulse only when the storage is at Fock state n=0.


Prerequisites:
- Identification of the resonator's resonance frequency when coupled to the qubit in question (referred to as "resonator_spectroscopy").
- Calibration of the IQ mixer connected to the resonator drive line (whether it's an external mixer or an Octave port).
- Identification of the qubit's resonance frequency (referred to as "qubit_spectroscopy").
- Calibration of the IQ mixer connected to the qubit drive line (whether it's an external mixer or an Octave port).
- Configuration of the x180_long pulse amplitude and duration to apply a selective pi-pulse on the qubit (n=0).
- Specification of the expected storage_thermalization_time of the storage in the configuration.

Before proceeding to the next node:
- Update the storage frequency, labeled as "storage_IF", in the configuration.
"""

from qm.qua import *
from qm import QuantumMachinesManager
from qm import SimulationConfig
from configuration import *
from qualang_tools.results import progress_counter, fetching_tool
from qualang_tools.plot import interrupt_on_close
from qualang_tools.loops import from_array
import matplotlib.pyplot as plt
import macros as macros


###################
# The QUA program #
###################
n_avg = 1000 # The number of averages
# Storage detuning sweep
center = 100 * u.MHz
span = 0.5 * u.MHz
df = 1 * u.kHz
dfs = np.arange(-span, +span + 0.1, df)


with program() as storage_spec:
n = declare(int) # QUA variable for the averaging loop
df = declare(int) # QUA variable for the qubit frequency
I = declare(fixed) # QUA variable for the measured 'I' quadrature
Q = declare(fixed) # QUA variable for the measured 'Q' quadrature
state = declare(bool)
I_st = declare_stream() # Stream for the 'I' quadrature
Q_st = declare_stream() # Stream for the 'Q' quadrature
n_st = declare_stream() # Stream for the averaging iteration 'n'
state_st = declare_stream()

with for_(n, 0, n < n_avg, n + 1):
with for_(*from_array(df, dfs)):
# Update the frequency of the digital oscillator linked to the storage element
update_frequency("storage", df + center)
# Play the cw pulse to the storage
play("cw", "storage")
align("qubit", "storage")
# Align the two elements to measure after playing the storage pulse.
# Measure the storage state by applying a selective pi-pulse to the qubit and measure the qubit state
play("x180_long", "qubit")
align("qubit", "resonator")
state, I, Q = macros.readout_macro(threshold=ge_threshold, state=state, I=I, Q=Q)

# Wait for the storage to decay to the ground state
align("storage", "resonator")
wait(storage_thermalization_time * u.ns, "storage")
# Save the 'I' & 'Q' quadratures to their respective streams
save(I, I_st)
save(Q, Q_st)
save(state, state_st)
# Save the averaging iteration to get the progress bar
save(n, n_st)

with stream_processing():
# Cast the data into a 1D vector, average the 1D vectors together and store the results on the OPX processor
I_st.buffer(len(dfs)).average().save("I")
Q_st.buffer(len(dfs)).average().save("Q")
state_st.boolean_to_int().buffer(len(dfs)).average().save("state")
n_st.save("iteration")

#####################################
# Open Communication with the QOP #
#####################################
qmm = QuantumMachinesManager(host=qop_ip, port=qop_port, cluster_name=cluster_name, octave=octave_config)

###########################
# Run or Simulate Program #
###########################
simulate = True

if simulate:
# Simulates the QUA program for the specified duration
simulation_config = SimulationConfig(duration=10_000) # In clock cycles = 4ns
job = qmm.simulate(config, storage_spec, simulation_config)
job.get_simulated_samples().con1.plot()
plt.show()

else:
# Open the quantum machine
qm = qmm.open_qm(config)
# Send the QUA program to the OPX, which compiles and executes it
job = qm.execute(storage_spec)
# Get results from QUA program
results = fetching_tool(job, data_list=["I", "Q", "state", "iteration"], mode="live")
# Live plotting
fig1, ax1 = plt.subplots(2, 1)
fig2, ax2 = plt.subplots(1, 1)
interrupt_on_close(fig1, job) # Interrupts the job when closing the figure
while results.is_processing():
# Fetch results
I, Q, state, iteration = results.fetch_all()
# Convert results into Volts
S = u.demod2volts(I + 1j * Q, readout_len)
R = np.abs(S) # Amplitude
phase = np.angle(S) # Phase
# Progress bar
progress_counter(iteration, n_avg, start_time=results.get_start_time())
# Plot results
fig1.suptitle(f"Storage spectroscopy - LO = {storage_LO / u.GHz} GHz")
ax1[0].clear()
ax1[1].clear()
ax1[0].cla()
ax1[0].plot((dfs + center) / u.MHz, R, ".")
ax1[0].set_xlabel("Storage intermediate frequency [MHz]")
ax1[0].set_ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
ax1[1].cla()
ax1[1].plot((dfs + center) / u.MHz, phase, ".")
ax1[1].set_xlabel("Storage intermediate frequency [MHz]")
ax1[1].set_ylabel("Phase [rad]")
plt.pause(1)
plt.tight_layout()

ax2.clear()
ax2.plot((dfs + center) / u.MHz, state, ".")
ax2.set_ylabel(r"$P_e$")
ax2.set_xlabel("Storage intermediate frequency [MHz]")
ax2.set_ylim(0, 1)
Loading
Loading