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.
- 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/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.33.dist-info → iqm_exa_common-26.0.dist-info}/METADATA +2 -2
- iqm_exa_common-26.0.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.33.dist-info/RECORD +0 -59
- {iqm_exa_common-25.33.dist-info → iqm_exa_common-26.0.dist-info}/LICENSE.txt +0 -0
- {iqm_exa_common-25.33.dist-info → iqm_exa_common-26.0.dist-info}/WHEEL +0 -0
- {iqm_exa_common-25.33.dist-info → iqm_exa_common-26.0.dist-info}/top_level.txt +0 -0
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
{% endmacro %}
|
|
30
30
|
|
|
31
31
|
{% macro slisttabrow(key, s) %}
|
|
32
|
-
<tr>
|
|
32
|
+
<tr style={{"color:gray" if s.read_only else ""}}>
|
|
33
33
|
<td style="text-align: left">
|
|
34
34
|
<span style="font-family: monospace;
|
|
35
35
|
font-weight: bold"
|
|
@@ -59,18 +59,18 @@
|
|
|
59
59
|
{% macro snlist(s, lvl=0) %}
|
|
60
60
|
{% if slistmode %}
|
|
61
61
|
<ul style="padding-left: 2em; list-style: none;">
|
|
62
|
-
{% for key, subs in s.
|
|
62
|
+
{% for key, subs in s.settings.items() %}{{ slistitem(key, subs) }}{% endfor %}
|
|
63
63
|
</ul>
|
|
64
64
|
{% else %}
|
|
65
|
-
{% if s.
|
|
65
|
+
{% if s.settings %}
|
|
66
66
|
<table style="margin-left: 1em;">
|
|
67
|
-
{% for key, subs in s.
|
|
67
|
+
{% for key, subs in s.settings.items() %}{{ slisttabrow(key, subs) }}{% endfor %}
|
|
68
68
|
</table>
|
|
69
69
|
{% endif %}
|
|
70
70
|
{% endif %}
|
|
71
|
-
{% if s.
|
|
71
|
+
{% if s.subtrees %}
|
|
72
72
|
<ul class="exa-subtree">
|
|
73
|
-
{% for key, subsn in s.
|
|
73
|
+
{% for key, subsn in s.subtrees.items() %}{{ snlistitem(key, subsn, lvl+1) }}{% endfor %}
|
|
74
74
|
</ul>
|
|
75
75
|
{% endif %}
|
|
76
76
|
{% endmacro %}
|
exa/common/data/value.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
from typing import Annotated, Any
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pydantic import PlainSerializer, PlainValidator, WithJsonSchema
|
|
6
|
+
from pydantic_core import core_schema
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def validate_value(value: Any) -> Any:
|
|
10
|
+
"""Validate (i.e. deserialize) JSON serializable value to Python type, to support complex and ndarray types."""
|
|
11
|
+
if isinstance(value, dict):
|
|
12
|
+
if "__complex__" in value:
|
|
13
|
+
value = complex(value["real"], value["imag"])
|
|
14
|
+
elif "__ndarray__" in value:
|
|
15
|
+
data = base64.b64decode(value["data"])
|
|
16
|
+
value = np.frombuffer(data, value["dtype"]).reshape(value["shape"])
|
|
17
|
+
return value
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def serialize_value(value: Any) -> Any:
|
|
21
|
+
"""Serialize value type to JSON serializable type, to support complex and ndarray types."""
|
|
22
|
+
if isinstance(value, complex):
|
|
23
|
+
value = {"__complex__": "true", "real": value.real, "imag": value.imag}
|
|
24
|
+
elif isinstance(value, np.ndarray):
|
|
25
|
+
# ensure array buffer is contiguous and in C order
|
|
26
|
+
value = np.require(value, requirements=["A", "C"])
|
|
27
|
+
data = base64.b64encode(value.data)
|
|
28
|
+
value = {"__ndarray__": "true", "data": data, "dtype": str(value.dtype), "shape": value.shape}
|
|
29
|
+
return value
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# TODO: We might want to rename these to ObservationValue and ObservationUncertainty, respectively.
|
|
33
|
+
Value = Annotated[
|
|
34
|
+
bool | str | int | float | complex | np.ndarray,
|
|
35
|
+
PlainValidator(validate_value),
|
|
36
|
+
PlainSerializer(serialize_value),
|
|
37
|
+
WithJsonSchema(core_schema.any_schema()),
|
|
38
|
+
]
|
|
39
|
+
Uncertainty = Annotated[
|
|
40
|
+
int | float | complex | np.ndarray,
|
|
41
|
+
PlainValidator(validate_value),
|
|
42
|
+
PlainSerializer(serialize_value),
|
|
43
|
+
WithJsonSchema(core_schema.any_schema()),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# TODO (Marko): Consider if we want to rename these?
|
|
47
|
+
# We have "as" imports so seems like more descriptive name would be better.
|
|
48
|
+
ObservationValue = Value
|
|
49
|
+
ObservationUncertainty = Uncertainty
|
exa/common/logger/logger.py
CHANGED
|
@@ -19,7 +19,7 @@ from typing import Any, Callable, Optional
|
|
|
19
19
|
BRIEF_DATEFMT = "%m-%d %H:%M:%S"
|
|
20
20
|
|
|
21
21
|
BRIEF = "[{asctime};{levelname:.1}{extra_info}] {message}"
|
|
22
|
-
VERBOSE = "[{asctime};{levelname};{processName}({process});{threadName}({thread});{name};{filename}:{lineno}:{funcName}{extra_info}] {message}" #
|
|
22
|
+
VERBOSE = "[{asctime};{levelname};{processName}({process});{threadName}({thread});{name};{filename}:{lineno}:{funcName}{extra_info}] {message}" # noqa: E501
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class ExtraFormatter(logging.Formatter):
|
|
@@ -17,7 +17,7 @@ from http import HTTPStatus
|
|
|
17
17
|
import io
|
|
18
18
|
import locale
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from urllib.parse import
|
|
20
|
+
from urllib.parse import urlparse
|
|
21
21
|
from urllib.request import url2pathname
|
|
22
22
|
|
|
23
23
|
from requests import Response
|
|
@@ -37,12 +37,8 @@ class FileAdapter(BaseAdapter):
|
|
|
37
37
|
raise ValueError("Invalid request method %s" % request.method)
|
|
38
38
|
|
|
39
39
|
url_parts = urlparse(request.url)
|
|
40
|
-
params: dict = parse_qs(url_parts.query, keep_blank_values=True)
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
path = url2pathname(str(Path(url_parts.path, f"{params['chip_label'][0]}.json")))
|
|
44
|
-
else:
|
|
45
|
-
path = url_parts.netloc + f"{str(Path(url2pathname(url_parts.path)))}.json"
|
|
41
|
+
path = url_parts.netloc + f"{str(Path(url2pathname(url_parts.path)))}.json"
|
|
46
42
|
response = Response()
|
|
47
43
|
try:
|
|
48
44
|
response.status_code = HTTPStatus.OK
|
|
@@ -19,7 +19,7 @@ from functools import cache
|
|
|
19
19
|
import json
|
|
20
20
|
import logging
|
|
21
21
|
from pathlib import Path
|
|
22
|
-
from typing import Any
|
|
22
|
+
from typing import Any
|
|
23
23
|
|
|
24
24
|
from packaging.version import Version
|
|
25
25
|
import requests
|
|
@@ -101,42 +101,6 @@ class QCMDataClient:
|
|
|
101
101
|
self._validate_chip_design_record(data, chip_label)
|
|
102
102
|
return data
|
|
103
103
|
|
|
104
|
-
def get_chad(self, chip_label: str) -> Optional[dict]:
|
|
105
|
-
"""DEPRECATED. Use :meth:`get_chip_design_record` instead."""
|
|
106
|
-
url_tail = f"/v2/chads/?chip_label={chip_label}&limit=1"
|
|
107
|
-
try:
|
|
108
|
-
response = self._send_request(self.session.get, f"{self.root_url}{url_tail}")
|
|
109
|
-
except RequestError as err:
|
|
110
|
-
if self._fallback_root_url:
|
|
111
|
-
response = self._send_request(self.session.get, f"{self._fallback_root_url}{url_tail}")
|
|
112
|
-
else:
|
|
113
|
-
raise err
|
|
114
|
-
data = response.json()["data"]
|
|
115
|
-
if data:
|
|
116
|
-
chad = data[0]
|
|
117
|
-
self._validate_chip_design_record(chad, chip_label)
|
|
118
|
-
return chad
|
|
119
|
-
return None
|
|
120
|
-
|
|
121
|
-
def get_qubit_design_properties(self, chip_label: str) -> dict | None:
|
|
122
|
-
"""DEPRECATED. Use :meth:`get_chip_design_record` instead."""
|
|
123
|
-
self.logger.warning(
|
|
124
|
-
"QCMDataClient.get_qubit_design_properties is deprecated, use QCMDataClient.get_chip_design_record instead."
|
|
125
|
-
)
|
|
126
|
-
url_tail = f"/qubit-design-properties/?chip_label={chip_label}&limit=10&content_format=json"
|
|
127
|
-
try:
|
|
128
|
-
response = self._send_request(self.session.get, f"{self.root_url}{url_tail}")
|
|
129
|
-
except RequestError as err:
|
|
130
|
-
if self._fallback_root_url:
|
|
131
|
-
response = self._send_request(self.session.get, f"{self._fallback_root_url}{url_tail}")
|
|
132
|
-
else:
|
|
133
|
-
raise err
|
|
134
|
-
data = response.json()["data"]
|
|
135
|
-
if data:
|
|
136
|
-
qdp = data[0]
|
|
137
|
-
return qdp
|
|
138
|
-
return None
|
|
139
|
-
|
|
140
104
|
def _send_request(self, http_method: Callable[..., requests.Response], url: str) -> requests.Response:
|
|
141
105
|
# Send the request and return the response.
|
|
142
106
|
# Will raise an error if respectively an error response code is returned.
|
|
@@ -18,16 +18,14 @@ serializing and deserializing sweep arguments before saving them to database.
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
import json
|
|
21
|
-
from typing import Any,
|
|
21
|
+
from typing import Any, cast
|
|
22
22
|
|
|
23
|
-
from exa.common.api.model.parameter_model import ParameterModel
|
|
24
|
-
from exa.common.api.model.setting_node_model import SettingNodeModel
|
|
25
|
-
from exa.common.api.model.sweep_model import SweepModel
|
|
26
23
|
from exa.common.control.sweep.sweep import Sweep
|
|
27
|
-
from exa.common.data.parameter import Parameter
|
|
24
|
+
from exa.common.data.parameter import Parameter
|
|
28
25
|
from exa.common.data.setting_node import SettingNode
|
|
26
|
+
from exa.common.helpers import json_helper
|
|
29
27
|
from exa.common.helpers.json_helper import decode_json, get_json_encoder
|
|
30
|
-
from exa.common.sweep.util import NdSweep, Sweeps
|
|
28
|
+
from exa.common.sweep.util import NdSweep, Sweeps
|
|
31
29
|
|
|
32
30
|
|
|
33
31
|
def encode_nd_sweeps(sweeps: NdSweep, **kwargs) -> str:
|
|
@@ -45,22 +43,7 @@ def encode_nd_sweeps(sweeps: NdSweep, **kwargs) -> str:
|
|
|
45
43
|
return json.dumps(sweeps, **kwargs)
|
|
46
44
|
|
|
47
45
|
|
|
48
|
-
def
|
|
49
|
-
"""Encode sweeps to a JSON string.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
return_parameters: Return parameters as specified by :meth:`~.Backend.sweep`.
|
|
53
|
-
kwargs: keyword arguments passed to json.dumps
|
|
54
|
-
|
|
55
|
-
Returns:
|
|
56
|
-
json as a string
|
|
57
|
-
|
|
58
|
-
"""
|
|
59
|
-
kwargs.setdefault("cls", _SweepDataEncoder)
|
|
60
|
-
return json.dumps(return_parameters, **kwargs)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def encode_return_parameters(return_parameters: dict[Parameter, Optional[NdSweep]], **kwargs) -> str:
|
|
46
|
+
def encode_return_parameters(return_parameters: dict[Parameter, NdSweep | None], **kwargs) -> str:
|
|
64
47
|
"""Encode sweeps to a JSON string.
|
|
65
48
|
|
|
66
49
|
Args:
|
|
@@ -102,40 +85,7 @@ def decode_and_validate_sweeps(sweeps_json: str) -> Sweeps:
|
|
|
102
85
|
return cast(Sweeps, decoded)
|
|
103
86
|
|
|
104
87
|
|
|
105
|
-
def
|
|
106
|
-
"""Deserialize return parameters.
|
|
107
|
-
|
|
108
|
-
For backwards compatibility, changes values of the return parameters dict to a new,
|
|
109
|
-
more general format: NdSweeps, which is a list of tuples of Sweeps.
|
|
110
|
-
|
|
111
|
-
A key in the dict may be a Parameter or a Setting; both will be converted to a Parameter.
|
|
112
|
-
The values in the dict are converted with the following rules:
|
|
113
|
-
|
|
114
|
-
* 1 is converted to an empty NdSweep, i.e., a scalar.
|
|
115
|
-
* Other integers are converted to a :func:`.linear_index_sweep`
|
|
116
|
-
* `Sweep` is converted to an NdSweep that contains only the sweep
|
|
117
|
-
* NdSweep and None are not converted.
|
|
118
|
-
|
|
119
|
-
Args:
|
|
120
|
-
json_str: JSON representation of the ``return_parameters`` loaded
|
|
121
|
-
from e.g. persistence
|
|
122
|
-
|
|
123
|
-
Return:
|
|
124
|
-
a reconstituted, typed ``return_parameters`` structure
|
|
125
|
-
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
def decode_key(key: str) -> Parameter:
|
|
129
|
-
parameter_or_setting: dict[str, Any] = json.loads(key)
|
|
130
|
-
parameter_dict = parameter_or_setting.get("parameter", parameter_or_setting)
|
|
131
|
-
return ParameterModel(**parameter_dict).decode()
|
|
132
|
-
|
|
133
|
-
raw: dict[str, Any] = _loads(json_str)
|
|
134
|
-
decoded: dict[Setting, Union[NdSweep, Sweep, int]] = {decode_key(key): value for key, value in raw.items()}
|
|
135
|
-
return _legacy_return_parameters_to_new_format(decoded)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def decode_return_parameters(json_str: str) -> dict[Parameter, Optional[NdSweep]]:
|
|
88
|
+
def decode_return_parameters(json_str: str) -> dict[Parameter, NdSweep | None]:
|
|
139
89
|
"""Deserialize return parameters.
|
|
140
90
|
|
|
141
91
|
For backwards compatibility, changes values of the return parameters dict to a new,
|
|
@@ -164,8 +114,7 @@ def decode_return_parameters(json_str: str) -> dict[Parameter, Optional[NdSweep]
|
|
|
164
114
|
raise ValueError(f"Outer type is not list type when decoding: {json_str}")
|
|
165
115
|
|
|
166
116
|
return_parameters = {
|
|
167
|
-
|
|
168
|
-
for param_and_sweeps in decoded
|
|
117
|
+
Parameter(**param_and_sweeps["parameter"]): param_and_sweeps["hard_sweeps"] for param_and_sweeps in decoded
|
|
169
118
|
}
|
|
170
119
|
return return_parameters
|
|
171
120
|
|
|
@@ -181,37 +130,8 @@ def decode_settings(json_str: str) -> SettingNode:
|
|
|
181
130
|
deserialized settings
|
|
182
131
|
|
|
183
132
|
"""
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def _legacy_return_parameters_to_new_format(
|
|
188
|
-
old: dict[Union[Parameter, Setting], Union[NdSweep, Sweep, int, None]],
|
|
189
|
-
) -> dict[Parameter, NdSweep]:
|
|
190
|
-
"""For backwards compatibility, changes values of the return parameters dict to a new,
|
|
191
|
-
more general format: NdSweeps, which is a list of tuples of Sweeps.
|
|
192
|
-
|
|
193
|
-
Args:
|
|
194
|
-
old: return parameters in old format.
|
|
195
|
-
|
|
196
|
-
Returns:
|
|
197
|
-
`old` coerced to the new format.
|
|
198
|
-
|
|
199
|
-
"""
|
|
200
|
-
new = {}
|
|
201
|
-
for key, value in old.items():
|
|
202
|
-
parameter = key.parameter if isinstance(key, Setting) else key
|
|
203
|
-
|
|
204
|
-
# Previously, int meant "I don't know the numerical coordinates of this hard sweep, so
|
|
205
|
-
# just give me a dummy sweep with integers from 0 to <coord>"
|
|
206
|
-
if isinstance(value, int):
|
|
207
|
-
# Length 1 means scalar, so no hard sweeps at all
|
|
208
|
-
new_value = linear_index_sweep(parameter, value) if value != 1 else []
|
|
209
|
-
elif isinstance(value, Sweep):
|
|
210
|
-
new_value = [(value,)]
|
|
211
|
-
else:
|
|
212
|
-
new_value = value
|
|
213
|
-
new[parameter] = new_value
|
|
214
|
-
return new
|
|
133
|
+
json_loads = json.loads(json_str)
|
|
134
|
+
return SettingNode(**json_loads)
|
|
215
135
|
|
|
216
136
|
|
|
217
137
|
def _loads(*args, **kwargs) -> Any:
|
|
@@ -228,12 +148,22 @@ def _loads(*args, **kwargs) -> Any:
|
|
|
228
148
|
"""
|
|
229
149
|
|
|
230
150
|
def _decode_json(obj: Any) -> Any:
|
|
151
|
+
if isinstance(obj, dict) and "parameter" in obj:
|
|
152
|
+
# This is kind of a hack to provide backwards compatibility for old data.
|
|
153
|
+
# Pydantic doesn't allow extra attributes with the current setup,
|
|
154
|
+
# but old database data can have "parent_name" and "parent_label", which are not part of the model.
|
|
155
|
+
obj["parameter"].pop("parent_name", None)
|
|
156
|
+
obj["parameter"].pop("parent_label", None)
|
|
157
|
+
|
|
231
158
|
if isinstance(obj, dict) and {"parameter", "data"}.issubset(obj):
|
|
232
|
-
|
|
233
|
-
|
|
159
|
+
data = [json_helper.decode_json(d) for d in obj["data"]]
|
|
160
|
+
return Sweep(parameter=Parameter(**obj["parameter"]), data=data)
|
|
161
|
+
json_data_return = decode_json(obj)
|
|
162
|
+
return json_data_return
|
|
234
163
|
|
|
235
164
|
kwargs.setdefault("object_hook", _decode_json)
|
|
236
|
-
|
|
165
|
+
loads = json.loads(*args, **kwargs)
|
|
166
|
+
return loads
|
|
237
167
|
|
|
238
168
|
|
|
239
169
|
class _SweepDataEncoder(json.JSONEncoder):
|
|
@@ -250,12 +180,14 @@ class _SweepDataEncoder(json.JSONEncoder):
|
|
|
250
180
|
|
|
251
181
|
"""
|
|
252
182
|
if isinstance(obj, Sweep):
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
183
|
+
obj = obj.model_copy().model_dump()
|
|
184
|
+
elif isinstance(obj, Parameter):
|
|
185
|
+
obj = obj.model_copy().model_dump()
|
|
186
|
+
elif isinstance(obj, tuple):
|
|
187
|
+
obj = get_json_encoder()[tuple](obj)
|
|
188
|
+
else:
|
|
189
|
+
obj = super().default(obj)
|
|
190
|
+
return obj
|
|
259
191
|
|
|
260
192
|
# JSONEncoder doesn't call `default` method for tuple, because
|
|
261
193
|
# it knows how to serialize it. In order to use custom encoder
|
exa/common/sweep/util.py
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
user-friendly format to canonic ones.
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
from exa.common.control.sweep.linear_sweep import LinearSweep
|
|
20
19
|
from exa.common.control.sweep.option import StartStopOptions
|
|
21
20
|
from exa.common.control.sweep.sweep import Sweep
|
|
22
21
|
from exa.common.data.parameter import Parameter
|
|
@@ -75,7 +74,7 @@ def convert_sweeps_to_list_of_tuples(sweeps: Sweeps) -> NdSweep:
|
|
|
75
74
|
return new_list
|
|
76
75
|
|
|
77
76
|
|
|
78
|
-
def linear_index_sweep(parameter: Parameter, length: int) -> list[tuple[
|
|
77
|
+
def linear_index_sweep(parameter: Parameter, length: int) -> list[tuple[Sweep]]:
|
|
79
78
|
"""Produce an NdSweep over a dummy index.
|
|
80
79
|
|
|
81
80
|
Can be used in places where a "hardware sweep" is needed but not really meaningful.
|
|
@@ -91,9 +90,9 @@ def linear_index_sweep(parameter: Parameter, length: int) -> list[tuple[LinearSw
|
|
|
91
90
|
"""
|
|
92
91
|
return [
|
|
93
92
|
(
|
|
94
|
-
|
|
95
|
-
Parameter(name=parameter.name + "_index", label=parameter.label + " index"),
|
|
96
|
-
StartStopOptions(0, length - 1, count=length),
|
|
93
|
+
Sweep(
|
|
94
|
+
parameter=Parameter(name=parameter.name + "_index", label=parameter.label + " index"),
|
|
95
|
+
data=StartStopOptions(0, length - 1, count=length).data,
|
|
97
96
|
),
|
|
98
97
|
)
|
|
99
98
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: iqm-exa-common
|
|
3
|
-
Version:
|
|
3
|
+
Version: 26.0
|
|
4
4
|
Summary: Framework for control and measurement of superconducting qubits: common library
|
|
5
5
|
Author-email: IQM Finland Oy <info@meetiqm.com>
|
|
6
6
|
License: Apache License
|
|
@@ -215,7 +215,7 @@ Description-Content-Type: text/x-rst
|
|
|
215
215
|
License-File: LICENSE.txt
|
|
216
216
|
Requires-Dist: iqm-data-definitions <3.0,>=2.6
|
|
217
217
|
Requires-Dist: numpy <3.0,>=1.25.2
|
|
218
|
-
Requires-Dist: pydantic <3.0,>=2.
|
|
218
|
+
Requires-Dist: pydantic <3.0,>=2.10.4
|
|
219
219
|
Requires-Dist: python-dotenv ==0.21.1
|
|
220
220
|
Requires-Dist: xarray ==2024.10.0
|
|
221
221
|
Requires-Dist: requests ==2.32.3
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
exa/common/__init__.py,sha256=AqxwNht57urLqMxFblY7SgxjzkQYW7pRRUUPS1j1Gck,962
|
|
2
|
+
exa/common/api/__init__.py,sha256=PAYujWNG8CtSQX_8U9gnubDh5cwGRhtOfLr0r0xGx3M,587
|
|
3
|
+
exa/common/api/proto_serialization/__init__.py,sha256=Vn2C79OqovFu5wJus9EHsMtK3Of1JLJDcpg2QZKmXsY,1565
|
|
4
|
+
exa/common/api/proto_serialization/_parameter.py,sha256=W0EBrVs3uZ-jX1HnlOrAB7_PKrzi8D_qNM0_SBht8XY,2895
|
|
5
|
+
exa/common/api/proto_serialization/array.py,sha256=iht5WlfGEkzXM3PcOFDXxd2TgXWFyYNyPt44CMqXnl8,3110
|
|
6
|
+
exa/common/api/proto_serialization/datum.py,sha256=gFIAncWpOhy4V56DKYdKoasEIpGu1yUerSuRQf65unY,4828
|
|
7
|
+
exa/common/api/proto_serialization/nd_sweep.py,sha256=B36KY32wTNFJE3Bk2QlN4BeMO0Se7AtwOectlIh5SCM,2661
|
|
8
|
+
exa/common/api/proto_serialization/sequence.py,sha256=2_84ILndHsc9c9KPKvckySIPJHByoH2fXIF-fTjrzvI,2377
|
|
9
|
+
exa/common/api/proto_serialization/setting_node.py,sha256=Ac-WuYKUTiVywRmRJHhdpfoswQWRQEIEgIWsn7IsA_g,3700
|
|
10
|
+
exa/common/control/__init__.py,sha256=00T_xV0lL20QZcEt__vWq81N1oGF0KpJMhTitfAI4VI,629
|
|
11
|
+
exa/common/control/sweep/__init__.py,sha256=GzKoQdQsLutcHhmrLPyPrW1n7Cpg766p3OWDHlRpuTs,607
|
|
12
|
+
exa/common/control/sweep/exponential_sweep.py,sha256=NjO0wiovyV4HTbS3hs0tptmPGSsQLNS9h-mYFQJzX8c,2039
|
|
13
|
+
exa/common/control/sweep/fixed_sweep.py,sha256=hfIv9-f-7WsIavs7JxwcBs-lSZTxV0D51Xv6gH3sywM,1411
|
|
14
|
+
exa/common/control/sweep/linear_sweep.py,sha256=Xn7gjysIQ2Xu_l15yBsqlUvnK5AJdN7KRaa3sNSjzd8,1884
|
|
15
|
+
exa/common/control/sweep/sweep.py,sha256=0ZzhbzcAQaByiX7dz5jDH0r1zmyEiW2B-v8edIyTDs8,3442
|
|
16
|
+
exa/common/control/sweep/sweep_values.py,sha256=pGvds0ZVgMIL6ztE6hGXEzC9E7rvAzeVwvHCG5v6XC8,2298
|
|
17
|
+
exa/common/control/sweep/option/__init__.py,sha256=Z01JS0FpchMvR8zfWBCs3jcPjh2H8X29YkN0SFxWLTY,906
|
|
18
|
+
exa/common/control/sweep/option/center_span_base_options.py,sha256=8F2HqHeq9RSMqEms6icuUqLlzkL6jT991eeRrKgikM8,2520
|
|
19
|
+
exa/common/control/sweep/option/center_span_options.py,sha256=3YI6DpBAXhon_DMRGzf-YugodSP1KX01MBtiqTvCJDM,2475
|
|
20
|
+
exa/common/control/sweep/option/constants.py,sha256=vdYrbi3EBlIST5mO498W2F6s-xqyoEVBAZ6zQEiHmmY,1417
|
|
21
|
+
exa/common/control/sweep/option/fixed_options.py,sha256=SyGSiEanD3xz4Wcy1rd9r2wj7hNaG3lQYH67WQDc72I,1047
|
|
22
|
+
exa/common/control/sweep/option/option_converter.py,sha256=RGNNQHNl8gTZUHO85IZseoo065s_kQSWP-AfrYhGbn4,3649
|
|
23
|
+
exa/common/control/sweep/option/start_stop_base_options.py,sha256=hZCIflh8NzK48cLLMer9U2yDRTM6mCEEuuF2zAZ2CYU,2361
|
|
24
|
+
exa/common/control/sweep/option/start_stop_options.py,sha256=HaQFNlhRPep2yrn_LuOkuZ0imwBEidpD0BTPWf-Svf4,3038
|
|
25
|
+
exa/common/control/sweep/option/sweep_options.py,sha256=BhKB7RHP0VXJ9iUQKVzeQOM4j_x9AsMhRJgoR3gkiaY,933
|
|
26
|
+
exa/common/data/__init__.py,sha256=F5SRe5QHBTjef4XJVQ63kO5Oxc_AiZnPbV560i7La0Y,644
|
|
27
|
+
exa/common/data/base_model.py,sha256=WWbQk5-9coz3qj4IzkEKvIkdcwHISi7XPb14K7ZTPHg,1936
|
|
28
|
+
exa/common/data/parameter.py,sha256=uUMyVt4wfKkwehd9CHbBPOGQ_PSnlI06pNMPjBySDGs,22999
|
|
29
|
+
exa/common/data/setting_node.py,sha256=WldawevXuOb2uN-bjZ04hWijKqX_MOdkyMsgxOZbrek,43190
|
|
30
|
+
exa/common/data/settingnode_v2.html.jinja2,sha256=mo-rlLLmU-Xxf6znJAisispAZK8sbV-2C13byKAtj_Q,3166
|
|
31
|
+
exa/common/data/value.py,sha256=etAYhIKyRmy30uzSd7MpzUhGhvCXcfhMq-PPteG8EcY,1893
|
|
32
|
+
exa/common/errors/__init__.py,sha256=ArMBdpmx1EUenBpzrSNG63kmUf7PM0gCqSYnaCnL9Qk,597
|
|
33
|
+
exa/common/errors/exa_error.py,sha256=eB_c-Qp1OchcBMr3LSyYitity4SochQh-nAghZmGLJU,975
|
|
34
|
+
exa/common/helpers/__init__.py,sha256=IgtVD3tojIFA4MTV2mT5uYM6jb2qny9kBIIhEZT2PuI,610
|
|
35
|
+
exa/common/helpers/data_helper.py,sha256=vhzJ63g1S2JqnCj0WJJuqWcuiIwKATnQeHdWw_3gkZg,1934
|
|
36
|
+
exa/common/helpers/json_helper.py,sha256=VTcYU8FRgv3tXPifuogUWmVAzt_4JoQ_laTHolyodtA,2672
|
|
37
|
+
exa/common/helpers/numpy_helper.py,sha256=KKKyZ_fD0O1gn7_InEQROYnX3WGMA6C1qHh8KzzjtUI,1062
|
|
38
|
+
exa/common/helpers/software_version_helper.py,sha256=kpuQer4p1p4cj9_CzwziBSCX7wuH-FvfNw8G8U-EI3Y,5162
|
|
39
|
+
exa/common/logger/__init__.py,sha256=1bIsGxHzfujXlkgtcAnWToKMkw3dpU5PEd_7LE_NpgQ,686
|
|
40
|
+
exa/common/logger/logger.py,sha256=qQOl2MEBlw69zKEKNWSPOFi7DSLOfsMXNmJYapqzlXY,5684
|
|
41
|
+
exa/common/qcm_data/__init__.py,sha256=VtsYkGoaniSjCkY0oQlqkcYJCtmC2sTDxfrIe_kpqZg,567
|
|
42
|
+
exa/common/qcm_data/chad_model.py,sha256=MQ1xuRODOA6uzb3GJ4fgYx9cXS8z1DeRGw6HYKA9Sio,11223
|
|
43
|
+
exa/common/qcm_data/chip_topology.py,sha256=OJU8-CXV7wfdxrn0HqryNZmxGRoffrg0vi0aMaiYbbY,19328
|
|
44
|
+
exa/common/qcm_data/file_adapter.py,sha256=U1XZm_PEswkW7kAztbWFLMufz4mPKPupWbh5yJXdZCI,2263
|
|
45
|
+
exa/common/qcm_data/immutable_base_model.py,sha256=QXmKIWQbsbWQvovXwKT1d9jtyf2LNJtjQquIwO52zOU,901
|
|
46
|
+
exa/common/qcm_data/qcm_data_client.py,sha256=tuNEosERVjtGYEq7RWaBzxST7OvRAWxN-VRYmhJLLN8,6094
|
|
47
|
+
exa/common/sweep/__init__.py,sha256=uEKk5AtzSgSnf8Y0geRPwUpqXIBIXpeCxsN64sX7F1o,591
|
|
48
|
+
exa/common/sweep/database_serialization.py,sha256=NUu1umxRQZpKtRmw1vNDsSbnofqbHvKFg_xQ2mdhY6k,7469
|
|
49
|
+
exa/common/sweep/util.py,sha256=-QE2AaH-WDkYAVH5-Z-30leLgY0x4efmby4kc1JTCgY,3732
|
|
50
|
+
iqm_exa_common-26.0.dist-info/LICENSE.txt,sha256=R6Q7eUrLyoCQgWYorQ8WJmVmWKYU3dxA3jYUp0wwQAw,11332
|
|
51
|
+
iqm_exa_common-26.0.dist-info/METADATA,sha256=hTPYpxCf4hTDYEFgVqF1x_RulSvJVd9pX7ETCUVB_38,14548
|
|
52
|
+
iqm_exa_common-26.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
|
|
53
|
+
iqm_exa_common-26.0.dist-info/top_level.txt,sha256=Clphg2toaZ3_jSFRPhjMNEmLurkMNMc4lkK2EFYsSlM,4
|
|
54
|
+
iqm_exa_common-26.0.dist-info/RECORD,,
|
exa/common/api/model/__init__.py
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Copyright 2024 IQM
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
"""Pydantic models for serializing control and data structures."""
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Copyright 2024 IQM
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
"""Pydantic model for Parameter."""
|
|
16
|
-
|
|
17
|
-
from __future__ import annotations
|
|
18
|
-
|
|
19
|
-
from typing import Optional, Tuple
|
|
20
|
-
|
|
21
|
-
from pydantic import BaseModel, ConfigDict
|
|
22
|
-
|
|
23
|
-
from exa.common.data.parameter import CollectionType, DataType, Parameter
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class ParameterModel(BaseModel):
|
|
27
|
-
"""Pydantic parameter model""" # noqa: D200
|
|
28
|
-
|
|
29
|
-
name: str
|
|
30
|
-
parent_name: Optional[str] = None
|
|
31
|
-
label: Optional[str] = None
|
|
32
|
-
parent_label: Optional[str] = None
|
|
33
|
-
unit: Optional[str] = None
|
|
34
|
-
data_type: Optional[int | Tuple[int, ...]] = None
|
|
35
|
-
collection_type: Optional[int] = None
|
|
36
|
-
element_indices: Optional[int | list[int]] = None
|
|
37
|
-
|
|
38
|
-
def __hash__(self):
|
|
39
|
-
return hash(
|
|
40
|
-
(
|
|
41
|
-
self.name,
|
|
42
|
-
self.parent_name,
|
|
43
|
-
self.label,
|
|
44
|
-
self.unit,
|
|
45
|
-
self.data_type,
|
|
46
|
-
self.collection_type,
|
|
47
|
-
self.element_indices,
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
def __eq__(self, other: ParameterModel):
|
|
52
|
-
return isinstance(other, ParameterModel) and (
|
|
53
|
-
(
|
|
54
|
-
self.name,
|
|
55
|
-
self.parent_name,
|
|
56
|
-
self.label,
|
|
57
|
-
self.parent_label,
|
|
58
|
-
self.unit,
|
|
59
|
-
self.data_type,
|
|
60
|
-
self.collection_type,
|
|
61
|
-
self.element_indices,
|
|
62
|
-
)
|
|
63
|
-
== (
|
|
64
|
-
other.name,
|
|
65
|
-
other.parent_name,
|
|
66
|
-
other.label,
|
|
67
|
-
other.parent_label,
|
|
68
|
-
other.unit,
|
|
69
|
-
other.data_type,
|
|
70
|
-
other.collection_type,
|
|
71
|
-
other.element_indices,
|
|
72
|
-
)
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
model_config = ConfigDict(extra="allow")
|
|
76
|
-
|
|
77
|
-
def decode(self) -> Parameter:
|
|
78
|
-
"""Creates instance of :class:`exa.common.data.parameter.Parameter` out of pydantic `ParameterModel`"""
|
|
79
|
-
if self.data_type is not None:
|
|
80
|
-
if isinstance(self.data_type, tuple):
|
|
81
|
-
dt = tuple(DataType(dt) for dt in self.data_type)
|
|
82
|
-
else:
|
|
83
|
-
dt = DataType(self.data_type)
|
|
84
|
-
else:
|
|
85
|
-
dt = None
|
|
86
|
-
ct = CollectionType(self.collection_type) if self.collection_type is not None else None
|
|
87
|
-
if self.element_indices is None:
|
|
88
|
-
return Parameter(self.name, self.label, self.unit, dt, ct)
|
|
89
|
-
return Parameter(self.parent_name, self.parent_label, self.unit, dt, ct, self.element_indices)
|
|
90
|
-
|
|
91
|
-
@classmethod
|
|
92
|
-
def encode(cls, parameter: Parameter) -> ParameterModel:
|
|
93
|
-
"""Creates pydantic `ParameterModel` out of :class:`exa.common.data.parameter.Parameter` instance"""
|
|
94
|
-
if parameter.data_type is not None:
|
|
95
|
-
if isinstance(parameter.data_type, tuple):
|
|
96
|
-
dt = tuple(dt.value for dt in parameter.data_type)
|
|
97
|
-
else:
|
|
98
|
-
dt = parameter.data_type.value
|
|
99
|
-
else:
|
|
100
|
-
dt = None
|
|
101
|
-
ct = parameter.collection_type.value if parameter.collection_type is not None else None
|
|
102
|
-
return ParameterModel(
|
|
103
|
-
name=parameter.name,
|
|
104
|
-
parent_name=parameter.parent_name,
|
|
105
|
-
label=parameter.label,
|
|
106
|
-
parent_label=parameter.parent_label,
|
|
107
|
-
unit=parameter.unit,
|
|
108
|
-
data_type=dt,
|
|
109
|
-
collection_type=ct,
|
|
110
|
-
element_indices=parameter.element_indices,
|
|
111
|
-
)
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Copyright 2024 IQM
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
"""Pydantic model for Setting."""
|
|
16
|
-
|
|
17
|
-
from __future__ import annotations
|
|
18
|
-
|
|
19
|
-
from typing import Any
|
|
20
|
-
|
|
21
|
-
from pydantic import BaseModel, ConfigDict, field_serializer
|
|
22
|
-
|
|
23
|
-
from exa.common.api.model.parameter_model import ParameterModel
|
|
24
|
-
from exa.common.data.parameter import Setting
|
|
25
|
-
from exa.common.helpers import json_helper
|
|
26
|
-
from exa.common.helpers.numpy_helper import coerce_numpy_type_to_native
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class SettingModel(BaseModel):
|
|
30
|
-
"""Pydantic setting model""" # noqa: D200
|
|
31
|
-
|
|
32
|
-
parameter: ParameterModel
|
|
33
|
-
value: Any = None
|
|
34
|
-
|
|
35
|
-
def __hash__(self):
|
|
36
|
-
return hash((self.parameter, self.value))
|
|
37
|
-
|
|
38
|
-
def __eq__(self, other: SettingModel):
|
|
39
|
-
return isinstance(other, SettingModel) and ((self.parameter, self.value) == (other.parameter, other.value))
|
|
40
|
-
|
|
41
|
-
model_config = ConfigDict(extra="allow")
|
|
42
|
-
|
|
43
|
-
@field_serializer("value")
|
|
44
|
-
def serialize_value(self, value: Any, _info):
|
|
45
|
-
encoder = json_helper.get_json_encoder().get(type(value))
|
|
46
|
-
if encoder:
|
|
47
|
-
return encoder(value)
|
|
48
|
-
return value
|
|
49
|
-
|
|
50
|
-
def decode(self) -> Setting:
|
|
51
|
-
"""Creates instance of :class:`exa.common.data.parameter.Setting` out of pydantic `SettingModel`"""
|
|
52
|
-
value = json_helper.decode_json(self.value)
|
|
53
|
-
parameter = ParameterModel.decode(self.parameter)
|
|
54
|
-
return Setting(parameter=parameter, value=value)
|
|
55
|
-
|
|
56
|
-
@classmethod
|
|
57
|
-
def encode(cls, setting: Setting) -> SettingModel:
|
|
58
|
-
"""Creates pydantic :class:`SettingModel` out of :class:`~exa.common.data.parameter.Setting` instance.
|
|
59
|
-
|
|
60
|
-
Converts numpy scalars to native types to avoid problems with JSON encoding.
|
|
61
|
-
"""
|
|
62
|
-
value = coerce_numpy_type_to_native(setting.value)
|
|
63
|
-
return SettingModel(parameter=ParameterModel.encode(setting.parameter), value=value)
|