iqm-exa-common 26.26.0__py3-none-any.whl → 26.27.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/data/parameter.py +30 -6
- exa/common/data/setting_node.py +33 -8
- {iqm_exa_common-26.26.0.dist-info → iqm_exa_common-26.27.0.dist-info}/METADATA +1 -1
- {iqm_exa_common-26.26.0.dist-info → iqm_exa_common-26.27.0.dist-info}/RECORD +7 -7
- {iqm_exa_common-26.26.0.dist-info → iqm_exa_common-26.27.0.dist-info}/LICENSE.txt +0 -0
- {iqm_exa_common-26.26.0.dist-info → iqm_exa_common-26.27.0.dist-info}/WHEEL +0 -0
- {iqm_exa_common-26.26.0.dist-info → iqm_exa_common-26.27.0.dist-info}/top_level.txt +0 -0
exa/common/data/parameter.py
CHANGED
|
@@ -61,7 +61,7 @@ import ast
|
|
|
61
61
|
from collections.abc import Hashable
|
|
62
62
|
import copy
|
|
63
63
|
from enum import IntEnum
|
|
64
|
-
from typing import Any, Self
|
|
64
|
+
from typing import Any, Self, TypeAlias
|
|
65
65
|
import warnings
|
|
66
66
|
|
|
67
67
|
import numpy as np
|
|
@@ -73,7 +73,9 @@ from exa.common.data.base_model import BaseModel
|
|
|
73
73
|
from exa.common.data.value import ObservationValue
|
|
74
74
|
from exa.common.errors.station_control_errors import ValidationError
|
|
75
75
|
|
|
76
|
-
CastType = str | list["CastType"] | None
|
|
76
|
+
CastType: TypeAlias = str | list["CastType"] | None
|
|
77
|
+
SourceType: TypeAlias = None | BaseModel | dict[str, Any]
|
|
78
|
+
"""Type for Setting sources."""
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
class DataType(IntEnum):
|
|
@@ -135,7 +137,7 @@ class DataType(IntEnum):
|
|
|
135
137
|
raise TypeError("Boolean data types can only be 'false', 'true, '0' or '1' (case-insensitive)")
|
|
136
138
|
elif self is DataType.STRING:
|
|
137
139
|
return value
|
|
138
|
-
else:
|
|
140
|
+
else: # TODO: can this be removed?
|
|
139
141
|
try:
|
|
140
142
|
return ast.literal_eval(value)
|
|
141
143
|
except (SyntaxError, ValueError): # if the value can not be evaluated, return the original value
|
|
@@ -407,12 +409,19 @@ class Setting(BaseModel):
|
|
|
407
409
|
path: str = ""
|
|
408
410
|
"""Path in the settings tree (starting from the root ``SettingNode``) for this setting."""
|
|
409
411
|
|
|
412
|
+
_source: SourceType = None
|
|
413
|
+
"""The source for this Setting value. May contain an observation (ObservationDefinition or ObservationData)
|
|
414
|
+
or a source-dict (e.g. ``{"type": "configuration_source", "configurator": "defaults_from_yml"}``). By default,
|
|
415
|
+
``None``, which denotes the source not being specified (e.g. hardcoded defaults). The source is stored in a private
|
|
416
|
+
attribute and thus is never serialized (the source field can contain non-serializable data such as Callables)."""
|
|
417
|
+
|
|
410
418
|
def __init__(
|
|
411
419
|
self,
|
|
412
420
|
parameter: Parameter | None = None,
|
|
413
421
|
value: ObservationValue | None = None,
|
|
414
422
|
read_only: bool = False,
|
|
415
423
|
path: str = "",
|
|
424
|
+
source: SourceType = None,
|
|
416
425
|
**kwargs,
|
|
417
426
|
) -> None:
|
|
418
427
|
super().__init__(
|
|
@@ -422,6 +431,7 @@ class Setting(BaseModel):
|
|
|
422
431
|
path=path,
|
|
423
432
|
**kwargs,
|
|
424
433
|
)
|
|
434
|
+
self._source = source
|
|
425
435
|
|
|
426
436
|
@model_validator(mode="after")
|
|
427
437
|
def validate_parameter_value_after(self) -> Self:
|
|
@@ -435,8 +445,17 @@ class Setting(BaseModel):
|
|
|
435
445
|
raise ValidationError(f"Invalid value '{self.value}' for parameter '{self.parameter}'.")
|
|
436
446
|
return self
|
|
437
447
|
|
|
438
|
-
def update(self, value: ObservationValue) -> Setting:
|
|
439
|
-
"""Create a new setting object with updated
|
|
448
|
+
def update(self, value: ObservationValue, source: SourceType = None) -> Setting:
|
|
449
|
+
"""Create a new setting object with updated value and source.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
value: New value for the setting.
|
|
453
|
+
source: New source for the setting.
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
Copy of ``self`` with modified properties.
|
|
457
|
+
|
|
458
|
+
"""
|
|
440
459
|
if self.read_only:
|
|
441
460
|
raise ValueError(
|
|
442
461
|
f"Can't update the value of {self.parameter.name} to {value} since the setting is read-only."
|
|
@@ -445,7 +464,7 @@ class Setting(BaseModel):
|
|
|
445
464
|
value = np.array(value)
|
|
446
465
|
# Need to create a new Setting here instead of using Pydantic model_copy().
|
|
447
466
|
# model_copy() can't handle backdoor settings without errors, i.e. values with a list of 2 elements.
|
|
448
|
-
return Setting(self.parameter, value, self.read_only, self.path)
|
|
467
|
+
return Setting(self.parameter, value, self.read_only, self.path, source=source)
|
|
449
468
|
|
|
450
469
|
@property
|
|
451
470
|
def name(self):
|
|
@@ -477,6 +496,11 @@ class Setting(BaseModel):
|
|
|
477
496
|
"""Element-wise indices of the parameter in ``self``."""
|
|
478
497
|
return self.parameter.element_indices
|
|
479
498
|
|
|
499
|
+
@property
|
|
500
|
+
def source(self) -> SourceType:
|
|
501
|
+
"""Return the source for this Setting's value."""
|
|
502
|
+
return self._source
|
|
503
|
+
|
|
480
504
|
@staticmethod
|
|
481
505
|
def get_by_name(name: str, values: set[Setting]) -> Setting | None:
|
|
482
506
|
return next((setting for setting in values if setting.parameter.name == name), None)
|
exa/common/data/setting_node.py
CHANGED
|
@@ -217,7 +217,7 @@ import jinja2
|
|
|
217
217
|
import numpy as np
|
|
218
218
|
|
|
219
219
|
from exa.common.data.base_model import BaseModel
|
|
220
|
-
from exa.common.data.parameter import CollectionType, Parameter, Setting
|
|
220
|
+
from exa.common.data.parameter import CollectionType, Parameter, Setting, SourceType
|
|
221
221
|
from exa.common.errors.exa_error import UnknownSettingError
|
|
222
222
|
from exa.common.qcm_data.chip_topology import sort_components
|
|
223
223
|
|
|
@@ -603,7 +603,7 @@ class SettingNode(BaseModel):
|
|
|
603
603
|
"""
|
|
604
604
|
for key, item in other.settings.items():
|
|
605
605
|
if key in self.settings and (prioritize_other or (self[key].value is None)):
|
|
606
|
-
self.settings[key] = Setting(self.settings[key].parameter, item.value)
|
|
606
|
+
self.settings[key] = Setting(self.settings[key].parameter, item.value, source=self.settings[key].source)
|
|
607
607
|
for key, item in other.subtrees.items():
|
|
608
608
|
if key in self.subtrees:
|
|
609
609
|
self.subtrees[key].merge_values(copy(item), prioritize_other)
|
|
@@ -678,16 +678,22 @@ class SettingNode(BaseModel):
|
|
|
678
678
|
new.subtrees[key] = cls.transform_node_types(subnode)
|
|
679
679
|
return new
|
|
680
680
|
|
|
681
|
-
def set_from_dict(
|
|
681
|
+
def set_from_dict(
|
|
682
|
+
self,
|
|
683
|
+
dct: dict[str, Any],
|
|
684
|
+
strict: bool = False,
|
|
685
|
+
source: SourceType = None,
|
|
686
|
+
) -> None:
|
|
682
687
|
"""Recursively set values to Settings, taking values from a dictionary that has similar tree structure.
|
|
683
|
-
Keys that are not found in self are ignored, unless
|
|
688
|
+
Keys that are not found in self are ignored, unless ``strict`` is True.
|
|
684
689
|
|
|
685
690
|
Args:
|
|
686
691
|
dct: Dictionary containing the new values to use.
|
|
687
|
-
strict: If True, will raise error if
|
|
692
|
+
strict: If True, will raise error if ``dct`` contains a setting that is not found in ``self``.
|
|
693
|
+
source: Source for the settings (this same source is applied to all settings from the dict).
|
|
688
694
|
|
|
689
695
|
Raises:
|
|
690
|
-
UnknownSettingError: If the condition of
|
|
696
|
+
UnknownSettingError: If the condition of ``strict`` happens.
|
|
691
697
|
|
|
692
698
|
"""
|
|
693
699
|
for key, value in dct.items():
|
|
@@ -704,9 +710,10 @@ class SettingNode(BaseModel):
|
|
|
704
710
|
self.settings[key].name,
|
|
705
711
|
self.settings[key].parameter.data_type.cast(value),
|
|
706
712
|
path=self.settings[key].path,
|
|
713
|
+
source=source,
|
|
707
714
|
)
|
|
708
715
|
else:
|
|
709
|
-
self.settings[key] = self.settings[key].update(value)
|
|
716
|
+
self.settings[key] = self.settings[key].update(value, source=source)
|
|
710
717
|
|
|
711
718
|
def setting_with_path_name(self, setting: Setting) -> Setting | None:
|
|
712
719
|
"""Get a copy of a setting with its name replaced with the path name."""
|
|
@@ -858,6 +865,7 @@ class SettingNode(BaseModel):
|
|
|
858
865
|
nodes: Iterable[Setting | Parameter | SettingNode] | dict[str, Setting | Parameter | SettingNode],
|
|
859
866
|
path: str,
|
|
860
867
|
override_values: dict[str, Any] | None = None,
|
|
868
|
+
override_source: SourceType = None,
|
|
861
869
|
) -> None:
|
|
862
870
|
"""Add nodes to ``self`` while creating the missing nodes in-between.
|
|
863
871
|
|
|
@@ -873,6 +881,8 @@ class SettingNode(BaseModel):
|
|
|
873
881
|
found in self, the associated nodes will be created automatically.
|
|
874
882
|
override_values: Optionally override the values for the `Settings` corresponding to ``nodes``. This dict
|
|
875
883
|
should have the same structure as ``nodes``, including matching names.
|
|
884
|
+
override_source: Optionally override the source for the ``Settings`` corresponding to ``nodes``. All the
|
|
885
|
+
settings will then have this same source.
|
|
876
886
|
|
|
877
887
|
"""
|
|
878
888
|
override_values = override_values or {}
|
|
@@ -899,9 +909,10 @@ class SettingNode(BaseModel):
|
|
|
899
909
|
latest_node[key] = node
|
|
900
910
|
else:
|
|
901
911
|
default_value = node.value if isinstance(node, Setting) else None
|
|
912
|
+
source = override_source or (node.source if isinstance(node, Setting) else None)
|
|
902
913
|
parameter = node.parameter if isinstance(node, Setting) else node
|
|
903
914
|
value = override_values.get(node.name) if override_values.get(node.name) is not None else default_value
|
|
904
|
-
latest_node[key] = Setting(parameter, value)
|
|
915
|
+
latest_node[key] = Setting(parameter, value, source=source)
|
|
905
916
|
|
|
906
917
|
def get_default_implementation_name(self, gate: str, locus: str | Iterable[str]) -> str:
|
|
907
918
|
"""Get the default implementation name for a given gate and locus.
|
|
@@ -1034,6 +1045,20 @@ class SettingNode(BaseModel):
|
|
|
1034
1045
|
|
|
1035
1046
|
raise ValueError(f"Locus {locus} cannot be found in the gate properties characterization settings.")
|
|
1036
1047
|
|
|
1048
|
+
def set_source(self, source: SourceType, ignore_nones: bool = True) -> None:
|
|
1049
|
+
"""Set source recursively to all Settings in ``self``.
|
|
1050
|
+
|
|
1051
|
+
Args:
|
|
1052
|
+
source: The source to set.
|
|
1053
|
+
ignore_nones: If ``True``, the source will not be set for Settings with ``None`` value.
|
|
1054
|
+
|
|
1055
|
+
"""
|
|
1056
|
+
for setting in self.settings.values():
|
|
1057
|
+
if not ignore_nones or setting.value is not None:
|
|
1058
|
+
setting._source = source
|
|
1059
|
+
for subtree in self.subtrees.values():
|
|
1060
|
+
subtree.set_source(source, ignore_nones=ignore_nones)
|
|
1061
|
+
|
|
1037
1062
|
def _get_symmetric_loci(self, gate: str, implementation: str, locus: str | Iterable[str]) -> list[str]:
|
|
1038
1063
|
if not isinstance(locus, str):
|
|
1039
1064
|
if self.gate_definitions[gate][implementation].symmetric.value:
|
|
@@ -25,8 +25,8 @@ exa/common/control/sweep/option/start_stop_options.py,sha256=NAOCJgXvL5J4zewNvsI
|
|
|
25
25
|
exa/common/control/sweep/option/sweep_options.py,sha256=BhKB7RHP0VXJ9iUQKVzeQOM4j_x9AsMhRJgoR3gkiaY,933
|
|
26
26
|
exa/common/data/__init__.py,sha256=F5SRe5QHBTjef4XJVQ63kO5Oxc_AiZnPbV560i7La0Y,644
|
|
27
27
|
exa/common/data/base_model.py,sha256=U508YCtvZabBGnKe1fqUimunmwGulOTM6DOKTUqnYp8,1835
|
|
28
|
-
exa/common/data/parameter.py,sha256=
|
|
29
|
-
exa/common/data/setting_node.py,sha256=
|
|
28
|
+
exa/common/data/parameter.py,sha256=93s9WH_GEBGLFy9LGWybtk3NEWLhTMdbf8Bf2Du7ruo,24397
|
|
29
|
+
exa/common/data/setting_node.py,sha256=oDU9mNOl5uo7C-Nnhq4fQYvlqMRLvj2u5JoZwU46nUk,44525
|
|
30
30
|
exa/common/data/settingnode_v2.html.jinja2,sha256=mo-rlLLmU-Xxf6znJAisispAZK8sbV-2C13byKAtj_Q,3166
|
|
31
31
|
exa/common/data/value.py,sha256=mtMws5UPGx1pCADK6Q2Tx4BwCXznvVRSNQRfcQ3NMmY,1853
|
|
32
32
|
exa/common/errors/__init__.py,sha256=ArMBdpmx1EUenBpzrSNG63kmUf7PM0gCqSYnaCnL9Qk,597
|
|
@@ -50,8 +50,8 @@ exa/common/qcm_data/qcm_data_client.py,sha256=Rze6yQd0xUeH6eUMv3wduYbiDCTP3C4WEC
|
|
|
50
50
|
exa/common/sweep/__init__.py,sha256=uEKk5AtzSgSnf8Y0geRPwUpqXIBIXpeCxsN64sX7F1o,591
|
|
51
51
|
exa/common/sweep/database_serialization.py,sha256=NUu1umxRQZpKtRmw1vNDsSbnofqbHvKFg_xQ2mdhY6k,7469
|
|
52
52
|
exa/common/sweep/util.py,sha256=-QE2AaH-WDkYAVH5-Z-30leLgY0x4efmby4kc1JTCgY,3732
|
|
53
|
-
iqm_exa_common-26.
|
|
54
|
-
iqm_exa_common-26.
|
|
55
|
-
iqm_exa_common-26.
|
|
56
|
-
iqm_exa_common-26.
|
|
57
|
-
iqm_exa_common-26.
|
|
53
|
+
iqm_exa_common-26.27.0.dist-info/LICENSE.txt,sha256=R6Q7eUrLyoCQgWYorQ8WJmVmWKYU3dxA3jYUp0wwQAw,11332
|
|
54
|
+
iqm_exa_common-26.27.0.dist-info/METADATA,sha256=I1IQC6jmYF7G7-OwfsDvzak23LoG1YIMnl2WIzCkhG4,14659
|
|
55
|
+
iqm_exa_common-26.27.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
|
|
56
|
+
iqm_exa_common-26.27.0.dist-info/top_level.txt,sha256=Clphg2toaZ3_jSFRPhjMNEmLurkMNMc4lkK2EFYsSlM,4
|
|
57
|
+
iqm_exa_common-26.27.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|