diff --git a/qutil/domains.py b/qutil/domains.py index 74b23cbe6720b467e6127fbb768286b06dd50de0..61c00e12b1c7b51b930ecb55856e9cf495f74eac 100644 --- a/qutil/domains.py +++ b/qutil/domains.py @@ -337,21 +337,21 @@ class DiscreteInterval(Interval[_IntegralT], Bounded[_IntegralT]): return ceil(self.min()) if value > self: return floor(self.max()) - return round(value) + return round(self.round(value)) def next_smallest(self, value: _RealT) -> int: if value < self: return ceil(self.min()) if value > self: return floor(self.max()) - return floor(value) + return floor(self.round(value)) def next_largest(self, value: _RealT) -> int: if value < self: return ceil(self.min()) if value > self: return floor(self.max()) - return ceil(value) + return ceil(self.round(value)) class ContinuousInterval(Interval[_RealT]): diff --git a/qutil/misc.py b/qutil/misc.py index 6653fa75ed738640b1f393af8c16344f01d8d829..4b37f01988ff427fd6dafd5c497e855c921a1c47 100644 --- a/qutil/misc.py +++ b/qutil/misc.py @@ -5,7 +5,7 @@ import logging import sys import time import warnings -from collections.abc import Callable, Hashable, Iterator, MutableMapping +from collections.abc import Callable, Hashable, Iterator, Mapping, MutableMapping, Sequence from contextlib import contextmanager from importlib import import_module from types import ModuleType @@ -300,16 +300,26 @@ def timeout(value: float, on_exceeded: Callable[[float], None] | None = None, raise TimeoutError(msg.format(exceeded.elapsed)) -def deprecate_kwarg(old: str, new: str | None = None): - """Decorator factory to deprecate the keyword argument *old*. +def deprecate_kwargs(*args, **kwargs): + """Decorator factory to deprecate keyword arguments. - Optionally in favor of *new*. + Optionally in favor of replacements. + + Parameters + ---------- + *args : + Positional arguments should be valid identifiers that will be + treated as keyword arguments to be deprecated without + replacement. + **kwargs : + Key-value pairs of keyword arguments that should be deprecated + *with* replacement: ``{old: new}``. Examples -------- Deprecate a kwarg in favor of a new name: - >>> @deprecate_kwarg('old', 'new') + >>> @deprecate_kwargs(old='new') ... def foo(new=3): ... print(new) @@ -328,7 +338,7 @@ def deprecate_kwarg(old: str, new: str | None = None): Only deprecate a kwarg without giving a replacement: - >>> @deprecate_kwarg('old') + >>> @deprecate_kwargs('old') ... def foo(new=3): ... print(new) @@ -347,26 +357,53 @@ def deprecate_kwarg(old: str, new: str | None = None): ... TypeError: foo() got an unexpected keyword argument 'old' + Multiple deprecations: + + >>> @deprecate_kwargs(old1='new1', old2='new2') + ... def foo(new1=3, new2=4): + ... print(new1, new2) + >>> with filter_warnings(action='error', category=DeprecationWarning): + ... foo(old1=4) + Traceback (most recent call last): + ... + DeprecationWarning: Keyword argument 'old1' of foo is deprecated. Use 'new1' instead. + >>> with filter_warnings(action='error', category=DeprecationWarning): + ... foo(old2=5) + Traceback (most recent call last): + ... + DeprecationWarning: Keyword argument 'old2' of foo is deprecated. Use 'new2' instead. + """ def decorator(func): @wraps(func) - def wrapped(*args, **kwargs): - if old not in kwargs: - return func(*args, **kwargs) - - msg = f"Keyword argument '{old}' of {func.__qualname__} is deprecated." - if new is not None: - if new in kwargs: - raise TypeError(msg + f" Cannot also specify '{new}'.") - else: - msg = msg + f" Use '{new}' instead." - kwargs[new] = kwargs.pop(old) - - warnings.warn(msg, DeprecationWarning, stacklevel=2) - return func(*args, **kwargs) + def wrapped(*a, **kw): + def _handle_single(o, n=None): + if o not in kw: + return + + msg = f"Keyword argument '{o}' of {func.__qualname__} is deprecated." + if n is not None: + if n in kw: + raise TypeError(msg + f" Cannot also specify '{n}'.") + else: + msg = msg + f" Use '{n}' instead." + kw[n] = kw.pop(o) + + warnings.warn(msg, DeprecationWarning, stacklevel=3) + + for arg in args: + _handle_single(arg) + for old, new in kwargs.items(): + _handle_single(old, new) + return func(*a, **kw) return wrapped return decorator + + +@deprecated('Use deprecate_kwargs instead') +def deprecate_kwarg(old: str, new: str | None = None): + return deprecate_kwargs(old=new) diff --git a/qutil/signal_processing/fourier_space.py b/qutil/signal_processing/fourier_space.py index 05643a953501a7d5e3f3622bce581b0fac76debd..85ede1e986486328543e19d2144a00476f69c9f8 100644 --- a/qutil/signal_processing/fourier_space.py +++ b/qutil/signal_processing/fourier_space.py @@ -48,7 +48,7 @@ from scipy import integrate from qutil import math from qutil.caching import cache from qutil.functools import wraps -from qutil.misc import deprecate_kwarg +from qutil.misc import deprecate_kwargs from qutil.signal_processing._common import _parse_filter_edges from qutil.typecheck import check_literals @@ -115,7 +115,7 @@ def Id(x: _S, f: _T, *_, **__) -> tuple[_S, _T]: return x, f -@deprecate_kwarg('deriv_order', 'order') +@deprecate_kwargs(deriv_order='order') def derivative(x, f, order: int = 0, overwrite_x: bool = False, **_) -> tuple[np.ndarray, np.ndarray]: r"""Compute the (anti-)derivative. diff --git a/qutil/ui/gate_layout/core.py b/qutil/ui/gate_layout/core.py index 7e5add7def890a587f36030c6cfdd537a85f3a8f..74fe595252a6568006dc0ce1603b0c2c04f52993 100644 --- a/qutil/ui/gate_layout/core.py +++ b/qutil/ui/gate_layout/core.py @@ -3,6 +3,7 @@ Created on Mon Apr 25 11:14:02 2022 @author: Hangleiter """ +import logging import pathlib import warnings from itertools import compress @@ -21,12 +22,11 @@ try: except ImportError: ezdxf = None - DEFAULT_TEXT_KWARGS = dict(backgroundcolor='black', horizontalalignment='center', verticalalignment='center', color='white', fontsize=8) - +LOG = logging.getLogger(__name__) class GateLayout: @@ -49,11 +49,12 @@ class GateLayout: gate_mask = [True]*14 + [False, True, False, True, False, True, False, True] if len(gate_names) != len(gate_mask): - warnings.warn(f"Gate name number is different from gate mask number: {len(gate_names)} != {len(gate_mask)}", - stacklevel=2) + warnings.warn("Gate name number is different from gate mask number: " + f"{len(gate_names)} != {len(gate_mask)}", stacklevel=2) self.fig = plt.figure(fignum) - self.ax = self.fig.add_axes([0, 0, 1, 1]) + self.fig.clear() + self.ax = self.fig.subplots() self.layout_file = layout_file self.gate_names = gate_names self.gate_mask = gate_mask @@ -61,7 +62,7 @@ class GateLayout: self.background_color = matplotlib.colors.to_hex(background_color) self.foreground_color = matplotlib.colors.to_hex(foreground_color) - self.cmap = matplotlib.cm.get_cmap(cmap) + self.cmap = matplotlib.colormaps.get_cmap(cmap) self.norm = matplotlib.colors.Normalize(v_min, v_max) self.v_min = self.norm.vmin self.v_max = self.norm.vmax @@ -100,7 +101,10 @@ class GateLayout: self.get_voltages(force) elif isinstance(voltages, dict): for gate, voltage in voltages.items(): - self._latest_voltages[self.gate_names.index(gate)] = voltage + try: + self._latest_voltages[self.gate_names.index(gate)] = voltage + except ValueError: + LOG.warning(f'Gate not found. Skipping: {gate}') else: self._latest_voltages[:] = np.broadcast_to(voltages, self._latest_voltages.shape) @@ -117,16 +121,18 @@ class GateLayout: return self._latest_voltages def _setup_figure(self): - vertices = get_vertices_from_dfx(self.layout_file, self.background_color, self.foreground_color) + vertices = get_vertices_from_dfx(self.layout_file, self.background_color, + self.foreground_color) if len(self.gate_names) != len(vertices): - warnings.warn(f"Found {len(vertices)} patches in file but got {len(self.gate_names)} gate names", stacklevel=2) + warnings.warn(f"Found {len(vertices)} patches in file but got {len(self.gate_names)} " + "gate names", stacklevel=2) patches = [] texts = [] for idx, verts in enumerate(vertices): verts += self._offset[None, :] - patches.append(matplotlib.patches.Polygon(verts, True, zorder=0)) - texts.append(matplotlib.text.Text(*verts.mean(axis=0), f'INIT', **self.text_kwargs)) + patches.append(matplotlib.patches.Polygon(verts, closed=True, zorder=0)) + texts.append(matplotlib.text.Text(*verts.mean(axis=0), 'INIT', **self.text_kwargs)) all_coords = np.array([(patch.get_xy().mean(axis=0)) for patch in patches]) diff --git a/qutil/ui/gate_layout/qcodes.py b/qutil/ui/gate_layout/qcodes.py index 1ce8fc9488dc7cfad6b8e2265cb9c8f739faac3c..44914172d17929762cf5b68c138d5417244310dc 100644 --- a/qutil/ui/gate_layout/qcodes.py +++ b/qutil/ui/gate_layout/qcodes.py @@ -17,6 +17,7 @@ class QcodesGateLayout(GateLayout): class MaskedGate(qc.Parameter): def __init__(self, name, *args, **kwargs): super().__init__(name, *args, **kwargs) + def get_raw(self): return 0