iqm-station-control-client 8.0.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.
- iqm/station_control/client/__init__.py +1 -1
- iqm/station_control/client/iqm_server/error.py +1 -1
- iqm/station_control/client/iqm_server/grpc_utils.py +5 -3
- iqm/station_control/client/iqm_server/iqm_server_client.py +276 -45
- iqm/station_control/client/list_models.py +18 -12
- iqm/station_control/client/serializers/__init__.py +1 -1
- iqm/station_control/client/serializers/channel_property_serializer.py +12 -6
- iqm/station_control/client/serializers/datetime_serializers.py +1 -1
- iqm/station_control/client/serializers/playlist_serializers.py +1 -1
- iqm/station_control/client/serializers/run_serializers.py +1 -1
- iqm/station_control/client/serializers/setting_node_serializer.py +1 -1
- iqm/station_control/client/serializers/struct_serializer.py +1 -1
- iqm/station_control/client/serializers/sweep_serializers.py +2 -3
- iqm/station_control/client/serializers/task_serializers.py +1 -1
- iqm/station_control/client/station_control.py +162 -443
- iqm/station_control/client/utils.py +44 -17
- iqm/station_control/interface/__init__.py +1 -1
- iqm/station_control/interface/list_with_meta.py +1 -1
- iqm/station_control/interface/models/__init__.py +13 -1
- iqm/station_control/interface/models/dut.py +1 -1
- iqm/station_control/interface/models/dynamic_quantum_architecture.py +98 -0
- iqm/station_control/interface/models/jobs.py +27 -3
- iqm/station_control/interface/models/observation.py +1 -1
- iqm/station_control/interface/models/observation_set.py +15 -1
- iqm/station_control/interface/models/run.py +1 -1
- iqm/station_control/interface/models/sequence.py +1 -1
- iqm/station_control/interface/models/static_quantum_architecture.py +40 -0
- iqm/station_control/interface/models/sweep.py +1 -1
- iqm/station_control/interface/models/type_aliases.py +7 -1
- iqm/station_control/interface/pydantic_base.py +1 -1
- iqm/station_control/interface/station_control.py +511 -0
- {iqm_station_control_client-8.0.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/METADATA +2 -2
- iqm_station_control_client-9.0.0.dist-info/RECORD +56 -0
- iqm/station_control/client/iqm_server/meta_class.py +0 -38
- iqm_station_control_client-8.0.0.dist-info/RECORD +0 -54
- {iqm_station_control_client-8.0.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/LICENSE.txt +0 -0
- {iqm_station_control_client-8.0.0.dist-info → iqm_station_control_client-9.0.0.dist-info}/WHEEL +0 -0
- {iqm_station_control_client-8.0.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
|
-
#
|
|
6
|
-
#
|
|
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
|
|
16
|
+
from collections.abc import Callable
|
|
12
17
|
|
|
18
|
+
import requests
|
|
13
19
|
from tqdm.auto import tqdm
|
|
14
20
|
|
|
15
|
-
from
|
|
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.
|
|
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
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
@@ -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."""
|
|
@@ -59,7 +59,7 @@ class JobData(PydanticBase):
|
|
|
59
59
|
class JobExecutorStatus(Enum):
|
|
60
60
|
"""Enumeration of different states a job can be in. The ordering of these statuses is important,
|
|
61
61
|
and execution logic relies on it. Thus, if a new status is added, ensure that it is slotted
|
|
62
|
-
in at the appropriate place. See the __lt__ implementation for further details.
|
|
62
|
+
in at the appropriate place. See the :meth:`__lt__` implementation for further details.
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
65
|
# Received by the server
|
|
@@ -111,9 +111,33 @@ class JobExecutorStatus(Enum):
|
|
|
111
111
|
def __lt__(self, other):
|
|
112
112
|
"""Enable comparison according to definition order.
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
:meta public:
|
|
115
|
+
|
|
116
|
+
.. doctest::
|
|
117
|
+
|
|
118
|
+
>>> JobExecutorStatus.RECEIVED < JobExecutorStatus.VALIDATION_STARTED
|
|
116
119
|
True
|
|
120
|
+
>>> sorted(list(JobExecutorStatus.__members__))
|
|
121
|
+
[
|
|
122
|
+
JobExecutorStatus.RECEIVED,
|
|
123
|
+
JobExecutorStatus.VALIDATION_STARTED,
|
|
124
|
+
JobExecutorStatus.VALIDATION_ENDED,
|
|
125
|
+
JobExecutorStatus.FETCH_CALIBRATION_STARTED,
|
|
126
|
+
JobExecutorStatus.FETCH_CALIBRATION_ENDED,
|
|
127
|
+
JobExecutorStatus.COMPILATION_STARTED,
|
|
128
|
+
JobExecutorStatus.COMPILATION_ENDED,
|
|
129
|
+
JobExecutorStatus.SAVE_SWEEP_METADATA_STARTED,
|
|
130
|
+
JobExecutorStatus.SAVE_SWEEP_METADATA_ENDED,
|
|
131
|
+
JobExecutorStatus.PENDING_EXECUTION,
|
|
132
|
+
JobExecutorStatus.EXECUTION_STARTED,
|
|
133
|
+
JobExecutorStatus.EXECUTION_ENDED,
|
|
134
|
+
JobExecutorStatus.POST_PROCESSING_PENDING,
|
|
135
|
+
JobExecutorStatus.POST_PROCESSING_STARTED,
|
|
136
|
+
JobExecutorStatus.POST_PROCESSING_ENDED,
|
|
137
|
+
JobExecutorStatus.READY,
|
|
138
|
+
JobExecutorStatus.FAILED,
|
|
139
|
+
JobExecutorStatus.ABORTED,
|
|
140
|
+
]
|
|
117
141
|
|
|
118
142
|
"""
|
|
119
143
|
if isinstance(other, str):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
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
|
|
@@ -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
|
|
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"]
|