From d1b8d95ab0d5237def1b225621e39797e0a39e37 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 11 Apr 2025 09:49:34 +0200 Subject: [PATCH 1/6] Replace do0d with a wrapper arond dond --- src/qcodes/dataset/dond/do_0d.py | 105 +++++++++++++------------------ src/qcodes/dataset/dond/do_nd.py | 21 ++++++- 2 files changed, 60 insertions(+), 66 deletions(-) diff --git a/src/qcodes/dataset/dond/do_0d.py b/src/qcodes/dataset/dond/do_0d.py index 0af215f27805..c0305a603270 100644 --- a/src/qcodes/dataset/dond/do_0d.py +++ b/src/qcodes/dataset/dond/do_0d.py @@ -1,92 +1,71 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, Literal, overload from opentelemetry import trace -from qcodes import config -from qcodes.parameters import ParameterBase +from .do_nd import DondKWargs, dond -from ..descriptions.detect_shapes import detect_shape_of_measurement -from ..measurements import Measurement -from ..threading import process_params_meas -from .do_nd_utils import _handle_plotting, _register_parameters, _set_write_period +if TYPE_CHECKING: + from typing_extensions import Unpack + + from .do_nd_utils import ( + AxesTupleListWithDataSet, + MultiAxesTupleListWithDataSet, + ParamMeasT, + ) LOG = logging.getLogger(__name__) TRACER = trace.get_tracer(__name__) -if TYPE_CHECKING: - from ..descriptions.versioning.rundescribertypes import Shapes - from ..experiment_container import Experiment - from .do_nd_utils import AxesTupleListWithDataSet, ParamMeasT + +@overload +def do0d( + *param_meas: ParamMeasT, squeeze: Literal[False], **kwargs: Unpack[DondKWargs] +) -> MultiAxesTupleListWithDataSet: ... + + +@overload +def do0d( + *param_meas: ParamMeasT, squeeze: Literal[True], **kwargs: Unpack[DondKWargs] +) -> AxesTupleListWithDataSet | MultiAxesTupleListWithDataSet: ... + + +@overload +def do0d( + *param_meas: ParamMeasT, squeeze: bool = True, **kwargs: Unpack[DondKWargs] +) -> AxesTupleListWithDataSet | MultiAxesTupleListWithDataSet: ... @TRACER.start_as_current_span("qcodes.dataset.do0d") def do0d( - *param_meas: ParamMeasT, - write_period: float | None = None, - measurement_name: str = "", - exp: Experiment | None = None, - do_plot: bool | None = None, - use_threads: bool | None = None, - log_info: str | None = None, -) -> AxesTupleListWithDataSet: + *param_meas: ParamMeasT, squeeze: bool = True, **kwargs: Unpack[DondKWargs] +) -> AxesTupleListWithDataSet | MultiAxesTupleListWithDataSet: """ Perform a measurement of a single parameter. This is probably most - useful for an ArrayParameter that already returns an array of data points + useful for a ParameterWithSetpoints that already returns an array of data points. Args: *param_meas: Parameter(s) to measure at each step or functions that will be called at each step. The function should take no arguments. The parameters and functions are called in the order they are supplied. - write_period: The time after which the data is actually written to the - database. - measurement_name: Name of the measurement. This will be passed down to - the dataset produced by the measurement. If not given, a default - value of 'results' is used for the dataset. - exp: The experiment to use for this measurement. - do_plot: should png and pdf versions of the images be saved after the - run. If None the setting will be read from ``qcodesrc.json`` - use_threads: If True measurements from each instrument will be done on - separate threads. If you are measuring from several instruments - this may give a significant speedup. - log_info: Message that is logged during the measurement. If None a default - message is used. + squeeze: If True, will return a tuple of QCoDeS DataSet, Matplotlib axis, + Matplotlib colorbar if only one group of measurements was performed + and a tuple of tuples of these if more than one group of measurements + was performed. If False, will always return a tuple where the first + member is a tuple of QCoDeS DataSet(s) and the second member is a tuple + of Matplotlib axis(es) and the third member is a tuple of Matplotlib + colorbar(s). + **kwargs: kwargs are the same as for dond and forwarded directly to dond. Returns: The QCoDeS dataset. """ - if do_plot is None: - do_plot = cast("bool", config.dataset.dond_plot) - meas = Measurement(name=measurement_name, exp=exp) - if log_info is not None: - meas._extra_log_info = log_info - else: - meas._extra_log_info = "Using 'qcodes.dataset.do0d'" - - measured_parameters = tuple( - param for param in param_meas if isinstance(param, ParameterBase) + return dond( + *param_meas, + squeeze=squeeze, + **kwargs, ) - - try: - shapes: Shapes | None = detect_shape_of_measurement( - measured_parameters, - ) - except TypeError: - LOG.exception( - f"Could not detect shape of {measured_parameters} " - f"falling back to unknown shape." - ) - shapes = None - - _register_parameters(meas, param_meas, shapes=shapes) - _set_write_period(meas, write_period) - - with meas.run() as datasaver: - datasaver.add_result(*process_params_meas(param_meas, use_threads=use_threads)) - dataset = datasaver.dataset - - return _handle_plotting(dataset, do_plot) diff --git a/src/qcodes/dataset/dond/do_nd.py b/src/qcodes/dataset/dond/do_nd.py index 6e79881dcb62..ecb17570fbf1 100644 --- a/src/qcodes/dataset/dond/do_nd.py +++ b/src/qcodes/dataset/dond/do_nd.py @@ -6,7 +6,7 @@ from collections.abc import Callable, Mapping, Sequence from contextlib import ExitStack from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Literal, cast, overload +from typing import TYPE_CHECKING, Any, Literal, NotRequired, cast, overload import numpy as np from opentelemetry import trace @@ -33,8 +33,6 @@ from .sweeps import AbstractSweep, TogetherSweep -LOG = logging.getLogger(__name__) - if TYPE_CHECKING: from qcodes.dataset.descriptions.versioning.rundescribertypes import Shapes from qcodes.dataset.dond.do_nd_utils import ( @@ -46,6 +44,7 @@ ) from qcodes.dataset.experiment_container import Experiment +LOG = logging.getLogger(__name__) SweepVarType = Any TRACER = trace.get_tracer(__name__) @@ -567,6 +566,22 @@ def parameters(self) -> tuple[ParameterBase, ...]: return self._parameters +class DondKWargs(TypedDict): + write_period: NotRequired[float | None] + measurement_name: NotRequired[str | Sequence[str]] + exp: NotRequired[Experiment | Sequence[Experiment] | None] + enter_actions: NotRequired[ActionsT] + exit_actions: NotRequired[ActionsT] + do_plot: NotRequired[bool | None] + show_progress: NotRequired[bool | None] + use_threads: NotRequired[bool | None] + additional_setpoints: NotRequired[Sequence[ParameterBase]] + log_info: NotRequired[str | None] + break_condition: NotRequired[BreakConditionT | None] + dataset_dependencies: NotRequired[Mapping[str, Sequence[ParamMeasT]]] + in_memory_cache: NotRequired[bool | None] + + @overload def dond( *params: AbstractSweep | TogetherSweep | ParamMeasT | Sequence[ParamMeasT], From 6d588bd005f2c4e7ef0d81d042599e982f51051b Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 11 Apr 2025 10:15:48 +0200 Subject: [PATCH 2/6] Simplify do0d --- src/qcodes/dataset/dond/do_0d.py | 42 +++++++------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/src/qcodes/dataset/dond/do_0d.py b/src/qcodes/dataset/dond/do_0d.py index c0305a603270..c1aec3344320 100644 --- a/src/qcodes/dataset/dond/do_0d.py +++ b/src/qcodes/dataset/dond/do_0d.py @@ -1,7 +1,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Literal, overload +from typing import TYPE_CHECKING, cast from opentelemetry import trace @@ -12,7 +12,6 @@ from .do_nd_utils import ( AxesTupleListWithDataSet, - MultiAxesTupleListWithDataSet, ParamMeasT, ) @@ -20,28 +19,10 @@ TRACER = trace.get_tracer(__name__) -@overload -def do0d( - *param_meas: ParamMeasT, squeeze: Literal[False], **kwargs: Unpack[DondKWargs] -) -> MultiAxesTupleListWithDataSet: ... - - -@overload -def do0d( - *param_meas: ParamMeasT, squeeze: Literal[True], **kwargs: Unpack[DondKWargs] -) -> AxesTupleListWithDataSet | MultiAxesTupleListWithDataSet: ... - - -@overload -def do0d( - *param_meas: ParamMeasT, squeeze: bool = True, **kwargs: Unpack[DondKWargs] -) -> AxesTupleListWithDataSet | MultiAxesTupleListWithDataSet: ... - - @TRACER.start_as_current_span("qcodes.dataset.do0d") def do0d( - *param_meas: ParamMeasT, squeeze: bool = True, **kwargs: Unpack[DondKWargs] -) -> AxesTupleListWithDataSet | MultiAxesTupleListWithDataSet: + *param_meas: ParamMeasT, **kwargs: Unpack[DondKWargs] +) -> AxesTupleListWithDataSet: """ Perform a measurement of a single parameter. This is probably most useful for a ParameterWithSetpoints that already returns an array of data points. @@ -51,21 +32,16 @@ def do0d( will be called at each step. The function should take no arguments. The parameters and functions are called in the order they are supplied. - squeeze: If True, will return a tuple of QCoDeS DataSet, Matplotlib axis, - Matplotlib colorbar if only one group of measurements was performed - and a tuple of tuples of these if more than one group of measurements - was performed. If False, will always return a tuple where the first - member is a tuple of QCoDeS DataSet(s) and the second member is a tuple - of Matplotlib axis(es) and the third member is a tuple of Matplotlib - colorbar(s). **kwargs: kwargs are the same as for dond and forwarded directly to dond. Returns: The QCoDeS dataset. """ - return dond( - *param_meas, - squeeze=squeeze, - **kwargs, + # since we only support entering parameters + # as a simple list or args we are sure to always + # get back a AxesTupleListWithDataSet and cast is safe + return cast( + "AxesTupleListWithDataSet", + dond(*param_meas, **kwargs, squeeze=True), ) From 3dd3d024e96cfaf2d6184f3a9213dd6e381bc9fc Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Fri, 11 Apr 2025 10:21:57 +0200 Subject: [PATCH 3/6] Replace do1d with a wrapper around dond --- src/qcodes/dataset/dond/do_1d.py | 158 +++++-------------------------- 1 file changed, 24 insertions(+), 134 deletions(-) diff --git a/src/qcodes/dataset/dond/do_1d.py b/src/qcodes/dataset/dond/do_1d.py index 719356881040..c5f9de1971a0 100644 --- a/src/qcodes/dataset/dond/do_1d.py +++ b/src/qcodes/dataset/dond/do_1d.py @@ -1,46 +1,24 @@ from __future__ import annotations import logging -import sys -import time from typing import TYPE_CHECKING, cast -import numpy as np from opentelemetry import trace -from tqdm.auto import tqdm -from qcodes import config -from qcodes.dataset.descriptions.detect_shapes import detect_shape_of_measurement -from qcodes.dataset.dond.do_nd_utils import ( - BreakConditionInterrupt, - _handle_plotting, - _register_actions, - _register_parameters, - _set_write_period, - catch_interrupts, -) -from qcodes.dataset.measurements import Measurement -from qcodes.dataset.threading import ( - SequentialParamsCaller, - ThreadPoolParamsCaller, - process_params_meas, -) -from qcodes.parameters import ParameterBase - -LOG = logging.getLogger(__name__) -TRACER = trace.get_tracer(__name__) +from .do_nd import DondKWargs, dond +from .sweeps import LinSweep if TYPE_CHECKING: - from collections.abc import Sequence + from typing_extensions import Unpack - from qcodes.dataset.descriptions.versioning.rundescribertypes import Shapes from qcodes.dataset.dond.do_nd_utils import ( - ActionsT, AxesTupleListWithDataSet, - BreakConditionT, ParamMeasT, ) - from qcodes.dataset.experiment_container import Experiment + from qcodes.parameters import ParameterBase + +LOG = logging.getLogger(__name__) +TRACER = trace.get_tracer(__name__) @TRACER.start_as_current_span("qcodes.dataset.do1d") @@ -51,17 +29,7 @@ def do1d( num_points: int, delay: float, *param_meas: ParamMeasT, - enter_actions: ActionsT = (), - exit_actions: ActionsT = (), - write_period: float | None = None, - measurement_name: str = "", - exp: Experiment | None = None, - do_plot: bool | None = None, - use_threads: bool | None = None, - additional_setpoints: Sequence[ParameterBase] = tuple(), - show_progress: bool | None = None, - log_info: str | None = None, - break_condition: BreakConditionT | None = None, + **kwargs: Unpack[DondKWargs], ) -> AxesTupleListWithDataSet: """ Perform a 1D scan of ``param_set`` from ``start`` to ``stop`` in @@ -74,106 +42,28 @@ def do1d( stop: End point of sweep num_points: Number of points in sweep delay: Delay after setting parameter before measurement is performed - param_meas: Parameter(s) to measure at each step or functions that + *param_meas: Parameter(s) to measure at each step or functions that will be called at each step. The function should take no arguments. The parameters and functions are called in the order they are supplied. - enter_actions: A list of functions taking no arguments that will be - called before the measurements start - exit_actions: A list of functions taking no arguments that will be - called after the measurements ends - write_period: The time after which the data is actually written to the - database. - additional_setpoints: A list of setpoint parameters to be registered in - the measurement but not scanned. - measurement_name: Name of the measurement. This will be passed down to - the dataset produced by the measurement. If not given, a default - value of 'results' is used for the dataset. - exp: The experiment to use for this measurement. - do_plot: should png and pdf versions of the images be saved after the - run. If None the setting will be read from ``qcodesrc.json`` - use_threads: If True measurements from each instrument will be done on - separate threads. If you are measuring from several instruments - this may give a significant speedup. - show_progress: should a progress bar be displayed during the - measurement. If None the setting will be read from ``qcodesrc.json`` - log_info: Message that is logged during the measurement. If None a default - message is used. - break_condition: Callable that takes no arguments. If returned True, - measurement is interrupted. + **kwargs: kwargs are the same as for dond and forwarded directly to dond. Returns: The QCoDeS dataset. """ - if do_plot is None: - do_plot = cast("bool", config.dataset.dond_plot) - if show_progress is None: - show_progress = config.dataset.dond_show_progress - - meas = Measurement(name=measurement_name, exp=exp) - if log_info is not None: - meas._extra_log_info = log_info - else: - meas._extra_log_info = "Using 'qcodes.dataset.do1d'" - - all_setpoint_params = (param_set, *tuple(s for s in additional_setpoints)) - - measured_parameters = tuple( - param for param in param_meas if isinstance(param, ParameterBase) + return cast( + "AxesTupleListWithDataSet", + dond( + LinSweep( + param=param_set, + start=start, + stop=stop, + delay=delay, + num_points=num_points, + ), + *param_meas, + **kwargs, + squeeze=True, + ), ) - try: - loop_shape = (num_points, *tuple(1 for _ in additional_setpoints)) - shapes: Shapes | None = detect_shape_of_measurement( - measured_parameters, loop_shape - ) - except TypeError: - LOG.exception( - f"Could not detect shape of {measured_parameters} " - f"falling back to unknown shape." - ) - shapes = None - - _register_parameters(meas, all_setpoint_params) - _register_parameters(meas, param_meas, setpoints=all_setpoint_params, shapes=shapes) - _set_write_period(meas, write_period) - _register_actions(meas, enter_actions, exit_actions) - - if use_threads is None: - use_threads = config.dataset.use_threads - - param_meas_caller = ( - ThreadPoolParamsCaller(*param_meas) - if use_threads - else SequentialParamsCaller(*param_meas) - ) - - # do1D enforces a simple relationship between measured parameters - # and set parameters. For anything more complicated this should be - # reimplemented from scratch - with ( - catch_interrupts() as interrupted, - meas.run() as datasaver, - param_meas_caller as call_param_meas, - ): - dataset = datasaver.dataset - additional_setpoints_data = process_params_meas(additional_setpoints) - setpoints = np.linspace(start, stop, num_points) - - # flush to prevent unflushed print's to visually interrupt tqdm bar - # updates - sys.stdout.flush() - sys.stderr.flush() - - for set_point in tqdm(setpoints, disable=not show_progress): - param_set.set(set_point) - time.sleep(delay) - datasaver.add_result( - (param_set, set_point), *call_param_meas(), *additional_setpoints_data - ) - - if callable(break_condition): - if break_condition(): - raise BreakConditionInterrupt("Break condition was met.") - - return _handle_plotting(dataset, do_plot, interrupted()) From 7f536ec1370067b2e646f004d9bc4bf716b4f60d Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 15 Apr 2025 09:17:09 +0200 Subject: [PATCH 4/6] Add changelog for 7065 --- docs/changes/newsfragments/7065.improved | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/changes/newsfragments/7065.improved diff --git a/docs/changes/newsfragments/7065.improved b/docs/changes/newsfragments/7065.improved new file mode 100644 index 000000000000..3b4eb5aace8c --- /dev/null +++ b/docs/changes/newsfragments/7065.improved @@ -0,0 +1,3 @@ +The implementation of ``do0d`` and ``do1d`` have been replaced with a wrapper around `dond`. +This aligns the keyword arguments with ``dond`` and ensures that these function support +the same features as ``dond``. The same change is planned for ``do2d`` in the future. From cde44886239dcfc30afc70249e488b0827a61c8e Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Wed, 16 Apr 2025 10:05:02 +0200 Subject: [PATCH 5/6] Keep original log messages --- src/qcodes/dataset/dond/do_0d.py | 3 +++ src/qcodes/dataset/dond/do_1d.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/qcodes/dataset/dond/do_0d.py b/src/qcodes/dataset/dond/do_0d.py index c1aec3344320..e20341fa00fd 100644 --- a/src/qcodes/dataset/dond/do_0d.py +++ b/src/qcodes/dataset/dond/do_0d.py @@ -38,6 +38,9 @@ def do0d( The QCoDeS dataset. """ + + kwargs.setdefault("log_info", "Using 'qcodes.dataset.do0d'") + # since we only support entering parameters # as a simple list or args we are sure to always # get back a AxesTupleListWithDataSet and cast is safe diff --git a/src/qcodes/dataset/dond/do_1d.py b/src/qcodes/dataset/dond/do_1d.py index c5f9de1971a0..352b2703654d 100644 --- a/src/qcodes/dataset/dond/do_1d.py +++ b/src/qcodes/dataset/dond/do_1d.py @@ -52,6 +52,8 @@ def do1d( The QCoDeS dataset. """ + kwargs.setdefault("log_info", "Using 'qcodes.dataset.do1d'") + return cast( "AxesTupleListWithDataSet", dond( From ce875c0ffceb1186ea4b552f0f356e7748306bce Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Wed, 16 Apr 2025 10:05:56 +0200 Subject: [PATCH 6/6] Add links to docs --- src/qcodes/dataset/dond/do_0d.py | 2 +- src/qcodes/dataset/dond/do_1d.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qcodes/dataset/dond/do_0d.py b/src/qcodes/dataset/dond/do_0d.py index e20341fa00fd..39c9d850bc38 100644 --- a/src/qcodes/dataset/dond/do_0d.py +++ b/src/qcodes/dataset/dond/do_0d.py @@ -32,7 +32,7 @@ def do0d( will be called at each step. The function should take no arguments. The parameters and functions are called in the order they are supplied. - **kwargs: kwargs are the same as for dond and forwarded directly to dond. + **kwargs: kwargs are the same as for :func:`dond` and forwarded directly to :func:`dond`. Returns: The QCoDeS dataset. diff --git a/src/qcodes/dataset/dond/do_1d.py b/src/qcodes/dataset/dond/do_1d.py index 352b2703654d..b413e568e20b 100644 --- a/src/qcodes/dataset/dond/do_1d.py +++ b/src/qcodes/dataset/dond/do_1d.py @@ -46,7 +46,7 @@ def do1d( will be called at each step. The function should take no arguments. The parameters and functions are called in the order they are supplied. - **kwargs: kwargs are the same as for dond and forwarded directly to dond. + **kwargs: kwargs are the same as for :func:`dond` and forwarded directly to :func:`dond`. Returns: The QCoDeS dataset.