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.
@@ -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 `value`."""
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)
@@ -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(self, dct: dict[str, Any], strict: bool = False) -> None:
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 `strict` is True.
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 `dct` contains a setting that is not found in `self`.
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 `strict` happens.
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-exa-common
3
- Version: 26.26.0
3
+ Version: 26.27.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
@@ -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=GI-BJcXe53Mf2PXPzvftCV7L2kd_uC-hOjH_J__1MLc,23313
29
- exa/common/data/setting_node.py,sha256=obA9Rl3uSQ-pOoAAMzSQvzbRPygSIaFOM4W4foxKKKk,43344
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.26.0.dist-info/LICENSE.txt,sha256=R6Q7eUrLyoCQgWYorQ8WJmVmWKYU3dxA3jYUp0wwQAw,11332
54
- iqm_exa_common-26.26.0.dist-info/METADATA,sha256=iD1DF1AEmak_PpscF39c2mw4n3OH3lAvZe4lZ74m1ww,14659
55
- iqm_exa_common-26.26.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
56
- iqm_exa_common-26.26.0.dist-info/top_level.txt,sha256=Clphg2toaZ3_jSFRPhjMNEmLurkMNMc4lkK2EFYsSlM,4
57
- iqm_exa_common-26.26.0.dist-info/RECORD,,
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,,