boulder-opal-scale-up-sdk 1.0.0__py3-none-any.whl → 1.0.2__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.
- boulder_opal_scale_up_sdk-1.0.2.dist-info/LICENSE +805 -0
- {boulder_opal_scale_up_sdk-1.0.0.dist-info → boulder_opal_scale_up_sdk-1.0.2.dist-info}/METADATA +17 -7
- boulder_opal_scale_up_sdk-1.0.2.dist-info/RECORD +60 -0
- boulderopalscaleupsdk/agent/worker.py +22 -11
- boulderopalscaleupsdk/common/dtypes.py +81 -114
- boulderopalscaleupsdk/device/__init__.py +5 -1
- boulderopalscaleupsdk/device/config_loader.py +15 -10
- boulderopalscaleupsdk/device/controller/__init__.py +17 -14
- boulderopalscaleupsdk/device/controller/base.py +12 -3
- boulderopalscaleupsdk/device/controller/qblox.py +43 -17
- boulderopalscaleupsdk/device/controller/quantum_machines.py +60 -59
- boulderopalscaleupsdk/device/controller/resolver.py +117 -0
- boulderopalscaleupsdk/device/defcal.py +62 -0
- boulderopalscaleupsdk/device/device.py +8 -2
- boulderopalscaleupsdk/device/processor/__init__.py +9 -1
- boulderopalscaleupsdk/device/processor/common.py +129 -20
- boulderopalscaleupsdk/device/processor/superconducting_processor.py +61 -15
- boulderopalscaleupsdk/experiments/__init__.py +8 -0
- boulderopalscaleupsdk/experiments/chi01_scan.py +56 -0
- boulderopalscaleupsdk/experiments/common.py +8 -4
- boulderopalscaleupsdk/experiments/power_rabi.py +3 -3
- boulderopalscaleupsdk/experiments/ramsey.py +15 -8
- boulderopalscaleupsdk/experiments/readout_classifier_calibration.py +24 -0
- boulderopalscaleupsdk/experiments/resonator_spectroscopy.py +3 -3
- boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py +10 -10
- boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py +3 -3
- boulderopalscaleupsdk/experiments/transmon_anharmonicity.py +68 -0
- boulderopalscaleupsdk/experiments/transmon_spectroscopy.py +69 -0
- boulderopalscaleupsdk/grpc_interceptors/auth.py +5 -2
- boulderopalscaleupsdk/plotting/__init__.py +20 -2
- boulderopalscaleupsdk/plotting/dtypes.py +81 -117
- boulderopalscaleupsdk/protobuf/v1/agent_pb2.py +17 -17
- boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi +8 -6
- boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py +13 -13
- boulderopalscaleupsdk/protobuf/v1/device_pb2.py +43 -25
- boulderopalscaleupsdk/protobuf/v1/device_pb2.pyi +50 -8
- boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py +116 -14
- boulderopalscaleupsdk/routines/__init__.py +1 -4
- boulderopalscaleupsdk/routines/resonator_mapping.py +8 -2
- boulderopalscaleupsdk/stubs/__init__.py +12 -0
- boulderopalscaleupsdk/stubs/dtypes.py +47 -0
- boulderopalscaleupsdk/stubs/maps.py +9 -0
- boulderopalscaleupsdk/third_party/quantum_machines/__init__.py +32 -0
- boulderopalscaleupsdk/third_party/quantum_machines/config.py +30 -9
- boulder_opal_scale_up_sdk-1.0.0.dist-info/RECORD +0 -50
- {boulder_opal_scale_up_sdk-1.0.0.dist-info → boulder_opal_scale_up_sdk-1.0.2.dist-info}/WHEEL +0 -0
{boulder_opal_scale_up_sdk-1.0.0.dist-info → boulder_opal_scale_up_sdk-1.0.2.dist-info}/METADATA
RENAMED
@@ -1,24 +1,23 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: boulder-opal-scale-up-sdk
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.2
|
4
4
|
Summary: Q-CTRL Boulder Opal Scale Up Python SDK
|
5
|
-
License:
|
5
|
+
License: https://q-ctrl.com/terms
|
6
6
|
Keywords: black opal,boulder opal,fire opal,nisq,open controls,q control,q ctrl,q-control,q-ctrl,qcontrol,qctrl,quantum,quantum algorithms,quantum circuits,quantum coding,quantum coding software,quantum computing,quantum control,quantum control software,quantum control theory,quantum engineering,quantum error correction,quantum firmware,quantum fundamentals,quantum sensing,qubit,qudit
|
7
7
|
Author: Q-CTRL
|
8
8
|
Author-email: support@q-ctrl.com
|
9
9
|
Maintainer: Q-CTRL
|
10
10
|
Maintainer-email: support@q-ctrl.com
|
11
|
-
Requires-Python: >=3.
|
11
|
+
Requires-Python: >=3.11,<3.13
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
13
13
|
Classifier: Environment :: Console
|
14
14
|
Classifier: Intended Audience :: Developers
|
15
15
|
Classifier: Intended Audience :: Education
|
16
16
|
Classifier: Intended Audience :: Science/Research
|
17
|
-
Classifier: License ::
|
17
|
+
Classifier: License :: Other/Proprietary License
|
18
18
|
Classifier: Natural Language :: English
|
19
19
|
Classifier: Operating System :: OS Independent
|
20
20
|
Classifier: Programming Language :: Python :: 3
|
21
|
-
Classifier: Programming Language :: Python :: 3.10
|
22
21
|
Classifier: Programming Language :: Python :: 3.11
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
24
23
|
Classifier: Topic :: Internet :: WWW/HTTP
|
@@ -26,13 +25,24 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
26
25
|
Classifier: Topic :: Scientific/Engineering :: Visualization
|
27
26
|
Classifier: Topic :: Software Development :: Embedded Systems
|
28
27
|
Classifier: Topic :: System :: Distributed Computing
|
29
|
-
|
28
|
+
Provides-Extra: quantum-machines
|
30
29
|
Requires-Dist: attrs (>=25.1.0,<26.0.0)
|
31
30
|
Requires-Dist: googleapis-common-protos (>=1.69.2,<2.0.0)
|
32
|
-
Requires-Dist:
|
31
|
+
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
32
|
+
Requires-Dist: pydantic (>=2.10.4,<3.0.0)
|
33
|
+
Requires-Dist: pydantic-settings (>=2.7.0,<3.0.0)
|
34
|
+
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
35
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
36
|
+
Requires-Dist: qm-qua (==1.2.1) ; extra == "quantum-machines"
|
33
37
|
Project-URL: Facebook, https://www.facebook.com/qctrl
|
34
38
|
Project-URL: GitHub, https://github.com/qctrl
|
35
39
|
Project-URL: Homepage, https://q-ctrl.com
|
36
40
|
Project-URL: LinkedIn, https://www.linkedin.com/company/q-ctrl/
|
37
41
|
Project-URL: X, https://x.com/qctrlHQ
|
38
42
|
Project-URL: YouTube, https://www.youtube.com/qctrl
|
43
|
+
Description-Content-Type: text/markdown
|
44
|
+
|
45
|
+
# Boulder Opal Scale Up SDK
|
46
|
+
|
47
|
+
The Boulder Opal Scale Up SDK package is a Python SDK for Q-CTRL Boulder Opal Scale Up. Scale Up provides a tailored solution to characterize and calibrate quantum hardware.
|
48
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
boulderopalscaleupsdk/__init__.py,sha256=nD3YDqPiE52mmuUrIlDUrYSyljpMsDJvc5HsubBUSs4,592
|
2
|
+
boulderopalscaleupsdk/agent/__init__.py,sha256=aFkAtHJDOdXA126JklxYz0ix1k4lCcLLS9DQp8zUKMk,1092
|
3
|
+
boulderopalscaleupsdk/agent/worker.py,sha256=XlbjvAjQgKMpRbbI04jzVY9NzqW5nO_D5wFq7fJC-qM,7937
|
4
|
+
boulderopalscaleupsdk/common/__init__.py,sha256=vLaJ1FP36xV5eX6VD7nShqxVBm3UzsBMIK1pmSiTIag,550
|
5
|
+
boulderopalscaleupsdk/common/dtypes.py,sha256=1Lf-kulA0vxFMCW1E5nfHifoCTOtq76fDwQTSco_qCQ,9601
|
6
|
+
boulderopalscaleupsdk/common/typeclasses.py,sha256=FZHyTcuCQH5niqFPZmIyoO2zN6mA661h8kyvDcYFryg,3033
|
7
|
+
boulderopalscaleupsdk/device/__init__.py,sha256=Bbo9KYQqNZloj2H9sl2PdfFvYMDS78q1Mu0Q0q5Ze5o,727
|
8
|
+
boulderopalscaleupsdk/device/common.py,sha256=ZLXpmORKQYntX7DeKZ8pd49Ju1jzidR2q2EeN3TNwTI,1909
|
9
|
+
boulderopalscaleupsdk/device/config_loader.py,sha256=RxCD77Z52lVaMYcimdyxoz9V3hb1Q5ta4xk1eU7wwxM,3367
|
10
|
+
boulderopalscaleupsdk/device/controller/__init__.py,sha256=ePkrCbX1ORm05fS0BEQ8cx7DPxGGN6IkUoNH6eQL1DM,1136
|
11
|
+
boulderopalscaleupsdk/device/controller/base.py,sha256=xuT4VzRulknxppiQpzYYn_TwuRNfV9wHMqjN2okA8Lw,793
|
12
|
+
boulderopalscaleupsdk/device/controller/qblox.py,sha256=c-wG9jAZHy6hCTO7ynKvwJbW1MPNJJh9ryfedthuuz8,22543
|
13
|
+
boulderopalscaleupsdk/device/controller/quantum_machines.py,sha256=m0_zO7Cn-Ea1-ewG7qgjEnt4GXtQMvBv_oz-dnOmI9k,5320
|
14
|
+
boulderopalscaleupsdk/device/controller/resolver.py,sha256=hW5U58dmJf_IGmD4EK_bbn0XU-N9autFBFHmqbRnY2Y,3812
|
15
|
+
boulderopalscaleupsdk/device/defcal.py,sha256=CL9T-oy94st20NyQZWW8mHJU4Nlx2DKZ-o5x1mf6L8c,1912
|
16
|
+
boulderopalscaleupsdk/device/device.py,sha256=e3jW6dTFmGAxniPLTSTRVQs5HUkSUvrqolOlv2xJKUw,1243
|
17
|
+
boulderopalscaleupsdk/device/processor/__init__.py,sha256=O9d4tMcii3OOGez845qXp06D_AxYMoHr-7ICEStlNro,1038
|
18
|
+
boulderopalscaleupsdk/device/processor/common.py,sha256=kRR5nufc8PoTDLxXbSeuprlCgBDrPHSdPom_go9fDOs,9136
|
19
|
+
boulderopalscaleupsdk/device/processor/superconducting_processor.py,sha256=c6Au_j5db611xOJF0zy6bpOanhm-7QYAMWerfKMwjzQ,12269
|
20
|
+
boulderopalscaleupsdk/experiments/__init__.py,sha256=kskcl-oMR8tRMo8_liSqmty82mtbl3WzB-IY5g88ng4,1628
|
21
|
+
boulderopalscaleupsdk/experiments/chi01_scan.py,sha256=qPFa6R1IJmctRId8K-DPejX5P0LXqXUmC-NO6-xmCQU,1862
|
22
|
+
boulderopalscaleupsdk/experiments/common.py,sha256=sT06O3I71iEY5AmXGRZfx-V6RQlBEq5B7hCRxnG64tM,2482
|
23
|
+
boulderopalscaleupsdk/experiments/power_rabi.py,sha256=_keqv3tW10d-1vnUPTsky9FvLnjFcKF3pcnYfOyNvaI,2059
|
24
|
+
boulderopalscaleupsdk/experiments/ramsey.py,sha256=R3pptmkqwHYhhlAyRbYBaSscuiB3RffodC23kKuqwFY,2050
|
25
|
+
boulderopalscaleupsdk/experiments/readout_classifier_calibration.py,sha256=mUgKgKiXTv4owykR6nYj-DYZvplR5Xr9Akj36b7WGA4,732
|
26
|
+
boulderopalscaleupsdk/experiments/resonator_spectroscopy.py,sha256=EmmLbDq0d-67F1D6biLlUd1-UYP4uiICSkrIeez2T1I,2191
|
27
|
+
boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py,sha256=ypb3ZX-OCzQGy3vSlpRct5MGZmvMMZk7pi8dD9EU5VU,2765
|
28
|
+
boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py,sha256=N1ibW6ZWSrsRs6-es0bBGkRSc-1WeqU4pwpKRSpZBhE,2225
|
29
|
+
boulderopalscaleupsdk/experiments/transmon_anharmonicity.py,sha256=5nb_wTHJ_rhNGAR7THAL2YNvBZKPFJmMwjamPKgP8ko,2706
|
30
|
+
boulderopalscaleupsdk/experiments/transmon_spectroscopy.py,sha256=Ib1OdJwCE7m0Q3N_l0Mx8uKBO6GjjM56BDroLDVCBRI,2324
|
31
|
+
boulderopalscaleupsdk/grpc_interceptors/__init__.py,sha256=PkdOlllLbRWycofvGCCxDY60Ydp_U0QM5l4u3Fsnno4,616
|
32
|
+
boulderopalscaleupsdk/grpc_interceptors/auth.py,sha256=PSe_b9ckqhcb3xf1Y4jpn1TjfEWfokkarqfUHIz6HIs,4165
|
33
|
+
boulderopalscaleupsdk/plotting/__init__.py,sha256=pkdCky3YHqc3PLRy_h9puJKAO4AqSJGebb26x7XayR4,959
|
34
|
+
boulderopalscaleupsdk/plotting/dtypes.py,sha256=PlVzGpoZfrqVULPJfpRQq3JmgjUgVH7ZPdPkIys_AnE,4737
|
35
|
+
boulderopalscaleupsdk/protobuf/v1/agent_pb2.py,sha256=yExXTLC3g_TnHJWkfYsF_kctsftxONyZOBtOFDTTuec,5364
|
36
|
+
boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi,sha256=YJdOKaOtI2PGk7ct_LuC8_6NeKsLCXMriVEt4-0TWYM,2536
|
37
|
+
boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py,sha256=UH8u9ZmvMjteAYKtTmA-IWSGZmhCQLHdg0_OuKCaYew,6875
|
38
|
+
boulderopalscaleupsdk/protobuf/v1/device_pb2.py,sha256=lu-FLsrwT_xnG0411zN2lE1yvtpIiMF22M8hvCpP4Zk,11429
|
39
|
+
boulderopalscaleupsdk/protobuf/v1/device_pb2.pyi,sha256=NCrnlIXEsTKZaXjgEdy1cXxgOu4Xli0qbUm4_mOXqf8,6527
|
40
|
+
boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py,sha256=0CtrO8gKkcfHn7flQLFAySyOVpdDgzct_EaMMF73rUY,19445
|
41
|
+
boulderopalscaleupsdk/protobuf/v1/task_pb2.py,sha256=7NWFFBEPij8B9dvexRia3B_T9-oOCDwayASCglvBtO4,7086
|
42
|
+
boulderopalscaleupsdk/protobuf/v1/task_pb2.pyi,sha256=QWciGKFD1AcaAhqgym0ZwNtIlNT85HhOTBdivEC6QJE,5952
|
43
|
+
boulderopalscaleupsdk/protobuf/v1/task_pb2_grpc.py,sha256=yP2FZ148RmSYFHivatCXuM13oRBDA5wBwmvhZQNBTXQ,5826
|
44
|
+
boulderopalscaleupsdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
45
|
+
boulderopalscaleupsdk/routines/__init__.py,sha256=jSrTzE_d-4z-wA8NK9akzVtUJJ_CG026qs8orMuWKfk,143
|
46
|
+
boulderopalscaleupsdk/routines/common.py,sha256=Lav4eH4GJyRJXBZwmhA3b34UTgck5x4URex3SBSctwM,224
|
47
|
+
boulderopalscaleupsdk/routines/resonator_mapping.py,sha256=A7Z5jGicezYMxvC4yeRrLu-MbYHfMaZ6h2AuMwBPrBo,475
|
48
|
+
boulderopalscaleupsdk/stubs/__init__.py,sha256=vLaJ1FP36xV5eX6VD7nShqxVBm3UzsBMIK1pmSiTIag,550
|
49
|
+
boulderopalscaleupsdk/stubs/dtypes.py,sha256=VH2QqmmaSegv03KEaZBqB-REHhvk5h3GPfQgCJTAYQE,1361
|
50
|
+
boulderopalscaleupsdk/stubs/maps.py,sha256=fgEwVyaJ1i5io_NCAABJb7Cyyl_zgdjb8xot75ujw8U,575
|
51
|
+
boulderopalscaleupsdk/third_party/__init__.py,sha256=b9T2IVfz9bMenSsLQuaf4A14ySinjNnSGx68hLsFHZE,596
|
52
|
+
boulderopalscaleupsdk/third_party/quantum_machines/__init__.py,sha256=ORD6crMw6gtoIp8dmusdwSiU4pje739fOacOfHXSOls,2541
|
53
|
+
boulderopalscaleupsdk/third_party/quantum_machines/config.py,sha256=tQsWx_nSXSFT4fRuMzuvliplyRsB9Sy70kHqsKuTA-A,20756
|
54
|
+
boulderopalscaleupsdk/third_party/quantum_machines/constants.py,sha256=5PpAi6MZ53yEinBHerY2ssi30BR37JnLBWL21irpZ8Y,870
|
55
|
+
boulderopalscaleupsdk/utils/__init__.py,sha256=vLaJ1FP36xV5eX6VD7nShqxVBm3UzsBMIK1pmSiTIag,550
|
56
|
+
boulderopalscaleupsdk/utils/serial_utils.py,sha256=SzvY-WFD0b8IGSh6PTv7F9y1W2IPPYmj8pY3AsX-xlM,2062
|
57
|
+
boulder_opal_scale_up_sdk-1.0.2.dist-info/LICENSE,sha256=wqX4S5Brcwkwo750l9grSspwm0cyMsZtZEa1Vx3_WiE,36587
|
58
|
+
boulder_opal_scale_up_sdk-1.0.2.dist-info/METADATA,sha256=SPWPGe2xUsza1tnuUBCgVzXHcBrJCxCVbz0Zrr5HlII,2419
|
59
|
+
boulder_opal_scale_up_sdk-1.0.2.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
60
|
+
boulder_opal_scale_up_sdk-1.0.2.dist-info/RECORD,,
|
@@ -27,6 +27,7 @@ from grpc import aio as grpc
|
|
27
27
|
from grpc import ssl_channel_credentials
|
28
28
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
29
29
|
|
30
|
+
from boulderopalscaleupsdk.common.dtypes import GrpcMetadata
|
30
31
|
from boulderopalscaleupsdk.protobuf.v1 import agent_pb2, task_pb2, task_pb2_grpc
|
31
32
|
|
32
33
|
LOG = logging.getLogger(__name__)
|
@@ -51,11 +52,11 @@ class TaskHandler(Protocol):
|
|
51
52
|
@abstractmethod
|
52
53
|
async def handle(
|
53
54
|
self,
|
54
|
-
request: agent_pb2.
|
55
|
+
request: agent_pb2.RunProgramRequest
|
55
56
|
| agent_pb2.RunQuantumMachinesMixerCalibrationRequest
|
56
57
|
| agent_pb2.DisplayResultsRequest,
|
57
58
|
) -> (
|
58
|
-
agent_pb2.
|
59
|
+
agent_pb2.RunProgramResponse
|
59
60
|
| agent_pb2.RunQuantumMachinesMixerCalibrationResponse
|
60
61
|
| agent_pb2.DisplayResultsResponse
|
61
62
|
| task_pb2.TaskErrorDetail
|
@@ -66,7 +67,7 @@ class TaskHandler(Protocol):
|
|
66
67
|
task: task_pb2.Task,
|
67
68
|
) -> any_pb2.Any | task_pb2.TaskErrorDetail:
|
68
69
|
request = (
|
69
|
-
|
70
|
+
_as_run_program_request(
|
70
71
|
task.data,
|
71
72
|
)
|
72
73
|
or _as_run_qua_calibration_request(task.data)
|
@@ -74,7 +75,7 @@ class TaskHandler(Protocol):
|
|
74
75
|
)
|
75
76
|
match request:
|
76
77
|
case (
|
77
|
-
agent_pb2.
|
78
|
+
agent_pb2.RunProgramRequest()
|
78
79
|
| agent_pb2.RunQuantumMachinesMixerCalibrationRequest()
|
79
80
|
| agent_pb2.DisplayResultsRequest()
|
80
81
|
):
|
@@ -92,10 +93,10 @@ def _as_any_message(message: Message) -> any_pb2.Any:
|
|
92
93
|
return msg
|
93
94
|
|
94
95
|
|
95
|
-
def
|
96
|
+
def _as_run_program_request(
|
96
97
|
task_result: any_pb2.Any,
|
97
|
-
) -> agent_pb2.
|
98
|
-
request = agent_pb2.
|
98
|
+
) -> agent_pb2.RunProgramRequest | None:
|
99
|
+
request = agent_pb2.RunProgramRequest()
|
99
100
|
unpacked: bool = task_result.Unpack(request) # type: ignore[reportUnknownMemberType]
|
100
101
|
if not unpacked:
|
101
102
|
return None
|
@@ -158,7 +159,10 @@ class Agent:
|
|
158
159
|
"""
|
159
160
|
host = url.split(":")[0]
|
160
161
|
if host in ["localhost", "127.0.0.1", "0.0.0.0", "::"]:
|
161
|
-
channel = grpc.insecure_channel(
|
162
|
+
channel = grpc.insecure_channel(
|
163
|
+
url,
|
164
|
+
interceptors=interceptors,
|
165
|
+
)
|
162
166
|
else:
|
163
167
|
channel = grpc.secure_channel(
|
164
168
|
url,
|
@@ -170,12 +174,13 @@ class Agent:
|
|
170
174
|
async def start_session(
|
171
175
|
self,
|
172
176
|
app: str,
|
177
|
+
metadata: GrpcMetadata,
|
173
178
|
device_name: str,
|
174
179
|
routine: str,
|
175
180
|
data: Struct | None,
|
176
181
|
) -> str:
|
177
182
|
if not self._channel:
|
178
|
-
raise RuntimeError("
|
183
|
+
raise RuntimeError("Cannot start session: agent is shutdown.")
|
179
184
|
_data = any_pb2.Any()
|
180
185
|
if data:
|
181
186
|
_data.Pack(data)
|
@@ -188,13 +193,18 @@ class Agent:
|
|
188
193
|
routine_name=routine,
|
189
194
|
data=_data,
|
190
195
|
),
|
196
|
+
metadata=metadata,
|
191
197
|
)
|
192
198
|
self._state = response.target_state
|
193
|
-
await self._resume(response)
|
199
|
+
await self._resume(response, metadata)
|
194
200
|
await self.shutdown()
|
195
201
|
return response.session_id
|
196
202
|
|
197
|
-
async def _resume(
|
203
|
+
async def _resume(
|
204
|
+
self,
|
205
|
+
response: task_pb2.AgentTasksResponse,
|
206
|
+
metadata: GrpcMetadata,
|
207
|
+
) -> None:
|
198
208
|
tasks = response.tasks
|
199
209
|
while self._state not in [
|
200
210
|
task_pb2.AGENT_STATE_SHUTDOWN_MANAGER_INITIATED,
|
@@ -228,6 +238,7 @@ class Agent:
|
|
228
238
|
results=task_results,
|
229
239
|
task_in_progress=[],
|
230
240
|
),
|
241
|
+
metadata=metadata,
|
231
242
|
)
|
232
243
|
|
233
244
|
tasks = _resp.tasks
|
@@ -13,6 +13,7 @@
|
|
13
13
|
|
14
14
|
from __future__ import annotations
|
15
15
|
|
16
|
+
import enum
|
16
17
|
from typing import overload
|
17
18
|
|
18
19
|
__all__ = [
|
@@ -21,38 +22,32 @@ __all__ = [
|
|
21
22
|
"TimeUnit",
|
22
23
|
]
|
23
24
|
|
24
|
-
import sys
|
25
|
-
from dataclasses import dataclass, field
|
26
25
|
from datetime import datetime, timedelta
|
27
26
|
from decimal import Decimal
|
28
|
-
from
|
29
|
-
from typing import Annotated, Any, Literal
|
27
|
+
from typing import Annotated, Any, Literal, Self
|
30
28
|
|
31
29
|
import numpy as np
|
32
30
|
from dateutil.parser import isoparse
|
33
|
-
from pydantic import BeforeValidator, PlainSerializer, TypeAdapter
|
34
|
-
from pydantic.dataclasses import dataclass
|
31
|
+
from pydantic import BeforeValidator, ConfigDict, Field, PlainSerializer, TypeAdapter
|
32
|
+
from pydantic.dataclasses import dataclass
|
35
33
|
|
36
|
-
|
37
|
-
from typing import Self
|
38
|
-
else:
|
39
|
-
from typing_extensions import Self
|
34
|
+
GrpcMetadata = list[tuple[str, str | bytes]]
|
40
35
|
|
41
36
|
|
42
37
|
class BaseType: ...
|
43
38
|
|
44
39
|
|
45
|
-
class FrequencyUnit(str, Enum):
|
40
|
+
class FrequencyUnit(str, enum.Enum):
|
46
41
|
Hz = "Hz"
|
47
42
|
|
48
43
|
|
49
|
-
@
|
44
|
+
@dataclass
|
50
45
|
class Frequency:
|
51
46
|
value: float
|
52
47
|
unit: FrequencyUnit # No default to guarantee clarity of units
|
53
48
|
|
54
49
|
@classmethod
|
55
|
-
def from_float_hz(cls, value: float) ->
|
50
|
+
def from_float_hz(cls, value: float) -> Self:
|
56
51
|
return cls(value, FrequencyUnit.Hz)
|
57
52
|
|
58
53
|
def to_int_hz(self) -> int:
|
@@ -120,92 +115,30 @@ class Frequency:
|
|
120
115
|
return self.__mul__(lhs)
|
121
116
|
|
122
117
|
|
123
|
-
class TimeUnit(str, Enum):
|
118
|
+
class TimeUnit(str, enum.Enum):
|
124
119
|
S = "s"
|
125
120
|
MS = "ms"
|
126
121
|
US = "us"
|
127
122
|
NS = "ns"
|
128
|
-
DT = "dt"
|
129
123
|
|
130
124
|
|
131
|
-
|
125
|
+
@dataclass
|
126
|
+
class InvalidDurationDiv:
|
127
|
+
message: str
|
128
|
+
data: Duration
|
132
129
|
|
133
130
|
|
134
|
-
@
|
135
|
-
class
|
136
|
-
|
137
|
-
A wrapper of _SiDuration and _DtDuration to manage the conversion.
|
138
|
-
"""
|
131
|
+
@dataclass
|
132
|
+
class InvalidDurationConversion:
|
133
|
+
message: str
|
139
134
|
|
140
|
-
value: int = field(compare=False)
|
141
|
-
unit: TimeUnit = field(compare=False)
|
142
|
-
dtype: Literal["duration"] = "duration"
|
143
|
-
_value: _SiDuration | _DtDuration = field(init=False, repr=False)
|
144
|
-
|
145
|
-
def __post_init__(self):
|
146
|
-
self._value = (
|
147
|
-
_DtDuration(self.value)
|
148
|
-
if self.unit == TimeUnit.DT
|
149
|
-
else _SiDuration(self.value, self.unit)
|
150
|
-
)
|
151
|
-
|
152
|
-
def is_si(self) -> bool:
|
153
|
-
return self.unit != TimeUnit.DT
|
154
|
-
|
155
|
-
@staticmethod
|
156
|
-
def from_si(d: Duration, name: str) -> Duration:
|
157
|
-
if d.unit == TimeUnit.DT:
|
158
|
-
raise TypeError(f"{name} must use SI time unit.")
|
159
|
-
return d
|
160
|
-
|
161
|
-
@staticmethod
|
162
|
-
def from_intlike(val: float, unit: TimeUnit) -> Duration:
|
163
|
-
if not np.double(val).is_integer():
|
164
|
-
raise ValueError("fail to create a Duration object. value must be an integer.")
|
165
|
-
return Duration(int(val), unit)
|
166
|
-
|
167
|
-
def convert(self, target: Duration | _SI_TIME) -> Duration:
|
168
|
-
"""
|
169
|
-
In particular, we only allow the following conversions:
|
170
|
-
|
171
|
-
# ((1000, "ms"), "s") -> (1, "s")
|
172
|
-
(_SiDuration, _SI_TIME) -> _SiDuration
|
173
135
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
# ((2, "dt"), (2, "ns")) -> (4, "ns")
|
178
|
-
(_DtDuration, _SiDuration) _> _SiDuration
|
179
|
-
"""
|
180
|
-
match self._value, getattr(target, "_value", target):
|
181
|
-
case _SiDuration(
|
182
|
-
_,
|
183
|
-
_,
|
184
|
-
), TimeUnit.S | TimeUnit.MS | TimeUnit.US | TimeUnit.NS:
|
185
|
-
converted = self._value.convert_to_si(target) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
|
186
|
-
case _SiDuration(_, _), _SiDuration(_, _):
|
187
|
-
converted = self._value.convert_to_dt(target) # type: ignore[arg-type, assignment] # pyright: ignore[reportArgumentType]
|
188
|
-
case _DtDuration(_, _), _SiDuration(_, _):
|
189
|
-
converted = self._value.convert(target) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
|
190
|
-
case _:
|
191
|
-
raise TypeError(f"cant't convert type {self.unit} to {target}")
|
192
|
-
return Duration(converted.value, converted.unit)
|
193
|
-
|
194
|
-
|
195
|
-
@dataclass(order=True)
|
196
|
-
class _DtDuration(BaseType):
|
136
|
+
@dataclass(config=ConfigDict(arbitrary_types_allowed=True))
|
137
|
+
class Duration:
|
197
138
|
value: int
|
198
|
-
unit:
|
199
|
-
|
200
|
-
|
201
|
-
return _SiDuration(self.value * target.value, target.unit)
|
202
|
-
|
203
|
-
|
204
|
-
@dataclass(order=True)
|
205
|
-
class _SiDuration(BaseType):
|
206
|
-
value: int = field(compare=False)
|
207
|
-
unit: _SI_TIME = field(compare=False)
|
208
|
-
_np_rep: np.timedelta64 = field(init=False, repr=False)
|
139
|
+
unit: TimeUnit
|
140
|
+
dtype: Literal["duration"] = "duration"
|
141
|
+
_np_rep: np.timedelta64 = Field(init=False, repr=False, exclude=True)
|
209
142
|
|
210
143
|
def __post_init__(self):
|
211
144
|
err = TypeError(
|
@@ -223,30 +156,54 @@ class _SiDuration(BaseType):
|
|
223
156
|
except ValueError as e:
|
224
157
|
raise err from e
|
225
158
|
|
226
|
-
def
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
159
|
+
def __gt__(self, other: Duration) -> bool:
|
160
|
+
return bool(self._np_rep > other._np_rep)
|
161
|
+
|
162
|
+
def __ge__(self, other: Duration) -> bool:
|
163
|
+
return bool(self._np_rep >= other._np_rep)
|
164
|
+
|
165
|
+
def __lt__(self, other: Duration) -> bool:
|
166
|
+
return bool(self._np_rep < other._np_rep)
|
167
|
+
|
168
|
+
def __le__(self, other: Duration) -> bool:
|
169
|
+
return bool(self._np_rep <= other._np_rep)
|
170
|
+
|
171
|
+
def __eq__(self, other: object) -> bool:
|
172
|
+
if isinstance(other, Duration):
|
173
|
+
return bool(self._np_rep == other._np_rep)
|
174
|
+
return False
|
175
|
+
|
176
|
+
def convert(self, unit: TimeUnit) -> Duration | InvalidDurationConversion:
|
244
177
|
val: np.float64 = self._np_rep / np.timedelta64(1, unit)
|
245
|
-
if
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
178
|
+
if val.is_integer():
|
179
|
+
return Duration(int(val), unit)
|
180
|
+
return InvalidDurationConversion(
|
181
|
+
f"fail to convert to {unit} with {self.value} {self.unit}.",
|
182
|
+
)
|
183
|
+
|
184
|
+
def to_ns(self) -> Duration:
|
185
|
+
match self.convert(TimeUnit.NS):
|
186
|
+
case InvalidDurationConversion():
|
187
|
+
raise TypeError(f"Cannot convert {self} to nanoseconds.")
|
188
|
+
case converted:
|
189
|
+
return converted
|
190
|
+
|
191
|
+
def strict_div(self, other: Duration) -> Duration | InvalidDurationDiv:
|
192
|
+
self_ns = self.to_ns()
|
193
|
+
other_ns = other.to_ns()
|
194
|
+
val = np.double(self_ns.value / other_ns.value)
|
195
|
+
if np.isclose(val, 0):
|
196
|
+
return Duration(int(val), TimeUnit.NS)
|
197
|
+
return InvalidDurationDiv(
|
198
|
+
f"{self} is not a multiple of {other}",
|
199
|
+
Duration(int(val), TimeUnit.NS),
|
200
|
+
)
|
201
|
+
|
202
|
+
@staticmethod
|
203
|
+
def from_intlike(val: float, unit: TimeUnit) -> Duration:
|
204
|
+
if not np.double(val).is_integer():
|
205
|
+
raise ValueError("Failed to create a Duration object. Value must be an integer.")
|
206
|
+
return Duration(int(val), unit)
|
250
207
|
|
251
208
|
def to_seconds(self) -> float:
|
252
209
|
return float(self._np_rep / np.timedelta64(1, "s"))
|
@@ -261,7 +218,7 @@ def ensure_frequency_hz(value: Any) -> Any:
|
|
261
218
|
case dict():
|
262
219
|
return TypeAdapter(Frequency).validate_python(value)
|
263
220
|
case _:
|
264
|
-
raise ValueError("Frequency
|
221
|
+
raise ValueError("Frequency must be numeric.")
|
265
222
|
|
266
223
|
|
267
224
|
FrequencyHzLike = Annotated[
|
@@ -280,13 +237,13 @@ def ensure_duration_ns(value: Any) -> Any:
|
|
280
237
|
case dict():
|
281
238
|
return TypeAdapter(Duration).validate_python(value)
|
282
239
|
case _:
|
283
|
-
raise ValueError("Duration
|
240
|
+
raise ValueError("Duration must be numeric.")
|
284
241
|
|
285
242
|
|
286
243
|
DurationNsLike = Annotated[Duration, BeforeValidator(ensure_duration_ns)]
|
287
244
|
|
288
245
|
|
289
|
-
@
|
246
|
+
@dataclass
|
290
247
|
class ISO8601Datetime:
|
291
248
|
value: datetime
|
292
249
|
|
@@ -351,3 +308,13 @@ ISO8601DatetimeUTCLike = Annotated[
|
|
351
308
|
BeforeValidator(_validate_iso_datetime),
|
352
309
|
PlainSerializer(_serialize_datetime),
|
353
310
|
]
|
311
|
+
|
312
|
+
|
313
|
+
class JobHistorySortOrder(enum.Enum):
|
314
|
+
CREATED_AT_DESC = 1
|
315
|
+
CREATED_AT_ASC = 2
|
316
|
+
|
317
|
+
|
318
|
+
DEFAULT_JOB_HISTORY_PAGE = 1
|
319
|
+
DEFAULT_JOB_HISTORY_PAGE_SIZE = 10
|
320
|
+
DEFAULT_JOB_HISTORY_SORT_ORDER = JobHistorySortOrder.CREATED_AT_DESC
|
@@ -13,4 +13,8 @@
|
|
13
13
|
|
14
14
|
__all__ = ["Device", "InvalidDevice", "InvalidDeviceComponent"]
|
15
15
|
|
16
|
-
from boulderopalscaleupsdk.device.device import
|
16
|
+
from boulderopalscaleupsdk.device.device import (
|
17
|
+
Device,
|
18
|
+
InvalidDevice,
|
19
|
+
InvalidDeviceComponent,
|
20
|
+
)
|
@@ -16,9 +16,13 @@ from pathlib import Path
|
|
16
16
|
from typing import Any
|
17
17
|
|
18
18
|
import yaml
|
19
|
-
from pydantic import BaseModel
|
19
|
+
from pydantic import BaseModel
|
20
20
|
|
21
|
-
from boulderopalscaleupsdk.device.controller
|
21
|
+
from boulderopalscaleupsdk.device.controller import (
|
22
|
+
ControllerInfoTypeAdapter,
|
23
|
+
QBLOXControllerInfo,
|
24
|
+
QuantumMachinesControllerInfo,
|
25
|
+
)
|
22
26
|
from boulderopalscaleupsdk.device.processor import (
|
23
27
|
SuperconductingProcessor,
|
24
28
|
SuperconductingProcessorTemplate,
|
@@ -31,11 +35,9 @@ class ProcessorArchitecture(str, Enum):
|
|
31
35
|
|
32
36
|
|
33
37
|
class DeviceInfo(BaseModel):
|
34
|
-
controller_info:
|
38
|
+
controller_info: QBLOXControllerInfo | QuantumMachinesControllerInfo
|
35
39
|
processor: SuperconductingProcessor # | OtherSDKProcessorType
|
36
40
|
|
37
|
-
model_config = ConfigDict(use_enum_values=True)
|
38
|
-
|
39
41
|
def to_dict(self) -> dict[str, Any]:
|
40
42
|
return sanitize_keys(self.model_dump(by_alias=True, mode="json"))
|
41
43
|
|
@@ -49,10 +51,13 @@ class DeviceConfigLoader:
|
|
49
51
|
|
50
52
|
layout_file = device_config_data.pop("layout_file", None)
|
51
53
|
if layout_file is None:
|
52
|
-
raise ValueError("
|
53
|
-
|
54
|
+
raise ValueError("Layout file is missing from device configuration data.")
|
55
|
+
|
56
|
+
layout_path = Path(layout_file)
|
57
|
+
if not layout_path.is_absolute():
|
58
|
+
self._validate_file_is_filename(layout_path.name)
|
59
|
+
layout_path = self.config_path.parent / layout_file
|
54
60
|
|
55
|
-
layout_path = self.config_path.parent / layout_file
|
56
61
|
device_layout_data = self._load_yaml_file(layout_path)
|
57
62
|
|
58
63
|
processed_device_config = {**device_config_data, **device_layout_data}
|
@@ -66,13 +71,13 @@ class DeviceConfigLoader:
|
|
66
71
|
device_config_dict,
|
67
72
|
)
|
68
73
|
device_info = DeviceInfo(
|
69
|
-
controller_info=
|
74
|
+
controller_info=ControllerInfoTypeAdapter.validate_python(
|
70
75
|
device_config_dict["controller_info"],
|
71
76
|
),
|
72
77
|
processor=SuperconductingProcessor.from_template(superconducting_template),
|
73
78
|
)
|
74
79
|
case other:
|
75
|
-
raise ValueError(f"Invalid or unsupported architecture {other}")
|
80
|
+
raise ValueError(f"Invalid or unsupported architecture {other}.")
|
76
81
|
return device_info
|
77
82
|
|
78
83
|
@staticmethod
|
@@ -12,21 +12,24 @@
|
|
12
12
|
# License for the specific language.
|
13
13
|
|
14
14
|
__all__ = [
|
15
|
-
"
|
16
|
-
"
|
17
|
-
"FluxPortConfig",
|
18
|
-
"OctaveConfig",
|
19
|
-
"PortRef",
|
15
|
+
"ControllerType",
|
16
|
+
"QBLOXControllerInfo",
|
20
17
|
"QuantumMachinesControllerInfo",
|
21
|
-
"ReadoutPortConfig",
|
22
18
|
]
|
23
19
|
|
24
|
-
from
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
20
|
+
from typing import TypeVar
|
21
|
+
|
22
|
+
from pydantic import TypeAdapter
|
23
|
+
|
24
|
+
from .base import ControllerType
|
25
|
+
from .qblox import QBLOXControllerInfo
|
26
|
+
from .quantum_machines import QuantumMachinesControllerInfo
|
27
|
+
|
28
|
+
ControllerInfoType = TypeVar(
|
29
|
+
"ControllerInfoType",
|
30
|
+
bound=QBLOXControllerInfo | QuantumMachinesControllerInfo,
|
31
|
+
)
|
32
|
+
|
33
|
+
ControllerInfoTypeAdapter: TypeAdapter[QBLOXControllerInfo | QuantumMachinesControllerInfo] = (
|
34
|
+
TypeAdapter(QBLOXControllerInfo | QuantumMachinesControllerInfo)
|
32
35
|
)
|
@@ -11,8 +11,17 @@
|
|
11
11
|
# distributed under the License is distributed on an "AS IS" BASIS. See the
|
12
12
|
# License for the specific language.
|
13
13
|
|
14
|
-
from pydantic import BaseModel
|
15
14
|
|
15
|
+
import enum
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
|
18
|
+
class ControllerType(str, enum.Enum):
|
19
|
+
QUANTUM_MACHINES = "quantum_machines"
|
20
|
+
QBLOX = "qblox"
|
21
|
+
|
22
|
+
|
23
|
+
class Backend(str, enum.Enum):
|
24
|
+
QUA = "QUA"
|
25
|
+
QBLOX = "QBLOX"
|
26
|
+
OPENQASM = "OPENQASM"
|
27
|
+
QBLOX_Q1ASM = "QBLOX_Q1ASM"
|