boulder-opal-scale-up-sdk 1.0.2__tar.gz → 1.0.4__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.2 → boulder_opal_scale_up_sdk-1.0.4}/PKG-INFO +1 -1
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/agent/worker.py +10 -5
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/common/dtypes.py +14 -1
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/common/typeclasses.py +22 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/common.py +0 -3
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/config_loader.py +1 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/qblox.py +129 -0
- boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/device/__init__.py → boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/device/defcal.py +9 -6
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/device/device.py +63 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/processor/__init__.py +0 -2
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/processor/common.py +37 -20
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/processor/superconducting_processor.py +28 -22
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/__init__.py +20 -2
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/chi01_scan.py +14 -8
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/common.py +15 -16
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/power_rabi.py +16 -12
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/power_rabi_ef.py +66 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/ramsey.py +15 -17
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/readout_classifier_calibration.py +44 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/resonator_spectroscopy.py +14 -17
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py +17 -20
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py +12 -12
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/t1.py +46 -0
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/t2.py +49 -0
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/t2_echo.py +49 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/transmon_anharmonicity.py +29 -26
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/transmon_spectroscopy.py +16 -21
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/waveforms.py +63 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/plotting/dtypes.py +1 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/device_pb2.py +20 -20
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/device_pb2.pyi +4 -2
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/task_pb2.py +16 -16
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/task_pb2.pyi +4 -2
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/routines/__init__.py +21 -0
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/routines/common.py +23 -0
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/routines/resonator_mapping.py +35 -0
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/routines/transmon_discovery.py +45 -0
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/routines/transmon_retuning.py +31 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/stubs/maps.py +11 -2
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/utils/__init__.py +12 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/pyproject.toml +1 -1
- boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/device/defcal.py +0 -62
- boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/device/device.py +0 -41
- boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/experiments/readout_classifier_calibration.py +0 -24
- boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/routines/__init__.py +0 -6
- boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/routines/common.py +0 -10
- boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/routines/resonator_mapping.py +0 -19
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/LICENSE +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/README.md +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/agent/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/common/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/stubs → boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/device}/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/base.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/quantum_machines.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/resolver.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/grpc_interceptors/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/grpc_interceptors/auth.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/plotting/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/agent_pb2.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/task_pb2_grpc.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/py.typed +0 -0
- {boulder_opal_scale_up_sdk-1.0.2/boulderopalscaleupsdk/utils → boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/stubs}/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/stubs/dtypes.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/quantum_machines/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/quantum_machines/config.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/quantum_machines/constants.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.2 → boulder_opal_scale_up_sdk-1.0.4}/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.4
|
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
|
@@ -27,7 +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
|
+
from boulderopalscaleupsdk.common.dtypes import GrpcMetadata, JobId
|
31
31
|
from boulderopalscaleupsdk.protobuf.v1 import agent_pb2, task_pb2, task_pb2_grpc
|
32
32
|
|
33
33
|
LOG = logging.getLogger(__name__)
|
@@ -178,7 +178,7 @@ class Agent:
|
|
178
178
|
device_name: str,
|
179
179
|
routine: str,
|
180
180
|
data: Struct | None,
|
181
|
-
) ->
|
181
|
+
) -> JobId | None:
|
182
182
|
if not self._channel:
|
183
183
|
raise RuntimeError("Cannot start session: agent is shutdown.")
|
184
184
|
_data = any_pb2.Any()
|
@@ -196,16 +196,18 @@ class Agent:
|
|
196
196
|
metadata=metadata,
|
197
197
|
)
|
198
198
|
self._state = response.target_state
|
199
|
-
await self._resume(response, metadata)
|
199
|
+
job_id: JobId | None = await self._resume(response, metadata)
|
200
200
|
await self.shutdown()
|
201
|
-
|
201
|
+
|
202
|
+
return job_id
|
202
203
|
|
203
204
|
async def _resume(
|
204
205
|
self,
|
205
206
|
response: task_pb2.AgentTasksResponse,
|
206
207
|
metadata: GrpcMetadata,
|
207
|
-
) -> None:
|
208
|
+
) -> JobId | None:
|
208
209
|
tasks = response.tasks
|
210
|
+
job_id = None
|
209
211
|
while self._state not in [
|
210
212
|
task_pb2.AGENT_STATE_SHUTDOWN_MANAGER_INITIATED,
|
211
213
|
task_pb2.AGENT_STATE_SHUTDOWN_FAULT,
|
@@ -243,6 +245,9 @@ class Agent:
|
|
243
245
|
|
244
246
|
tasks = _resp.tasks
|
245
247
|
self._state = _resp.target_state
|
248
|
+
job_id = _resp.job_id
|
249
|
+
|
250
|
+
return job_id or response.job_id
|
246
251
|
|
247
252
|
async def shutdown(
|
248
253
|
self,
|
@@ -28,11 +28,13 @@ from typing import Annotated, Any, Literal, Self
|
|
28
28
|
|
29
29
|
import numpy as np
|
30
30
|
from dateutil.parser import isoparse
|
31
|
-
from pydantic import BeforeValidator, ConfigDict, Field, PlainSerializer, TypeAdapter
|
31
|
+
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, PlainSerializer, TypeAdapter
|
32
32
|
from pydantic.dataclasses import dataclass
|
33
33
|
|
34
34
|
GrpcMetadata = list[tuple[str, str | bytes]]
|
35
35
|
|
36
|
+
JobId = str
|
37
|
+
|
36
38
|
|
37
39
|
class BaseType: ...
|
38
40
|
|
@@ -315,6 +317,17 @@ class JobHistorySortOrder(enum.Enum):
|
|
315
317
|
CREATED_AT_ASC = 2
|
316
318
|
|
317
319
|
|
320
|
+
class JobSummary(BaseModel):
|
321
|
+
id: str
|
322
|
+
name: str
|
323
|
+
device_name: str
|
324
|
+
session_id: str
|
325
|
+
created_at: ISO8601DatetimeUTCLike
|
326
|
+
|
327
|
+
def __str__(self):
|
328
|
+
return f"JobSummary(name = {self.name}, id = {self.id})"
|
329
|
+
|
330
|
+
|
318
331
|
DEFAULT_JOB_HISTORY_PAGE = 1
|
319
332
|
DEFAULT_JOB_HISTORY_PAGE_SIZE = 10
|
320
333
|
DEFAULT_JOB_HISTORY_SORT_ORDER = JobHistorySortOrder.CREATED_AT_DESC
|
@@ -83,3 +83,25 @@ class Combine(Protocol[T]):
|
|
83
83
|
return destination
|
84
84
|
|
85
85
|
return Combine.create(_combine)
|
86
|
+
|
87
|
+
|
88
|
+
class Eq(Protocol[T]):
|
89
|
+
@abstractmethod
|
90
|
+
def eq(self, first: T, second: T) -> bool: ...
|
91
|
+
|
92
|
+
def eq_option(self, first: T | None, second: T | None) -> bool:
|
93
|
+
match first, second:
|
94
|
+
case None, None:
|
95
|
+
return True
|
96
|
+
case _ if first is not None and second is not None:
|
97
|
+
return self.eq(first, second)
|
98
|
+
case _:
|
99
|
+
return False
|
100
|
+
|
101
|
+
@classmethod
|
102
|
+
def create(cls, eq_fn: Callable[[T, T], bool]) -> "Eq[T]":
|
103
|
+
class _Eq(Eq):
|
104
|
+
def eq(self, first: T, second: T) -> bool:
|
105
|
+
return eq_fn(first, second)
|
106
|
+
|
107
|
+
return _Eq()
|
@@ -34,6 +34,7 @@ class ProcessorArchitecture(str, Enum):
|
|
34
34
|
Superconducting = "superconducting"
|
35
35
|
|
36
36
|
|
37
|
+
# TODO: Remove this in the next release of SDK
|
37
38
|
class DeviceInfo(BaseModel):
|
38
39
|
controller_info: QBLOXControllerInfo | QuantumMachinesControllerInfo
|
39
40
|
processor: SuperconductingProcessor # | OtherSDKProcessorType
|
@@ -18,6 +18,7 @@ QBLOX Quantum Control Stack
|
|
18
18
|
__all__ = (
|
19
19
|
"DEFAULT_MODULE_CONSTRAINTS",
|
20
20
|
"AcquisitionConfig",
|
21
|
+
"BitStrideArrayEncoding",
|
21
22
|
"ChannelType",
|
22
23
|
"ComplexChannel",
|
23
24
|
"IndexedData",
|
@@ -40,15 +41,19 @@ __all__ = (
|
|
40
41
|
"SequenceProgram",
|
41
42
|
"SequencerAddr",
|
42
43
|
"SequencerAddrType",
|
44
|
+
"SequencerResults",
|
45
|
+
"process_sequencer_output",
|
43
46
|
"validate_channel",
|
44
47
|
)
|
45
48
|
|
46
49
|
import dataclasses
|
47
50
|
import enum
|
51
|
+
import math
|
48
52
|
import re
|
49
53
|
from dataclasses import dataclass
|
50
54
|
from typing import Annotated, Any, ClassVar, Literal, Self, TypeVar
|
51
55
|
|
56
|
+
import numpy as np
|
52
57
|
from pydantic import BaseModel, BeforeValidator, Field, PlainSerializer, model_validator
|
53
58
|
|
54
59
|
from boulderopalscaleupsdk.device.controller.base import Backend, ControllerType
|
@@ -373,6 +378,7 @@ class SequenceProgram(BaseModel):
|
|
373
378
|
weights: dict[str, IndexedData] = {}
|
374
379
|
acquisitions: dict[str, AcquisitionConfig] = {}
|
375
380
|
acquisition_scopes: list[str] = []
|
381
|
+
acquisition_shapes: dict[str, tuple[int, ...]] = {}
|
376
382
|
params: SequencerParams = SequencerParams()
|
377
383
|
params_only: bool = False
|
378
384
|
|
@@ -417,6 +423,14 @@ class PreparedProgram(BaseModel):
|
|
417
423
|
modules: dict[ModuleAddrType, PreparedModule] # The set of modules this program will target.
|
418
424
|
sequence_programs: dict[str, PreparedSequenceProgram] # The individual element programs.
|
419
425
|
|
426
|
+
@property
|
427
|
+
def sequencers(self) -> dict[SequencerAddr, str]:
|
428
|
+
return {psp.sequencer_addr: name for name, psp in self.sequence_programs.items()}
|
429
|
+
|
430
|
+
def get_sequencer_program(self, seq_addr: SequencerAddr) -> SequenceProgram:
|
431
|
+
prog_name = self.sequencers[seq_addr]
|
432
|
+
return self.sequence_programs[prog_name].sequence_program
|
433
|
+
|
420
434
|
def dumps(self) -> str:
|
421
435
|
return self.model_dump_json()
|
422
436
|
|
@@ -428,6 +442,9 @@ class PreparedProgram(BaseModel):
|
|
428
442
|
# ==================================================================================================
|
429
443
|
# Results
|
430
444
|
# ==================================================================================================
|
445
|
+
MAX_ACQUISITION_BINS = 131072
|
446
|
+
|
447
|
+
|
431
448
|
class OutputScopedAcquisitionData(BaseModel): # pragma: no cover
|
432
449
|
"""
|
433
450
|
Scoped acquisition data for a single path in `OutputScopedAcquisition`.
|
@@ -544,6 +561,118 @@ class OutputIndexedAcquisition(BaseModel): # pragma: no cover
|
|
544
561
|
OutputSequencerAcquisitions = dict[str, OutputIndexedAcquisition] # pragma: no cover
|
545
562
|
|
546
563
|
|
564
|
+
@dataclass
|
565
|
+
class SequencerResults:
|
566
|
+
"""
|
567
|
+
Sequencer results formatted as a complex signal.
|
568
|
+
|
569
|
+
The real component corresponds to results on path0, whilst the imaginary component corresponds
|
570
|
+
to the results on path1.
|
571
|
+
"""
|
572
|
+
|
573
|
+
scopes: dict[str, np.ndarray]
|
574
|
+
bins: dict[str, np.ndarray]
|
575
|
+
|
576
|
+
|
577
|
+
def process_sequencer_output(
|
578
|
+
program: SequenceProgram,
|
579
|
+
output: OutputSequencerAcquisitions,
|
580
|
+
) -> SequencerResults:
|
581
|
+
"""
|
582
|
+
Process the output from executing a sequencer into a simplified SequencerResults data structure.
|
583
|
+
|
584
|
+
Parameters
|
585
|
+
----------
|
586
|
+
program: SequenceProgram
|
587
|
+
The corresponding program that was executed
|
588
|
+
output: OutputSequencerAcquisitions
|
589
|
+
The results of one sequencer's execution
|
590
|
+
|
591
|
+
Returns
|
592
|
+
-------
|
593
|
+
SequencerResults
|
594
|
+
"""
|
595
|
+
bins = {}
|
596
|
+
scopes = {}
|
597
|
+
for acq_ref, acq_result in output.items():
|
598
|
+
acquisition = acq_result.acquisition
|
599
|
+
|
600
|
+
raw_bin = acquisition.bins.integration
|
601
|
+
shape = program.acquisition_shapes.get(acq_ref)
|
602
|
+
if shape is None or len(shape) == 1:
|
603
|
+
bins[acq_ref] = np.array(raw_bin.path0) + 1j * np.array(raw_bin.path1)
|
604
|
+
else:
|
605
|
+
bse = BitStrideArrayEncoding.from_desired(shape)
|
606
|
+
bins[acq_ref] = bse.decode(raw_bin.path0) + 1j * bse.decode(raw_bin.path1)
|
607
|
+
|
608
|
+
raw_scope = acquisition.scope
|
609
|
+
if acq_ref in program.acquisition_scopes:
|
610
|
+
scopes[acq_ref] = np.array(raw_scope.path0.data) + 1j * np.array(raw_scope.path1.data)
|
611
|
+
|
612
|
+
return SequencerResults(scopes=scopes, bins=bins)
|
613
|
+
|
614
|
+
|
615
|
+
@dataclass
|
616
|
+
class BitStrideArrayEncoding:
|
617
|
+
"""
|
618
|
+
Encode a multi-dimensional array such that each dimensional index occupies an integer number of
|
619
|
+
bits (the bit-stride).
|
620
|
+
|
621
|
+
In this encoding, a linear index is calculated by left-shifting each element in the index by its
|
622
|
+
corresponding bit-stride:
|
623
|
+
|
624
|
+
linear_index = Σ (ii << bb) ∀ (ii, bb) ∈ zip(index, bit_strides)
|
625
|
+
|
626
|
+
Examples
|
627
|
+
--------
|
628
|
+
Given a desired_shape of (3, 5), the first index will have a bit-stride of 2 and the second
|
629
|
+
index will have a bit-stride of 3. To determine the linear index for a sample, we will use:
|
630
|
+
|
631
|
+
def linear_index(idx0: int, idx1: int) -> int:
|
632
|
+
return (idx0 << 2) + (idx1 << 3)
|
633
|
+
|
634
|
+
Note, the encoded data will occupy 2^2 * 2^3 samples and have (2^2 * 2^3 - 3 * 5) unused data
|
635
|
+
points.
|
636
|
+
|
637
|
+
>>> bse = BitStrideArrayEncoding.from_desired((3, 5))
|
638
|
+
>>> bse.encoded_shape
|
639
|
+
(4, 8)
|
640
|
+
>>> bse.bit_stride
|
641
|
+
(2, 3)
|
642
|
+
"""
|
643
|
+
|
644
|
+
desired_shape: tuple[int, ...]
|
645
|
+
encoded_shape: tuple[int, ...]
|
646
|
+
bit_stride: tuple[int, ...]
|
647
|
+
|
648
|
+
@staticmethod
|
649
|
+
def _round_power2_32bit(val: int) -> int:
|
650
|
+
val -= 1
|
651
|
+
val |= val >> 1
|
652
|
+
val |= val >> 2
|
653
|
+
val |= val >> 4
|
654
|
+
val |= val >> 8
|
655
|
+
val |= val >> 16
|
656
|
+
val += 1
|
657
|
+
return val
|
658
|
+
|
659
|
+
@classmethod
|
660
|
+
def from_desired(cls, desired_shape: tuple[int, ...]) -> Self:
|
661
|
+
encoded_shape = tuple(
|
662
|
+
BitStrideArrayEncoding._round_power2_32bit(dim) for dim in desired_shape
|
663
|
+
)
|
664
|
+
bit_stride = tuple(int(math.log2(dim)) for dim in encoded_shape)
|
665
|
+
return cls(
|
666
|
+
desired_shape=desired_shape,
|
667
|
+
encoded_shape=encoded_shape,
|
668
|
+
bit_stride=bit_stride,
|
669
|
+
)
|
670
|
+
|
671
|
+
def decode(self, values: list[float]) -> np.ndarray:
|
672
|
+
decoded = np.reshape(values, self.encoded_shape)
|
673
|
+
return decoded[tuple(slice(0, dim) for dim in self.desired_shape)]
|
674
|
+
|
675
|
+
|
547
676
|
# ==================================================================================================
|
548
677
|
# Utilities
|
549
678
|
# ==================================================================================================
|
@@ -11,10 +11,13 @@
|
|
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
|
-
|
14
|
+
from typing import Literal
|
15
15
|
|
16
|
-
from
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
from pydantic import BaseModel
|
17
|
+
|
18
|
+
|
19
|
+
class DefCalData(BaseModel):
|
20
|
+
gate: str
|
21
|
+
addr: list[str]
|
22
|
+
body: str
|
23
|
+
status: Literal["calibrated", "uncalibrated"]
|
@@ -0,0 +1,63 @@
|
|
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
|
+
from pydantic import BaseModel
|
16
|
+
from pydantic.dataclasses import dataclass
|
17
|
+
|
18
|
+
from boulderopalscaleupsdk.common.dtypes import ISO8601DatetimeUTCLike
|
19
|
+
from boulderopalscaleupsdk.device.controller import (
|
20
|
+
QBLOXControllerInfo,
|
21
|
+
QuantumMachinesControllerInfo,
|
22
|
+
)
|
23
|
+
from boulderopalscaleupsdk.device.defcal import DefCalData
|
24
|
+
from boulderopalscaleupsdk.device.processor import SuperconductingProcessor
|
25
|
+
|
26
|
+
|
27
|
+
@dataclass
|
28
|
+
class EmptyDefCalData:
|
29
|
+
message: str
|
30
|
+
|
31
|
+
|
32
|
+
@dataclass
|
33
|
+
class DeviceData:
|
34
|
+
# TODO: retire DeviceInfo the next SDK release
|
35
|
+
qpu: SuperconductingProcessor # | OtherSDKProcessorType
|
36
|
+
controller_info: QBLOXControllerInfo | QuantumMachinesControllerInfo
|
37
|
+
_defcals: dict[tuple[str, tuple[str, ...]], DefCalData]
|
38
|
+
|
39
|
+
def get_defcal(self, gate: str, addr: tuple[str, ...]) -> DefCalData | EmptyDefCalData:
|
40
|
+
"""
|
41
|
+
Get the defcal data for a specific gate and address alias.
|
42
|
+
"""
|
43
|
+
if self._defcals == {}:
|
44
|
+
return EmptyDefCalData(message="No defcal data available in a fresh device.")
|
45
|
+
_addr = tuple(i.lower() for i in sorted(addr))
|
46
|
+
defcal = self._defcals.get((gate, _addr))
|
47
|
+
if defcal is None:
|
48
|
+
return EmptyDefCalData(
|
49
|
+
message=f"No defcal data found for gate '{gate}' and address '{_addr}'.",
|
50
|
+
)
|
51
|
+
return defcal
|
52
|
+
|
53
|
+
|
54
|
+
class DeviceSummary(BaseModel):
|
55
|
+
id: str
|
56
|
+
organization_id: str
|
57
|
+
name: str
|
58
|
+
provider: str
|
59
|
+
updated_at: ISO8601DatetimeUTCLike
|
60
|
+
created_at: ISO8601DatetimeUTCLike
|
61
|
+
|
62
|
+
def __str__(self):
|
63
|
+
return f"DeviceSummary(name = {self.name}, id = {self.id})"
|
@@ -14,7 +14,6 @@
|
|
14
14
|
__all__ = [
|
15
15
|
"CalibrationThresholds",
|
16
16
|
"ComponentParameter",
|
17
|
-
"DurationComponentParameter",
|
18
17
|
"FloatComponentParameter",
|
19
18
|
"SuperconductingProcessor",
|
20
19
|
"SuperconductingProcessorTemplate",
|
@@ -24,7 +23,6 @@ __all__ = [
|
|
24
23
|
from .common import (
|
25
24
|
CalibrationThresholds,
|
26
25
|
ComponentParameter,
|
27
|
-
DurationComponentParameter,
|
28
26
|
FloatComponentParameter,
|
29
27
|
update_parameter,
|
30
28
|
)
|
@@ -25,7 +25,7 @@ from boulderopalscaleupsdk.common.dtypes import (
|
|
25
25
|
Frequency,
|
26
26
|
ISO8601DatetimeUTCLike,
|
27
27
|
)
|
28
|
-
from boulderopalscaleupsdk.common.typeclasses import Combine
|
28
|
+
from boulderopalscaleupsdk.common.typeclasses import Combine, Eq
|
29
29
|
|
30
30
|
T = TypeVar("T")
|
31
31
|
T2 = TypeVar("T2")
|
@@ -97,23 +97,40 @@ class ComponentParameter(Generic[T]):
|
|
97
97
|
@staticmethod
|
98
98
|
def combine(
|
99
99
|
combine_instance: Combine[T],
|
100
|
+
eq_instance: Eq[T],
|
100
101
|
) -> "Combine[ComponentParameter[T]]":
|
101
102
|
def _combine(
|
102
103
|
first: ComponentParameter[T],
|
103
104
|
other: ComponentParameter[Any],
|
104
105
|
) -> ComponentParameter[T]:
|
106
|
+
_value = combine_instance.combine(first.value, other.value)
|
107
|
+
_err_minus = combine_instance.combine_option(
|
108
|
+
first.err_minus,
|
109
|
+
other.err_minus,
|
110
|
+
)
|
111
|
+
_err_plus = combine_instance.combine_option(
|
112
|
+
first.err_plus,
|
113
|
+
other.err_plus,
|
114
|
+
)
|
115
|
+
_calibration_status = other.calibration_status
|
116
|
+
|
117
|
+
_updated_at: ISO8601DatetimeUTCLike | None
|
118
|
+
if (
|
119
|
+
not eq_instance.eq(_value, first.value)
|
120
|
+
or not eq_instance.eq_option(_err_minus, first.err_minus)
|
121
|
+
or not eq_instance.eq_option(_err_plus, first.err_plus)
|
122
|
+
or _calibration_status != first.calibration_status
|
123
|
+
):
|
124
|
+
_updated_at = datetime.now(tz=UTC)
|
125
|
+
else:
|
126
|
+
_updated_at = first.updated_at
|
127
|
+
|
105
128
|
return ComponentParameter(
|
106
|
-
value=
|
107
|
-
err_minus=
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
err_plus=combine_instance.combine_option(
|
112
|
-
first.err_plus,
|
113
|
-
other.err_plus,
|
114
|
-
),
|
115
|
-
calibration_status=other.calibration_status,
|
116
|
-
updated_at=datetime.now(tz=UTC),
|
129
|
+
value=_value,
|
130
|
+
err_minus=_err_minus,
|
131
|
+
err_plus=_err_plus,
|
132
|
+
calibration_status=_calibration_status,
|
133
|
+
updated_at=_updated_at,
|
117
134
|
)
|
118
135
|
|
119
136
|
return Combine[ComponentParameter[T]].create(_combine)
|
@@ -127,8 +144,13 @@ class ComponentParameter(Generic[T]):
|
|
127
144
|
updated_at=self.updated_at,
|
128
145
|
)
|
129
146
|
|
130
|
-
def merge_with(
|
131
|
-
|
147
|
+
def merge_with(
|
148
|
+
self,
|
149
|
+
other: "ComponentParameter[T]",
|
150
|
+
combine_value: "Combine[T]",
|
151
|
+
eq_value: Eq[T],
|
152
|
+
):
|
153
|
+
combined = ComponentParameter[T].combine(combine_value, eq_value).combine(self, other)
|
132
154
|
self.value = combined.value
|
133
155
|
self.err_minus = combined.err_minus
|
134
156
|
self.err_plus = combined.err_plus
|
@@ -178,7 +200,7 @@ class ComponentParameter(Generic[T]):
|
|
178
200
|
):
|
179
201
|
self.calibration_status = _get_calibration_status_from_thresholds(
|
180
202
|
value=to_float(self.value),
|
181
|
-
confidence_interval=to_float(self.err_plus)
|
203
|
+
confidence_interval=to_float(self.err_plus) + to_float(self.err_minus),
|
182
204
|
calibration_thresholds=calibration_thresholds,
|
183
205
|
)
|
184
206
|
self.updated_at = datetime.now(UTC)
|
@@ -250,8 +272,3 @@ FloatComponentParameter = Annotated[
|
|
250
272
|
ComponentParameter[float],
|
251
273
|
BeforeValidator(lambda value: ComponentParameter.from_value(value, float)),
|
252
274
|
]
|
253
|
-
|
254
|
-
DurationComponentParameter = Annotated[
|
255
|
-
ComponentParameter[Duration],
|
256
|
-
BeforeValidator(lambda value: ComponentParameter.from_value(value, Duration)),
|
257
|
-
]
|
@@ -23,11 +23,9 @@ from boulderopalscaleupsdk.common.dtypes import (
|
|
23
23
|
from boulderopalscaleupsdk.device.common import (
|
24
24
|
Component,
|
25
25
|
ComponentRef,
|
26
|
-
Processor,
|
27
26
|
)
|
28
27
|
from boulderopalscaleupsdk.device.processor import (
|
29
28
|
ComponentParameter,
|
30
|
-
DurationComponentParameter,
|
31
29
|
FloatComponentParameter,
|
32
30
|
)
|
33
31
|
|
@@ -37,23 +35,23 @@ class Transmon(Component[Literal["tunable"]]):
|
|
37
35
|
dtype: Literal["transmon"] = "transmon"
|
38
36
|
traits: list[Literal["tunable"]] = Field(default=["tunable"])
|
39
37
|
|
40
|
-
freq_01: FloatComponentParameter
|
41
|
-
default=
|
38
|
+
freq_01: FloatComponentParameter = Field(
|
39
|
+
default=ComponentParameter(value=(0.0)),
|
42
40
|
json_schema_extra={"display": {"label": "freq_01", "unit": "MHz", "scale": 1e-6}},
|
43
41
|
)
|
44
|
-
anharm: FloatComponentParameter
|
45
|
-
default=
|
42
|
+
anharm: FloatComponentParameter = Field(
|
43
|
+
default=ComponentParameter(value=0.0),
|
46
44
|
json_schema_extra={"display": {"label": "anharm", "unit": "MHz", "scale": 1e-6}},
|
47
45
|
)
|
48
|
-
t1:
|
46
|
+
t1: ComponentParameter[Duration] = Field(
|
49
47
|
default=ComponentParameter(value=Duration(0, TimeUnit.NS)),
|
50
48
|
json_schema_extra={"display": {"label": "t1", "unit": "µs", "scale": 1e6}},
|
51
49
|
)
|
52
|
-
t2:
|
50
|
+
t2: ComponentParameter[Duration] = Field(
|
53
51
|
default=ComponentParameter(value=Duration(0, TimeUnit.NS)),
|
54
52
|
json_schema_extra={"display": {"label": "t2", "unit": "µs", "scale": 1e6}},
|
55
53
|
)
|
56
|
-
t2_echo:
|
54
|
+
t2_echo: ComponentParameter[Duration] = Field(
|
57
55
|
default=ComponentParameter(value=Duration(0, TimeUnit.NS)),
|
58
56
|
json_schema_extra={"display": {"label": "t2_echo", "unit": "µs", "scale": 1e6}},
|
59
57
|
)
|
@@ -65,15 +63,23 @@ class Transmon(Component[Literal["tunable"]]):
|
|
65
63
|
default=ComponentParameter(value=0.0),
|
66
64
|
json_schema_extra={"display": {"label": "sx_vp", "unit": "V", "scale": 1}},
|
67
65
|
)
|
66
|
+
x_ef_vp: FloatComponentParameter = Field(
|
67
|
+
default=ComponentParameter(value=0.0),
|
68
|
+
json_schema_extra={"display": {"label": "x_ef_vp", "unit": "V", "scale": 1}},
|
69
|
+
)
|
70
|
+
sx_ef_vp: FloatComponentParameter = Field(
|
71
|
+
default=ComponentParameter(value=0.0),
|
72
|
+
json_schema_extra={"display": {"label": "sx_ef_vp", "unit": "V", "scale": 1}},
|
73
|
+
)
|
68
74
|
|
69
75
|
# Tunable transmon parameters.
|
70
76
|
dc_bias: FloatComponentParameter = Field(
|
71
77
|
default=ComponentParameter(value=0.0),
|
72
78
|
json_schema_extra={"display": {"label": "dc_bias", "unit": "V", "scale": 1}},
|
73
79
|
)
|
74
|
-
|
80
|
+
bias_offset: FloatComponentParameter = Field(
|
75
81
|
default=ComponentParameter(value=0.0),
|
76
|
-
json_schema_extra={"display": {"label": "
|
82
|
+
json_schema_extra={"display": {"label": "bias_offset", "unit": "V", "scale": 1}},
|
77
83
|
)
|
78
84
|
bias_period: FloatComponentParameter = Field(
|
79
85
|
default=ComponentParameter(value=0.0),
|
@@ -85,20 +91,20 @@ class Resonator(Component[Literal["readout"]]):
|
|
85
91
|
dtype: Literal["resonator"] = "resonator"
|
86
92
|
traits: list[Literal["readout"]] = Field(default=["readout"])
|
87
93
|
|
88
|
-
frequency_high: FloatComponentParameter
|
89
|
-
default=
|
94
|
+
frequency_high: FloatComponentParameter = Field(
|
95
|
+
default=ComponentParameter(value=0.0),
|
90
96
|
json_schema_extra={"display": {"label": "frequency_high", "unit": "MHz", "scale": 1e-6}},
|
91
97
|
)
|
92
|
-
frequency_low: FloatComponentParameter
|
93
|
-
default=
|
98
|
+
frequency_low: FloatComponentParameter = Field(
|
99
|
+
default=ComponentParameter(value=0.0),
|
94
100
|
json_schema_extra={"display": {"label": "frequency_low", "unit": "MHz", "scale": 1e-6}},
|
95
101
|
)
|
96
|
-
kappa_low: FloatComponentParameter
|
97
|
-
default=
|
102
|
+
kappa_low: FloatComponentParameter = Field(
|
103
|
+
default=ComponentParameter(value=0.0),
|
98
104
|
json_schema_extra={"display": {"label": "kappa_low", "unit": "MHz", "scale": 1e-6}},
|
99
105
|
)
|
100
|
-
kappa_high: FloatComponentParameter
|
101
|
-
default=
|
106
|
+
kappa_high: FloatComponentParameter = Field(
|
107
|
+
default=ComponentParameter(value=0.0),
|
102
108
|
json_schema_extra={"display": {"label": "kappa_high", "unit": "MHz", "scale": 1e-6}},
|
103
109
|
)
|
104
110
|
vp_low: FloatComponentParameter = Field(
|
@@ -128,9 +134,9 @@ class Coupler(Component[Literal["tunable"]]):
|
|
128
134
|
default=ComponentParameter(value=0.0),
|
129
135
|
json_schema_extra={"display": {"label": "dc_bias", "unit": "V", "scale": 1}},
|
130
136
|
)
|
131
|
-
|
137
|
+
bias_offset: FloatComponentParameter = Field(
|
132
138
|
default=ComponentParameter(value=0.0),
|
133
|
-
json_schema_extra={"display": {"label": "
|
139
|
+
json_schema_extra={"display": {"label": "bias_offset", "unit": "V", "scale": 1}},
|
134
140
|
)
|
135
141
|
bias_period: FloatComponentParameter = Field(
|
136
142
|
default=ComponentParameter(value=0.0),
|
@@ -182,7 +188,7 @@ class SuperconductingProcessorTemplate(BaseModel):
|
|
182
188
|
)
|
183
189
|
|
184
190
|
|
185
|
-
class SuperconductingProcessor(
|
191
|
+
class SuperconductingProcessor(BaseModel):
|
186
192
|
nodes: dict[ComponentRef, SuperconductingComponentType]
|
187
193
|
edges: list[Edge]
|
188
194
|
|