From c896e80e01a376c220b2acc0049e2f5e77c52e3c Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Fri, 23 Feb 2024 18:17:35 +0400 Subject: [PATCH 01/28] draft batching_mode --- src/qibolab/__init__.py | 1 + src/qibolab/execution_parameters.py | 12 ++++++++++++ src/qibolab/instruments/unrolling.py | 20 ++++++++++++++++++++ src/qibolab/instruments/zhinst.py | 10 +++++++--- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/qibolab/__init__.py b/src/qibolab/__init__.py index 9afb03f12f..340191d5bd 100644 --- a/src/qibolab/__init__.py +++ b/src/qibolab/__init__.py @@ -9,6 +9,7 @@ from qibolab.execution_parameters import ( AcquisitionType, AveragingMode, + BatchingMode, ExecutionParameters, ) from qibolab.platform import Platform diff --git a/src/qibolab/execution_parameters.py b/src/qibolab/execution_parameters.py index b317caf138..68b131a7a8 100644 --- a/src/qibolab/execution_parameters.py +++ b/src/qibolab/execution_parameters.py @@ -37,6 +37,16 @@ class AveragingMode(Enum): """SEQUENTIAL: Worse averaging for noise[Avoid]""" +class BatchingMode(Enum): + """Data acquisition from hardware.""" + + SEQUENCES = auto() + """Demodulate, integrate the waveform and discriminate among states based + on the voltages.""" + DURATION = auto() + """Demodulate and integrate the waveform.""" + + RESULTS_TYPE = { AveragingMode.CYCLIC: { AcquisitionType.INTEGRATION: AveragedIntegratedResults, @@ -72,6 +82,8 @@ class ExecutionParameters: """Data acquisition type.""" averaging_mode: AveragingMode = AveragingMode.SINGLESHOT """Data averaging mode.""" + batching_mode: BatchingMode = BatchingMode.SEQUENCES + """Unrolling batching mode.""" @property def results_type(self): diff --git a/src/qibolab/instruments/unrolling.py b/src/qibolab/instruments/unrolling.py index 2ae60824d1..770013301a 100644 --- a/src/qibolab/instruments/unrolling.py +++ b/src/qibolab/instruments/unrolling.py @@ -17,6 +17,26 @@ def batch_max_sequences(sequences, max_size): return chunked(sequences, max_size) +def batch_max_duration(sequences, max_duration): + """Split a list of sequences to batches with a trying to get an waveform + memory for the control pulses estimate using the duration of the sequence. + + Args: + sequences (list): List of :class:`qibolab.pulses.PulseSequence` objects. + max_duration (int): Maximum number of readout pulses in a single batch. + """ + batch_duration, batch = 0, [] + for sequence in sequences: + duration = sequence.duration - sequence.ro_pulses.duration + if duration + batch_duration > max_duration: + yield batch + batch_duration, batch = duration, [sequence] + else: + batch.append(sequence) + batch_duration += duration + yield batch + + def batch_max_readout(sequences, max_measurements): """Split a list of sequences to batches with a maximum number of readout pulses in each. diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index 233c7b0356..544fab6572 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -16,11 +16,11 @@ ) from qibo.config import log -from qibolab import AcquisitionType, AveragingMode, ExecutionParameters +from qibolab import AcquisitionType, AveragingMode, BatchingMode, ExecutionParameters from qibolab.couplers import Coupler from qibolab.instruments.abstract import Controller from qibolab.instruments.port import Port -from qibolab.instruments.unrolling import batch_max_sequences +from qibolab.instruments.unrolling import batch_max_duration, batch_max_sequences from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType from qibolab.qubits import Qubit from qibolab.sweeper import Parameter, Sweeper @@ -55,6 +55,8 @@ MAX_SEQUENCES = 150 """Maximum number of subsequences in a single sequence.""" +MAX_DURATION = 10_000 +"""Maximum duration of the control pulses.""" def select_pulse(pulse, pulse_type): @@ -1403,7 +1405,9 @@ def sweep_recursion_nt( else: self.define_exp(qubits, couplers, options, exp, exp_calib) - def split_batches(self, sequences): + def split_batches(self, sequences, batching_mode: BatchingMode): + if batching_mode is BatchingMode.DURATION: + return batch_max_duration(sequences, MAX_DURATION) return batch_max_sequences(sequences, MAX_SEQUENCES) def play_sim(self, qubits, sequence, options, sim_time): From 1064358413d03f856fcd9d8add36290eb1dd3775 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Fri, 23 Feb 2024 18:24:11 +0400 Subject: [PATCH 02/28] tests --- tests/test_instruments_zhinst.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_instruments_zhinst.py b/tests/test_instruments_zhinst.py index 4edf272fa5..fba0fecb97 100644 --- a/tests/test_instruments_zhinst.py +++ b/tests/test_instruments_zhinst.py @@ -4,7 +4,13 @@ import numpy as np import pytest -from qibolab import AcquisitionType, AveragingMode, ExecutionParameters, create_platform +from qibolab import ( + AcquisitionType, + AveragingMode, + BatchingMode, + ExecutionParameters, + create_platform, +) from qibolab.instruments.zhinst import ZhPulse, ZhSweeperLine, Zurich from qibolab.pulses import ( IIR, @@ -723,7 +729,8 @@ def test_sim(dummy_qrc): sequence.add(qf_pulses[qubit]) -def test_split_batches(dummy_qrc): +@pytest.mark.parametrize("batching", [BatchingMode.DURATION, BatchingMode.SEQUENCES]) +def test_split_batches(dummy_qrc, batching): platform = create_platform("zurich") instrument = platform.instruments["EL_ZURO"] @@ -734,7 +741,7 @@ def test_split_batches(dummy_qrc): sequence.add(platform.create_MZ_pulse(0, start=measurement_start)) sequence.add(platform.create_MZ_pulse(1, start=measurement_start)) - batches = list(instrument.split_batches(200 * [sequence])) + batches = list(instrument.split_batches(200 * [sequence], batching)) assert len(batches) == 2 assert len(batches[0]) == 150 assert len(batches[1]) == 50 From 9d487568718bea3bfd6f3150887e2ddaea35b2a3 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 23 Feb 2024 16:28:04 +0100 Subject: [PATCH 03/28] feat: Propose a unified batching algorithm --- src/qibolab/instruments/unrolling.py | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/qibolab/instruments/unrolling.py b/src/qibolab/instruments/unrolling.py index 770013301a..859d8febc4 100644 --- a/src/qibolab/instruments/unrolling.py +++ b/src/qibolab/instruments/unrolling.py @@ -3,8 +3,12 @@ May be reused by different instruments. """ +from dataclasses import asdict, dataclass, field, fields + from more_itertools import chunked +from qibolab.pulses import PulseSequence + def batch_max_sequences(sequences, max_size): """Split a list of sequences to batches with a maximum number of sequences @@ -59,3 +63,64 @@ def batch_max_readout(sequences, max_measurements): batch.append(sequence) batch_measurements += nmeasurements yield batch + + +def _waveform(sequence: PulseSequence): + # TODO: deduplicate pulses + # TODO: count Rectangular and delays separately + # TODO: check if readout duration is faithful for the readout pulse + return sequence.duration + + +def _readout(sequence: PulseSequence): + return len(sequence.ro_pulses) + + +def _instructions(sequence: PulseSequence): + return len(sequence) + + +@dataclass(frozen=True, order=True) +class Bounds: + """Instument memory limitations proxies.""" + + waveforms: int = field(metadata={"count": _waveform}) + """Waveforms estimated size.""" + readout: int = field(metadata={"count": _readout}) + """Number of readouts.""" + instructions: int = field(metadata={"count": _instructions}) + """Number of readouts.""" + + @classmethod + def update(cls, sequence: PulseSequence): + up = {} + for f in fields(cls): + up[f.name] = f.metadata["count"](sequence) + + return cls(**up) + + def __add__(self, other: "Bounds") -> "Bounds": + new = {} + for (k, x), (_, y) in zip(asdict(self).items(), asdict(other).items()): + new[k] = x + y + + return type(self)(**new) + + +def batch(sequences: list[PulseSequence], bounds: Bounds): + """Split a list of sequences to batches. + + Takes into account the various limitations throught the mechanics defined in + :cls:`Bounds`, and the numerical limitations specified by the `bounds` argument. + """ + counters = Bounds(0, 0, 0) + batch = [] + for sequence in sequences: + update_ = Bounds.update(sequence) + if counters + update_ > bounds: + yield batch + counters, batch = update_, [sequence] + else: + batch.append(sequence) + counters += update_ + yield batch From 03fc57a51f3a2fb89a11a5c37231f3aa18c51ecc Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 26 Feb 2024 13:00:33 +0100 Subject: [PATCH 04/28] chore: Lift unrolling top-level --- src/qibolab/instruments/qblox/controller.py | 2 +- src/qibolab/instruments/qm/controller.py | 2 +- src/qibolab/instruments/zhinst.py | 2 +- src/qibolab/{instruments => }/unrolling.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/qibolab/{instruments => }/unrolling.py (98%) diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index 22626773ab..a709262bc2 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -10,9 +10,9 @@ from qibolab.instruments.qblox.cluster_qcm_rf import QcmRf from qibolab.instruments.qblox.cluster_qrm_rf import QrmRf from qibolab.instruments.qblox.sequencer import SAMPLING_RATE -from qibolab.instruments.unrolling import batch_max_sequences from qibolab.pulses import PulseSequence, PulseType from qibolab.sweeper import Parameter, Sweeper, SweeperType +from qibolab.unrolling import batch_max_sequences MAX_BATCH_SIZE = 30 """Maximum number of sequences that can be unrolled in a single one diff --git a/src/qibolab/instruments/qm/controller.py b/src/qibolab/instruments/qm/controller.py index aea41b4cb4..a5476d2250 100644 --- a/src/qibolab/instruments/qm/controller.py +++ b/src/qibolab/instruments/qm/controller.py @@ -9,9 +9,9 @@ from qibolab import AveragingMode from qibolab.instruments.abstract import Controller -from qibolab.instruments.unrolling import batch_max_sequences from qibolab.pulses import PulseType from qibolab.sweeper import Parameter +from qibolab.unrolling import batch_max_sequences from .acquisition import declare_acquisitions, fetch_results from .config import SAMPLING_RATE, QMConfig diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index 544fab6572..bf919a2d4d 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -20,10 +20,10 @@ from qibolab.couplers import Coupler from qibolab.instruments.abstract import Controller from qibolab.instruments.port import Port -from qibolab.instruments.unrolling import batch_max_duration, batch_max_sequences from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType from qibolab.qubits import Qubit from qibolab.sweeper import Parameter, Sweeper +from qibolab.unrolling import batch_max_duration, batch_max_sequences # this env var just needs to be set os.environ["LABONEQ_TOKEN"] = "not required" diff --git a/src/qibolab/instruments/unrolling.py b/src/qibolab/unrolling.py similarity index 98% rename from src/qibolab/instruments/unrolling.py rename to src/qibolab/unrolling.py index 859d8febc4..b523d6a0ad 100644 --- a/src/qibolab/instruments/unrolling.py +++ b/src/qibolab/unrolling.py @@ -7,7 +7,7 @@ from more_itertools import chunked -from qibolab.pulses import PulseSequence +from .pulses import PulseSequence def batch_max_sequences(sequences, max_size): From deea10c9fc4d770ef7c13242351de9e74762d4d8 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Mon, 26 Feb 2024 16:20:42 +0400 Subject: [PATCH 05/28] Refactor pulse sequence batching and add bounds --- src/qibolab/instruments/unrolling.py | 21 ++++++++++++----- src/qibolab/instruments/zhinst.py | 34 ++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/qibolab/instruments/unrolling.py b/src/qibolab/instruments/unrolling.py index 859d8febc4..3d779ef594 100644 --- a/src/qibolab/instruments/unrolling.py +++ b/src/qibolab/instruments/unrolling.py @@ -66,10 +66,11 @@ def batch_max_readout(sequences, max_measurements): def _waveform(sequence: PulseSequence): - # TODO: deduplicate pulses - # TODO: count Rectangular and delays separately - # TODO: check if readout duration is faithful for the readout pulse - return sequence.duration + # TODO: deduplicate pulses (Not yet as drivers may not support it yet) + # TODO: count Rectangular and delays separately (Zurich Instruments supports this) + # TODO: Any constant part of a pulse should be counted only once (Zurich Instruments supports this) + # TODO: check if readout duration is faithful for the readout pulse (I would only check the control pulses) + return sequence.duration - sequence.ro_pulses.duration def _readout(sequence: PulseSequence): @@ -80,7 +81,7 @@ def _instructions(sequence: PulseSequence): return len(sequence) -@dataclass(frozen=True, order=True) +@dataclass(frozen=True, order=False) class Bounds: """Instument memory limitations proxies.""" @@ -89,7 +90,7 @@ class Bounds: readout: int = field(metadata={"count": _readout}) """Number of readouts.""" instructions: int = field(metadata={"count": _instructions}) - """Number of readouts.""" + """Instructions estimated size.""" @classmethod def update(cls, sequence: PulseSequence): @@ -106,6 +107,14 @@ def __add__(self, other: "Bounds") -> "Bounds": return type(self)(**new) + def __lt__(self, other: "Bounds") -> bool: + return any(getattr(self, f.name) < getattr(other, f.name) for f in fields(self)) + + def __ge__(self, other: "Bounds") -> bool: + return all( + getattr(self, f.name) >= getattr(other, f.name) for f in fields(self) + ) + def batch(sequences: list[PulseSequence], bounds: Bounds): """Split a list of sequences to batches. diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index 544fab6572..a0a972de8e 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -16,11 +16,14 @@ ) from qibo.config import log -from qibolab import AcquisitionType, AveragingMode, BatchingMode, ExecutionParameters +from qibolab import AcquisitionType, AveragingMode, ExecutionParameters from qibolab.couplers import Coupler from qibolab.instruments.abstract import Controller from qibolab.instruments.port import Port -from qibolab.instruments.unrolling import batch_max_duration, batch_max_sequences +from qibolab.instruments.unrolling import ( + Bounds, + batch, +) from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType from qibolab.qubits import Qubit from qibolab.sweeper import Parameter, Sweeper @@ -53,10 +56,19 @@ SWEEPER_BIAS = {"bias"} SWEEPER_START = {"start"} -MAX_SEQUENCES = 150 -"""Maximum number of subsequences in a single sequence.""" -MAX_DURATION = 10_000 -"""Maximum duration of the control pulses.""" + +MAX_DURATION = int(4e4) +"""Maximum duration of the control pulses [1q 40ns] [Rough estimate].""" +MAX_READOUT = int(1e6) +"""Maximum number of readout pulses [Not estimated].""" +MAX_INSTRUCTIONS = int(1e6) +"""Maximum instructions size [Not estimated].""" + +BOUNDS = Bounds( + waveforms=MAX_DURATION, + readout=MAX_READOUT, + instructions=MAX_READOUT, +) def select_pulse(pulse, pulse_type): @@ -1405,10 +1417,12 @@ def sweep_recursion_nt( else: self.define_exp(qubits, couplers, options, exp, exp_calib) - def split_batches(self, sequences, batching_mode: BatchingMode): - if batching_mode is BatchingMode.DURATION: - return batch_max_duration(sequences, MAX_DURATION) - return batch_max_sequences(sequences, MAX_SEQUENCES) + def split_batches(self, sequences, bounds=BOUNDS): + return batch(sequences, bounds) + + # if batching_mode is BatchingMode.DURATION: + # return batch_max_duration(sequences, MAX_DURATION) + # return batch_max_sequences(sequences, MAX_SEQUENCES) def play_sim(self, qubits, sequence, options, sim_time): """Play pulse sequence.""" From 86f56f16890f5290da8b8a1a01658ab73d6e7f99 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:26:23 +0000 Subject: [PATCH 06/28] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibolab/instruments/zhinst.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index 1dd3ea5e0b..629f0c2077 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -20,14 +20,10 @@ from qibolab.couplers import Coupler from qibolab.instruments.abstract import Controller from qibolab.instruments.port import Port -from qibolab.unrolling import ( - Bounds, - batch, -) from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType from qibolab.qubits import Qubit from qibolab.sweeper import Parameter, Sweeper -from qibolab.unrolling import batch_max_duration, batch_max_sequences +from qibolab.unrolling import Bounds, batch # this env var just needs to be set os.environ["LABONEQ_TOKEN"] = "not required" From 4dcd58785872332ba9bccde5e53fff525059ad54 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Mon, 26 Feb 2024 16:27:24 +0400 Subject: [PATCH 07/28] remove zurich unused --- src/qibolab/instruments/zhinst.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index 1dd3ea5e0b..bb840ec83f 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -20,14 +20,10 @@ from qibolab.couplers import Coupler from qibolab.instruments.abstract import Controller from qibolab.instruments.port import Port -from qibolab.unrolling import ( - Bounds, - batch, -) from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType from qibolab.qubits import Qubit from qibolab.sweeper import Parameter, Sweeper -from qibolab.unrolling import batch_max_duration, batch_max_sequences +from qibolab.unrolling import Bounds, batch # this env var just needs to be set os.environ["LABONEQ_TOKEN"] = "not required" @@ -1421,10 +1417,6 @@ def sweep_recursion_nt( def split_batches(self, sequences, bounds=BOUNDS): return batch(sequences, bounds) - # if batching_mode is BatchingMode.DURATION: - # return batch_max_duration(sequences, MAX_DURATION) - # return batch_max_sequences(sequences, MAX_SEQUENCES) - def play_sim(self, qubits, sequence, options, sim_time): """Play pulse sequence.""" From 266769585268670a11aaf5c5c5459ef9b2f34dab Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Mon, 26 Feb 2024 16:44:32 +0400 Subject: [PATCH 08/28] Refactor batch splitting logic in controller classes --- src/qibolab/instruments/qblox/controller.py | 22 +++++--- src/qibolab/instruments/qm/controller.py | 20 +++++-- src/qibolab/instruments/zhinst.py | 4 +- src/qibolab/unrolling.py | 58 +-------------------- tests/test_instruments_zhinst.py | 19 +++---- 5 files changed, 41 insertions(+), 82 deletions(-) diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index a709262bc2..2c0147726a 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -12,11 +12,21 @@ from qibolab.instruments.qblox.sequencer import SAMPLING_RATE from qibolab.pulses import PulseSequence, PulseType from qibolab.sweeper import Parameter, Sweeper, SweeperType -from qibolab.unrolling import batch_max_sequences +from qibolab.unrolling import Bounds, batch + +MAX_DURATION = int(4e4) # Translate SEQUENCER_MEMORY = 2**17 into pulse duration +"""Maximum duration of the control pulses [1q 40ns] [Rough estimate].""" +MAX_READOUT = int(1e6) +"""Maximum number of readout pulses [Not estimated].""" +MAX_INSTRUCTIONS = int(1e6) +"""Maximum instructions size [Not estimated].""" + +BOUNDS = Bounds( + waveforms=MAX_DURATION, + readout=MAX_READOUT, + instructions=MAX_READOUT, +) -MAX_BATCH_SIZE = 30 -"""Maximum number of sequences that can be unrolled in a single one -(independent of measurements).""" SEQUENCER_MEMORY = 2**17 @@ -218,8 +228,8 @@ def _execute_pulse_sequence( def play(self, qubits, couplers, sequence, options): return self._execute_pulse_sequence(qubits, sequence, options) - def split_batches(self, sequences): - return batch_max_sequences(sequences, MAX_BATCH_SIZE) + def split_batches(self, sequences, bounds=BOUNDS): + return batch(sequences, bounds) def sweep( self, diff --git a/src/qibolab/instruments/qm/controller.py b/src/qibolab/instruments/qm/controller.py index a5476d2250..df5f619ea3 100644 --- a/src/qibolab/instruments/qm/controller.py +++ b/src/qibolab/instruments/qm/controller.py @@ -11,7 +11,7 @@ from qibolab.instruments.abstract import Controller from qibolab.pulses import PulseType from qibolab.sweeper import Parameter -from qibolab.unrolling import batch_max_sequences +from qibolab.unrolling import Bounds, batch from .acquisition import declare_acquisitions, fetch_results from .config import SAMPLING_RATE, QMConfig @@ -23,7 +23,19 @@ OCTAVE_ADDRESS_OFFSET = 11000 """Offset to be added to Octave addresses, because they must be 11xxx, where xxx are the last three digits of the Octave IP address.""" -MAX_BATCH_SIZE = 30 + +MAX_DURATION = int(4e4) +"""Maximum duration of the control pulses [1q 40ns] [Rough estimate].""" +MAX_READOUT = int(30) +"""Maximum number of readout pulses [Not estimated].""" +MAX_INSTRUCTIONS = int(1e6) +"""Maximum instructions size [Not estimated].""" + +BOUNDS = Bounds( + waveforms=MAX_DURATION, + readout=MAX_READOUT, + instructions=MAX_READOUT, +) def declare_octaves(octaves, host, calibration_path=None): @@ -364,5 +376,5 @@ def sweep(self, qubits, couplers, sequence, options, *sweepers): result = self.execute_program(experiment) return fetch_results(result, acquisitions) - def split_batches(self, sequences): - return batch_max_sequences(sequences, MAX_BATCH_SIZE) + def split_batches(self, sequences, bounds=BOUNDS): + return batch(sequences, bounds) diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index bb840ec83f..8a2ed59200 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -56,7 +56,7 @@ MAX_DURATION = int(4e4) """Maximum duration of the control pulses [1q 40ns] [Rough estimate].""" -MAX_READOUT = int(1e6) +MAX_READOUT = 150 """Maximum number of readout pulses [Not estimated].""" MAX_INSTRUCTIONS = int(1e6) """Maximum instructions size [Not estimated].""" @@ -64,7 +64,7 @@ BOUNDS = Bounds( waveforms=MAX_DURATION, readout=MAX_READOUT, - instructions=MAX_READOUT, + instructions=MAX_INSTRUCTIONS, ) diff --git a/src/qibolab/unrolling.py b/src/qibolab/unrolling.py index c9f909cf8a..3b145a083c 100644 --- a/src/qibolab/unrolling.py +++ b/src/qibolab/unrolling.py @@ -5,71 +5,15 @@ from dataclasses import asdict, dataclass, field, fields -from more_itertools import chunked - from .pulses import PulseSequence -def batch_max_sequences(sequences, max_size): - """Split a list of sequences to batches with a maximum number of sequences - in each. - - Args: - sequences (list): List of :class:`qibolab.pulses.PulseSequence` objects. - max_size (int): Maximum number of sequences in a single batch. - """ - return chunked(sequences, max_size) - - -def batch_max_duration(sequences, max_duration): - """Split a list of sequences to batches with a trying to get an waveform - memory for the control pulses estimate using the duration of the sequence. - - Args: - sequences (list): List of :class:`qibolab.pulses.PulseSequence` objects. - max_duration (int): Maximum number of readout pulses in a single batch. - """ - batch_duration, batch = 0, [] - for sequence in sequences: - duration = sequence.duration - sequence.ro_pulses.duration - if duration + batch_duration > max_duration: - yield batch - batch_duration, batch = duration, [sequence] - else: - batch.append(sequence) - batch_duration += duration - yield batch - - -def batch_max_readout(sequences, max_measurements): - """Split a list of sequences to batches with a maximum number of readout - pulses in each. - - Useful for sequence unrolling on instruments that support a maximum number of readout pulses - in a single sequence due to memory limitations. - - Args: - sequences (list): List of :class:`qibolab.pulses.PulseSequence` objects. - max_measurements (int): Maximum number of readout pulses in a single batch. - """ - - batch_measurements, batch = 0, [] - for sequence in sequences: - nmeasurements = len(sequence.ro_pulses) - if nmeasurements + batch_measurements > max_measurements: - yield batch - batch_measurements, batch = nmeasurements, [sequence] - else: - batch.append(sequence) - batch_measurements += nmeasurements - yield batch - - def _waveform(sequence: PulseSequence): # TODO: deduplicate pulses (Not yet as drivers may not support it yet) # TODO: count Rectangular and delays separately (Zurich Instruments supports this) # TODO: Any constant part of a pulse should be counted only once (Zurich Instruments supports this) # TODO: check if readout duration is faithful for the readout pulse (I would only check the control pulses) + # TODO: Handle multiple qubits or do all devices have the same memory for each channel ? return sequence.duration - sequence.ro_pulses.duration diff --git a/tests/test_instruments_zhinst.py b/tests/test_instruments_zhinst.py index fba0fecb97..304834690e 100644 --- a/tests/test_instruments_zhinst.py +++ b/tests/test_instruments_zhinst.py @@ -4,13 +4,7 @@ import numpy as np import pytest -from qibolab import ( - AcquisitionType, - AveragingMode, - BatchingMode, - ExecutionParameters, - create_platform, -) +from qibolab import AcquisitionType, AveragingMode, ExecutionParameters, create_platform from qibolab.instruments.zhinst import ZhPulse, ZhSweeperLine, Zurich from qibolab.pulses import ( IIR, @@ -729,8 +723,7 @@ def test_sim(dummy_qrc): sequence.add(qf_pulses[qubit]) -@pytest.mark.parametrize("batching", [BatchingMode.DURATION, BatchingMode.SEQUENCES]) -def test_split_batches(dummy_qrc, batching): +def test_split_batches(dummy_qrc): platform = create_platform("zurich") instrument = platform.instruments["EL_ZURO"] @@ -741,10 +734,10 @@ def test_split_batches(dummy_qrc, batching): sequence.add(platform.create_MZ_pulse(0, start=measurement_start)) sequence.add(platform.create_MZ_pulse(1, start=measurement_start)) - batches = list(instrument.split_batches(200 * [sequence], batching)) - assert len(batches) == 2 - assert len(batches[0]) == 150 - assert len(batches[1]) == 50 + batches = list(instrument.split_batches(200 * [sequence])) + assert len(batches) == 3 + assert len(batches[0]) == 75 + assert len(batches[1]) == 75 @pytest.fixture(scope="module") From dfeebb88e6eb9c0916c52864944c1105a4648c39 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Mon, 26 Feb 2024 16:56:30 +0400 Subject: [PATCH 09/28] Remove BatchingMode enum and related code --- src/qibolab/__init__.py | 1 - src/qibolab/execution_parameters.py | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/src/qibolab/__init__.py b/src/qibolab/__init__.py index 340191d5bd..9afb03f12f 100644 --- a/src/qibolab/__init__.py +++ b/src/qibolab/__init__.py @@ -9,7 +9,6 @@ from qibolab.execution_parameters import ( AcquisitionType, AveragingMode, - BatchingMode, ExecutionParameters, ) from qibolab.platform import Platform diff --git a/src/qibolab/execution_parameters.py b/src/qibolab/execution_parameters.py index 68b131a7a8..b317caf138 100644 --- a/src/qibolab/execution_parameters.py +++ b/src/qibolab/execution_parameters.py @@ -37,16 +37,6 @@ class AveragingMode(Enum): """SEQUENTIAL: Worse averaging for noise[Avoid]""" -class BatchingMode(Enum): - """Data acquisition from hardware.""" - - SEQUENCES = auto() - """Demodulate, integrate the waveform and discriminate among states based - on the voltages.""" - DURATION = auto() - """Demodulate and integrate the waveform.""" - - RESULTS_TYPE = { AveragingMode.CYCLIC: { AcquisitionType.INTEGRATION: AveragedIntegratedResults, @@ -82,8 +72,6 @@ class ExecutionParameters: """Data acquisition type.""" averaging_mode: AveragingMode = AveragingMode.SINGLESHOT """Data averaging mode.""" - batching_mode: BatchingMode = BatchingMode.SEQUENCES - """Unrolling batching mode.""" @property def results_type(self): From aff3f24ef5e8841f180d63605413d3eec65fed7b Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 26 Feb 2024 14:06:48 +0100 Subject: [PATCH 10/28] chore: Implement order on bounds with minimal operations --- src/qibolab/unrolling.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/qibolab/unrolling.py b/src/qibolab/unrolling.py index 3b145a083c..e417436d34 100644 --- a/src/qibolab/unrolling.py +++ b/src/qibolab/unrolling.py @@ -4,6 +4,7 @@ """ from dataclasses import asdict, dataclass, field, fields +from functools import total_ordering from .pulses import PulseSequence @@ -25,7 +26,8 @@ def _instructions(sequence: PulseSequence): return len(sequence) -@dataclass(frozen=True, order=False) +@total_ordering +@dataclass(frozen=True, eq=True) class Bounds: """Instument memory limitations proxies.""" @@ -54,11 +56,6 @@ def __add__(self, other: "Bounds") -> "Bounds": def __lt__(self, other: "Bounds") -> bool: return any(getattr(self, f.name) < getattr(other, f.name) for f in fields(self)) - def __ge__(self, other: "Bounds") -> bool: - return all( - getattr(self, f.name) >= getattr(other, f.name) for f in fields(self) - ) - def batch(sequences: list[PulseSequence], bounds: Bounds): """Split a list of sequences to batches. From dd20f4eb3f0e10a8d500e7ad08649c3a9a9d127c Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Mon, 26 Feb 2024 17:29:33 +0400 Subject: [PATCH 11/28] Update MAX_READOUT constant to 250 --- src/qibolab/instruments/zhinst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index 8a2ed59200..58f2bdd75e 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -56,7 +56,7 @@ MAX_DURATION = int(4e4) """Maximum duration of the control pulses [1q 40ns] [Rough estimate].""" -MAX_READOUT = 150 +MAX_READOUT = 250 """Maximum number of readout pulses [Not estimated].""" MAX_INSTRUCTIONS = int(1e6) """Maximum instructions size [Not estimated].""" From 060bbf8b90d8a35a0fd1c78a254283cc7da20bfd Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 26 Feb 2024 14:44:53 +0100 Subject: [PATCH 12/28] refactor: Drop batch splitting functions In favor of the usage of the single one with multiple bounds --- src/qibolab/instruments/abstract.py | 23 ++++++--------------- src/qibolab/instruments/dummy.py | 13 ++++++------ src/qibolab/instruments/qblox/controller.py | 17 +++++++-------- src/qibolab/instruments/qm/controller.py | 17 +++++++-------- src/qibolab/instruments/zhinst.py | 22 +++++++++----------- src/qibolab/platform.py | 16 +++++++------- tests/test_instruments_zhinst.py | 5 +++-- 7 files changed, 49 insertions(+), 64 deletions(-) diff --git a/src/qibolab/instruments/abstract.py b/src/qibolab/instruments/abstract.py index a181a3ca26..e034cda672 100644 --- a/src/qibolab/instruments/abstract.py +++ b/src/qibolab/instruments/abstract.py @@ -2,7 +2,9 @@ from dataclasses import asdict, dataclass from typing import Optional -from qibolab.instruments.port import Port +from qibolab.unrolling import Bounds + +from .port import Port InstrumentId = str @@ -56,6 +58,9 @@ class Controller(Instrument): PortType = Port """Class used by the instrument to instantiate ports.""" + BOUNDS: Bounds = Bounds(0, 0, 0) + """Estimated limitations of the device memory.""" + def __init__(self, name, address): super().__init__(name, address) self._ports = {} @@ -90,22 +95,6 @@ def play(self, *args, **kwargs): the acquired :class:`qibolab.result.ExecutionResults` object. """ - def split_batches(self, sequences): # pragma: no cover - """Split sequences to batches each of which can be unrolled and played - as a single sequence. - - Args: - sequence (list): List of :class:`qibolab.pulses.PulseSequence` to be played. - - Returns: - Iterator of batches that can be unrolled in a single one. - Default will return all sequences as a single batch. - """ - raise RuntimeError( - f"Instrument of type {type(self)} does not support " - "batch splitting for sequence unrolling." - ) - @abstractmethod def sweep(self, *args, **kwargs): """Play a pulse sequence while sweeping one or more parameters. diff --git a/src/qibolab/instruments/dummy.py b/src/qibolab/instruments/dummy.py index 1859ed2703..5be2dce451 100644 --- a/src/qibolab/instruments/dummy.py +++ b/src/qibolab/instruments/dummy.py @@ -5,13 +5,15 @@ from qibo.config import log from qibolab import AcquisitionType, AveragingMode, ExecutionParameters -from qibolab.instruments.abstract import Controller -from qibolab.instruments.oscillator import LocalOscillator -from qibolab.instruments.port import Port from qibolab.platform import Coupler, Qubit from qibolab.pulses import PulseSequence from qibolab.qubits import QubitId from qibolab.sweeper import Sweeper +from qibolab.unrolling import Bounds + +from .abstract import Controller +from .oscillator import LocalOscillator +from .port import Port SAMPLING_RATE = 1 @@ -74,6 +76,8 @@ class DummyInstrument(Controller): instruments. """ + BOUNDS = Bounds(1, 1, 1) + PortType = DummyPort @property @@ -127,9 +131,6 @@ def play( return results - def split_batches(self, sequences): - return [sequences] - def sweep( self, qubits: Dict[QubitId, Qubit], diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index 2c0147726a..976ff0b765 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -12,7 +12,7 @@ from qibolab.instruments.qblox.sequencer import SAMPLING_RATE from qibolab.pulses import PulseSequence, PulseType from qibolab.sweeper import Parameter, Sweeper, SweeperType -from qibolab.unrolling import Bounds, batch +from qibolab.unrolling import Bounds MAX_DURATION = int(4e4) # Translate SEQUENCER_MEMORY = 2**17 into pulse duration """Maximum duration of the control pulses [1q 40ns] [Rough estimate].""" @@ -21,12 +21,6 @@ MAX_INSTRUCTIONS = int(1e6) """Maximum instructions size [Not estimated].""" -BOUNDS = Bounds( - waveforms=MAX_DURATION, - readout=MAX_READOUT, - instructions=MAX_READOUT, -) - SEQUENCER_MEMORY = 2**17 @@ -38,6 +32,12 @@ class QbloxController(Controller): modules (dict): A dictionay with the qblox modules connected to the experiment. """ + BOUNDS = Bounds( + waveforms=MAX_DURATION, + readout=MAX_READOUT, + instructions=MAX_READOUT, + ) + def __init__( self, name, address: str, modules, internal_reference_clock: bool = True ): @@ -228,9 +228,6 @@ def _execute_pulse_sequence( def play(self, qubits, couplers, sequence, options): return self._execute_pulse_sequence(qubits, sequence, options) - def split_batches(self, sequences, bounds=BOUNDS): - return batch(sequences, bounds) - def sweep( self, qubits: dict, diff --git a/src/qibolab/instruments/qm/controller.py b/src/qibolab/instruments/qm/controller.py index df5f619ea3..7bdc804546 100644 --- a/src/qibolab/instruments/qm/controller.py +++ b/src/qibolab/instruments/qm/controller.py @@ -11,7 +11,7 @@ from qibolab.instruments.abstract import Controller from qibolab.pulses import PulseType from qibolab.sweeper import Parameter -from qibolab.unrolling import Bounds, batch +from qibolab.unrolling import Bounds from .acquisition import declare_acquisitions, fetch_results from .config import SAMPLING_RATE, QMConfig @@ -31,12 +31,6 @@ MAX_INSTRUCTIONS = int(1e6) """Maximum instructions size [Not estimated].""" -BOUNDS = Bounds( - waveforms=MAX_DURATION, - readout=MAX_READOUT, - instructions=MAX_READOUT, -) - def declare_octaves(octaves, host, calibration_path=None): """Initiate Octave configuration and add octaves info. @@ -132,6 +126,12 @@ class QMController(Controller): """Dictionary containing the :class:`qibolab.instruments.qm.devices.Octave` instruments being used.""" + BOUNDS = Bounds( + waveforms=MAX_DURATION, + readout=MAX_READOUT, + instructions=MAX_READOUT, + ) + time_of_flight: int = 0 """Time of flight used for hardware signal integration.""" smearing: int = 0 @@ -375,6 +375,3 @@ def sweep(self, qubits, couplers, sequence, options, *sweepers): else: result = self.execute_program(experiment) return fetch_results(result, acquisitions) - - def split_batches(self, sequences, bounds=BOUNDS): - return batch(sequences, bounds) diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index 58f2bdd75e..3d177364a2 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -18,12 +18,13 @@ from qibolab import AcquisitionType, AveragingMode, ExecutionParameters from qibolab.couplers import Coupler -from qibolab.instruments.abstract import Controller -from qibolab.instruments.port import Port from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType from qibolab.qubits import Qubit from qibolab.sweeper import Parameter, Sweeper -from qibolab.unrolling import Bounds, batch +from qibolab.unrolling import Bounds + +from .abstract import Controller +from .port import Port # this env var just needs to be set os.environ["LABONEQ_TOKEN"] = "not required" @@ -61,12 +62,6 @@ MAX_INSTRUCTIONS = int(1e6) """Maximum instructions size [Not estimated].""" -BOUNDS = Bounds( - waveforms=MAX_DURATION, - readout=MAX_READOUT, - instructions=MAX_INSTRUCTIONS, -) - def select_pulse(pulse, pulse_type): """Pulse translation.""" @@ -303,6 +298,12 @@ class Zurich(Controller): PortType = ZhPort + BOUNDS = Bounds( + waveforms=MAX_DURATION, + readout=MAX_READOUT, + instructions=MAX_INSTRUCTIONS, + ) + def __init__( self, name, device_setup, use_emulation=False, time_of_flight=0.0, smearing=0.0 ): @@ -1414,9 +1415,6 @@ def sweep_recursion_nt( else: self.define_exp(qubits, couplers, options, exp, exp_calib) - def split_batches(self, sequences, bounds=BOUNDS): - return batch(sequences, bounds) - def play_sim(self, qubits, sequence, options, sim_time): """Play pulse sequence.""" diff --git a/src/qibolab/platform.py b/src/qibolab/platform.py index 0371f4b6b0..c925fc595e 100644 --- a/src/qibolab/platform.py +++ b/src/qibolab/platform.py @@ -7,12 +7,13 @@ import networkx as nx from qibo.config import log, raise_error -from qibolab.couplers import Coupler -from qibolab.execution_parameters import ExecutionParameters -from qibolab.instruments.abstract import Controller, Instrument, InstrumentId -from qibolab.pulses import FluxPulse, PulseSequence, ReadoutPulse -from qibolab.qubits import Qubit, QubitId, QubitPair, QubitPairId -from qibolab.sweeper import Sweeper +from .couplers import Coupler +from .execution_parameters import ExecutionParameters +from .instruments.abstract import Controller, Instrument, InstrumentId +from .pulses import FluxPulse, PulseSequence, ReadoutPulse +from .qubits import Qubit, QubitId, QubitPair, QubitPairId +from .sweeper import Sweeper +from .unrolling import batch InstrumentMap = Dict[InstrumentId, Instrument] QubitMap = Dict[QubitId, Qubit] @@ -239,7 +240,8 @@ def execute_pulse_sequences( } results = defaultdict(list) - for batch in self._controller.split_batches(sequences): + bounds = kwargs.get("bounds", self._controller.BOUNDS) + for batch in batch(sequences, bounds): sequence, readouts = unroll_sequences(batch, options.relaxation_time) result = self._execute(sequence, options, **kwargs) for serial, new_serials in readouts.items(): diff --git a/tests/test_instruments_zhinst.py b/tests/test_instruments_zhinst.py index 304834690e..075e13be0c 100644 --- a/tests/test_instruments_zhinst.py +++ b/tests/test_instruments_zhinst.py @@ -19,6 +19,7 @@ Rectangular, ) from qibolab.sweeper import Parameter, Sweeper, SweeperType +from qibolab.unrolling import batch from .conftest import get_instrument @@ -723,7 +724,7 @@ def test_sim(dummy_qrc): sequence.add(qf_pulses[qubit]) -def test_split_batches(dummy_qrc): +def test_batching(dummy_qrc): platform = create_platform("zurich") instrument = platform.instruments["EL_ZURO"] @@ -734,7 +735,7 @@ def test_split_batches(dummy_qrc): sequence.add(platform.create_MZ_pulse(0, start=measurement_start)) sequence.add(platform.create_MZ_pulse(1, start=measurement_start)) - batches = list(instrument.split_batches(200 * [sequence])) + batches = list(batch(200 * [sequence], instrument.BOUNDS)) assert len(batches) == 3 assert len(batches[0]) == 75 assert len(batches[1]) == 75 From 0edb4604bc5ba3a14ee77e1f96b870e493794a1e Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 27 Feb 2024 11:48:36 +0400 Subject: [PATCH 13/28] Fix loop for unrolling in platform --- src/qibolab/platform.py | 4 ++-- src/qibolab/unrolling.py | 9 ++++++--- tests/test_instruments_zhinst.py | 9 +++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/qibolab/platform.py b/src/qibolab/platform.py index c925fc595e..c14d691201 100644 --- a/src/qibolab/platform.py +++ b/src/qibolab/platform.py @@ -241,8 +241,8 @@ def execute_pulse_sequences( results = defaultdict(list) bounds = kwargs.get("bounds", self._controller.BOUNDS) - for batch in batch(sequences, bounds): - sequence, readouts = unroll_sequences(batch, options.relaxation_time) + for b in batch(sequences, bounds): + sequence, readouts = unroll_sequences(b, options.relaxation_time) result = self._execute(sequence, options, **kwargs) for serial, new_serials in readouts.items(): results[serial].extend(result[ser] for ser in new_serials) diff --git a/src/qibolab/unrolling.py b/src/qibolab/unrolling.py index e417436d34..3b145a083c 100644 --- a/src/qibolab/unrolling.py +++ b/src/qibolab/unrolling.py @@ -4,7 +4,6 @@ """ from dataclasses import asdict, dataclass, field, fields -from functools import total_ordering from .pulses import PulseSequence @@ -26,8 +25,7 @@ def _instructions(sequence: PulseSequence): return len(sequence) -@total_ordering -@dataclass(frozen=True, eq=True) +@dataclass(frozen=True, order=False) class Bounds: """Instument memory limitations proxies.""" @@ -56,6 +54,11 @@ def __add__(self, other: "Bounds") -> "Bounds": def __lt__(self, other: "Bounds") -> bool: return any(getattr(self, f.name) < getattr(other, f.name) for f in fields(self)) + def __ge__(self, other: "Bounds") -> bool: + return all( + getattr(self, f.name) >= getattr(other, f.name) for f in fields(self) + ) + def batch(sequences: list[PulseSequence], bounds: Bounds): """Split a list of sequences to batches. diff --git a/tests/test_instruments_zhinst.py b/tests/test_instruments_zhinst.py index 075e13be0c..22e35bfc49 100644 --- a/tests/test_instruments_zhinst.py +++ b/tests/test_instruments_zhinst.py @@ -735,10 +735,11 @@ def test_batching(dummy_qrc): sequence.add(platform.create_MZ_pulse(0, start=measurement_start)) sequence.add(platform.create_MZ_pulse(1, start=measurement_start)) - batches = list(batch(200 * [sequence], instrument.BOUNDS)) - assert len(batches) == 3 - assert len(batches[0]) == 75 - assert len(batches[1]) == 75 + batches = list(batch(600 * [sequence], instrument.BOUNDS)) + # These sequences get limited by the number of measuraments (600/250/2) + assert len(batches) == 5 + assert len(batches[0]) == 125 + assert len(batches[1]) == 125 @pytest.fixture(scope="module") From 8ebf6ffab3274119c55893869d126e6e97bb3d27 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 27 Feb 2024 09:28:07 +0100 Subject: [PATCH 14/28] fix: Keep only minimal bug correction, retain former improvements --- src/qibolab/unrolling.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/qibolab/unrolling.py b/src/qibolab/unrolling.py index 3b145a083c..e417436d34 100644 --- a/src/qibolab/unrolling.py +++ b/src/qibolab/unrolling.py @@ -4,6 +4,7 @@ """ from dataclasses import asdict, dataclass, field, fields +from functools import total_ordering from .pulses import PulseSequence @@ -25,7 +26,8 @@ def _instructions(sequence: PulseSequence): return len(sequence) -@dataclass(frozen=True, order=False) +@total_ordering +@dataclass(frozen=True, eq=True) class Bounds: """Instument memory limitations proxies.""" @@ -54,11 +56,6 @@ def __add__(self, other: "Bounds") -> "Bounds": def __lt__(self, other: "Bounds") -> bool: return any(getattr(self, f.name) < getattr(other, f.name) for f in fields(self)) - def __ge__(self, other: "Bounds") -> bool: - return all( - getattr(self, f.name) >= getattr(other, f.name) for f in fields(self) - ) - def batch(sequences: list[PulseSequence], bounds: Bounds): """Split a list of sequences to batches. From 43560094dbd9b058bf0320fe3c3544cc9d0928b7 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 27 Feb 2024 09:43:18 +0100 Subject: [PATCH 15/28] fix: Define greater rather than less, since it's the one directly used --- src/qibolab/unrolling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibolab/unrolling.py b/src/qibolab/unrolling.py index e417436d34..d221bc6f52 100644 --- a/src/qibolab/unrolling.py +++ b/src/qibolab/unrolling.py @@ -53,8 +53,8 @@ def __add__(self, other: "Bounds") -> "Bounds": return type(self)(**new) - def __lt__(self, other: "Bounds") -> bool: - return any(getattr(self, f.name) < getattr(other, f.name) for f in fields(self)) + def __gt__(self, other: "Bounds") -> bool: + return any(getattr(self, f.name) > getattr(other, f.name) for f in fields(self)) def batch(sequences: list[PulseSequence], bounds: Bounds): From a38d2f7f3aceef4ed8f9d005f30ee331e453383a Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 27 Feb 2024 09:58:30 +0100 Subject: [PATCH 16/28] docs: Add docstrings to the Bounds dunders --- src/qibolab/unrolling.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/qibolab/unrolling.py b/src/qibolab/unrolling.py index d221bc6f52..ccb7107172 100644 --- a/src/qibolab/unrolling.py +++ b/src/qibolab/unrolling.py @@ -47,6 +47,7 @@ def update(cls, sequence: PulseSequence): return cls(**up) def __add__(self, other: "Bounds") -> "Bounds": + """Sum bounds element by element.""" new = {} for (k, x), (_, y) in zip(asdict(self).items(), asdict(other).items()): new[k] = x + y @@ -54,6 +55,7 @@ def __add__(self, other: "Bounds") -> "Bounds": return type(self)(**new) def __gt__(self, other: "Bounds") -> bool: + """Define ordering as exceeding any bound.""" return any(getattr(self, f.name) > getattr(other, f.name) for f in fields(self)) @@ -66,11 +68,11 @@ def batch(sequences: list[PulseSequence], bounds: Bounds): counters = Bounds(0, 0, 0) batch = [] for sequence in sequences: - update_ = Bounds.update(sequence) - if counters + update_ > bounds: + update = Bounds.update(sequence) + if counters + update > bounds: yield batch - counters, batch = update_, [sequence] + counters, batch = update, [sequence] else: batch.append(sequence) - counters += update_ + counters += update yield batch From 22feafac8508210471723a758b204286660a2156 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 27 Feb 2024 17:11:48 +0400 Subject: [PATCH 17/28] Add tests for unrolling.py --- src/qibolab/unrolling.py | 1 + tests/test_unrolling.py | 67 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/test_unrolling.py diff --git a/src/qibolab/unrolling.py b/src/qibolab/unrolling.py index ccb7107172..df9b250845 100644 --- a/src/qibolab/unrolling.py +++ b/src/qibolab/unrolling.py @@ -19,6 +19,7 @@ def _waveform(sequence: PulseSequence): def _readout(sequence: PulseSequence): + # TODO: Do we count 1 readout per pulse or 1 readout per multiplexed readout ? return len(sequence.ro_pulses) diff --git a/tests/test_unrolling.py b/tests/test_unrolling.py new file mode 100644 index 0000000000..b13d3a71da --- /dev/null +++ b/tests/test_unrolling.py @@ -0,0 +1,67 @@ +"""Tests ``unrolling.py``.""" + +import pytest + +from qibolab.pulses import Drag, Pulse, PulseSequence, PulseType, Rectangular +from qibolab.unrolling import Bounds, batch + + +def test_bounds_update(): + p1 = Pulse(400, 40, 0.9, int(100e6), 0, Drag(5, 1), 3, PulseType.DRIVE) + p2 = Pulse(500, 40, 0.9, int(100e6), 0, Drag(5, 1), 2, PulseType.DRIVE) + p3 = Pulse(600, 40, 0.9, int(100e6), 0, Drag(5, 1), 1, PulseType.DRIVE) + + p4 = Pulse(440, 1000, 0.9, int(20e6), 0, Rectangular(), 3, PulseType.READOUT) + p5 = Pulse(540, 1000, 0.9, int(20e6), 0, Rectangular(), 2, PulseType.READOUT) + p6 = Pulse(640, 1000, 0.9, int(20e6), 0, Rectangular(), 1, PulseType.READOUT) + + ps = PulseSequence(p1, p2, p3, p4, p5, p6) + bounds = Bounds.update(ps) + + assert bounds.waveforms == 40 + assert bounds.readout == 3 + assert bounds.instructions == 6 + + +def test_bounds_add(): + bounds1 = Bounds(2, 1, 3) + bounds2 = Bounds(1, 2, 1) + + bounds_sum = bounds1 + bounds2 + + assert bounds_sum.waveforms == 3 + assert bounds_sum.readout == 3 + assert bounds_sum.instructions == 4 + + +def test_bounds_comparison(): + bounds1 = Bounds(2, 1, 3) + bounds2 = Bounds(1, 2, 1) + + assert bounds1 > bounds2 + assert not bounds2 < bounds1 + + +@pytest.mark.parametrize( + "bounds", + [ + Bounds(150, int(10e6), int(10e6)), + Bounds(int(10e6), 10, int(10e6)), + Bounds(int(10e6), int(10e6), 20), + ], +) +def test_batch(bounds): + p1 = Pulse(400, 40, 0.9, int(100e6), 0, Drag(5, 1), 3, PulseType.DRIVE) + p2 = Pulse(500, 40, 0.9, int(100e6), 0, Drag(5, 1), 2, PulseType.DRIVE) + p3 = Pulse(600, 40, 0.9, int(100e6), 0, Drag(5, 1), 1, PulseType.DRIVE) + + p4 = Pulse(440, 1000, 0.9, int(20e6), 0, Rectangular(), 3, PulseType.READOUT) + p5 = Pulse(540, 1000, 0.9, int(20e6), 0, Rectangular(), 2, PulseType.READOUT) + p6 = Pulse(640, 1000, 0.9, int(20e6), 0, Rectangular(), 1, PulseType.READOUT) + + ps = PulseSequence(p1, p2, p3, p4, p5, p6) + + sequences = 10 * [ps] + + batches = list(batch(sequences, bounds)) + assert len(batches) == 4 From 5fa3da4c8ae0b697f8012fd13bd145e96fd12348 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 28 Feb 2024 12:10:44 +0400 Subject: [PATCH 18/28] Update batch test assertion --- tests/test_unrolling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unrolling.py b/tests/test_unrolling.py index b13d3a71da..5551aacad5 100644 --- a/tests/test_unrolling.py +++ b/tests/test_unrolling.py @@ -64,4 +64,4 @@ def test_batch(bounds): sequences = 10 * [ps] batches = list(batch(sequences, bounds)) - assert len(batches) == 4 + assert len(batches) > 1 From 78b5103af3eb20fd9d7f29485176d55a19857837 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 28 Feb 2024 12:38:58 +0400 Subject: [PATCH 19/28] Update bounds assertions in test_unrolling.py --- tests/test_unrolling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_unrolling.py b/tests/test_unrolling.py index 5551aacad5..95ca775050 100644 --- a/tests/test_unrolling.py +++ b/tests/test_unrolling.py @@ -18,9 +18,9 @@ def test_bounds_update(): ps = PulseSequence(p1, p2, p3, p4, p5, p6) bounds = Bounds.update(ps) - assert bounds.waveforms == 40 + assert bounds.waveforms >= 40 assert bounds.readout == 3 - assert bounds.instructions == 6 + assert bounds.instructions > 1 def test_bounds_add(): From 9458088bff7d633ba6d2c68fbb3d5549d1815944 Mon Sep 17 00:00:00 2001 From: Hayk Sargsyan <52532457+hay-k@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:04:28 +0400 Subject: [PATCH 20/28] fix infinite recursion when number of bins is larger than max --- src/qibolab/instruments/qblox/controller.py | 50 ++++++++++++++++----- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index 22626773ab..5b386b2b59 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -1,4 +1,5 @@ import signal +from dataclasses import replace import numpy as np from qblox_instruments.qcodes_drivers.cluster import Cluster as QbloxCluster @@ -12,6 +13,7 @@ from qibolab.instruments.qblox.sequencer import SAMPLING_RATE from qibolab.instruments.unrolling import batch_max_sequences from qibolab.pulses import PulseSequence, PulseType +from qibolab.result import IntegratedResults, SampleResults from qibolab.sweeper import Parameter, Sweeper, SweeperType MAX_BATCH_SIZE = 30 @@ -479,12 +481,7 @@ def _sweep_recursion( result = self._execute_pulse_sequence( qubits, sequence, options, sweepers ) - for pulse in sequence.ro_pulses: - if results[pulse.id]: - results[pulse.id] += result[pulse.serial] - else: - results[pulse.id] = result[pulse.serial] - results[pulse.qubit] = results[pulse.id] + self._add_to_results(sequence, results, result) else: sweepers_repetitions = 1 for sweeper in sweepers: @@ -493,19 +490,22 @@ def _sweep_recursion( # split nshots max_rt_nshots = (SEQUENCER_MEMORY) // sweepers_repetitions num_full_sft_iterations = nshots // max_rt_nshots - num_bins = max_rt_nshots * sweepers_repetitions + result_chunks = [] for sft_iteration in range(num_full_sft_iterations + 1): _nshots = min( max_rt_nshots, nshots - sft_iteration * max_rt_nshots ) - self._sweep_recursion( + + res = self._execute_pulse_sequence( qubits, sequence, - options, - *sweepers, - results=results, + replace(options, nshots=_nshots), + sweepers, ) + result_chunks.append(res) + result = self._combine_result_chunks(result_chunks) + self._add_to_results(sequence, results, result) else: for _ in range(nshots): num_bins = 1 @@ -538,3 +538,31 @@ def _sweep_recursion( *((split_sweeper,) + sweepers[1:]), results=results, ) + + @staticmethod + def _combine_result_chunks(result_chunks): + res = result_chunks[0] + some_result = next(iter(res.values())) + if isinstance(some_result, IntegratedResults): + attribute = "voltage" + elif isinstance(some_result, SampleResults): + attribute = "samples" + else: + raise ValueError(f"Unknown acquired result type {type(some_result)}") + for chunk in result_chunks[1:]: + for key, value in chunk.items(): + appended = np.append( + getattr(res[key], attribute), getattr(value, attribute), axis=0 + ) + setattr(res[key], attribute, appended) + + return res + + @staticmethod + def _add_to_results(sequence, results, results_to_add): + for pulse in sequence.ro_pulses: + if results[pulse.id]: + results[pulse.id] += results_to_add[pulse.serial] + else: + results[pulse.id] = results_to_add[pulse.serial] + results[pulse.qubit] = results[pulse.id] From 95c3dbd1904754627d09f4c6782123c392945a87 Mon Sep 17 00:00:00 2001 From: Hayk Sargsyan <52532457+hay-k@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:12:16 +0400 Subject: [PATCH 21/28] Remove the handling of unrealistic sweep scenario --- src/qibolab/instruments/qblox/controller.py | 35 +++------------------ 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index 5b386b2b59..86342d150c 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -507,37 +507,10 @@ def _sweep_recursion( result = self._combine_result_chunks(result_chunks) self._add_to_results(sequence, results, result) else: - for _ in range(nshots): - num_bins = 1 - for sweeper in sweepers[1:]: - num_bins *= len(sweeper.values) - sweeper = sweepers[0] - max_rt_iterations = (SEQUENCER_MEMORY) // num_bins - num_full_sft_iterations = ( - len(sweeper.values) // max_rt_iterations - ) - num_bins = nshots * max_rt_iterations - for sft_iteration in range(num_full_sft_iterations + 1): - _from = sft_iteration * max_rt_iterations - _to = min( - (sft_iteration + 1) * max_rt_iterations, - len(sweeper.values), - ) - _values = sweeper.values[_from:_to] - split_sweeper = Sweeper( - parameter=sweeper.parameter, - values=_values, - pulses=sweeper.pulses, - qubits=sweeper.qubits, - ) - - self._sweep_recursion( - qubits, - sequence, - options, - *((split_sweeper,) + sweepers[1:]), - results=results, - ) + raise ValueError( + f"Requested sweep has {sweepers_repetitions} total number of sweep points. " + f"Maximum supported is {SEQUENCER_MEMORY}" + ) @staticmethod def _combine_result_chunks(result_chunks): From aa987b112ca781e9088bd7d4ea8a9acf58014d10 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 1 Mar 2024 16:28:11 +0100 Subject: [PATCH 22/28] build: Missing PyVISA dependency to LO extra --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 695a8b625e..4bd89a34b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,7 +83,7 @@ qblox = ["qblox-instruments", "qcodes", "qcodes_contrib_drivers", "pyvisa-py"] qm = ["qm-qua", "qualang-tools"] zh = ["laboneq"] rfsoc = ["qibosoq"] -los = ["qcodes", "qcodes_contrib_drivers"] +los = ["qcodes", "qcodes_contrib_drivers", "pyvisa-py"] [tool.poe.tasks] From a2a7f6351d984611385cc4d9857d93de2eec659b Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 12 Mar 2024 16:38:12 +0100 Subject: [PATCH 23/28] chore: Poetry lock --- poetry.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index deee004ff5..c9bd71920f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5733,7 +5733,7 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.link testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [extras] -los = ["qcodes", "qcodes_contrib_drivers"] +los = ["pyvisa-py", "qcodes", "qcodes_contrib_drivers"] qblox = ["pyvisa-py", "qblox-instruments", "qcodes", "qcodes_contrib_drivers"] qm = ["qm-qua", "qualang-tools"] rfsoc = ["qibosoq"] @@ -5742,4 +5742,4 @@ zh = ["laboneq"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "f734a6030191d61773c570137889916f6ebc7782487d37d1d7ee74e18f8b5777" +content-hash = "51df6e543cb9770924dd733f400730d0deb8ba3a8386eb4031f302a1797a3d97" From 47d1832966a21649c984c32fe5088ba144dad296 Mon Sep 17 00:00:00 2001 From: Andrea Date: Tue, 12 Mar 2024 12:29:06 -0400 Subject: [PATCH 24/28] buid: remove bound to numpy 1.24 --- poetry.lock | 273 +++++++++++++++++++++++++------------------------ pyproject.toml | 2 +- 2 files changed, 141 insertions(+), 134 deletions(-) diff --git a/poetry.lock b/poetry.lock index c9bd71920f..1ce6dc41fa 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1244,13 +1244,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.28.1" +version = "2.28.2" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"}, - {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"}, + {file = "google-auth-2.28.2.tar.gz", hash = "sha256:80b8b4969aa9ed5938c7828308f20f035bc79f9d8fb8120bf9dc8db20b41ba30"}, + {file = "google_auth-2.28.2-py2.py3-none-any.whl", hash = "sha256:9fd67bbcd40f16d9d42f950228e9cf02a2ded4ae49198b27432d0cded5a74c38"}, ] [package.dependencies] @@ -1267,13 +1267,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "googleapis-common-protos" -version = "1.62.0" +version = "1.63.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.62.0.tar.gz", hash = "sha256:83f0ece9f94e5672cced82f592d2a5edf527a96ed1794f0bab36d5735c996277"}, - {file = "googleapis_common_protos-1.62.0-py2.py3-none-any.whl", hash = "sha256:4750113612205514f9f6aa4cb00d523a94f3e8c06c5ad2fee466387dc4875f07"}, + {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, + {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, ] [package.dependencies] @@ -1284,69 +1284,69 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "grpcio" -version = "1.62.0" +version = "1.62.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.7" files = [ - {file = "grpcio-1.62.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:136ffd79791b1eddda8d827b607a6285474ff8a1a5735c4947b58c481e5e4271"}, - {file = "grpcio-1.62.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:d6a56ba703be6b6267bf19423d888600c3f574ac7c2cc5e6220af90662a4d6b0"}, - {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:4cd356211579043fce9f52acc861e519316fff93980a212c8109cca8f47366b6"}, - {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e803e9b58d8f9b4ff0ea991611a8d51b31c68d2e24572cd1fe85e99e8cc1b4f8"}, - {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4c04fe33039b35b97c02d2901a164bbbb2f21fb9c4e2a45a959f0b044c3512c"}, - {file = "grpcio-1.62.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:95370c71b8c9062f9ea033a0867c4c73d6f0ff35113ebd2618171ec1f1e903e0"}, - {file = "grpcio-1.62.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c912688acc05e4ff012c8891803659d6a8a8b5106f0f66e0aed3fb7e77898fa6"}, - {file = "grpcio-1.62.0-cp310-cp310-win32.whl", hash = "sha256:821a44bd63d0f04e33cf4ddf33c14cae176346486b0df08b41a6132b976de5fc"}, - {file = "grpcio-1.62.0-cp310-cp310-win_amd64.whl", hash = "sha256:81531632f93fece32b2762247c4c169021177e58e725494f9a746ca62c83acaa"}, - {file = "grpcio-1.62.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3fa15850a6aba230eed06b236287c50d65a98f05054a0f01ccedf8e1cc89d57f"}, - {file = "grpcio-1.62.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:36df33080cd7897623feff57831eb83c98b84640b016ce443305977fac7566fb"}, - {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:7a195531828b46ea9c4623c47e1dc45650fc7206f8a71825898dd4c9004b0928"}, - {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab140a3542bbcea37162bdfc12ce0d47a3cda3f2d91b752a124cc9fe6776a9e2"}, - {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f9d6c3223914abb51ac564dc9c3782d23ca445d2864321b9059d62d47144021"}, - {file = "grpcio-1.62.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fbe0c20ce9a1cff75cfb828b21f08d0a1ca527b67f2443174af6626798a754a4"}, - {file = "grpcio-1.62.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38f69de9c28c1e7a8fd24e4af4264726637b72f27c2099eaea6e513e7142b47e"}, - {file = "grpcio-1.62.0-cp311-cp311-win32.whl", hash = "sha256:ce1aafdf8d3f58cb67664f42a617af0e34555fe955450d42c19e4a6ad41c84bd"}, - {file = "grpcio-1.62.0-cp311-cp311-win_amd64.whl", hash = "sha256:eef1d16ac26c5325e7d39f5452ea98d6988c700c427c52cbc7ce3201e6d93334"}, - {file = "grpcio-1.62.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8aab8f90b2a41208c0a071ec39a6e5dbba16fd827455aaa070fec241624ccef8"}, - {file = "grpcio-1.62.0-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:62aa1659d8b6aad7329ede5d5b077e3d71bf488d85795db517118c390358d5f6"}, - {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:0d7ae7fc7dbbf2d78d6323641ded767d9ec6d121aaf931ec4a5c50797b886532"}, - {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f359d635ee9428f0294bea062bb60c478a8ddc44b0b6f8e1f42997e5dc12e2ee"}, - {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d48e5b1f8f4204889f1acf30bb57c30378e17c8d20df5acbe8029e985f735c"}, - {file = "grpcio-1.62.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:662d3df5314ecde3184cf87ddd2c3a66095b3acbb2d57a8cada571747af03873"}, - {file = "grpcio-1.62.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92cdb616be44c8ac23a57cce0243af0137a10aa82234f23cd46e69e115071388"}, - {file = "grpcio-1.62.0-cp312-cp312-win32.whl", hash = "sha256:0b9179478b09ee22f4a36b40ca87ad43376acdccc816ce7c2193a9061bf35701"}, - {file = "grpcio-1.62.0-cp312-cp312-win_amd64.whl", hash = "sha256:614c3ed234208e76991992342bab725f379cc81c7dd5035ee1de2f7e3f7a9842"}, - {file = "grpcio-1.62.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:7e1f51e2a460b7394670fdb615e26d31d3260015154ea4f1501a45047abe06c9"}, - {file = "grpcio-1.62.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:bcff647e7fe25495e7719f779cc219bbb90b9e79fbd1ce5bda6aae2567f469f2"}, - {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:56ca7ba0b51ed0de1646f1735154143dcbdf9ec2dbe8cc6645def299bb527ca1"}, - {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e84bfb2a734e4a234b116be208d6f0214e68dcf7804306f97962f93c22a1839"}, - {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c1488b31a521fbba50ae86423f5306668d6f3a46d124f7819c603979fc538c4"}, - {file = "grpcio-1.62.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:98d8f4eb91f1ce0735bf0b67c3b2a4fea68b52b2fd13dc4318583181f9219b4b"}, - {file = "grpcio-1.62.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b3d3d755cfa331d6090e13aac276d4a3fb828bf935449dc16c3d554bf366136b"}, - {file = "grpcio-1.62.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a33f2bfd8a58a02aab93f94f6c61279be0f48f99fcca20ebaee67576cd57307b"}, - {file = "grpcio-1.62.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:5e709f7c8028ce0443bddc290fb9c967c1e0e9159ef7a030e8c21cac1feabd35"}, - {file = "grpcio-1.62.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:2f3d9a4d0abb57e5f49ed5039d3ed375826c2635751ab89dcc25932ff683bbb6"}, - {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:62ccb92f594d3d9fcd00064b149a0187c246b11e46ff1b7935191f169227f04c"}, - {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:921148f57c2e4b076af59a815467d399b7447f6e0ee10ef6d2601eb1e9c7f402"}, - {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f897b16190b46bc4d4aaf0a32a4b819d559a37a756d7c6b571e9562c360eed72"}, - {file = "grpcio-1.62.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1bc8449084fe395575ed24809752e1dc4592bb70900a03ca42bf236ed5bf008f"}, - {file = "grpcio-1.62.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81d444e5e182be4c7856cd33a610154fe9ea1726bd071d07e7ba13fafd202e38"}, - {file = "grpcio-1.62.0-cp38-cp38-win32.whl", hash = "sha256:88f41f33da3840b4a9bbec68079096d4caf629e2c6ed3a72112159d570d98ebe"}, - {file = "grpcio-1.62.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc2836cb829895ee190813446dce63df67e6ed7b9bf76060262c55fcd097d270"}, - {file = "grpcio-1.62.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:fcc98cff4084467839d0a20d16abc2a76005f3d1b38062464d088c07f500d170"}, - {file = "grpcio-1.62.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:0d3dee701e48ee76b7d6fbbba18ba8bc142e5b231ef7d3d97065204702224e0e"}, - {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:b7a6be562dd18e5d5bec146ae9537f20ae1253beb971c0164f1e8a2f5a27e829"}, - {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29cb592c4ce64a023712875368bcae13938c7f03e99f080407e20ffe0a9aa33b"}, - {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eda79574aec8ec4d00768dcb07daba60ed08ef32583b62b90bbf274b3c279f7"}, - {file = "grpcio-1.62.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7eea57444a354ee217fda23f4b479a4cdfea35fb918ca0d8a0e73c271e52c09c"}, - {file = "grpcio-1.62.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0e97f37a3b7c89f9125b92d22e9c8323f4e76e7993ba7049b9f4ccbe8bae958a"}, - {file = "grpcio-1.62.0-cp39-cp39-win32.whl", hash = "sha256:39cd45bd82a2e510e591ca2ddbe22352e8413378852ae814549c162cf3992a93"}, - {file = "grpcio-1.62.0-cp39-cp39-win_amd64.whl", hash = "sha256:b71c65427bf0ec6a8b48c68c17356cb9fbfc96b1130d20a07cb462f4e4dcdcd5"}, - {file = "grpcio-1.62.0.tar.gz", hash = "sha256:748496af9238ac78dcd98cce65421f1adce28c3979393e3609683fcd7f3880d7"}, + {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, + {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, + {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, + {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, + {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, + {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, + {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, + {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, + {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, + {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, + {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, + {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, + {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, + {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, + {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, + {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, + {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, + {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, + {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, + {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, + {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, + {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, + {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, + {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, + {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, + {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, + {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, + {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, + {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, + {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, + {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, + {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, + {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, + {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, + {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, + {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.62.0)"] +protobuf = ["grpcio-tools (>=1.62.1)"] [[package]] name = "grpclib" @@ -1575,32 +1575,32 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.0.1" +version = "7.0.2" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, + {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, + {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "importlib-resources" -version = "6.1.2" +version = "6.1.3" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"}, - {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"}, + {file = "importlib_resources-6.1.3-py3-none-any.whl", hash = "sha256:4c0269e3580fe2634d364b39b38b961540a7738c02cb984e98add8b4221d793d"}, + {file = "importlib_resources-6.1.3.tar.gz", hash = "sha256:56fb4525197b78544a3354ea27793952ab93f935bb4bf746b846bb1015020f2b"}, ] [package.dependencies] @@ -1608,7 +1608,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +testing = ["jaraco.collections", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -1812,13 +1812,13 @@ referencing = ">=0.31.0" [[package]] name = "jupyter-client" -version = "8.6.0" +version = "8.6.1" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_client-8.6.0-py3-none-any.whl", hash = "sha256:909c474dbe62582ae62b758bca86d6518c85234bdee2d908c778db6d72f39d99"}, - {file = "jupyter_client-8.6.0.tar.gz", hash = "sha256:0642244bb83b4764ae60d07e010e15f0e2d275ec4e918a8f7b80fbbef3ca60c7"}, + {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, + {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, ] [package.dependencies] @@ -1835,13 +1835,13 @@ test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pyt [[package]] name = "jupyter-core" -version = "5.7.1" +version = "5.7.2" description = "Jupyter core package. A base package on which Jupyter projects rely." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_core-5.7.1-py3-none-any.whl", hash = "sha256:c65c82126453a723a2804aa52409930434598fd9d35091d63dfb919d2b765bb7"}, - {file = "jupyter_core-5.7.1.tar.gz", hash = "sha256:de61a9d7fc71240f688b2fb5ab659fbb56979458dc66a71decd098e03c79e218"}, + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, ] [package.dependencies] @@ -1851,7 +1851,7 @@ traitlets = ">=5.3" [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] -test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] [[package]] name = "jupyterlab-pygments" @@ -2510,13 +2510,13 @@ files = [ [[package]] name = "nbclient" -version = "0.9.0" +version = "0.9.1" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.9.0-py3-none-any.whl", hash = "sha256:a3a1ddfb34d4a9d17fc744d655962714a866639acd30130e9be84191cd97cd15"}, - {file = "nbclient-0.9.0.tar.gz", hash = "sha256:4b28c207877cf33ef3a9838cdc7a54c5ceff981194a82eac59d558f05487295e"}, + {file = "nbclient-0.9.1-py3-none-any.whl", hash = "sha256:2c50a866e8dd6c5f655de47d2e252c82d2ebe978574e760ac229f5950593a434"}, + {file = "nbclient-0.9.1.tar.gz", hash = "sha256:4f7b78c6c2a380e228f8a3bb469b847cb24e5b8ad6fda410691b5621e05ce5a2"}, ] [package.dependencies] @@ -2528,7 +2528,7 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] -test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] name = "nbconvert" @@ -2570,13 +2570,13 @@ webpdf = ["playwright"] [[package]] name = "nbformat" -version = "5.9.2" +version = "5.10.2" description = "The Jupyter Notebook format" optional = false python-versions = ">=3.8" files = [ - {file = "nbformat-5.9.2-py3-none-any.whl", hash = "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9"}, - {file = "nbformat-5.9.2.tar.gz", hash = "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192"}, + {file = "nbformat-5.10.2-py3-none-any.whl", hash = "sha256:7381189a0d537586b3f18bae5dbad347d7dd0a7cf0276b09cdcd5c24d38edd99"}, + {file = "nbformat-5.10.2.tar.gz", hash = "sha256:c535b20a0d4310167bf4d12ad31eccfb0dc61e6392d6f8c570ab5b45a06a49a3"}, ] [package.dependencies] @@ -2639,39 +2639,47 @@ test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] [[package]] name = "numpy" -version = "1.24.4" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, - {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, - {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, - {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, - {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, - {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, - {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, - {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, - {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, - {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, - {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, - {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, - {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, - {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, - {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, - {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, - {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, - {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, - {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, - {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, - {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, - {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, - {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, - {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, - {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, - {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, - {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, - {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] @@ -2820,13 +2828,13 @@ files = [ [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] @@ -3521,13 +3529,13 @@ files = [ [[package]] name = "pydantic" -version = "2.6.3" +version = "2.6.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"}, - {file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] @@ -3804,13 +3812,13 @@ cp2110 = ["hidapi"] [[package]] name = "pytest" -version = "8.0.2" +version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, - {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, ] [package.dependencies] @@ -3818,11 +3826,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.3.0,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -4027,7 +4035,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -5265,18 +5272,18 @@ telegram = ["requests"] [[package]] name = "traitlets" -version = "5.14.1" +version = "5.14.2" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, - {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, + {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"}, + {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"] [[package]] name = "typing-extensions" @@ -5742,4 +5749,4 @@ zh = ["laboneq"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "51df6e543cb9770924dd733f400730d0deb8ba3a8386eb4031f302a1797a3d97" +content-hash = "a0bb21ef5247ebb10ab6b333b1f50696134d1fb9074900fb5ffca104d1ec4d8f" diff --git a/pyproject.toml b/pyproject.toml index 4bd89a34b1..3ae7804449 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ include = ["*.out", "*.yml"] python = ">=3.9,<3.12" qibo = ">=0.2.3" networkx = "^3.0" -numpy = "==1.24.4" +numpy = "^1.26.4" more-itertools = "^9.1.0" qblox-instruments = { version = "0.11.0", optional = true } qcodes = { version = "^0.37.0", optional = true } From 7b40d9df8f0a616dcd8be25173238b0b35998bf6 Mon Sep 17 00:00:00 2001 From: Hayk Sargsyan <52532457+hay-k@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:59:27 +0400 Subject: [PATCH 25/28] some simplification to combining of results --- src/qibolab/instruments/qblox/controller.py | 29 +++++++++------------ 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index 86342d150c..a526011b7d 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -13,7 +13,7 @@ from qibolab.instruments.qblox.sequencer import SAMPLING_RATE from qibolab.instruments.unrolling import batch_max_sequences from qibolab.pulses import PulseSequence, PulseType -from qibolab.result import IntegratedResults, SampleResults +from qibolab.result import SampleResults from qibolab.sweeper import Parameter, Sweeper, SweeperType MAX_BATCH_SIZE = 30 @@ -513,23 +513,18 @@ def _sweep_recursion( ) @staticmethod - def _combine_result_chunks(result_chunks): - res = result_chunks[0] - some_result = next(iter(res.values())) - if isinstance(some_result, IntegratedResults): - attribute = "voltage" - elif isinstance(some_result, SampleResults): - attribute = "samples" - else: - raise ValueError(f"Unknown acquired result type {type(some_result)}") - for chunk in result_chunks[1:]: - for key, value in chunk.items(): - appended = np.append( - getattr(res[key], attribute), getattr(value, attribute), axis=0 + def _combine_result_chunks(chunks): + some_chunk = next(iter(chunks)) + some_result = next(iter(some_chunk.values())) + attribute = "samples" if isinstance(some_result, SampleResults) else "voltage" + return { + key: some_result.__class__( + np.concatenate( + [getattr(chunk[key], attribute) for chunk in chunks], axis=0 ) - setattr(res[key], attribute, appended) - - return res + ) + for key in some_chunk.keys() + } @staticmethod def _add_to_results(sequence, results, results_to_add): From 58eaaeeb0d0a8c160e625210b35aa4c4581e4bb7 Mon Sep 17 00:00:00 2001 From: Hayk Sargsyan <52532457+hay-k@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:29:33 +0400 Subject: [PATCH 26/28] add some tests --- tests/test_instruments_qblox_controller.py | 51 +++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/tests/test_instruments_qblox_controller.py b/tests/test_instruments_qblox_controller.py index 7ef64ea68e..3b47daef1b 100644 --- a/tests/test_instruments_qblox_controller.py +++ b/tests/test_instruments_qblox_controller.py @@ -1,6 +1,13 @@ +from unittest.mock import Mock + +import numpy as np import pytest -from qibolab.instruments.qblox.controller import QbloxController +from qibolab import AveragingMode, ExecutionParameters +from qibolab.instruments.qblox.controller import SEQUENCER_MEMORY, QbloxController +from qibolab.pulses import Gaussian, Pulse, PulseSequence, ReadoutPulse, Rectangular +from qibolab.result import IntegratedResults +from qibolab.sweeper import Parameter, Sweeper from .qblox_fixtures import connected_controller, controller @@ -12,6 +19,48 @@ def test_init(controller: QbloxController): assert controller._reference_clock in ["internal", "external"] +def test_sweep_too_many_bins(platform, controller): + """Sweeps that require more bins than the hardware supports should be split + and executed.""" + qubit = platform.qubits[0] + pulse = Pulse(0, 40, 0.05, int(3e9), 0.0, Gaussian(5), qubit.drive.name, qubit=0) + ro_pulse = ReadoutPulse( + 0, 40, 0.05, int(3e9), 0.0, Rectangular(), qubit.readout.name, qubit=0 + ) + sequence = PulseSequence(pulse, ro_pulse) + + # These values shall result into execution in two rounds + shots = 128 + sweep_len = (SEQUENCER_MEMORY + 431) // shots + + mock_data = np.array([1, 2, 3, 4]) + sweep_ampl = Sweeper(Parameter.amplitude, np.random.rand(sweep_len), pulses=[pulse]) + params = ExecutionParameters( + nshots=shots, relaxation_time=10, averaging_mode=AveragingMode.SINGLESHOT + ) + controller._execute_pulse_sequence = Mock( + return_value={ro_pulse.serial: IntegratedResults(mock_data)} + ) + res = controller.sweep( + {0: platform.qubits[0]}, platform.couplers, sequence, params, sweep_ampl + ) + expected_data = np.append(mock_data, mock_data) # + assert np.array_equal(res[ro_pulse.serial].voltage, expected_data) + + +def test_sweep_too_many_sweep_points(platform, controller): + """Sweeps that require too many bins because simply the number of sweep + points is too large should be rejected.""" + qubit = platform.qubits[0] + pulse = Pulse(0, 40, 0.05, int(3e9), 0.0, Gaussian(5), qubit.drive.name, qubit=0) + sweep = Sweeper( + Parameter.amplitude, np.random.rand(SEQUENCER_MEMORY + 17), pulses=[pulse] + ) + params = ExecutionParameters(nshots=12, relaxation_time=10) + with pytest.raises(ValueError, match="total number of sweep points"): + controller.sweep({0: qubit}, {}, PulseSequence(pulse), params, sweep) + + @pytest.mark.qpu def connect(connected_controller: QbloxController): connected_controller.connect() From b758fd44d0fea627e2e4f5618ccf67a7bc298b63 Mon Sep 17 00:00:00 2001 From: Hayk Sargsyan <52532457+hay-k@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:31:29 +0400 Subject: [PATCH 27/28] invert the if statement --- src/qibolab/instruments/qblox/controller.py | 40 ++++++++++----------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/qibolab/instruments/qblox/controller.py b/src/qibolab/instruments/qblox/controller.py index a526011b7d..772e480024 100644 --- a/src/qibolab/instruments/qblox/controller.py +++ b/src/qibolab/instruments/qblox/controller.py @@ -486,32 +486,30 @@ def _sweep_recursion( sweepers_repetitions = 1 for sweeper in sweepers: sweepers_repetitions *= len(sweeper.values) - if sweepers_repetitions < SEQUENCER_MEMORY: - # split nshots - max_rt_nshots = (SEQUENCER_MEMORY) // sweepers_repetitions - num_full_sft_iterations = nshots // max_rt_nshots - - result_chunks = [] - for sft_iteration in range(num_full_sft_iterations + 1): - _nshots = min( - max_rt_nshots, nshots - sft_iteration * max_rt_nshots - ) - - res = self._execute_pulse_sequence( - qubits, - sequence, - replace(options, nshots=_nshots), - sweepers, - ) - result_chunks.append(res) - result = self._combine_result_chunks(result_chunks) - self._add_to_results(sequence, results, result) - else: + if sweepers_repetitions > SEQUENCER_MEMORY: raise ValueError( f"Requested sweep has {sweepers_repetitions} total number of sweep points. " f"Maximum supported is {SEQUENCER_MEMORY}" ) + max_rt_nshots = SEQUENCER_MEMORY // sweepers_repetitions + num_full_sft_iterations = nshots // max_rt_nshots + result_chunks = [] + for sft_iteration in range(num_full_sft_iterations + 1): + _nshots = min( + max_rt_nshots, nshots - sft_iteration * max_rt_nshots + ) + + res = self._execute_pulse_sequence( + qubits, + sequence, + replace(options, nshots=_nshots), + sweepers, + ) + result_chunks.append(res) + result = self._combine_result_chunks(result_chunks) + self._add_to_results(sequence, results, result) + @staticmethod def _combine_result_chunks(chunks): some_chunk = next(iter(chunks)) From c9b5948e02ee686c2e5ca701a14d4bd06736cc57 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:29:08 +0400 Subject: [PATCH 28/28] Update src/qibolab/instruments/qm/controller.py --- src/qibolab/instruments/qm/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibolab/instruments/qm/controller.py b/src/qibolab/instruments/qm/controller.py index 7bdc804546..88e312ce5c 100644 --- a/src/qibolab/instruments/qm/controller.py +++ b/src/qibolab/instruments/qm/controller.py @@ -129,7 +129,7 @@ class QMController(Controller): BOUNDS = Bounds( waveforms=MAX_DURATION, readout=MAX_READOUT, - instructions=MAX_READOUT, + instructions=MAX_INSTRUCTIONS, ) time_of_flight: int = 0