Skip to content

Commit

Permalink
Merge pull request #1143 from qiboteam/qblox-fixes
Browse files Browse the repository at this point in the history
Qblox driver fixes for executing on iqm5q
  • Loading branch information
alecandido authored Feb 13, 2025
2 parents ec44271 + 8fc8b13 commit 2e1d034
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 25 deletions.
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ qibo = "^0.2.8"
numpy = "^1.26.4"
scipy = "^1.13.0"
pydantic = "^2.6.4"
qblox-instruments = { version = "^0.14.2", optional = true }
qblox-instruments = { version = "^0.12.0", optional = true }
lark = { version = "^1.1.9", optional = true }
qcodes = { version = "^0.37.0", optional = true }
qcodes_contrib_drivers = { version = "0.18.0", optional = true }
Expand Down Expand Up @@ -57,7 +57,7 @@ nbsphinx = "^0.9.1"
ipython = "^8.12.0"
sphinx-copybutton = "^0.5.1"
# extras
qblox-instruments = "^0.14.2"
qblox-instruments = "^0.12.0"
qcodes = "^0.37.0"
qcodes_contrib_drivers = "0.18.0"
qibosoq = { version = "^0.1.2", python = "<3.12" }
Expand Down
12 changes: 8 additions & 4 deletions src/qibolab/_core/instruments/qblox/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,20 @@ def _configure(
for idx, ((ch, address), sequencer) in enumerate(
zip(chs, module.sequencers)
):
sequencers[slot][ch] = idx
seq = sequences.get(ch, Q1Sequence.empty())
# configure all sequencers
config.sequencer(
sequencer,
address,
sequences.get(ch, Q1Sequence.empty()),
seq,
ch,
self.channels,
configs,
acquisition,
)
# only collect active sequencers
if not seq.is_empty:
sequencers[slot][ch] = idx

return sequencers

Expand All @@ -180,7 +184,7 @@ def _execute(
for slot, seqs in sequencers.items():
for ch, seq in seqs.items():
# wait all sequencers
status = self.cluster.get_sequencer_status(slot, seq, timeout=10)
status = self.cluster.get_sequencer_status(slot, seq, timeout=1)
if status.status is not qblox.SequencerStatuses.OKAY:
raise RuntimeError(status)
sequence = sequences.get(ch)
Expand All @@ -190,7 +194,7 @@ def _execute(
if len(seq_acqs) == 0:
# not an acquisition channel, or unused
continue
self.cluster.get_acquisition_status(slot, seq, timeout=10)
self.cluster.get_acquisition_status(slot, seq, timeout=1)
if acquisition is AcquisitionType.RAW:
for name in seq_acqs:
self.cluster.store_scope_acquisition(slot, seq, name)
Expand Down
52 changes: 37 additions & 15 deletions src/qibolab/_core/instruments/qblox/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from typing import Optional, cast

import numpy as np
from qblox_instruments.qcodes_drivers.module import Module
from qblox_instruments.qcodes_drivers.sequencer import Sequencer

Expand Down Expand Up @@ -58,13 +59,15 @@ def local_address(self):
Description adapted from
https://docs.qblox.com/en/main/api_reference/cluster.html#qblox_instruments.Cluster.connect_sequencer
"""
direction = "in" if self.input else "out"
for port in self.ports:
if port is not None:
assert port > 0
channels = (
str(self.ports[0])
str(self.ports[0] - 1)
if self.ports[1] is None
else f"{self.ports[0]}_{self.ports[1]}"
else f"{self.ports[0] - 1}_{self.ports[1] - 1}"
)
return f"{direction}{channels}"
return f"out{channels}"


def _iqout(id_: ChannelId, channel: Channel) -> Optional[ChannelId]:
Expand Down Expand Up @@ -122,8 +125,21 @@ def module(

# set lo frequencies
for iq, lo in _los(mod_channels, channels):
n = PortAddress.from_path(channels[iq].path).ports[0]
getattr(mod, f"out{n}_lo_freq")(cast(OscillatorConfig, configs[lo]).frequency)
n = PortAddress.from_path(channels[iq].path).ports[0] - 1
attr = f"out{n}_in{n}_lo_freq" if mod.is_qrm_type else f"out{n}_lo_freq"
getattr(mod, attr)(cast(OscillatorConfig, configs[lo]).frequency)


def _integration_length(sequence: Q1Sequence) -> Optional[int]:
"""Find integration length based on sequence waveform lengths."""
lengths = {len(waveform.data) for waveform in sequence.waveforms.values()}
if len(lengths) == 0:
return None
if len(lengths) == 1:
return lengths.pop()
raise NotImplementedError(
"Cannot acquire different lengths using the same sequencer."
)


def sequencer(
Expand All @@ -136,13 +152,6 @@ def sequencer(
acquisition: AcquisitionType,
):
"""Configure sequencer-wide settings."""
# upload sequence
# - ensure JSON compatibility of the sent dictionary
seq.sequence(json.loads(sequence.model_dump_json()))

# configure the sequencers to synchronize
seq.sync_en(True)

config = configs[channel_id]

# set parameters
Expand All @@ -155,9 +164,11 @@ def sequencer(
# acquisition
if address.input:
assert isinstance(config, AcquisitionConfig)
seq.integration_length_acq(1000)
length = _integration_length(sequence)
if length is not None:
seq.integration_length_acq(length)
# discrimination
seq.thresholded_acq_rotation(config.iq_angle)
seq.thresholded_acq_rotation(np.degrees(config.iq_angle % (2 * np.pi)))
seq.thresholded_acq_threshold(config.threshold)
# demodulation
seq.demod_en_acq(acquisition is not AcquisitionType.RAW)
Expand All @@ -172,3 +183,14 @@ def sequencer(

# connect to physical address
seq.connect_sequencer(address.local_address)

# avoid sequence operations for inactive sequencers, including synchronization
if sequence.is_empty:
return

# upload sequence
# - ensure JSON compatibility of the sent dictionary
seq.sequence(json.loads(sequence.model_dump_json()))

# configure the sequencers to synchronize
seq.sync_en(True)
1 change: 1 addition & 0 deletions src/qibolab/_core/instruments/qblox/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def integration_lenghts(
(mod_id, i): seq.integration_length_acq()
for mod_id, mod in modules.items()
for i, seq in enumerate(mod.sequencers)
if hasattr(seq, "integration_length_acq")
},
{
acq: channels_to_sequencer[ch]
Expand Down
4 changes: 4 additions & 0 deletions src/qibolab/_core/instruments/qblox/sequence/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ def empty(cls):
waveforms={}, weights={}, acquisitions={}, program=Program(elements=[])
)

@property
def is_empty(self) -> bool:
return len(self.program.elements) == 0

@property
def integration_lengths(self) -> dict[MeasureId, Optional[int]]:
"""Determine the integration lengths fixed by weights.
Expand Down

0 comments on commit 2e1d034

Please sign in to comment.