Skip to content

Qubit spectroscopy

Glad to have you here! In this section, we'll uncover:

  1. Parametric Waveform Generation:
    • Intermediate Frequency Update
  2. Real-Time Processing:
    • Processing of Acquired Waveforms
  3. Timing in QUA

Eager to dive in?

The QUA program

The first parametric waveform adjustment we'll discuss is updating the frequency of the oscillator associated with qubit. The scanning of the signal driving the qubit enables performing spectroscopy.

qubit_spectroscopy.py
# Go to line 33
import qm.qua as qua
from qm import QuantumMachinesManager
from OPX1000configuration import config, qop_ip, cluster_name, cloud_username, cloud_password # or OPXplusconfiguration, depending on your hardware
import numpy as np
from scipy import signal
from qualang_tools.results import progress_counter, fetching_tool
from qualang_tools.plot import interrupt_on_close
from qm import SimulationConfig, LoopbackInterface
from qualang_tools.loops import from_array
import warnings

warnings.filterwarnings("ignore")

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

n_avg = 2
frequencies = np.arange(30 * u.MHz, 70 * u.MHz + 0.1, 10 * u.MHz) 

with qua.program() as qubit_spec:
    n = qua.declare(int)
    f = qua.declare(int) 
    I = qua.declare(qua.fixed) 
    Q = qua.declare(qua.fixed)
    I_st = qua.declare_stream() 
    Q_st = qua.declare_stream() 
    n_st = qua.declare_stream() 

    with qua.for_(n, 0, n < n_avg, n + 1):
        with qua.for_(*from_array(f, frequencies)):
            qua.update_frequency("qubit", f)  # (1)!
            qua.play("saturation", "qubit")
            qua.align("qubit", "resonator")
            qua.measure(
                "readout",
                "resonator",
                None,
                qua.dual_demod.full("cos", "sin", I),
                qua.dual_demod.full("minus_sin", "cos", Q),
            )
            qua.wait(thermalization_time // 4, "resonator")
            qua.save(I, I_st)
            qua.save(Q, Q_st)
        qua.save(n, n_st)

    with qua.stream_processing():
        I_st.buffer(len(frequencies)).average().save("I")
        Q_st.buffer(len(frequencies)).average().save("Q")
        n_st.save("iteration")

#####################################
#  Open Communication with the QOP  #
#####################################

simulate = True
simulation_in_cloud = True

if simulate: 

    if simulation_in_cloud:
        client = QmSaas(email=cloud_username, password=cloud_password)
        instance = client.simulator(QoPVersion.v3_2_0)
        instance.spawn()
        qmm = QuantumMachinesManager(host=instance.host,
                                    port=instance.port,
                                    connection_headers=instance.default_connection_headers)
    else:
        qmm = QuantumMachinesManager(host=qop_ip, 
                                    cluster_name=cluster_name)

    simulation_config = SimulationConfig(duration=1_000) # duration is in units of clock cycles, i.e., 4 nanoseconds
    job = qmm.simulate(config, qubit_spec, simulation_config)
    job.get_simulated_samples().con1.plot()
    if simulation_in_cloud:
        instance.close()
    plt.show()

else:

    qm = qmm.open_qm(config)

    job = qm.execute(qubit_spec)

    results = fetching_tool(job, data_list=["I", "Q", "iteration"], mode="live")
    fig = plt.figure()
    interrupt_on_close(fig, job)
    while results.is_processing():
        I, Q, iteration = results.fetch_all()
        S = u.demod2volts(I + 1j * Q, readout_len)
        R = np.abs(S)
        phase = np.angle(S)
        progress_counter(iteration, n_avg, start_time=results.get_start_time())
        plt.suptitle("Qubit spectroscopy")
        plt.subplot(211)
        plt.cla()
        plt.plot(frequencies / u.MHz, R, ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
        plt.subplot(212)
        plt.cla()
        plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel("Phase [rad]")
        plt.pause(1)
        plt.tight_layout()

plt.figure()
results = fetching_tool(job, data_list=["I", "Q", "iteration"])
I, Q, iteration = results.fetch_all()
S = u.demod2volts(I + 1j * Q, readout_len)
R = np.abs(S)
phase = np.angle(S)
plt.suptitle("Qubit spectroscopy")
plt.subplot(211)
plt.plot(frequencies / u.MHz, R, ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
plt.subplot(212)
plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel("Phase [rad]")
plt.tight_layout()
plt.show()
  1. The update_frequency('qubit', f) function in QUA is translated by the compiler into PPU instructions, which modify the oscillator's frequency linked to qubit. The oscillator's frequency is determined by the QUA variable f, which the PPU can adjust in real-time. In this example, f changes within with qua.for_() at line 30..

In line 37 (below), the final arguments of measure(...) instruct the PPU to dual demodulate the signal in real-time. This processed data, ready in tens of nanoseconds, is saved to I and Q QUA variables on the PPU. Demodulation and measure() helps retrieve information from your quantum device.

qubit_spectroscopy.py
# Go to lines 37-43
import qm.qua as qua
from qm import QuantumMachinesManager
from OPX1000configuration import config, qop_ip, cluster_name, cloud_username, cloud_password # or OPXplusconfiguration, depending on your hardware
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
from qualang_tools.results import progress_counter, fetching_tool
from qualang_tools.plot import interrupt_on_close
from qm import SimulationConfig, LoopbackInterface
from qualang_tools.loops import from_array
import warnings

warnings.filterwarnings("ignore")

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

n_avg = 2
frequencies = np.arange(30 * u.MHz, 70 * u.MHz + 0.1, 10 * u.MHz) 

with qua.program() as qubit_spec:
    n = qua.declare(int)
    f = qua.declare(int) 
    I = qua.declare(qua.fixed) 
    Q = qua.declare(qua.fixed)
    I_st = qua.declare_stream() 
    Q_st = qua.declare_stream() 
    n_st = qua.declare_stream() 

    with qua.for_(n, 0, n < n_avg, n + 1):
        with qua.for_(*from_array(f, frequencies)):
            qua.update_frequency("qubit", f)
            qua.play("saturation", "qubit")
            qua.align("qubit", "resonator")
            qua.measure(
                "readout",
                "resonator",
                None,
                qua.dual_demod.full("cos", "sin", I),  # (1)!
                qua.dual_demod.full("minus_sin", "cos", Q),
            )
            qua.wait(thermalization_time // 4, "resonator")
            qua.save(I, I_st)
            qua.save(Q, Q_st)
        qua.save(n, n_st)

    with qua.stream_processing():
        I_st.buffer(len(frequencies)).average().save("I")
        Q_st.buffer(len(frequencies)).average().save("Q")
        n_st.save("iteration")

#####################################
#  Open Communication with the QOP  #
#####################################

simulate = True
simulation_in_cloud = True

if simulate: 

    if simulation_in_cloud:
        client = QmSaas(email=cloud_username, password=cloud_password)
        instance = client.simulator(QoPVersion.v3_2_0)
        instance.spawn()
        qmm = QuantumMachinesManager(host=instance.host,
                                    port=instance.port,
                                    connection_headers=instance.default_connection_headers)
    else:
        qmm = QuantumMachinesManager(host=qop_ip, 
                                    cluster_name=cluster_name)

    simulation_config = SimulationConfig(duration=1_000) # duration is in units of clock cycles, i.e., 4 nanoseconds
    job = qmm.simulate(config, qubit_spec, simulation_config)
    job.get_simulated_samples().con1.plot()
    if simulation_in_cloud:
        instance.close()
    plt.show()

else:

    qm = qmm.open_qm(config)

    job = qm.execute(qubit_spec)

    results = fetching_tool(job, data_list=["I", "Q", "iteration"], mode="live")
    fig = plt.figure()
    interrupt_on_close(fig, job)
    while results.is_processing():
        I, Q, iteration = results.fetch_all()
        S = u.demod2volts(I + 1j * Q, readout_len)
        R = np.abs(S)
        phase = np.angle(S)
        progress_counter(iteration, n_avg, start_time=results.get_start_time())
        plt.suptitle("Qubit spectroscopy")
        plt.subplot(211)
        plt.cla()
        plt.plot(frequencies / u.MHz, R, ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
        plt.subplot(212)
        plt.cla()
        plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel("Phase [rad]")
        plt.pause(1)
        plt.tight_layout()

plt.figure()
results = fetching_tool(job, data_list=["I", "Q", "iteration"])
I, Q, iteration = results.fetch_all()
S = u.demod2volts(I + 1j * Q, readout_len)
R = np.abs(S)
phase = np.angle(S)
plt.suptitle("Qubit spectroscopy")
plt.subplot(211)
plt.plot(frequencies / u.MHz, R, ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
plt.subplot(212)
plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel("Phase [rad]")
plt.tight_layout()
plt.show()
  1. This code line performs dual demodulation on the digitized signal making use of the integration weights stored in the configuration dictionary. Note that the processed data is routed to the I and Q QUA variables.

OPX+ or OPX1000 systems contain PPUs that support multiple threads, facilitating parallelism in QUA. Typically, qubit and resonator run on separate threads. To play their signals sequentially in qubit_spectroscopy.py, we must synchronize the timing between these threads.

qubit_spectroscopy.py
# Go to lines 35 and 39
import qm.qua as qua
from OPX1000configuration import config, qop_ip, cluster_name, cloud_username, cloud_password # or OPXplusconfiguration, depending on your hardware
from OPX1000configuration import config, qop_ip, cluster_name, cloud_username, cloud_password # or OPXplusconfiguration, depending on your hardware
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
from qualang_tools.results import progress_counter, fetching_tool
from qualang_tools.plot import interrupt_on_close
from qm import SimulationConfig, LoopbackInterface
from qualang_tools.loops import from_array
import warnings

warnings.filterwarnings("ignore")

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

n_avg = 2
frequencies = np.arange(30 * u.MHz, 70 * u.MHz + 0.1, 10 * u.MHz) 

with qua.program() as qubit_spec:
    n = qua.declare(int)
    f = qua.declare(int) 
    I = qua.declare(qua.fixed) 
    Q = qua.declare(qua.fixed)
    I_st = qua.declare_stream() 
    Q_st = qua.declare_stream() 
    n_st = qua.declare_stream() 

    with qua.for_(n, 0, n < n_avg, n + 1):
        with qua.for_(*from_array(f, frequencies)):
            qua.update_frequency("qubit", f)
            qua.play("saturation", "qubit")  # (1)!
            qua.align("qubit", "resonator")
            qua.measure(
                "readout",
                "resonator",  # (2)!
                None,
                qua.dual_demod.full("cos", "sin", I),
                qua.dual_demod.full("minus_sin", "cos", Q),
            )
            qua.wait(thermalization_time // 4, "resonator")
            qua.save(I, I_st)
            qua.save(Q, Q_st)
        qua.save(n, n_st)

    with qua.stream_processing():
        I_st.buffer(len(frequencies)).average().save("I")
        Q_st.buffer(len(frequencies)).average().save("Q")
        n_st.save("iteration")

#####################################
#  Open Communication with the QOP  #
#####################################

simulate = True
simulation_in_cloud = True

if simulate: 

    if simulation_in_cloud:
        client = QmSaas(email=cloud_username, password=cloud_password)
        instance = client.simulator(QoPVersion.v3_2_0)
        instance.spawn()
        qmm = QuantumMachinesManager(host=instance.host,
                                    port=instance.port,
                                    connection_headers=instance.default_connection_headers)
    else:
        qmm = QuantumMachinesManager(host=qop_ip, 
                                    cluster_name=cluster_name)

    simulation_config = SimulationConfig(duration=1_000) # duration is in units of clock cycles, i.e., 4 nanoseconds
    job = qmm.simulate(config, qubit_spec, simulation_config)
    job.get_simulated_samples().con1.plot()
    if simulation_in_cloud:
        instance.close()
    plt.show()

else:

    qm = qmm.open_qm(config)

    job = qm.execute(qubit_spec)

    results = fetching_tool(job, data_list=["I", "Q", "iteration"], mode="live")
    fig = plt.figure()
    interrupt_on_close(fig, job)
    while results.is_processing():
        I, Q, iteration = results.fetch_all()
        S = u.demod2volts(I + 1j * Q, readout_len)
        R = np.abs(S)
        phase = np.angle(S)
        progress_counter(iteration, n_avg, start_time=results.get_start_time())
        plt.suptitle("Qubit spectroscopy")
        plt.subplot(211)
        plt.cla()
        plt.plot(frequencies / u.MHz, R, ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
        plt.subplot(212)
        plt.cla()
        plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel("Phase [rad]")
        plt.pause(1)
        plt.tight_layout()

plt.figure()
results = fetching_tool(job, data_list=["I", "Q", "iteration"])
I, Q, iteration = results.fetch_all()
S = u.demod2volts(I + 1j * Q, readout_len)
R = np.abs(S)
phase = np.angle(S)
plt.suptitle("Qubit spectroscopy")
plt.subplot(211)
plt.plot(frequencies / u.MHz, R, ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
plt.subplot(212)
plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel("Phase [rad]")
plt.tight_layout()
plt.show()
  1. In our configuration.py, the qubit element is defined to work with include MWInput. This causes the compiler to allocate two threads specifically for the qubit in the QUA program. Additionally, the system ensures that different elements get different PPU threads. Consequently, the qubit_spectroscopy.py uses by default 4 threads.

  2. Thread usage is also set by the number of real-time inputs to qua.measure(). Here, two qua.dual_demod instructions use two threads, matching the threads the compiler allocates to resonator. However, qua.measure() could potentially use four qua.dual_demod instructions, requiring four threads.

The align() command aligns the qubit and resonator threads, ensuring synchronized, sequential execution.

qubit_spectroscopy.py
# Go to line 36
import qm.qua as qua
from qm import QuantumMachinesManager
from OPX1000configuration import config, qop_ip, cluster_name, cloud_username, cloud_password # or OPXplusconfiguration, depending on your hardware
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
from qualang_tools.results import progress_counter, fetching_tool
from qualang_tools.plot import interrupt_on_close
from qm import SimulationConfig, LoopbackInterface
from qualang_tools.loops import from_array
import warnings

warnings.filterwarnings("ignore")

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

n_avg = 2
frequencies = np.arange(30 * u.MHz, 70 * u.MHz + 0.1, 10 * u.MHz) 

with qua.program() as qubit_spec:
    n = qua.declare(int)
    f = qua.declare(int) 
    I = qua.declare(qua.fixed) 
    Q = qua.declare(qua.fixed)
    I_st = qua.declare_stream() 
    Q_st = qua.declare_stream() 
    n_st = qua.declare_stream() 

    with qua.for_(n, 0, n < n_avg, n + 1):
        with qua.for_(*from_array(f, frequencies)):
            qua.update_frequency("qubit", f)
            qua.play("saturation", "qubit")
            qua.align("qubit", "resonator")  # (1)!
            qua.measure(
                "readout",
                "resonator",
                None,
                qua.dual_demod.full("cos", "sin", I),
                qua.dual_demod.full("minus_sin", "cos", Q),
            )
            qua.wait(thermalization_time // 4, "resonator")
            qua.save(I, I_st)
            qua.save(Q, Q_st)
        qua.save(n, n_st)

    with qua.stream_processing():
        I_st.buffer(len(frequencies)).average().save("I")
        Q_st.buffer(len(frequencies)).average().save("Q")
        n_st.save("iteration")

#####################################
#  Open Communication with the QOP  #
#####################################

simulate = True
simulation_in_cloud = True

if simulate: 

    if simulation_in_cloud:
        client = QmSaas(email=cloud_username, password=cloud_password)
        instance = client.simulator(QoPVersion.v3_2_0)
        instance.spawn()
        qmm = QuantumMachinesManager(host=instance.host,
                                    port=instance.port,
                                    connection_headers=instance.default_connection_headers)
    else:
        qmm = QuantumMachinesManager(host=qop_ip, 
                                    cluster_name=cluster_name)

    simulation_config = SimulationConfig(duration=1_000) # duration is in units of clock cycles, i.e., 4 nanoseconds
    job = qmm.simulate(config, qubit_spec, simulation_config)
    job.get_simulated_samples().con1.plot()
    if simulation_in_cloud:
        instance.close()
    plt.show()

else:

    qm = qmm.open_qm(config)

    job = qm.execute(qubit_spec)

    results = fetching_tool(job, data_list=["I", "Q", "iteration"], mode="live")
    fig = plt.figure()
    interrupt_on_close(fig, job)
    while results.is_processing():
        I, Q, iteration = results.fetch_all()
        S = u.demod2volts(I + 1j * Q, readout_len)
        R = np.abs(S)
        phase = np.angle(S)
        progress_counter(iteration, n_avg, start_time=results.get_start_time())
        plt.suptitle("Qubit spectroscopy")
        plt.subplot(211)
        plt.cla()
        plt.plot(frequencies / u.MHz, R, ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
        plt.subplot(212)
        plt.cla()
        plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
        plt.xlabel("Qubit intermediate frequency [MHz]")
        plt.ylabel("Phase [rad]")
        plt.pause(1)
        plt.tight_layout()

plt.figure()
results = fetching_tool(job, data_list=["I", "Q", "iteration"])
I, Q, iteration = results.fetch_all()
S = u.demod2volts(I + 1j * Q, readout_len)
R = np.abs(S)
phase = np.angle(S)
plt.suptitle("Qubit spectroscopy")
plt.subplot(211)
plt.plot(frequencies / u.MHz, R, ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel(r"$R=\sqrt{I^2 + Q^2}$ [V]")
plt.subplot(212)
plt.plot(frequencies / u.MHz, signal.detrend(np.unwrap(phase)), ".")
plt.xlabel("Qubit intermediate frequency [MHz]")
plt.ylabel("Phase [rad]")
plt.tight_layout()
plt.show()
  1. This code ensures the qubit's thread finishes tasks like update_frequency and saturation before starting the resonator's qua.measure(). With qua.align(), manual timing adjustments aren't needed for qua.measure(). While this demonstrates a basic use of qua.align(), its real power is in coordinating unpredictable quantum experiments, showcasing the strategic design of the PPU and QM platform.

QUA-libs

You can find the full qubit spectroscopy program in our qua-libs github repo in the following link.

Live Data Fetching

You will be learning more about live data fetching in the Power-Time Rabi module, but for now I want to share with you that you can't do live data fetching in simulations.

Other Real-Time Processing methods

It is worth highlighting that besides demodulation you also get access to time tagging (with nanoseconds and picosecond resolution) as well as integration. You just simply need to interchange the demod commands in measure() for the ones relevant to your experiment.

Linking QUA program to Configuration

In the QUA code qua.play('saturation', 'qubit') and qua.measure('readout', 'resonator', None, ...) directly use the dictionary. The outcome of spectroscopy is to have calibrated the qubit's intermediate frequency.

The Configuration

Below we point out where the intermediate frequencies for qubit and resonator can be adjusted for future use. In the QUA program, we modified the qubit frequency in real-time during calibration. However, due to the semi-static configuration, once we identify the resonance frequencies, we will updated them on the dictionary.

configuration.py
# Go to lines 10, 28, and 31

config = {
    ...
    "elements": {
        "qubit": {
            "MWInput": {
                "port": (con, mw_fem, 1),
            },
            "intermediate_frequency": qubit_IF, # (1)!
            "operations": {
                "cw": "const_pulse",
                "saturation": "saturation_pulse",
                "pi": "pi_pulse",
                "pi_half": "pi_half_pulse",
                "x180": "x180_pulse",
                "x90": "x90_pulse",
                "-x90": "-x90_pulse",
                "y90": "y90_pulse",
                "y180": "y180_pulse",
                "-y90": "-y90_pulse",
            },
        },
        "resonator": {
            "MWInput": {
                "port": (con, mw_fem, 2),
            },
            "intermediate_frequency": resonator_IF, # (2)!
            "operations": {
                "cw": "const_pulse",
                "readout": "readout_pulse", # (3)!
            },
            "MWOutput": {
                "port": (con, mw_fem, 1),
            },
            "time_of_flight": time_of_flight,
            "smearing": 0,
        },
    },
    ...
}
  1. At the start of this file, the qubit_IF Python variable indicates the frequency in Hz for setting the oscillator.
  2. For the resonator, the frequency is given by the resonator_IF variable.
  3. Note that the QUA program uses the readout operation, which should be paired with the real-time dual demodulation processing.

In lines 38 and 39 of the QUA program we instruct the PPU to dual demodulate the digitized signal and as part of the process the signal gets multiplied by cos, sin, and minus_sin which can be found in the integration weights dictionary below. The weights we used are the simplest one being constant for the duration of the demodulation window.

configuration.py
config = {
    ...
    "pulses": {
        "readout_pulse": {
            "operation": "measurement",
            "length": readout_len,
            "waveforms": {
                "I": "readout_wf",
                "Q": "zero_wf",
            },
            "integration_weights": { # (1)!
                "cos": "cosine_weights",
                "sin": "sine_weights",
                "minus_sin": "minus_sine_weights",
                "rotated_cos": "rotated_cosine_weights",
                "rotated_sin": "rotated_sine_weights",
                "rotated_minus_sin": "rotated_minus_sine_weights",
            },
            "digital_marker": "ON",
        },
    },
    "integration_weights": { # (2)!
        "cosine_weights": {
            "cosine": [(1.0, readout_len)],
            "sine": [(0.0, readout_len)],
        },
        "sine_weights": {
            "cosine": [(0.0, readout_len)],
            "sine": [(1.0, readout_len)],
        },
        "minus_sine_weights": {
            "cosine": [(0.0, readout_len)],
            "sine": [(-1.0, readout_len)],
        },
    },
    ...
}
  1. There is wide variety of integration weights that can be invoked during real-time processing in a measure() command and are listed below. In the QUA code we use cos, sin, and minus_sin pointing to weights listed in the integration weights dictionary.

  2. Each weight can have an arbitrary name (string) to fit your experimental needs, and as you can see here each weight has a cosine and sine thus allowing introducing a rotation matrix to the digitized signal during measure(). You can read more about the syntax of the wieghts in the documentation.

Demod length and Pulse length

On the QM Platform, the output Pulse length (defining the microwave signal duration) and the raw ADC recording window are separate from the demodulation window. This means the demodulation window can be extended beyond the Pulse length. This is useful, for instance, when you want to capture the resonator's ring-down in the demodulation window. You could send a pulse for 1 microsecond and demodulate for 1.5 microseconds.

Now it is your turn

  1. Without using qua.update_frequency(), create a spectroscopy sweep for the following frequencies [2, 3, 6, 8, 12] MHz. Verify that you succesfully created the five different pulses either using simulated_samples or an oscilloscope.
  2. Modify the real-time dual demodulation process to exclusively utilize qua.demod.full() and still achieve identical result as I and Q.
  3. Adjust the QUA program to generate the qubit and the resonator pulse to coincide in time as well as to have the same duration.

Test your knowledge

  1. Considering that the qubit and resonator operate in separate threads having the possibility of generatingn paralellism, and needing qua.align() to make them sequential. How come at each for-loop iteration the qubit pulse always happens after the resonator pulse?

Coming up

By working with the Rabi protocol, a fundamental experiment for qubits, you will learn to adjust pulse amplitudes and durations in real-time when sent to your qubit. Additionally, you'll explore real-time control flow techniques.