microsoft / microsoft/Qcodes

Parameters with continuously dynamically-sized setpoints

Open
#6,888 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
459
Forks
359
Avg merge
3d 6h
Merged PRs (30d)
73

Description

I would like to implement a new [measurement class of the API](https://www.swabianinstruments.com/static/documentation/TimeTagger/api/Measurements.html#startstop) as a parameter for the [Swabian Instruments TimeTagger driver](https://qcodes.github.io/Qcodes_contrib_drivers/api/generated/qcodes_contrib_drivers.drivers.SwabianInstruments.html).

The measurement generates a histogram of time differences between subsequent clicks. Since these can be arbitrarily far apart, the bins are dynamic with only the smallest bin width being defined at start time. Hence, when [getting the data](https://www.swabianinstruments.com/static/documentation/TimeTagger/api/Measurements.html#StartStop.getData) at any point after the measurement has started, the array returned will (most likely) have a different shape than the last time it was fetched. To account for this, both the time bins (the setpoints in qcodes lingo) and the counts within (the actual data) are returned together.

The qcodes parameter most naturally suited for this (an array with setpoints) would of course be a `ParameterWithSetpoints`. However, that requires both an `Arrays` validator *and* setpoints with an `Arrays` validator with a given `shape` -- which is unknown at setup time. To avoid a chicken-egg problem when making the shape dynamic, i.e., a callback, one would have to call the `getData()` method of the API for each validator to determine the shape of the data. But then the actual `get` on the parameter might already have a new shape, because the measurement is ongoing in between calls.

Next, one could think about using `MultiParameter`. It requires some hacking to make the setpoints and shapes dynamic, but this sort of works:

```python
class DynamicMultiParameter(MultiParameter):
@property
def shapes(self):
return tuple(array.shape for array in self.get_latest())

@shapes.setter
def shapes(self, val):
# the constructor wants to set this
pass

@property
def setpoints(self):
return tuple(tuple(np.arange(n) for n in shape) for shape in self.shapes)

@setpoints.setter
def setpoints(self, val):
# the constructor wants to set this
pass

class MyParam(DynamicMultiParameter):
def get_raw(self):
# api returns array of shape (n, 2)
return tuple(self.instrument.api.getData().T)

```

While it works, it's not perfect because one of the parameters in this `MultiParameter` is the others' setpoint, and we'd like to reflect that relationship. The same goes of course for the trivial solution `Parameter(..., get_cmd=api.getData)`.

My questions now are:
- does anybody have a neater solution?
- if not, can we adapt `ParameterWithSetpoints` to allow for dynamically sized setpoints/data somehow?
- or else, what would a new parameter type look like that reflects this sort of relationship, and would it have a place in the qcodes core?

Contributor guide

Open the contributing guide

Research direction

Read the ParameterWithSetpoints and MultiParameter entry points, then inspect the Swabian Instruments TimeTagger StartStop.getData API described in the issue. The issue has no settled implementation or acceptance criterion, so work would first need agreement on the parameter relationship and what dynamic shapes should guarantee.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, data
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.