Node classes
The execution of most of the nodes consists of a single schedule compilation, a single measurement and a single post-processing. Although for most of the nodes this workflow suffices, there are exceptions while this workflow can become limiting in more advanced implementations.
To allow greater flexibility in the node implementations the nodes are categorized:
ScheduleNode
: The simple way of doing the measurement and having an analysis afterward. This node compiles one time- There is only
node.schedule_samplespace
if the sweeping takes place within the schedule.
- There is only
ExternalParameterNode
: A looping over an external parameter during the sweep. This node compiles several times.- There are both
node.schedule_samplespace
andnode.external_samplespace
if there are sweeping parameters outside the schedule. For example thecoupler_spectroscopy
node sweeps thedc_current
outside of the schedule:
- There are both
Below, there is an example for an ExternalParameterNode
implementation.
import numpy as np
from tergite_autocalibration.lib.nodes.coupler.spectroscopy.analysis import (
CouplerSpectroscopyNodeAnalysis,
)
from tergite_autocalibration.lib.nodes.external_parameter_node import (
ExternalParameterNode,
)
from tergite_autocalibration.lib.nodes.qubit_control.spectroscopy.measurement import (
Two_Tones_Multidim,
)
from tergite_autocalibration.lib.utils.samplespace import qubit_samples
from tergite_autocalibration.utils.dto.enums import MeasurementMode
from tergite_autocalibration.utils.hardware.spi import SpiDAC
class CouplerSpectroscopyNode(ExternalParameterNode):
= Two_Tones_Multidim
measurement_obj = CouplerSpectroscopyNodeAnalysis
analysis_obj = ["parking_current", "current_range"]
coupler_qois
def __init__(
self, name: str, all_qubits: list[str], couplers: list[str], **schedule_keywords
):super().__init__(name, all_qubits, **schedule_keywords)
self.name = name
self.couplers = couplers
self.qubit_state = 0
self.schedule_keywords["qubit_state"] = self.qubit_state
self.coupled_qubits = self.get_coupled_qubits()
self.coupler = self.couplers[0]
self.mode = MeasurementMode.real
self.spi_dac = SpiDAC(self.mode)
self.dac = self.spi_dac.create_spi_dac(self.coupler)
self.all_qubits = self.coupled_qubits
self.schedule_samplespace = {
"spec_frequencies": {
for qubit in self.all_qubits
qubit: qubit_samples(qubit)
}
}
self.external_samplespace = {
"dc_currents": {self.coupler: np.arange(-2.5e-3, 2.5e-4, 280e-6)},
}
def get_coupled_qubits(self) -> list:
if len(self.couplers) > 1:
print("Multiple couplers, lets work with only one")
= self.couplers[0].split(sep="_")
coupled_qubits self.coupler = self.couplers[0]
return coupled_qubits
def pre_measurement_operation(self, reduced_ext_space):
= reduced_ext_space["dc_currents"]
iteration_dict = list(iteration_dict.values())[0]
this_iteration_value print(f"{ this_iteration_value = }")
self.spi_dac.set_dac_current(self.dac, this_iteration_value)
def final_operation(self):
print("Final Operation")
self.spi_dac.set_dac_current(self.dac, 0)
Please read the guide about how to create a new
node to learn more about nodes. This guide also contains an example for a ScheduleNode
.
Examples of nodes requiring an external samplespace
coupler_spectroscopy
sweeps thedc_current
which is set by the SPI rack not the clusterT1
sweeps a repetition index to repeat the measurement many timesrandomized_benchmarking
sweeps different seeds. Although the seed is a schedule parameter, sweeping outside the schedule improves memory utilization.