iqm-station-control-client 9.8.0__py3-none-any.whl → 9.10.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.
@@ -38,6 +38,9 @@ LOCUS_SEPARATOR: Final[str] = "__"
38
38
  Locus: TypeAlias = tuple[str, ...]
39
39
  """Sequence of QPU component physical names a quantum operation acts on. The order may matter."""
40
40
 
41
+ Suffixes: TypeAlias = dict[str, str]
42
+ """Suffixes in a dut_field, split into key/value pairs."""
43
+
41
44
 
42
45
  def locus_to_locus_str(locus: Locus) -> str:
43
46
  """Convert a locus into a locus string."""
@@ -61,7 +64,7 @@ class UnknownObservationError(RuntimeError):
61
64
  """Observation name was syntactically correct but contained unknown elements."""
62
65
 
63
66
 
64
- def _parse_suffixes(suffixes: Iterable[str]) -> dict[str, str]:
67
+ def _parse_suffixes(suffixes: Iterable[str]) -> Suffixes:
65
68
  """Parse the given suffixes and return them in a sorted dictionary."""
66
69
  suffix_dict = {}
67
70
  for suffix in suffixes:
@@ -227,7 +230,7 @@ class QONMetric(QON):
227
230
  """Sequence of names of QPU components on which the gate/operation is applied."""
228
231
  metric: str
229
232
  """Measured metric."""
230
- suffixes: dict[str, str] = field(default_factory=dict)
233
+ suffixes: Suffixes = field(default_factory=dict)
231
234
  """Suffixes defining the metric further (if any)."""
232
235
 
233
236
  @property
@@ -366,7 +369,7 @@ class QONGateParam(QON):
366
369
  )
367
370
 
368
371
 
369
- def _split_obs_name(obs_name: str) -> tuple[list[str], dict[str, Any]]:
372
+ def _split_obs_name(obs_name: str) -> tuple[list[str], Suffixes]:
370
373
  """Split the given observation name into path elements and suffixes."""
371
374
  # some observation names may have suffixes, split them off
372
375
  fragments = obs_name.split(_SUFFIX_SEPARATOR)
@@ -377,6 +380,19 @@ def _split_obs_name(obs_name: str) -> tuple[list[str], dict[str, Any]]:
377
380
  return path, suffixes
378
381
 
379
382
 
383
+ def _convert_to_float(value: Any) -> float:
384
+ """Attempt to convert the given value to float.
385
+
386
+ Raises:
387
+ ValueError: Conversion not possible.
388
+
389
+ """
390
+ try:
391
+ return float(value) # type: ignore[arg-type]
392
+ except (ValueError, TypeError) as e:
393
+ raise ValueError(f"Cannot convert value to float: {type(value)}") from e
394
+
395
+
380
396
  GATE_FIDELITY_METHODS = {
381
397
  "prx": "rb",
382
398
  "measure": "ssro",
@@ -406,8 +422,8 @@ class ObservationFinder(dict):
406
422
  """
407
423
 
408
424
  def __init__(self, observations: Iterable[ObservationBase], skip_unparseable: bool = False):
409
- def parse_observation_into_dict(name: str, dictionary: dict[str, Any]) -> tuple[dict[str, Any], str]:
410
- """Insert the given observation name, split into path elements, into a nested dictionary.
425
+ def parse_observation_into_dict(name: str, dictionary: dict[str, Any]) -> tuple[dict[str, Any], str, Suffixes]:
426
+ """Help insert the given observation name, split into path elements, into a nested dictionary.
411
427
 
412
428
  The returned values allow the caller to insert whatever they want under the last path element
413
429
  of ``name`` in the nested dict.
@@ -417,7 +433,8 @@ class ObservationFinder(dict):
417
433
  dictionary: Nested dictionary in which the path elements of ``name`` are inserted.
418
434
 
419
435
  Returns:
420
- The dict corresponding to the second-last path element of ``name``, last path element of ``name``.
436
+ The dict corresponding to the second-last path element of ``name``, last path element of ``name``,
437
+ suffixes in ``name``.
421
438
 
422
439
  Raises:
423
440
  ValueError: Failed to parse ``name`` because it was syntactically incorrect.
@@ -443,12 +460,20 @@ class ObservationFinder(dict):
443
460
  raise UnknownObservationError("Unknown observation domain.")
444
461
  for path_element in path[:-1]:
445
462
  dictionary = dictionary.setdefault(path_element, {})
446
- return dictionary, path[-1]
463
+ return dictionary, path[-1], suffixes
447
464
 
448
465
  for obs in observations:
449
466
  try:
450
- last_dict, last_element = parse_observation_into_dict(obs.dut_field, self)
451
- last_dict[last_element] = obs
467
+ last_dict, last_element, _ = parse_observation_into_dict(obs.dut_field, self)
468
+ # TODO how to handle suffixes?
469
+ if (existing_obs := last_dict.get(last_element)) is not None:
470
+ logger.warning(
471
+ "Repeated observations: using %s, ignoring %s",
472
+ existing_obs.dut_field,
473
+ obs.dut_field,
474
+ )
475
+ else:
476
+ last_dict[last_element] = obs
452
477
  except (ValueError, UnknownObservationError) as err:
453
478
  message = f"{obs.dut_field}: {err}"
454
479
  if skip_unparseable:
@@ -503,8 +528,8 @@ class ObservationFinder(dict):
503
528
  pass # skip this key if conversion fails
504
529
  return result
505
530
 
506
- def _get_path_value(self, path: Iterable[str]) -> float:
507
- """Follow ``path``, return the final value."""
531
+ def _follow_path(self, path: Iterable[str]) -> dict[str, Any] | ObservationBase:
532
+ """Follow ``path``, return the final node/value."""
508
533
  node: dict[str, Any] | ObservationBase = self
509
534
  for step in path:
510
535
  if isinstance(node, ObservationBase):
@@ -513,25 +538,18 @@ class ObservationFinder(dict):
513
538
  if next_node is None:
514
539
  raise KeyError(f"path step '{step}' could not be found")
515
540
  node = next_node
541
+ return node
516
542
 
543
+ def _get_path_value(self, path: Iterable[str]) -> float:
544
+ """Follow ``path``, return the final value."""
545
+ node = self._follow_path(path)
517
546
  if not isinstance(node, ObservationBase):
518
547
  raise KeyError(f"path {path} does not end in an observation")
519
-
520
- try:
521
- return float(node.value) # type: ignore[arg-type]
522
- except (ValueError, TypeError) as e:
523
- raise ValueError(f"Cannot convert value to float: {type(node.value)}") from e
548
+ return _convert_to_float(node.value)
524
549
 
525
550
  def _get_path_node(self, path: Iterable[str]) -> dict[str, Any]:
526
551
  """Follow ``path``, return the final node."""
527
- node: dict[str, Any] | ObservationBase = self
528
- for step in path:
529
- if isinstance(node, ObservationBase):
530
- raise KeyError(f"path step '{step}' could not be found")
531
- next_node = node.get(step)
532
- if next_node is None:
533
- raise KeyError(f"path step '{step}' could not be found")
534
- node = next_node
552
+ node = self._follow_path(path)
535
553
  if isinstance(node, ObservationBase):
536
554
  raise KeyError(f"path {path} does not end in a node")
537
555
  return node
@@ -577,14 +595,7 @@ class ObservationFinder(dict):
577
595
  node = self._get_path_node(["metrics", "ssro", gate_name, impl_name, locus_str])
578
596
  error_0_to_1 = node["error_0_to_1"].value
579
597
  error_1_to_0 = node["error_1_to_0"].value
580
-
581
- def convert_to_float(value):
582
- try:
583
- return float(value) # type: ignore[arg-type]
584
- except (ValueError, TypeError) as e:
585
- raise ValueError(f"Cannot convert value to float: {type(value)}") from e
586
-
587
- return convert_to_float(error_0_to_1), convert_to_float(error_1_to_0)
598
+ return _convert_to_float(error_0_to_1), _convert_to_float(error_1_to_0)
588
599
  except KeyError:
589
600
  logger.warning("Missing errors for %s.%s.%s.", gate_name, impl_name, locus_str)
590
601
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-station-control-client
3
- Version: 9.8.0
3
+ Version: 9.10.0
4
4
  Summary: Python client for communicating with Station Control Service
5
5
  Author-email: IQM Finland Oy <info@meetiqm.com>
6
6
  License: Apache License
@@ -215,12 +215,12 @@ Description-Content-Type: text/x-rst
215
215
  License-File: LICENSE.txt
216
216
  Requires-Dist: iqm-exa-common <27,>=26
217
217
  Requires-Dist: iqm-data-definitions <3.0,>=2.13
218
- Requires-Dist: opentelemetry-exporter-otlp ==1.25.0
218
+ Requires-Dist: opentelemetry-exporter-otlp <2.0,>=1.25.0
219
219
  Requires-Dist: protobuf <5.0,>=4.25.3
220
220
  Requires-Dist: types-protobuf
221
221
  Requires-Dist: grpcio <2.0,>=1.65.4
222
222
  Requires-Dist: pydantic <3.0,>=2.10.4
223
- Requires-Dist: PyYAML ==6.0
223
+ Requires-Dist: PyYAML <7.0,>=6.0
224
224
  Requires-Dist: requests ==2.32.3
225
225
  Requires-Dist: types-requests
226
226
  Requires-Dist: tqdm >=4.59.0
@@ -1,7 +1,7 @@
1
1
  iqm/station_control/client/__init__.py,sha256=1ND-AkIE9xLGIscH3WN44eyll9nlFhXeyCm-8EDFGQ4,942
2
2
  iqm/station_control/client/list_models.py,sha256=7JJyn5jCBOvMBJWdsVsPRLhtChMxqLa6O9hElpeXEt8,2701
3
3
  iqm/station_control/client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- iqm/station_control/client/qon.py,sha256=7OLZPYwLUt-wx0bk8cmG3eVplGAgdTIQXpwKomFc9Q0,22495
4
+ iqm/station_control/client/qon.py,sha256=--vWZ8ueX3pFIZPSt4N_5eQt4Ski_FJCceXDHKr77Ns,22782
5
5
  iqm/station_control/client/station_control.py,sha256=cQqHuTu3K7iVtZzdtIuihTXa_GPydyZFIjZtyCPzh7s,29208
6
6
  iqm/station_control/client/utils.py,sha256=-6K4KgOgA4iyUCqX-w26JvFxlwlGBehGj4tIWCEbn74,3360
7
7
  iqm/station_control/client/iqm_server/__init__.py,sha256=nLsRHN1rnOKXwuzaq_liUpAYV3sis5jkyHccSdacV7U,624
@@ -52,8 +52,8 @@ iqm/station_control/interface/models/sequence.py,sha256=boWlMfP3woVgVObW3OaNbxsU
52
52
  iqm/station_control/interface/models/static_quantum_architecture.py,sha256=gsfJKlYsfZVEK3dqEKXkBSIHiY14DGwNbhPJdNHMtNM,1435
53
53
  iqm/station_control/interface/models/sweep.py,sha256=HFoFIrKhlYmHIBfGltY2O9_J28OvkkZILRbDHuqR0wc,2509
54
54
  iqm/station_control/interface/models/type_aliases.py,sha256=gEYJ8zOpV1m0NVyYUbAL43psp9Lxw_0t68mYlI65Sds,1262
55
- iqm_station_control_client-9.8.0.dist-info/LICENSE.txt,sha256=R6Q7eUrLyoCQgWYorQ8WJmVmWKYU3dxA3jYUp0wwQAw,11332
56
- iqm_station_control_client-9.8.0.dist-info/METADATA,sha256=2KR6OuIUaYaP9Vnky4FyklA27cbqaTnau3dPHjhY2Hk,14096
57
- iqm_station_control_client-9.8.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
58
- iqm_station_control_client-9.8.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
59
- iqm_station_control_client-9.8.0.dist-info/RECORD,,
55
+ iqm_station_control_client-9.10.0.dist-info/LICENSE.txt,sha256=R6Q7eUrLyoCQgWYorQ8WJmVmWKYU3dxA3jYUp0wwQAw,11332
56
+ iqm_station_control_client-9.10.0.dist-info/METADATA,sha256=9SztJZ-XCq-bP8s7NO3Eek5r4h8Hg448HqeU_p_F2wU,14107
57
+ iqm_station_control_client-9.10.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
58
+ iqm_station_control_client-9.10.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
59
+ iqm_station_control_client-9.10.0.dist-info/RECORD,,