From dabb5834a99569c6724e3a69a192ddde2d43dc4d Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 10:58:24 +0400 Subject: [PATCH 01/34] Add support for kernels in dummy platform --- src/qibolab/dummy.py | 15 ++++++++------- src/qibolab/dummy/kernels.npz | Bin 0 -> 1312 bytes src/qibolab/kernels.py | 21 +++++++++++++++++++++ src/qibolab/qubits.py | 5 +++-- src/qibolab/serialize.py | 14 ++++---------- 5 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 src/qibolab/dummy/kernels.npz create mode 100644 src/qibolab/kernels.py diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index fef0b6b10..f5c6c3708 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -3,6 +3,7 @@ from qibolab.channels import Channel, ChannelMap from qibolab.instruments.dummy import DummyInstrument, DummyLocalOscillator +from qibolab.kernels import Kernels from qibolab.platform import Platform from qibolab.serialize import load_qubits, load_runcard, load_settings @@ -21,12 +22,7 @@ def remove_couplers(runcard): return runcard -def load_dummy_runcard(): - """Loads the runcard YAML of the dummy platform.""" - return load_runcard(pathlib.Path(__file__).parent / "dummy.yml") - - -def create_dummy(with_couplers: bool = True): +def create_dummy(with_kernels: bool = True, with_couplers: bool = True): """Create a dummy platform using the dummy instrument. Args: @@ -40,7 +36,8 @@ def create_dummy(with_couplers: bool = True): twpa_pump.frequency = 1e9 twpa_pump.power = 10 - runcard = load_dummy_runcard() + runcard = load_runcard(pathlib.Path(__file__).parent / "dummy.yml") + extras_folder = pathlib.Path(__file__).parent / "dummy/" if not with_couplers: runcard = remove_couplers(runcard) @@ -65,6 +62,10 @@ def create_dummy(with_couplers: bool = True): channels["twpa"].local_oscillator = twpa_pump qubits, couplers, pairs = load_qubits(runcard) + if with_kernels: + kernels = Kernels.load(path=extras_folder / "kernels.npz") + for q, qubit in qubits.items(): + qubit.kernel = kernels[str(q)] settings = load_settings(runcard) # map channels to qubits diff --git a/src/qibolab/dummy/kernels.npz b/src/qibolab/dummy/kernels.npz new file mode 100644 index 0000000000000000000000000000000000000000..3cb33f1557f3acee8702a269c215d0fe7ef47a52 GIT binary patch literal 1312 zcmWIWW@Zs#fB;2?JG!1XW&k-L%*r6bV4#;*P|3(302Tl#0!e_tWWP|~fJjD$GKOmP zl+@znB6TYTb(=H`bsYuuwEUuyqQt!T{Gyapkhoi7PH`$wyf`DVAQi~hFxJu3QK(g* z0B}KE0V5ySLs%c|1MqssaCmvgXn1+YczAioWaxQ^kx7IZS789lYYYqxj373+XbA8o zR?B&y7I5|>Udue7J>Voxyp~)h1_p34B3_FPGXn!Sssg-O*+3p+1;YP8nwtg00{}ho B-2ngq literal 0 HcmV?d00001 diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py new file mode 100644 index 000000000..6f49b9815 --- /dev/null +++ b/src/qibolab/kernels.py @@ -0,0 +1,21 @@ +from dataclasses import dataclass +from pathlib import Path + +import numpy as np + +from qibolab.qubits import QubitId + + +@dataclass +class Kernels: + data: dict[str, np.ndarray] + + @classmethod + def load(cls, path: Path): + return cls(data=dict(np.load(path))) + + def dump(self, path: Path): + np.savez(path, **self.data) + + def __getitem__(self, qubit: QubitId): + return self.data[qubit] diff --git a/src/qibolab/qubits.py b/src/qibolab/qubits.py index b701ee07a..90804f72e 100644 --- a/src/qibolab/qubits.py +++ b/src/qibolab/qubits.py @@ -1,7 +1,8 @@ from dataclasses import dataclass, field, fields -from pathlib import Path from typing import List, Optional, Tuple, Union +import numpy as np + from qibolab.channels import Channel from qibolab.couplers import Coupler from qibolab.native import SingleQubitNatives, TwoQubitNatives @@ -75,7 +76,7 @@ class Qubit: # parameters for single shot classification threshold: Optional[float] = None iq_angle: float = 0.0 - kernel_path: Optional[Path] = None + kernel: Optional[np.ndarray] = field(default=None, repr=False) # required for mixers (not sure if it should be here) mixer_drive_g: float = 0.0 mixer_drive_phi: float = 0.0 diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index 9d99054c0..275c85162 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -33,9 +33,7 @@ def load_settings(runcard: dict) -> Settings: return Settings(**runcard["settings"]) -def load_qubits( - runcard: dict, extras_folder: Path = None -) -> Tuple[QubitMap, CouplerMap, QubitPairMap]: +def load_qubits(runcard: dict) -> Tuple[QubitMap, CouplerMap, QubitPairMap]: """Load qubits and pairs from the runcard. Uses the native gate and characterization sections of the runcard to @@ -48,10 +46,7 @@ def load_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"]: @@ -147,9 +142,8 @@ def dump_characterization(qubits: QubitMap, couplers: CouplerMap = None) -> dict } 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 qubit["kernel"] is not None: + del qubit["kernel"] if couplers: characterization["coupler"] = { From c54053548587d41f768bee6b61c9ad6cf5adc490 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 11:24:36 +0400 Subject: [PATCH 02/34] fix: Update Zurich instrument and qubit objects --- src/qibolab/instruments/zhinst.py | 21 ++++++++------------- tests/dummy_qrc/zurich.py | 11 ++++++++--- tests/dummy_qrc/zurich.yml | 5 ----- tests/dummy_qrc/zurich/kernels.npz | Bin 0 -> 1312 bytes 4 files changed, 16 insertions(+), 21 deletions(-) create mode 100644 tests/dummy_qrc/zurich/kernels.npz diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index ba6d68ef7..4a43cd506 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -4,7 +4,6 @@ import os from collections import defaultdict from dataclasses import dataclass, replace -from pathlib import Path from typing import Dict, List, Tuple, Union import laboneq._token @@ -318,7 +317,6 @@ def __init__( 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 @@ -442,9 +440,6 @@ def register_readout_line(self, qubit, intermediate_frequency, options): 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, @@ -452,7 +447,7 @@ def register_readout_line(self, qubit, intermediate_frequency, options): threshold = None if options.acquisition_type == AcquisitionType.DISCRIMINATION: - if self.kernels[q].is_file(): + if qubit.kernel is not None: # Kernels don't work with the software modulation on the acquire signal oscillator = None else: @@ -1039,15 +1034,15 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type iq_angle_readout_schedule[i].append(iq_angle) weights = {} - for i, (pulses, qubits, iq_angles) in enumerate( + for i, (pulses, qubits_r, iq_angles) in enumerate( zip( readout_schedule.values(), qubit_readout_schedule.values(), iq_angle_readout_schedule.values(), ) ): - qd_finish = self.find_subsequence_finish(i, "drive", qubits) - qf_finish = self.find_subsequence_finish(i, "flux", qubits) + qd_finish = self.find_subsequence_finish(i, "drive", qubits_r) + qf_finish = self.find_subsequence_finish(i, "flux", qubits_r) cf_finish = self.find_subsequence_finish(i, "couplerflux", couplers) finish_times = np.array( [ @@ -1064,7 +1059,7 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type play_after = f"sequence_{latest_sequence['line']}_{i}" # Section on the outside loop allows for multiplex with exp.section(uid=f"sequence_measure_{i}", play_after=play_after): - for pulse, q, iq_angle in zip(pulses, qubits, iq_angles): + for pulse, q, iq_angle in zip(pulses, qubits_r, iq_angles): pulse.zhpulse.uid += str(i) exp.delay( @@ -1073,13 +1068,13 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type ) if ( - self.kernels[q].is_file() + qubits[q].kernel is not None and acquisition_type == lo.AcquisitionType.DISCRIMINATION ): - kernels = np.load(self.kernels[q]) + kernel = qubits[q].kernel weight = lo.pulse_library.sampled_pulse_complex( uid="weight" + str(q), - samples=kernels[str(q)] * np.exp(1j * iq_angle), + samples=kernel * np.exp(1j * iq_angle), ) else: diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 7a251739c..393ecfbba 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -9,6 +9,7 @@ from qibolab.channels import Channel, ChannelMap from qibolab.instruments.dummy import DummyLocalOscillator as LocalOscillator from qibolab.instruments.zhinst import Zurich +from qibolab.kernels import Kernels from qibolab.serialize import ( load_instrument_settings, load_qubits, @@ -17,11 +18,11 @@ ) RUNCARD = pathlib.Path(__file__).parent / "zurich.yml" -FOLDER = pathlib.Path(__file__).parent / "iqm5q/" +FOLDER = pathlib.Path(__file__).parent / "zurich/" N_QUBITS = 5 -def create(runcard_path=RUNCARD): +def create(runcard_path=RUNCARD, with_kernels: bool = True): """IQM 5q-chip controlled Zurich Instrumetns (Zh) SHFQC, HDAWGs and PQSC. Args: @@ -172,7 +173,11 @@ def create(runcard_path=RUNCARD): # create qubit objects runcard = load_runcard(runcard_path) - qubits, couplers, pairs = load_qubits(runcard, FOLDER) + qubits, couplers, pairs = load_qubits(runcard) + if with_kernels and (FOLDER / "kernels.npz").is_file(): + kernels = Kernels.load(path=FOLDER / "kernels.npz") + for q, qubit in qubits.items(): + qubit.kernel = kernels[str(q)] settings = load_settings(runcard) # assign channels to qubits and sweetspots(operating points) diff --git a/tests/dummy_qrc/zurich.yml b/tests/dummy_qrc/zurich.yml index b3a7ef795..91dba6003 100644 --- a/tests/dummy_qrc/zurich.yml +++ b/tests/dummy_qrc/zurich.yml @@ -240,7 +240,6 @@ characterization: # parameters for single shot classification threshold: 0.8836 iq_angle: -1.551 - kernel_path: "kernel_q0.npz" 1: readout_frequency: 4_931_000_000 @@ -250,7 +249,6 @@ 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 @@ -262,7 +260,6 @@ characterization: # parameters for single shot classification threshold: -0.0593 iq_angle: -0.667 - kernel_path: "kernel_q2.npz" 3: readout_frequency: 5_783_000_000 drive_frequency: 4_100_000_000 @@ -271,7 +268,6 @@ 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 @@ -283,7 +279,6 @@ 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 diff --git a/tests/dummy_qrc/zurich/kernels.npz b/tests/dummy_qrc/zurich/kernels.npz new file mode 100644 index 0000000000000000000000000000000000000000..3cb33f1557f3acee8702a269c215d0fe7ef47a52 GIT binary patch literal 1312 zcmWIWW@Zs#fB;2?JG!1XW&k-L%*r6bV4#;*P|3(302Tl#0!e_tWWP|~fJjD$GKOmP zl+@znB6TYTb(=H`bsYuuwEUuyqQt!T{Gyapkhoi7PH`$wyf`DVAQi~hFxJu3QK(g* z0B}KE0V5ySLs%c|1MqssaCmvgXn1+YczAioWaxQ^kx7IZS789lYYYqxj373+XbA8o zR?B&y7I5|>Udue7J>Voxyp~)h1_p34B3_FPGXn!Sssg-O*+3p+1;YP8nwtg00{}ho B-2ngq literal 0 HcmV?d00001 From ab5b64cc5b8fe378cf0a831f98745b5e9bf71344 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 11:33:56 +0400 Subject: [PATCH 03/34] fix: key error if not all kernels --- src/qibolab/dummy.py | 4 ++-- tests/dummy_qrc/zurich.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index f5c6c3708..8b94b9956 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -64,8 +64,8 @@ def create_dummy(with_kernels: bool = True, with_couplers: bool = True): qubits, couplers, pairs = load_qubits(runcard) if with_kernels: kernels = Kernels.load(path=extras_folder / "kernels.npz") - for q, qubit in qubits.items(): - qubit.kernel = kernels[str(q)] + for q in kernels.data.keys(): + qubits[q].kernel = kernels[str(q)] settings = load_settings(runcard) # map channels to qubits diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 393ecfbba..263c4a2af 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -176,8 +176,8 @@ def create(runcard_path=RUNCARD, with_kernels: bool = True): qubits, couplers, pairs = load_qubits(runcard) if with_kernels and (FOLDER / "kernels.npz").is_file(): kernels = Kernels.load(path=FOLDER / "kernels.npz") - for q, qubit in qubits.items(): - qubit.kernel = kernels[str(q)] + for q in kernels.data.keys(): + qubits[q].kernel = kernels[str(q)] settings = load_settings(runcard) # assign channels to qubits and sweetspots(operating points) From 92e20eb65f9dad24ce872131348d0111001c2601 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 12:21:20 +0400 Subject: [PATCH 04/34] fix: qubit kernel assignment and serialization --- src/qibolab/dummy.py | 6 +++++- src/qibolab/kernels.py | 1 + src/qibolab/serialize.py | 14 +++++++++++++- tests/dummy_qrc/zurich.py | 6 +++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index 8b94b9956..4e12d09b2 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -65,7 +65,11 @@ def create_dummy(with_kernels: bool = True, with_couplers: bool = True): if with_kernels: kernels = Kernels.load(path=extras_folder / "kernels.npz") for q in kernels.data.keys(): - qubits[q].kernel = kernels[str(q)] + # To handle Kernel.save() using strings + if q in qubits.keys(): + qubits[q].kernel = kernels[q] + else: + qubits[int(q)].kernel = kernels[q] settings = load_settings(runcard) # map channels to qubits diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index 6f49b9815..3c3a9bd62 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -15,6 +15,7 @@ def load(cls, path: Path): return cls(data=dict(np.load(path))) def dump(self, path: Path): + # This saves the QubitID as a str np.savez(path, **self.data) def __getitem__(self, qubit: QubitId): diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index 275c85162..d4d568807 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -11,6 +11,7 @@ import yaml from qibolab.couplers import Coupler +from qibolab.kernels import Kernels from qibolab.native import CouplerNatives, SingleQubitNatives, TwoQubitNatives from qibolab.platform import ( CouplerMap, @@ -140,10 +141,14 @@ def dump_characterization(qubits: QubitMap, couplers: CouplerMap = None) -> dict characterization = { "single_qubit": {q: qubit.characterization for q, qubit in qubits.items()}, } + + kernels = {} for q in qubits: qubit = characterization["single_qubit"][q] if qubit["kernel"] is not None: - del qubit["kernel"] + kernel = qubit.pop("kernel") + kernels[q] = kernel + kernels = Kernels(kernels) if couplers: characterization["coupler"] = { @@ -177,6 +182,13 @@ def dump_runcard(platform: Platform, path: Path): path (pathlib.Path): Path that the yaml file will be saved. """ + kernels = {} + for qubit in platform.qubits.values(): + if qubit.kernel is not None: + kernels[str(qubit.name)] = qubit.kernel + qubit.kernel = None + Kernels(kernels).dump(Path(__file__).parent / "dummy/kernels.npz") + settings = { "nqubits": platform.nqubits, "settings": asdict(platform.settings), diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 263c4a2af..615b966c3 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -177,7 +177,11 @@ def create(runcard_path=RUNCARD, with_kernels: bool = True): if with_kernels and (FOLDER / "kernels.npz").is_file(): kernels = Kernels.load(path=FOLDER / "kernels.npz") for q in kernels.data.keys(): - qubits[q].kernel = kernels[str(q)] + # To handle Kernel.save() using strings + if q in qubits.keys(): + qubits[q].kernel = kernels[q] + else: + qubits[int(q)].kernel = kernels[q] settings = load_settings(runcard) # assign channels to qubits and sweetspots(operating points) From 8f8f0f632a3a4de4b011dddc09881ca06d12fe8a Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 12:45:35 +0400 Subject: [PATCH 05/34] Update kernels.npz file --- src/qibolab/dummy/kernels.npz | Bin 1312 -> 22 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/qibolab/dummy/kernels.npz b/src/qibolab/dummy/kernels.npz index 3cb33f1557f3acee8702a269c215d0fe7ef47a52..15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7 100644 GIT binary patch literal 22 NcmWIWW@Tf*000g10H*)| literal 1312 zcmWIWW@Zs#fB;2?JG!1XW&k-L%*r6bV4#;*P|3(302Tl#0!e_tWWP|~fJjD$GKOmP zl+@znB6TYTb(=H`bsYuuwEUuyqQt!T{Gyapkhoi7PH`$wyf`DVAQi~hFxJu3QK(g* z0B}KE0V5ySLs%c|1MqssaCmvgXn1+YczAioWaxQ^kx7IZS789lYYYqxj373+XbA8o zR?B&y7I5|>Udue7J>Voxyp~)h1_p34B3_FPGXn!Sssg-O*+3p+1;YP8nwtg00{}ho B-2ngq From e3e0a8c2aeafef50776508f7bf195ed464d3fee6 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 16:35:58 +0400 Subject: [PATCH 06/34] fix: Refactor kernel loading and saving --- src/qibolab/dummy.py | 2 +- src/qibolab/kernels.py | 29 +++++++++++++++++------------ tests/dummy_qrc/zurich.py | 2 +- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index 4e12d09b2..ed85cda98 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -64,7 +64,7 @@ def create_dummy(with_kernels: bool = True, with_couplers: bool = True): qubits, couplers, pairs = load_qubits(runcard) if with_kernels: kernels = Kernels.load(path=extras_folder / "kernels.npz") - for q in kernels.data.keys(): + for q in kernels: # To handle Kernel.save() using strings if q in qubits.keys(): qubits[q].kernel = kernels[q] diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index 3c3a9bd62..c0169ef18 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -1,22 +1,27 @@ -from dataclasses import dataclass from pathlib import Path +import json import numpy as np - from qibolab.qubits import QubitId +class Kernels(dict[QubitId, np.ndarray]): -@dataclass -class Kernels: - data: dict[str, np.ndarray] + """ + A dictionary subclass for handling Qubit Kernels. + This class extends the built-in dict class and maps QubitId to numpy arrays. + It provides methods to load and dump the kernels from and to a file. + """ + @classmethod def load(cls, path: Path): - return cls(data=dict(np.load(path))) - + """Class method to load kernels from a file. The file should contain a serialized dictionary + where keys are serialized QubitId and values are numpy arrays.""" + raw_dict = dict(np.load(path)) + return cls({json.loads(key): value for key, value in raw_dict.items()}) + def dump(self, path: Path): - # This saves the QubitID as a str - np.savez(path, **self.data) - - def __getitem__(self, qubit: QubitId): - return self.data[qubit] + """Instance method to dump the kernels to a file. The keys (QubitId) are serialized to strings + and the values (numpy arrays) are kept as is.""" + serialzed_dict = {json.dumps(key): value for key, value in self.items()} + np.savez(path, **serialzed_dict) diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 615b966c3..3deb119cc 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -176,7 +176,7 @@ def create(runcard_path=RUNCARD, with_kernels: bool = True): qubits, couplers, pairs = load_qubits(runcard) if with_kernels and (FOLDER / "kernels.npz").is_file(): kernels = Kernels.load(path=FOLDER / "kernels.npz") - for q in kernels.data.keys(): + for q in kernels: # To handle Kernel.save() using strings if q in qubits.keys(): qubits[q].kernel = kernels[q] From dac24748ab2cfa30218189d15f698edb3dc29572 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 16:38:03 +0400 Subject: [PATCH 07/34] Refactor code to simplify handling of kernel assignment --- src/qibolab/dummy.py | 7 ++----- tests/dummy_qrc/zurich.py | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index ed85cda98..f1d495507 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -65,11 +65,8 @@ def create_dummy(with_kernels: bool = True, with_couplers: bool = True): if with_kernels: kernels = Kernels.load(path=extras_folder / "kernels.npz") for q in kernels: - # To handle Kernel.save() using strings - if q in qubits.keys(): - qubits[q].kernel = kernels[q] - else: - qubits[int(q)].kernel = kernels[q] + qubits[q].kernel = kernels[q] + settings = load_settings(runcard) # map channels to qubits diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 3deb119cc..02a29b3cc 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -177,11 +177,8 @@ def create(runcard_path=RUNCARD, with_kernels: bool = True): if with_kernels and (FOLDER / "kernels.npz").is_file(): kernels = Kernels.load(path=FOLDER / "kernels.npz") for q in kernels: - # To handle Kernel.save() using strings - if q in qubits.keys(): - qubits[q].kernel = kernels[q] - else: - qubits[int(q)].kernel = kernels[q] + qubits[q].kernel = kernels[q] + settings = load_settings(runcard) # assign channels to qubits and sweetspots(operating points) From cff89460e5197e8de9e3014ca79444d9b76086ff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:36:17 +0000 Subject: [PATCH 08/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibolab/kernels.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index c0169ef18..bdf23d837 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -1,27 +1,34 @@ -from pathlib import Path import json +from pathlib import Path import numpy as np + from qibolab.qubits import QubitId + class Kernels(dict[QubitId, np.ndarray]): + """A dictionary subclass for handling Qubit Kernels. + This class extends the built-in dict class and maps QubitId to numpy + arrays. It provides methods to load and dump the kernels from and to + a file. """ - A dictionary subclass for handling Qubit Kernels. - This class extends the built-in dict class and maps QubitId to numpy arrays. - It provides methods to load and dump the kernels from and to a file. - """ - @classmethod def load(cls, path: Path): - """Class method to load kernels from a file. The file should contain a serialized dictionary - where keys are serialized QubitId and values are numpy arrays.""" - raw_dict = dict(np.load(path)) + """Class method to load kernels from a file. + + The file should contain a serialized dictionary where keys are + serialized QubitId and values are numpy arrays. + """ + raw_dict = dict(np.load(path)) return cls({json.loads(key): value for key, value in raw_dict.items()}) - + def dump(self, path: Path): - """Instance method to dump the kernels to a file. The keys (QubitId) are serialized to strings - and the values (numpy arrays) are kept as is.""" + """Instance method to dump the kernels to a file. + + The keys (QubitId) are serialized to strings and the values + (numpy arrays) are kept as is. + """ serialzed_dict = {json.dumps(key): value for key, value in self.items()} np.savez(path, **serialzed_dict) From 5980fe7eae8b55de30f45fd8b1bf92acdcf7ca46 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Tue, 16 Jan 2024 17:53:30 +0400 Subject: [PATCH 09/34] Add extras folder to load_qubits --- src/qibolab/dummy.py | 10 ++-------- src/qibolab/kernels.py | 8 ++++---- src/qibolab/serialize.py | 9 ++++++++- tests/dummy_qrc/zurich.py | 10 ++-------- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index f1d495507..d58ab7a07 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -3,7 +3,6 @@ from qibolab.channels import Channel, ChannelMap from qibolab.instruments.dummy import DummyInstrument, DummyLocalOscillator -from qibolab.kernels import Kernels from qibolab.platform import Platform from qibolab.serialize import load_qubits, load_runcard, load_settings @@ -22,7 +21,7 @@ def remove_couplers(runcard): return runcard -def create_dummy(with_kernels: bool = True, with_couplers: bool = True): +def create_dummy(with_couplers: bool = True): """Create a dummy platform using the dummy instrument. Args: @@ -61,12 +60,7 @@ def create_dummy(with_kernels: bool = True, with_couplers: bool = True): channels["readout"].attenuation = 0 channels["twpa"].local_oscillator = twpa_pump - qubits, couplers, pairs = load_qubits(runcard) - if with_kernels: - kernels = Kernels.load(path=extras_folder / "kernels.npz") - for q in kernels: - qubits[q].kernel = kernels[q] - + qubits, couplers, pairs = load_qubits(runcard, extras_folder) settings = load_settings(runcard) # map channels to qubits diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index bdf23d837..eb5fa2b25 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -21,8 +21,9 @@ def load(cls, path: Path): The file should contain a serialized dictionary where keys are serialized QubitId and values are numpy arrays. """ - raw_dict = dict(np.load(path)) - return cls({json.loads(key): value for key, value in raw_dict.items()}) + return cls( + {json.loads(key): value for key, value in dict(np.load(path)).items()} + ) def dump(self, path: Path): """Instance method to dump the kernels to a file. @@ -30,5 +31,4 @@ def dump(self, path: Path): The keys (QubitId) are serialized to strings and the values (numpy arrays) are kept as is. """ - serialzed_dict = {json.dumps(key): value for key, value in self.items()} - np.savez(path, **serialzed_dict) + np.savez(path, **{json.dumps(key): value for key, value in self.items()}) diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index d4d568807..898334f02 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -34,7 +34,9 @@ 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 @@ -48,6 +50,11 @@ def load_qubits(runcard: dict) -> Tuple[QubitMap, CouplerMap, QubitPairMap]: for q, char in runcard["characterization"]["single_qubit"].items() } + if (extras_folder / "kernels.npz").is_file(): + kernels = Kernels.load(path=extras_folder / "kernels.npz") + for q in kernels: + qubits[q].kernel = kernels[q] + couplers = {} pairs = {} if "coupler" in runcard["characterization"]: diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 02a29b3cc..8f9fd326d 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -9,7 +9,6 @@ from qibolab.channels import Channel, ChannelMap from qibolab.instruments.dummy import DummyLocalOscillator as LocalOscillator from qibolab.instruments.zhinst import Zurich -from qibolab.kernels import Kernels from qibolab.serialize import ( load_instrument_settings, load_qubits, @@ -22,7 +21,7 @@ N_QUBITS = 5 -def create(runcard_path=RUNCARD, with_kernels: bool = True): +def create(runcard_path=RUNCARD): """IQM 5q-chip controlled Zurich Instrumetns (Zh) SHFQC, HDAWGs and PQSC. Args: @@ -172,13 +171,8 @@ def create(runcard_path=RUNCARD, with_kernels: bool = True): channels[ch].local_oscillator = local_oscillators[lo] # create qubit objects - runcard = load_runcard(runcard_path) + runcard = load_runcard(runcard_path, FOLDER) qubits, couplers, pairs = load_qubits(runcard) - if with_kernels and (FOLDER / "kernels.npz").is_file(): - kernels = Kernels.load(path=FOLDER / "kernels.npz") - for q in kernels: - qubits[q].kernel = kernels[q] - settings = load_settings(runcard) # assign channels to qubits and sweetspots(operating points) From 2cc10483f881034224490a576520a9f343296e2d Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Tue, 16 Jan 2024 18:04:35 +0400 Subject: [PATCH 10/34] Update src/qibolab/serialize.py Co-authored-by: Alessandro Candido --- src/qibolab/serialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index 898334f02..6984f9fba 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -50,7 +50,7 @@ def load_qubits( for q, char in runcard["characterization"]["single_qubit"].items() } - if (extras_folder / "kernels.npz").is_file(): + if extras_folder is not None: kernels = Kernels.load(path=extras_folder / "kernels.npz") for q in kernels: qubits[q].kernel = kernels[q] From 0c5a7abc51703ba4c708ec7df5f0d8dfa847e4c2 Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 11:52:14 +0400 Subject: [PATCH 11/34] Update src/qibolab/serialize.py Co-authored-by: Alessandro Candido --- src/qibolab/serialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index 6984f9fba..12f69ac1a 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -149,7 +149,7 @@ def dump_characterization(qubits: QubitMap, couplers: CouplerMap = None) -> dict "single_qubit": {q: qubit.characterization for q, qubit in qubits.items()}, } - kernels = {} + kernels = Kernels() for q in qubits: qubit = characterization["single_qubit"][q] if qubit["kernel"] is not None: From b383fa23e42da60c87414772721df21baa18b7c6 Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 11:52:22 +0400 Subject: [PATCH 12/34] Update src/qibolab/serialize.py Co-authored-by: Alessandro Candido --- src/qibolab/serialize.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index 12f69ac1a..b80711977 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -155,7 +155,6 @@ def dump_characterization(qubits: QubitMap, couplers: CouplerMap = None) -> dict if qubit["kernel"] is not None: kernel = qubit.pop("kernel") kernels[q] = kernel - kernels = Kernels(kernels) if couplers: characterization["coupler"] = { From f7e73e1786b5c7e519fa61b5d723a33f771a783c Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 11:53:35 +0400 Subject: [PATCH 13/34] Update tests/dummy_qrc/zurich.py Co-authored-by: Alessandro Candido --- tests/dummy_qrc/zurich.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index 8f9fd326d..e1a6d8d32 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -17,7 +17,7 @@ ) RUNCARD = pathlib.Path(__file__).parent / "zurich.yml" -FOLDER = pathlib.Path(__file__).parent / "zurich/" +FOLDER = pathlib.Path(__file__).parent / "zurich" N_QUBITS = 5 From 58999148b264da5fb1f7b42db7bbd4f7d0b044ad Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 11:53:49 +0400 Subject: [PATCH 14/34] Update src/qibolab/instruments/zhinst.py Co-authored-by: Andrea Pasquale --- 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 4a43cd506..b067bfcc5 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -1034,7 +1034,7 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type iq_angle_readout_schedule[i].append(iq_angle) weights = {} - for i, (pulses, qubits_r, iq_angles) in enumerate( + for i, (pulses, qubits_readout, iq_angles) in enumerate( zip( readout_schedule.values(), qubit_readout_schedule.values(), From f4ec7b4f2aceaac473ad67d2bbe829d4d6137084 Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 11:53:56 +0400 Subject: [PATCH 15/34] Update src/qibolab/instruments/zhinst.py Co-authored-by: Andrea Pasquale --- 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 b067bfcc5..c44c403db 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -1059,7 +1059,7 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type play_after = f"sequence_{latest_sequence['line']}_{i}" # Section on the outside loop allows for multiplex with exp.section(uid=f"sequence_measure_{i}", play_after=play_after): - for pulse, q, iq_angle in zip(pulses, qubits_r, iq_angles): + for pulse, q, iq_angle in zip(pulses, qubits_readout, iq_angles): pulse.zhpulse.uid += str(i) exp.delay( From a32876c377bacbf8999cf71a00d4862b5001d1a7 Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 11:54:05 +0400 Subject: [PATCH 16/34] Update src/qibolab/instruments/zhinst.py Co-authored-by: Andrea Pasquale --- src/qibolab/instruments/zhinst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibolab/instruments/zhinst.py b/src/qibolab/instruments/zhinst.py index c44c403db..9049ce883 100644 --- a/src/qibolab/instruments/zhinst.py +++ b/src/qibolab/instruments/zhinst.py @@ -1041,8 +1041,8 @@ def measure_relax(self, exp, qubits, couplers, relaxation_time, acquisition_type iq_angle_readout_schedule.values(), ) ): - qd_finish = self.find_subsequence_finish(i, "drive", qubits_r) - qf_finish = self.find_subsequence_finish(i, "flux", qubits_r) + qd_finish = self.find_subsequence_finish(i, "drive", qubits_readout) + qf_finish = self.find_subsequence_finish(i, "flux", qubits_readout) cf_finish = self.find_subsequence_finish(i, "couplerflux", couplers) finish_times = np.array( [ From e216176a5ecdf46a9c9328cabeee278f2a7034ca Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 11:55:27 +0400 Subject: [PATCH 17/34] Update src/qibolab/dummy.py Co-authored-by: Andrea Pasquale --- src/qibolab/dummy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index d58ab7a07..18114f710 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -36,7 +36,7 @@ def create_dummy(with_couplers: bool = True): twpa_pump.power = 10 runcard = load_runcard(pathlib.Path(__file__).parent / "dummy.yml") - extras_folder = pathlib.Path(__file__).parent / "dummy/" + extras_folder = pathlib.Path(__file__).parent / "dummy" if not with_couplers: runcard = remove_couplers(runcard) From 0f3ca6d3edf3fc2734d5ff6f978143dcb37468a7 Mon Sep 17 00:00:00 2001 From: Juan Cereijo Date: Wed, 17 Jan 2024 12:00:03 +0400 Subject: [PATCH 18/34] Update src/qibolab/kernels.py Co-authored-by: Alessandro Candido --- src/qibolab/kernels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index eb5fa2b25..b233908b9 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -22,7 +22,7 @@ def load(cls, path: Path): serialized QubitId and values are numpy arrays. """ return cls( - {json.loads(key): value for key, value in dict(np.load(path)).items()} + {json.loads(key): value for key, value in np.load(path).items()} ) def dump(self, path: Path): From 7f7bee9903d9efaeee0e81b98204848f610a69c4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 08:00:16 +0000 Subject: [PATCH 19/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibolab/kernels.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index b233908b9..480fe7841 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -21,9 +21,7 @@ def load(cls, path: Path): The file should contain a serialized dictionary where keys are serialized QubitId and values are numpy arrays. """ - return cls( - {json.loads(key): value for key, value in np.load(path).items()} - ) + return cls({json.loads(key): value for key, value in np.load(path).items()}) def dump(self, path: Path): """Instance method to dump the kernels to a file. From e12a93e9c00642fc686cb32f5a116617eb8924d5 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Wed, 17 Jan 2024 12:15:37 +0400 Subject: [PATCH 20/34] fix: Refactor kernel saving and loading --- src/qibolab/kernels.py | 4 +++- src/qibolab/serialize.py | 10 ++++++---- tests/dummy_qrc/zurich.py | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index 480fe7841..654f97575 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -29,4 +29,6 @@ def dump(self, path: Path): The keys (QubitId) are serialized to strings and the values (numpy arrays) are kept as is. """ - np.savez(path, **{json.dumps(key): value for key, value in self.items()}) + np.savez( + path, **{json.dumps(qubit_id): value for qubit_id, value in self.items()} + ) diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index b80711977..ef3b364b6 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -23,6 +23,8 @@ ) from qibolab.qubits import Qubit, QubitPair +KERNELS_FILE = "kernels.npz" + def load_runcard(path: Path) -> dict: """Load runcard YAML to a dictionary.""" @@ -51,7 +53,7 @@ def load_qubits( } if extras_folder is not None: - kernels = Kernels.load(path=extras_folder / "kernels.npz") + kernels = Kernels.load(path=extras_folder / KERNELS_FILE) for q in kernels: qubits[q].kernel = kernels[q] @@ -188,12 +190,12 @@ def dump_runcard(platform: Platform, path: Path): path (pathlib.Path): Path that the yaml file will be saved. """ - kernels = {} + kernels = Kernels() for qubit in platform.qubits.values(): if qubit.kernel is not None: - kernels[str(qubit.name)] = qubit.kernel + kernels[qubit.name] = qubit.kernel qubit.kernel = None - Kernels(kernels).dump(Path(__file__).parent / "dummy/kernels.npz") + Kernels(kernels).dump(Path(__file__).parent / "dummy" / KERNELS_FILE) settings = { "nqubits": platform.nqubits, diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich.py index e1a6d8d32..1ff4fe654 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich.py @@ -171,8 +171,8 @@ def create(runcard_path=RUNCARD): channels[ch].local_oscillator = local_oscillators[lo] # create qubit objects - runcard = load_runcard(runcard_path, FOLDER) - qubits, couplers, pairs = load_qubits(runcard) + runcard = load_runcard(runcard_path) + qubits, couplers, pairs = load_qubits(runcard, FOLDER) settings = load_settings(runcard) # assign channels to qubits and sweetspots(operating points) From b0b24f966df593f3252b83f282e5de89a120bd51 Mon Sep 17 00:00:00 2001 From: Jacfomg Date: Fri, 19 Jan 2024 21:45:24 +0400 Subject: [PATCH 21/34] fix: path with platform name for Kernels --- src/qibolab/qubits.py | 2 +- src/qibolab/serialize.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/qibolab/qubits.py b/src/qibolab/qubits.py index 90804f72e..1ba6771f2 100644 --- a/src/qibolab/qubits.py +++ b/src/qibolab/qubits.py @@ -15,7 +15,7 @@ Not all channels are required to operate a qubit. """ -EXCLUDED_FIELDS = CHANNEL_NAMES + ("name", "native_gates", "_flux") +EXCLUDED_FIELDS = CHANNEL_NAMES + ("name", "native_gates", "kernels", "_flux") """Qubit dataclass fields that are excluded by the ``characterization`` property.""" diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index ef3b364b6..070518787 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -152,11 +152,9 @@ def dump_characterization(qubits: QubitMap, couplers: CouplerMap = None) -> dict } kernels = Kernels() - for q in qubits: - qubit = characterization["single_qubit"][q] - if qubit["kernel"] is not None: - kernel = qubit.pop("kernel") - kernels[q] = kernel + for qubit in qubits.values(): + if qubit.kernel is not None: + kernels[qubit.name] = qubit.kernel if couplers: characterization["coupler"] = { @@ -195,7 +193,10 @@ def dump_runcard(platform: Platform, path: Path): if qubit.kernel is not None: kernels[qubit.name] = qubit.kernel qubit.kernel = None - Kernels(kernels).dump(Path(__file__).parent / "dummy" / KERNELS_FILE) + name = platform.name + if platform.name == "dummy_couplers": + name = "dummy" + Kernels(kernels).dump(Path(__file__).parent / name / KERNELS_FILE) settings = { "nqubits": platform.nqubits, From 9f37fd890796abc37bcaccd146b675f1a3fda6bb Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 24 Jan 2024 19:09:12 +0400 Subject: [PATCH 22/34] refactor: Introduce folders for platform serialization --- src/qibolab/__init__.py | 11 +++--- src/qibolab/dummy.py | 13 +++++-- src/qibolab/{ => dummy}/dummy.yml | 0 src/qibolab/kernels.py | 5 ++- src/qibolab/qubits.py | 2 +- src/qibolab/serialize.py | 49 ++++++++++++++++--------- tests/dummy_qrc/{ => qblox}/qblox.py | 7 ++-- tests/dummy_qrc/{ => qblox}/qblox.yml | 0 tests/dummy_qrc/{ => qm}/qm.py | 7 ++-- tests/dummy_qrc/{ => qm}/qm.yml | 0 tests/dummy_qrc/{ => rfsoc}/rfsoc.py | 7 ++-- tests/dummy_qrc/{ => rfsoc}/rfsoc.yml | 0 tests/dummy_qrc/{ => zurich}/zurich.py | 16 ++++---- tests/dummy_qrc/{ => zurich}/zurich.yml | 0 tests/test_dummy.py | 1 - tests/test_platform.py | 12 +++--- 16 files changed, 79 insertions(+), 51 deletions(-) rename src/qibolab/{ => dummy}/dummy.yml (100%) rename tests/dummy_qrc/{ => qblox}/qblox.py (96%) rename tests/dummy_qrc/{ => qblox}/qblox.yml (100%) rename tests/dummy_qrc/{ => qm}/qm.py (96%) rename tests/dummy_qrc/{ => qm}/qm.yml (100%) rename tests/dummy_qrc/{ => rfsoc}/rfsoc.py (91%) rename tests/dummy_qrc/{ => rfsoc}/rfsoc.yml (100%) rename tests/dummy_qrc/{ => zurich}/zurich.py (93%) rename tests/dummy_qrc/{ => zurich}/zurich.yml (100%) diff --git a/src/qibolab/__init__.py b/src/qibolab/__init__.py index 885318857..ebc080d29 100644 --- a/src/qibolab/__init__.py +++ b/src/qibolab/__init__.py @@ -29,13 +29,14 @@ def get_platforms_path(): return Path(profiles) -def create_platform(name, runcard=None): +def create_platform(name, path: Path = None) -> Platform: """A platform for executing quantum algorithms. It consists of a quantum processor QPU and a set of controlling instruments. Args: name (str): name of the platform. Options are 'tiiq', 'qili' and 'icarusq'. + path (pathlib.Path): path with platform serialization Returns: The plaform class. """ @@ -44,17 +45,17 @@ def create_platform(name, runcard=None): return create_dummy(with_couplers=name == "dummy_couplers") - platform = get_platforms_path() / f"{name}.py" + platform = get_platforms_path() / f"{name}" if not platform.exists(): raise_error(ValueError, f"Platform {name} does not exist.") - spec = importlib.util.spec_from_file_location("platform", platform) + spec = importlib.util.spec_from_file_location("platform", platform / f"{name}.py") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) - if runcard is None: + if path is None: return module.create() - return module.create(runcard) + return module.create(path) def execute_qasm(circuit: str, platform, runcard=None, initial_state=None, nshots=1000): diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy.py index 18114f710..7684264b0 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy.py @@ -3,8 +3,12 @@ from qibolab.channels import Channel, ChannelMap from qibolab.instruments.dummy import DummyInstrument, DummyLocalOscillator +from qibolab.kernels import Kernels from qibolab.platform import Platform -from qibolab.serialize import load_qubits, load_runcard, load_settings +from qibolab.serialize import KERNELS_FILE, load_qubits, load_runcard, load_settings + +DUMMY_FOLDER = pathlib.Path(__file__).parent / "dummy" +DUMMY_RUNCARD = "dummy.yml" def remove_couplers(runcard): @@ -35,8 +39,9 @@ def create_dummy(with_couplers: bool = True): twpa_pump.frequency = 1e9 twpa_pump.power = 10 - runcard = load_runcard(pathlib.Path(__file__).parent / "dummy.yml") - extras_folder = pathlib.Path(__file__).parent / "dummy" + runcard = load_runcard(DUMMY_FOLDER / "dummy.yml") + kernels = Kernels.load(DUMMY_FOLDER / KERNELS_FILE) + if not with_couplers: runcard = remove_couplers(runcard) @@ -60,7 +65,7 @@ def create_dummy(with_couplers: bool = True): channels["readout"].attenuation = 0 channels["twpa"].local_oscillator = twpa_pump - qubits, couplers, pairs = load_qubits(runcard, extras_folder) + qubits, couplers, pairs = load_qubits(runcard, kernels) settings = load_settings(runcard) # map channels to qubits diff --git a/src/qibolab/dummy.yml b/src/qibolab/dummy/dummy.yml similarity index 100% rename from src/qibolab/dummy.yml rename to src/qibolab/dummy/dummy.yml diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index 654f97575..5fa8f61ce 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -5,6 +5,8 @@ from qibolab.qubits import QubitId +KERNELS_FILE = "kernels.npz" + class Kernels(dict[QubitId, np.ndarray]): """A dictionary subclass for handling Qubit Kernels. @@ -30,5 +32,6 @@ def dump(self, path: Path): (numpy arrays) are kept as is. """ np.savez( - path, **{json.dumps(qubit_id): value for qubit_id, value in self.items()} + path / KERNELS_FILE, + **{json.dumps(qubit_id): value for qubit_id, value in self.items()} ) diff --git a/src/qibolab/qubits.py b/src/qibolab/qubits.py index 1ba6771f2..867640401 100644 --- a/src/qibolab/qubits.py +++ b/src/qibolab/qubits.py @@ -15,7 +15,7 @@ Not all channels are required to operate a qubit. """ -EXCLUDED_FIELDS = CHANNEL_NAMES + ("name", "native_gates", "kernels", "_flux") +EXCLUDED_FIELDS = CHANNEL_NAMES + ("name", "native_gates", "kernel", "_flux") """Qubit dataclass fields that are excluded by the ``characterization`` property.""" diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index 070518787..31f38b98b 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -37,7 +37,7 @@ def load_settings(runcard: dict) -> Settings: def load_qubits( - runcard: dict, extras_folder: Path = None + runcard: dict, kernels: Kernels = None ) -> Tuple[QubitMap, CouplerMap, QubitPairMap]: """Load qubits and pairs from the runcard. @@ -52,8 +52,7 @@ def load_qubits( for q, char in runcard["characterization"]["single_qubit"].items() } - if extras_folder is not None: - kernels = Kernels.load(path=extras_folder / KERNELS_FILE) + if kernels is not None: for q in kernels: qubits[q].kernel = kernels[q] @@ -151,11 +150,6 @@ def dump_characterization(qubits: QubitMap, couplers: CouplerMap = None) -> dict "single_qubit": {q: qubit.characterization for q, qubit in qubits.items()}, } - kernels = Kernels() - for qubit in qubits.values(): - if qubit.kernel is not None: - kernels[qubit.name] = qubit.kernel - if couplers: characterization["coupler"] = { c.name: {"sweetspot": c.sweetspot} for c in couplers.values() @@ -188,16 +182,6 @@ def dump_runcard(platform: Platform, path: Path): path (pathlib.Path): Path that the yaml file will be saved. """ - kernels = Kernels() - for qubit in platform.qubits.values(): - if qubit.kernel is not None: - kernels[qubit.name] = qubit.kernel - qubit.kernel = None - name = platform.name - if platform.name == "dummy_couplers": - name = "dummy" - Kernels(kernels).dump(Path(__file__).parent / name / KERNELS_FILE) - settings = { "nqubits": platform.nqubits, "settings": asdict(platform.settings), @@ -223,3 +207,32 @@ def dump_runcard(platform: Platform, path: Path): path.write_text( yaml.dump(settings, sort_keys=False, indent=4, default_flow_style=None) ) + + +def dump_kernels(platform: Platform, path: Path): + """Creates Kernels instance from platform and dumps as npz. + + Args: + platform (qibolab.platform.Platform): The platform to be serialized. + path (pathlib.Path): Path that the kernels file will be saved. + """ + + # create and dump kernels + kernels = Kernels() + for qubit in platform.qubits.values(): + if qubit.kernel is not None: + kernels[qubit.name] = qubit.kernel + + kernels.dump(path / KERNELS_FILE) + + +def dump_platform(platform: Platform, path: Path): + """Platform serialization as runcard (yaml) and kernels (npz). + + Args: + platform (qibolab.platform.Platform): The platform to be serialized. + path (pathlib.Path): Path where yaml and npz will be dumped. + """ + + dump_kernels(platform=platform, path=path) + dump_runcard(platform=platform, path=path) diff --git a/tests/dummy_qrc/qblox.py b/tests/dummy_qrc/qblox/qblox.py similarity index 96% rename from tests/dummy_qrc/qblox.py rename to tests/dummy_qrc/qblox/qblox.py index 644c5c424..cda319c8b 100644 --- a/tests/dummy_qrc/qblox.py +++ b/tests/dummy_qrc/qblox/qblox.py @@ -17,17 +17,18 @@ NAME = "qblox" ADDRESS = "192.168.0.6" TIME_OF_FLIGHT = 500 -RUNCARD = pathlib.Path(__file__).parent / "qblox.yml" +FOLDER = pathlib.Path(__file__).parent +RUNCARD = "qblox.yml" -def create(runcard_path=RUNCARD): +def create(folder: pathlib.Path = FOLDER): """QuantWare 5q-chip controlled using qblox cluster. Args: runcard_path (str): Path to the runcard file. """ - runcard = load_runcard(runcard_path) + runcard = load_runcard(folder / RUNCARD) modules = {} # DEBUG: debug folder = report folder diff --git a/tests/dummy_qrc/qblox.yml b/tests/dummy_qrc/qblox/qblox.yml similarity index 100% rename from tests/dummy_qrc/qblox.yml rename to tests/dummy_qrc/qblox/qblox.yml diff --git a/tests/dummy_qrc/qm.py b/tests/dummy_qrc/qm/qm.py similarity index 96% rename from tests/dummy_qrc/qm.py rename to tests/dummy_qrc/qm/qm.py index 745a77f6e..2998230b3 100644 --- a/tests/dummy_qrc/qm.py +++ b/tests/dummy_qrc/qm/qm.py @@ -11,10 +11,11 @@ load_settings, ) -RUNCARD = pathlib.Path(__file__).parent / "qm.yml" +FOLDER = pathlib.Path(__file__).parent +RUNCARD = "qm.yml" -def create(runcard_path=RUNCARD): +def create(folder: pathlib.Path = FOLDER): """Dummy platform using Quantum Machines (QM) OPXs and Rohde Schwarz local oscillators. @@ -69,7 +70,7 @@ def create(runcard_path=RUNCARD): channels["L4-26"].local_oscillator = local_oscillators[5] # create qubit objects - runcard = load_runcard(runcard_path) + runcard = load_runcard(FOLDER / RUNCARD) qubits, couplers, pairs = load_qubits(runcard) # assign channels to qubits diff --git a/tests/dummy_qrc/qm.yml b/tests/dummy_qrc/qm/qm.yml similarity index 100% rename from tests/dummy_qrc/qm.yml rename to tests/dummy_qrc/qm/qm.yml diff --git a/tests/dummy_qrc/rfsoc.py b/tests/dummy_qrc/rfsoc/rfsoc.py similarity index 91% rename from tests/dummy_qrc/rfsoc.py rename to tests/dummy_qrc/rfsoc/rfsoc.py index 5bab3f5f1..f21492354 100644 --- a/tests/dummy_qrc/rfsoc.py +++ b/tests/dummy_qrc/rfsoc/rfsoc.py @@ -12,10 +12,11 @@ load_settings, ) -RUNCARD = pathlib.Path(__file__).parent / "rfsoc.yml" +FOLDER = pathlib.Path(__file__).parent +RUNCARD = "rfsoc.yml" -def create(runcard_path=RUNCARD): +def create(folder: pathlib.Path = FOLDER): """Dummy platform using QICK project on the RFSoC4x2 board. Used in ``test_instruments_rfsoc.py``. @@ -34,7 +35,7 @@ def create(runcard_path=RUNCARD): lo_era = ERA("ErasynthLO", "192.168.0.212", ethernet=True) channels["L3-18_ro"].local_oscillator = lo_era - runcard = load_runcard(runcard_path) + runcard = load_runcard(FOLDER / RUNCARD) qubits, couplers, pairs = load_qubits(runcard) # assign channels to qubits diff --git a/tests/dummy_qrc/rfsoc.yml b/tests/dummy_qrc/rfsoc/rfsoc.yml similarity index 100% rename from tests/dummy_qrc/rfsoc.yml rename to tests/dummy_qrc/rfsoc/rfsoc.yml diff --git a/tests/dummy_qrc/zurich.py b/tests/dummy_qrc/zurich/zurich.py similarity index 93% rename from tests/dummy_qrc/zurich.py rename to tests/dummy_qrc/zurich/zurich.py index 1ff4fe654..c61aad30a 100644 --- a/tests/dummy_qrc/zurich.py +++ b/tests/dummy_qrc/zurich/zurich.py @@ -9,6 +9,7 @@ from qibolab.channels import Channel, ChannelMap from qibolab.instruments.dummy import DummyLocalOscillator as LocalOscillator from qibolab.instruments.zhinst import Zurich +from qibolab.kernels import KERNELS_FILE, Kernels from qibolab.serialize import ( load_instrument_settings, load_qubits, @@ -16,16 +17,16 @@ load_settings, ) -RUNCARD = pathlib.Path(__file__).parent / "zurich.yml" -FOLDER = pathlib.Path(__file__).parent / "zurich" +RUNCARD = "zurich.yml" +FOLDER = pathlib.Path(__file__).parent N_QUBITS = 5 -def create(runcard_path=RUNCARD): - """IQM 5q-chip controlled Zurich Instrumetns (Zh) SHFQC, HDAWGs and PQSC. +def create(path: pathlib.Path = FOLDER): + """IQM 5q-chip controlled Zurich Instruments (Zh) SHFQC, HDAWGs and PQSC. Args: - runcard_path (str): Path to the runcard file. + path (str): Path to configuration folder. """ device_setup = DeviceSetup("EL_ZURO") @@ -171,8 +172,9 @@ def create(runcard_path=RUNCARD): channels[ch].local_oscillator = local_oscillators[lo] # create qubit objects - runcard = load_runcard(runcard_path) - qubits, couplers, pairs = load_qubits(runcard, FOLDER) + runcard = load_runcard(FOLDER / RUNCARD) + kernels = Kernels.load(FOLDER / KERNELS_FILE) + qubits, couplers, pairs = load_qubits(runcard, kernels) settings = load_settings(runcard) # assign channels to qubits and sweetspots(operating points) diff --git a/tests/dummy_qrc/zurich.yml b/tests/dummy_qrc/zurich/zurich.yml similarity index 100% rename from tests/dummy_qrc/zurich.yml rename to tests/dummy_qrc/zurich/zurich.yml diff --git a/tests/test_dummy.py b/tests/test_dummy.py index ffc02b422..8109833b9 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -165,7 +165,6 @@ def test_dummy_single_sweep_coupler( sweeper = Sweeper(parameter, parameter_range, couplers=[platform.couplers[0]]) else: sweeper = Sweeper(parameter, parameter_range, pulses=[coupler_pulse]) - print(sweeper) options = ExecutionParameters( nshots=nshots, averaging_mode=average, diff --git a/tests/test_platform.py b/tests/test_platform.py index 09c0193a1..706a0c657 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -11,7 +11,7 @@ from qibolab import create_platform from qibolab.backends import QibolabBackend -from qibolab.dummy import load_dummy_runcard +from qibolab.dummy import DUMMY_FOLDER, DUMMY_RUNCARD from qibolab.execution_parameters import ExecutionParameters from qibolab.instruments.qblox.controller import QbloxController from qibolab.instruments.rfsoc.driver import RFSoC @@ -60,15 +60,17 @@ def test_platform_pickle(platform): assert new_platform.is_connected == platform.is_connected -def test_dump_runcard(platform): - path = pathlib.Path(__file__).parent / "test.yml" +def test_dump_runcard(platform, tmp_path): + path = tmp_path / "test.yml" dump_runcard(platform, path) final_runcard = load_runcard(path) if platform.name == "dummy" or platform.name == "dummy_couplers": - target_runcard = load_dummy_runcard() + target_runcard = load_runcard(DUMMY_FOLDER / DUMMY_RUNCARD) else: target_path = ( - pathlib.Path(__file__).parent / "dummy_qrc" / f"{platform.name}.yml" + pathlib.Path(__file__).parent + / "dummy_qrc" + / f"{platform.name}/{platform.name}.yml" ) target_runcard = load_runcard(target_path) # for the characterization section the dumped runcard may contain From 1e6a7dc4f438af6298a2d51c7c9caab17288a7b5 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 24 Jan 2024 19:19:29 +0400 Subject: [PATCH 23/34] fix: Fix test in doc --- doc/source/main-documentation/qibolab.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/main-documentation/qibolab.rst b/doc/source/main-documentation/qibolab.rst index e7e772b37..494b12b38 100644 --- a/doc/source/main-documentation/qibolab.rst +++ b/doc/source/main-documentation/qibolab.rst @@ -213,14 +213,14 @@ Following the tutorial in :doc:`/tutorials/lab`, we can continue the initializat from pathlib import Path from qibolab.serialize import load_qubits, load_runcard - runcard_path = Path.cwd().parent / "src" / "qibolab" / "dummy.yml" + path = Path.cwd().parent / "src" / "qibolab" / "dummy" ch_map = ChannelMap() ch_map |= channel1 ch_map |= channel2 ch_map |= channel3 - runcard = load_runcard(runcard_path) + runcard = load_runcard(path / "dummy.yml") qubits, couplers, pairs = load_qubits(runcard) qubits[0].drive = channel1 From 3636038ebbd2979e3be8887c920d90463f1347be Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 24 Jan 2024 22:37:44 +0400 Subject: [PATCH 24/34] refactor: Standardize file names --- src/qibolab/__init__.py | 5 +++-- src/qibolab/dummy/__init__.py | 0 src/qibolab/dummy/{dummy.yml => parameters.yml} | 0 src/qibolab/{dummy.py => dummy/platform.py} | 9 ++++----- src/qibolab/kernels.py | 8 +++++--- src/qibolab/serialize.py | 11 ++++++----- .../qblox/{qblox.yml => parameters.yml} | 0 tests/dummy_qrc/qblox/{qblox.py => platform.py} | 8 ++++---- tests/dummy_qrc/qm/{qm.yml => parameters.yml} | 0 tests/dummy_qrc/qm/{qm.py => platform.py} | 7 ++++--- .../rfsoc/{rfsoc.yml => parameters.yml} | 0 tests/dummy_qrc/rfsoc/{rfsoc.py => platform.py} | 7 ++++--- .../zurich/{zurich.yml => parameters.yml} | 0 .../dummy_qrc/zurich/{zurich.py => platform.py} | 9 ++++----- tests/test_platform.py | 17 +++++------------ 15 files changed, 39 insertions(+), 42 deletions(-) create mode 100644 src/qibolab/dummy/__init__.py rename src/qibolab/dummy/{dummy.yml => parameters.yml} (100%) rename src/qibolab/{dummy.py => dummy/platform.py} (90%) rename tests/dummy_qrc/qblox/{qblox.yml => parameters.yml} (100%) rename tests/dummy_qrc/qblox/{qblox.py => platform.py} (95%) rename tests/dummy_qrc/qm/{qm.yml => parameters.yml} (100%) rename tests/dummy_qrc/qm/{qm.py => platform.py} (96%) rename tests/dummy_qrc/rfsoc/{rfsoc.yml => parameters.yml} (100%) rename tests/dummy_qrc/rfsoc/{rfsoc.py => platform.py} (91%) rename tests/dummy_qrc/zurich/{zurich.yml => parameters.yml} (100%) rename tests/dummy_qrc/zurich/{zurich.py => platform.py} (97%) diff --git a/src/qibolab/__init__.py b/src/qibolab/__init__.py index ebc080d29..26a2b5c78 100644 --- a/src/qibolab/__init__.py +++ b/src/qibolab/__init__.py @@ -12,6 +12,7 @@ ExecutionParameters, ) from qibolab.platform import Platform +from qibolab.serialize import PLATFORM __version__ = im.version(__package__) @@ -41,7 +42,7 @@ def create_platform(name, path: Path = None) -> Platform: The plaform class. """ if name == "dummy" or name == "dummy_couplers": - from qibolab.dummy import create_dummy + from qibolab.dummy.platform import create_dummy return create_dummy(with_couplers=name == "dummy_couplers") @@ -49,7 +50,7 @@ def create_platform(name, path: Path = None) -> Platform: if not platform.exists(): raise_error(ValueError, f"Platform {name} does not exist.") - spec = importlib.util.spec_from_file_location("platform", platform / f"{name}.py") + spec = importlib.util.spec_from_file_location("platform", platform / PLATFORM) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) diff --git a/src/qibolab/dummy/__init__.py b/src/qibolab/dummy/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/qibolab/dummy/dummy.yml b/src/qibolab/dummy/parameters.yml similarity index 100% rename from src/qibolab/dummy/dummy.yml rename to src/qibolab/dummy/parameters.yml diff --git a/src/qibolab/dummy.py b/src/qibolab/dummy/platform.py similarity index 90% rename from src/qibolab/dummy.py rename to src/qibolab/dummy/platform.py index 7684264b0..05e91fcd0 100644 --- a/src/qibolab/dummy.py +++ b/src/qibolab/dummy/platform.py @@ -5,10 +5,9 @@ from qibolab.instruments.dummy import DummyInstrument, DummyLocalOscillator from qibolab.kernels import Kernels from qibolab.platform import Platform -from qibolab.serialize import KERNELS_FILE, load_qubits, load_runcard, load_settings +from qibolab.serialize import load_qubits, load_runcard, load_settings -DUMMY_FOLDER = pathlib.Path(__file__).parent / "dummy" -DUMMY_RUNCARD = "dummy.yml" +FOLDER = pathlib.Path(__file__).parent def remove_couplers(runcard): @@ -39,8 +38,8 @@ def create_dummy(with_couplers: bool = True): twpa_pump.frequency = 1e9 twpa_pump.power = 10 - runcard = load_runcard(DUMMY_FOLDER / "dummy.yml") - kernels = Kernels.load(DUMMY_FOLDER / KERNELS_FILE) + runcard = load_runcard(FOLDER) + kernels = Kernels.load(FOLDER) if not with_couplers: runcard = remove_couplers(runcard) diff --git a/src/qibolab/kernels.py b/src/qibolab/kernels.py index 5fa8f61ce..9ee1ae60d 100644 --- a/src/qibolab/kernels.py +++ b/src/qibolab/kernels.py @@ -5,7 +5,7 @@ from qibolab.qubits import QubitId -KERNELS_FILE = "kernels.npz" +KERNELS = "kernels.npz" class Kernels(dict[QubitId, np.ndarray]): @@ -23,7 +23,9 @@ def load(cls, path: Path): The file should contain a serialized dictionary where keys are serialized QubitId and values are numpy arrays. """ - return cls({json.loads(key): value for key, value in np.load(path).items()}) + return cls( + {json.loads(key): value for key, value in np.load(path / KERNELS).items()} + ) def dump(self, path: Path): """Instance method to dump the kernels to a file. @@ -32,6 +34,6 @@ def dump(self, path: Path): (numpy arrays) are kept as is. """ np.savez( - path / KERNELS_FILE, + path / KERNELS, **{json.dumps(qubit_id): value for qubit_id, value in self.items()} ) diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index 31f38b98b..ca49528d0 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -11,7 +11,7 @@ import yaml from qibolab.couplers import Coupler -from qibolab.kernels import Kernels +from qibolab.kernels import KERNELS, Kernels from qibolab.native import CouplerNatives, SingleQubitNatives, TwoQubitNatives from qibolab.platform import ( CouplerMap, @@ -23,12 +23,13 @@ ) from qibolab.qubits import Qubit, QubitPair -KERNELS_FILE = "kernels.npz" +RUNCARD = "parameters.yml" +PLATFORM = "platform.py" def load_runcard(path: Path) -> dict: """Load runcard YAML to a dictionary.""" - return yaml.safe_load(path.read_text()) + return yaml.safe_load((path / RUNCARD).read_text()) def load_settings(runcard: dict) -> Settings: @@ -204,7 +205,7 @@ def dump_runcard(platform: Platform, path: Path): platform.qubits, platform.couplers ) - path.write_text( + (path / RUNCARD).write_text( yaml.dump(settings, sort_keys=False, indent=4, default_flow_style=None) ) @@ -223,7 +224,7 @@ def dump_kernels(platform: Platform, path: Path): if qubit.kernel is not None: kernels[qubit.name] = qubit.kernel - kernels.dump(path / KERNELS_FILE) + kernels.dump(path / KERNELS) def dump_platform(platform: Platform, path: Path): diff --git a/tests/dummy_qrc/qblox/qblox.yml b/tests/dummy_qrc/qblox/parameters.yml similarity index 100% rename from tests/dummy_qrc/qblox/qblox.yml rename to tests/dummy_qrc/qblox/parameters.yml diff --git a/tests/dummy_qrc/qblox/qblox.py b/tests/dummy_qrc/qblox/platform.py similarity index 95% rename from tests/dummy_qrc/qblox/qblox.py rename to tests/dummy_qrc/qblox/platform.py index cda319c8b..6c7c47be3 100644 --- a/tests/dummy_qrc/qblox/qblox.py +++ b/tests/dummy_qrc/qblox/platform.py @@ -14,11 +14,9 @@ load_settings, ) -NAME = "qblox" ADDRESS = "192.168.0.6" TIME_OF_FLIGHT = 500 FOLDER = pathlib.Path(__file__).parent -RUNCARD = "qblox.yml" def create(folder: pathlib.Path = FOLDER): @@ -28,7 +26,7 @@ def create(folder: pathlib.Path = FOLDER): runcard_path (str): Path to the runcard file. """ - runcard = load_runcard(folder / RUNCARD) + runcard = load_runcard(folder) modules = {} # DEBUG: debug folder = report folder @@ -112,4 +110,6 @@ def create(folder: pathlib.Path = FOLDER): settings = load_settings(runcard) - return Platform("qblox", qubits, pairs, instruments, settings, resonator_type="2D") + return Platform( + str(FOLDER), qubits, pairs, instruments, settings, resonator_type="2D" + ) diff --git a/tests/dummy_qrc/qm/qm.yml b/tests/dummy_qrc/qm/parameters.yml similarity index 100% rename from tests/dummy_qrc/qm/qm.yml rename to tests/dummy_qrc/qm/parameters.yml diff --git a/tests/dummy_qrc/qm/qm.py b/tests/dummy_qrc/qm/platform.py similarity index 96% rename from tests/dummy_qrc/qm/qm.py rename to tests/dummy_qrc/qm/platform.py index 2998230b3..3c54d6ccc 100644 --- a/tests/dummy_qrc/qm/qm.py +++ b/tests/dummy_qrc/qm/platform.py @@ -12,7 +12,6 @@ ) FOLDER = pathlib.Path(__file__).parent -RUNCARD = "qm.yml" def create(folder: pathlib.Path = FOLDER): @@ -70,7 +69,7 @@ def create(folder: pathlib.Path = FOLDER): channels["L4-26"].local_oscillator = local_oscillators[5] # create qubit objects - runcard = load_runcard(FOLDER / RUNCARD) + runcard = load_runcard(folder) qubits, couplers, pairs = load_qubits(runcard) # assign channels to qubits @@ -102,4 +101,6 @@ def create(folder: pathlib.Path = FOLDER): instruments.update({lo.name: lo for lo in local_oscillators}) settings = load_settings(runcard) instruments = load_instrument_settings(runcard, instruments) - return Platform("qm", qubits, pairs, instruments, settings, resonator_type="2D") + return Platform( + str(FOLDER), qubits, pairs, instruments, settings, resonator_type="2D" + ) diff --git a/tests/dummy_qrc/rfsoc/rfsoc.yml b/tests/dummy_qrc/rfsoc/parameters.yml similarity index 100% rename from tests/dummy_qrc/rfsoc/rfsoc.yml rename to tests/dummy_qrc/rfsoc/parameters.yml diff --git a/tests/dummy_qrc/rfsoc/rfsoc.py b/tests/dummy_qrc/rfsoc/platform.py similarity index 91% rename from tests/dummy_qrc/rfsoc/rfsoc.py rename to tests/dummy_qrc/rfsoc/platform.py index f21492354..b8729571b 100644 --- a/tests/dummy_qrc/rfsoc/rfsoc.py +++ b/tests/dummy_qrc/rfsoc/platform.py @@ -13,7 +13,6 @@ ) FOLDER = pathlib.Path(__file__).parent -RUNCARD = "rfsoc.yml" def create(folder: pathlib.Path = FOLDER): @@ -35,7 +34,7 @@ def create(folder: pathlib.Path = FOLDER): lo_era = ERA("ErasynthLO", "192.168.0.212", ethernet=True) channels["L3-18_ro"].local_oscillator = lo_era - runcard = load_runcard(FOLDER / RUNCARD) + runcard = load_runcard(FOLDER) qubits, couplers, pairs = load_qubits(runcard) # assign channels to qubits @@ -47,4 +46,6 @@ def create(folder: pathlib.Path = FOLDER): instruments = {inst.name: inst for inst in [controller, lo_twpa, lo_era]} settings = load_settings(runcard) instruments = load_instrument_settings(runcard, instruments) - return Platform("rfsoc", qubits, pairs, instruments, settings, resonator_type="3D") + return Platform( + str(FOLDER), qubits, pairs, instruments, settings, resonator_type="3D" + ) diff --git a/tests/dummy_qrc/zurich/zurich.yml b/tests/dummy_qrc/zurich/parameters.yml similarity index 100% rename from tests/dummy_qrc/zurich/zurich.yml rename to tests/dummy_qrc/zurich/parameters.yml diff --git a/tests/dummy_qrc/zurich/zurich.py b/tests/dummy_qrc/zurich/platform.py similarity index 97% rename from tests/dummy_qrc/zurich/zurich.py rename to tests/dummy_qrc/zurich/platform.py index c61aad30a..e74c11672 100644 --- a/tests/dummy_qrc/zurich/zurich.py +++ b/tests/dummy_qrc/zurich/platform.py @@ -9,7 +9,7 @@ from qibolab.channels import Channel, ChannelMap from qibolab.instruments.dummy import DummyLocalOscillator as LocalOscillator from qibolab.instruments.zhinst import Zurich -from qibolab.kernels import KERNELS_FILE, Kernels +from qibolab.kernels import Kernels from qibolab.serialize import ( load_instrument_settings, load_qubits, @@ -17,7 +17,6 @@ load_settings, ) -RUNCARD = "zurich.yml" FOLDER = pathlib.Path(__file__).parent N_QUBITS = 5 @@ -172,8 +171,8 @@ def create(path: pathlib.Path = FOLDER): channels[ch].local_oscillator = local_oscillators[lo] # create qubit objects - runcard = load_runcard(FOLDER / RUNCARD) - kernels = Kernels.load(FOLDER / KERNELS_FILE) + runcard = load_runcard(path) + kernels = Kernels.load(path) qubits, couplers, pairs = load_qubits(runcard, kernels) settings = load_settings(runcard) @@ -195,7 +194,7 @@ def create(path: pathlib.Path = FOLDER): instruments.update({lo.name: lo for lo in local_oscillators}) instruments = load_instrument_settings(runcard, instruments) return Platform( - "zurich", + str(FOLDER), qubits, pairs, instruments, diff --git a/tests/test_platform.py b/tests/test_platform.py index 706a0c657..9a065a2b8 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -1,6 +1,5 @@ """Tests :class:`qibolab.platforms.multiqubit.MultiqubitPlatform` and :class:`qibolab.platforms.platform.DesignPlatform`.""" -import os import pathlib import pickle import warnings @@ -11,7 +10,7 @@ from qibolab import create_platform from qibolab.backends import QibolabBackend -from qibolab.dummy import DUMMY_FOLDER, DUMMY_RUNCARD +from qibolab.dummy.platform import FOLDER from qibolab.execution_parameters import ExecutionParameters from qibolab.instruments.qblox.controller import QbloxController from qibolab.instruments.rfsoc.driver import RFSoC @@ -61,17 +60,12 @@ def test_platform_pickle(platform): def test_dump_runcard(platform, tmp_path): - path = tmp_path / "test.yml" - dump_runcard(platform, path) - final_runcard = load_runcard(path) + dump_runcard(platform, tmp_path) + final_runcard = load_runcard(tmp_path) if platform.name == "dummy" or platform.name == "dummy_couplers": - target_runcard = load_runcard(DUMMY_FOLDER / DUMMY_RUNCARD) + target_runcard = load_runcard(FOLDER) else: - target_path = ( - pathlib.Path(__file__).parent - / "dummy_qrc" - / f"{platform.name}/{platform.name}.yml" - ) + target_path = pathlib.Path(__file__).parent / "dummy_qrc" / f"{platform.name}" target_runcard = load_runcard(target_path) # for the characterization section the dumped runcard may contain # some default ``Qubit`` parameters @@ -85,7 +79,6 @@ def test_dump_runcard(platform, tmp_path): target_instruments = target_runcard.pop("instruments") final_instruments = final_runcard.pop("instruments") assert final_instruments == target_instruments - os.remove(path) @pytest.fixture(scope="module") From 4b8a505c55dfd7ad5e15be73a21c82527c234b19 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 24 Jan 2024 22:47:08 +0400 Subject: [PATCH 25/34] fix: Fix doctest --- doc/source/main-documentation/qibolab.rst | 2 +- src/qibolab/platform.py | 2 +- src/qibolab/sweeper.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/main-documentation/qibolab.rst b/doc/source/main-documentation/qibolab.rst index 494b12b38..59c74a615 100644 --- a/doc/source/main-documentation/qibolab.rst +++ b/doc/source/main-documentation/qibolab.rst @@ -220,7 +220,7 @@ Following the tutorial in :doc:`/tutorials/lab`, we can continue the initializat ch_map |= channel2 ch_map |= channel3 - runcard = load_runcard(path / "dummy.yml") + runcard = load_runcard(path) qubits, couplers, pairs = load_qubits(runcard) qubits[0].drive = channel1 diff --git a/src/qibolab/platform.py b/src/qibolab/platform.py index 5a5e995e7..b53eda61f 100644 --- a/src/qibolab/platform.py +++ b/src/qibolab/platform.py @@ -265,7 +265,7 @@ def sweep( .. testcode:: import numpy as np - from qibolab.dummy import create_dummy + from qibolab.dummy.platform import create_dummy from qibolab.sweeper import Sweeper, Parameter from qibolab.pulses import PulseSequence from qibolab.execution_parameters import ExecutionParameters diff --git a/src/qibolab/sweeper.py b/src/qibolab/sweeper.py index c8539faca..56edddc31 100644 --- a/src/qibolab/sweeper.py +++ b/src/qibolab/sweeper.py @@ -55,7 +55,7 @@ class Sweeper: .. testcode:: import numpy as np - from qibolab.dummy import create_dummy + from qibolab.dummy.platform import create_dummy from qibolab.sweeper import Sweeper, Parameter from qibolab.pulses import PulseSequence from qibolab import ExecutionParameters From 55723ed72bafe05b64c8b7c3c4549a7058532100 Mon Sep 17 00:00:00 2001 From: Andrea Date: Thu, 25 Jan 2024 10:37:00 +0400 Subject: [PATCH 26/34] fix: Fix tests related to qibosoq 0.1.1 --- tests/test_instruments_rfsoc.py | 71 ++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/tests/test_instruments_rfsoc.py b/tests/test_instruments_rfsoc.py index 67aa7f796..555ddd3fa 100644 --- a/tests/test_instruments_rfsoc.py +++ b/tests/test_instruments_rfsoc.py @@ -107,20 +107,79 @@ def test_convert_pulse(dummy_qrc): qubit.feedback.port = controller.ports(1) qubit.readout.local_oscillator.frequency = 1e6 - pulse = Pulse(0, 40, 0.9, 50e6, 0, Drag(5, 2), 0, PulseType.DRIVE, 0) + pulse = Pulse( + start=0, + duration=40, + amplitude=0.9, + frequency=50e6, + relative_phase=0, + shape=Drag(5, 2), + channel=0, + type=PulseType.DRIVE, + qubit=0, + ) targ = rfsoc_pulses.Drag( - 50, 0.9, 0, 0, 0.04, pulse.serial, "drive", 4, None, rel_sigma=5, beta=2 + type="drive", + frequency=50, + amplitude=0.9, + start_delay=0, + duration=0.04, + adc=None, + dac=4, + name=pulse.serial, + relative_phase=0, + rel_sigma=5, + beta=2, ) assert convert(pulse, platform.qubits, 0, sampling_rate=1) == targ - pulse = Pulse(0, 40, 0.9, 50e6, 0, Gaussian(2), 0, PulseType.DRIVE, 0) + pulse = Pulse( + start=0, + duration=40, + amplitude=0.9, + frequency=50e6, + relative_phase=0, + shape=Gaussian(2), + channel=0, + type=PulseType.DRIVE, + qubit=0, + ) targ = rfsoc_pulses.Gaussian( - 50, 0.9, 0, 0, 0.04, pulse.serial, "drive", 4, None, rel_sigma=2 + frequency=50, + amplitude=0.9, + start_delay=0, + relative_phase=0, + duration=0.04, + name=pulse.serial, + type="drive", + dac=4, + adc=None, + rel_sigma=2, ) assert convert(pulse, platform.qubits, 0, sampling_rate=1) == targ - pulse = Pulse(0, 40, 0.9, 50e6, 0, Rectangular(), 0, PulseType.READOUT, 0) - targ = rfsoc_pulses.Rectangular(49, 0.9, 0, 0, 0.04, pulse.serial, "readout", 2, 1) + pulse = Pulse( + start=0, + duration=40, + amplitude=0.9, + frequency=50e6, + relative_phase=0, + shape=Rectangular(), + channel=0, + type=PulseType.READOUT, + qubit=0, + ) + targ = rfsoc_pulses.Rectangular( + frequency=49, + amplitude=0.9, + start_delay=0, + relative_phase=0, + duration=0.04, + name=pulse.serial, + type="readout", + dac=2, + adc=1, + ) assert convert(pulse, platform.qubits, 0, sampling_rate=1) == targ From 26e30d8c419d894d4d1db2da44269603aac7ac00 Mon Sep 17 00:00:00 2001 From: Andrea Date: Thu, 25 Jan 2024 15:23:48 +0400 Subject: [PATCH 27/34] doc: Improve documentation following new folder structure --- doc/source/getting-started/experiment.rst | 31 ++++++++++++++++++++--- doc/source/tutorials/lab.rst | 16 ++++++------ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/doc/source/getting-started/experiment.rst b/doc/source/getting-started/experiment.rst index 50db88446..7aa67b740 100644 --- a/doc/source/getting-started/experiment.rst +++ b/doc/source/getting-started/experiment.rst @@ -5,7 +5,19 @@ Define the platform ------------------- To launch experiments on quantum hardware, users have first to define their platform. -A platform is composed of a python file, with instruments information, and of a runcard file, with calibration parameters. +To define a platform the user needs to provide a folder with the following structure: + +.. code-block:: bash + + my_platform/ + platform.py + parameters.json + kernels.npz # (optional) + +where ``platform.py`` contains instruments information, ``parameters.json`` +includes calibration parameters and ``kernels.npz`` is an optional +file with additional calibration parameters. + More information about defining platforms is provided in :doc:`../tutorials/lab` and several examples can be found at `TII dedicated repository `_. For a first experiment, let's define a single qubit platform at the path previously specified. @@ -13,7 +25,7 @@ For simplicity, the qubit will be controlled by a RFSoC-based system, althought .. testcode:: python - # my_platform.py + # my_platform/platform.py import pathlib @@ -53,7 +65,20 @@ For simplicity, the qubit will be controlled by a RFSoC-based system, althought settings = load_settings(runcard) return Platform(NAME, qubits, pairs, instruments, settings, resonator_type="3D") -And the we can define the runcard: +.. note:: + + The ``platform.py`` file must contain a ``create_function`` with the following signature: + + .. code-block:: python + + import pathlib + from qibolab.platform import Platform + + + def create(folder: Path) -> Platform: + """Function that generates Qibolab platform.""" + +And the we can define the runcard ``my_platform/parameters.json``: .. code-block:: yaml diff --git a/doc/source/tutorials/lab.rst b/doc/source/tutorials/lab.rst index 9a41bc6eb..3c5d86b58 100644 --- a/doc/source/tutorials/lab.rst +++ b/doc/source/tutorials/lab.rst @@ -408,7 +408,7 @@ the above runcard: from qibolab.instruments.dummy import DummyInstrument - def create(): + def create(folder: Path): # Create a controller instrument instrument = DummyInstrument("my_instrument", "0.0.0.0:0") @@ -422,7 +422,7 @@ the above runcard: channels |= Channel("chf2", port=instrument["o5"]) # create ``Qubit`` and ``QubitPair`` objects by loading the runcard - runcard = load_runcard(Path(__file__).parent / "my_platform.yml") + runcard = load_runcard(folder) qubits, couplers, pairs = load_qubits(runcard) # assign channels to the qubit @@ -444,7 +444,7 @@ With the following additions for coupler architectures: .. testcode:: python - def create(): + def create(folder): # Create a controller instrument instrument = DummyInstrument("my_instrument", "0.0.0.0:0") @@ -459,7 +459,7 @@ With the following additions for coupler architectures: channels |= Channel("chfc0", port=instrument["o6"]) # create ``Qubit`` and ``QubitPair`` objects by loading the runcard - runcard = load_runcard(Path(__file__).parent / "my_platform.yml") + runcard = load_runcard(folder) qubits, couplers, pairs = load_qubits(runcard) # assign channels to the qubit @@ -486,8 +486,8 @@ With the following additions for coupler architectures: couplers=couplers, ) -Note that this assumes that the runcard is saved as ``my_platform.yml`` in the -same directory with the Python file that contains ``create()``. +Note that this assumes that the runcard is saved as ``/parameters.json`` where ```` +is the directory containing ``platform.py``. Instrument settings @@ -612,7 +612,7 @@ in this case ``"twpa_pump"``. from qibolab.instruments.oscillator import LocalOscillator - def create(): + def create(folder: Path): # Create a controller instrument instrument = DummyInstrument("my_instrument", "0.0.0.0:0") twpa = LocalOscillator("twpa_pump", "0.0.0.1") @@ -625,7 +625,7 @@ in this case ``"twpa_pump"``. channels |= Channel("ch1in", port=instrument["i1"]) # create ``Qubit`` and ``QubitPair`` objects by loading the runcard - runcard = load_runcard(Path(__file__).parent / "my_platform.yml") + runcard = load_runcard(folder) qubits, pairs = load_qubits(runcard) # assign channels to the qubit From d33823c63bd85dd6fd9478cf33c3099cc147ee3b Mon Sep 17 00:00:00 2001 From: Andrea Date: Thu, 25 Jan 2024 15:46:13 +0400 Subject: [PATCH 28/34] doc: Update QIBOLAB_PLATFORMS meaning --- doc/source/getting-started/experiment.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/source/getting-started/experiment.rst b/doc/source/getting-started/experiment.rst index 7aa67b740..5a26fc76a 100644 --- a/doc/source/getting-started/experiment.rst +++ b/doc/source/getting-started/experiment.rst @@ -131,19 +131,20 @@ And the we can define the runcard ``my_platform/parameters.json``: Setting up the environment -------------------------- -After defining the platform, we must instruct ``qibolab`` of the location of the create file. +After defining the platform, we must instruct ``qibolab`` of the location of the platform(s). +We need to define the path that contains platform folders. This can be done using an environment variable: for Unix based systems: .. code-block:: bash - export QIBOLAB_PLATFORMS= + export QIBOLAB_PLATFORMS= for Windows: .. code-block:: bash - $env:QIBOLAB_PLATFORMS="" + $env:QIBOLAB_PLATFORMS="" To avoid having to repeat this export command for every session, this line can be added to the ``.bashrc`` file (or alternatives as ``.zshrc``). From fa13c5d9aa7c1747cf2050bac5830f9dde52f2da Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 25 Jan 2024 16:25:13 +0100 Subject: [PATCH 29/34] fix: Usage of runcard folders in docs Partial port from #782 --- doc/source/getting-started/experiment.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/getting-started/experiment.rst b/doc/source/getting-started/experiment.rst index 5a26fc76a..b092c0f59 100644 --- a/doc/source/getting-started/experiment.rst +++ b/doc/source/getting-started/experiment.rst @@ -36,14 +36,14 @@ For simplicity, the qubit will be controlled by a RFSoC-based system, althought from qibolab.serialize import load_qubits, load_runcard, load_settings NAME = "my_platform" # name of the platform - ADDRESS = "192.168.0.1" # ip adress of the controller + ADDRESS = "192.168.0.1" # ip address of the controller PORT = 6000 # port of the controller - # path to runcard file with calibration parameter - RUNCARD = pathlib.Path.cwd() / "my_platform.yml" + # folder containing runcard with calibration parameters + FOLDER = pathlib.Path.cwd() - def create(runcard_path=RUNCARD): + def create(folder=FOLDER): # Instantiate controller instruments controller = RFSoC(NAME, ADDRESS, PORT) @@ -54,7 +54,7 @@ For simplicity, the qubit will be controlled by a RFSoC-based system, althought channels |= Channel("drive", port=controller[0]) # create qubit objects - runcard = load_runcard(runcard_path) + runcard = load_runcard(folder) qubits, pairs = load_qubits(runcard) # assign channels to qubits qubits[0].readout = channels["L3-22_ro"] From d869de08af0a09a3bb7dbaf05ee7efcae0d436de Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 25 Jan 2024 16:36:29 +0100 Subject: [PATCH 30/34] fix: Postpone more json occurrences --- doc/source/getting-started/experiment.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/getting-started/experiment.rst b/doc/source/getting-started/experiment.rst index b092c0f59..4ce04808c 100644 --- a/doc/source/getting-started/experiment.rst +++ b/doc/source/getting-started/experiment.rst @@ -11,10 +11,10 @@ To define a platform the user needs to provide a folder with the following struc my_platform/ platform.py - parameters.json + parameters.yml kernels.npz # (optional) -where ``platform.py`` contains instruments information, ``parameters.json`` +where ``platform.py`` contains instruments information, ``parameters.yml`` includes calibration parameters and ``kernels.npz`` is an optional file with additional calibration parameters. @@ -78,7 +78,7 @@ For simplicity, the qubit will be controlled by a RFSoC-based system, althought def create(folder: Path) -> Platform: """Function that generates Qibolab platform.""" -And the we can define the runcard ``my_platform/parameters.json``: +And the we can define the runcard ``my_platform/parameters.yml``: .. code-block:: yaml From c13a8a71b7ea8f4a4712dd2045f19e6f0b98ca5e Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 26 Jan 2024 08:36:53 +0100 Subject: [PATCH 31/34] Update doc/source/tutorials/lab.rst Co-authored-by: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> --- doc/source/tutorials/lab.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/tutorials/lab.rst b/doc/source/tutorials/lab.rst index 3c5d86b58..d0ba8ba71 100644 --- a/doc/source/tutorials/lab.rst +++ b/doc/source/tutorials/lab.rst @@ -486,7 +486,7 @@ With the following additions for coupler architectures: couplers=couplers, ) -Note that this assumes that the runcard is saved as ``/parameters.json`` where ```` +Note that this assumes that the runcard is saved as ``/parameters.yml`` where ```` is the directory containing ``platform.py``. From 5e46e6f43bc6a6a0920d0ee669a48f0e26c2a4e9 Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 26 Jan 2024 13:12:07 +0400 Subject: [PATCH 32/34] fix: Fix dummy interface --- src/qibolab/__init__.py | 2 +- src/qibolab/dummy/__init__.py | 1 + src/qibolab/platform.py | 2 +- src/qibolab/sweeper.py | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qibolab/__init__.py b/src/qibolab/__init__.py index 26a2b5c78..9afb03f12 100644 --- a/src/qibolab/__init__.py +++ b/src/qibolab/__init__.py @@ -42,7 +42,7 @@ def create_platform(name, path: Path = None) -> Platform: The plaform class. """ if name == "dummy" or name == "dummy_couplers": - from qibolab.dummy.platform import create_dummy + from qibolab.dummy import create_dummy return create_dummy(with_couplers=name == "dummy_couplers") diff --git a/src/qibolab/dummy/__init__.py b/src/qibolab/dummy/__init__.py index e69de29bb..137e075db 100644 --- a/src/qibolab/dummy/__init__.py +++ b/src/qibolab/dummy/__init__.py @@ -0,0 +1 @@ +from .platform import create_dummy diff --git a/src/qibolab/platform.py b/src/qibolab/platform.py index b53eda61f..5a5e995e7 100644 --- a/src/qibolab/platform.py +++ b/src/qibolab/platform.py @@ -265,7 +265,7 @@ def sweep( .. testcode:: import numpy as np - from qibolab.dummy.platform import create_dummy + from qibolab.dummy import create_dummy from qibolab.sweeper import Sweeper, Parameter from qibolab.pulses import PulseSequence from qibolab.execution_parameters import ExecutionParameters diff --git a/src/qibolab/sweeper.py b/src/qibolab/sweeper.py index 56edddc31..c8539faca 100644 --- a/src/qibolab/sweeper.py +++ b/src/qibolab/sweeper.py @@ -55,7 +55,7 @@ class Sweeper: .. testcode:: import numpy as np - from qibolab.dummy.platform import create_dummy + from qibolab.dummy import create_dummy from qibolab.sweeper import Sweeper, Parameter from qibolab.pulses import PulseSequence from qibolab import ExecutionParameters From a3fd68fba1a0f69e7c78a2c4e6196e2af4a6eeb8 Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 26 Jan 2024 15:00:17 +0400 Subject: [PATCH 33/34] refactor: Dump kernels only if they are not empty --- src/qibolab/serialize.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/qibolab/serialize.py b/src/qibolab/serialize.py index ca49528d0..6ddcdb7ad 100644 --- a/src/qibolab/serialize.py +++ b/src/qibolab/serialize.py @@ -11,7 +11,7 @@ import yaml from qibolab.couplers import Coupler -from qibolab.kernels import KERNELS, Kernels +from qibolab.kernels import Kernels from qibolab.native import CouplerNatives, SingleQubitNatives, TwoQubitNatives from qibolab.platform import ( CouplerMap, @@ -218,13 +218,15 @@ def dump_kernels(platform: Platform, path: Path): path (pathlib.Path): Path that the kernels file will be saved. """ - # create and dump kernels + # create kernels kernels = Kernels() for qubit in platform.qubits.values(): if qubit.kernel is not None: kernels[qubit.name] = qubit.kernel - kernels.dump(path / KERNELS) + # dump only if not None + if kernels: + kernels.dump(path) def dump_platform(platform: Platform, path: Path): From 8f49f89ec3706b9b21f23441d96813a4eb375bb8 Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 26 Jan 2024 15:50:00 +0400 Subject: [PATCH 34/34] test: Implement additional tests dumping and loading --- tests/test_platform.py | 51 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/tests/test_platform.py b/tests/test_platform.py index 9a065a2b8..0dca4522b 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -7,16 +7,25 @@ import numpy as np import pytest from qibo.models import Circuit +from qibo.result import CircuitResult from qibolab import create_platform from qibolab.backends import QibolabBackend +from qibolab.dummy import create_dummy from qibolab.dummy.platform import FOLDER from qibolab.execution_parameters import ExecutionParameters from qibolab.instruments.qblox.controller import QbloxController from qibolab.instruments.rfsoc.driver import RFSoC +from qibolab.kernels import Kernels from qibolab.platform import Platform, unroll_sequences from qibolab.pulses import PulseSequence, Rectangular -from qibolab.serialize import dump_runcard, load_runcard +from qibolab.serialize import ( + dump_kernels, + dump_platform, + dump_runcard, + load_runcard, + load_settings, +) from .conftest import find_instrument @@ -81,6 +90,46 @@ def test_dump_runcard(platform, tmp_path): assert final_instruments == target_instruments +@pytest.mark.parametrize("has_kernels", [False, True]) +def test_kernels(tmp_path, has_kernels): + """Test dumping and loading of `Kernels`.""" + + platform = create_dummy() + if has_kernels: + for qubit in platform.qubits: + platform.qubits[qubit].kernel = np.random.rand(10) + + dump_kernels(platform, tmp_path) + + if has_kernels: + kernels = Kernels.load(tmp_path) + for qubit in platform.qubits: + np.testing.assert_array_equal(platform.qubits[qubit].kernel, kernels[qubit]) + else: + with pytest.raises(FileNotFoundError): + Kernels.load(tmp_path) + + +@pytest.mark.parametrize("has_kernels", [False, True]) +def test_dump_platform(tmp_path, has_kernels): + """Test platform dump and loading runcard and kernels.""" + + platform = create_dummy() + if has_kernels: + for qubit in platform.qubits: + platform.qubits[qubit].kernel = np.random.rand(10) + + dump_platform(platform, tmp_path) + + settings = load_settings(load_runcard(tmp_path)) + if has_kernels: + kernels = Kernels.load(tmp_path) + for qubit in platform.qubits: + np.testing.assert_array_equal(platform.qubits[qubit].kernel, kernels[qubit]) + + assert settings == platform.settings + + @pytest.fixture(scope="module") def qpu_platform(connected_platform): connected_platform.connect()