Skip to content

Commit

Permalink
refactor: Introduce folders for platform serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
andrea-pasquale committed Jan 24, 2024
1 parent 3416a8e commit 0351323
Show file tree
Hide file tree
Showing 16 changed files with 79 additions and 56 deletions.
11 changes: 6 additions & 5 deletions src/qibolab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand All @@ -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):
Expand Down
18 changes: 9 additions & 9 deletions src/qibolab/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -21,11 +25,6 @@ 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):
"""Create a dummy platform using the dummy instrument.
Expand All @@ -40,8 +39,9 @@ def create_dummy(with_couplers: bool = True):
twpa_pump.frequency = 1e9
twpa_pump.power = 10

runcard = load_dummy_runcard()
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)

Expand All @@ -65,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
Expand Down
File renamed without changes.
5 changes: 4 additions & 1 deletion src/qibolab/kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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()}
)
2 changes: 1 addition & 1 deletion src/qibolab/qubits.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down
49 changes: 31 additions & 18 deletions src/qibolab/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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]

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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),
Expand All @@ -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)
7 changes: 4 additions & 3 deletions tests/dummy_qrc/qblox.py → tests/dummy_qrc/qblox/qblox.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
File renamed without changes.
7 changes: 4 additions & 3 deletions tests/dummy_qrc/qm.py → tests/dummy_qrc/qm/qm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
File renamed without changes.
7 changes: 4 additions & 3 deletions tests/dummy_qrc/rfsoc.py → tests/dummy_qrc/rfsoc/rfsoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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``.
Expand All @@ -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
Expand Down
File renamed without changes.
16 changes: 9 additions & 7 deletions tests/dummy_qrc/zurich.py → tests/dummy_qrc/zurich/zurich.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,24 @@
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,
load_runcard,
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")
Expand Down Expand Up @@ -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)
Expand Down
File renamed without changes.
1 change: 0 additions & 1 deletion tests/test_dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 7 additions & 5 deletions tests/test_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 0351323

Please sign in to comment.