Skip to content

Commit

Permalink
docs: add Meta instrument.
Browse files Browse the repository at this point in the history
  • Loading branch information
giulioungaretti committed Aug 3, 2016
1 parent e76f95b commit aa5e6db
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 50 deletions.
108 changes: 108 additions & 0 deletions docs/user/meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
" MWE of meta instrument (with debugging enabled) "
import concurrent.futures as futures
import logging
import multiprocessing as mp
import time

from functools import partial

from qcodes import Instrument


# agreesive logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s- %(message)s')

class MyInstrument(Instrument):

def __init__(self, name, **kwargs):
super().__init__(name, **kwargs)
self.value=0
self.add_parameter('x', get_cmd=self.getx, set_cmd=self.setx)

def getx(self):
return self.value

def setx(self, val):
logging.debug("set {}".format(val))
self.value = val
# simulate delay 5 seconds
time.sleep(5)
logging.debug("done {}".format(val))
return


class Meta(Instrument):
shared_kwargs = ['instruments']

# Instruments will be a list of RemoteInstrument objects, which can be
# given to a server on creation but not later on, so it needs to be
# listed in shared_kwargs

def __init__(self, name, instruments=(), **kwargs):
super().__init__(name, **kwargs)
self._instrument_list = instruments
self.no_instruments = len(instruments)
for gate in range(len(self._instrument_list)):
self.add_parameter('c%d' % gate,
get_cmd=partial(self._get, gate=gate),
set_cmd=partial(self._set, gate=gate))

self.add_parameter("setBoth", set_cmd=partial(self._set_both))
self.add_parameter("setBothAsync", set_cmd=partial(self._set_async))

def _set_both(self, value):
for i in self._instrument_list:
i.set('x', value)

def _set_async(self, value):
with futures.ThreadPoolExecutor(max_workers=self.no_instruments+10) as executor:
jobs = []
for i in self._instrument_list:
job = executor.submit(partial(i.set, 'x'), value)
jobs.append(job)

futures.wait(jobs)

def _get(self, gate):
value =self._instrument_list[gate].get('x')
logging.debug('Meta get gate %s' % (value))
return value

def _set(self, value, gate):
logging.debug('Meta set gate %s @ value %s' % (gate, value))
i = self._instrument_list[gate]
i.set('x', value)



if __name__ == '__main__':

mp.set_start_method('spawn')

base1 = MyInstrument(name='zero', server_name="foo")
base2 = MyInstrument(name='one', server_name="bar")

meta_server_name = "meta_server"
meta = Meta(name='meta', server_name=meta_server_name,
instruments=[base1, base2])


print("--- set meta --- ")
meta.c1.set(25)
print(meta.c1.get())
print(base1.x.get())

print("--- set base --- ")
base1.x.set(1)
print(meta.c1.get())
print(base1.x.get())


print("--- both --- ")
meta.setBoth(0)
print(base2.x.get())
print(base1.x.get())

print("--- no block --- ")
meta.setBothAsync(10)
logging.debug(base1.x.get())
101 changes: 51 additions & 50 deletions docs/user/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ In this tutorial we'll walk through *****
Writing a Driver
----------------

Write a simple driver example
Write a simple driver example
with commented code
- add parameter
- add validator
Expand All @@ -35,12 +35,15 @@ Explain the mock mock
.. todo:: missing


Composite Instruments
---------------------
The concept of a composite instrument is that of having
.. __metainstrument :

Meta Instruments
---------------------
The concept of a meta instrument is that of having
two separate Instrument, real or virtual, whose actions can
the be controlled from the composite instrument.
In the following example we will create two dummy instruments and a composite instruments.
the be controlled from the meta instrument.
In the following example we will create two dummy instruments and a meta instruments.
>>>>>>> docs: add Meta instrument.
All the instruments will live on a InstrumentServer.


Expand All @@ -64,43 +67,46 @@ First we create an instrument:
def setx(self, val):
self.x=val
Then we create the composite instrument, this will hold any of the base
Then we create the meta instrument, this will hold any of the base
instruments.
Since we want the composite instrument to be able to talk to the base instrumetns
Since we want the meta instrument to be able to talk to the base instruments
we need to include a list of them as shared_kwargs.


.. note:: Every InstrumentServer needs to have identical shared_kwargs among all the instruments loaded there. That's because these args get loaded into the server when it's created, then passed on to each instrument that's loaded there during its construction on the server side.
.. note:: Every InstrumentServer needs to have identical shared_kwargs among all the instruments loaded there. That's because these args get loaded into the server when it's created, then passed on to each instrument that's loaded there during its construction on the server side.

.. code:: python
class base1base2(Instrument):
class Meta(Instrument):
shared_kwargs = ['instruments']
# Instruments will be a list of RemoteInstrument objects, which can be
# given to a server on creation but not later on, so it needs to be
# listed in shared_kwargs
def __init__(self, name, instruments=(), **kwargs):
super().__init__(name, **kwargs)
self._instrument_list = instruments
self.no_instruments = len(instruments)
for gate in range(len(self._instrument_list)):
self.add_parameter('c%d' % gate,
get_cmd=partial(self._get, gate=gate),
set_cmd=partial(self._set, gate=gate))
self.add_parameter("setBoth", set_cmd= partial(self._set_both))
self.add_parameter("setBoth", set_cmd=partial(self._set_both))
self.add_parameter("setBothAsync", set_cmd=partial(self._set_async))
def _set_both(self, value):
for i in self._instrument_list:
i.set('x', value)
def _get(self, gate):
logging.debug('base1base2._get: %s' % (gate,))
return self._instrument_list[gate].get('x')
value =self._instrument_list[gate].get('x')
logging.debug('Meta get gate %s' % (value))
return value
def _set(self, value, gate):
logging.debug('base1base2._set: gate %s, value %s' % (gate, value))
logging.debug('Meta set gate %s @ value %s' % (gate, value))
i = self._instrument_list[gate]
i.set('x', value)
Expand All @@ -116,52 +122,54 @@ Let's put these babies on servers:
That means that base1 and base2 don't know about eachoter.

.. code:: python
composite_server_name
composite = base1base2(name='composite', server_name=composite_server_name,
meta_server_name = "meta_server"
meta = Meta(name='meta', server_name=meta_server_name,
instruments=[base1, base2])
.. notes:: Composite instruments go on a different server from the
low-level instruments it references, because reaons.
.. notes:: Meta instruments go on a different server from the
low-level instruments it references, because reasons.


And now one case use the composite as expected:
And now one case use the meta as expected:

.. code:: python
print("--- set composite --- ")
composite.c1.set(25)
print(composite.c1.get())
print("--- set meta --- ")
meta.c1.set(25)
print(meta.c1.get())
>>> 25
print(base1.x.get())
>>> 25
print("--- set base --- ")
base1.x.set(1)
print(composite.c1.get())
print(meta.c1.get())
>>> 1
print(base1.x.get())
>>> 1
composite.setBoth(0)
meta.setBoth(0)
print(base1.x.get())
>>> 0
print(base0.x.get())
>>> 0
Async Composite
~~~~~~~~~~~~~~~
Say you want to set two instruments at the same time.
Async Meta
==========

Say you want to set two instruments at the same time.
You can use the following:

.. note:: the curernt architecture is so b0rken, you MUST one server per base instrument
.. note:: the curernt architecture is so that you MUST one server per base instrument

The base instrument classe stays the same, composite gets a new method f.ex:
The base instrument class stays the same, meta gets a new method f.ex:

.. code:: python
class base1base2(Instrument):
class Meta(Instrument):
shared_kwargs = ['instruments']
# Instruments will be a list of RemoteInstrument objects, which can be
Expand All @@ -175,8 +183,8 @@ The base instrument classe stays the same, composite gets a new method f.ex:
self.add_parameter('c%d' % gate,
get_cmd=partial(self._get, gate=gate),
set_cmd=partial(self._set, gate=gate))
self.add_parameter("setBoth", set_cmd= partial(self._set_both))
self.add_parameter("setBothAsync", set_cmd= partial(self._set_async))
self.add_parameter("setBoth", set_cmd=partial(self._set_both))
self.add_parameter("setBothAsync", set_cmd=partial(self._set_async))
def _set_both(self, value):
for i in self._instrument_list:
Expand All @@ -188,33 +196,26 @@ The base instrument classe stays the same, composite gets a new method f.ex:
for i in self._instrument_list:
job = executor.submit(partial(i.set, 'x'), value)
jobs.append(job)
futures.wait(jobs)
futures.wait(jobs)
def _get(self, gate):
logging.debug('base1base2._get: %s' % (gate,))
return self._instrument_list[gate].get('x')
value =self._instrument_list[gate].get('x')
logging.debug('Meta get gate %s' % (value))
return value
def _set(self, value, gate):
logging.debug('base1base2._set: gate %s, value %s' % (gate, value))
logging.debug('Meta set gate %s @ value %s' % (gate, value))
i = self._instrument_list[gate]
i.set('x', value)
# note the different server names
# that's required
base0 = MyInstrument(name='zero', server_name="foo")
base1 = MyInstrument(name='one', server_name="bar")
composite_server_name = "composite_server"
composite = base1base2(name='composite', server_name=composite_server_name,
instruments=[base0, base1])
This way:
>>> meta.setBothAsync(0)

This way:
>>> composite.setBothAsync(0)

will set both instrument at the same time, say it takes 10 seconds per set,
will set both instrument at the same time, say it takes 10 seconds per set,
then setting two things will take 10 seconds, not 20 seconds.


For a complete working example see :download:`this example script <./meta.py>`.

Avanced
-------
Expand Down

0 comments on commit aa5e6db

Please sign in to comment.