See also

A Jupyter notebook version of this tutorial can be downloaded here.

image0

[1]:
from qblox_scheduler import (
    BasicElectronicNVElement,
    HardwareAgent,
    QuantumDevice,
)

Getting started with Color Center qubits#

In this notebook we will discuss how to get started with Color center qubits using Qblox hardware. The goal of this tutorial is to introduce the most important elements in the hardware- and Device-Under-Test (DUT) configuration files for the color center qubit architecture. This will be done by taking snippets of the configuration files inside the dependencies/configs folder and discussing the most important concepts.

We consider a simple system containing one color center qubit in a NV sample.

The hardware configuration file#

To operate the color center qubit, we will use a cluster with a QCM-RF for microwave control, a QRM (or alternatively a QTM) for photon detection and a QCM for controlling the readout laser.

[2]:
hardware_description = {
    "cluster": {
        "instrument_type": "Cluster",
        "modules": {
            "4": {"instrument_type": "QRM"},  # slot 4
            "6": {"instrument_type": "QCM_RF"},  # slot 6
            "8": {"instrument_type": "QCM"},  # slot 8
            "10": {"instrument_type": "QTM"},  # slot 10
        },
        "ref": "internal",
        "ip": None,
    }
}

In the cell above, the line "ref": "internal" indicates that the cluster will use its own internal 10MHz clock instead of an external one. For setups involving multiple clusters it is recommended to use an external source instead, to avoid timing mistakes during measurements. The ip-address of the instrument may be specified in the corresponding line. When no ip-address is provided a dummy cluster will be used, which may be used to test the compilation of schedules or other tasks that do not require an actual instrument.

After the layout of the cluster has been determined, the connectivity from the cluster to the color center qubit has to be specified. This is done in the “connectivity” part of the hardware configuration as follows.

[3]:
connectivity = {
    "graph": [
        ["cluster0.module8.real_output_0", "optical_mod_red_laser.if"],
        ["red_laser.output", "optical_mod_red_laser.lo"],
        ["optical_mod_red_laser.out", "qe0:optical_control"],
        ["cluster0.module6.real_input_0", "qe0:optical_readout"],
        ["cluster0.module4.complex_output_0", "qe0:mw"],
        ["cluster0.module8.digital_output_0", "qe0:marker"],
        ["cluster0.module10.digital_input_0", "qtm0:in"],
        ["cluster0.module10.digital_input_1", "qtm1:in"],
    ]
}

This connectivity cell defines the experiment’s signal path graph. It maps physical hardware ports to logical qubit ports using a list of [source, destination] pairs.

Key connections established here link specific Cluster modules to the ports of a quantum element, namely: qe0:

  • qe0:mw (microwave control) is linked to cluster0.module4.complex_output_0.

  • qe0:optical_readout is linked to cluster0.module6.real_input_0.

  • qe0:marker is linked to cluster0.module8.digital_output_0.

This mapping uses the mandatory <qubit_name>:<port_name> syntax (e.g., qe0:mw). This critical abstraction allows users to target logical qubit ports (e.g.,mw) in their experiments, instead of needing to know the specific hardware path (e.g.,module4.complex_output_0) they are connected to.

The graph also defines other necessary links:

  • Internal hardware connections: e.g., linking the red_laser.output to the optical_mod_red_laser.lo.

  • Time Tagger inputs: Linking cluster0.module10’s digital inputs to qtm0:in and qtm1:in.

After defining the connectivity, we can specify hardware options that set analog parameters for the various signal paths. These parameters include output attenuation levels, modulation frequencies, mixer corrections, and input gains.

[4]:
hardware_options = (
    {
        "output_att": {
            "q0:mw-q0.01": 30,  # port-clock notation = <port_notation>-<clock_notation>
            "q2:mw-q2.01": 30,
            "q0:res-q0.ro": 34,
            "q2:res-q2.ro": 34,
        },
        "modulation_frequencies": {
            "q0:mw-q0.01": {"interm_freq": 80.0e6},
            "q2:mw-q2.01": {"interm_freq": 80.0e6},
            "q0:res-q0.ro": {"lo_freq": 7.6e9},
            "q2:res-q2.ro": {"lo_freq": 7.6e9},
        },
    },
)
[5]:
hardware_options = {
    "mixer_corrections": {
        "qe0:mw-qe0.spec": {
            "auto_lo_cal": "on_lo_interm_freq_change",
            "auto_sideband_cal": "on_interm_freq_change",
        }
    },
    "modulation_frequencies": {
        "qe0:mw-qe0.spec": {"interm_freq": None, "lo_freq": 2.8e9},
        "qe0:mw-qe0.del1": {"interm_freq": None, "lo_freq": 2.8e9},
        "qe0:mw-qe0.del2": {"interm_freq": None, "lo_freq": 2.8e9},
        "qe0:optical_control-qe0.ge0": {"interm_freq": 200e6, "lo_freq": None},
        "qe0:optical_readout-qe0.ge0": {"interm_freq": 0, "lo_freq": None},
    },
    "input_gain": {"qe0:optical_readout-qe0.ge0": 0},
    "sequencer_options": {"qe0:optical_readout-qe0.ge0": {"ttl_acq_threshold": 0.5}},
    "digitization_thresholds": {
        "qtm0:in-digital": {"analog_threshold": 0.8},
        "qtm1:in-digital": {"analog_threshold": 0.8},
    },
}

Here, we have introduced the clock notation of <qubit_name>.<clock_name>, where the two names are separated by a point (e.g., qe0.spec).

In a similar fashion, we define the port-clock pair as <port_notation>-<clock_notation>, where the port and clock notations are separated by a hyphen (e.g., qe0:mw-qe0.spec). This final notation allows us to specify distinct analog settings for signals routed to different qubits, even if they originate from the same module (multiplexing).

In the cell above, the following hardware options are specified:

  1. mixer_corrections: Specifies the automatic LO and sideband calibration behavior for a specific port-clock pair.

  2. modulation_frequencies: Allows the user to specify either the frequency of the LO or the intermediate frequency (IF) of the NCO. The remaining value will be set automatically by the instrument, according to the relationship qubit_frequency = interm_freq + lo_freq.

  3. input_gain: Sets the analog input gain for a specific port-clock.

  4. sequencer_options: Configures specific sequencer parameters, such as the ttl_acq_threshold for optical readout.

  5. digitization_thresholds: Sets the analog voltage threshold for the digital time tagger inputs (e.g., qtm0:in-digital).

Combining the contents of the connectivity, device, and hardware options yields the complete hardware configuration file (e.g., in ./dependencies/configs/hw_config.json). This allows for operations on our color center setup using Qblox hardware.

Setting up the quantum device for color-centers#

Complementary to the hardware configuration file is the device-under-test (DUT) configuration file. Where the hardware configuration file is used to make the hardware agent aware of the physical layout of the lab (connectivity, photon detectors, etc.), the DUT configuration makes the hardware agent aware of the layout of the physical qubit and its properties.

The first component of a DUT configuration file contains the “elements” of the device under test.

Here, we set up a quantum device element for a Qblox cluster and its modules, that represents your color center qubit or a sample where multiple qubits resides.

You can initialize a device element using the QuantumDevice class from qblox-scheduler:

[6]:
qubit = QuantumDevice("nv_center")

NV centers differ from superconducting and spin qubits in how they operate. They rely on a combination of microwave control, optical excitation, and photon-based detection, each defined by specific control and readout parameters.

To manage such parameters, we add an NV center element to the qubit using BasicElectronicNVElement:

[7]:
qe0 = BasicElectronicNVElement("qe0")
qubit.add_element(qe0)

Adding the NV center element allows us to configure qubit parameters through its submodule attributes. For example, in an ODMR experiment, we can set the microwave pulse amplitude and duration as follows:

[8]:
qe0.spectroscopy_operation.amplitude = 0.5
qe0.spectroscopy_operation.duration = 2e-6  # μs

Next, we configure the measurement gate. This involves setting the readout laser duration (controlled via AOM by the QCM) and the acquisition window for detecting fluorescent photons:

[9]:
qe0.measure.pulse_amplitude = 0.5
qe0.measure.pulse_duration = 2e-6  # seconds
qe0.measure.acq_duration = 1.5e-6  # seconds

Where:

  • pulse_amplitude: readout laser power

  • pulse_duration: duration of the readout laser

  • acq_duration: acquisition window for photon detection

Similarly, BasicElectronicNVElement supports all key parameters needed for color-center experiments, including charge reset, X-Y axis rotations, and charge and resonance counts. These parameters are the rest of the key-value pairs of BasicElectronicNVElement, used to specify settings that will be compiled when a gate is created on that particular device element, as follows.

  • reset: a passive Reset gate will have a duration of 2 us.

  • rxy: a X gate will have an amplitude of 0.5 a.u. and last for 200 ns.

  • measure: a Measure operation will turn on the AOM with amplitude of 0.75 a.u. for 2 us and open the QTM acquisition window for 1.5 us.

The clock_freqs specify the frequencies of the qubit, such as the the readout frequency or the frequency of its \(\ket{0} \rightarrow \ket{1}\) transition.

When combined inside a single .json or .yaml file the elements and edges together make up a complete description of the qubit. This allows the user to add gates operating on qubits to their schedule, as opposed to having to keep track of pulse-level instructions like amplitude or duration. The device-level description can be flexibly extended to suit the user’s needs, for example by adding additional qubit types or inter-qubit connections such as couplers.

For convenience, you can download a pre-filled quantum device configuration file for color centers in this notebook (at the top of the page). You can import the JSON file using the Hardware Agent, as shown in the following section.

The HardwareAgent#

The HardwareAgent is a software representation of the complete experimental setup, meant to conveniently run experiments by (for example) managing pulse delays, output paths and amplitude settings. Using the configuration files discussed above, we can now create an agent that is aware of the physical setup in the lab and properties of the color center qubit. Create a new HardwareAgent by running the cell below.

[10]:
# Create a new hardware agent based on the configuration files discussed above
hw_agent = HardwareAgent(
    hardware_configuration="./dependencies/configs/hw_config.json",
    quantum_device_configuration="./dependencies/configs/dut_config.json",
)

# Create an alias for a specific element of the DUT to convenienty interact with its properties
qubit = hw_agent.quantum_device.get_element("qe0")

Throughout experiments it is possible to overwrite (gate) parameter settings of the DUT configuration file. For example when configuring the pi-pulse amplitude, the user may want to store a new value to use it in later experiments. This is done in the following way.

[11]:
# Change the amplitude of the pi pulse from 0.12 defined above to 0.15
qubit.rxy.amp180 = 0.15

Afterwards, the new values may be stored inside a new .yaml or .json file by running the following cell.

[12]:
# Store the (changed) DUT configuration file in a new file
hw_agent.quantum_device.to_json_file("./dependencies/configs", add_timestamp=True)
[12]:
'./dependencies/configs/nv_center_2025-10-30_00-37-16_UTC.json'

With this information, we are ready to start experiments on Color Center qubits.