Source code for semi_cr.core.trial.machine

"""QUAM machine building for gate-sweep trials on the OPX1000.

This module turns a plain description of the physical wiring — which LF-FEM
outputs drive which gates, and which output/input pair forms the readout
path — into a QUAM machine (:class:`NQCPMachine`) whose generated config can
be loaded onto a quantum machine.

The wiring is expressed with small frozen dataclasses (:class:`OPXOutputRef`,
:class:`OPXInputRef`, :class:`OPXWiring`) that address OPX terminals
explicitly. The longer-term goal is *gate-addressed* wiring: once station
calibration is trustworthy, an ``OPXWiring.from_station(...)`` factory can
route from a device gate through the station graph to the real OPX terminal
(see ``semi_cr.apps.spincontrol.calibration_demo`` for the prototype of that
routing).
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

from quam import QuamRoot
from quam.components.channels import InOutSingleChannel, SingleChannel, StickyChannelAddon
from quam.components.ports import LFFEMAnalogInputPort, LFFEMAnalogOutputPort
from quam.components.pulses import SquareReadoutPulse
from quam.core import quam_dataclass
from quam_builder.architecture.quantum_dots.components import GateSet

if TYPE_CHECKING:
    from collections.abc import Mapping

DEFAULT_SWEEP_GATES = ("P1", "P2")
PULSE_AMPLITUDE = 0
PULSE_LENGTH = 16


[docs] @dataclass(frozen=True) class OPXOutputRef: """Explicit address of an OPX1000 LF-FEM analog output.""" controller_id: str fem_id: int port_id: int upsampling_mode: str = "pulse"
[docs] @dataclass(frozen=True) class OPXInputRef: """Explicit address of an OPX1000 LF-FEM analog input.""" controller_id: str fem_id: int port_id: int
[docs] @dataclass(frozen=True) class OPXWiring: """How gates and the readout path map onto OPX terminals. ``sweep_gates`` names the two gates a 2D sweep varies; both must be keys of ``gate_outputs``. """ gate_outputs: Mapping[str, OPXOutputRef] readout_output: OPXOutputRef readout_input: OPXInputRef sweep_gates: tuple[str, str] = DEFAULT_SWEEP_GATES
[docs] def output_port_from_ref(ref: OPXOutputRef) -> LFFEMAnalogOutputPort: return LFFEMAnalogOutputPort( upsampling_mode=ref.upsampling_mode, controller_id=ref.controller_id, fem_id=ref.fem_id, port_id=ref.port_id, )
[docs] def input_port_from_ref(ref: OPXInputRef) -> LFFEMAnalogInputPort: return LFFEMAnalogInputPort( controller_id=ref.controller_id, fem_id=ref.fem_id, port_id=ref.port_id, )
[docs] def build_gate_channels( gate_ports: Mapping[str, LFFEMAnalogOutputPort], ) -> dict[str, SingleChannel]: """One sticky single channel per gate, so voltages hold between pulses.""" channels: dict[str, SingleChannel] = {} for gate_id, gate_port in gate_ports.items(): channels[gate_id] = SingleChannel( id=gate_id, opx_output=gate_port, sticky=StickyChannelAddon(duration=PULSE_LENGTH, analog=True, digital=False), ) return channels
[docs] @quam_dataclass class NQCPMachine(QuamRoot): gate_set: GateSet readout_resonator: InOutSingleChannel
[docs] def build_machine( gate_ports: Mapping[str, LFFEMAnalogOutputPort], readout_output_port: LFFEMAnalogOutputPort, readout_input_port: LFFEMAnalogInputPort, *, readout_length: int, ) -> NQCPMachine: """Build the QUAM machine. ``readout_length`` (ns) is a calibration parameter, not a property of the machine wiring, hence it is passed in rather than baked in here. """ channels = build_gate_channels(gate_ports) resonator_channel = InOutSingleChannel( id="R1", opx_input=readout_input_port, opx_output=readout_output_port, intermediate_frequency=0, operations={ "readout": SquareReadoutPulse(amplitude=PULSE_AMPLITUDE, length=readout_length), }, ) return NQCPMachine( gate_set=GateSet(id="dot_plungers", channels=channels), readout_resonator=resonator_channel, )
[docs] def build_machine_from_wiring(wiring: OPXWiring, *, readout_length: int) -> NQCPMachine: return build_machine( gate_ports={gate: output_port_from_ref(ref) for gate, ref in wiring.gate_outputs.items()}, readout_output_port=output_port_from_ref(wiring.readout_output), readout_input_port=input_port_from_ref(wiring.readout_input), readout_length=readout_length, )