Skip to content

Commit

Permalink
Multiparameter fixes (#512)
Browse files Browse the repository at this point in the history
* Fix: Partial fix for #499 propagate units to subarray

* Fix: This is a setpoint array by definition

* fix: test_loop rename setpoint arrays

* fix: test_measure rename setpoint arrays

* Fix: Add setpoint_units to Array and Multiparameter

* Fix: add handling of setpoint units to loops

* Add test for multiparameter units setpoint_units

* Remove assert

* Add setpoint_units to meta_arrr so its correctly shapshoted

* Fix: remove unused variable
  • Loading branch information
jenshnielsen authored and giulioungaretti committed Mar 7, 2017
1 parent de88c36 commit b5da731
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 26 deletions.
25 changes: 21 additions & 4 deletions qcodes/instrument/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,9 @@ class ArrayParameter(_BaseParameter):
per setpoint array. Ignored if a setpoint is a DataArray, which
already has a label.
TODO (alexcjohnson) we need setpoint_units (and in MultiParameter)
setpoint_units (Optional[Tuple[str]]): one label (like ``v``)
per setpoint array. Ignored if a setpoint is a DataArray, which
already has a unit.
docstring (Optional[str]): documentation string for the __doc__
field of the object. The __doc__ field of the instance is used by
Expand All @@ -448,14 +450,14 @@ class ArrayParameter(_BaseParameter):
def __init__(self, name, shape, instrument=None,
label=None, unit=None, units=None,
setpoints=None, setpoint_names=None, setpoint_labels=None,
docstring=None, snapshot_get=True, metadata=None):
setpoint_units=None, docstring=None, snapshot_get=True, metadata=None):
super().__init__(name, instrument, snapshot_get, metadata)

if self.has_set: # TODO (alexcjohnson): can we support, ala Combine?
raise AttributeError('ArrayParameters do not support set '
'at this time.')

self._meta_attrs.extend(['setpoint_names', 'setpoint_labels',
self._meta_attrs.extend(['setpoint_names', 'setpoint_labels', 'setpoint_units',
'label', 'unit'])

self.label = name if label is None else label
Expand Down Expand Up @@ -488,10 +490,15 @@ def __init__(self, name, shape, instrument=None,
not is_sequence_of(setpoint_labels, (nt, str),
shape=sp_shape)):
raise ValueError('setpoint_labels must be a tuple of strings')
if (setpoint_units is not None and
not is_sequence_of(setpoint_units, (nt, str),
shape=sp_shape)):
raise ValueError('setpoint_units must be a tuple of strings')

self.setpoints = setpoints
self.setpoint_names = setpoint_names
self.setpoint_labels = setpoint_labels
self.setpoint_units = setpoint_units

self.__doc__ = os.linesep.join((
'Parameter class:',
Expand Down Expand Up @@ -591,6 +598,10 @@ class MultiParameter(_BaseParameter):
``labels``) per setpoint array. Ignored if a setpoint is a
DataArray, which already has a label.
setpoint_units (Optional[Tuple[Tuple[str]]]): one unit (like
``V``) per setpoint array. Ignored if a setpoint is a
DataArray, which already has a unit.
docstring (Optional[str]): documentation string for the __doc__
field of the object. The __doc__ field of the instance is used by
some help systems, but not all
Expand All @@ -604,14 +615,15 @@ class MultiParameter(_BaseParameter):
def __init__(self, name, names, shapes, instrument=None,
labels=None, units=None,
setpoints=None, setpoint_names=None, setpoint_labels=None,
setpoint_units=None,
docstring=None, snapshot_get=True, metadata=None):
super().__init__(name, instrument, snapshot_get, metadata)

if self.has_set: # TODO (alexcjohnson): can we support, ala Combine?
raise AttributeError('MultiParameters do not support set '
'at this time.')

self._meta_attrs.extend(['setpoint_names', 'setpoint_labels',
self._meta_attrs.extend(['setpoint_names', 'setpoint_labels', 'setpoint_units',
'names', 'labels', 'units'])

if not is_sequence_of(names, str):
Expand Down Expand Up @@ -643,9 +655,14 @@ def __init__(self, name, names, shapes, instrument=None,
raise ValueError(
'setpoint_labels must be a tuple of tuples of strings')

if not _is_nested_sequence_or_none(setpoint_units, (nt, str), shapes):
raise ValueError(
'setpoint_units must be a tuple of tuples of strings')

self.setpoints = setpoints
self.setpoint_names = setpoint_names
self.setpoint_labels = setpoint_labels
self.setpoint_units = setpoint_units

self.__doc__ = os.linesep.join((
'MultiParameter class:',
Expand Down
29 changes: 19 additions & 10 deletions qcodes/loops.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,18 +556,25 @@ def _parameter_arrays(self, action):
action_indices = ((),)
else:
raise ValueError('a gettable parameter must have .name or .names')

if hasattr(action, 'names') and hasattr(action, 'units'):
units = action.units
elif hasattr(action, 'unit'):
units = (action.unit,)
else:
units = tuple(['']*len(names))
num_arrays = len(names)
shapes = getattr(action, 'shapes', None)
sp_vals = getattr(action, 'setpoints', None)
sp_names = getattr(action, 'setpoint_names', None)
sp_labels = getattr(action, 'setpoint_labels', None)
sp_units = getattr(action, 'setpoint_units', None)

if shapes is None:
shapes = (getattr(action, 'shape', ()),) * num_arrays
sp_vals = (sp_vals,) * num_arrays
sp_names = (sp_names,) * num_arrays
sp_labels = (sp_labels,) * num_arrays
sp_units = (sp_units,) * num_arrays
else:
sp_blank = (None,) * num_arrays
# _fill_blank both supplies defaults and tests length
Expand All @@ -576,34 +583,36 @@ def _parameter_arrays(self, action):
sp_vals = self._fill_blank(sp_vals, sp_blank)
sp_names = self._fill_blank(sp_names, sp_blank)
sp_labels = self._fill_blank(sp_labels, sp_blank)
sp_units = self._fill_blank(sp_units, sp_blank)

# now loop through these all, to make the DataArrays
# record which setpoint arrays we've made, so we don't duplicate
all_setpoints = {}
for name, full_name, label, shape, i, sp_vi, sp_ni, sp_li in zip(
names, full_names, labels, shapes, action_indices,
sp_vals, sp_names, sp_labels):
for name, full_name, label, unit, shape, i, sp_vi, sp_ni, sp_li, sp_ui in zip(
names, full_names, labels, units, shapes, action_indices,
sp_vals, sp_names, sp_labels, sp_units):

if shape is None or shape == ():
shape, sp_vi, sp_ni, sp_li = (), (), (), ()
shape, sp_vi, sp_ni, sp_li, sp_ui= (), (), (), (), ()
else:
sp_blank = (None,) * len(shape)
sp_vi = self._fill_blank(sp_vi, sp_blank)
sp_ni = self._fill_blank(sp_ni, sp_blank)
sp_li = self._fill_blank(sp_li, sp_blank)
sp_ui = self._fill_blank(sp_ui, sp_blank)

setpoints = ()
# loop through dimensions of shape to make the setpoint arrays
for j, (vij, nij, lij) in enumerate(zip(sp_vi, sp_ni, sp_li)):
sp_def = (shape[: 1 + j], j, setpoints, vij, nij, lij)
for j, (vij, nij, lij, uij) in enumerate(zip(sp_vi, sp_ni, sp_li, sp_ui)):
sp_def = (shape[: 1 + j], j, setpoints, vij, nij, lij, uij)
if sp_def not in all_setpoints:
all_setpoints[sp_def] = self._make_setpoint_array(*sp_def)
out.append(all_setpoints[sp_def])
setpoints = setpoints + (all_setpoints[sp_def],)

# finally, make the output data array with these setpoints
out.append(DataArray(name=name, full_name=full_name, label=label,
shape=shape, action_indices=i,
shape=shape, action_indices=i, unit=unit,
set_arrays=setpoints, parameter=action))

return out
Expand All @@ -617,7 +626,7 @@ def _fill_blank(self, inputs, blanks):
raise ValueError('Wrong number of inputs supplied')

def _make_setpoint_array(self, shape, i, prev_setpoints, vals, name,
label):
label, unit):
if vals is None:
vals = self._default_setpoints(shape)
elif isinstance(vals, DataArray):
Expand Down Expand Up @@ -645,7 +654,7 @@ def _make_setpoint_array(self, shape, i, prev_setpoints, vals, name,
name = 'index{}'.format(i)

return DataArray(name=name, label=label, set_arrays=prev_setpoints,
shape=shape, preset_data=vals)
shape=shape, preset_data=vals, unit=unit, is_setpoint=True)

def _default_setpoints(self, shape):
if len(shape) == 1:
Expand Down
28 changes: 28 additions & 0 deletions qcodes/tests/instrument_mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,31 @@ def __init__(self, **kwargs):

def get(self):
return self._return


class MultiSetPointParam(MultiParameter):
"""
Multiparameter which only purpose it to test that units, setpoints
and so on are copied correctly to the individual arrays in the datarray.
"""
def __init__(self):
name = 'testparameter'
shapes = ((5,), (5,))
names = ('this', 'that')
labels = ('this label', 'that label')
units = ('this unit', 'that unit')
sp_base = tuple(np.linspace(5, 9, 5))
setpoints = ((sp_base,), (sp_base,))
setpoint_names = (('this_setpoint',), ('this_setpoint',))
setpoint_labels = (('this setpoint',), ('this setpoint',))
setpoint_units = (('this setpointunit',), ('this setpointunit',))
super().__init__(name, names, shapes,
labels=labels,
units=units,
setpoints=setpoints,
setpoint_labels=setpoint_labels,
setpoint_names=setpoint_names,
setpoint_units=setpoint_units)

def get(self):
return np.zeros(5), np.ones(5)
18 changes: 9 additions & 9 deletions qcodes/tests/test_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def test_sync_no_overwrite(self):
self.gates.chan2[0:1:1], 0.000001))
data = loop.get_data_set(name='testsweep', write_period=0.01)
_ = loop.with_bg_task(data.sync).run()
assert not np.isnan(data.chan2).any()
assert not np.isnan(data.chan2_set).any()

def sleeper(t):
time.sleep(t)
Expand Down Expand Up @@ -467,7 +467,7 @@ def test_composite_params(self):
self.assertEqual(data.p1_set.tolist(), [1, 2])
self.assertEqual(data.one.tolist(), [1, 1])
self.assertEqual(data.onetwo.tolist(), [[1, 2]] * 2)
self.assertEqual(data.index0.tolist(), [[0, 1]] * 2)
self.assertEqual(data.index0_set.tolist(), [[0, 1]] * 2)

# give it setpoints, names, and labels
mg.setpoints = (None, ((10, 11),))
Expand All @@ -478,8 +478,8 @@ def test_composite_params(self):

data = loop.run_temp()

self.assertEqual(data.highest.tolist(), [[10, 11]] * 2)
self.assertEqual(data.highest.label, sp_label)
self.assertEqual(data.highest_set.tolist(), [[10, 11]] * 2)
self.assertEqual(data.highest_set.label, sp_label)

# setpoints as DataArray - name and label here override
# setpoint_names and setpoint_labels attributes
Expand All @@ -490,8 +490,8 @@ def test_composite_params(self):
mg.setpoints = (None, (sp_dataarray,))

data = loop.run_temp()
self.assertEqual(data.bgn.tolist(), [[6, 7]] * 2)
self.assertEqual(data.bgn.label, new_sp_label)
self.assertEqual(data.bgn_set.tolist(), [[6, 7]] * 2)
self.assertEqual(data.bgn_set.label, new_sp_label)

# muck things up and test for errors
mg.setpoints = (None, ((1, 2, 3),))
Expand Down Expand Up @@ -522,16 +522,16 @@ def test_composite_params(self):

self.assertEqual(data.p1_set.tolist(), [1, 2])
self.assertEqual(data.arr.tolist(), [[4, 5, 6]] * 2)
self.assertEqual(data.index0.tolist(), [[0, 1, 2]] * 2)
self.assertEqual(data.index0_set.tolist(), [[0, 1, 2]] * 2)

mg = MultiGetter(arr2d=((21, 22), (23, 24)))
loop = Loop(self.p1[1:3:1], 0.001).each(mg)
data = loop.run_temp()

self.assertEqual(data.p1_set.tolist(), [1, 2])
self.assertEqual(data.arr2d.tolist(), [[[21, 22], [23, 24]]] * 2)
self.assertEqual(data.index0.tolist(), [[0, 1]] * 2)
self.assertEqual(data.index1.tolist(), [[[0, 1]] * 2] * 2)
self.assertEqual(data.index0_set.tolist(), [[0, 1]] * 2)
self.assertEqual(data.index1_set.tolist(), [[[0, 1]] * 2] * 2)

def test_bad_actors(self):
def f():
Expand Down
37 changes: 34 additions & 3 deletions qcodes/tests/test_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from qcodes.instrument.parameter import ManualParameter
from qcodes.measure import Measure

from .instrument_mocks import MultiGetter
from .instrument_mocks import MultiGetter, MultiSetPointParam

import numpy as np
from numpy.testing import assert_array_equal

class TestMeasure(TestCase):
def setUp(self):
Expand Down Expand Up @@ -34,7 +36,7 @@ def test_simple_scalar(self):
def test_simple_array(self):
data = Measure(MultiGetter(arr=(1.2, 3.4))).run_temp()

self.assertEqual(data.index0.tolist(), [0, 1])
self.assertEqual(data.index0_set.tolist(), [0, 1])
self.assertEqual(data.arr.tolist(), [1.2, 3.4])
self.assertEqual(len(data.arrays), 2, data.arrays)

Expand All @@ -44,6 +46,35 @@ def test_array_and_scalar(self):

self.assertEqual(data.single_set.tolist(), [0])
self.assertEqual(data.P1.tolist(), [42])
self.assertEqual(data.index0.tolist(), [0, 1])
self.assertEqual(data.index0_set.tolist(), [0, 1])
self.assertEqual(data.arr.tolist(), [5, 6])
self.assertEqual(len(data.arrays), 4, data.arrays)


class TestMeasureMulitParameter(TestCase):
def setUp(self):
self.p1 = MultiSetPointParam()


def test_metadata(self):
c = Measure(self.p1).run()
self.assertEqual(c.metadata['arrays']['this']['unit'], 'this unit')
self.assertEqual(c.metadata['arrays']['this']['name'], 'this')
self.assertEqual(c.metadata['arrays']['this']['label'], 'this label')
self.assertEqual(c.metadata['arrays']['this']['is_setpoint'], False)
self.assertEqual(c.metadata['arrays']['this']['shape'], (5,))
assert_array_equal(c.this.ndarray, np.zeros(5))

self.assertEqual(c.metadata['arrays']['that']['unit'],'that unit')
self.assertEqual(c.metadata['arrays']['that']['name'], 'that')
self.assertEqual(c.metadata['arrays']['that']['label'], 'that label')
self.assertEqual(c.metadata['arrays']['that']['is_setpoint'], False)
self.assertEqual(c.metadata['arrays']['that']['shape'], (5,))
assert_array_equal(c.that.ndarray, np.ones(5))

self.assertEqual(c.metadata['arrays']['this_setpoint_set']['unit'], 'this setpointunit')
self.assertEqual(c.metadata['arrays']['this_setpoint_set']['name'], 'this_setpoint')
self.assertEqual(c.metadata['arrays']['this_setpoint_set']['label'], 'this setpoint')
self.assertEqual(c.metadata['arrays']['this_setpoint_set']['is_setpoint'], True)
self.assertEqual(c.metadata['arrays']['this_setpoint_set']['shape'], (5,))
assert_array_equal(c.this_setpoint_set.ndarray, np.linspace(5, 9, 5))

0 comments on commit b5da731

Please sign in to comment.