From b02acc10a082d9d66820b25f7c80b3ab74468a5c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 13 Nov 2024 19:01:43 +0100 Subject: [PATCH 01/34] feat: Add 0.2 parameters' platform upgrader Still in progress --- convert.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 convert.py diff --git a/convert.py b/convert.py new file mode 100644 index 00000000..4f806615 --- /dev/null +++ b/convert.py @@ -0,0 +1,83 @@ +import argparse +import json +from pathlib import Path + + +def extract_configs() -> dict: + return {} + + +def channel(qubit: str, gate: str): + kind = ( + "drive" if gate.startswith("RX") else "acquisition" if gate == "MZ" else "flux" + ) + return f"{qubit}/{kind}" + + +def pulse(o: dict) -> dict: + return {} + + +def acquisition(o: dict) -> dict: + duration = o["duration"] + return { + "kind": "readout", + "acquisition": {"kind": "acquisition", "duration": duration}, + "probe": { + "kind": "pulse", + "duration": duration, + "amplitude": o["amplitude"], + "envelope": {}, + "relative_phase": o["phase"], + }, + } + + +def pulse_like(o: dict) -> dict: + return acquisition(o) if o["type"] == "ro" else pulse(o) + + +def single_pulse(o: dict) -> dict: + return { + id: {gid: [(channel(id, gid), pulse_like(gate))] for gid, gate in gates.items()} + for id, gates in o.items() + } + + +def extract_natives(o: dict) -> dict: + return { + "single_qubit": single_pulse(o["single_qubit"]), + "coupler": single_pulse(o["coupler"]), + "two_qubit": {}, + } + + +def upgrade(o: dict) -> dict: + return { + "settings": o["settings"], + "configs": extract_configs(), + "native_gates": extract_natives(o["native_gates"]), + } + + +def convert(path: Path): + params = json.loads(path.read_text()) + new = upgrade(params) + path.with_stem(path.stem + "-new").write_text(json.dumps(new, indent=4)) + + +def parse(): + parser = argparse.ArgumentParser() + parser.add_argument("path", nargs="*", type=Path) + return parser.parse_args() + + +def main(): + args = parse() + + for p in args.path: + convert(p) + + +if __name__ == "__main__": + main() From 2c3303df6b6a2f575c1bf1c6c6af3c2f083090e9 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 13 Nov 2024 19:12:53 +0100 Subject: [PATCH 02/34] feat: Parse pulses But envelopes... --- convert.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/convert.py b/convert.py index 4f806615..087fe34c 100644 --- a/convert.py +++ b/convert.py @@ -3,7 +3,7 @@ from pathlib import Path -def extract_configs() -> dict: +def configs() -> dict: return {} @@ -14,22 +14,25 @@ def channel(qubit: str, gate: str): return f"{qubit}/{kind}" -def pulse(o: dict) -> dict: +def envelope(o: str) -> dict: return {} +def pulse(o: dict) -> dict: + return { + "kind": "pulse", + "duration": o["duration"], + "amplitude": o["amplitude"], + "envelope": envelope(o["shape"]), + "relative_phase": o.get("phase"), + } + + def acquisition(o: dict) -> dict: - duration = o["duration"] return { "kind": "readout", - "acquisition": {"kind": "acquisition", "duration": duration}, - "probe": { - "kind": "pulse", - "duration": duration, - "amplitude": o["amplitude"], - "envelope": {}, - "relative_phase": o["phase"], - }, + "acquisition": {"kind": "acquisition", "duration": o["duration"]}, + "probe": pulse(o), } @@ -44,7 +47,7 @@ def single_pulse(o: dict) -> dict: } -def extract_natives(o: dict) -> dict: +def natives(o: dict) -> dict: return { "single_qubit": single_pulse(o["single_qubit"]), "coupler": single_pulse(o["coupler"]), @@ -55,8 +58,8 @@ def extract_natives(o: dict) -> dict: def upgrade(o: dict) -> dict: return { "settings": o["settings"], - "configs": extract_configs(), - "native_gates": extract_natives(o["native_gates"]), + "configs": configs(), + "native_gates": natives(o["native_gates"]), } From 278db6c36a9f12b34429abe09e645ca4cbfc1c2b Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 13 Nov 2024 20:39:55 +0100 Subject: [PATCH 03/34] feat: Start parsing shapes --- convert.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/convert.py b/convert.py index 087fe34c..ca38c48b 100644 --- a/convert.py +++ b/convert.py @@ -1,5 +1,6 @@ import argparse import json +import re from pathlib import Path @@ -14,8 +15,25 @@ def channel(qubit: str, gate: str): return f"{qubit}/{kind}" +SHAPES = {"rectangular": {}, "gaussian": {"rel_sigma": lambda s: 1 / float(s)}} +SHAPE = re.compile(r"(?P[a-zA-Z_]\w*)\((?P.*)\)") +ARG = re.compile(r"(?:(?P\w+)=)?(?P.*)") + + def envelope(o: str) -> dict: - return {} + shape = SHAPE.match(o) + assert shape is not None + kind = shape["kind"].lower() + kwargs = {} + for param, spec in zip(shape["args"].split(","), SHAPES[kind].items()): + arg = ARG.match(param) + assert arg is not None + try: + name = arg["name"] + except KeyError: + name = spec[0] + kwargs[name] = spec[1](arg["value"]) + return {"kind": kind, **kwargs} def pulse(o: dict) -> dict: From 1faaf19ced97473445a89ef20a7d4813777421b8 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 15:19:30 +0100 Subject: [PATCH 04/34] fix: Replace regex custom parser with Python one --- convert.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/convert.py b/convert.py index ca38c48b..b1056431 100644 --- a/convert.py +++ b/convert.py @@ -1,8 +1,10 @@ import argparse +import ast import json -import re from pathlib import Path +import numpy as np + def configs() -> dict: return {} @@ -15,24 +17,28 @@ def channel(qubit: str, gate: str): return f"{qubit}/{kind}" -SHAPES = {"rectangular": {}, "gaussian": {"rel_sigma": lambda s: 1 / float(s)}} -SHAPE = re.compile(r"(?P[a-zA-Z_]\w*)\((?P.*)\)") -ARG = re.compile(r"(?:(?P\w+)=)?(?P.*)") +SHAPES = { + "rectangular": {}, + "gaussian": {"rel_sigma": lambda s: 1 / s}, + "custom": {"i_": lambda s: np.array(s)}, +} def envelope(o: str) -> dict: - shape = SHAPE.match(o) - assert shape is not None - kind = shape["kind"].lower() + expr = ast.parse(o).body[0] + assert isinstance(expr, ast.Expr) + call = expr.value + assert isinstance(call, ast.Call) + assert isinstance(call.func, ast.Name) + kind = call.func.id.lower() kwargs = {} - for param, spec in zip(shape["args"].split(","), SHAPES[kind].items()): - arg = ARG.match(param) - assert arg is not None - try: - name = arg["name"] - except KeyError: - name = spec[0] - kwargs[name] = spec[1](arg["value"]) + shape = SHAPES[kind] + for arg, spec in zip(call.args, shape.items()): + assert isinstance(arg, ast.Constant) + kwargs[spec[0]] = spec[1](arg.value) + for arg in call.keywords: + assert isinstance(arg.value, ast.Constant) + kwargs[arg.arg] = arg.value.value return {"kind": kind, **kwargs} From c26a854086d9de58a31b3ecbf81b08d0f1856e0e Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 15:20:09 +0100 Subject: [PATCH 05/34] fix: Make couplers' section optional --- convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/convert.py b/convert.py index b1056431..bf18265e 100644 --- a/convert.py +++ b/convert.py @@ -74,7 +74,7 @@ def single_pulse(o: dict) -> dict: def natives(o: dict) -> dict: return { "single_qubit": single_pulse(o["single_qubit"]), - "coupler": single_pulse(o["coupler"]), + "coupler": single_pulse(o["coupler"]) if "coupler" in o else {}, "two_qubit": {}, } From 599d2357e81b6c87f6110b4731e06ea6ef4dff75 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 15:33:54 +0100 Subject: [PATCH 06/34] feat: Add support for drag envelope --- convert.py | 1 + 1 file changed, 1 insertion(+) diff --git a/convert.py b/convert.py index bf18265e..473836de 100644 --- a/convert.py +++ b/convert.py @@ -20,6 +20,7 @@ def channel(qubit: str, gate: str): SHAPES = { "rectangular": {}, "gaussian": {"rel_sigma": lambda s: 1 / s}, + "drag": {"rel_sigma": lambda s: 1 / s, "beta": lambda s: s}, "custom": {"i_": lambda s: np.array(s)}, } From 391e3238fa5ea63f65309ae0850038f93a19f621 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 16:55:53 +0100 Subject: [PATCH 07/34] feat: Introduce two qubit gates conversion --- convert.py | 62 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/convert.py b/convert.py index 473836de..f58f7e51 100644 --- a/convert.py +++ b/convert.py @@ -3,17 +3,17 @@ import json from pathlib import Path -import numpy as np + +NONSERIAL = lambda: None +"""Raise an error if survives in the final object to be serialized.""" def configs() -> dict: return {} -def channel(qubit: str, gate: str): - kind = ( - "drive" if gate.startswith("RX") else "acquisition" if gate == "MZ" else "flux" - ) +def channel(qubit: str, type_: str) -> str: + kind = "flux" if type_ == "qf" else "acquisition" if type_ == "ro" else "drive" return f"{qubit}/{kind}" @@ -21,7 +21,7 @@ def channel(qubit: str, gate: str): "rectangular": {}, "gaussian": {"rel_sigma": lambda s: 1 / s}, "drag": {"rel_sigma": lambda s: 1 / s, "beta": lambda s: s}, - "custom": {"i_": lambda s: np.array(s)}, + "custom": {"i_": lambda s: s}, } @@ -35,11 +35,10 @@ def envelope(o: str) -> dict: kwargs = {} shape = SHAPES[kind] for arg, spec in zip(call.args, shape.items()): - assert isinstance(arg, ast.Constant) - kwargs[spec[0]] = spec[1](arg.value) + kwargs[spec[0]] = spec[1](ast.literal_eval(arg)) for arg in call.keywords: assert isinstance(arg.value, ast.Constant) - kwargs[arg.arg] = arg.value.value + kwargs[arg.arg] = ast.literal_eval(arg.value) return {"kind": kind, **kwargs} @@ -61,13 +60,52 @@ def acquisition(o: dict) -> dict: } +def virtualz(o: dict) -> dict: + return {} + + def pulse_like(o: dict) -> dict: - return acquisition(o) if o["type"] == "ro" else pulse(o) + return ( + acquisition(o) + if o["type"] == "ro" + else virtualz(o) + if o["type"] == "virtual_z" + else pulse(o) + ) + + +def try_(f): + try: + return f() + except Exception: + breakpoint() def single_pulse(o: dict) -> dict: return { - id: {gid: [(channel(id, gid), pulse_like(gate))] for gid, gate in gates.items()} + id: { + gid: [(channel(id, gate["type"]), pulse_like(gate))] + for gid, gate in gates.items() + } + for id, gates in o.items() + } + + +def two_qubit(o: dict) -> dict: + return { + id: { + gid: [ + ( + channel( + pulse.get("qubit", pulse.get("coupler", NONSERIAL)), + pulse["type"], + ), + pulse_like(pulse), + ) + for pulse in gate + ] + for gid, gate in gates.items() + } for id, gates in o.items() } @@ -76,7 +114,7 @@ def natives(o: dict) -> dict: return { "single_qubit": single_pulse(o["single_qubit"]), "coupler": single_pulse(o["coupler"]) if "coupler" in o else {}, - "two_qubit": {}, + "two_qubit": two_qubit(o["two_qubit"]), } From e4afe97bbb5ca2d03cd0741c4f2fd4e5dbf1625f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 16:58:16 +0100 Subject: [PATCH 08/34] fix: Support virtual z events --- convert.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/convert.py b/convert.py index f58f7e51..f7998042 100644 --- a/convert.py +++ b/convert.py @@ -61,7 +61,7 @@ def acquisition(o: dict) -> dict: def virtualz(o: dict) -> dict: - return {} + return {"kind": "virtualz", "phase": o["phase"]} def pulse_like(o: dict) -> dict: @@ -74,13 +74,6 @@ def pulse_like(o: dict) -> dict: ) -def try_(f): - try: - return f() - except Exception: - breakpoint() - - def single_pulse(o: dict) -> dict: return { id: { From 11eea24cf18f2165fbca392e179cd95dd759a748 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 17:13:32 +0100 Subject: [PATCH 09/34] fix: Use qibolab 0.2 array serialization --- convert.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/convert.py b/convert.py index f7998042..c7cdbd3e 100644 --- a/convert.py +++ b/convert.py @@ -3,6 +3,8 @@ import json from pathlib import Path +from pydantic import TypeAdapter +from qibolab._core.serialize import NdArray NONSERIAL = lambda: None """Raise an error if survives in the final object to be serialized.""" @@ -21,7 +23,7 @@ def channel(qubit: str, type_: str) -> str: "rectangular": {}, "gaussian": {"rel_sigma": lambda s: 1 / s}, "drag": {"rel_sigma": lambda s: 1 / s, "beta": lambda s: s}, - "custom": {"i_": lambda s: s}, + "custom": {"i_": lambda s: TypeAdapter(NdArray).dump_json(s).decode()}, } From a0f1f86cbd97671d5055d27e9683090b890c1835 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 17:14:06 +0100 Subject: [PATCH 10/34] fix: Handle couplers with unique ids --- convert.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/convert.py b/convert.py index c7cdbd3e..ca91b023 100644 --- a/convert.py +++ b/convert.py @@ -15,8 +15,15 @@ def configs() -> dict: def channel(qubit: str, type_: str) -> str: - kind = "flux" if type_ == "qf" else "acquisition" if type_ == "ro" else "drive" - return f"{qubit}/{kind}" + kind = ( + "flux" + if type_ == "qf" or type_ == "coupler" + else "acquisition" + if type_ == "ro" + else "drive" + ) + element = qubit if type_ != "coupler" else f"coupler_{qubit}" + return f"{element}/{kind}" SHAPES = { @@ -108,7 +115,9 @@ def two_qubit(o: dict) -> dict: def natives(o: dict) -> dict: return { "single_qubit": single_pulse(o["single_qubit"]), - "coupler": single_pulse(o["coupler"]) if "coupler" in o else {}, + "coupler": { + f"coupler_{k}": v for k, v in single_pulse(o.get("coupler", {})).items() + }, "two_qubit": two_qubit(o["two_qubit"]), } From abcdf957c287a93600cd3da53a4e987a920acaae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:14:25 +0000 Subject: [PATCH 11/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- convert.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/convert.py b/convert.py index ca91b023..8578ee68 100644 --- a/convert.py +++ b/convert.py @@ -18,9 +18,7 @@ def channel(qubit: str, type_: str) -> str: kind = ( "flux" if type_ == "qf" or type_ == "coupler" - else "acquisition" - if type_ == "ro" - else "drive" + else "acquisition" if type_ == "ro" else "drive" ) element = qubit if type_ != "coupler" else f"coupler_{qubit}" return f"{element}/{kind}" @@ -77,9 +75,7 @@ def pulse_like(o: dict) -> dict: return ( acquisition(o) if o["type"] == "ro" - else virtualz(o) - if o["type"] == "virtual_z" - else pulse(o) + else virtualz(o) if o["type"] == "virtual_z" else pulse(o) ) From 7f0bfb43d36bfd6d65bdb09da2d5873e85d333f7 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 18:08:43 +0100 Subject: [PATCH 12/34] feat: Introduce configs extraction --- convert.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/convert.py b/convert.py index 8578ee68..a15f3b81 100644 --- a/convert.py +++ b/convert.py @@ -10,15 +10,58 @@ """Raise an error if survives in the final object to be serialized.""" -def configs() -> dict: +def channel_config(o: dict) -> dict: return {} +def configs( + instruments: dict, single: dict, couplers: dict, characterization: dict +) -> dict: + return ( + { + f"{k}/bounds": (v["bounds"] | {"kind": "bounds"}) + for k, v in instruments.items() + if "bounds" in v + } + | { + k: (v | {"kind": "oscillator"}) + for k, v in instruments.items() + if "twpa" in k + } + | { + channel(id, pulse["type"]): channel_config(pulse) + for id, gates in single.items() + for gate, pulse in gates.items() + } + | { + channel(id, "ro"): { + "kind": "acquisition", + "delay": 0.0, + "smearing": 0.0, + "threshold": char["threshold"], + "iq_angle": char["iq_angle"], + "kernel": None, + } + for id, char in characterization["single_qubit"].items() + } + | { + channel(id, "qf"): {"kind": "dc", "offset": char["sweetspot"]} + for id, char in characterization["single_qubit"].items() + } + | { + channel(id, "coupler"): {"kind": "dc", "offset": char["sweetspot"]} + for id, char in characterization.get("coupler", {}).items() + } + ) + + def channel(qubit: str, type_: str) -> str: kind = ( "flux" if type_ == "qf" or type_ == "coupler" - else "acquisition" if type_ == "ro" else "drive" + else "acquisition" + if type_ == "ro" + else "drive" ) element = qubit if type_ != "coupler" else f"coupler_{qubit}" return f"{element}/{kind}" @@ -75,7 +118,9 @@ def pulse_like(o: dict) -> dict: return ( acquisition(o) if o["type"] == "ro" - else virtualz(o) if o["type"] == "virtual_z" else pulse(o) + else virtualz(o) + if o["type"] == "virtual_z" + else pulse(o) ) @@ -121,7 +166,12 @@ def natives(o: dict) -> dict: def upgrade(o: dict) -> dict: return { "settings": o["settings"], - "configs": configs(), + "configs": configs( + o["instruments"], + o["native_gates"]["single_qubit"], + o["native_gates"].get("coupler", {}), + o["characterization"], + ), "native_gates": natives(o["native_gates"]), } From c5686c660d90bff43127e09cc155d963d5750bf4 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 18:14:01 +0100 Subject: [PATCH 13/34] fix: Split drive12 and probe channels from drive and acquisition --- convert.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/convert.py b/convert.py index a15f3b81..7db0b829 100644 --- a/convert.py +++ b/convert.py @@ -2,6 +2,7 @@ import ast import json from pathlib import Path +from typing import Optional from pydantic import TypeAdapter from qibolab._core.serialize import NdArray @@ -29,7 +30,7 @@ def configs( if "twpa" in k } | { - channel(id, pulse["type"]): channel_config(pulse) + channel(id, pulse["type"], gate=gate): channel_config(pulse) for id, gates in single.items() for gate, pulse in gates.items() } @@ -55,12 +56,16 @@ def configs( ) -def channel(qubit: str, type_: str) -> str: +def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: kind = ( "flux" if type_ == "qf" or type_ == "coupler" + else "probe" + if gate == "MZ" else "acquisition" if type_ == "ro" + else "drive12" + if gate == "RX12" else "drive" ) element = qubit if type_ != "coupler" else f"coupler_{qubit}" From ec0f0147afc57d2a798fbc96372d07543c31d1d9 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Nov 2024 18:19:19 +0100 Subject: [PATCH 14/34] feat: Complete IQ channels fill --- convert.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/convert.py b/convert.py index 7db0b829..3bf51cae 100644 --- a/convert.py +++ b/convert.py @@ -11,8 +11,8 @@ """Raise an error if survives in the final object to be serialized.""" -def channel_config(o: dict) -> dict: - return {} +def channel_from_pulse(pulse: dict) -> dict: + return {"kind": "iq", "frequency": pulse["frequency"]} def configs( @@ -30,7 +30,7 @@ def configs( if "twpa" in k } | { - channel(id, pulse["type"], gate=gate): channel_config(pulse) + channel(id, pulse["type"], gate=gate): channel_from_pulse(pulse) for id, gates in single.items() for gate, pulse in gates.items() } From ac6258cb135af5a83df39ff0a0a06192d2fd1e9a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:23:09 +0000 Subject: [PATCH 15/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- convert.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/convert.py b/convert.py index 3bf51cae..5aaac7a5 100644 --- a/convert.py +++ b/convert.py @@ -60,13 +60,15 @@ def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: kind = ( "flux" if type_ == "qf" or type_ == "coupler" - else "probe" - if gate == "MZ" - else "acquisition" - if type_ == "ro" - else "drive12" - if gate == "RX12" - else "drive" + else ( + "probe" + if gate == "MZ" + else ( + "acquisition" + if type_ == "ro" + else "drive12" if gate == "RX12" else "drive" + ) + ) ) element = qubit if type_ != "coupler" else f"coupler_{qubit}" return f"{element}/{kind}" @@ -123,9 +125,7 @@ def pulse_like(o: dict) -> dict: return ( acquisition(o) if o["type"] == "ro" - else virtualz(o) - if o["type"] == "virtual_z" - else pulse(o) + else virtualz(o) if o["type"] == "virtual_z" else pulse(o) ) From dea80f569bb6f1e97a436a0cd23f0e0b5c40bfce Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Fri, 15 Nov 2024 16:36:22 +0100 Subject: [PATCH 16/34] feat: script to generate calibration.json --- generate_calibration.py | 64 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 generate_calibration.py diff --git a/generate_calibration.py b/generate_calibration.py new file mode 100644 index 00000000..5fe56f00 --- /dev/null +++ b/generate_calibration.py @@ -0,0 +1,64 @@ +import argparse +import json +from pathlib import Path + + + +def single_qubits(o: dict) -> dict: + return { q: { + "resonator": { + "bare_frequency": k["bare_resonator_frequency"], + "dressed_frequency": k["readout_frequency"], + }, + "qubit": { + "frequency_01": k["drive_frequency"], + "sweetspot": k["sweetspot"], + }, + "readout":{ + "fidelity": k["readout_fidelity"], + "ground_state": k["mean_gnd_states"], + "excited_state": k["mean_exc_states"], + }, + "t1": [k["T1"], None], + "t2": [k["T2"], None], + "t2_spin_echo": [k["T2_spin_echo"], None], + "rb_fidelity": [k["gate_fidelity"], None], + } for q, k in o.items()} + + +def two_qubits(o:dict) -> dict: + return {qq :{ + "rb_fidelity": [k["gate_fidelity"], None], + "cz_fidelity": [k["cz_fidelity"], None], + } for qq, k in o.items()} + + +def upgrade(o: dict) -> dict: + return { + "single_qubits": single_qubits(o["characterization"]["single_qubit"] + ), + "two_qubits": two_qubits(o["characterization"]["two_qubit"]), + } + + +def convert(path: Path): + params = json.loads(path.read_text()) + new = upgrade(params) + path.with_stem("calibration_new").write_text(json.dumps(new, indent=4)) + + +def parse(): + parser = argparse.ArgumentParser() + parser.add_argument("path", nargs="*", type=Path) + return parser.parse_args() + + +def main(): + args = parse() + + for p in args.path: + convert(p) + + +if __name__ == "__main__": + main() From dbf695489ebf9681eb4cfd24d1809625f3794ecf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:40:52 +0000 Subject: [PATCH 17/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- generate_calibration.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/generate_calibration.py b/generate_calibration.py index 5fe56f00..77aa04d3 100644 --- a/generate_calibration.py +++ b/generate_calibration.py @@ -3,18 +3,18 @@ from pathlib import Path - def single_qubits(o: dict) -> dict: - return { q: { + return { + q: { "resonator": { "bare_frequency": k["bare_resonator_frequency"], "dressed_frequency": k["readout_frequency"], - }, + }, "qubit": { "frequency_01": k["drive_frequency"], "sweetspot": k["sweetspot"], }, - "readout":{ + "readout": { "fidelity": k["readout_fidelity"], "ground_state": k["mean_gnd_states"], "excited_state": k["mean_exc_states"], @@ -23,20 +23,24 @@ def single_qubits(o: dict) -> dict: "t2": [k["T2"], None], "t2_spin_echo": [k["T2_spin_echo"], None], "rb_fidelity": [k["gate_fidelity"], None], - } for q, k in o.items()} + } + for q, k in o.items() + } -def two_qubits(o:dict) -> dict: - return {qq :{ - "rb_fidelity": [k["gate_fidelity"], None], - "cz_fidelity": [k["cz_fidelity"], None], - } for qq, k in o.items()} +def two_qubits(o: dict) -> dict: + return { + qq: { + "rb_fidelity": [k["gate_fidelity"], None], + "cz_fidelity": [k["cz_fidelity"], None], + } + for qq, k in o.items() + } def upgrade(o: dict) -> dict: return { - "single_qubits": single_qubits(o["characterization"]["single_qubit"] - ), + "single_qubits": single_qubits(o["characterization"]["single_qubit"]), "two_qubits": two_qubits(o["characterization"]["two_qubit"]), } From ecd2508ced5943827182284f68b2faa55ef9b1c7 Mon Sep 17 00:00:00 2001 From: Andrea Pasquale Date: Fri, 15 Nov 2024 17:57:15 +0100 Subject: [PATCH 18/34] chore: Unify scripts --- convert.py | 41 +++++++++++++++++++++++-- generate_calibration.py | 68 ----------------------------------------- 2 files changed, 39 insertions(+), 70 deletions(-) delete mode 100644 generate_calibration.py diff --git a/convert.py b/convert.py index 5aaac7a5..1165f01a 100644 --- a/convert.py +++ b/convert.py @@ -1,9 +1,10 @@ +"""Converts parameters.json to parameters.json and calibration.json +""" import argparse import ast import json from pathlib import Path from typing import Optional - from pydantic import TypeAdapter from qibolab._core.serialize import NdArray @@ -180,11 +181,48 @@ def upgrade(o: dict) -> dict: "native_gates": natives(o["native_gates"]), } +def single_qubits_cal(o: dict) -> dict: + return { q: { + "resonator": { + "bare_frequency": k["bare_resonator_frequency"], + "dressed_frequency": k["readout_frequency"], + }, + "qubit": { + "frequency_01": k["drive_frequency"], + "sweetspot": k["sweetspot"], + }, + "readout":{ + "fidelity": k["readout_fidelity"], + "ground_state": k["mean_gnd_states"], + "excited_state": k["mean_exc_states"], + }, + "t1": [k["T1"], None], + "t2": [k["T2"], None], + "t2_spin_echo": [k["T2_spin_echo"], None], + "rb_fidelity": [k["gate_fidelity"], None], + } for q, k in o.items()} + + +def two_qubits_cal(o:dict) -> dict: + return {qq :{ + "rb_fidelity": [k["gate_fidelity"], None], + "cz_fidelity": [k["cz_fidelity"], None], + } for qq, k in o.items()} + + +def upgrade_cal(o: dict) -> dict: + return { + "single_qubits": single_qubits_cal(o["characterization"]["single_qubit"] + ), + "two_qubits": two_qubits_cal(o["characterization"]["two_qubit"]), + } def convert(path: Path): params = json.loads(path.read_text()) new = upgrade(params) + cal = upgrade_cal(params) path.with_stem(path.stem + "-new").write_text(json.dumps(new, indent=4)) + path.with_stem("calibration").write_text(json.dumps(new, indent=4)) def parse(): @@ -195,7 +233,6 @@ def parse(): def main(): args = parse() - for p in args.path: convert(p) diff --git a/generate_calibration.py b/generate_calibration.py deleted file mode 100644 index 77aa04d3..00000000 --- a/generate_calibration.py +++ /dev/null @@ -1,68 +0,0 @@ -import argparse -import json -from pathlib import Path - - -def single_qubits(o: dict) -> dict: - return { - q: { - "resonator": { - "bare_frequency": k["bare_resonator_frequency"], - "dressed_frequency": k["readout_frequency"], - }, - "qubit": { - "frequency_01": k["drive_frequency"], - "sweetspot": k["sweetspot"], - }, - "readout": { - "fidelity": k["readout_fidelity"], - "ground_state": k["mean_gnd_states"], - "excited_state": k["mean_exc_states"], - }, - "t1": [k["T1"], None], - "t2": [k["T2"], None], - "t2_spin_echo": [k["T2_spin_echo"], None], - "rb_fidelity": [k["gate_fidelity"], None], - } - for q, k in o.items() - } - - -def two_qubits(o: dict) -> dict: - return { - qq: { - "rb_fidelity": [k["gate_fidelity"], None], - "cz_fidelity": [k["cz_fidelity"], None], - } - for qq, k in o.items() - } - - -def upgrade(o: dict) -> dict: - return { - "single_qubits": single_qubits(o["characterization"]["single_qubit"]), - "two_qubits": two_qubits(o["characterization"]["two_qubit"]), - } - - -def convert(path: Path): - params = json.loads(path.read_text()) - new = upgrade(params) - path.with_stem("calibration_new").write_text(json.dumps(new, indent=4)) - - -def parse(): - parser = argparse.ArgumentParser() - parser.add_argument("path", nargs="*", type=Path) - return parser.parse_args() - - -def main(): - args = parse() - - for p in args.path: - convert(p) - - -if __name__ == "__main__": - main() From b78e49816793c04896462e48fd0271dea88d7b56 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:58:31 +0000 Subject: [PATCH 19/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- convert.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/convert.py b/convert.py index 1165f01a..23f31d09 100644 --- a/convert.py +++ b/convert.py @@ -1,10 +1,12 @@ """Converts parameters.json to parameters.json and calibration.json """ + import argparse import ast import json from pathlib import Path from typing import Optional + from pydantic import TypeAdapter from qibolab._core.serialize import NdArray @@ -181,17 +183,19 @@ def upgrade(o: dict) -> dict: "native_gates": natives(o["native_gates"]), } + def single_qubits_cal(o: dict) -> dict: - return { q: { + return { + q: { "resonator": { "bare_frequency": k["bare_resonator_frequency"], "dressed_frequency": k["readout_frequency"], - }, + }, "qubit": { "frequency_01": k["drive_frequency"], "sweetspot": k["sweetspot"], }, - "readout":{ + "readout": { "fidelity": k["readout_fidelity"], "ground_state": k["mean_gnd_states"], "excited_state": k["mean_exc_states"], @@ -200,23 +204,28 @@ def single_qubits_cal(o: dict) -> dict: "t2": [k["T2"], None], "t2_spin_echo": [k["T2_spin_echo"], None], "rb_fidelity": [k["gate_fidelity"], None], - } for q, k in o.items()} + } + for q, k in o.items() + } -def two_qubits_cal(o:dict) -> dict: - return {qq :{ - "rb_fidelity": [k["gate_fidelity"], None], - "cz_fidelity": [k["cz_fidelity"], None], - } for qq, k in o.items()} +def two_qubits_cal(o: dict) -> dict: + return { + qq: { + "rb_fidelity": [k["gate_fidelity"], None], + "cz_fidelity": [k["cz_fidelity"], None], + } + for qq, k in o.items() + } def upgrade_cal(o: dict) -> dict: return { - "single_qubits": single_qubits_cal(o["characterization"]["single_qubit"] - ), + "single_qubits": single_qubits_cal(o["characterization"]["single_qubit"]), "two_qubits": two_qubits_cal(o["characterization"]["two_qubit"]), } + def convert(path: Path): params = json.loads(path.read_text()) new = upgrade(params) From d24d35b67ccd476895d9de7f51eaf4726ec80e17 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:16:25 +0400 Subject: [PATCH 20/34] fix: dump calibration file instead of dumping parameters-new twice --- convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/convert.py b/convert.py index 23f31d09..3bf7d9d1 100644 --- a/convert.py +++ b/convert.py @@ -231,7 +231,7 @@ def convert(path: Path): new = upgrade(params) cal = upgrade_cal(params) path.with_stem(path.stem + "-new").write_text(json.dumps(new, indent=4)) - path.with_stem("calibration").write_text(json.dumps(new, indent=4)) + path.with_stem("calibration").write_text(json.dumps(cal, indent=4)) def parse(): From 295b016e7f9e16fce62ae2070fbf473d89862bbc Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:58:15 +0400 Subject: [PATCH 21/34] feat: update convert.py script to generate QM specific configs --- convert.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/convert.py b/convert.py index 3bf7d9d1..b3b9563b 100644 --- a/convert.py +++ b/convert.py @@ -4,6 +4,7 @@ import argparse import ast import json +from dataclasses import dataclass from pathlib import Path from typing import Optional @@ -18,10 +19,51 @@ def channel_from_pulse(pulse: dict) -> dict: return {"kind": "iq", "frequency": pulse["frequency"]} +@dataclass +class QmConnection: + instrument: str + port: str + output_mode: str = "triggered" + + +def qm_configs(conf: dict, instruments: dict, instrument_channels: dict) -> dict: + for channel, conn in instrument_channels.items(): + connection = QmConnection(**conn) + settings = instruments.get(connection.instrument, {}).get(connection.port, {}) + if channel in conf: + kind = conf[channel]["kind"] + if kind == "acquisition": + conf[channel].update( + {"kind": "qm-acquisition", "gain": settings.get("gain", 0)} + ) + elif kind == "dc": + conf[channel].update( + { + "kind": "opx-output", + "filter": settings.get("filter", {}), + "output_mode": settings.get("output_mode", "direct"), + } + ) + else: + raise NotImplementedError + else: + conf[channel] = { + "kind": "octave-oscillator", + "frequency": settings["lo_frequency"], + "power": settings["gain"], + "output_mode": connection.output_mode, + } + return conf + + def configs( - instruments: dict, single: dict, couplers: dict, characterization: dict + instruments: dict, + single: dict, + couplers: dict, + characterization: dict, + connections: Optional[dict] = None, ) -> dict: - return ( + conf = ( { f"{k}/bounds": (v["bounds"] | {"kind": "bounds"}) for k, v in instruments.items() @@ -57,6 +99,12 @@ def configs( for id, char in characterization.get("coupler", {}).items() } ) + if connections is not None: + if connections["kind"] == "qm": + conf = qm_configs(conf, instruments, connections["channels"]) + else: + raise NotImplementedError + return conf def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: @@ -171,7 +219,7 @@ def natives(o: dict) -> dict: } -def upgrade(o: dict) -> dict: +def upgrade(o: dict, connections: Optional[dict] = None) -> dict: return { "settings": o["settings"], "configs": configs( @@ -179,6 +227,7 @@ def upgrade(o: dict) -> dict: o["native_gates"]["single_qubit"], o["native_gates"].get("coupler", {}), o["characterization"], + connections, ), "native_gates": natives(o["native_gates"]), } @@ -226,9 +275,14 @@ def upgrade_cal(o: dict) -> dict: } -def convert(path: Path): +def convert(path: Path, connections_path: Optional[Path] = None): params = json.loads(path.read_text()) - new = upgrade(params) + connections = ( + json.loads(connections_path.read_text()) + if connections_path is not None + else None + ) + new = upgrade(params, connections) cal = upgrade_cal(params) path.with_stem(path.stem + "-new").write_text(json.dumps(new, indent=4)) path.with_stem("calibration").write_text(json.dumps(cal, indent=4)) @@ -237,13 +291,20 @@ def convert(path: Path): def parse(): parser = argparse.ArgumentParser() parser.add_argument("path", nargs="*", type=Path) + parser.add_argument("--connections", nargs="*", default=None, type=Path) return parser.parse_args() def main(): args = parse() - for p in args.path: - convert(p) + connections = args.connections + if connections is not None: + assert len(args.path) == len(connections) + else: + connections = len(args.path) * [None] + + for p, c in zip(args.path, connections): + convert(p, c) if __name__ == "__main__": From 6e3773c31533eca7b96a440574880f523a15f354 Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:58:42 +0400 Subject: [PATCH 22/34] chore: add connections files to avoid dependency on qibolab --- qw11q/connections.json | 189 +++++++++++++++++++++++++++++++++ qw5q_platinum/connections.json | 70 ++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 qw11q/connections.json create mode 100644 qw5q_platinum/connections.json diff --git a/qw11q/connections.json b/qw11q/connections.json new file mode 100644 index 00000000..8de2d21a --- /dev/null +++ b/qw11q/connections.json @@ -0,0 +1,189 @@ +{ + "kind": "qm", + "channels": { + "A1A4/drive_lo": { + "instrument": "octave4", + "port": "o2" + }, + "A2A3/drive_lo": { + "instrument": "octave4", + "port": "o4" + }, + "A5D5/drive_lo": { + "instrument": "octave6", + "port": "o2" + }, + "A6D4/drive_lo": { + "instrument": "octave6", + "port": "o4" + }, + "B1/drive_lo": { + "instrument": "octave2", + "port": "o2" + }, + "B2/drive_lo": { + "instrument": "octave2", + "port": "o4" + }, + "B3/drive_lo": { + "instrument": "octave3", + "port": "o1" + }, + "B4/drive_lo": { + "instrument": "octave3", + "port": "o4" + }, + "B5/drive_lo": { + "instrument": "octave3", + "port": "o3" + }, + "D1/drive_lo": { + "instrument": "octave5", + "port": "o2" + }, + "D2D3/drive_lo": { + "instrument": "octave5", + "port": "o4" + }, + "A/probe_lo": { + "instrument": "octave4", + "port": "o1" + }, + "B/probe_lo": { + "instrument": "octave2", + "port": "o1" + }, + "D/probe_lo": { + "instrument": "octave5", + "port": "o1" + }, + "A1/flux": { + "instrument": "con7", + "port": "o3" + }, + "A2/flux": { + "instrument": "con7", + "port": "o4" + }, + "A3/flux": { + "instrument": "con7", + "port": "o5" + }, + "A4/flux": { + "instrument": "con7", + "port": "o6" + }, + "A5/flux": { + "instrument": "con7", + "port": "o7" + }, + "A6/flux": { + "instrument": "con7", + "port": "o8" + }, + "B1/flux": { + "instrument": "con4", + "port": "o1" + }, + "B2/flux": { + "instrument": "con4", + "port": "o2" + }, + "B3/flux": { + "instrument": "con4", + "port": "o3" + }, + "B4/flux": { + "instrument": "con4", + "port": "o4" + }, + "B5/flux": { + "instrument": "con4", + "port": "o5" + }, + "D1/flux": { + "instrument": "con9", + "port": "o3" + }, + "D2/flux": { + "instrument": "con9", + "port": "o4" + }, + "D3/flux": { + "instrument": "con9", + "port": "o5" + }, + "D4/flux": { + "instrument": "con9", + "port": "o6" + }, + "D5/flux": { + "instrument": "con9", + "port": "o7" + }, + "A1/acquisition": { + "instrument": "con5", + "port": "i1" + }, + "A2/acquisition": { + "instrument": "con5", + "port": "i1" + }, + "A3/acquisition": { + "instrument": "con5", + "port": "i1" + }, + "A4/acquisition": { + "instrument": "con5", + "port": "i1" + }, + "A5/acquisition": { + "instrument": "con5", + "port": "i1" + }, + "A6/acquisition": { + "instrument": "con5", + "port": "i1" + }, + "B1/acquisition": { + "instrument": "con2", + "port": "i1" + }, + "B2/acquisition": { + "instrument": "con2", + "port": "i1" + }, + "B3/acquisition": { + "instrument": "con2", + "port": "i1" + }, + "B4/acquisition": { + "instrument": "con2", + "port": "i1" + }, + "B5/acquisition": { + "instrument": "con2", + "port": "i1" + }, + "D1/acquisition": { + "instrument": "con6", + "port": "i1" + }, + "D2/acquisition": { + "instrument": "con6", + "port": "i1" + }, + "D3/acquisition": { + "instrument": "con6", + "port": "i1" + }, + "D4/acquisition": { + "instrument": "con6", + "port": "i1" + }, + "D5/acquisition": { + "instrument": "con6", + "port": "i1" + } + } +} diff --git a/qw5q_platinum/connections.json b/qw5q_platinum/connections.json new file mode 100644 index 00000000..c7ada2c8 --- /dev/null +++ b/qw5q_platinum/connections.json @@ -0,0 +1,70 @@ +{ + "kind": "qm", + "channels": { + "01/drive_lo": { + "instrument": "octave1", + "port": "o2", + "output_mode": "always_on" + }, + "2/drive_lo": { + "instrument": "octave1", + "port": "o1", + "output_mode": "always_on" + }, + "3/drive_lo": { + "instrument": "octave1", + "port": "o4", + "output_mode": "always_on" + }, + "4/drive_lo": { + "instrument": "octave2", + "port": "o2", + "output_mode": "always_on" + }, + "probe_lo": { + "instrument": "octave2", + "port": "o1", + "output_mode": "always_on" + }, + "0/flux": { + "instrument": "con1", + "port": "4/o4" + }, + "1/flux": { + "instrument": "con1", + "port": "4/o1" + }, + "2/flux": { + "instrument": "con1", + "port": "4/o3" + }, + "3/flux": { + "instrument": "con1", + "port": "4/o2" + }, + "4/flux": { + "instrument": "con1", + "port": "4/o5" + }, + "0/acquisition": { + "instrument": "con1", + "port": "2/i1" + }, + "1/acquisition": { + "instrument": "con1", + "port": "2/i1" + }, + "2/acquisition": { + "instrument": "con1", + "port": "2/i1" + }, + "3/acquisition": { + "instrument": "con1", + "port": "2/i1" + }, + "4/acquisition": { + "instrument": "con1", + "port": "2/i1" + } + } +} From d74b944bd1c6778f57dd7f9fc4e37c04953df54b Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:31:20 +0400 Subject: [PATCH 23/34] fix: 0 instead of null relative_phase --- convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/convert.py b/convert.py index b3b9563b..9eb79734 100644 --- a/convert.py +++ b/convert.py @@ -156,7 +156,7 @@ def pulse(o: dict) -> dict: "duration": o["duration"], "amplitude": o["amplitude"], "envelope": envelope(o["shape"]), - "relative_phase": o.get("phase"), + "relative_phase": o.get("phase", 0.0), } From 5be90444a6cbf60d5702bdf06411d563299e9a0c Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:50:01 +0400 Subject: [PATCH 24/34] fix: time of flight for QM platforms --- convert.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/convert.py b/convert.py index 9eb79734..1cb2dc75 100644 --- a/convert.py +++ b/convert.py @@ -13,6 +13,8 @@ NONSERIAL = lambda: None """Raise an error if survives in the final object to be serialized.""" +QM_TIME_OF_FLIGHT = 224 +"""Default time of flight for QM platforms (in 0.1 this was hard-coded in platform.py).""" def channel_from_pulse(pulse: dict) -> dict: @@ -34,7 +36,11 @@ def qm_configs(conf: dict, instruments: dict, instrument_channels: dict) -> dict kind = conf[channel]["kind"] if kind == "acquisition": conf[channel].update( - {"kind": "qm-acquisition", "gain": settings.get("gain", 0)} + { + "kind": "qm-acquisition", + "delay": QM_TIME_OF_FLIGHT, + "gain": settings.get("gain", 0), + } ) elif kind == "dc": conf[channel].update( From ebb9121212ac351e30f6b8ebc868ebbadde64e5c Mon Sep 17 00:00:00 2001 From: Stavros Efthymiou <35475381+stavros11@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:40:03 +0400 Subject: [PATCH 25/34] fix: multiply amplitudes by 2 when QM is used --- convert.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/convert.py b/convert.py index 1cb2dc75..3221ac42 100644 --- a/convert.py +++ b/convert.py @@ -156,21 +156,21 @@ def envelope(o: str) -> dict: return {"kind": kind, **kwargs} -def pulse(o: dict) -> dict: +def pulse(o: dict, rescale: float) -> dict: return { "kind": "pulse", "duration": o["duration"], - "amplitude": o["amplitude"], + "amplitude": rescale * o["amplitude"], "envelope": envelope(o["shape"]), "relative_phase": o.get("phase", 0.0), } -def acquisition(o: dict) -> dict: +def acquisition(o: dict, rescale: float) -> dict: return { "kind": "readout", "acquisition": {"kind": "acquisition", "duration": o["duration"]}, - "probe": pulse(o), + "probe": pulse(o, rescale), } @@ -178,25 +178,25 @@ def virtualz(o: dict) -> dict: return {"kind": "virtualz", "phase": o["phase"]} -def pulse_like(o: dict) -> dict: +def pulse_like(o: dict, rescale: float) -> dict: return ( - acquisition(o) + acquisition(o, rescale) if o["type"] == "ro" - else virtualz(o) if o["type"] == "virtual_z" else pulse(o) + else virtualz(o) if o["type"] == "virtual_z" else pulse(o, rescale) ) -def single_pulse(o: dict) -> dict: +def single_pulse(o: dict, rescale: float) -> dict: return { id: { - gid: [(channel(id, gate["type"]), pulse_like(gate))] + gid: [(channel(id, gate["type"]), pulse_like(gate, rescale))] for gid, gate in gates.items() } for id, gates in o.items() } -def two_qubit(o: dict) -> dict: +def two_qubit(o: dict, rescale: float) -> dict: return { id: { gid: [ @@ -205,7 +205,7 @@ def two_qubit(o: dict) -> dict: pulse.get("qubit", pulse.get("coupler", NONSERIAL)), pulse["type"], ), - pulse_like(pulse), + pulse_like(pulse, rescale), ) for pulse in gate ] @@ -215,17 +215,19 @@ def two_qubit(o: dict) -> dict: } -def natives(o: dict) -> dict: +def natives(o: dict, rescale: float) -> dict: return { - "single_qubit": single_pulse(o["single_qubit"]), + "single_qubit": single_pulse(o["single_qubit"], rescale), "coupler": { - f"coupler_{k}": v for k, v in single_pulse(o.get("coupler", {})).items() + f"coupler_{k}": v + for k, v in single_pulse(o.get("coupler", {}), rescale).items() }, - "two_qubit": two_qubit(o["two_qubit"]), + "two_qubit": two_qubit(o["two_qubit"], rescale), } def upgrade(o: dict, connections: Optional[dict] = None) -> dict: + rescale = 2 if connections is not None and connections["kind"] == "qm" else 1 return { "settings": o["settings"], "configs": configs( @@ -235,7 +237,7 @@ def upgrade(o: dict, connections: Optional[dict] = None) -> dict: o["characterization"], connections, ), - "native_gates": natives(o["native_gates"]), + "native_gates": natives(o["native_gates"], rescale), } From 5ee5cff26608c05d3c41a0c06bb4fa3d7d6f011c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 2 Dec 2024 12:26:51 +0100 Subject: [PATCH 26/34] fix: Scope, document, and simplify connections argument --- convert.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/convert.py b/convert.py index 3221ac42..87aefc6e 100644 --- a/convert.py +++ b/convert.py @@ -1,5 +1,4 @@ -"""Converts parameters.json to parameters.json and calibration.json -""" +"""Converts parameters.json to parameters.json and calibration.json.""" import argparse import ast @@ -14,7 +13,8 @@ NONSERIAL = lambda: None """Raise an error if survives in the final object to be serialized.""" QM_TIME_OF_FLIGHT = 224 -"""Default time of flight for QM platforms (in 0.1 this was hard-coded in platform.py).""" +"""Default time of flight for QM platforms (in 0.1 this was hard-coded in +platform.py).""" def channel_from_pulse(pulse: dict) -> dict: @@ -123,7 +123,9 @@ def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: else ( "acquisition" if type_ == "ro" - else "drive12" if gate == "RX12" else "drive" + else "drive12" + if gate == "RX12" + else "drive" ) ) ) @@ -182,7 +184,9 @@ def pulse_like(o: dict, rescale: float) -> dict: return ( acquisition(o, rescale) if o["type"] == "ro" - else virtualz(o) if o["type"] == "virtual_z" else pulse(o, rescale) + else virtualz(o) + if o["type"] == "virtual_z" + else pulse(o, rescale) ) @@ -299,20 +303,21 @@ def convert(path: Path, connections_path: Optional[Path] = None): def parse(): parser = argparse.ArgumentParser() parser.add_argument("path", nargs="*", type=Path) - parser.add_argument("--connections", nargs="*", default=None, type=Path) + parser.add_argument( + "--qm-connections", + nargs="?", + default=None, + type=Path, + help="path to JSON file with connections for QM", + ) return parser.parse_args() def main(): args = parse() - connections = args.connections - if connections is not None: - assert len(args.path) == len(connections) - else: - connections = len(args.path) * [None] - for p, c in zip(args.path, connections): - convert(p, c) + for p in args.path: + convert(p, args.qm_connections) if __name__ == "__main__": From 3e641e9094c6232e70700351b4fd5cfd3648ea27 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 2 Dec 2024 12:31:02 +0100 Subject: [PATCH 27/34] build: Add nix development env --- .envrc | 9 + .gitignore | 3 + flake.lock | 673 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 54 +++++ 4 files changed, 739 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..3af52d88 --- /dev/null +++ b/.envrc @@ -0,0 +1,9 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs=" +fi + +watch_file flake.nix +watch_file flake.lock +if ! use flake . --impure; then + echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2 +fi diff --git a/.gitignore b/.gitignore index 68bc17f9..78526dda 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,6 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# Nix +.devenv/ diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..bf265bdc --- /dev/null +++ b/flake.lock @@ -0,0 +1,673 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "devenv", + "flake-compat" + ], + "git-hooks": [ + "devenv", + "pre-commit-hooks" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726520618, + "narHash": "sha256-jOsaBmJ/EtX5t/vbylCdS7pWYcKGmWOKg4QKUzKr6dA=", + "owner": "cachix", + "repo": "cachix", + "rev": "695525f9086542dfb09fde0871dbf4174abbf634", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "cachix_2": { + "inputs": { + "devenv": "devenv_3", + "flake-compat": [ + "devenv", + "cachix", + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "cachix", + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_3", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks_2" + }, + "locked": { + "lastModified": 1730551965, + "narHash": "sha256-pZjOiaIW3AZinSmTCubFgoxRQzqmHFiOkHBJYZ1RdnA=", + "owner": "cachix", + "repo": "devenv", + "rev": "d3bb56ceaeef625712b050bd9343dbdb01bf37b1", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "cachix": "cachix_2", + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix_2", + "nixpkgs": [ + "devenv", + "cachix", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "cachix", + "git-hooks" + ] + }, + "locked": { + "lastModified": 1723156315, + "narHash": "sha256-0JrfahRMJ37Rf1i0iOOn+8Z4CLvbcGNwa2ChOAVrp/8=", + "owner": "cachix", + "repo": "devenv", + "rev": "ff5eb4f2accbcda963af67f1a1159e3f6c7f5f91", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_3": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix", + "nixpkgs": "nixpkgs", + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "devenv", + "cachix", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "python-rewrite", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1697646580, + "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_2" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix_3": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-parts": "flake-parts", + "libgit2": "libgit2", + "nixpkgs": "nixpkgs_2", + "nixpkgs-23-11": "nixpkgs-23-11", + "nixpkgs-regression": "nixpkgs-regression_3", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1727438425, + "narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=", + "owner": "domenkozar", + "repo": "nix", + "rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.24", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-23-11": { + "locked": { + "lastModified": 1717159533, + "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1730504152, + "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_2": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_3": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1720386169, + "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1730531603, + "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "nix" + ], + "flake-utils": "flake-utils_2", + "gitignore": [ + "devenv", + "nix" + ], + "nixpkgs": [ + "devenv", + "nix", + "nixpkgs" + ], + "nixpkgs-stable": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712897695, + "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_2": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1726745158, + "narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_3" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..91df8bed --- /dev/null +++ b/flake.nix @@ -0,0 +1,54 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + devenv = { + url = "github:cachix/devenv"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-parts.url = "github:hercules-ci/flake-parts"; + }; + + outputs = { + self, + nixpkgs, + devenv, + flake-parts, + ... + } @ inputs: + flake-parts.lib.mkFlake {inherit inputs;} { + imports = [inputs.devenv.flakeModule]; + systems = ["x86_64-linux" "aarch64-darwin"]; + + perSystem = {pkgs, ...}: { + packages.default = pkgs.poetry2nix.mkPoetryApplication { + projectDir = self; + preferWheels = true; + }; + + devenv.shells.default = { + packages = with pkgs; [poethepoet pre-commit stdenv.cc.cc.lib]; + + languages = { + python = { + enable = true; + venv = { + enable = true; + requirements = '' + qibolab [qblox] @ git+https://github.com/qiboteam/qibolab.git@qblox + + # dev deps + ipython + pdbpp + ''; + }; + }; + }; + }; + }; + }; + + nixConfig = { + extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; + extra-substituters = "https://devenv.cachix.org"; + }; +} From 64a4db18de7eed2fba7a77d806bfa4b9657b1e77 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 2 Dec 2024 12:50:55 +0100 Subject: [PATCH 28/34] fix: Make calibration converter compatible with qblox --- convert.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/convert.py b/convert.py index 87aefc6e..a7a7986d 100644 --- a/convert.py +++ b/convert.py @@ -123,9 +123,7 @@ def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: else ( "acquisition" if type_ == "ro" - else "drive12" - if gate == "RX12" - else "drive" + else "drive12" if gate == "RX12" else "drive" ) ) ) @@ -184,9 +182,7 @@ def pulse_like(o: dict, rescale: float) -> dict: return ( acquisition(o, rescale) if o["type"] == "ro" - else virtualz(o) - if o["type"] == "virtual_z" - else pulse(o, rescale) + else virtualz(o) if o["type"] == "virtual_z" else pulse(o, rescale) ) @@ -264,7 +260,7 @@ def single_qubits_cal(o: dict) -> dict: "t1": [k["T1"], None], "t2": [k["T2"], None], "t2_spin_echo": [k["T2_spin_echo"], None], - "rb_fidelity": [k["gate_fidelity"], None], + "rb_fidelity": [k["gate_fidelity"], None] if "gate_fidelity" in k else None, } for q, k in o.items() } @@ -283,7 +279,11 @@ def two_qubits_cal(o: dict) -> dict: def upgrade_cal(o: dict) -> dict: return { "single_qubits": single_qubits_cal(o["characterization"]["single_qubit"]), - "two_qubits": two_qubits_cal(o["characterization"]["two_qubit"]), + "two_qubits": ( + two_qubits_cal(o["characterization"]["two_qubit"]) + if "two_qubit" in o["characterization"] + else {} + ), } From 9fc3998a9ed78614d9027d67a87c933f9169c4f8 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 2 Dec 2024 15:08:58 +0100 Subject: [PATCH 29/34] feat: Reuse connections for a qblox marker, refactor qm --- convert.py | 127 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 51 deletions(-) diff --git a/convert.py b/convert.py index a7a7986d..2be30c15 100644 --- a/convert.py +++ b/convert.py @@ -28,48 +28,13 @@ class QmConnection: output_mode: str = "triggered" -def qm_configs(conf: dict, instruments: dict, instrument_channels: dict) -> dict: - for channel, conn in instrument_channels.items(): - connection = QmConnection(**conn) - settings = instruments.get(connection.instrument, {}).get(connection.port, {}) - if channel in conf: - kind = conf[channel]["kind"] - if kind == "acquisition": - conf[channel].update( - { - "kind": "qm-acquisition", - "delay": QM_TIME_OF_FLIGHT, - "gain": settings.get("gain", 0), - } - ) - elif kind == "dc": - conf[channel].update( - { - "kind": "opx-output", - "filter": settings.get("filter", {}), - "output_mode": settings.get("output_mode", "direct"), - } - ) - else: - raise NotImplementedError - else: - conf[channel] = { - "kind": "octave-oscillator", - "frequency": settings["lo_frequency"], - "power": settings["gain"], - "output_mode": connection.output_mode, - } - return conf - - def configs( instruments: dict, single: dict, couplers: dict, characterization: dict, - connections: Optional[dict] = None, ) -> dict: - conf = ( + return ( { f"{k}/bounds": (v["bounds"] | {"kind": "bounds"}) for k, v in instruments.items() @@ -105,12 +70,6 @@ def configs( for id, char in characterization.get("coupler", {}).items() } ) - if connections is not None: - if connections["kind"] == "qm": - conf = qm_configs(conf, instruments, connections["channels"]) - else: - raise NotImplementedError - return conf def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: @@ -226,15 +185,72 @@ def natives(o: dict, rescale: float) -> dict: } +def qm(conf: dict, instruments: dict, instrument_channels: dict) -> dict: + for channel, conn in instrument_channels.items(): + connection = QmConnection(**conn) + settings = instruments.get(connection.instrument, {}).get(connection.port, {}) + if channel in conf: + kind = conf[channel]["kind"] + if kind == "acquisition": + conf[channel].update( + { + "kind": "qm-acquisition", + "delay": QM_TIME_OF_FLIGHT, + "gain": settings.get("gain", 0), + } + ) + elif kind == "dc": + conf[channel].update( + { + "kind": "opx-output", + "filter": settings.get("filter", {}), + "output_mode": settings.get("output_mode", "direct"), + } + ) + else: + raise NotImplementedError + else: + conf[channel] = { + "kind": "octave-oscillator", + "frequency": settings["lo_frequency"], + "power": settings["gain"], + "output_mode": connection.output_mode, + } + return conf + + +def qblox(configs: dict, instruments: dict): + return configs + + +def device_specific(o: dict, configs: dict, connections: Optional[dict]): + return ( + configs + if connections is None + else ( + qm(configs, o["instruments"], connections["channels"]) + if connections["kind"] == "qm" + else ( + qblox(configs, o["instruments"]) + if connections["kind"] == "qblox" + else NONSERIAL + ) + ) + ) + + def upgrade(o: dict, connections: Optional[dict] = None) -> dict: rescale = 2 if connections is not None and connections["kind"] == "qm" else 1 return { "settings": o["settings"], - "configs": configs( - o["instruments"], - o["native_gates"]["single_qubit"], - o["native_gates"].get("coupler", {}), - o["characterization"], + "configs": device_specific( + o, + configs( + o["instruments"], + o["native_gates"]["single_qubit"], + o["native_gates"].get("coupler", {}), + o["characterization"], + ), connections, ), "native_gates": natives(o["native_gates"], rescale), @@ -287,8 +303,17 @@ def upgrade_cal(o: dict) -> dict: } -def convert(path: Path, connections_path: Optional[Path] = None): +def convert(path: Path, args: argparse.Namespace): params = json.loads(path.read_text()) + connections_path = ( + args.connections + if args.connections is not None + else ( + (path.parent / "connections.json") + if (path.parent / "connections.json").exists() + else None + ) + ) connections = ( json.loads(connections_path.read_text()) if connections_path is not None @@ -304,11 +329,11 @@ def parse(): parser = argparse.ArgumentParser() parser.add_argument("path", nargs="*", type=Path) parser.add_argument( - "--qm-connections", + "--connections", nargs="?", default=None, type=Path, - help="path to JSON file with connections for QM", + help="path to JSON file with connections", ) return parser.parse_args() @@ -317,7 +342,7 @@ def main(): args = parse() for p in args.path: - convert(p, args.qm_connections) + convert(p, args) if __name__ == "__main__": From 9624d6093d2ff12a2894338c8fbce13a1e395300 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 2 Dec 2024 17:21:52 +0100 Subject: [PATCH 30/34] feat: Extract qblox-specific info in converter --- convert.py | 52 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/convert.py b/convert.py index 2be30c15..ef7d4dd8 100644 --- a/convert.py +++ b/convert.py @@ -5,7 +5,7 @@ import json from dataclasses import dataclass from pathlib import Path -from typing import Optional +from typing import Callable, Optional from pydantic import TypeAdapter from qibolab._core.serialize import NdArray @@ -219,11 +219,49 @@ def qm(conf: dict, instruments: dict, instrument_channels: dict) -> dict: return conf -def qblox(configs: dict, instruments: dict): - return configs - - -def device_specific(o: dict, configs: dict, connections: Optional[dict]): +def qblox(configs: dict, instruments: dict, channels: dict) -> dict: + MODS = {"qcm_bb", "qcm_rf", "qrm_rf"} + c = ( + configs + | { + f"{inst}/{port}/lo": { + "kind": "oscillator", + "frequency": settings["lo_frequency"], + } + for inst, ports in instruments.items() + if any(mod in inst for mod in MODS) + for port, settings in ports.items() + if "lo_frequency" in settings + } + | { + f"{inst}/{port}/mixer": { + "kind": "iq-mixer", + "offset_i": settings["mixer_calibration"][0], + "offset_q": settings["mixer_calibration"][1], + } + for inst, ports in instruments.items() + if any(mod in inst for mod in MODS) + for port, settings in ports.items() + if "mixer_calibration" in settings + } + ) + for inst, ports in instruments.items(): + if any(mod in inst for mod in MODS): + for port, settings in ports.items(): + if "attenuation" in settings: + chs = ["drive", "drive12"] if "qcm" in inst else ["probe"] + for q in channels[inst][port[1:]]: + for ch in chs: + d = c[f"{q[1:]}/{ch}"] + d["kind"] = "qblox-iq" + d["attenuation"] = settings["attenuation"] + + return c + + +def device_specific( + o: dict, configs: dict, connections: Optional[dict] +) -> dict | Callable: return ( configs if connections is None @@ -231,7 +269,7 @@ def device_specific(o: dict, configs: dict, connections: Optional[dict]): qm(configs, o["instruments"], connections["channels"]) if connections["kind"] == "qm" else ( - qblox(configs, o["instruments"]) + qblox(configs, o["instruments"], connections["channels"]) if connections["kind"] == "qblox" else NONSERIAL ) From aa8a969ff0d26b2e7744faa542d206a2ce78a824 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 2 Dec 2024 17:22:47 +0100 Subject: [PATCH 31/34] feat: Split iqm5q channels map as data Used as input for the converter --- iqm5q/connections.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 iqm5q/connections.json diff --git a/iqm5q/connections.json b/iqm5q/connections.json new file mode 100644 index 00000000..6b0e2ff4 --- /dev/null +++ b/iqm5q/connections.json @@ -0,0 +1,14 @@ +{ + "kind": "qblox", + "channels": { + "qrm_rf0": { "1": ["q0", "q1"] }, + "qrm_rf1": { "1": ["q2", "q3", "q4"] }, + "qcm_rf0": { "1": ["q1"], "2": ["q2"] }, + "qcm_rf1": { "1": ["q3"], "2": ["q4"] }, + "qcm_rf2": { "1": ["q0"] }, + "qcm0": { "1": ["q0"], "2": ["q1"], "3": ["q2"], "4": ["q3"] }, + "qcm1": { "1": ["q4"], "2": ["c1"], "4": ["c3"] }, + "qcm2": { "2": ["c4"] }, + "qcm3": { "1": ["c0"] } + } +} From 6d4c6fe14e20370b1a4569ba4fb59ae092ca510c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 4 Dec 2024 18:03:49 +0100 Subject: [PATCH 32/34] fix: Properly convert custom pulses, adding empty q component --- convert.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/convert.py b/convert.py index ef7d4dd8..f677b248 100644 --- a/convert.py +++ b/convert.py @@ -7,6 +7,7 @@ from pathlib import Path from typing import Callable, Optional +import numpy as np from pydantic import TypeAdapter from qibolab._core.serialize import NdArray @@ -82,7 +83,9 @@ def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: else ( "acquisition" if type_ == "ro" - else "drive12" if gate == "RX12" else "drive" + else "drive12" + if gate == "RX12" + else "drive" ) ) ) @@ -107,11 +110,15 @@ def envelope(o: str) -> dict: kind = call.func.id.lower() kwargs = {} shape = SHAPES[kind] - for arg, spec in zip(call.args, shape.items()): - kwargs[spec[0]] = spec[1](ast.literal_eval(arg)) + for arg, (attr, map_) in zip(call.args, shape.items()): + kwargs[attr] = map_(ast.literal_eval(arg)) for arg in call.keywords: assert isinstance(arg.value, ast.Constant) kwargs[arg.arg] = ast.literal_eval(arg.value) + if kind == "custom" and "q_" not in kwargs: + kwargs["q_"] = ( + TypeAdapter(NdArray).dump_json(np.zeros_like(kwargs["i_"])).decode() + ) return {"kind": kind, **kwargs} @@ -141,7 +148,9 @@ def pulse_like(o: dict, rescale: float) -> dict: return ( acquisition(o, rescale) if o["type"] == "ro" - else virtualz(o) if o["type"] == "virtual_z" else pulse(o, rescale) + else virtualz(o) + if o["type"] == "virtual_z" + else pulse(o, rescale) ) From 22c157875b413b59d52ca7294f620f0181b53adc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 17:05:02 +0000 Subject: [PATCH 33/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- convert.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/convert.py b/convert.py index f677b248..53bc9d41 100644 --- a/convert.py +++ b/convert.py @@ -83,9 +83,7 @@ def channel(qubit: str, type_: str, gate: Optional[str] = None) -> str: else ( "acquisition" if type_ == "ro" - else "drive12" - if gate == "RX12" - else "drive" + else "drive12" if gate == "RX12" else "drive" ) ) ) @@ -148,9 +146,7 @@ def pulse_like(o: dict, rescale: float) -> dict: return ( acquisition(o, rescale) if o["type"] == "ro" - else virtualz(o) - if o["type"] == "virtual_z" - else pulse(o, rescale) + else virtualz(o) if o["type"] == "virtual_z" else pulse(o, rescale) ) From 50ed5ca3a4e33c8c9e93d89c49b6441020fe0747 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 5 Dec 2024 18:24:04 +0100 Subject: [PATCH 34/34] fix: Convert attenuation into the power slot --- convert.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/convert.py b/convert.py index 53bc9d41..d71f3d71 100644 --- a/convert.py +++ b/convert.py @@ -224,14 +224,15 @@ def qm(conf: dict, instruments: dict, instrument_channels: dict) -> dict: return conf -def qblox(configs: dict, instruments: dict, channels: dict) -> dict: +def qblox(configs: dict, instruments: dict) -> dict: MODS = {"qcm_bb", "qcm_rf", "qrm_rf"} - c = ( + return ( configs | { f"{inst}/{port}/lo": { "kind": "oscillator", "frequency": settings["lo_frequency"], + "power": settings["attenuation"], } for inst, ports in instruments.items() if any(mod in inst for mod in MODS) @@ -250,18 +251,6 @@ def qblox(configs: dict, instruments: dict, channels: dict) -> dict: if "mixer_calibration" in settings } ) - for inst, ports in instruments.items(): - if any(mod in inst for mod in MODS): - for port, settings in ports.items(): - if "attenuation" in settings: - chs = ["drive", "drive12"] if "qcm" in inst else ["probe"] - for q in channels[inst][port[1:]]: - for ch in chs: - d = c[f"{q[1:]}/{ch}"] - d["kind"] = "qblox-iq" - d["attenuation"] = settings["attenuation"] - - return c def device_specific( @@ -274,7 +263,7 @@ def device_specific( qm(configs, o["instruments"], connections["channels"]) if connections["kind"] == "qm" else ( - qblox(configs, o["instruments"], connections["channels"]) + qblox(configs, o["instruments"]) if connections["kind"] == "qblox" else NONSERIAL )