From f94276b11c73a8b82542e1ec10f0141a125d5ad7 Mon Sep 17 00:00:00 2001 From: Roland Siegbert Date: Sun, 21 Apr 2024 21:18:53 +0200 Subject: [PATCH] Move instruments to .py and literalincludes into docs --- .../includes/instrument/instrument0.py | 42 +++++++ .../includes/instrument/instrument1.py | 79 ++++++++++++ .../instrument/proprietary_instruments.py | 54 +++++++++ doc/source/tutorials/instrument.rst | 113 +----------------- 4 files changed, 177 insertions(+), 111 deletions(-) create mode 100644 doc/source/tutorials/includes/instrument/instrument0.py create mode 100644 doc/source/tutorials/includes/instrument/instrument1.py create mode 100644 doc/source/tutorials/includes/instrument/proprietary_instruments.py diff --git a/doc/source/tutorials/includes/instrument/instrument0.py b/doc/source/tutorials/includes/instrument/instrument0.py new file mode 100644 index 000000000..a08d3b8b6 --- /dev/null +++ b/doc/source/tutorials/includes/instrument/instrument0.py @@ -0,0 +1,42 @@ +# let's suppose that there is already avaiable a base driver for connection +# and control of the device +from proprietary_instruments import BiaserDriver + +from qibolab.instruments.abstract import Instrument + + +class Biaser(Instrument): + """An instrument that delivers constant biases.""" + + def __init__(self, name, address, min_value=-1, max_value=1): + super().__init__(name, address) + self.max_value: float = ( + max_value # attribute example, maximum value of voltage allowed + ) + self.min_value: float = ( + min_value # attribute example, minimum value of voltage allowed + ) + self.bias: float = 0 + + self.device = BiaserDriver(address) + + def connect(self): + """Check if a connection is avaiable.""" + if not self.device.is_connected: + raise ConnectionError("Biaser not connected") + + def disconnect(self): + """Method not used.""" + + # FIXME:: *args, **kwargs are not passed on + def setup(self): + """Set biaser parameters.""" + self.device.set_range(self.min_value, self.max_value) + + def start(self): + """Start biasing.""" + self.device.on(bias) + + def stop(self): + """Stop biasing.""" + self.device.off(bias) diff --git a/doc/source/tutorials/includes/instrument/instrument1.py b/doc/source/tutorials/includes/instrument/instrument1.py new file mode 100644 index 000000000..dfe5f5cb7 --- /dev/null +++ b/doc/source/tutorials/includes/instrument/instrument1.py @@ -0,0 +1,79 @@ +from typing import Union + +from proprietary_instruments import ControllerDriver + +from qibolab.execution_parameters import ExecutionParameters +from qibolab.instruments.abstract import Controller +from qibolab.pulses import PulseSequence +from qibolab.qubits import Qubit +from qibolab.result import IntegratedResults, SampleResults +from qibolab.sweeper import Sweeper + + +class MyController(Controller): + def __init__(self, name, address): + self.device = ControllerDriver(address) + super().__init__(name, address) + + def connect(self): + """Empty method to comply with Instrument interface.""" + + def start(self): + """Empty method to comply with Instrument interface.""" + + def stop(self): + """Empty method to comply with Instrument interface.""" + + def disconnect(self): + """Empty method to comply with Instrument interface.""" + + # FIXME:: *args, **kwargs are not passed on + def setup(self): + """Empty method to comply with Instrument interface.""" + + # FIXME:: this seems to be incompatbile with the ABC, too + def play( + self, + qubits: dict[int, Qubit], + sequence: PulseSequence, + execution_parameters: ExecutionParameters, + ) -> dict[str, Union[IntegratedResults, SampleResults]]: + """Executes a PulseSequence.""" + + # usually, some modification on the qubit objects, sequences or + # parameters is needed so that the qibolab interface comply with the one + # of the device here these are equal + results = self.device.run_experiment(qubits, sequence, execution_parameters) + + # also the results are, in qibolab, specific objects that need some kind + # of conversion. Refer to the results section in the documentation. + return results + + # FIXME:: this seems to be incompatbile with the ABC, too + def sweep( + self, + qubits: dict[int, Qubit], + sequence: PulseSequence, + execution_parameters: ExecutionParameters, + *sweepers: Sweeper, + ) -> dict[str, Union[IntegratedResults, SampleResults]]: + # usually, some modification on the qubit objects, sequences or + # parameters is needed so that the qibolab interface comply with the one + # of the device here these are equal + results = self.device.run_scan(qubits, sequence, sweepers, execution_parameters) + + # also the results are, in qibolab, specific objects that need some kind + # of conversion. Refer to the results section in the documentation. + return results + + def play_sequences( + self, + qubits: dict[int, Qubit], + sequences: list[PulseSequence], + execution_parameters: ExecutionParameters, + ) -> dict[str, Union[IntegratedResults, SampleResults]]: + """This method is used for sequence unrolling sweeps. + + Here not implemented. + """ + raise NotImplementedError diff --git a/doc/source/tutorials/includes/instrument/proprietary_instruments.py b/doc/source/tutorials/includes/instrument/proprietary_instruments.py new file mode 100644 index 000000000..4b905d3b3 --- /dev/null +++ b/doc/source/tutorials/includes/instrument/proprietary_instruments.py @@ -0,0 +1,54 @@ +"""Dummy class to provide a device driver for example in instrument0.py.""" + +from enum import Enum + + +class State(Enum): + OFF = 0 + ON = 1 + + +class BiaserDriver: + + def __init__(self, address): + self.address = address + self.bias = 0 + self.state = State.OFF + + def is_connected(self): + return True + + def set_range(self, min_value=0, max_value=65536): + self.min_value = min_value + self.max_value = max_value + + def on(self, bias=0): + self.bias = bias + self.state = State.ON + + def off(self, bias=0): + self.bias = bias + self.state = State.OFF + + +class ControllerDriver: + + def __init__(self, address): + self.address = address + self.bias = 0 + self.state = State.OFF + + def is_connected(self): + return True + + def set_range(self, min_value=0, max_value=65536): + self.min_value = min_value + self.max_value = max_value + + def on(self, bias=0): + self.bias = bias + self.state = State.ON + + def off(self, bias=0): + self.bias = bias + self.state = State.OFF diff --git a/doc/source/tutorials/instrument.rst b/doc/source/tutorials/instrument.rst index db30ce641..015d036e8 100644 --- a/doc/source/tutorials/instrument.rst +++ b/doc/source/tutorials/instrument.rst @@ -48,50 +48,7 @@ A good example of a instrument driver is the Here, let's write a basic example of instrument whose job is to deliver a fixed bias for the duration of the experiment: -.. code-block:: python - - from qibolab.instruments.abstract import Instrument - - # let's suppose that there is already avaiable a base driver for connection - # and control of the device - from proprietary_instruments import biaser_driver - - - class Biaser(Instrument): - """An instrument that delivers constand biases.""" - - def __init__(self, name, address, min_value=-1, max_value=1): - super().__init__(name, address) - self.max_value: float = ( - max_value # attribute example, maximum value of voltage allowed - ) - self.min_value: float = ( - min_value # attribute example, minimum value of voltage allowed - ) - self.bias: float = 0 - - self.device = biaser_driver(address) - - def connect(self): - """Check if a connection is avaiable.""" - if not self.device.is_connected: - raise ConnectionError("Biaser not connected") - - def disconnect(self): - """Method not used.""" - - def setup(self): - """Set biaser parameters.""" - self.device.set_range(self.min_value, self.max_value) - - def start(self): - """Start biasing.""" - self.device.on(bias) - - def stop(self): - """Stop biasing.""" - self.device.off(bias) - +.. literalinclude:: ./includes/instrument/instrument0.py Add a controller ---------------- @@ -111,73 +68,7 @@ complex than the local oscillator ones. Let's see a minimal example: -.. code-block:: python - - from qibolab.instruments.abstract import Controller - from proprietary_instruments import controller_driver - - - class MyController(Controller): - def __init__(self, name, address): - self.device = controller_driver(address) - super().__init__(name, address) - - def connect(self): - """Empty method to comply with Instrument interface.""" - - def start(self): - """Empty method to comply with Instrument interface.""" - - def stop(self): - """Empty method to comply with Instrument interface.""" - - def disconnect(self): - """Empty method to comply with Instrument interface.""" - - def setup(self): - """Empty method to comply with Instrument interface.""" - - def play( - self, - qubits: dict[int, Qubit], - sequence: PulseSequence, - execution_parameters: ExecutionParameters, - ) -> dict[str, Union[IntegratedResults, SampleResults]]: - """Executes a PulseSequence.""" - - # usually, some modification on the qubit objects, sequences or - # parameters is needed so that the qibolab interface comply with the one - # of the device here these are equal - results = self.device.run_experiment(qubits, sequence, execution_parameters) - - # also the results are, in qibolab, specific objects that need some kind - # of conversion. Refer to the results section in the documentation. - return results - - def sweep( - self, - qubits: dict[int, Qubit], - sequence: PulseSequence, - execution_parameters: ExecutionParameters, - *sweepers: Sweeper, - ) -> dict[str, Union[IntegratedResults, SampleResults]]: - # usually, some modification on the qubit objects, sequences or - # parameters is needed so that the qibolab interface comply with the one - # of the device here these are equal - results = self.device.run_scan(qubits, sequence, sweepers, execution_parameters) - - # also the results are, in qibolab, specific objects that need some kind - # of conversion. Refer to the results section in the documentation. - return results - - def play_sequences( - self, - qubits: dict[int, Qubit], - sequences: List[PulseSequence], - execution_parameters: ExecutionParameters, - ) -> dict[str, Union[IntegratedResults, SampleResults]]: - """This method is used for sequence unrolling sweeps. Here not implemented.""" - raise NotImplementedError +.. literalinclude:: ./includes/instrument/instrument1.py As we saw in :doc:`lab`, to instantiate a platform at some point you have to write something like this: