Skip to content

Chevron-Ramsey

Welcome aboard! This module will guide us through:

  1. Parametric Waveform Generation:
    • Phase Update
  2. Real-Time Processing:
    • Mathematics

Excited to get going?

The QUA program

The PPU's design allows you to easily adjust both the global and relative phases of the pulses sent to your qubits using QUA commands. In this example it will be the relative phase between two pi-half pulses. Kevin needs to explain frame_rotation().

chevron_ramsey.py
# Go to lines 43 and 61
from qm.qua import *
from qm import QuantumMachinesManager
from qm import SimulationConfig, LoopbackInterface
from OPX1000configuration import config, qop_ip, cluster_name, cloud_username, cloud_password # or OPXplusconfiguration, depending on your hardware
import numpy as np
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 warnings

warnings.filterwarnings("ignore")

##############################
# Program-specific variables #
##############################
n_avg = 1

freq_array = np.linspace(-10 * u.MHz / 2, 10 * u.MHz / 2, 5)

taus = np.arange(4, 8, 1)

###################
# The QUA program #
###################
with qua.program() as ramsey_freq_duration:
    n = qua.declare(int)
    f = qua.declare(int) 
    delay = 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, freq_array)):
            with qua.for_(*from_array(delay, taus)):  

                qua.play("x90", "qubit")
                qua.wait(delay, "qubit")
                qua.frame_rotation_2pi(  # (1)!
                    qua.Cast.mul_fixed_by_int(
                        qua.Cast.mul_fixed_by_int(1e-9, f), 4 * delay
                    ),
                    "qubit")
                qua.play("x90", "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.reset_frame("qubit")  # (2)!
        qua.save(n, n_st)

    with qua.stream_processing():
        I_st.buffer(len(taus)).buffer(len(freq_array)).average().save("I")
        Q_st.buffer(len(taus)).buffer(len(freq_array)).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, ramsey_freq_duration, 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(ramsey_freq_duration)
    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.subplot(211)
        plt.cla()
        plt.title(r"Ramsey chevron $R=\sqrt{I^2 + Q^2}$")
        plt.pcolor(taus * 4, freq_array / u.MHz, R)
        plt.xlabel("Qubit detuning [MHz]")
        plt.ylabel("Idle time [ns]")
        plt.subplot(212)
        plt.cla()
        plt.title("Ramsey chevron phase")
        plt.pcolor(taus * 4, freq_array / u.MHz, np.unwrap(phase))
        plt.xlabel("Qubit detuning [MHz]")
        plt.ylabel("Idle time [ns]")
        plt.tight_layout()
        plt.pause(1)

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.subplot(211)
plt.title(r"Ramsey chevron $R=\sqrt{I^2 + Q^2}$")
plt.pcolor(taus * 4, freq_array / u.MHz, R)
plt.xlabel("Qubit detuning [MHz]")
plt.ylabel("Idle time [ns]")
plt.subplot(212)
plt.cla()
plt.title("Ramsey chevron phase")
plt.pcolor(taus * 4, freq_array / u.MHz, np.unwrap(phase))
plt.xlabel("Qubit detuning [MHz]")
plt.ylabel("Idle time [ns]")
plt.tight_layout()
plt.show()
  1. This code line adjusts the oscillator's relative phase compared to the qubit's reference frame. This allows you to perform rotations on the Bloch sphere along any axis that's relevant to your experiment. Moreover, the rotation axis can be determined by a measurement outcome, enabling dynamic circuit operations.
  2. If you need to reset the relative phase of the pulses to its initial state in relation to the qubit during your quantum experiment, you can use the reset_frame() function in the element qubit thread.

The PPU stores variables and performs math operations, enabling real-time waveform adjustments. These operations can interact with QUA variables influenced by quantum measurements. We'll explore a phase calculation using the incrementing delay QUA variable.

chevron_ramsey.py
# Go to lines 46 and 47
from qm.qua import *
from qm import QuantumMachinesManager
from qm import SimulationConfig, LoopbackInterface
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 qualang_tools.results import progress_counter, fetching_tool
from qualang_tools.plot import interrupt_on_close
from qualang_tools.loops import from_array
import warnings

warnings.filterwarnings("ignore")

##############################
# Program-specific variables #
##############################
n_avg = 1

freq_array = np.linspace(-10 * u.MHz / 2, 10 * u.MHz / 2, 5)

taus = np.arange(4, 8, 1)

###################
# The QUA program #
###################
with qua.program() as ramsey_freq_duration:
    n = qua.declare(int)
    f = qua.declare(int) 
    delay = 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, freq_array)):
            with qua.for_(*from_array(delay, taus)):  

                qua.play("x90", "qubit")
                qua.wait(delay, "qubit")
                qua.frame_rotation_2pi(
                    qua.Cast.mul_fixed_by_int(  # (1)!
                        qua.Cast.mul_fixed_by_int(1e-9, f), 4 * delay
                    ),
                    "qubit")
                qua.play("x90", "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.reset_frame("qubit")
        qua.save(n, n_st)

    with qua.stream_processing():
        I_st.buffer(len(taus)).buffer(len(freq_array)).average().save("I")
        Q_st.buffer(len(taus)).buffer(len(freq_array)).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, ramsey_freq_duration, 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(ramsey_freq_duration)
    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.subplot(211)
        plt.cla()
        plt.title(r"Ramsey chevron $R=\sqrt{I^2 + Q^2}$")
        plt.pcolor(taus * 4, freq_array / u.MHz, R)
        plt.xlabel("Qubit detuning [MHz]")
        plt.ylabel("Idle time [ns]")
        plt.subplot(212)
        plt.cla()
        plt.title("Ramsey chevron phase")
        plt.pcolor(taus * 4, freq_array / u.MHz, np.unwrap(phase))
        plt.xlabel("Qubit detuning [MHz]")
        plt.ylabel("Idle time [ns]")
        plt.tight_layout()
        plt.pause(1)

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.subplot(211)
plt.title(r"Ramsey chevron $R=\sqrt{I^2 + Q^2}$")
plt.pcolor(taus * 4, freq_array / u.MHz, R)
plt.xlabel("Qubit detuning [MHz]")
plt.ylabel("Idle time [ns]")
plt.subplot(212)
plt.cla()
plt.title("Ramsey chevron phase")
plt.pcolor(taus * 4, freq_array / u.MHz, np.unwrap(phase))
plt.xlabel("Qubit detuning [MHz]")
plt.ylabel("Idle time [ns]")
plt.tight_layout()
plt.show()
  1. Every QUA variable is represented using 32 bits. The PPU doesn't automatically handle type casting between them. To smoothly handle casting between various variable types, you can utilize the Cast function in the Math library.
  2. In this example, we're multiplying fixed-value variables by a QUA variable, which changes with each iteration of the for-loops.

QUA-libs

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

In the reference frame of your qubit

Each element is linked to an oscillator in its associated PPU core. When a new QUA program begins, the phase of these oscillators is reset to a specific value. Throughout the QUA program's execution, the phase of the oscillators remains continuous. This distinctive trait ensures automatic coherence with the qubit you're working on. As a result, you stay in the qubit's rotating frame without needing any calculations for subsequent pulse manipulations. Skillfully managing pulse phases is an inherent feature of the QM platform.

Now it is your turn

  1. The function frame_rotation_2pi(Cast.mul_fixed_by_int(Cast.mul_fixed_by_int(1e-9, f), 4 * delay),"qubit") has three inner operations: Cast.mul_fixed_by_int(1e-9, f), 4 * delay, and Cast.mul_fixed_by_int(). Decompose these operations, perform them individually, and save the results into QUA variables and pass them to a data stream. Retrieve the results onto your computer and verify that the PPU executed the calculations correctly.
  2. Implement the frame_rotation_2pi() function using the amplitude matrix and employ real-time mathematical operations to calculate both cosine and sine.

Test your knowledge

  1. How can you utilize real-time mathematics together with frame_rotation() to replicate the functionality of frame_rotation_2pi()?
  2. How would include delay = 0 into the QUA program to create qua.play('x90', 'qubit') - qua.wait(0) - qua.play('x90', 'qubit')? Do not use QUA if-statement.
  3. What would the result be if you were to remove qua.reset_frame() in line 60 of the QUA program?

Coming up

In the upcoming module, we'll elevate a routine experiment by introducing advanced techniques. You'll navigate through the process of performing an Active Reset, making real-time decisions based on the outcomes from your device.