Skip to content

Commit

Permalink
Merge branch 'main' into zh_measurement_sequences fix kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacfomg committed Dec 12, 2023
2 parents 879827b + 7cbaaa6 commit a143ceb
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 58 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
86 changes: 41 additions & 45 deletions src/qibolab/instruments/zhinst.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
from collections import defaultdict
from dataclasses import dataclass, replace
from pathlib import Path
from typing import Dict, Tuple, Union

import laboneq._token
Expand All @@ -18,7 +19,7 @@

from qibolab import AcquisitionType, AveragingMode, ExecutionParameters
from qibolab.couplers import Coupler
from qibolab.instruments.abstract import INSTRUMENTS_DATA_FOLDER, Controller
from qibolab.instruments.abstract import Controller
from qibolab.instruments.port import Port
from qibolab.instruments.unrolling import batch_max_sequences
from qibolab.pulses import CouplerFluxPulse, FluxPulse, PulseSequence, PulseType
Expand Down Expand Up @@ -314,6 +315,7 @@ def __init__(self, name, device_setup, use_emulation=False, time_of_flight=0.0,
self.smearing = smearing
self.chip = "iqm5q"
"Parameters read from the runcard not part of ExecutionParameters"
self.kernels = defaultdict(Path)

self.exp = None
self.experiment = None
Expand Down Expand Up @@ -383,6 +385,7 @@ def calibration_step(self, qubits, couplers, options):
self.register_readout_line(
qubit=qubit,
intermediate_frequency=qubit.readout_frequency - qubit.readout.local_oscillator.frequency,
options=options,
)
if options.fast_reset is not False:
if len(self.sequence[f"drive{qubit.name}"]) == 0:
Expand All @@ -392,7 +395,7 @@ def calibration_step(self, qubits, couplers, options):
)
self.device_setup.set_calibration(self.calibration)

def register_readout_line(self, qubit, intermediate_frequency):
def register_readout_line(self, qubit, intermediate_frequency, options):
"""Registers qubit measure and acquire lines to calibration and signal map.
Note
Expand Down Expand Up @@ -433,18 +436,29 @@ def register_readout_line(self, qubit, intermediate_frequency):
self.signal_map[f"acquire{q}"] = self.device_setup.logical_signal_groups[f"q{q}"].logical_signals[
"acquire_line"
]

if qubit.kernel_path:
self.kernels[q] = qubit.kernel_path

oscillator = lo.Oscillator(
frequency=intermediate_frequency,
modulation_type=lo.ModulationType.SOFTWARE,
)
threshold = None

if options.acquisition_type == AcquisitionType.DISCRIMINATION:
if self.kernels[q].is_file():
# Kernels don't work with the software modulation on the acquire signal
oscillator = None
else:
# To keep compatibility with angle and threshold discrimination (Remove when possible)
threshold = qubit.threshold

self.calibration[f"/logical_signal_groups/q{q}/acquire_line"] = lo.SignalCalibration(
oscillator=lo.Oscillator(
frequency=intermediate_frequency,
modulation_type=lo.ModulationType.SOFTWARE,
),
local_oscillator=lo.Oscillator(
uid="lo_shfqa_a" + str(q),
frequency=int(qubit.readout.local_oscillator.frequency),
),
oscillator=oscillator,
range=qubit.feedback.power_range,
port_delay=self.time_of_flight * NANO_TO_SECONDS,
threshold=qubit.threshold,
threshold=threshold,
)

def register_drive_line(self, qubit, intermediate_frequency):
Expand Down Expand Up @@ -962,34 +976,20 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type
for pulse, q, iq_angle in zip(pulses, qubits, iq_angles):
pulse.zhpulse.uid += str(i)

if i == 0:
# Integration weights definition or load from the chip folder
weights_file = (
INSTRUMENTS_DATA_FOLDER
/ f"{self.chip}/weights/integration_weights_optimization_qubit_{q}.npy"
exp.delay(
signal=f"acquire{q}",
time=self.smearing * NANO_TO_SECONDS,
)

if self.kernels[q].is_file() and acquisition_type == lo.AcquisitionType.DISCRIMINATION:
kernels = np.load(self.kernels[q])
weight = lo.pulse_library.sampled_pulse_complex(
uid="weight" + str(q),
samples=kernels[str(q)] * np.exp(1j * iq_angle),
)
if weights_file.is_file():
samples = np.load(
weights_file,
allow_pickle=True,
)
if acquisition_type == lo.AcquisitionType.DISCRIMINATION:
weight = lo.pulse_library.sampled_pulse_complex(
uid="weight" + str(q),
# samples=samples[0] * np.exp(1j * qubit.iq_angle),
samples=samples[0] * np.exp(1j * iq_angle),
)
else:
weight = lo.pulse_library.sampled_pulse_complex(
uid="weight" + str(q),
samples=samples[0],
)
else:
# We adjust for smearing and remove smearing/2 at the end
exp.delay(
signal=f"acquire{q}",
time=self.smearing * NANO_TO_SECONDS,
)

else:
if i == 0:
if acquisition_type == lo.AcquisitionType.DISCRIMINATION:
weight = lo.pulse_library.sampled_pulse_complex(
samples=np.ones(
Expand All @@ -1007,13 +1007,10 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type
- 1.5 * self.smearing * NANO_TO_SECONDS,
amplitude=1,
)

weights[q] = weight
elif i != 0:
exp.delay(
signal=f"acquire{q}",
time=self.smearing * NANO_TO_SECONDS,
)
weight = weights[q]
elif i != 0:
weight = weights[q]

measure_pulse_parameters = {"phase": 0}

Expand All @@ -1035,7 +1032,6 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type
measure_pulse_parameters=measure_pulse_parameters,
measure_pulse_amplitude=None,
acquire_delay=self.time_of_flight * NANO_TO_SECONDS,
# reset_delay=relaxation_time * NANO_TO_SECONDS,
reset_delay=reset_delay,
)

Expand Down
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
7 changes: 6 additions & 1 deletion tests/test_instruments_zhinst.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,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

0 comments on commit a143ceb

Please sign in to comment.