Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: cddb5b9ae4f8
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1f5a49d263f3
Choose a head ref
  • 6 commits
  • 5 files changed
  • 1 contributor

Commits on Aug 6, 2015

  1. Copy the full SHA
    7180552 View commit details
  2. Copy the full SHA
    263ff86 View commit details
  3. Copy the full SHA
    fd79572 View commit details
  4. language/environment: remove attr_rtresult

    This function has the typical side effect of clearing the result. When happening in build(), this caused minor trouble when using pipelined scheduling (no result displayed at all until run() proceeds).
    sbourdeauducq committed Aug 6, 2015
    Copy the full SHA
    59a29ae View commit details
  5. Copy the full SHA
    9261254 View commit details
  6. Copy the full SHA
    1f5a49d View commit details
Showing with 143 additions and 52 deletions.
  1. +108 −37 artiq/gui/displays.py
  2. +2 −0 artiq/gui/tools.py
  3. +5 −7 artiq/language/environment.py
  4. +11 −2 artiq/master/worker_db.py
  5. +17 −6 examples/master/repository/flopping_f_simulation.py
145 changes: 108 additions & 37 deletions artiq/gui/displays.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,77 @@
from collections import OrderedDict
import numpy as np

from quamash import QtGui
import pyqtgraph as pg
from pyqtgraph import dockarea


class _SimpleSettings(QtGui.QDialog):
def __init__(self, parent, prev_name, prev_settings,
result_list, create_cb):
class _BaseSettings(QtGui.QDialog):
def __init__(self, parent, window_title, prev_name, create_cb):
QtGui.QDialog.__init__(self, parent=parent)
self.setWindowTitle(self._window_title)
self.setWindowTitle(window_title)

grid = QtGui.QGridLayout()
self.setLayout(grid)
self.grid = QtGui.QGridLayout()
self.setLayout(self.grid)

grid.addWidget(QtGui.QLabel("Name:"), 0, 0)
self.name = name = QtGui.QLineEdit()
grid.addWidget(name, 0, 1)
self.grid.addWidget(QtGui.QLabel("Name:"), 0, 0)
self.name = QtGui.QLineEdit()
self.grid.addWidget(self.name, 0, 1)
if prev_name is not None:
name.insert(prev_name)
self.name.setText(prev_name)

grid.addWidget(QtGui.QLabel("Result:"))
self.result = result = QtGui.QComboBox()
grid.addWidget(result, 1, 1)
result.addItems(result_list)
result.setEditable(True)
if "result" in prev_settings:
result.setEditText(prev_settings["result"])
def on_accept():
create_cb(self.name.text(), self.get_input())
self.accepted.connect(on_accept)

def add_buttons(self):
buttons = QtGui.QDialogButtonBox(
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
grid.addWidget(buttons, 2, 0, 1, 2)
self.grid.addWidget(buttons, self.grid.rowCount(), 0, 1, 2)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)

def on_accept():
create_cb(name.text(), {"result": result.currentText()})
self.accepted.connect(on_accept)

def accept(self):
if self.name.text() and self.result.currentText():
if self.name.text() and self.validate_input():
QtGui.QDialog.accept(self)

def validate_input(self):
raise NotImplementedError

def get_input(self):
raise NotImplementedError


class _SimpleSettings(_BaseSettings):
def __init__(self, parent, prev_name, prev_settings,
result_list, create_cb):
_BaseSettings.__init__(self, parent, self._window_title,
prev_name, create_cb)

self.result_widgets = dict()
for row, (has_none, key) in enumerate(self._result_keys):
self.grid.addWidget(QtGui.QLabel(key.capitalize() + ":"))
w = QtGui.QComboBox()
self.grid.addWidget(w, row + 1, 1)
if has_none:
w.addItem("<None>")
w.addItems(result_list)
w.setEditable(True)
if key in prev_settings:
w.setEditText(prev_settings[key])
self.result_widgets[key] = w
self.add_buttons()

def validate_input(self):
return all(w.currentText() for w in self.result_widgets.values())

def get_input(self):
return {k: v.currentText() for k, v in self.result_widgets.items()}


class NumberDisplaySettings(_SimpleSettings):
_window_title = "Number display"
_result_keys = [(False, "result")]


class NumberDisplay(dockarea.Dock):
@@ -70,6 +97,7 @@ def update_data(self, data):

class XYDisplaySettings(_SimpleSettings):
_window_title = "XY plot"
_result_keys = [(False, "y"), (True, "x"), (True, "error"), (True, "fit")]


class XYDisplay(dockarea.Dock):
@@ -81,22 +109,56 @@ def __init__(self, name, settings):
self.addWidget(self.plot)

def data_sources(self):
return {self.settings["result"]}
s = {self.settings["y"]}
for k in "x", "error", "fit":
if self.settings[k] != "<None>":
s.add(self.settings[k])
return s

def update_data(self, data):
result = self.settings["result"]
result_y = self.settings["y"]
result_x = self.settings["x"]
result_error = self.settings["error"]
result_fit = self.settings["fit"]

try:
y = data[result]
y = data[result_y]
except KeyError:
return
self.plot.clear()
if not y:
x = data.get(result_x, None)
if x is None:
x = list(range(len(y)))
error = data.get(result_error, None)
fit = data.get(result_fit, None)

if not y or len(y) != len(x):
return
self.plot.plot(y)
if error is not None and hasattr(error, "__len__"):
if not len(error):
error = None
elif len(error) != len(y):
return
if fit is not None:
if not len(fit):
fit = None
elif len(fit) != len(y):
return

self.plot.clear()
self.plot.plot(x, y, pen=None, symbol="x")
if error is not None:
# See https://github.com/pyqtgraph/pyqtgraph/issues/211
if hasattr(error, "__len__") and not isinstance(error, np.ndarray):
error = np.array(error)
errbars = pg.ErrorBarItem(x=np.array(x), y=np.array(y), height=error)
self.plot.addItem(errbars)
if fit is not None:
self.plot.plot(x, fit)


class HistogramDisplaySettings(_SimpleSettings):
_window_title = "Histogram"
_result_keys = [(False, "y"), (True, "x")]


class HistogramDisplay(dockarea.Dock):
@@ -108,19 +170,28 @@ def __init__(self, name, settings):
self.addWidget(self.plot)

def data_sources(self):
return {self.settings["result"]}
s = {self.settings["y"]}
if self.settings["x"] != "<None>":
s.add(self.settings["x"])
return s

def update_data(self, data):
result = self.settings["result"]
result_y = self.settings["y"]
result_x = self.settings["x"]
try:
y = data[result]
y = data[result_y]
if result_x == "<None>":
x = None
else:
x = data[result_x]
except KeyError:
return
x = list(range(len(y)+1))
self.plot.clear()
if not y:
return
self.plot.plot(x, y, stepMode=True, fillLevel=0, brush=(0, 0, 255, 150))
if x is None:
x = list(range(len(y)+1))

if y and len(x) == len(y) + 1:
self.plot.clear()
self.plot.plot(x, y, stepMode=True, fillLevel=0, brush=(0, 0, 255, 150))


display_types = OrderedDict([
2 changes: 2 additions & 0 deletions artiq/gui/tools.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@ def force_spinbox_value(spinbox, value):


def short_format(v):
if v is None:
return "None"
t = type(v)
if t is int or t is float:
return str(v)
12 changes: 5 additions & 7 deletions artiq/language/environment.py
Original file line number Diff line number Diff line change
@@ -201,13 +201,15 @@ def set_parameter(self, key, value):
raise ValueError("Parameter database not present")
self.__pdb.set(key, value)

def set_result(self, key, value, realtime=False):
def set_result(self, key, value, realtime=False, store=True):
"""Writes the value of a result.
:param realtime: Marks the result as real-time, making it immediately
available to clients such as the user interface. Returns a
``Notifier`` instance that can be used to modify mutable results
(such as lists) and synchronize the modifications with the clients.
:param store: Defines if the result should be stored permanently,
e.g. in HDF5 output. Default is to store.
"""
if self.__rdb is None:
raise ValueError("Result database not present")
@@ -217,17 +219,13 @@ def set_result(self, key, value, realtime=False):
self.__rdb.rt[key] = value
notifier = self.__rdb.rt[key]
notifier.kernel_attr_init = False
self.__rdb.set_store(key, store)
return notifier
else:
if key in self.__rdb.rt.read:
raise ValueError("Result is already realtime")
self.__rdb.nrt[key] = value

def attr_rtresult(self, key, init_value):
"""Writes the value of a real-time result and sets the corresponding
``Notifier`` as attribute. The names of the result and of the
attribute are the same."""
setattr(self, key, set_result(key, init_value, True))
self.__rdb.set_store(key, store)

def get_result(self, key):
"""Retrieves the value of a result.
13 changes: 11 additions & 2 deletions artiq/master/worker_db.py
Original file line number Diff line number Diff line change
@@ -91,16 +91,25 @@ class ResultDB:
def __init__(self):
self.rt = Notifier(dict())
self.nrt = dict()
self.store = set()

def get(self, key):
try:
return self.nrt[key]
except KeyError:
return self.rt[key].read

def set_store(self, key, store):
if store:
self.store.add(key)
else:
self.store.discard(key)

def write_hdf5(self, f):
result_dict_to_hdf5(f, self.rt.read)
result_dict_to_hdf5(f, self.nrt)
result_dict_to_hdf5(
f, {k: v for k, v in self.rt.read.items() if k in self.store})
result_dict_to_hdf5(
f, {k: v for k, v in self.nrt.items() if k in self.store})


def _create_device(desc, dmgr):
23 changes: 17 additions & 6 deletions examples/master/repository/flopping_f_simulation.py
Original file line number Diff line number Diff line change
@@ -31,14 +31,18 @@ def build(self):
default=LinearScan(1000, 2000, 100)))

self.attr_argument("F0", NumberValue(1500, min=1000, max=2000))
self.attr_argument("noise_amplitude", NumberValue(0.1, min=0, max=100))

self.frequency = self.set_result("flopping_f_frequency", [], True)
self.brightness = self.set_result("flopping_f_brightness", [], True)
self.attr_argument("noise_amplitude", NumberValue(0.1, min=0, max=100,
step=0.01))

self.attr_device("scheduler")

def run(self):
self.frequency = self.set_result("flopping_f_frequency", [],
realtime=True, store=False)
self.brightness = self.set_result("flopping_f_brightness", [],
realtime=True)
self.set_result("flopping_f_fit", [], realtime=True, store=False)

for frequency in self.frequency_scan:
brightness = model(frequency, self.F0) + self.noise_amplitude*random.random()
self.frequency.append(frequency)
@@ -48,9 +52,16 @@ def run(self):
self.scheduler.priority, time.time() + 20, False)

def analyze(self):
# Use get_result so that analyze can be run stand-alone.
frequency = self.get_result("flopping_f_frequency")
brightness = self.get_result("flopping_f_brightness")
popt, pcov = curve_fit(model_numpy,
self.frequency.read, self.brightness.read,
frequency, brightness,
p0=[self.get_parameter("flopping_freq")])
perr = np.sqrt(np.diag(pcov))
if perr < 0.1:
self.set_parameter("flopping_freq", float(popt))
F0 = float(popt)
self.set_parameter("flopping_freq", F0)
self.set_result("flopping_f_fit",
[model(x, F0) for x in frequency],
realtime=True, store=False)