Source code for semi_cr.core.station.chip
"""
This module declares a Chip class representing a quantum chip and its associated devices. For now,
it's deserializable from the QCoDes YAML format, but in the future it should hold more chip-related information.
Example of a chip configuration in YAML format:
.. code-block:: yaml
instruments:
X002001C1:
type: semi_cr.core.lab.instrument.chip.Chip
init:
devices:
D1:
type: semi_cr.core.lab.devices.multiplexer.Multiplexer
pins:
T2:
type: Gate
pads:
- QboardII[8]
T4:
type: Gate
pads:
- QboardII[17]
B1:
type: Gate
pads:
- QboardII[38]
B4:
type: Gate
pads:
- QboardII[32]
RF_In:
type: RFInput
pads:
- QboardII[RF1]
RF_Out_1:
type: RFOutput
pads:
- QboardII[RF4]
RF_Out_2:
type: RFOutput
pads:
- QboardII[RF8]
RF_Out_3:
type: RFOutput
pads:
- QboardII[RF10]
RF_Out_4:
type: RFOutput
pads:
- QboardII[RF12]
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from semi_cr.core.station.device import QuantumDevice
from semi_cr.core.station.graph import GraphEdge, GraphNode
if TYPE_CHECKING:
from collections.abc import Iterable
from quam_builder.architecture.quantum_dots import BaseQuamQD
from semi_cr.core.station.instruments.qcodes_proxy import QCoDeSInstrumentSpec
from semi_cr.core.station.station import NQCPStation
[docs]
@dataclass
class Chip:
chip_id: str
parent_ref: NQCPStation = field(repr=False, compare=False)
devices: list[QuantumDevice] = field(default_factory=list, compare=False, repr=False)
def __rich__(self): # noqa: PLW3201
yield ("Chip ID", self.chip_id)
[docs]
def device(self, device_id: str) -> QuantumDevice:
"""Gets a device by name.
:returns: The device matching ``device_id``.
:raises KeyError: If no matching device exists.
"""
for device in self.devices:
if device.device_id == device_id:
return device
raise KeyError(f"Device not found: {device_id}")
[docs]
@classmethod
def from_instrument_spec(cls, spec: QCoDeSInstrumentSpec, parent_ref: NQCPStation) -> Chip:
"""Given the parsed yaml of a chip, return a Chip object. Recursively creates QuantumDevice objects.
`spec` is a tuple:
- First element is the identifier of the Chip
- Second element is the QCoDes compatible YAML structure underneath it
"""
chip_id = spec[0]
aux_data = spec[1]
# Just a quick sanity check...
if aux_data["type"] != "semi_cr.core.lab.instrument.chip.Chip":
raise ValueError("Chip type not supported")
# Instantiate QuantumDevice objects from the chip's manifest
device_configs = aux_data["init"]["devices"].items()
# device_configs are now tuples of key -> dict pairs
chip = cls(
chip_id=chip_id,
parent_ref=parent_ref,
)
chip.devices = [QuantumDevice.from_instrument_spec(device_config, chip) for device_config in device_configs]
return chip
[docs]
def graph_nodes(self) -> Iterable[GraphNode]:
"""Iterates over all devices to return the graph nodes for this chip."""
for device in self.devices:
yield from device.graph_nodes()
[docs]
def graph_edges(self) -> Iterable[GraphEdge]:
"""Iterates over all devices to return any graph edges for this chip."""
for device in self.devices:
yield from device.graph_edges()
[docs]
def to_quam(self) -> BaseQuamQD:
"""Exports a representation of the current chip for use with quam-builder, such as the gate tuning web app
Torbjørn presented. At the meeting on 2026-09-28 we decided that it should live on our Chip representation,
not device or station.
:returns: A :class:`BaseQuamQD` representation of the current chip."""
# We start by making some QM-related imports. We don't want to do that globally because it carries side
# effects and takes forever. Disabling PLC0415 momentarily lets us do that.
from quam_builder.architecture.quantum_dots import BaseQuamQD # noqa: PLC0415
state = BaseQuamQD()
# TODO: Write a test suite for this integration WHILE WE DESIGN IT to make sure the API stays user-centric
# TODO: Look at the builders `build_quam.py` (is in quam_builder/builder/quantum_dots on release/nightly)
raise NotImplementedError()
return state