Skip to content

Commit

Permalink
Feature/two fixed transmons qua (#250)
Browse files Browse the repository at this point in the history
* init

* rename_renumber_files_tree

* adopted to the simple qua config

* Working in 08_ramsey_chevron

* finished_08_ramsey_chevron

* ro_freq_opt

* 09b_ro_opt_amp_finished

* up_to_ro_weights

* iq_blobs_ready

* coherent_done

* stark_drag_done

* revised for Two-Fixed-Transmons

* added a parameter for phase correction

* end of allxy, RB, interleaved BR

* removed a line

* added CNOT

* end of allxy, RB, interleaved BR

* tested until 13 -> WIP 14_drag

* all-cross-resonance_runs

* opx_configuration_pass_execution

* configuration_lf_fem_validated

* configuration_with_octave - opx and lf

* applied black

* applied black

* applied black

---------

Co-authored-by: HiroQM <[email protected]>
  • Loading branch information
KevinAVR and HiroQM authored Oct 4, 2024
1 parent b6e0f37 commit f49327c
Show file tree
Hide file tree
Showing 119 changed files with 11,845 additions and 422 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
A simple sandbox to showcase different QUA functionalities during the installation.
"""

from qm.qua import *
from qm import QuantumMachinesManager, SimulationConfig
from configuration_lf_fem_and_octave import *

###################
# QUA Program #
###################

with program() as PROGRAM:

play("cw", "rr1")
play("cw", "q2_xy")

#####################################
# 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 = False

if simulate:
# Simulates the QUA program for the specified duration
simulation_config = SimulationConfig(duration=1_000) # In clock cycles = 4ns
# Simulate blocks python until the simulation is done

job = qmm.simulate(config, PROGRAM, simulation_config)
# Plot the simulated samples
job.get_simulated_samples().con1.plot()

else:
# Open a quantum machine to execute the QUA program
qm = qmm.open_qm(config)

# Send the QUA program to the OPX, which compiles and executes it - Execute does not block python!
job = qm.execute(PROGRAM)
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
"""
TIME OF FLIGHT
This sequence involves sending a readout pulse and capturing the raw ADC traces.
The data undergoes post-processing to calibrate three distinct parameters:
- Time of Flight: This represents the internal processing time and the propagation delay of the readout pulse.
Its value can be adjusted in the configuration under "time_of_flight".
This value is utilized to offset the acquisition window relative to when the readout pulse is dispatched.
- Analog Inputs Offset: Due to minor impedance mismatches, the signals captured by the OPX might exhibit slight offsets.
These can be rectified in the configuration at: config/controllers/"con1"/analog_inputs, enhancing the demodulation process.
- Analog Inputs Gain: If a signal is constrained by digitization or if it saturates the ADC,
the variable gain of the OPX analog input can be modified to fit the signal within the ADC range of +/-0.5V.
This gain, ranging from -12 dB to 20 dB, can also be adjusted in the configuration at: config/controllers/"con1"/analog_inputs.
"""

from qm.qua import *
from qm import QuantumMachinesManager, SimulationConfig
from qm import SimulationConfig

from configuration_mw_fem import *
import matplotlib.pyplot as plt
from scipy.signal import savgol_filter
from qualang_tools.results.data_handler import DataHandler

##################
# Parameters #
##################

n_avg = 5000 # The number of averages
save_data_dict = {
"n_avg": n_avg,
"config": config,
}

###################
# QUA Program #
###################

with program() as PROGRAM:
n = declare(int) # QUA variable for the averaging loop
adc_st = declare_stream(adc_trace=True) # The stream to store the raw ADC trace

# # OPTIONAL to check time-of-flight at arbitrary frequency
# update_frequency('q1_rr', 100e6)

with for_(n, 0, n < n_avg, n + 1):
# Reset the phase of the digital oscillator associated to the resonator element. Needed to average the cosine signal.
reset_if_phase("rr1")
reset_if_phase("rr2")
# Sends the readout pulse and stores the raw ADC traces in the stream called "adc_st"
measure("readout", "rr1", adc_st)
measure("readout", "rr2", None)
# Wait for the resonators to empty
wait(depletion_time * u.ns, "rr1")
wait(depletion_time * u.ns, "rr2")

with stream_processing():
# Will save average:
adc_st.input1().average().save("adc1")
# # Will save only last run:
adc_st.input1().save("adc1_single_run")


#####################################
# 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 = False

if simulate:
# Simulates the QUA program for the specified duration
simulation_config = SimulationConfig(duration=1_000) # In clock cycles = 4ns
# Simulate blocks python until the simulation is done
job = qmm.simulate(config, PROGRAM, simulation_config)
# Plot the simulated samples
job.get_simulated_samples().con1.plot()

else:
try:
# Open a quantum machine to execute the QUA program
qm = qmm.open_qm(config)
# Send the QUA program to the OPX, which compiles and executes it
job = qm.execute(PROGRAM)
# Creates a result handle to fetch data from the OPX
res_handles = job.result_handles
# Waits (blocks the Python console) until all results have been acquired
res_handles.wait_for_all_values()
# Fetch the raw ADC traces and convert them into Volts
adc1 = u.raw2volts(res_handles.get("adc1").fetch_all())
adc1_single_run = u.raw2volts(res_handles.get("adc1_single_run").fetch_all())

save_data_dict["adc1"] = adc1
save_data_dict["adc1_single"] = adc1_single_run

# Derive the average values
adc1_mean = np.mean(adc1)
# Remove the average values
adc1_unbiased = adc1 - np.mean(adc1)
# Filter the data to get the pulse arrival time
signal = savgol_filter(np.abs(adc1_unbiased), 11, 3)
# Detect the arrival of the readout signal
th = (np.mean(signal[:100]) + np.mean(signal[:-100])) / 2
delay = np.where(signal > th)[0][0]

# Plot data for each rl
fig = plt.figure(figsize=(12, 6))

# Plot for single run
plt.subplot(121)
plt.title("Single run")
plt.plot(adc1_single_run.real, label="Input 1 real")
plt.plot(adc1_single_run.imag, label="Input 1 image")
plt.axhline(y=0)
plt.xlabel("Time [ns]")
plt.ylabel("Signal amplitude [V]")
plt.legend()

# Plot for averaged run
plt.subplot(122)
plt.title("Averaged run")
plt.plot(adc1.real, label="Input 1 real")
plt.plot(adc1.imag, label="Input 1 imag")
plt.axhline(y=0)
plt.xlabel("Time [ns]")
plt.legend()
plt.tight_layout()

# Save results
script_name = Path(__file__).name
data_handler = DataHandler(root_data_folder=save_dir)
save_data_dict.update({"fig_live": fig})
data_handler.additional_files = {script_name: script_name, **default_additional_files}
data_handler.save_data(data=save_data_dict, name="time_of_flight")

except Exception as e:
print(f"An exception occurred: {e}")

finally:
qm.close()
print("Experiment QM is now closed")
plt.show()
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"""
RESONATOR SPECTROSCOPY INDIVIDUAL RESONATORS
This sequence involves measuring the resonator by sending a readout pulse and demodulating the signals to extract the
'I' and 'Q' quadratures across varying readout intermediate frequencies.
The data is then post-processed to determine the resonator resonance frequency.
This frequency can be used to update the readout intermediate frequency in the configuration under "resonator_IF".
Prerequisites:
- Ensure calibration of the time of flight, offsets, and gains (referenced as "time_of_flight").
- Calibrate the IQ mixer connected to the readout line (whether it's an external mixer or an Octave port).
- Define the readout pulse amplitude and duration in the configuration.
- Specify the expected resonator depletion time in the configuration.
Before proceeding to the next node:
- Update the readout frequency, labeled as "resonator_IF_q1" and "resonator_IF_q2", in the configuration.
"""

from qm.qua import *
from qm import QuantumMachinesManager, SimulationConfig
from configuration_mw_fem import *
from qualang_tools.results import fetching_tool
from qualang_tools.loops import from_array
import matplotlib.pyplot as plt
from scipy import signal
from qualang_tools.results.data_handler import DataHandler

##################
# Parameters #
##################

resonator = "rr1"
resonator_LO = resonator_LO

n_avg = 200 # The number of averages
frequencies = {
"rr1": np.arange(-50e6, +50e6, 100e3),
"rr2": np.arange(-50e6, +50e6, 100e3),
}

save_data_dict = {
"resonator": resonator,
"resonator_LO": resonator_LO,
"frequencies": frequencies,
"n_avg": n_avg,
"config": config,
}

###################
# QUA Program #
###################

with program() as PROGRAM:
n = declare(int) # QUA variable for the averaging loop
f = declare(int) # QUA variable for the readout frequency --> Hz int 32 up to 2^32
I = declare(fixed) # QUA variable for the measured 'I' quadrature --> signed 4.28 [-8, 8)
Q = declare(fixed) # QUA variable for the measured 'Q' quadrature --> signed 4.28 [-8, 8)
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(f, frequencies[resonator])): # QUA for_ loop for sweeping the frequency
# Update the frequency of the digital oscillator linked to the resonator element
update_frequency(resonator, f)
# Measure the resonator (send a readout pulse and demodulate the signals to get the 'I' & 'Q' quadratures)
measure(
"readout" * amp(1),
resonator,
None,
dual_demod.full("cos", "sin", I),
dual_demod.full("minus_sin", "cos", Q),
)
# Wait for the resonator to deplete
wait(depletion_time * u.ns, resonator)
# Save the 'I' & 'Q' quadratures to their respective streams
save(I, I_st)
save(Q, Q_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(frequencies[resonator])).average().save("I")
Q_st.buffer(len(frequencies[resonator])).average().save("Q")

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

#######################
# Simulate or execute #
#######################
simulate = False

if simulate:
# Simulates the QUA program for the specified duration
simulation_config = SimulationConfig(duration=1_000) # In clock cycles = 4ns
# Simulate blocks python until the simulation is done
job = qmm.simulate(config, PROGRAM, simulation_config)
# Plot the simulated samples
job.get_simulated_samples().con1.plot()
plt.show(block=False)
else:
try:
# Open a quantum machine to execute the QUA program
qm = qmm.open_qm(config)
# Send the QUA program to the OPX, which compiles and executes it
job = qm.execute(PROGRAM)
# Get results from QUA program
results = fetching_tool(job, data_list=["I", "Q"]) # this one already waits for all values
# plotting
fig = plt.figure()
I, Q = results.fetch_all()
# Convert results into Volts
S = I + 1j * Q
R = np.abs(S) # Amplitude
phase = np.angle(S) # Phase
plt.suptitle(f"Resonator spectroscopy for {resonator} - LO = {resonator_LO / u.GHz} GHz")
ax1 = plt.subplot(211)
plt.plot((frequencies[resonator]) / u.MHz, R, ".")
plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
plt.subplot(212, sharex=ax1)
plt.plot((frequencies[resonator]) / u.MHz, signal.detrend(np.unwrap(phase)), ".")
plt.xlabel("Intermediate frequency [MHz]")
plt.ylabel("Phase [rad]")
plt.tight_layout()

save_data_dict[resonator + "_I"] = I
save_data_dict[resonator + "_Q"] = Q

# Save results
script_name = Path(__file__).name
data_handler = DataHandler(root_data_folder=save_dir)
save_data_dict.update({"fig_live": fig})
data_handler.additional_files = {script_name: script_name, **default_additional_files}
data_handler.save_data(data=save_data_dict, name="resonator_spectroscopy_single")

except Exception as e:
print(f"An exception occurred: {e}")

finally:
qm.close()
print("Experiment QM is now closed")
plt.show(block=True)
Loading

0 comments on commit f49327c

Please sign in to comment.