Skip to content

Commit

Permalink
Merge branch 'main' into alvaro/latest
Browse files Browse the repository at this point in the history
  • Loading branch information
aorgazf committed Dec 15, 2023
2 parents 17646b0 + 05c64b7 commit 9e97a44
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 161 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repos:
args:
- --line-length=120
- repo: https://github.com/pycqa/isort
rev: 5.12.0
rev: 5.13.1
hooks:
- id: isort
args: ["--profile", "black"]
Expand Down
13 changes: 8 additions & 5 deletions src/qibolab/instruments/qblox/cluster_qcm_bb.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,13 @@ def _get_next_sequencer(self, port, frequency, qubits: dict):
Exception = If attempting to set a parameter without a connection to the instrument.
"""
# select the qubit relative to specific port
qubit = None
for _qubit in qubits.values():
if _qubit.flux.port.name == port and _qubit.flux.port.module.name == self.name:
qubit = _qubit
if _qubit.flux.port is not None:
if _qubit.flux.port.name == port and _qubit.flux.port.module.name == self.name:
qubit = _qubit
else:
log.warning(f"Qubit {_qubit.name} has no flux line connected")
# select a new sequencer and configure it as required
next_sequencer_number = self._free_sequencers_numbers.pop(0)
if next_sequencer_number != self.DEFAULT_SEQUENCERS[port]:
Expand Down Expand Up @@ -328,11 +332,10 @@ def _get_next_sequencer(self, port, frequency, qubits: dict):
# value=qubits[qubit].sweetspot,
# )

self.ports[port].offset = qubit.sweetspot

self.ports[port].offset = qubit.sweetspot if qubit else 0
# create sequencer wrapper
sequencer = Sequencer(next_sequencer_number)
sequencer.qubit = qubit.name
sequencer.qubit = qubit.name if qubit else None
return sequencer

def get_if(self, pulse):
Expand Down
322 changes: 182 additions & 140 deletions src/qibolab/instruments/zhinst.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/qibolab/qubits.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass, field, fields
from pathlib import Path
from typing import List, Optional, Tuple, Union

from qibolab.channels import Channel
Expand Down Expand Up @@ -75,6 +76,7 @@ class Qubit:
# parameters for single shot classification
threshold: Optional[float] = None
iq_angle: float = 0.0
kernel_path: Optional[Path] = None
# required for mixers (not sure if it should be here)
mixer_drive_g: float = 0.0
mixer_drive_phi: float = 0.0
Expand Down
14 changes: 12 additions & 2 deletions src/qibolab/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,18 @@ def load_settings(runcard: dict) -> Settings:
return Settings(**runcard["settings"])


def load_qubits(runcard: dict) -> Tuple[QubitMap, CouplerMap, QubitPairMap]:
def load_qubits(runcard: dict, extras_folder: Path = None) -> Tuple[QubitMap, CouplerMap, QubitPairMap]:
"""Load qubits and pairs from the runcard.
Uses the native gate and characterization sections of the runcard
to parse the :class:`qibolab.qubits.Qubit` and :class:`qibolab.qubits.QubitPair`
objects.
"""
qubits = {q: Qubit(q, **char) for q, char in runcard["characterization"]["single_qubit"].items()}

if extras_folder is not None:
single_qubit = runcard["characterization"]["single_qubit"]
for qubit in qubits.values():
qubit.kernel_path = extras_folder / single_qubit[qubit.name]["kernel_path"]
couplers = {}
pairs = {}
if "coupler" in runcard["characterization"]:
Expand Down Expand Up @@ -108,6 +111,12 @@ def dump_qubits(qubits: QubitMap, pairs: QubitPairMap, couplers: CouplerMap = No
characterization = {
"single_qubit": {q: qubit.characterization for q, qubit in qubits.items()},
}
for q in qubits:
qubit = characterization["single_qubit"][q]
kernel_path = qubit["kernel_path"]
if kernel_path is not None:
qubit["kernel_path"] = kernel_path.name

if couplers:
characterization["coupler"] = {c.name: {"sweetspot": c.sweetspot} for c in couplers.values()}

Expand Down Expand Up @@ -144,6 +153,7 @@ def dump_runcard(platform: Platform, path: Path):
"topology": [list(pair) for pair in platform.pairs],
"instruments": dump_instruments(platform.instruments),
}

if platform.couplers:
settings["couplers"] = list(platform.couplers)
settings["topology"] = {coupler: list(pair) for pair, coupler in zip(platform.pairs, platform.couplers)}
Expand Down
4 changes: 2 additions & 2 deletions tests/dummy_qrc/zurich.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
)

RUNCARD = pathlib.Path(__file__).parent / "zurich.yml"

FOLDER = pathlib.Path(__file__).parent / "iqm5q/"
N_QUBITS = 5


Expand Down Expand Up @@ -137,7 +137,7 @@ def create(runcard_path=RUNCARD):

# create qubit objects
runcard = load_runcard(runcard_path)
qubits, couplers, pairs = load_qubits(runcard)
qubits, couplers, pairs = load_qubits(runcard, FOLDER)
settings = load_settings(runcard)

# assign channels to qubits and sweetspots(operating points)
Expand Down
12 changes: 5 additions & 7 deletions tests/dummy_qrc/zurich.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,7 @@ characterization:
# parameters for single shot classification
threshold: 0.8836
iq_angle: -1.551
# alpha: 217.492 MHz
# To save power values on the runcard
# ro_range_lp: -15
# ro_range_hp: -15
# qd_range: 0
# flux_range: -0
kernel_path: "kernel_q0.npz"

1:
readout_frequency: 4_931_000_000
Expand All @@ -260,6 +255,7 @@ characterization:
sweetspot: 0.0
mean_gnd_states: [0, 0]
mean_exc_states: [0, 0]
kernel_path: "kernel_q1.npz"
2:
readout_frequency: 6.109e+9 #6_112_000_000
drive_frequency: 4_300_587_281 # 4_401_600_000 #4_541_100_000
Expand All @@ -271,7 +267,7 @@ characterization:
# parameters for single shot classification
threshold: -0.0593
iq_angle: -0.667
# alpha: 208 MHz
kernel_path: "kernel_q2.npz"
3:
readout_frequency: 5_783_000_000
drive_frequency: 4_100_000_000
Expand All @@ -280,6 +276,7 @@ characterization:
sweetspot: 0.0
mean_gnd_states: [0, 0]
mean_exc_states: [0, 0]
kernel_path: "kernel_q3.npz"
4:
readout_frequency: 5_515_000_000
drive_frequency: 4_196_800_000
Expand All @@ -291,6 +288,7 @@ characterization:
# parameters for single shot classification
threshold: 0.233806 #0.370954 #0.350665
iq_angle: 0.481 # -91.712 #191.016
kernel_path: "kernel_q4.npz"
coupler:
0:
sweetspot: 0.0
Expand Down
81 changes: 77 additions & 4 deletions tests/test_instruments_zhinst.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import math
import re

import numpy as np
import pytest

Expand Down Expand Up @@ -172,7 +175,12 @@ def test_zhinst_register_readout_line(dummy_qrc):
platform = create_platform("zurich")
platform.setup()
IQM5q = platform.instruments["EL_ZURO"]
IQM5q.register_readout_line(platform.qubits[0], intermediate_frequency=int(1e6))

options = ExecutionParameters(
relaxation_time=300e-6, acquisition_type=AcquisitionType.INTEGRATION, averaging_mode=AveragingMode.CYCLIC
)

IQM5q.register_readout_line(platform.qubits[0], intermediate_frequency=int(1e6), options=options)

assert "measure0" in IQM5q.signal_map
assert "acquire0" in IQM5q.signal_map
Expand Down Expand Up @@ -678,10 +686,10 @@ 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(20 * [sequence]))
batches = list(instrument.split_batches(200 * [sequence]))
assert len(batches) == 2
assert len(batches[0]) == 16
assert len(batches[1]) == 4
assert len(batches[0]) == 150
assert len(batches[1]) == 50


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -785,3 +793,68 @@ def test_experiment_sweep_2d_specific(connected_platform, instrument):
)

assert len(results[ro_pulses[qubit].serial]) > 0


def get_previous_subsequence_finish(instrument, name):
"""
Look recursively for sub_section finish times.
"""
signal_name = re.sub("sequence_", "", name)
signal_name = re.sub(r"_\d+$", "", signal_name)
signal_name = re.sub(r"flux", "bias", signal_name)
finish = 0
for section in instrument.experiment.sections[0].children:
if section.uid == name:
for pulse in section.children:
if pulse.signal == signal_name:
try:
finish += pulse.time
except AttributeError:
# not a laboneq Delay class object, skipping
pass
try:
finish += pulse.pulse.length
except AttributeError:
# not a laboneq PlayPulse class object, skipping
pass
return finish


def test_experiment_measurement_sequence(dummy_qrc):
platform = create_platform("zurich")
platform.setup()
IQM5q = platform.instruments["EL_ZURO"]

sequence = PulseSequence()
qubits = {0: platform.qubits[0]}
platform.qubits = qubits
couplers = {}

readout_pulse_start = 40

for qubit in qubits:
qubit_drive_pulse_1 = platform.create_qubit_drive_pulse(qubit, start=0, duration=40)
ro_pulse = platform.create_qubit_readout_pulse(qubit, start=readout_pulse_start)
qubit_drive_pulse_2 = platform.create_qubit_drive_pulse(qubit, start=readout_pulse_start + 50, duration=40)
sequence.add(qubit_drive_pulse_1)
sequence.add(ro_pulse)
sequence.add(qubit_drive_pulse_2)

options = ExecutionParameters(
relaxation_time=4, acquisition_type=AcquisitionType.INTEGRATION, averaging_mode=AveragingMode.CYCLIC
)

IQM5q.experiment_flow(qubits, couplers, sequence, options)
measure_start = 0
for section in IQM5q.experiment.sections[0].children:
if section.uid == "sequence_measure_0":
measure_start += get_previous_subsequence_finish(IQM5q, section.play_after)
for pulse in section.children:
try:
if pulse.signal == "measure0":
measure_start += pulse.time
except AttributeError:
# not a laboneq delay class object, skipping
pass

assert math.isclose(measure_start * 1e9, readout_pulse_start, rel_tol=1e-4)

0 comments on commit 9e97a44

Please sign in to comment.