iqm-exa-common 25.33__py3-none-any.whl → 26.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. exa/common/api/proto_serialization/_parameter.py +4 -3
  2. exa/common/api/proto_serialization/nd_sweep.py +3 -8
  3. exa/common/api/proto_serialization/sequence.py +5 -5
  4. exa/common/control/sweep/exponential_sweep.py +15 -47
  5. exa/common/control/sweep/fixed_sweep.py +10 -14
  6. exa/common/control/sweep/linear_sweep.py +15 -40
  7. exa/common/control/sweep/option/__init__.py +1 -1
  8. exa/common/control/sweep/option/center_span_base_options.py +14 -7
  9. exa/common/control/sweep/option/center_span_options.py +13 -6
  10. exa/common/control/sweep/option/constants.py +2 -2
  11. exa/common/control/sweep/option/fixed_options.py +8 -2
  12. exa/common/control/sweep/option/option_converter.py +4 -8
  13. exa/common/control/sweep/option/start_stop_base_options.py +20 -6
  14. exa/common/control/sweep/option/start_stop_options.py +20 -5
  15. exa/common/control/sweep/option/sweep_options.py +9 -0
  16. exa/common/control/sweep/sweep.py +52 -16
  17. exa/common/control/sweep/sweep_values.py +58 -0
  18. exa/common/data/base_model.py +40 -0
  19. exa/common/data/parameter.py +123 -68
  20. exa/common/data/setting_node.py +481 -135
  21. exa/common/data/settingnode_v2.html.jinja2 +6 -6
  22. exa/common/data/value.py +49 -0
  23. exa/common/logger/logger.py +1 -1
  24. exa/common/qcm_data/file_adapter.py +2 -6
  25. exa/common/qcm_data/qcm_data_client.py +1 -37
  26. exa/common/sweep/database_serialization.py +30 -98
  27. exa/common/sweep/util.py +4 -5
  28. {iqm_exa_common-25.33.dist-info → iqm_exa_common-26.0.dist-info}/METADATA +2 -2
  29. iqm_exa_common-26.0.dist-info/RECORD +54 -0
  30. exa/common/api/model/__init__.py +0 -15
  31. exa/common/api/model/parameter_model.py +0 -111
  32. exa/common/api/model/setting_model.py +0 -63
  33. exa/common/api/model/setting_node_model.py +0 -72
  34. exa/common/api/model/sweep_model.py +0 -63
  35. exa/common/control/sweep/function_sweep.py +0 -35
  36. exa/common/control/sweep/option/function_options.py +0 -26
  37. exa/common/control/sweep/utils.py +0 -43
  38. iqm_exa_common-25.33.dist-info/RECORD +0 -59
  39. {iqm_exa_common-25.33.dist-info → iqm_exa_common-26.0.dist-info}/LICENSE.txt +0 -0
  40. {iqm_exa_common-25.33.dist-info → iqm_exa_common-26.0.dist-info}/WHEEL +0 -0
  41. {iqm_exa_common-25.33.dist-info → iqm_exa_common-26.0.dist-info}/top_level.txt +0 -0
@@ -27,13 +27,14 @@ _collection_types_inv = {v: k for k, v in _COLLECTION_TYPES.items()}
27
27
 
28
28
  _DATA_TYPES = {
29
29
  DataType.ANYTHING: ppb.Parameter.DataType.DATA_TYPE_UNSPECIFIED,
30
- DataType.NUMBER: ppb.Parameter.DataType.DATA_TYPE_FLOAT64,
30
+ DataType.NUMBER: ppb.Parameter.DataType.DATA_TYPE_FLOAT64, # Deprecated
31
31
  DataType.STRING: ppb.Parameter.DataType.DATA_TYPE_STRING,
32
32
  DataType.COMPLEX: ppb.Parameter.DataType.DATA_TYPE_COMPLEX128,
33
33
  DataType.BOOLEAN: ppb.Parameter.DataType.DATA_TYPE_BOOL,
34
+ DataType.INT: ppb.Parameter.DataType.DATA_TYPE_INT64,
35
+ DataType.FLOAT: ppb.Parameter.DataType.DATA_TYPE_FLOAT64,
34
36
  }
35
- _data_types_inv = {v: k for k, v in _DATA_TYPES.items()}
36
- _data_types_inv[ppb.Parameter.DataType.DATA_TYPE_INT64] = DataType.NUMBER # TODO add DataType.INT?
37
+ _data_types_inv = {v: k for k, v in _DATA_TYPES.items() if k != DataType.NUMBER}
37
38
 
38
39
 
39
40
  def pack(parameter: Parameter) -> ppb.Parameter:
@@ -18,8 +18,6 @@ import iqm.data_definitions.common.v1.sweep_pb2 as spb
18
18
 
19
19
  from exa.common.api.proto_serialization import sequence
20
20
  import exa.common.api.proto_serialization._parameter as param_proto
21
- from exa.common.control.sweep.fixed_sweep import FixedSweep
22
- from exa.common.control.sweep.option.fixed_options import FixedOptions
23
21
  from exa.common.control.sweep.sweep import Sweep
24
22
  from exa.common.data.parameter import DataType, Parameter
25
23
  from exa.common.sweep.util import NdSweep
@@ -41,10 +39,7 @@ def pack(nd_sweep: NdSweep, minimal: bool = True) -> spb.CartesianSweep:
41
39
 
42
40
 
43
41
  def unpack(proto: spb.CartesianSweep) -> NdSweep:
44
- """Convert protobuf representation into a NdSweep. Reverse operation of :func:`.pack`.
45
-
46
- Note: All sweeps will be of type :class:`.FixedSweep`.
47
- """
42
+ """Convert protobuf representation into a NdSweep. Reverse operation of :func:`.pack`."""
48
43
  nd_sweep = []
49
44
  for parallel_proto in reversed(proto.parallel_sweeps):
50
45
  parallel = tuple(_unpack_single_sweep(sweep) for sweep in parallel_proto.single_parameter_sweeps)
@@ -59,10 +54,10 @@ def _pack_single_sweep(sweep: Sweep, minimal: bool) -> spb.SingleParameterSweep:
59
54
  return spb.SingleParameterSweep(**kwargs)
60
55
 
61
56
 
62
- def _unpack_single_sweep(proto: spb.SingleParameterSweep) -> FixedSweep:
57
+ def _unpack_single_sweep(proto: spb.SingleParameterSweep) -> Sweep:
63
58
  sweep_values = sequence.unpack(proto.values)
64
59
  if proto.HasField("parameter"):
65
60
  parameter = param_proto.unpack(proto.parameter)
66
61
  else:
67
62
  parameter = Parameter(proto.parameter_name, data_type=DataType.ANYTHING)
68
- return FixedSweep(parameter, FixedOptions(sweep_values))
63
+ return Sweep(parameter=parameter, data=sweep_values)
@@ -37,18 +37,18 @@ def pack(values: Sequence) -> dpb.Sequence:
37
37
  if not values:
38
38
  return target
39
39
  dtype = type(values[0])
40
- if dtype == complex: # noqa: E721
40
+ if dtype is complex:
41
41
  target_field = target.complex128_array
42
42
  target_field.real.MergeFrom(np.real(values))
43
43
  target_field.imag.MergeFrom(np.imag(values))
44
44
  return target
45
- if dtype == bool: # noqa: E721
45
+ if dtype is bool:
46
46
  target_field = target.bool_array
47
- elif dtype == int: # noqa: E721
47
+ elif dtype is int:
48
48
  target_field = target.int64_array
49
- elif dtype == float: # noqa: E721
49
+ elif dtype is float:
50
50
  target_field = target.float64_array
51
- elif dtype == str: # noqa: E721
51
+ elif dtype is str:
52
52
  target_field = target.string_array
53
53
  else:
54
54
  raise TypeError(f"Unsupported numpy array type {dtype} for a sequence.")
@@ -14,19 +14,15 @@
14
14
 
15
15
  """Sweep specification with exponentially spaced values."""
16
16
 
17
- from dataclasses import dataclass, field
18
- import logging
19
- import math
20
- from typing import List, Union
21
-
22
- import numpy as np
17
+ from typing import Any
18
+ import warnings
23
19
 
24
20
  from exa.common.control.sweep.option import CenterSpanBaseOptions, StartStopBaseOptions
25
21
  from exa.common.control.sweep.sweep import Sweep
22
+ from exa.common.data.parameter import Parameter
26
23
  from exa.common.errors.exa_error import InvalidSweepOptionsTypeError
27
24
 
28
25
 
29
- @dataclass(frozen=True)
30
26
  class ExponentialSweep(Sweep):
31
27
  """Generates parameter values spaced evenly on a geometric progression based on `options`.
32
28
 
@@ -41,43 +37,15 @@ class ExponentialSweep(Sweep):
41
37
 
42
38
  """
43
39
 
44
- logger: logging.Logger = field(init=False)
45
-
46
- def __post_init__(self):
47
- object.__setattr__(self, "logger", logging.getLogger(__name__ + "." + self.__class__.__name__))
48
- if isinstance(self.options, StartStopBaseOptions):
49
- self.__from_start_stop_base(self.options)
50
- elif isinstance(self.options, CenterSpanBaseOptions):
51
- self.__from_center_span_base(self.options)
52
- else:
53
- raise InvalidSweepOptionsTypeError(str(type(self.options)))
54
-
55
- def __from_start_stop_base(self, options: StartStopBaseOptions) -> None:
56
- self.logger.debug(f"EXPONENTS: ({options.start}, {options.stop}) with base {options.base}")
57
- self._validate_value(options.start, "start")
58
- self._validate_value(options.stop, "stop")
59
- if options.start == 0 or options.stop == 0:
60
- raise ValueError("Exponential range sweep start and stop values must not be zero.")
61
- options = StartStopBaseOptions(
62
- math.pow(options.base, options.start),
63
- math.pow(options.base, options.stop),
64
- count=options.count,
65
- )
66
- object.__setattr__(self, "_data", self.__generate(options))
67
-
68
- def __from_center_span_base(self, options: CenterSpanBaseOptions) -> None:
69
- start = options.center - (options.span / 2)
70
- stop = options.center + (options.span / 2)
71
- self.logger.debug("EXPONENTS: ({}, {}) with base {}".format(start, stop, options.base))
72
- (start, stop) = (start, stop) if options.asc else (stop, start)
73
- start_stop_base_options = StartStopBaseOptions(
74
- start,
75
- stop,
76
- count=options.count,
77
- base=options.base,
78
- )
79
- self.__from_start_stop_base(start_stop_base_options)
80
-
81
- @staticmethod
82
- def __generate(options: StartStopBaseOptions) -> List[Union[int, float, complex]]:
83
- return np.geomspace(options.start, options.stop, options.count, endpoint=True).tolist()
40
+ def __init__(
41
+ self,
42
+ parameter: Parameter,
43
+ options: StartStopBaseOptions | CenterSpanBaseOptions | None = None,
44
+ *,
45
+ data: list[Any] | None = None,
46
+ **kwargs,
47
+ ) -> None:
48
+ warnings.warn("ExponentialSweep is deprecated, use Sweep instead.", DeprecationWarning)
49
+ if options and not isinstance(options, StartStopBaseOptions | CenterSpanBaseOptions):
50
+ raise InvalidSweepOptionsTypeError(str(type(options)))
51
+ super().__init__(parameter, options, data=data, **kwargs)
@@ -14,26 +14,22 @@
14
14
 
15
15
  """Sweep specification with arbitrary values."""
16
16
 
17
- from dataclasses import dataclass
18
-
19
- import numpy as np
17
+ from typing import Any
18
+ import warnings
20
19
 
21
20
  from exa.common.control.sweep.option import FixedOptions
22
21
  from exa.common.control.sweep.sweep import Sweep
22
+ from exa.common.data.parameter import Parameter
23
23
  from exa.common.errors.exa_error import InvalidSweepOptionsTypeError
24
24
 
25
25
 
26
- @dataclass(frozen=True)
27
26
  class FixedSweep(Sweep):
28
27
  """A sweep over arbitrary set of values, given by `options`."""
29
28
 
30
- def __post_init__(self):
31
- if not isinstance(self.options, FixedOptions):
32
- raise InvalidSweepOptionsTypeError(str(type(self.options)))
33
-
34
- if not all(self.parameter.validate(value) for value in self.options.fixed):
35
- raise ValueError(
36
- f"Invalid fixed range data {self.options.fixed} for parameter type {self.parameter.data_type.name}."
37
- )
38
- data = np.asarray(self.options.fixed).tolist()
39
- object.__setattr__(self, "_data", data)
29
+ def __init__(
30
+ self, parameter: Parameter, options: FixedOptions | None = None, *, data: list[Any] | None = None, **kwargs
31
+ ) -> None:
32
+ warnings.warn("FixedSweep is deprecated, use Sweep instead.", DeprecationWarning)
33
+ if options and not isinstance(options, FixedOptions):
34
+ raise InvalidSweepOptionsTypeError(str(type(options)))
35
+ super().__init__(parameter, options, data=data, **kwargs)
@@ -14,18 +14,15 @@
14
14
 
15
15
  """Sweep specification with linearly spaced values."""
16
16
 
17
- from dataclasses import dataclass
18
- import math
19
- from typing import Union
20
-
21
- import numpy as np
17
+ from typing import Any
18
+ import warnings
22
19
 
23
20
  from exa.common.control.sweep.option import CenterSpanOptions, StartStopOptions
24
21
  from exa.common.control.sweep.sweep import Sweep
22
+ from exa.common.data.parameter import Parameter
25
23
  from exa.common.errors.exa_error import InvalidSweepOptionsTypeError
26
24
 
27
25
 
28
- @dataclass(frozen=True)
29
26
  class LinearSweep(Sweep):
30
27
  """Generates evenly spaced parameter values based on `options`.
31
28
 
@@ -38,37 +35,15 @@ class LinearSweep(Sweep):
38
35
 
39
36
  """
40
37
 
41
- def __post_init__(self):
42
- if isinstance(self.options, StartStopOptions):
43
- self.__from_start_stop(self.options)
44
- elif isinstance(self.options, CenterSpanOptions):
45
- self.__from_center_span(self.options)
46
- else:
47
- raise InvalidSweepOptionsTypeError(str(type(self.options)))
48
-
49
- def __from_start_stop(self, options: StartStopOptions) -> None:
50
- self._validate_value(options.start, "start")
51
- self._validate_value(options.stop, "stop")
52
- self._validate_value(options.step, "step")
53
- object.__setattr__(self, "_data", self.__generate(options))
54
-
55
- def __from_center_span(self, options: CenterSpanOptions) -> None:
56
- self._validate_value(options.center, "center")
57
- self._validate_value(options.span, "span")
58
- start = options.center - (options.span / 2)
59
- stop = options.center + (options.span / 2)
60
- (start, stop) = (start, stop) if options.asc else (stop, start)
61
- start_stop_options = StartStopOptions(start, stop, count=options.count, step=options.step)
62
- self.__from_start_stop(start_stop_options)
63
-
64
- def __generate(self, options: StartStopOptions) -> list[Union[int, float, complex]]:
65
- if options.step is not None:
66
- count = 1 + math.ceil(abs(options.stop - options.start) / float(np.abs(options.step)))
67
- data = self.__generate_by_count(options, count)
68
- else:
69
- data = self.__generate_by_count(options, options.count)
70
- return data
71
-
72
- @staticmethod
73
- def __generate_by_count(options: StartStopOptions, count: int) -> list[Union[int, float, complex]]:
74
- return np.linspace(options.start, options.stop, count, endpoint=True).tolist()
38
+ def __init__(
39
+ self,
40
+ parameter: Parameter,
41
+ options: StartStopOptions | CenterSpanOptions | None = None,
42
+ *,
43
+ data: list[Any] | None = None,
44
+ **kwargs,
45
+ ) -> None:
46
+ warnings.warn("LinearSweep is deprecated, use Sweep instead.", DeprecationWarning)
47
+ if options and not isinstance(options, StartStopOptions | CenterSpanOptions):
48
+ raise InvalidSweepOptionsTypeError(str(type(options)))
49
+ super().__init__(parameter, options, data=data, **kwargs)
@@ -11,12 +11,12 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
14
15
  """Range specifications for Sweeps."""
15
16
 
16
17
  from .center_span_base_options import CenterSpanBaseOptions
17
18
  from .center_span_options import CenterSpanOptions
18
19
  from .fixed_options import FixedOptions
19
- from .function_options import FunctionOptions
20
20
  from .start_stop_base_options import StartStopBaseOptions
21
21
  from .start_stop_options import StartStopOptions
22
22
  from .sweep_options import SweepOptions
@@ -12,12 +12,12 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """Range specification used with ExponentialSweep."""
15
+ """Range specification used with exponential sweeps."""
16
16
 
17
17
  from dataclasses import dataclass
18
- from typing import Union
19
18
 
20
19
  from exa.common.control.sweep.option.constants import DEFAULT_BASE, DEFAULT_COUNT
20
+ from exa.common.control.sweep.option.start_stop_base_options import StartStopBaseOptions
21
21
  from exa.common.control.sweep.option.sweep_options import SweepOptions
22
22
 
23
23
 
@@ -32,17 +32,17 @@ class CenterSpanBaseOptions(SweepOptions):
32
32
  """
33
33
 
34
34
  #: Value of interval center for the power.
35
- center: Union[int, float]
35
+ center: int | float
36
36
  #: Size of the interval for the power
37
- span: Union[int, float]
37
+ span: int | float
38
38
  #: Number of values to generate. Default to
39
39
  #: :const:`exa.common.control.sweep.option.constants.DEFAULT_COUNT`.
40
- count: int = None
40
+ count: int | None = None
41
41
  #: Number, that is raised to the power of the range with the center `center` and the size of `span`.
42
42
  # Default to :const:`exa.common.control.sweep.option.constants.DEFAULT_BASE`.
43
- base: Union[int, float] = None
43
+ base: int | float | None = None
44
44
  #: Order of generated values. Default to ascending
45
- asc: bool = None
45
+ asc: bool | None = None
46
46
 
47
47
  def __post_init__(self):
48
48
  if self.count is None:
@@ -51,3 +51,10 @@ class CenterSpanBaseOptions(SweepOptions):
51
51
  object.__setattr__(self, "base", DEFAULT_BASE)
52
52
  if self.asc is None:
53
53
  object.__setattr__(self, "asc", True)
54
+
55
+ @property
56
+ def data(self) -> list[int | float | complex]:
57
+ start = self.center - (self.span / 2)
58
+ stop = self.center + (self.span / 2)
59
+ (start, stop) = (start, stop) if self.asc else (stop, start)
60
+ return StartStopBaseOptions(start, stop, count=self.count, base=self.base).data
@@ -15,9 +15,9 @@
15
15
  """Range specification to define a range around a center value."""
16
16
 
17
17
  from dataclasses import dataclass
18
- from typing import Union
19
18
 
20
19
  from exa.common.control.sweep.option.constants import DEFAULT_COUNT
20
+ from exa.common.control.sweep.option.start_stop_options import StartStopOptions
21
21
  from exa.common.control.sweep.option.sweep_options import SweepOptions
22
22
 
23
23
 
@@ -32,17 +32,17 @@ class CenterSpanOptions(SweepOptions):
32
32
  """
33
33
 
34
34
  #: Value of interval center.
35
- center: Union[int, float, complex]
35
+ center: int | float | complex
36
36
  #: Size of the interval.
37
- span: Union[int, float, complex]
37
+ span: int | float | complex
38
38
  #: Number of values to generate.
39
39
  #: If `count` and `step` are empty, the default value of count is
40
40
  #: :const:`exa.common.control.sweep.option.constants.DEFAULT_COUNT`.
41
- count: int = None
41
+ count: int | None = None
42
42
  #: Size of spacing between values.
43
- step: Union[int, float, complex] = None
43
+ step: int | float | complex = None
44
44
  #: Order of generated values. Default to ascending
45
- asc: bool = None
45
+ asc: bool | None = None
46
46
 
47
47
  def __post_init__(self):
48
48
  if self.count is not None and self.step is not None:
@@ -51,3 +51,10 @@ class CenterSpanOptions(SweepOptions):
51
51
  object.__setattr__(self, "count", DEFAULT_COUNT)
52
52
  if self.asc is None:
53
53
  object.__setattr__(self, "asc", True)
54
+
55
+ @property
56
+ def data(self) -> list[int | float | complex]:
57
+ start = self.center - (self.span / 2)
58
+ stop = self.center + (self.span / 2)
59
+ (start, stop) = (start, stop) if self.asc else (stop, start)
60
+ return StartStopOptions(start, stop, count=self.count, step=self.step).data
@@ -14,14 +14,14 @@
14
14
 
15
15
  """Helper constants for SweepOptions classes."""
16
16
 
17
- from typing import Any, Dict
17
+ from typing import Any
18
18
 
19
19
  #: Default value for `count` value in options.
20
20
  DEFAULT_COUNT: int = 101
21
21
  #: Default value for `base` value in options.
22
22
  DEFAULT_BASE: int = 10
23
23
  #: Dictionary with all possible types of options
24
- OPTIONS_TYPE: Dict[str, Any] = {
24
+ OPTIONS_TYPE: dict[str, Any] = {
25
25
  "start": "start",
26
26
  "stop": "stop",
27
27
  "center": "center",
@@ -15,9 +15,11 @@
15
15
  """Range specification for arbitrary set of values."""
16
16
 
17
17
  from dataclasses import dataclass
18
- from typing import List, Union
18
+
19
+ import numpy as np
19
20
 
20
21
  from exa.common.control.sweep.option.sweep_options import SweepOptions
22
+ from exa.common.control.sweep.sweep_values import SweepValues
21
23
 
22
24
 
23
25
  @dataclass(frozen=True)
@@ -25,4 +27,8 @@ class FixedOptions(SweepOptions):
25
27
  """Range fixed options."""
26
28
 
27
29
  #: List of values.
28
- fixed: List[Union[int, float, complex, bool, str]]
30
+ fixed: SweepValues
31
+
32
+ @property
33
+ def data(self) -> SweepValues:
34
+ return np.asarray(self.fixed).tolist()
@@ -15,7 +15,7 @@
15
15
  """Helper to create a SweepOptions instance from a dict."""
16
16
 
17
17
  import ast
18
- from typing import Any, Dict, List
18
+ from typing import Any
19
19
 
20
20
  from exa.common.control.sweep.option import (
21
21
  CenterSpanBaseOptions,
@@ -28,7 +28,7 @@ from exa.common.control.sweep.option.constants import OPTIONS_TYPE
28
28
  from exa.common.control.sweep.option.sweep_options import SweepOptions
29
29
 
30
30
 
31
- def convert_to_options(config: Dict[str, Any]) -> SweepOptions:
31
+ def convert_to_options(config: dict[str, Any]) -> SweepOptions:
32
32
  """Creates one of the options object based on configuration dictionary.
33
33
 
34
34
  - If configuration has keys ``start`` and ``stop``, :class:`.StartStopOptions` is created.
@@ -37,10 +37,6 @@ def convert_to_options(config: Dict[str, Any]) -> SweepOptions:
37
37
  - If configuration has keys ``center_exp`` and ``span_exp``, :class:`.CenterSpanBaseOptions` is created
38
38
  - If configuration has keys ``fixed``, :class:`.FixedOptions` is created.
39
39
 
40
- .. note::
41
- There is no support for callable options, so it is impossible to create
42
- :class:`.FunctionOptions` out of configuration
43
-
44
40
  Args:
45
41
  config: Configuration dictionary.
46
42
 
@@ -72,9 +68,9 @@ def convert_to_options(config: Dict[str, Any]) -> SweepOptions:
72
68
  raise ValueError(f"Config {config} cannot be converted to range options")
73
69
 
74
70
 
75
- def __update_config(config: Dict[str, Any], keys: List[str]) -> Dict[str, Any]:
71
+ def __update_config(config: dict[str, Any], keys: list[str]) -> dict[str, Any]:
76
72
  return {key: ast.literal_eval("".join(str(value).strip())) for key, value in config.items() if key in keys}
77
73
 
78
74
 
79
- def __rename_key(config: Dict[str, Any], old_key: str, new_key: str) -> None:
75
+ def __rename_key(config: dict[str, Any], old_key: str, new_key: str) -> None:
80
76
  config[new_key] = config.pop(old_key)
@@ -12,14 +12,19 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- """Range specification used with ExponentialSweep."""
15
+ """Range specification used with exponential sweeps."""
16
16
 
17
17
  from dataclasses import dataclass
18
- from typing import Union
18
+ import logging
19
+ import math
20
+
21
+ import numpy as np
19
22
 
20
23
  from exa.common.control.sweep.option.constants import DEFAULT_BASE, DEFAULT_COUNT
21
24
  from exa.common.control.sweep.option.sweep_options import SweepOptions
22
25
 
26
+ logger = logging.getLogger(__name__)
27
+
23
28
 
24
29
  @dataclass(frozen=True)
25
30
  class StartStopBaseOptions(SweepOptions):
@@ -30,18 +35,27 @@ class StartStopBaseOptions(SweepOptions):
30
35
  """
31
36
 
32
37
  #: The power for the start of the interval.
33
- start: Union[int, float, complex]
38
+ start: int | float | complex
34
39
  #: The power for the end of the interval.
35
- stop: Union[int, float, complex]
40
+ stop: int | float | complex
36
41
  #: Number of values to generate. Default to
37
42
  #: :const:`exa.common.control.sweep.option.constants.DEFAULT_COUNT`.
38
- count: int = None
43
+ count: int | None = None
39
44
  #: Number, that is raised to the power `start` or `stop`. Default to
40
45
  #: :const:`exa.common.control.sweep.option.constants.DEFAULT_BASE`.
41
- base: Union[int, float] = None
46
+ base: int | float | None = None
42
47
 
43
48
  def __post_init__(self):
44
49
  if self.count is None:
45
50
  object.__setattr__(self, "count", DEFAULT_COUNT)
46
51
  if self.base is None:
47
52
  object.__setattr__(self, "base", DEFAULT_BASE)
53
+ if self.start == 0 or self.stop == 0:
54
+ raise ValueError("Exponential range sweep start and stop values must not be zero.")
55
+
56
+ @property
57
+ def data(self) -> list[int | float | complex]:
58
+ logger.debug(f"EXPONENTS: ({self.start}, {self.stop}) with base {self.base}")
59
+ start = math.pow(self.base, self.start)
60
+ stop = math.pow(self.base, self.stop)
61
+ return np.geomspace(start, stop, self.count, endpoint=True).tolist()
@@ -15,10 +15,13 @@
15
15
  """Range specification to define a linearly spaced interval."""
16
16
 
17
17
  from dataclasses import dataclass
18
- from typing import Union
18
+ import math
19
+
20
+ import numpy as np
19
21
 
20
22
  from exa.common.control.sweep.option.constants import DEFAULT_COUNT
21
23
  from exa.common.control.sweep.option.sweep_options import SweepOptions
24
+ from exa.common.control.sweep.sweep_values import SweepValues
22
25
 
23
26
 
24
27
  @dataclass(frozen=True)
@@ -32,16 +35,16 @@ class StartStopOptions(SweepOptions):
32
35
  """
33
36
 
34
37
  #: Starting value of interval.
35
- start: Union[int, float, complex]
38
+ start: int | float | complex
36
39
  #: Stopping value of interval.
37
- stop: Union[int, float, complex]
40
+ stop: int | float | complex
38
41
  #: Number of values to generate. Must be non-negative.
39
42
  #: If `count` and `step` are empty, the default value of count is
40
43
  #: :const:`exa.common.control.sweep.option.constants.DEFAULT_COUNT`.
41
- count: int = None
44
+ count: int | None = None
42
45
  #: Size of spacing between values. Must be non-zero.
43
46
  #: If both `count` and `step` are not empty, only `count` is used
44
- step: Union[int, float, complex] = None
47
+ step: int | float | complex | None = None
45
48
 
46
49
  def __post_init__(self):
47
50
  if self.count is not None and self.step is not None:
@@ -55,3 +58,15 @@ class StartStopOptions(SweepOptions):
55
58
  )
56
59
  if self.count is not None and self.count <= 0:
57
60
  raise ValueError("Count value specified for range must be greater than zero.")
61
+
62
+ @property
63
+ def data(self) -> list[int | float | complex]:
64
+ if self.step is not None:
65
+ count = 1 + math.ceil(abs(self.stop - self.start) / float(np.abs(self.step)))
66
+ data = self._generate_by_count(count)
67
+ else:
68
+ data = self._generate_by_count(self.count)
69
+ return data
70
+
71
+ def _generate_by_count(self, count: int) -> SweepValues:
72
+ return np.linspace(self.start, self.stop, count, endpoint=True).tolist()
@@ -12,9 +12,18 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ """Base class for sweep options."""
16
+
15
17
  from dataclasses import dataclass
16
18
 
19
+ from exa.common.control.sweep.sweep_values import SweepValues
20
+
17
21
 
18
22
  @dataclass(frozen=True)
19
23
  class SweepOptions:
20
24
  """Base immutable class for sweep options."""
25
+
26
+ @property
27
+ def data(self) -> SweepValues:
28
+ """List of values for :attr:`parameter`"""
29
+ raise NotImplementedError