|
9 | 9 | from qblox_instruments.qcodes_drivers.module import Module |
10 | 10 | from qcodes.instrument import find_or_create_instrument |
11 | 11 |
|
| 12 | +from qibolab import Delay |
12 | 13 | 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 | +) |
14 | 19 | from qibolab._core.identifier import ChannelId, Result |
15 | 20 | from qibolab._core.instruments.abstract import Controller |
16 | 21 | from qibolab._core.pulses.pulse import PulseId |
|
24 | 29 | from .log import Logger |
25 | 30 | from .results import AcquiredData, extract, integration_lenghts |
26 | 31 | 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 |
29 | 40 |
|
30 | 41 | __all__ = ["Cluster"] |
31 | 42 |
|
@@ -100,19 +111,40 @@ def play( |
100 | 111 | sweepers: list[ParallelSweepers], |
101 | 112 | ) -> dict[PulseId, Result]: |
102 | 113 | """Execute the given experiment.""" |
| 114 | + assert options.relaxation_time is not None |
| 115 | + |
103 | 116 | results = {} |
104 | 117 | log = Logger(configs) |
105 | 118 |
|
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: |
108 | 145 | # full reset of the cluster, to erase leftover configurations and sequencer |
109 | 146 | # 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 |
113 | 147 | self.reset() |
114 | | - # split shots in batches, in case the required experiment exceeds the |
115 | | - # allowed memory |
116 | 148 | psres = [] |
117 | 149 | for shots in batch_shots(ps, sweepers, options): |
118 | 150 | options_ = options.model_copy(update={"nshots": shots}) |
|
0 commit comments