Source code for quantify_scheduler.device_under_test.spin_element

# Repository: https://gitlab.com/quantify-os/quantify-scheduler
# Licensed according to the LICENCE file on the main branch
"""The module contains definitions related to spin qubit elements."""
import math
from typing import Any, Dict

from qcodes.instrument import InstrumentChannel
from qcodes.instrument.base import InstrumentBase
from qcodes.instrument.parameter import (
    ManualParameter,
    Parameter,
)
from qcodes.utils import validators

from quantify_scheduler.backends.graph_compilation import (
    DeviceCompilationConfig,
    OperationCompilationConfig,
)
from quantify_scheduler.device_under_test.device_element import DeviceElement
from quantify_scheduler.device_under_test.transmon_element import (
    DispersiveMeasurement,
    IdlingReset,
    ReferenceMagnitude,
)
from quantify_scheduler.helpers.validators import Numbers
from quantify_scheduler.operations import (
    composite_factories,
    measurement_factories,
    pulse_factories,
    pulse_library,
)


[docs] class PortsSpin(InstrumentChannel): """Submodule containing the ports.""" def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any) -> None: super().__init__(parent=parent, name=name)
[docs] self.microwave = Parameter( name="microwave", instrument=self, initial_cache_value=kwargs.get("microwave", f"{parent.name}:mw"), set_cmd=False, )
"""Name of the element's microwave port."""
[docs] self.gate = Parameter( name="gate", instrument=self, initial_cache_value=kwargs.get("gate", f"{parent.name}:gt"), set_cmd=False, )
"""Name of the element's ohmic gate port."""
[docs] self.readout = Parameter( name="readout", instrument=self, initial_cache_value=kwargs.get("readout", f"{parent.name}:res"), set_cmd=False, )
"""Name of the element's readout port."""
[docs] class ClocksFrequenciesSpin(InstrumentChannel): """Submodule containing the clock frequencies specifying the transitions to address.""" def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any) -> None: super().__init__(parent=parent, name=name)
[docs] self.f_larmor = ManualParameter( name="f_larmor", instrument=self, label="Larmor frequency", unit="Hz", initial_value=kwargs.get("f_larmor", math.nan), vals=Numbers(min_value=0, max_value=1e12, allow_nan=True), )
"""Larmor frequency for the spin qubit"""
[docs] self.readout = ManualParameter( name="readout", instrument=self, label="Readout frequency", unit="Hz", initial_value=kwargs.get("readout", math.nan), vals=Numbers(min_value=0, max_value=1e12, allow_nan=True), )
"""Frequency of the ro clock. """
[docs] class RxyGaussian(InstrumentChannel): """ Submodule containing parameters for performing an Rxy operation. The Rxy operation uses a Gaussian pulse. """ def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any) -> None: super().__init__(parent=parent, name=name)
[docs] self.amp180 = ManualParameter( name="amp180", instrument=self, label=r"$\pi-pulse amplitude$", initial_value=kwargs.get("amp180", math.nan), unit="", vals=Numbers(min_value=-10, max_value=10, allow_nan=True), )
r"""Amplitude required to perform a $\pi$ pulse."""
[docs] self.duration = ManualParameter( name="duration", instrument=self, initial_value=kwargs.get("duration", 20e-9), unit="s", vals=validators.Numbers(min_value=0, max_value=1), )
"""Duration of the control pulse.""" self.add_submodule( name="reference_magnitude", submodule=ReferenceMagnitude( parent=self, name="reference_magnitude", dBm=kwargs.get("reference_magnitude_dBm", math.nan), V=kwargs.get("reference_magnitude_V", math.nan), A=kwargs.get("reference_magnitude_A", math.nan), ), ) """Reference magnitude."""
[docs] class DispersiveMeasurementSpin(DispersiveMeasurement): """ Submodule containing parameters to perform a measurement. The measurement that is performed is using :func:`~quantify_scheduler.operations.measurement_factories.dispersive_measurement_spin`. """ def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any) -> None: super().__init__(parent=parent, name=name, **kwargs)
[docs] self.gate_pulse_amp = ManualParameter( name="gate_pulse_amp", instrument=self, initial_value=kwargs.get("gate_pulse_amp", 0), unit="", vals=validators.Numbers(min_value=-1, max_value=1), )
"""Amplitude of the gate pulse."""
[docs] class BasicSpinElement(DeviceElement): """ A device element representing a Loss–DiVincenzo Spin qubit. The element refers to the intrinsic spin-1/2 degree of freedom of individual electrons/holes trapped in quantum dots. The charge of the particule is coupled to a resonator. Parameters ---------- name The name of the spin element. kwargs Can be used to pass submodule initialization data by using submodule name as keyword and as argument a dictionary containing the submodule parameter names and their value. """ def __init__(self, name: str, **kwargs: Any) -> None:
[docs] submodules_to_add = { "reset": IdlingReset, "rxy": RxyGaussian, "measure": DispersiveMeasurementSpin, "ports": PortsSpin, "clock_freqs": ClocksFrequenciesSpin, }
[docs] submodule_data = { sub_name: kwargs.pop(sub_name, {}) for sub_name in submodules_to_add }
super().__init__(name, **kwargs) for sub_name, sub_class in submodules_to_add.items(): self.add_submodule( sub_name, sub_class( parent=self, name=sub_name, **submodule_data.get(sub_name, {}) ), )
[docs] self.reset: IdlingReset
"""Submodule :class:`~.IdlingReset`."""
[docs] self.rxy: RxyGaussian
"""Submodule :class:`~.RxyGaussian`."""
[docs] self.measure: DispersiveMeasurementSpin
"""Submodule :class:`~.DispersiveMeasurementSpin`."""
[docs] self.ports: PortsSpin
"""Submodule :class:`~.PortsSpin`."""
[docs] self.clock_freqs: ClocksFrequenciesSpin
"""Submodule :class:`~.ClocksFrequenciesSpin`."""
[docs] def _generate_config(self) -> Dict[str, Dict[str, OperationCompilationConfig]]: """ Generate part of the device configuration specific to a single qubit. This method is intended to be used when this object is part of a device object containing multiple elements. """ qubit_config = { f"{self.name}": { "reset": OperationCompilationConfig( factory_func=pulse_library.IdlePulse, factory_kwargs={ "duration": self.reset.duration(), }, ), # example of a pulse with a parametrized mapping, using a factory "Rxy": OperationCompilationConfig( factory_func=pulse_factories.rxy_gauss_pulse, factory_kwargs={ "amp180": self.rxy.amp180(), "port": self.ports.microwave(), "clock": f"{self.name}.f_larmor", "duration": self.rxy.duration(), "reference_magnitude": pulse_library.ReferenceMagnitude.from_parameter( self.rxy.reference_magnitude ), }, gate_info_factory_kwargs=[ "theta", "phi", ], # the keys from the gate info to pass to the factory function ), "Rz": OperationCompilationConfig( factory_func=pulse_factories.phase_shift, factory_kwargs={ "clock": f"{self.name}.f_larmor", }, gate_info_factory_kwargs=[ "theta", ], # the keys from the gate info to pass to the factory function ), "H": OperationCompilationConfig( factory_func=composite_factories.hadamard_as_y90z, factory_kwargs={ "qubit": f"{self.name}", }, ), # the measurement also has a parametrized mapping, and uses a # factory function. "measure": OperationCompilationConfig( factory_func=measurement_factories.dispersive_measurement_spin, factory_kwargs={ "port": self.ports.readout(), "clock": f"{self.name}.ro", "gate_port": self.ports.gate(), "pulse_type": self.measure.pulse_type(), "pulse_amp": self.measure.pulse_amp(), "gate_pulse_amp": self.measure.gate_pulse_amp(), "pulse_duration": self.measure.pulse_duration(), "acq_delay": self.measure.acq_delay(), "acq_duration": self.measure.integration_time(), "acq_channel": self.measure.acq_channel(), "acq_protocol_default": "SSBIntegrationComplex", "reset_clock_phase": self.measure.reset_clock_phase(), "reference_magnitude": pulse_library.ReferenceMagnitude.from_parameter( self.measure.reference_magnitude ), "acq_weights_a": self.measure.acq_weights_a(), "acq_weights_b": self.measure.acq_weights_b(), "acq_weights_sampling_rate": self.measure.acq_weights_sampling_rate(), "acq_rotation": self.measure.acq_rotation(), "acq_threshold": self.measure.acq_threshold(), "num_points": self.measure.num_points(), "freq": None, }, gate_info_factory_kwargs=[ "acq_channel_override", "acq_index", "bin_mode", "acq_protocol", "feedback_trigger_label", ], ), } } return qubit_config
[docs] def generate_device_config(self) -> DeviceCompilationConfig: """ Generate a valid device config. The config will be used for the quantify-scheduler making use of the :func:`~.circuit_to_device.compile_circuit_to_device_with_config_validation` function. This enables the settings of this qubit to be used in isolation. .. note: This config is only valid for single qubit experiments. """ cfg_dict = { "elements": self._generate_config(), "clocks": { f"{self.name}.f_larmor": self.clock_freqs.f_larmor(), f"{self.name}.ro": self.clock_freqs.readout(), }, "edges": {}, } dev_cfg = DeviceCompilationConfig.model_validate(cfg_dict) return dev_cfg