Source code for qibolab.instruments.dummy

from dataclasses import dataclass
from typing import Dict, List, Optional

import numpy as np
from qibo.config import log

from qibolab import AcquisitionType, AveragingMode, ExecutionParameters
from qibolab.platform import Coupler, Qubit
from qibolab.pulses import PulseSequence
from qibolab.qubits import QubitId
from qibolab.sweeper import Sweeper
from qibolab.unrolling import Bounds

from .abstract import Controller
from .oscillator import LocalOscillator
from .port import Port

SAMPLING_RATE = 1


[docs]@dataclass class DummyPort(Port): name: str offset: float = 0.0 lo_frequency: int = 0 lo_power: int = 0 gain: int = 0 attenuation: int = 0 power_range: int = 0 filters: Optional[dict] = None
[docs]class DummyDevice: """Dummy device that does nothing but follows the QCoDeS interface. Used by :class:`qibolab.instruments.dummy.DummyLocalOscillator`. """
[docs] def set(self, name, value): """Set device property."""
[docs] def get(self, name): """Get device property.""" return 0
[docs] def on(self): """Turn device on."""
[docs] def off(self): """Turn device on."""
[docs] def close(self): """Close connection with device."""
[docs]class DummyLocalOscillator(LocalOscillator): """Dummy local oscillator instrument. Useful for testing the interface defined in :class:`qibolab.instruments.oscillator.LocalOscillator`. """
[docs] def create(self): return DummyDevice()
[docs]class DummyInstrument(Controller): """Dummy instrument that returns random voltage values. Useful for testing code without requiring access to hardware. Args: name (str): name of the instrument. address (int): address to connect to the instrument. Not used since the instrument is dummy, it only exists to keep the same interface with other instruments. """ BOUNDS = Bounds(1, 1, 1) PortType = DummyPort @property def sampling_rate(self): return SAMPLING_RATE
[docs] def connect(self): log.info(f"Connecting to {self.name} instrument.")
[docs] def disconnect(self): log.info(f"Disconnecting {self.name} instrument.")
[docs] def setup(self, *args, **kwargs): log.info(f"Setting up {self.name} instrument.")
[docs] def get_values(self, options, ro_pulse, shape): if options.acquisition_type is AcquisitionType.DISCRIMINATION: if options.averaging_mode is AveragingMode.SINGLESHOT: values = np.random.randint(2, size=shape) elif options.averaging_mode is AveragingMode.CYCLIC: values = np.random.rand(*shape) elif options.acquisition_type is AcquisitionType.RAW: samples = int(ro_pulse.duration * SAMPLING_RATE) waveform_shape = tuple(samples * dim for dim in shape) values = ( np.random.rand(*waveform_shape) * 100 + 1j * np.random.rand(*waveform_shape) * 100 ) elif options.acquisition_type is AcquisitionType.INTEGRATION: values = np.random.rand(*shape) * 100 + 1j * np.random.rand(*shape) * 100 return values
[docs] def play( self, qubits: Dict[QubitId, Qubit], couplers: Dict[QubitId, Coupler], sequence: PulseSequence, options: ExecutionParameters, ): exp_points = ( 1 if options.averaging_mode is AveragingMode.CYCLIC else options.nshots ) shape = (exp_points,) results = {} for ro_pulse in sequence.ro_pulses: values = np.squeeze(self.get_values(options, ro_pulse, shape)) results[ro_pulse.qubit] = results[ro_pulse.serial] = options.results_type( values ) return results
[docs] def sweep( self, qubits: Dict[QubitId, Qubit], couplers: Dict[QubitId, Coupler], sequence: PulseSequence, options: ExecutionParameters, *sweepers: List[Sweeper], ): results = {} if options.averaging_mode is not AveragingMode.CYCLIC: shape = (options.nshots,) + tuple( len(sweeper.values) for sweeper in sweepers ) else: shape = tuple(len(sweeper.values) for sweeper in sweepers) for ro_pulse in sequence.ro_pulses: values = self.get_values(options, ro_pulse, shape) results[ro_pulse.qubit] = results[ro_pulse.serial] = options.results_type( values ) return results