Skip to content

Commit c9031e7

Browse files
committed
unroll singleshot in qblox
1 parent dcc9e13 commit c9031e7

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

src/qibolab/_core/instruments/qblox/cluster.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99
from qblox_instruments.qcodes_drivers.module import Module
1010
from qcodes.instrument import find_or_create_instrument
1111

12+
from qibolab import Delay
1213
from qibolab._core.components import AcquisitionChannel, Configs, DcConfig, IqChannel
13-
from qibolab._core.execution_parameters import AcquisitionType, ExecutionParameters
14+
from qibolab._core.execution_parameters import (
15+
AcquisitionType,
16+
AveragingMode,
17+
ExecutionParameters,
18+
)
1419
from qibolab._core.identifier import ChannelId, Result
1520
from qibolab._core.instruments.abstract import Controller
1621
from qibolab._core.pulses.pulse import PulseId
@@ -24,8 +29,14 @@
2429
from .log import Logger
2530
from .results import AcquiredData, extract, integration_lenghts
2631
from .sequence import Q1Sequence, compile
27-
from .utils import batch_shots, concat_shots, lo_configs, time_of_flights
28-
from .validate import assert_channels_exclusion, validate_sequence
32+
from .utils import (
33+
batch_shots,
34+
concat_shots,
35+
get_per_shot_memory,
36+
lo_configs,
37+
time_of_flights,
38+
)
39+
from .validate import ACQUISITION_MEMORY, assert_channels_exclusion, validate_sequence
2940

3041
__all__ = ["Cluster"]
3142

@@ -100,19 +111,40 @@ def play(
100111
sweepers: list[ParallelSweepers],
101112
) -> dict[PulseId, Result]:
102113
"""Execute the given experiment."""
114+
assert options.relaxation_time is not None
115+
103116
results = {}
104117
log = Logger(configs)
105118

106-
# no unrolling yet: act one sequence at a time, and merge results
107-
for ps in sequences:
119+
if options.averaging_mode == AveragingMode.SINGLESHOT:
120+
batch_memory = 0
121+
batched_list: list[list[PulseSequence]] = [[]]
122+
for ps in sequences:
123+
per_shot_memory = get_per_shot_memory(ps, sweepers, options)
124+
125+
if batch_memory + per_shot_memory > ACQUISITION_MEMORY:
126+
batched_list.append([])
127+
batch_memory = 0
128+
129+
batch_memory += per_shot_memory
130+
batched_list[-1].append(ps)
131+
132+
batched_seqs = []
133+
for batch in batched_list:
134+
batched = batch[0]
135+
for ps in batch[1:]:
136+
# the pipe operation aligns all channels so we only have to add the
137+
# Delay to a single channel
138+
batched |= [(ps[0][0], Delay(duration=options.relaxation_time))]
139+
batched |= ps
140+
batched_seqs.append(batched)
141+
else:
142+
batched_seqs = sequences
143+
144+
for ps in batched_seqs:
108145
# full reset of the cluster, to erase leftover configurations and sequencer
109146
# synchronization registration
110-
# NOTE: until not unrolled, each sequence execution should be independent
111-
# TODO: once unrolled, this reset should be preserved, since it is required
112-
# for multiple experiments sharing the same connection
113147
self.reset()
114-
# split shots in batches, in case the required experiment exceeds the
115-
# allowed memory
116148
psres = []
117149
for shots in batch_shots(ps, sweepers, options):
118150
options_ = options.model_copy(update={"nshots": shots})

src/qibolab/_core/instruments/qblox/utils.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@
1313
from .validate import ACQUISITION_MEMORY
1414

1515

16+
def get_per_shot_memory(
17+
sequence: PulseSequence,
18+
sweepers: list[ParallelSweepers],
19+
options: ExecutionParameters,
20+
) -> float:
21+
"""Compute the memory per shot."""
22+
bins = np.prod(options.bins(sweepers)[1:])
23+
acquisitions = max(
24+
sum(1 for p in pulses if isinstance(p, (Acquisition, Readout)))
25+
for pulses in sequence.by_channel.values()
26+
)
27+
return float(bins * acquisitions)
28+
29+
1630
def batch_shots(
1731
sequence: PulseSequence,
1832
sweepers: list[ParallelSweepers],
@@ -28,12 +42,7 @@ def batch_shots(
2842
if options.averaging_mode is not AveragingMode.SINGLESHOT:
2943
return [options.nshots]
3044

31-
bins = np.prod(options.bins(sweepers)[1:])
32-
acquisitions = max(
33-
sum(1 for p in pulses if isinstance(p, (Acquisition, Readout)))
34-
for pulses in sequence.by_channel.values()
35-
)
36-
per_shot_memory = bins * acquisitions
45+
per_shot_memory = get_per_shot_memory(sequence, sweepers, options)
3746
max_shots = int(ACQUISITION_MEMORY // per_shot_memory)
3847
nfull, remainder = np.divmod(options.nshots, max_shots)
3948
return [max_shots] * int(nfull) + [int(remainder)]

0 commit comments

Comments
 (0)