boulder-opal-scale-up-sdk 1.0.3__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.3 → boulder_opal_scale_up_sdk-1.0.4}/PKG-INFO +1 -1
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/common/dtypes.py +12 -1
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/config_loader.py +1 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/qblox.py +129 -0
- boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/device/device.py → boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/device/defcal.py +7 -9
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/device/device.py +63 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/processor/__init__.py +0 -2
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/processor/common.py +1 -6
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/processor/superconducting_processor.py +11 -4
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/__init__.py +16 -2
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/chi01_scan.py +10 -9
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/common.py +0 -16
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/power_rabi.py +13 -10
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/power_rabi_ef.py +66 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/ramsey.py +13 -12
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/readout_classifier_calibration.py +44 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/resonator_spectroscopy.py +11 -11
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_bias.py +13 -13
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/resonator_spectroscopy_by_power.py +12 -12
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/t1.py +8 -16
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/t2.py +9 -8
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/t2_echo.py +49 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/transmon_anharmonicity.py +16 -17
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/experiments/transmon_spectroscopy.py +10 -10
- boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/experiments/waveforms.py +63 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/plotting/dtypes.py +1 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/device_pb2.py +20 -20
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/device_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.3/boulderopalscaleupsdk/device/__init__.py → boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/routines/common.py +9 -5
- 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.3 → 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.3 → boulder_opal_scale_up_sdk-1.0.4}/pyproject.toml +1 -1
- boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/device/defcal.py +0 -62
- boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/experiments/readout_classifier_calibration.py +0 -28
- boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/routines/__init__.py +0 -6
- boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/routines/common.py +0 -10
- boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/routines/resonator_mapping.py +0 -19
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/LICENSE +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/README.md +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/agent/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/agent/worker.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/common/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/common/typeclasses.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/stubs → boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/device}/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/common.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/base.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/quantum_machines.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/device/controller/resolver.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/grpc_interceptors/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/grpc_interceptors/auth.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/plotting/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/agent_pb2.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/agent_pb2.pyi +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/agent_pb2_grpc.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/device_pb2_grpc.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/task_pb2.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/task_pb2.pyi +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/protobuf/v1/task_pb2_grpc.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/py.typed +0 -0
- {boulder_opal_scale_up_sdk-1.0.3/boulderopalscaleupsdk/utils → boulder_opal_scale_up_sdk-1.0.4/boulderopalscaleupsdk/stubs}/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/stubs/dtypes.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/quantum_machines/__init__.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/quantum_machines/config.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → boulder_opal_scale_up_sdk-1.0.4}/boulderopalscaleupsdk/third_party/quantum_machines/constants.py +0 -0
- {boulder_opal_scale_up_sdk-1.0.3 → 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
|
@@ -28,7 +28,7 @@ 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]]
|
@@ -317,6 +317,17 @@ class JobHistorySortOrder(enum.Enum):
|
|
317
317
|
CREATED_AT_ASC = 2
|
318
318
|
|
319
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
|
+
|
320
331
|
DEFAULT_JOB_HISTORY_PAGE = 1
|
321
332
|
DEFAULT_JOB_HISTORY_PAGE_SIZE = 10
|
322
333
|
DEFAULT_JOB_HISTORY_SORT_ORDER = JobHistorySortOrder.CREATED_AT_DESC
|
@@ -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,15 +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
|
+
from typing import Literal
|
14
15
|
|
15
|
-
from pydantic
|
16
|
+
from pydantic import BaseModel
|
16
17
|
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@dataclass
|
24
|
-
class InvalidDeviceComponent:
|
25
|
-
message: str
|
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
|
)
|
@@ -200,7 +200,7 @@ class ComponentParameter(Generic[T]):
|
|
200
200
|
):
|
201
201
|
self.calibration_status = _get_calibration_status_from_thresholds(
|
202
202
|
value=to_float(self.value),
|
203
|
-
confidence_interval=to_float(self.err_plus)
|
203
|
+
confidence_interval=to_float(self.err_plus) + to_float(self.err_minus),
|
204
204
|
calibration_thresholds=calibration_thresholds,
|
205
205
|
)
|
206
206
|
self.updated_at = datetime.now(UTC)
|
@@ -272,8 +272,3 @@ FloatComponentParameter = Annotated[
|
|
272
272
|
ComponentParameter[float],
|
273
273
|
BeforeValidator(lambda value: ComponentParameter.from_value(value, float)),
|
274
274
|
]
|
275
|
-
|
276
|
-
DurationComponentParameter = Annotated[
|
277
|
-
ComponentParameter[Duration],
|
278
|
-
BeforeValidator(lambda value: ComponentParameter.from_value(value, Duration)),
|
279
|
-
]
|
@@ -26,7 +26,6 @@ from boulderopalscaleupsdk.device.common import (
|
|
26
26
|
)
|
27
27
|
from boulderopalscaleupsdk.device.processor import (
|
28
28
|
ComponentParameter,
|
29
|
-
DurationComponentParameter,
|
30
29
|
FloatComponentParameter,
|
31
30
|
)
|
32
31
|
|
@@ -44,15 +43,15 @@ class Transmon(Component[Literal["tunable"]]):
|
|
44
43
|
default=ComponentParameter(value=0.0),
|
45
44
|
json_schema_extra={"display": {"label": "anharm", "unit": "MHz", "scale": 1e-6}},
|
46
45
|
)
|
47
|
-
t1:
|
46
|
+
t1: ComponentParameter[Duration] = Field(
|
48
47
|
default=ComponentParameter(value=Duration(0, TimeUnit.NS)),
|
49
48
|
json_schema_extra={"display": {"label": "t1", "unit": "µs", "scale": 1e6}},
|
50
49
|
)
|
51
|
-
t2:
|
50
|
+
t2: ComponentParameter[Duration] = Field(
|
52
51
|
default=ComponentParameter(value=Duration(0, TimeUnit.NS)),
|
53
52
|
json_schema_extra={"display": {"label": "t2", "unit": "µs", "scale": 1e6}},
|
54
53
|
)
|
55
|
-
t2_echo:
|
54
|
+
t2_echo: ComponentParameter[Duration] = Field(
|
56
55
|
default=ComponentParameter(value=Duration(0, TimeUnit.NS)),
|
57
56
|
json_schema_extra={"display": {"label": "t2_echo", "unit": "µs", "scale": 1e6}},
|
58
57
|
)
|
@@ -64,6 +63,14 @@ class Transmon(Component[Literal["tunable"]]):
|
|
64
63
|
default=ComponentParameter(value=0.0),
|
65
64
|
json_schema_extra={"display": {"label": "sx_vp", "unit": "V", "scale": 1}},
|
66
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
|
+
)
|
67
74
|
|
68
75
|
# Tunable transmon parameters.
|
69
76
|
dc_bias: FloatComponentParameter = Field(
|
@@ -19,32 +19,38 @@ __all__ = [
|
|
19
19
|
"CWSIterable",
|
20
20
|
"Chi01Scan",
|
21
21
|
"ConstantWaveform",
|
22
|
+
"DragCosineWaveform",
|
22
23
|
"Experiment",
|
23
24
|
"GaussianWaveform",
|
24
25
|
"HypIterable",
|
25
26
|
"LinspaceIterable",
|
27
|
+
"LogspaceIterable",
|
26
28
|
"PowerRabi",
|
29
|
+
"PowerRabiEF",
|
27
30
|
"Ramsey",
|
28
31
|
"RangeIterable",
|
29
32
|
"ReadoutClassifierCalibration",
|
30
33
|
"ResonatorSpectroscopy",
|
31
34
|
"ResonatorSpectroscopyByBias",
|
32
35
|
"ResonatorSpectroscopyByPower",
|
36
|
+
"T2Echo",
|
33
37
|
"TransmonAnharmonicity",
|
34
38
|
"TransmonSpectroscopy",
|
39
|
+
"Waveform",
|
40
|
+
"update_amplitude",
|
35
41
|
]
|
36
42
|
|
37
43
|
from .chi01_scan import Chi01Scan
|
38
44
|
from .common import (
|
39
|
-
ConstantWaveform,
|
40
45
|
CWSIterable,
|
41
46
|
Experiment,
|
42
|
-
GaussianWaveform,
|
43
47
|
HypIterable,
|
44
48
|
LinspaceIterable,
|
49
|
+
LogspaceIterable,
|
45
50
|
RangeIterable,
|
46
51
|
)
|
47
52
|
from .power_rabi import PowerRabi
|
53
|
+
from .power_rabi_ef import PowerRabiEF
|
48
54
|
from .ramsey import Ramsey
|
49
55
|
from .readout_classifier_calibration import ReadoutClassifierCalibration
|
50
56
|
from .resonator_spectroscopy import ResonatorSpectroscopy
|
@@ -52,5 +58,13 @@ from .resonator_spectroscopy_by_bias import ResonatorSpectroscopyByBias
|
|
52
58
|
from .resonator_spectroscopy_by_power import ResonatorSpectroscopyByPower
|
53
59
|
from .t1 import T1
|
54
60
|
from .t2 import T2
|
61
|
+
from .t2_echo import T2Echo
|
55
62
|
from .transmon_anharmonicity import TransmonAnharmonicity
|
56
63
|
from .transmon_spectroscopy import TransmonSpectroscopy
|
64
|
+
from .waveforms import (
|
65
|
+
ConstantWaveform,
|
66
|
+
DragCosineWaveform,
|
67
|
+
GaussianWaveform,
|
68
|
+
Waveform,
|
69
|
+
update_amplitude,
|
70
|
+
)
|
@@ -14,13 +14,13 @@
|
|
14
14
|
from pydantic import PrivateAttr
|
15
15
|
|
16
16
|
from .common import (
|
17
|
-
ConstantWaveform,
|
18
17
|
CWSIterable,
|
19
18
|
Experiment,
|
20
19
|
HypIterable,
|
21
20
|
LinspaceIterable,
|
22
21
|
RangeIterable,
|
23
22
|
)
|
23
|
+
from .waveforms import ConstantWaveform
|
24
24
|
|
25
25
|
DEFAULT_RECYCLE_DELAY_NS = 10_000
|
26
26
|
DEFAULT_SHOT_COUNT = 100
|
@@ -34,20 +34,20 @@ class Chi01Scan(Experiment):
|
|
34
34
|
Attributes
|
35
35
|
----------
|
36
36
|
transmon : str
|
37
|
-
The reference for the transmon to target
|
37
|
+
The reference for the transmon to target.
|
38
38
|
frequencies : list[int] or LinspaceIterable or RangeIterable or CWSIterable
|
39
39
|
or HypIterable or None, optional
|
40
|
-
The frequencies at which to scan.
|
41
|
-
|
40
|
+
The frequencies at which to scan, in Hz.
|
41
|
+
Defaults to a scan around the readout frequency.
|
42
42
|
recycle_delay_ns : int, optional
|
43
|
-
The delay
|
44
|
-
Defaults to 10000 ns.
|
43
|
+
The delay between consecutive shots, in nanoseconds. Defaults to 10,000 ns.
|
45
44
|
shot_count : int, optional
|
46
|
-
The number of shots to
|
47
|
-
Defaults to 100.
|
45
|
+
The number of shots to take. Defaults to 100.
|
48
46
|
measure_waveform : ConstantWaveform or None, optional
|
49
47
|
The waveform to use for the measurement pulse.
|
50
|
-
|
48
|
+
Defaults to the measurement defcal.
|
49
|
+
run_mixer_calibration: bool
|
50
|
+
Whether to run mixer calibrations before running a program. Defaults to False.
|
51
51
|
"""
|
52
52
|
|
53
53
|
_experiment_name: str = PrivateAttr("chi01_scan")
|
@@ -59,3 +59,4 @@ class Chi01Scan(Experiment):
|
|
59
59
|
recycle_delay_ns: int = DEFAULT_RECYCLE_DELAY_NS
|
60
60
|
shot_count: int = DEFAULT_SHOT_COUNT
|
61
61
|
measure_waveform: ConstantWaveform | None = None
|
62
|
+
run_mixer_calibration: bool = False
|
@@ -14,7 +14,6 @@
|
|
14
14
|
from typing import Literal
|
15
15
|
|
16
16
|
from pydantic import BaseModel, ConfigDict, Field
|
17
|
-
from pydantic.dataclasses import dataclass
|
18
17
|
|
19
18
|
|
20
19
|
class Experiment(BaseModel):
|
@@ -98,18 +97,3 @@ class HypIterable(_RangeIterable):
|
|
98
97
|
width: float
|
99
98
|
# The number of values. Defaults to 51.
|
100
99
|
count: int = Field(default=51)
|
101
|
-
|
102
|
-
|
103
|
-
@dataclass
|
104
|
-
class ConstantWaveform:
|
105
|
-
duration_ns: int
|
106
|
-
scale: float
|
107
|
-
waveform_type: Literal["constant"] = "constant"
|
108
|
-
|
109
|
-
|
110
|
-
@dataclass
|
111
|
-
class GaussianWaveform:
|
112
|
-
duration_ns: int
|
113
|
-
amplitude: float
|
114
|
-
sigma: float
|
115
|
-
waveform_type: Literal["gaussian"] = "gaussian"
|
@@ -14,14 +14,13 @@
|
|
14
14
|
from pydantic import PrivateAttr
|
15
15
|
|
16
16
|
from .common import (
|
17
|
-
ConstantWaveform,
|
18
17
|
CWSIterable,
|
19
18
|
Experiment,
|
20
|
-
GaussianWaveform,
|
21
19
|
HypIterable,
|
22
20
|
LinspaceIterable,
|
23
21
|
RangeIterable,
|
24
22
|
)
|
23
|
+
from .waveforms import ConstantWaveform, Waveform
|
25
24
|
|
26
25
|
DEFAULT_SHOT_COUNT = 400
|
27
26
|
|
@@ -33,29 +32,33 @@ class PowerRabi(Experiment):
|
|
33
32
|
Parameters
|
34
33
|
----------
|
35
34
|
transmon : str
|
36
|
-
The reference for the transmon to target
|
35
|
+
The reference for the transmon to target.
|
37
36
|
scales : list[float] or LinspaceIterable or RangeIterable
|
38
37
|
or CWSIterable or HypIterable or None, optional
|
39
|
-
The scaling factors for the drive pulse amplitude.
|
40
|
-
|
38
|
+
The scaling factors for the drive pulse amplitude.
|
39
|
+
If None, a default scan will be used.
|
40
|
+
drive_waveform : Waveform
|
41
41
|
The waveform to use for the drive pulse.
|
42
42
|
recycle_delay_ns : int
|
43
|
-
The delay
|
43
|
+
The delay between consecutive shots, in nanoseconds.
|
44
44
|
shot_count : int, optional
|
45
|
-
The number of shots to
|
45
|
+
The number of shots to take. Defaults to 400.
|
46
46
|
pulse_vp : float, optional
|
47
|
-
The voltage per pulse.
|
47
|
+
The voltage per pulse, in Volts. Defaults to the amplitude of the X gate.
|
48
48
|
measure_waveform : ConstantWaveform or None, optional
|
49
49
|
The waveform to use for the measurement pulse.
|
50
|
-
|
50
|
+
Defaults to the measurement defcal.
|
51
|
+
run_mixer_calibration: bool
|
52
|
+
Whether to run mixer calibrations before running a program. Defaults to False.
|
51
53
|
"""
|
52
54
|
|
53
55
|
_experiment_name: str = PrivateAttr("power_rabi")
|
54
56
|
|
55
57
|
transmon: str
|
56
58
|
scales: list[float] | LinspaceIterable | RangeIterable | CWSIterable | HypIterable | None = None
|
57
|
-
drive_waveform:
|
59
|
+
drive_waveform: Waveform
|
58
60
|
recycle_delay_ns: int
|
59
61
|
shot_count: int = DEFAULT_SHOT_COUNT
|
60
62
|
pulse_vp: float | None = None
|
61
63
|
measure_waveform: ConstantWaveform | None = None
|
64
|
+
run_mixer_calibration: bool = False
|
@@ -0,0 +1,66 @@
|
|
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
|
+
from pydantic import PrivateAttr
|
15
|
+
|
16
|
+
from .common import (
|
17
|
+
CWSIterable,
|
18
|
+
Experiment,
|
19
|
+
HypIterable,
|
20
|
+
LinspaceIterable,
|
21
|
+
RangeIterable,
|
22
|
+
)
|
23
|
+
from .waveforms import ConstantWaveform, Waveform
|
24
|
+
|
25
|
+
DEFAULT_RECYCLE_DELAY_NS = 200_000
|
26
|
+
DEFAULT_SHOT_COUNT = 400
|
27
|
+
DEFAULT_DURATION_NS = 2_000
|
28
|
+
|
29
|
+
|
30
|
+
class PowerRabiEF(Experiment):
|
31
|
+
"""
|
32
|
+
Parameters for running a Power Rabi EF experiment.
|
33
|
+
|
34
|
+
Parameters
|
35
|
+
----------
|
36
|
+
transmon : str
|
37
|
+
The reference for the transmon to target.
|
38
|
+
scales : list[float] or LinspaceIterable or RangeIterable
|
39
|
+
or CWSIterable or HypIterable or None, optional
|
40
|
+
The scaling factors for the drive pulse amplitude.
|
41
|
+
If None, a default scan will be used.
|
42
|
+
drive_waveform : Waveform
|
43
|
+
The waveform to use for the drive pulse.
|
44
|
+
recycle_delay_ns : int
|
45
|
+
The delay between consecutive shots, in nanoseconds.
|
46
|
+
shot_count : int, optional
|
47
|
+
The number of shots to take. Defaults to 400.
|
48
|
+
pulse_vp : float, optional
|
49
|
+
The voltage per pulse, in Volts. Defaults to the amplitude of the X gate.
|
50
|
+
measure_waveform : ConstantWaveform or None, optional
|
51
|
+
The waveform to use for the measurement pulse.
|
52
|
+
Defaults to the measurement defcal.
|
53
|
+
run_mixer_calibration: bool
|
54
|
+
Whether to run mixer calibrations before running a program. Defaults to False.
|
55
|
+
"""
|
56
|
+
|
57
|
+
_experiment_name: str = PrivateAttr("power_rabi_ef")
|
58
|
+
|
59
|
+
transmon: str
|
60
|
+
scales: list[float] | LinspaceIterable | RangeIterable | CWSIterable | HypIterable | None = None
|
61
|
+
drive_waveform: Waveform
|
62
|
+
recycle_delay_ns: int = DEFAULT_RECYCLE_DELAY_NS
|
63
|
+
shot_count: int = DEFAULT_SHOT_COUNT
|
64
|
+
pulse_vp: float | None = None
|
65
|
+
measure_waveform: ConstantWaveform | None = None
|
66
|
+
run_mixer_calibration: bool = False
|