iqm-station-control-client 8.1.0__py3-none-any.whl → 9.0.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.
Files changed (36) hide show
  1. iqm/station_control/client/__init__.py +1 -1
  2. iqm/station_control/client/iqm_server/error.py +1 -1
  3. iqm/station_control/client/iqm_server/grpc_utils.py +5 -3
  4. iqm/station_control/client/iqm_server/iqm_server_client.py +276 -45
  5. iqm/station_control/client/list_models.py +18 -12
  6. iqm/station_control/client/serializers/__init__.py +1 -1
  7. iqm/station_control/client/serializers/channel_property_serializer.py +12 -6
  8. iqm/station_control/client/serializers/datetime_serializers.py +1 -1
  9. iqm/station_control/client/serializers/playlist_serializers.py +1 -1
  10. iqm/station_control/client/serializers/run_serializers.py +1 -1
  11. iqm/station_control/client/serializers/setting_node_serializer.py +1 -1
  12. iqm/station_control/client/serializers/struct_serializer.py +1 -1
  13. iqm/station_control/client/serializers/sweep_serializers.py +2 -3
  14. iqm/station_control/client/serializers/task_serializers.py +1 -1
  15. iqm/station_control/client/station_control.py +162 -443
  16. iqm/station_control/client/utils.py +44 -17
  17. iqm/station_control/interface/__init__.py +1 -1
  18. iqm/station_control/interface/list_with_meta.py +1 -1
  19. iqm/station_control/interface/models/__init__.py +13 -1
  20. iqm/station_control/interface/models/dut.py +1 -1
  21. iqm/station_control/interface/models/dynamic_quantum_architecture.py +98 -0
  22. iqm/station_control/interface/models/observation.py +1 -1
  23. iqm/station_control/interface/models/observation_set.py +15 -1
  24. iqm/station_control/interface/models/run.py +1 -1
  25. iqm/station_control/interface/models/sequence.py +1 -1
  26. iqm/station_control/interface/models/static_quantum_architecture.py +40 -0
  27. iqm/station_control/interface/models/sweep.py +1 -1
  28. iqm/station_control/interface/models/type_aliases.py +7 -1
  29. iqm/station_control/interface/pydantic_base.py +1 -1
  30. iqm/station_control/interface/station_control.py +511 -0
  31. {iqm_station_control_client-8.1.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/METADATA +2 -2
  32. {iqm_station_control_client-8.1.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/RECORD +35 -33
  33. iqm/station_control/client/iqm_server/meta_class.py +0 -38
  34. {iqm_station_control_client-8.1.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/LICENSE.txt +0 -0
  35. {iqm_station_control_client-8.1.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/WHEEL +0 -0
  36. {iqm_station_control_client-8.1.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/top_level.txt +0 -0
@@ -1,20 +1,27 @@
1
- # ********************************************************************************
2
- # Copyright (c) 2021-2024 IQM Finland Oy.
3
- # All rights reserved. Confidential and proprietary.
1
+ # Copyright 2025 IQM
4
2
  #
5
- # Distribution or reproduction of any information contained herein
6
- # is prohibited without IQM Finland Oy’s prior written permission.
7
- # ********************************************************************************
8
-
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.
9
14
  """Utility functions for IQM Station Control Client."""
10
15
 
11
- from collections.abc import Callable, Iterable
16
+ from collections.abc import Callable
12
17
 
18
+ import requests
13
19
  from tqdm.auto import tqdm
14
20
 
15
- from exa.common.data.value import ObservationValue
21
+ from iqm.station_control.client.iqm_server.iqm_server_client import IqmServerClient
22
+ from iqm.station_control.client.station_control import StationControlClient
16
23
  from iqm.station_control.interface.models import Statuses
17
- from iqm.station_control.interface.models.observation import ObservationBase
24
+ from iqm.station_control.interface.station_control import StationControlInterface
18
25
 
19
26
 
20
27
  def get_progress_bar_callback() -> Callable[[Statuses], None]:
@@ -31,14 +38,34 @@ def get_progress_bar_callback() -> Callable[[Statuses], None]:
31
38
  return _create_and_update_progress_bars
32
39
 
33
40
 
34
- def calset_from_observations(calset_observations: Iterable[ObservationBase]) -> dict[str, ObservationValue]:
35
- """Create a calibration set from the given observations.
41
+ def init_station_control(
42
+ root_url: str, get_token_callback: Callable[[], str] | None = None, **kwargs
43
+ ) -> StationControlInterface:
44
+ """Initialize a new station control instance connected to the given remote.
36
45
 
37
- Args:
38
- calset_observations: observations that form a calibration set
46
+ Client implementation is selected automatically based on the remote station: if the remote station
47
+ is running the IQM Server software stack, then the IQM Server client implementation (with a limited
48
+ feature set) is chosen. If the remote station is running the SC software stack, then the Station
49
+ Control client implementation (with the full feature set) is chosen.
39
50
 
40
- Returns:
41
- calibration set
51
+ Args:
52
+ root_url: Remote station control service URL. For IQM Server remotes, this is the "Quantum Computer URL"
53
+ value from the web dashboard.
54
+ get_token_callback: A callback function that returns a token (str) which will be passed in Authorization
55
+ header in all requests.
42
56
 
43
57
  """
44
- return {obs.dut_field: obs.value for obs in calset_observations}
58
+ try:
59
+ headers = {"Authorization": get_token_callback()} if get_token_callback else {}
60
+ response = requests.get(f"{root_url}/about", headers=headers)
61
+ response.raise_for_status()
62
+ about = response.json()
63
+ if isinstance(about, dict) and about.get("iqm_server") is True:
64
+ # If about information has iqm_server flag, it means that we're communicating
65
+ # with IQM server instead of direct Station Control service, hence we need to
66
+ # use the specialized client
67
+ return IqmServerClient(root_url, get_token_callback=get_token_callback, **kwargs)
68
+ # Using direct station control by default
69
+ return StationControlClient(root_url=root_url, get_token_callback=get_token_callback, **kwargs)
70
+ except Exception as e:
71
+ raise RuntimeError("Failed to initialize the client.") from e
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -14,8 +14,17 @@
14
14
  """Station control interface models."""
15
15
 
16
16
  from iqm.station_control.interface.models.dut import DutData, DutFieldData
17
+ from iqm.station_control.interface.models.dynamic_quantum_architecture import (
18
+ DynamicQuantumArchitecture,
19
+ GateImplementationInfo,
20
+ GateInfo,
21
+ Locus,
22
+ )
17
23
  from iqm.station_control.interface.models.jobs import (
24
+ JobData,
18
25
  JobExecutorStatus,
26
+ JobResult,
27
+ TimelineEntry,
19
28
  )
20
29
  from iqm.station_control.interface.models.observation import (
21
30
  ObservationData,
@@ -27,6 +36,8 @@ from iqm.station_control.interface.models.observation_set import (
27
36
  ObservationSetData,
28
37
  ObservationSetDefinition,
29
38
  ObservationSetUpdate,
39
+ ObservationSetWithObservations,
40
+ QualityMetrics,
30
41
  )
31
42
  from iqm.station_control.interface.models.run import RunData, RunDefinition, RunLite
32
43
  from iqm.station_control.interface.models.sequence import (
@@ -35,6 +46,7 @@ from iqm.station_control.interface.models.sequence import (
35
46
  SequenceResultData,
36
47
  SequenceResultDefinition,
37
48
  )
49
+ from iqm.station_control.interface.models.static_quantum_architecture import StaticQuantumArchitecture
38
50
  from iqm.station_control.interface.models.sweep import SweepData, SweepDefinition
39
51
  from iqm.station_control.interface.models.type_aliases import (
40
52
  DutType,
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -0,0 +1,98 @@
1
+ # Copyright 2025 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
+ """Dynamic quantum architecture (DQA) related interface models."""
15
+
16
+ from uuid import UUID
17
+
18
+ from pydantic import Field, StrictStr
19
+
20
+ from iqm.station_control.interface.pydantic_base import PydanticBase
21
+
22
+ Locus = tuple[StrictStr, ...]
23
+ """Names of the QPU components (typically qubits) a quantum operation instance is acting on, e.g. `("QB1", "QB2")`."""
24
+
25
+
26
+ class GateImplementationInfo(PydanticBase):
27
+ """Information about an implementation of a quantum gate/operation."""
28
+
29
+ loci: tuple[Locus, ...] = Field(
30
+ examples=[(("COMP_R", "QB1"), ("COMP_R", "QB2"))],
31
+ )
32
+ """Loci for which this gate implementation has been calibrated."""
33
+
34
+
35
+ class GateInfo(PydanticBase):
36
+ """Information about a quantum gate/operation."""
37
+
38
+ implementations: dict[str, GateImplementationInfo] = Field(
39
+ examples=[
40
+ {
41
+ "tgss": GateImplementationInfo(loci=(("COMP_R", "QB1"), ("COMP_R", "QB2"))),
42
+ "crf": GateImplementationInfo(loci=(("COMP_R", "QB1"), ("COMP_R", "QB2"))),
43
+ }
44
+ ],
45
+ )
46
+ """Mapping of available implementation names to information about the implementations."""
47
+
48
+ default_implementation: str = Field(
49
+ examples=["tgss"],
50
+ )
51
+ """Default implementation for the gate.
52
+
53
+ Used unless overridden by :attr:`override_default_implementation`,
54
+ or unless the user requests a specific implementation for a particular gate in the circuit using
55
+ :attr:`iqm.cocos.app.api.request_models.Instruction.implementation`."""
56
+
57
+ override_default_implementation: dict[Locus, str] = Field(
58
+ examples=[{("COMP_R", "QB2"): "crf"}],
59
+ )
60
+ """Mapping of loci to implementation names that override ``default_implementation`` for those loci."""
61
+
62
+
63
+ class DynamicQuantumArchitecture(PydanticBase):
64
+ """The dynamic quantum architecture (DQA).
65
+
66
+ Describes gates/operations for which calibration data exists in the calibration set.
67
+ """
68
+
69
+ calibration_set_id: UUID = Field(
70
+ examples=["cd4dd889-b88b-4370-ba01-eb8262ad9c53"],
71
+ )
72
+ """ID of the calibration set from which this DQA was generated."""
73
+
74
+ qubits: list[str] = Field(
75
+ examples=[["QB1", "QB2"]],
76
+ )
77
+ """Qubits that appear in at least one gate locus in the calibration set."""
78
+
79
+ computational_resonators: list[str] = Field(
80
+ examples=[["COMP_R"]],
81
+ )
82
+ """Computational resonators that appear in at least one gate locus in the calibration set."""
83
+
84
+ gates: dict[str, GateInfo] = Field(
85
+ examples=[
86
+ {
87
+ "cz": GateInfo(
88
+ implementations={
89
+ "tgss": GateImplementationInfo(loci=(("COMP_R", "QB1"), ("COMP_R", "QB2"))),
90
+ "crf": GateImplementationInfo(loci=(("COMP_R", "QB1"), ("COMP_R", "QB2"))),
91
+ },
92
+ default_implementation="tgss",
93
+ override_default_implementation={("COMP_R", "QB2"): "crf"},
94
+ )
95
+ }
96
+ ],
97
+ )
98
+ """Mapping of gate names to information about the gates."""
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import uuid
18
18
 
19
19
  from pydantic import ConfigDict, Field
20
20
 
21
+ from iqm.station_control.interface.models.observation import ObservationLite
21
22
  from iqm.station_control.interface.models.type_aliases import ObservationSetType
22
23
  from iqm.station_control.interface.pydantic_base import PydanticBase
23
24
 
@@ -74,3 +75,16 @@ class ObservationSetUpdate(PydanticBase):
74
75
  """
75
76
  invalid: bool
76
77
  """Flag indicating if the object is invalid. Automated systems must not use invalid objects."""
78
+
79
+
80
+ class ObservationSetWithObservations(ObservationSetData):
81
+ """The content of the observation set stored in the database, with a list of observations."""
82
+
83
+ observations: list[ObservationLite]
84
+ """Observations belonging to the observation set."""
85
+
86
+
87
+ class QualityMetrics(ObservationSetWithObservations):
88
+ """The content of the quality metric set stored in the database, with a list of observations and calibration set."""
89
+
90
+ calibration_set: ObservationSetData
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -0,0 +1,40 @@
1
+ # Copyright 2025 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
+ """Static quantum architecture (SQA) related interface models."""
15
+
16
+ from pydantic import Field
17
+
18
+ from iqm.station_control.interface.pydantic_base import PydanticBase
19
+
20
+
21
+ class StaticQuantumArchitecture(PydanticBase):
22
+ """The static quantum architecture (SQA) provides information about the QPU.
23
+
24
+ For example, the names of its components and the connections between them.
25
+ """
26
+
27
+ qubits: list[str] = Field(
28
+ examples=[["QB1", "QB2"]],
29
+ )
30
+ """Names of the qubits on the QPU, sorted."""
31
+
32
+ computational_resonators: list[str] = Field(
33
+ examples=[["CR1"]],
34
+ )
35
+ """Names of the computational resonators on the QPU, sorted."""
36
+
37
+ connectivity: list[tuple[str, ...]] = Field(
38
+ examples=[[("QB1", "QB2"), ("QB1", "CR1")]],
39
+ )
40
+ """Components (qubits and computational resonators) connected by a coupler on the QPU, sorted."""
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -14,9 +14,15 @@
14
14
  """Type hint aliases used in the station control interface."""
15
15
 
16
16
  from typing import Literal
17
+ from uuid import UUID
17
18
 
18
19
  import numpy as np
19
20
 
21
+ # Allow using string UUIDs in API calls directly for convenience.
22
+ # StrUUID works if UUIDs will be serialized to strings by the client anyway,
23
+ # and then deserialized back to UUID on the server side.
24
+ StrUUID = str | UUID
25
+
20
26
  DutType = Literal["chip", "twpa"]
21
27
  GetObservationsMode = Literal["all_latest", "tags_and", "tags_or", "sequence"]
22
28
  ObservationSetType = Literal["calibration-set", "characterization-set", "generic-set", "quality-metric-set"]
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.