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: 9a1cad5ceca3
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: ae914d261125
Choose a head ref
  • 3 commits
  • 4 files changed
  • 1 contributor

Commits on Jun 18, 2016

  1. Copy the full SHA
    80cf321 View commit details
  2. add MultiScanManager

    sbourdeauducq committed Jun 18, 2016
    Copy the full SHA
    ec3e779 View commit details
  3. Copy the full SHA
    ae914d2 View commit details
Showing with 84 additions and 11 deletions.
  1. +1 −1 artiq/dashboard/datasets.py
  2. +22 −6 artiq/dashboard/experiments.py
  3. +17 −0 artiq/examples/master/repository/multi_scan.py
  4. +44 −4 artiq/language/scan.py
2 changes: 1 addition & 1 deletion artiq/dashboard/datasets.py
Original file line number Diff line number Diff line change
@@ -165,7 +165,7 @@ def edit_clicked(self):
logger.error("Cannot edit dataset %s: "
"type %s is not supported", key, t)
return
dialog_cls(self, self.dataset_ctl, key, value, persist).exec_()
dialog_cls(self, self.dataset_ctl, key, value, persist).open()

def delete_clicked(self):
idx = self.table.selectedIndexes()
28 changes: 22 additions & 6 deletions artiq/dashboard/experiments.py
Original file line number Diff line number Diff line change
@@ -50,6 +50,9 @@ def __init__(self, manager, dock, expurl):
self.setHorizontalScrollMode(self.ScrollPerPixel)
self.setVerticalScrollMode(self.ScrollPerPixel)

self.setStyleSheet("QTreeWidget {background: " +
self.palette().midlight().color().name() + " ;}")

self.viewport().installEventFilter(_WheelFilter(self.viewport()))

self._groups = dict()
@@ -60,16 +63,28 @@ def __init__(self, manager, dock, expurl):
if not arguments:
self.addTopLevelItem(QtWidgets.QTreeWidgetItem(["No arguments"]))

gradient = QtGui.QLinearGradient(
0, 0, 0, QtGui.QFontMetrics(self.font()).lineSpacing()*2.5)
gradient.setColorAt(0, self.palette().base().color())
gradient.setColorAt(1, self.palette().midlight().color())
for name, argument in arguments.items():
entry = argty_to_entry[argument["desc"]["ty"]](argument)
widget_item = QtWidgets.QTreeWidgetItem([name])
self._arg_to_entry_widgetitem[name] = entry, widget_item

for col in range(3):
widget_item.setBackground(col, gradient)
font = widget_item.font(0)
font.setBold(True)
widget_item.setFont(0, font)

if argument["group"] is None:
self.addTopLevelItem(widget_item)
else:
self._get_group(argument["group"]).addChild(widget_item)
self.setItemWidget(widget_item, 1, entry)
fix_layout = LayoutWidget()
fix_layout.addWidget(entry)
self.setItemWidget(widget_item, 1, fix_layout)
recompute_argument = QtWidgets.QToolButton()
recompute_argument.setToolTip("Re-run the experiment's build "
"method and take the default value")
@@ -123,12 +138,12 @@ def _get_group(self, name):
if name in self._groups:
return self._groups[name]
group = QtWidgets.QTreeWidgetItem([name])
for c in 0, 1:
group.setBackground(c, QtGui.QBrush(QtGui.QColor(100, 100, 100)))
group.setForeground(c, QtGui.QBrush(QtGui.QColor(220, 220, 255)))
font = group.font(c)
for col in range(3):
group.setBackground(col, self.palette().mid())
group.setForeground(col, self.palette().brightText())
font = group.font(col)
font.setBold(True)
group.setFont(c, font)
group.setFont(col, font)
self.addTopLevelItem(group)
self._groups[name] = group
return group
@@ -185,6 +200,7 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):

def __init__(self, manager, expurl):
QtWidgets.QMdiSubWindow.__init__(self)
self.resize(800, 600)
self.setWindowTitle(expurl)
self.setWindowIcon(QtWidgets.QApplication.style().standardIcon(
QtWidgets.QStyle.SP_FileDialogContentsView))
17 changes: 17 additions & 0 deletions artiq/examples/master/repository/multi_scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from artiq.experiment import *


class MultiScan(EnvExperiment):
def build(self):
self.setattr_argument("a", Scannable(default=LinearScan(0, 10, 4)))
self.setattr_argument("b", Scannable(default=LinearScan(0, 10, 4)))
self.setattr_argument("c", Scannable(default=LinearScan(0, 10, 4)))

def run(self):
msm = MultiScanManager(
("a", self.a),
("b", self.b),
("c", self.c),
)
for point in msm:
print("a={} b={} c={}".format(point.a, point.b, point.c))
48 changes: 44 additions & 4 deletions artiq/language/scan.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@
A scan object (e.g. :class:`artiq.language.scan.LinearScan`) represents a
one-dimensional sweep of a numerical range. Multi-dimensional scans are
constructed by combining several scan objects.
constructed by combining several scan objects, for example using
:class:`artiq.language.scan.MultiScanManager`.
Iterate on a scan object to scan it, e.g. ::
@@ -14,12 +15,11 @@
yielding the same values each time. Iterating concurrently on the
same scan object (e.g. via nested loops) is also supported, and the
iterators are independent from each other.
Scan objects are supported both on the host and the core device.
"""

import random
import inspect
from itertools import product

from artiq.language.core import *
from artiq.language.environment import NoDefault, DefaultMissing
@@ -28,7 +28,7 @@

__all__ = ["ScanObject",
"NoScan", "LinearScan", "RandomScan", "ExplicitScan",
"Scannable"]
"Scannable", "MultiScanManager"]


class ScanObject:
@@ -222,3 +222,43 @@ def describe(self):
d["global_max"] = self.global_max
d["ndecimals"] = self.ndecimals
return d


class MultiScanManager:
"""
Makes an iterator that returns elements from the first scan object until
it is exhausted, then proceeds to the next iterable, until all of the
scan objects are exhausted. Used for treating consecutive scans as a
single scan.
Scan objects must be passed as a list of tuples (name, scan_object).
Íteration produces scan points that have attributes that correspond
to the names of the scan objects, and have the last value yielded by
that scan object.
"""
def __init__(self, *args):
self.names = [a[0] for a in args]
self.scan_objects = [a[1] for a in args]

class ScanPoint:
def __init__(self, **kwargs):
self.attr = set()
for k, v in kwargs.items():
setattr(self, k, v)
self.attr.add(k)

def __repr__(self):
return ("<ScanPoint " +
" ".join("{}={}".format(k, getattr(self, k))
for k in self.attr) +
">")

self.scan_point_cls = ScanPoint

def _gen(self):
for values in product(*self.scan_objects):
d = {k: v for k, v in zip(self.names, values)}
yield self.scan_point_cls(**d)

def __iter__(self):
return self._gen()