Skip to content

Commit

Permalink
Move instruments to .py and literalincludes into docs
Browse files Browse the repository at this point in the history
  • Loading branch information
rscircus committed Jul 2, 2024
1 parent 0b60d8e commit f94276b
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 111 deletions.
42 changes: 42 additions & 0 deletions doc/source/tutorials/includes/instrument/instrument0.py
Original file line number Diff line number Diff line change
@@ -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)
79 changes: 79 additions & 0 deletions doc/source/tutorials/includes/instrument/instrument1.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
113 changes: 2 additions & 111 deletions doc/source/tutorials/instrument.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------
Expand All @@ -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:
Expand Down

0 comments on commit f94276b

Please sign in to comment.