iqm-exa-common 25.34__py3-none-any.whl → 26.1__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.
- exa/common/api/proto_serialization/_parameter.py +4 -3
- exa/common/api/proto_serialization/nd_sweep.py +3 -8
- exa/common/api/proto_serialization/sequence.py +5 -5
- exa/common/api/proto_serialization/setting_node.py +3 -1
- exa/common/control/sweep/exponential_sweep.py +15 -47
- exa/common/control/sweep/fixed_sweep.py +10 -14
- exa/common/control/sweep/linear_sweep.py +15 -40
- exa/common/control/sweep/option/__init__.py +1 -1
- exa/common/control/sweep/option/center_span_base_options.py +14 -7
- exa/common/control/sweep/option/center_span_options.py +13 -6
- exa/common/control/sweep/option/constants.py +2 -2
- exa/common/control/sweep/option/fixed_options.py +8 -2
- exa/common/control/sweep/option/option_converter.py +4 -8
- exa/common/control/sweep/option/start_stop_base_options.py +20 -6
- exa/common/control/sweep/option/start_stop_options.py +20 -5
- exa/common/control/sweep/option/sweep_options.py +9 -0
- exa/common/control/sweep/sweep.py +52 -16
- exa/common/control/sweep/sweep_values.py +58 -0
- exa/common/data/base_model.py +40 -0
- exa/common/data/parameter.py +123 -68
- exa/common/data/setting_node.py +481 -135
- exa/common/data/settingnode_v2.html.jinja2 +6 -6
- exa/common/data/value.py +49 -0
- exa/common/logger/logger.py +1 -1
- exa/common/qcm_data/file_adapter.py +2 -6
- exa/common/qcm_data/qcm_data_client.py +1 -37
- exa/common/sweep/database_serialization.py +30 -98
- exa/common/sweep/util.py +4 -5
- {iqm_exa_common-25.34.dist-info → iqm_exa_common-26.1.dist-info}/METADATA +2 -2
- iqm_exa_common-26.1.dist-info/RECORD +54 -0
- exa/common/api/model/__init__.py +0 -15
- exa/common/api/model/parameter_model.py +0 -111
- exa/common/api/model/setting_model.py +0 -63
- exa/common/api/model/setting_node_model.py +0 -72
- exa/common/api/model/sweep_model.py +0 -63
- exa/common/control/sweep/function_sweep.py +0 -35
- exa/common/control/sweep/option/function_options.py +0 -26
- exa/common/control/sweep/utils.py +0 -43
- iqm_exa_common-25.34.dist-info/RECORD +0 -59
- {iqm_exa_common-25.34.dist-info → iqm_exa_common-26.1.dist-info}/LICENSE.txt +0 -0
- {iqm_exa_common-25.34.dist-info → iqm_exa_common-26.1.dist-info}/WHEEL +0 -0
- {iqm_exa_common-25.34.dist-info → iqm_exa_common-26.1.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) ->
|
|
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
|
|
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
|
|
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
|
|
45
|
+
if dtype is bool:
|
|
46
46
|
target_field = target.bool_array
|
|
47
|
-
elif dtype
|
|
47
|
+
elif dtype is int:
|
|
48
48
|
target_field = target.int64_array
|
|
49
|
-
elif dtype
|
|
49
|
+
elif dtype is float:
|
|
50
50
|
target_field = target.float64_array
|
|
51
|
-
elif dtype
|
|
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.")
|
|
@@ -85,4 +85,6 @@ def unpack(proto: spb.SettingNode) -> SettingNode:
|
|
|
85
85
|
"""
|
|
86
86
|
settings = {key: _unpack_setting(content) for key, content in proto.settings.items()}
|
|
87
87
|
nodes = {key: unpack(content) for key, content in proto.subnodes.items()}
|
|
88
|
-
|
|
88
|
+
# Names are currently NEVER aligned with the paths when deserializing. This is safe to do, since currently nothing
|
|
89
|
+
# in the server-side assumes path==name, but if such logic is added this needs to be reconsidered.
|
|
90
|
+
return SettingNode(name=proto.name, **(settings | nodes), align_name=False)
|
|
@@ -14,19 +14,15 @@
|
|
|
14
14
|
|
|
15
15
|
"""Sweep specification with exponentially spaced values."""
|
|
16
16
|
|
|
17
|
-
from
|
|
18
|
-
import
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if not
|
|
35
|
-
raise
|
|
36
|
-
|
|
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
|
|
18
|
-
import
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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:
|
|
35
|
+
center: int | float
|
|
36
36
|
#: Size of the interval for the power
|
|
37
|
-
span:
|
|
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:
|
|
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:
|
|
35
|
+
center: int | float | complex
|
|
36
36
|
#: Size of the interval.
|
|
37
|
-
span:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
15
|
+
"""Range specification used with exponential sweeps."""
|
|
16
16
|
|
|
17
17
|
from dataclasses import dataclass
|
|
18
|
-
|
|
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:
|
|
38
|
+
start: int | float | complex
|
|
34
39
|
#: The power for the end of the interval.
|
|
35
|
-
stop:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
38
|
+
start: int | float | complex
|
|
36
39
|
#: Stopping value of interval.
|
|
37
|
-
stop:
|
|
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:
|
|
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
|