Skip to content

Commit

Permalink
Merge branch 'main' into qmfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
stavros11 committed Mar 18, 2024
2 parents 6496316 + 0bd68b0 commit fc98cf2
Show file tree
Hide file tree
Showing 13 changed files with 462 additions and 288 deletions.
275 changes: 141 additions & 134 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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]
Expand Down
23 changes: 6 additions & 17 deletions src/qibolab/instruments/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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.
Expand Down
13 changes: 7 additions & 6 deletions src/qibolab/instruments/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -74,6 +76,8 @@ class DummyInstrument(Controller):
instruments.
"""

BOUNDS = Bounds(1, 1, 1)

PortType = DummyPort

@property
Expand Down Expand Up @@ -127,9 +131,6 @@ def play(

return results

def split_batches(self, sequences):
return [sequences]

def sweep(
self,
qubits: Dict[QubitId, Qubit],
Expand Down
123 changes: 62 additions & 61 deletions src/qibolab/instruments/qblox/controller.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import signal
from dataclasses import replace

import numpy as np
from qblox_instruments.qcodes_drivers.cluster import Cluster as QbloxCluster
Expand All @@ -10,13 +11,18 @@
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.result import SampleResults
from qibolab.sweeper import Parameter, Sweeper, SweeperType
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]."""
MAX_READOUT = int(1e6)
"""Maximum number of readout pulses [Not estimated]."""
MAX_INSTRUCTIONS = int(1e6)
"""Maximum instructions size [Not estimated]."""

MAX_BATCH_SIZE = 30
"""Maximum number of sequences that can be unrolled in a single one
(independent of measurements)."""
SEQUENCER_MEMORY = 2**17


Expand All @@ -28,6 +34,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
):
Expand Down Expand Up @@ -218,9 +230,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):
return batch_max_sequences(sequences, MAX_BATCH_SIZE)

def sweep(
self,
qubits: dict,
Expand Down Expand Up @@ -479,62 +488,54 @@ 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:
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
num_bins = max_rt_nshots * sweepers_repetitions

for sft_iteration in range(num_full_sft_iterations + 1):
_nshots = min(
max_rt_nshots, nshots - sft_iteration * max_rt_nshots
)
self._sweep_recursion(
qubits,
sequence,
options,
*sweepers,
results=results,
)
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,
)
if sweepers_repetitions > SEQUENCER_MEMORY:
raise ValueError(
f"Requested sweep has {sweepers_repetitions} total number of sweep points. "
f"Maximum supported is {SEQUENCER_MEMORY}"
)

self._sweep_recursion(
qubits,
sequence,
options,
*((split_sweeper,) + sweepers[1:]),
results=results,
)
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))
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
)
)
for key in some_chunk.keys()
}

@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]
19 changes: 14 additions & 5 deletions src/qibolab/instruments/qm/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 Bounds

from .acquisition import declare_acquisitions, fetch_results
from .config import SAMPLING_RATE, QMConfig
Expand All @@ -23,7 +23,13 @@
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]."""


def declare_octaves(octaves, host, calibration_path=None):
Expand Down Expand Up @@ -120,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_INSTRUCTIONS,
)

time_of_flight: int = 0
"""Time of flight used for hardware signal integration."""
smearing: int = 0
Expand Down Expand Up @@ -364,6 +376,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):
return batch_max_sequences(sequences, MAX_BATCH_SIZE)
41 changes: 0 additions & 41 deletions src/qibolab/instruments/unrolling.py

This file was deleted.

Loading

0 comments on commit fc98cf2

Please sign in to comment.