tergite-autocalibration

Creating a new node

This tutorial is about how to create a new node class. The word node is an overloaded term, used in many contexts, so, what does a node mean here? If you put all steps to characterize a qubit in a chain, then it could be seen as a directed graph with the calibration steps as nodes. Take a look at the node overview to get an impression on how it looks like. In this guide, we will cover the topics:

  1. File locations for nodes within the framework
  2. How to implement a node
  3. Register the node in the framework

Following these steps, you can easily contribute to the automatic calibration with your own nodes in a couple of hours.

Base classes

The base classes on which all nodes are built can be found in tergite_autocalibration/lib/base. There are base classes for all three components of a node, the node itself, the measurement and the analysis.

Nodes can be either QubitNode or CouplerNode, both inherit from a BaseNode class providing common interfaces and functionalities. They will have different quantities of interest depending on the type (qubit_qoi for QubitNode) A Node also need to implement a Schedule; there are two files in tergite_autocalibration/lib/nodes

Both files provide classes with multiple inheritance that combine type and schedule, i.e. ScheduleQubitNode or ExternalParameterFixedScheduleCouplerNode, that can be used in nodes to simplify implementation of new nodes for developers.

Measurements only have a base class providing a common interface.

Analysis classes have a more complex composition and inheritance structure. The entry point is always a BaseNodeAnalysis class, implemented in the BaseAllCouplersAnalysis or BaseAllQubitsAnalysis classes. Each node analysis will loop over all elements (qubits or couplers) defining a relevant analysis class, either QubitAnalysis or CouplerAnalysis. These analyses will have a common structure with a setup phase (where all dataset information is read and processed), a property that returns a quantity of interest (QOI) object, and then a call to a function updating the REDIS database. The analysis will be very node specific. CouplerAnalysis classes combine information from two QubitAnalyses; information can be stored both at the coupler level, for example all CZ gate information, or at the qubit level within the coupler (in REDIS couplers:coupler_name:qubit_name:qubit_information). This allows to handle information for the same qubit when used in different couplers. Plots are also handled in the analysis class, with the Qubit/Coupler analyses handling plotting according to the information required (i.e. plotting all qubits in a resonator_spectropy, both qubits in all couplers in a coupler_spectroscopy, or a single plot per coupler for two qubit gate randomized benchmarking).

Where are the nodes located?

The nodes are located in tergite_autocalibration/lib/nodes. If you open that module, you will find that there are four submodules for different kind of nodes:

Please create a new submodule for your node in one of the four submodules listed above. Essentially, a proper package in the end should contain:

Before we are going to a look on how this would be implemented in detail, a quick note on naming conventions.

Naming conventions

Since we are creating a lot of node, measurement and analysis objects, there are some naming conventions to make it more standardized and understandable to learn the framework. Taking the rabi oscillations as an example, we have:

When there are some more complicated nodes for which you do not know how to name it, please just take a look at the already existing nodes and make a guess how it feels to name it correctly. Also, when there is a node that starts with an abbreviation, please have all letter for the abbreviation capitalized e.g.: cz_calibration would be the node name for the CZCalibration node class.

Node implementation details

All node classes are supposed follow the same interface as described in the BaseNodeclass. Below, for example, you have the rabi oscillations node:

import numpy as np

from tergite_autocalibration.lib.nodes.schedule_node import ScheduleNode
from tergite_autocalibration.lib.nodes.qubit_control.rabi_oscillations.measurement import RabiOscillationsMeasurement
from tergite_autocalibration.lib.nodes.qubit_control.rabi_oscillations.analysis import RabiQubitAnalysis

class RabiOscillationsNode(ScheduleQubitNode):
  measurement_obj = RabiOscillationsMeasurement
  analysis_obj = RabiQubitAnalysis
  qubit_qois = ["rxy:amp180"]

  def __init__(self, name: str, all_qubits: list[str], **schedule_keywords):
    super().__init__(name, all_qubits, **schedule_keywords)
    self.schedule_samplespace = {
      "mw_amplitudes": {
        qubit: np.linspace(0.002, 0.90, 61) for qubit in self.all_qubits
      }
    }

As you can see, it inherits from the ScheduleQubitNode class, which contains a very simple definition of a node that runs a simple sweep over a single quantity for all qubits defined in the run_config. More information about other node classes can be found in the description of the base classes above. Furthermore, you can see that the node has three class attributes:

Also, you can see in the constructor, there is an attribute called schedule_samplespace. Here, we define in the measurement, what quantity will be swept over.

Creating a measurement_obj

The measurement_obj is implemented in the measurement.py file of your node submodule. To initialize we require a dictionary of the extended transmons:

transmons: dict[str, ExtendedTransmon]

It must contain a method called schedule_function that expects the node’s samplespace as input and returns the complete schedule.

Creating an analysis_obj

The analysis_obj is implemented in the analysis.py file from your module and contains the class that perform the analysis. See the description in the Base classes above to understand which classes need to be implemented and which base classes to use.

Some useful information:

Node types and samplespaces

In the example above, the node inherits from the class ScheduleNode. This is one option for the node behaviour:

When you are implementing a node, you can choose which of the two abstract node classes fit better with the behaviour of your new node. Also, if you want to implement a more sophisticated measurement procedure, you can override the procedures in the measurement function or in other places. Check out the article about node classes for more details.

Register the node in the framework

To add the node to the framework, you have to register it in two places - the node factory and the calibration graph. Also, please do not forget to write documentation for your node.

Node factory

A factory is a programming pattern to create complex objects such as our nodes in a bit more clearly interfaced way. The factory contains a register for all nodes, that map their name to the respective class where the node is implemented. When you are adding a node, please register your node name, by adding it to the tergite_autocalibration.lib.utils.node_factory.NodeFactory class under the self.node_name_mapping attribute in the dictionary.

Calibration graph

In the file tergite_autocalibration/lib/utils/graph.py in the list graph_dependencies insert the edges that describe the position of the new node in the Directed Acyclic Graph. There are two entries required (or one entry if the new node is the last on its path):

It is possible to have multiple dependencies, i.e. a node can be set to run only if two different nodes are run.

This will result in both previous_node_1 and previous_node_2 to be run before the new_node.

Documentation

Please add your node to the list of available nodes in this documentation.

If possible, please create a separate page explaining your node’s functionality and link it there. Include relevant information about:

Figures

Figure are managed from the figure_util.py file in the util folder of the base node directory. All figure should be created by provided utility function to assure consistent formatting. It is possible to create additional figure by implementing the “save_other_plots” function in your analysis node. Please create the new figure using this utility function. The typical usage is to display all 1D distributions when performing a 2D scan. An example can be found in the punchout node.

For implementation details, refer to the Node types section.