boulder-opal-scale-up-sdk 1.0.4__tar.gz → 1.0.6__tar.gz
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.4 → boulder_opal_scale_up_sdk-1.0.6}/PKG-INFO +1 -1
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/agent/worker.py +36 -4
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/common/dtypes.py +41 -2
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/constants.py +15 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/controller/qblox.py +94 -26
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/controller/quantum_machines.py +86 -17
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/device.py +5 -1
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/processor/common.py +3 -3
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/processor/superconducting_processor.py +23 -3
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/errors.py +21 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/__init__.py +16 -2
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/chi01_scan.py +9 -7
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/cz_spectroscopy_by_bias.py +84 -0
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/drag_leakage_calibration.py +66 -0
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/fine_amplitude_calibration.py +54 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/power_rabi.py +10 -7
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/power_rabi_ef.py +10 -9
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/ramsey.py +9 -7
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/ramsey_ef.py +62 -0
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/readout_classifier_calibration.py → boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/readout_classifier.py +16 -6
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/readout_optimization.py +57 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/resonator_spectroscopy.py +9 -7
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py +12 -11
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py +10 -9
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/t1.py +8 -6
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/t2.py +12 -10
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/t2_echo.py +12 -10
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/transmon_anharmonicity.py +13 -12
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/transmon_spectroscopy.py +10 -8
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/voltage_bias_fine_tune.py +58 -0
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/experiments/zz_ramsey.py +59 -0
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/grpc_interceptors/error.py +318 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/plotting/dtypes.py +10 -8
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/agent_pb2.py +9 -3
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi +14 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py +34 -0
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/protobuf/v1/device_pb2.py +97 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/device_pb2.pyi +67 -41
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py +100 -66
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/protobuf/v1/job_pb2.py +47 -0
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/protobuf/v1/job_pb2.pyi +54 -0
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/protobuf/v1/job_pb2_grpc.py +138 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/routines/__init__.py +10 -1
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/routines/transmon_retuning.py → boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/routines/one_qubit_calibration.py +10 -5
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/routines/resonator_mapping.py +1 -1
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/routines/transmon_coherence.py +34 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/routines/transmon_discovery.py +5 -9
- boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/routines/transmon_retuning.py +41 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/third_party/quantum_machines/__init__.py +1 -1
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/third_party/quantum_machines/config.py +51 -48
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/pyproject.toml +2 -4
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/protobuf/v1/device_pb2.py +0 -89
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/stubs/dtypes.py +0 -47
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/stubs/maps.py +0 -18
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/utils/__init__.py +0 -12
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/LICENSE +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/README.md +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/agent/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/common/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/common/typeclasses.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/common.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/config_loader.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/controller/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/controller/base.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/controller/resolver.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/defcal.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/device/processor/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/common.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/experiments/waveforms.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/grpc_interceptors/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/grpc_interceptors/auth.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/plotting/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/task_pb2.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/task_pb2.pyi +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/protobuf/v1/task_pb2_grpc.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/py.typed +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/routines/common.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/third_party/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/third_party/quantum_machines/constants.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/stubs → boulder_opal_scale_up_sdk-1.0.6/boulderopalscaleupsdk/utils}/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.4 → boulder_opal_scale_up_sdk-1.0.6}/boulderopalscaleupsdk/utils/serial_utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: boulder-opal-scale-up-sdk
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.6
|
4
4
|
Summary: Q-CTRL Boulder Opal Scale Up Python SDK
|
5
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
|
@@ -17,6 +17,7 @@ Contains logic for agent worker.
|
|
17
17
|
"""
|
18
18
|
|
19
19
|
import logging
|
20
|
+
import os
|
20
21
|
from abc import abstractmethod
|
21
22
|
from typing import Protocol
|
22
23
|
|
@@ -28,6 +29,10 @@ from grpc import ssl_channel_credentials
|
|
28
29
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
29
30
|
|
30
31
|
from boulderopalscaleupsdk.common.dtypes import GrpcMetadata, JobId
|
32
|
+
from boulderopalscaleupsdk.constants import (
|
33
|
+
DEFAULT_GRPC_MAX_RECEIVE_MESSAGE_LENGTH,
|
34
|
+
DEFAULT_GRPC_MAX_SEND_MESSAGE_LENGTH,
|
35
|
+
)
|
31
36
|
from boulderopalscaleupsdk.protobuf.v1 import agent_pb2, task_pb2, task_pb2_grpc
|
32
37
|
|
33
38
|
LOG = logging.getLogger(__name__)
|
@@ -54,11 +59,13 @@ class TaskHandler(Protocol):
|
|
54
59
|
self,
|
55
60
|
request: agent_pb2.RunProgramRequest
|
56
61
|
| agent_pb2.RunQuantumMachinesMixerCalibrationRequest
|
57
|
-
| agent_pb2.DisplayResultsRequest
|
62
|
+
| agent_pb2.DisplayResultsRequest
|
63
|
+
| agent_pb2.AskRequest,
|
58
64
|
) -> (
|
59
65
|
agent_pb2.RunProgramResponse
|
60
66
|
| agent_pb2.RunQuantumMachinesMixerCalibrationResponse
|
61
67
|
| agent_pb2.DisplayResultsResponse
|
68
|
+
| agent_pb2.AskResponse
|
62
69
|
| task_pb2.TaskErrorDetail
|
63
70
|
): ...
|
64
71
|
|
@@ -67,9 +74,8 @@ class TaskHandler(Protocol):
|
|
67
74
|
task: task_pb2.Task,
|
68
75
|
) -> any_pb2.Any | task_pb2.TaskErrorDetail:
|
69
76
|
request = (
|
70
|
-
_as_run_program_request(
|
71
|
-
|
72
|
-
)
|
77
|
+
_as_run_program_request(task.data)
|
78
|
+
or _as_ask_request(task.data)
|
73
79
|
or _as_run_qua_calibration_request(task.data)
|
74
80
|
or _as_display_results_request(task.data)
|
75
81
|
)
|
@@ -78,6 +84,7 @@ class TaskHandler(Protocol):
|
|
78
84
|
agent_pb2.RunProgramRequest()
|
79
85
|
| agent_pb2.RunQuantumMachinesMixerCalibrationRequest()
|
80
86
|
| agent_pb2.DisplayResultsRequest()
|
87
|
+
| agent_pb2.AskRequest()
|
81
88
|
):
|
82
89
|
return _as_any_message(await self.handle(request))
|
83
90
|
case None:
|
@@ -104,6 +111,17 @@ def _as_run_program_request(
|
|
104
111
|
return request
|
105
112
|
|
106
113
|
|
114
|
+
def _as_ask_request(
|
115
|
+
task_result: any_pb2.Any,
|
116
|
+
) -> agent_pb2.AskRequest | None:
|
117
|
+
request = agent_pb2.AskRequest()
|
118
|
+
unpacked: bool = task_result.Unpack(request) # type: ignore[reportUnknownMemberType]
|
119
|
+
if not unpacked:
|
120
|
+
return None
|
121
|
+
|
122
|
+
return request
|
123
|
+
|
124
|
+
|
107
125
|
def _as_run_qua_calibration_request(
|
108
126
|
task_result: any_pb2.Any,
|
109
127
|
) -> agent_pb2.RunQuantumMachinesMixerCalibrationRequest | None:
|
@@ -158,16 +176,30 @@ class Agent:
|
|
158
176
|
Create a gRPC channel.
|
159
177
|
"""
|
160
178
|
host = url.split(":")[0]
|
179
|
+
grpc_max_send_message_length = os.getenv(
|
180
|
+
"GRPC_MAX_SEND_MESSAGE_LENGTH",
|
181
|
+
DEFAULT_GRPC_MAX_SEND_MESSAGE_LENGTH,
|
182
|
+
)
|
183
|
+
grpc_max_receive_message_length = os.getenv(
|
184
|
+
"GRPC_MAX_RECEIVE_MESSAGE_LENGTH",
|
185
|
+
DEFAULT_GRPC_MAX_RECEIVE_MESSAGE_LENGTH,
|
186
|
+
)
|
187
|
+
options = [
|
188
|
+
("grpc.max_send_message_length", grpc_max_send_message_length),
|
189
|
+
("grpc.max_receive_message_length", grpc_max_receive_message_length),
|
190
|
+
]
|
161
191
|
if host in ["localhost", "127.0.0.1", "0.0.0.0", "::"]:
|
162
192
|
channel = grpc.insecure_channel(
|
163
193
|
url,
|
164
194
|
interceptors=interceptors,
|
195
|
+
options=options,
|
165
196
|
)
|
166
197
|
else:
|
167
198
|
channel = grpc.secure_channel(
|
168
199
|
url,
|
169
200
|
credentials=ssl_channel_credentials(),
|
170
201
|
interceptors=interceptors,
|
202
|
+
options=options,
|
171
203
|
)
|
172
204
|
return channel
|
173
205
|
|
@@ -14,7 +14,9 @@
|
|
14
14
|
from __future__ import annotations
|
15
15
|
|
16
16
|
import enum
|
17
|
-
from typing import overload
|
17
|
+
from typing import TypeAlias, overload
|
18
|
+
|
19
|
+
import grpc
|
18
20
|
|
19
21
|
__all__ = [
|
20
22
|
"Duration",
|
@@ -31,6 +33,8 @@ from dateutil.parser import isoparse
|
|
31
33
|
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, PlainSerializer, TypeAdapter
|
32
34
|
from pydantic.dataclasses import dataclass
|
33
35
|
|
36
|
+
from boulderopalscaleupsdk.plotting.dtypes import Plot # noqa: TC001
|
37
|
+
|
34
38
|
GrpcMetadata = list[tuple[str, str | bytes]]
|
35
39
|
|
36
40
|
JobId = str
|
@@ -316,6 +320,14 @@ class JobHistorySortOrder(enum.Enum):
|
|
316
320
|
CREATED_AT_DESC = 1
|
317
321
|
CREATED_AT_ASC = 2
|
318
322
|
|
323
|
+
@staticmethod
|
324
|
+
def from_int(value: int) -> JobHistorySortOrder | None:
|
325
|
+
match value:
|
326
|
+
case 1 | 2:
|
327
|
+
return JobHistorySortOrder(value)
|
328
|
+
case _:
|
329
|
+
return None
|
330
|
+
|
319
331
|
|
320
332
|
class JobSummary(BaseModel):
|
321
333
|
id: str
|
@@ -325,9 +337,36 @@ class JobSummary(BaseModel):
|
|
325
337
|
created_at: ISO8601DatetimeUTCLike
|
326
338
|
|
327
339
|
def __str__(self):
|
328
|
-
return f
|
340
|
+
return f'JobSummary(name="{self.name}", id="{self.id}")'
|
341
|
+
|
342
|
+
|
343
|
+
class JobDataEntry(BaseModel):
|
344
|
+
message: str
|
345
|
+
created_at: ISO8601DatetimeUTCLike
|
346
|
+
dt: str
|
347
|
+
elapsed_time: str
|
348
|
+
plots: list[Plot] = []
|
349
|
+
|
350
|
+
class Config:
|
351
|
+
extra = "allow"
|
352
|
+
|
353
|
+
|
354
|
+
class JobData(BaseModel):
|
355
|
+
id: str
|
356
|
+
name: str
|
357
|
+
session_id: str
|
358
|
+
created_at: ISO8601DatetimeUTCLike
|
359
|
+
device_name: str
|
360
|
+
data: list[JobDataEntry]
|
329
361
|
|
330
362
|
|
331
363
|
DEFAULT_JOB_HISTORY_PAGE = 1
|
332
364
|
DEFAULT_JOB_HISTORY_PAGE_SIZE = 10
|
333
365
|
DEFAULT_JOB_HISTORY_SORT_ORDER = JobHistorySortOrder.CREATED_AT_DESC
|
366
|
+
|
367
|
+
SyncClientInterceptor: TypeAlias = (
|
368
|
+
grpc.UnaryUnaryClientInterceptor
|
369
|
+
| grpc.UnaryStreamClientInterceptor
|
370
|
+
| grpc.StreamUnaryClientInterceptor
|
371
|
+
| grpc.StreamStreamClientInterceptor
|
372
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Copyright 2025 Q-CTRL. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Q-CTRL Terms of service (the "License"). Unauthorized
|
4
|
+
# copying or use of this file, via any medium, is strictly prohibited.
|
5
|
+
# Proprietary and confidential. You may not use this file except in compliance
|
6
|
+
# with the License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# https://q-ctrl.com/terms
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS. See the
|
12
|
+
# License for the specific language.
|
13
|
+
|
14
|
+
DEFAULT_GRPC_MAX_RECEIVE_MESSAGE_LENGTH = 1024 * 1024 * 50 # 50MB
|
15
|
+
DEFAULT_GRPC_MAX_SEND_MESSAGE_LENGTH = 1024 * 1024 * 50 # 50MB
|
@@ -257,18 +257,18 @@ ChannelType = RealChannel | ComplexChannel
|
|
257
257
|
# ==================================================================================================
|
258
258
|
# Controller information
|
259
259
|
# ==================================================================================================
|
260
|
-
class
|
260
|
+
class PortConnection(BaseModel): # pragma: no cover
|
261
261
|
"""
|
262
|
-
The connections involved for a
|
262
|
+
The connections involved for a QPU port.
|
263
263
|
|
264
264
|
Attributes
|
265
265
|
----------
|
266
266
|
ch_out: ChannelType
|
267
|
-
The output channel that will signal towards the
|
268
|
-
ch_in: ChannelType, optional
|
269
|
-
The input channel from which signals will be acquired from the
|
270
|
-
not all modules support acquisitions. If an input channel is specified, it must be
|
271
|
-
on the same module as the output channel.
|
267
|
+
The output channel that will signal towards the QPU port
|
268
|
+
ch_in: ChannelType or None, optional
|
269
|
+
The input channel from which signals will be acquired from the QPU port. This is optional,
|
270
|
+
as not all modules support acquisitions. If an input channel is specified, it must be
|
271
|
+
located on the same module as the output channel.
|
272
272
|
|
273
273
|
Notes
|
274
274
|
-----
|
@@ -276,20 +276,21 @@ class ElementConnection(BaseModel): # pragma: no cover
|
|
276
276
|
direction is outwards from the control stack. The following diagram depicts a simple setup with
|
277
277
|
the arrows indicating a control channel.
|
278
278
|
|
279
|
-
┌────────┐
|
280
|
-
│ │─── out ──►│
|
281
|
-
│ QBLOX │
|
282
|
-
│ Stack │
|
283
|
-
│ │─── out ──►│
|
284
|
-
│ │◄── in ────│
|
285
|
-
└────────┘
|
279
|
+
┌────────┐ ┌───────────────┐
|
280
|
+
│ │─── out ──►│ Port: p_xy1 │
|
281
|
+
│ QBLOX │ └───────────────┘
|
282
|
+
│ Stack │ ┌───────────────┐
|
283
|
+
│ │─── out ──►│ Port: p_flrr0 │
|
284
|
+
│ │◄── in ────│ │
|
285
|
+
└────────┘ └───────────────┘
|
286
|
+
QPU fridge
|
286
287
|
"""
|
287
288
|
|
288
289
|
ch_out: ChannelType
|
289
290
|
ch_in: ChannelType | None = None
|
290
291
|
|
291
292
|
@model_validator(mode="after")
|
292
|
-
def validate_channels(self) -> "
|
293
|
+
def validate_channels(self) -> "PortConnection":
|
293
294
|
if self.ch_in is not None and self.ch_in.module != self.ch_out.module:
|
294
295
|
raise ValueError("I/O channels for an element must be on the same module.")
|
295
296
|
return self
|
@@ -309,38 +310,102 @@ class QBLOXControllerInfo(BaseModel): # pragma: no cover
|
|
309
310
|
The type of controller, which is always `ControllerType.QBLOX` for this class.
|
310
311
|
modules: dict[ModuleAddrType, ModuleType]
|
311
312
|
The modules connected to the QBLOX stack.
|
312
|
-
|
313
|
-
The
|
313
|
+
port_config: dict[str, PortConnection]
|
314
|
+
The dictionary of ports with their types and addresses.
|
314
315
|
"""
|
315
316
|
|
316
317
|
controller_type: Literal[ControllerType.QBLOX] = ControllerType.QBLOX
|
317
318
|
modules: dict[ModuleAddrType, ModuleType]
|
318
|
-
|
319
|
+
port_config: dict[str, PortConnection]
|
319
320
|
|
320
321
|
|
321
322
|
# ==================================================================================================
|
322
323
|
# Instrument management
|
323
324
|
# ==================================================================================================
|
324
325
|
class SequencerParams(BaseModel):
|
325
|
-
nco_freq: float | None = Field(default=None
|
326
|
+
nco_freq: float | None = Field(default=None)
|
326
327
|
gain_awg_path0: float | None = Field(default=None, ge=-1.0, le=1.0)
|
327
328
|
offset_awg_path0: float | None = Field(default=None, ge=-1.0, le=1.0)
|
328
329
|
gain_awg_path1: float | None = Field(default=None, ge=-1.0, le=1.0)
|
329
330
|
offset_awg_path1: float | None = Field(default=None, ge=-1.0, le=1.0)
|
330
331
|
marker_ovr_en: bool | None = Field(default=None)
|
331
|
-
marker_ovr_value: int | None = Field(default=None)
|
332
|
+
marker_ovr_value: int | None = Field(default=None, ge=0, le=15)
|
332
333
|
mod_en_awg: bool | None = Field(default=None)
|
333
334
|
demod_en_acq: bool | None = Field(default=None)
|
334
335
|
sync_en: bool | None = Field(default=None)
|
335
336
|
nco_prop_delay_comp_en: bool | None = Field(default=True)
|
336
|
-
integration_length_acq: int | None = Field(default=None)
|
337
|
+
integration_length_acq: int | None = Field(default=None, ge=4, le=16777212, multiple_of=4)
|
337
338
|
|
338
339
|
|
339
|
-
class
|
340
|
-
|
341
|
-
|
340
|
+
class QcmParams(BaseModel):
|
341
|
+
out0_offset: float | None = Field(default=None, ge=-2.5, le=2.5)
|
342
|
+
out1_offset: float | None = Field(default=None, ge=-2.5, le=2.5)
|
343
|
+
out2_offset: float | None = Field(default=None, ge=-2.5, le=2.5)
|
344
|
+
out3_offset: float | None = Field(default=None, ge=-2.5, le=2.5)
|
345
|
+
|
346
|
+
def update(self, other: Self) -> None:
|
347
|
+
if self == other:
|
348
|
+
return # Nothing to do
|
349
|
+
|
350
|
+
self.out0_offset = pick_only_one_or_raise(self.out0_offset, other.out0_offset)
|
351
|
+
self.out1_offset = pick_only_one_or_raise(self.out1_offset, other.out1_offset)
|
352
|
+
self.out2_offset = pick_only_one_or_raise(self.out2_offset, other.out2_offset)
|
353
|
+
self.out3_offset = pick_only_one_or_raise(self.out3_offset, other.out3_offset)
|
354
|
+
|
355
|
+
|
356
|
+
class QcmRfParams(BaseModel):
|
357
|
+
out0_att: int | None = Field(default=None, ge=0, le=60, multiple_of=2)
|
358
|
+
out1_att: int | None = Field(default=None, ge=0, le=60, multiple_of=2)
|
359
|
+
|
342
360
|
out0_lo_freq: float | None = Field(default=None, gt=0)
|
343
361
|
out0_lo_en: bool | None = Field(default=None)
|
362
|
+
out1_lo_freq: float | None = Field(default=None, gt=0)
|
363
|
+
out1_lo_en: bool | None = Field(default=None)
|
364
|
+
|
365
|
+
def update(self, other: Self) -> None:
|
366
|
+
if self == other:
|
367
|
+
return # Nothing to do
|
368
|
+
|
369
|
+
self.out0_att = pick_only_one_or_raise(self.out0_att, other.out0_att)
|
370
|
+
self.out1_att = pick_only_one_or_raise(self.out1_att, other.out1_att)
|
371
|
+
self.out0_lo_freq = pick_only_one_or_raise(self.out0_lo_freq, other.out0_lo_freq)
|
372
|
+
self.out0_lo_en = pick_only_one_or_raise(self.out0_lo_en, other.out0_lo_en)
|
373
|
+
self.out1_lo_freq = pick_only_one_or_raise(self.out1_lo_freq, other.out1_lo_freq)
|
374
|
+
self.out1_lo_en = pick_only_one_or_raise(self.out1_lo_en, other.out1_lo_en)
|
375
|
+
|
376
|
+
|
377
|
+
class QrmRfParams(BaseModel):
|
378
|
+
out0_att: int | None = Field(default=None, ge=0, le=60, multiple_of=2)
|
379
|
+
|
380
|
+
out0_in0_lo_freq: float | None = Field(default=None, gt=0)
|
381
|
+
out0_in0_lo_en: bool | None = Field(default=None)
|
382
|
+
|
383
|
+
def update(self, other: Self) -> None:
|
384
|
+
if self == other:
|
385
|
+
return # Nothing to do
|
386
|
+
|
387
|
+
self.out0_att = pick_only_one_or_raise(self.out0_att, other.out0_att)
|
388
|
+
self.out0_in0_lo_freq = pick_only_one_or_raise(
|
389
|
+
self.out0_in0_lo_freq,
|
390
|
+
other.out0_in0_lo_freq,
|
391
|
+
)
|
392
|
+
self.out0_in0_lo_en = pick_only_one_or_raise(self.out0_in0_lo_en, other.out0_in0_lo_en)
|
393
|
+
|
394
|
+
|
395
|
+
ModuleParams = QcmParams | QcmRfParams | QrmRfParams
|
396
|
+
|
397
|
+
|
398
|
+
T0 = TypeVar("T0")
|
399
|
+
|
400
|
+
|
401
|
+
def pick_only_one_or_raise(a: T0 | None, b: T0 | None) -> T0 | None:
|
402
|
+
if a == b:
|
403
|
+
return a
|
404
|
+
if a is None:
|
405
|
+
return b
|
406
|
+
if b is None:
|
407
|
+
return a
|
408
|
+
raise ValueError(f"Cannot resolve conflict between given parameters {a} and {b}!")
|
344
409
|
|
345
410
|
|
346
411
|
# ==================================================================================================
|
@@ -414,7 +479,7 @@ class PreparedSequenceProgram(BaseModel): # pragma: no cover
|
|
414
479
|
|
415
480
|
|
416
481
|
class PreparedModule(BaseModel):
|
417
|
-
params: ModuleParams
|
482
|
+
params: ModuleParams
|
418
483
|
|
419
484
|
|
420
485
|
class PreparedProgram(BaseModel):
|
@@ -661,7 +726,10 @@ class BitStrideArrayEncoding:
|
|
661
726
|
encoded_shape = tuple(
|
662
727
|
BitStrideArrayEncoding._round_power2_32bit(dim) for dim in desired_shape
|
663
728
|
)
|
664
|
-
|
729
|
+
# Right-most (most nested) dimension takes least significant bits!
|
730
|
+
exponents = tuple(int(math.log2(dim)) for dim in encoded_shape)
|
731
|
+
n_bits = sum(exponents)
|
732
|
+
bit_stride = tuple(n_bits - sum(exponents[: idx + 1]) for idx in range(len(exponents)))
|
665
733
|
return cls(
|
666
734
|
desired_shape=desired_shape,
|
667
735
|
encoded_shape=encoded_shape,
|
@@ -11,10 +11,11 @@
|
|
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 typing import Literal, Self
|
14
|
+
from typing import Annotated, Any, Literal, Self
|
15
15
|
|
16
16
|
from pydantic import (
|
17
17
|
BaseModel,
|
18
|
+
BeforeValidator,
|
18
19
|
ConfigDict,
|
19
20
|
Field,
|
20
21
|
field_serializer,
|
@@ -31,10 +32,78 @@ from boulderopalscaleupsdk.third_party.quantum_machines.constants import (
|
|
31
32
|
QUA_CLOCK_CYCLE,
|
32
33
|
)
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
|
36
|
+
class OPXPortMapping(BaseModel):
|
37
|
+
type: Literal["controller"] = "controller"
|
38
|
+
controller_id: str
|
39
|
+
port_id: int
|
40
|
+
|
41
|
+
def to_native_opx_port_type(self) -> qm_config.NativeOPXPortType:
|
42
|
+
return qm_config.NativeOPXPortType(
|
43
|
+
controller_id=self.controller_id,
|
44
|
+
port_id=self.port_id,
|
45
|
+
)
|
46
|
+
|
47
|
+
@staticmethod
|
48
|
+
def from_native_opx_port_type(
|
49
|
+
port_type: qm_config.NativeOPXPortType,
|
50
|
+
) -> "OPXPortMapping":
|
51
|
+
return OPXPortMapping(
|
52
|
+
controller_id=port_type.controller_id,
|
53
|
+
port_id=port_type.port_id,
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
def ensure_opx_port_mapping(value: Any) -> Any:
|
58
|
+
match value:
|
59
|
+
case OPXPortMapping():
|
60
|
+
return value
|
61
|
+
case list([str(controller_id), float(port_id)]):
|
62
|
+
if not port_id.is_integer():
|
63
|
+
raise ValueError("port_id must be an integer")
|
64
|
+
return OPXPortMapping(controller_id=controller_id, port_id=int(port_id))
|
65
|
+
case list([str(controller_id), int(port_id)]):
|
66
|
+
return OPXPortMapping(controller_id=controller_id, port_id=port_id)
|
67
|
+
case tuple((str(controller_id), float(port_id))):
|
68
|
+
if not port_id.is_integer():
|
69
|
+
raise ValueError("port_id must be an integer")
|
70
|
+
return OPXPortMapping(controller_id=controller_id, port_id=int(port_id))
|
71
|
+
case tuple((str(controller_id), int(port_id))):
|
72
|
+
return OPXPortMapping(controller_id=controller_id, port_id=port_id)
|
73
|
+
case dict({"type": "controller", "controller_id": str(), "port_id": int() | float()}):
|
74
|
+
return OPXPortMapping.model_validate(value)
|
75
|
+
case _:
|
76
|
+
raise ValueError("Invalid Port Mapping")
|
77
|
+
|
78
|
+
|
79
|
+
OPXPortMappingLike = Annotated[
|
80
|
+
OPXPortMapping,
|
81
|
+
BeforeValidator(ensure_opx_port_mapping),
|
82
|
+
]
|
83
|
+
|
84
|
+
|
85
|
+
class OPX1000PortMapping(BaseModel):
|
86
|
+
type: Literal["frontend_module"] = "frontend_module"
|
87
|
+
controller_id: str
|
88
|
+
fem_id: int
|
89
|
+
port_id: int
|
90
|
+
|
91
|
+
def to_native_opx1000_port_type(self) -> qm_config.NativeOPX1000PortType:
|
92
|
+
return qm_config.NativeOPX1000PortType(
|
93
|
+
controller_id=self.controller_id,
|
94
|
+
fem_id=self.fem_id,
|
95
|
+
port_id=self.port_id,
|
96
|
+
)
|
97
|
+
|
98
|
+
@staticmethod
|
99
|
+
def from_native_opx1000_port_type(
|
100
|
+
port_type: qm_config.NativeOPX1000PortType,
|
101
|
+
) -> "OPX1000PortMapping":
|
102
|
+
return OPX1000PortMapping(
|
103
|
+
controller_id=port_type.controller_id,
|
104
|
+
fem_id=port_type.fem_id,
|
105
|
+
port_id=port_type.port_id,
|
106
|
+
)
|
38
107
|
|
39
108
|
|
40
109
|
class QuaProgram(BaseModel):
|
@@ -74,17 +143,17 @@ class OctaveConfig(qm_config.OctaveConfig121):
|
|
74
143
|
|
75
144
|
class DrivePortConfig(BaseModel):
|
76
145
|
port_type: Literal["drive"] = "drive"
|
77
|
-
port_mapping:
|
146
|
+
port_mapping: OPXPortMappingLike | OPX1000PortMapping
|
78
147
|
|
79
148
|
|
80
149
|
class FluxPortConfig(BaseModel):
|
81
150
|
port_type: Literal["flux"] = "flux"
|
82
|
-
port_mapping:
|
151
|
+
port_mapping: OPXPortMappingLike | OPX1000PortMapping
|
83
152
|
|
84
153
|
|
85
154
|
class ReadoutPortConfig(BaseModel):
|
86
155
|
port_type: Literal["readout"] = "readout"
|
87
|
-
port_mapping:
|
156
|
+
port_mapping: OPXPortMappingLike | OPX1000PortMapping
|
88
157
|
time_of_flight: DurationNsLike
|
89
158
|
smearing: DurationNsLike = Field(default=Duration(0, TimeUnit.NS))
|
90
159
|
|
@@ -119,22 +188,22 @@ class QuantumMachinesControllerInfo(BaseModel):
|
|
119
188
|
----------
|
120
189
|
controller_type : Literal[ControllerType.QUANTUM_MACHINES]
|
121
190
|
The type of controller, which is always `ControllerType.QUANTUM_MACHINES`.
|
122
|
-
controllers : dict[
|
123
|
-
A dictionary mapping controller references to their respective configurations.
|
191
|
+
controllers : dict[str, OPXControllerConfig | OPX1000ControllerConfig]
|
192
|
+
A dictionary mapping controller references (str) to their respective configurations.
|
124
193
|
The configurations can be either OPXControllerConfig or OPX1000ControllerConfig.
|
125
194
|
Derived from OPX Config.
|
126
|
-
octaves : dict[
|
127
|
-
A dictionary mapping octave references to their respective configurations.
|
195
|
+
octaves : dict[str, OctaveConfig]
|
196
|
+
A dictionary mapping octave references (str) to their respective configurations.
|
128
197
|
Derived from OPX Config.
|
129
|
-
port_config : dict[
|
130
|
-
A dictionary mapping port references to their respective port configurations.
|
198
|
+
port_config : dict[str, DrivePortConfig | FluxPortConfig | ReadoutPortConfig]
|
199
|
+
A dictionary mapping port references (str) to their respective port configurations.
|
131
200
|
The configurations can be DrivePortConfig, FluxPortConfig, or ReadoutPortConfig.
|
132
201
|
Not derived from OPX Config, this is our custom config.
|
133
202
|
"""
|
134
203
|
|
135
204
|
controller_type: Literal[ControllerType.QUANTUM_MACHINES] = ControllerType.QUANTUM_MACHINES
|
136
|
-
controllers: dict[
|
205
|
+
controllers: dict[str, OPXControllerConfig | OPX1000ControllerConfig] = Field(
|
137
206
|
default={},
|
138
207
|
)
|
139
|
-
octaves: dict[
|
140
|
-
port_config: dict[
|
208
|
+
octaves: dict[str, OctaveConfig] = Field(default={})
|
209
|
+
port_config: dict[str, DrivePortConfig | FluxPortConfig | ReadoutPortConfig]
|
@@ -29,6 +29,9 @@ class EmptyDefCalData:
|
|
29
29
|
message: str
|
30
30
|
|
31
31
|
|
32
|
+
DeviceName = str
|
33
|
+
|
34
|
+
|
32
35
|
@dataclass
|
33
36
|
class DeviceData:
|
34
37
|
# TODO: retire DeviceInfo the next SDK release
|
@@ -58,6 +61,7 @@ class DeviceSummary(BaseModel):
|
|
58
61
|
provider: str
|
59
62
|
updated_at: ISO8601DatetimeUTCLike
|
60
63
|
created_at: ISO8601DatetimeUTCLike
|
64
|
+
copied_from: DeviceName | None = None
|
61
65
|
|
62
66
|
def __str__(self):
|
63
|
-
return f
|
67
|
+
return f'DeviceSummary(name="{self.name}", id="{self.id}")'
|
@@ -191,8 +191,8 @@ class ComponentParameter(Generic[T]):
|
|
191
191
|
self.err_minus = None
|
192
192
|
self.err_plus = None
|
193
193
|
else:
|
194
|
-
self.err_minus = from_float(to_float(std)
|
195
|
-
self.err_plus = from_float(to_float(std)
|
194
|
+
self.err_minus = from_float(to_float(std))
|
195
|
+
self.err_plus = from_float(to_float(std))
|
196
196
|
if (
|
197
197
|
not isinstance(calibration_thresholds, EllipsisType)
|
198
198
|
and self.err_minus is not None
|
@@ -259,7 +259,7 @@ def _get_calibration_status_from_thresholds(
|
|
259
259
|
confidence_interval: float,
|
260
260
|
calibration_thresholds: CalibrationThresholds,
|
261
261
|
) -> CalibrationStatusT:
|
262
|
-
relative_uncertainty = abs(confidence_interval / value)
|
262
|
+
relative_uncertainty = 0.5 * abs(confidence_interval / value)
|
263
263
|
|
264
264
|
if relative_uncertainty < calibration_thresholds.good:
|
265
265
|
return "good"
|
@@ -161,7 +161,27 @@ class Feedline(Component[Literal["feedline"]]):
|
|
161
161
|
traits: list = Field(default=[])
|
162
162
|
|
163
163
|
|
164
|
-
|
164
|
+
class TWPA(Component[Literal["twpa"]]):
|
165
|
+
dtype: Literal["twpa"] = "twpa"
|
166
|
+
traits: list = Field(default=[])
|
167
|
+
|
168
|
+
impedance: FloatComponentParameter = Field(
|
169
|
+
default=ComponentParameter(value=0.0),
|
170
|
+
json_schema_extra={"display": {"label": "impedance", "unit": "Ohm", "scale": 1}},
|
171
|
+
)
|
172
|
+
|
173
|
+
# Tunable TWPA parameters
|
174
|
+
pump_power: FloatComponentParameter = Field(
|
175
|
+
default=ComponentParameter(value=0.0),
|
176
|
+
json_schema_extra={"display": {"label": "pump_power", "unit": "dBm", "scale": 1}},
|
177
|
+
)
|
178
|
+
pump_freq: FloatComponentParameter = Field(
|
179
|
+
default=ComponentParameter(value=(0.0)),
|
180
|
+
json_schema_extra={"display": {"label": "pump_freq", "unit": "MHz", "scale": 1e-6}},
|
181
|
+
)
|
182
|
+
|
183
|
+
|
184
|
+
SuperconductingComponentType = Transmon | Resonator | Port | Feedline | Filter | Coupler | TWPA
|
165
185
|
|
166
186
|
|
167
187
|
class Edge(BaseModel):
|
@@ -176,14 +196,14 @@ class TemplateParam(BaseModel):
|
|
176
196
|
|
177
197
|
|
178
198
|
class ProcessorTemplate(BaseModel):
|
179
|
-
elements: dict[str,
|
199
|
+
elements: dict[str, SuperconductingComponentType]
|
180
200
|
edges: list[Edge]
|
181
201
|
|
182
202
|
|
183
203
|
class SuperconductingProcessorTemplate(BaseModel):
|
184
204
|
build: list[TemplateParam]
|
185
205
|
templates: dict[str, ProcessorTemplate]
|
186
|
-
device_parameters: dict[str,
|
206
|
+
device_parameters: dict[str, SuperconductingComponentType] = Field(
|
187
207
|
default={},
|
188
208
|
)
|
189
209
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright 2025 Q-CTRL. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Q-CTRL Terms of service (the "License"). Unauthorized
|
4
|
+
# copying or use of this file, via any medium, is strictly prohibited.
|
5
|
+
# Proprietary and confidential. You may not use this file except in compliance
|
6
|
+
# with the License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# https://q-ctrl.com/terms
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS. See the
|
12
|
+
# License for the specific language.
|
13
|
+
|
14
|
+
|
15
|
+
class ScaleUpServerError(Exception):
|
16
|
+
"""
|
17
|
+
Exception raised by client based on server behavior.
|
18
|
+
"""
|
19
|
+
|
20
|
+
def __init__(self, message: str):
|
21
|
+
super().__init__(message)
|