From 0c2f88a3c1ae8e2db84c7bc447132d725d2a23a7 Mon Sep 17 00:00:00 2001 From: Dean Poulos Date: Thu, 27 Jun 2024 00:18:05 +1000 Subject: [PATCH 1/9] Add final action for setting offset of LF-FEM analog output port. --- quam/utils/config.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/quam/utils/config.py b/quam/utils/config.py index e46fdaef..aae3bb38 100644 --- a/quam/utils/config.py +++ b/quam/utils/config.py @@ -11,6 +11,16 @@ def generate_config_final_actions(qua_config): qua_config (dict): The generated qua config. """ for controller_cfg in qua_config["controllers"].values(): + if "fems" in controller_cfg: + for fem in controller_cfg["fems"].values(): + if fem.get("type") == "LF": + if "analog_outputs" in fem: + for analog_output in fem["analog_outputs"].values(): + analog_output.setdefault("offset", 0.0) + if "analog_inputs" in fem: + for analog_input in fem["analog_inputs"].values(): + analog_input.setdefault("offset", 0.0) + if "analog_outputs" in controller_cfg: for analog_output in controller_cfg["analog_outputs"].values(): analog_output.setdefault("offset", 0.0) From b9e1bd256aafe9f6c09999d462adf49e61df956e Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 27 Jun 2024 10:23:31 +0200 Subject: [PATCH 2/9] modifications + tests --- quam/utils/config.py | 17 ++- tests/config/test_config_final_actions.py | 139 ++++++++++++++++++++++ 2 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 tests/config/test_config_final_actions.py diff --git a/quam/utils/config.py b/quam/utils/config.py index aae3bb38..8f5619cc 100644 --- a/quam/utils/config.py +++ b/quam/utils/config.py @@ -10,16 +10,15 @@ def generate_config_final_actions(qua_config): Args: qua_config (dict): The generated qua config. """ + # Add default dc offset 0V to all analog outputs and inputs if not set for controller_cfg in qua_config["controllers"].values(): - if "fems" in controller_cfg: - for fem in controller_cfg["fems"].values(): - if fem.get("type") == "LF": - if "analog_outputs" in fem: - for analog_output in fem["analog_outputs"].values(): - analog_output.setdefault("offset", 0.0) - if "analog_inputs" in fem: - for analog_input in fem["analog_inputs"].values(): - analog_input.setdefault("offset", 0.0) + for fem in controller_cfg.get("fems", {}).values(): + if fem.get("type") != "LF": + continue + for analog_output in fem.get("analog_outputs", {}).values(): + analog_output.setdefault("offset", 0.0) + for analog_input in fem.get("analog_inputs", {}).values(): + analog_input.setdefault("offset", 0.0) if "analog_outputs" in controller_cfg: for analog_output in controller_cfg["analog_outputs"].values(): diff --git a/tests/config/test_config_final_actions.py b/tests/config/test_config_final_actions.py new file mode 100644 index 00000000..e7d0f538 --- /dev/null +++ b/tests/config/test_config_final_actions.py @@ -0,0 +1,139 @@ +from quam.utils.config import generate_config_final_actions + + +def test_config_no_overwrite_existing_offset(): + cfg = { + "controllers": { + "con1": { + "analog_outputs": {1: {"offset": 0.5}}, + "analog_inputs": {2: {"offset": 0.5}}, + }, + }, + } + + generate_config_final_actions(cfg) + + assert cfg == { + "controllers": { + "con1": { + "analog_outputs": {1: {"offset": 0.5}}, + "analog_inputs": {2: {"offset": 0.5}}, + }, + }, + } + + +def test_config_default_offset(): + cfg = { + "controllers": { + "con1": { + "analog_outputs": {1: {}}, + "analog_inputs": {2: {}}, + }, + }, + } + + generate_config_final_actions(cfg) + + assert cfg == { + "controllers": { + "con1": { + "analog_outputs": {1: {"offset": 0.0}}, + "analog_inputs": {2: {"offset": 0.0}}, + }, + }, + } + + +def test_config_default_offset_LF_FEM(): + cfg = { + "controllers": { + "con1": { + "fems": { + 2: { + "type": "LF", + "analog_outputs": {1: {}}, + "analog_inputs": {2: {}}, + }, + }, + }, + }, + } + + generate_config_final_actions(cfg) + + assert cfg == { + "controllers": { + "con1": { + "fems": { + 2: { + "type": "LF", + "analog_outputs": {1: {"offset": 0.0}}, + "analog_inputs": {2: {"offset": 0.0}}, + }, + }, + }, + }, + } + + +def test_config_no_overwrite_existing_offset_LF_FEM(): + cfg = { + "controllers": { + "con1": { + "fems": { + 2: { + "type": "LF", + "analog_outputs": {1: {"offset": 0.5}}, + "analog_inputs": {2: {"offset": 0.5}}, + }, + }, + }, + }, + } + + generate_config_final_actions(cfg) + + assert cfg == { + "controllers": { + "con1": { + "fems": { + 2: { + "type": "LF", + "analog_outputs": {1: {"offset": 0.5}}, + "analog_inputs": {2: {"offset": 0.5}}, + }, + }, + }, + }, + } + + +def test_config_default_offset_no_outputs_inputs_entries(): + cfg = {"controllers": {"con1": {}}} + + generate_config_final_actions(cfg) + + assert cfg == {"controllers": {"con1": {}}} + + +def test_config_default_offset_no_outputs_inputs(): + cfg = { + "controllers": { + "con1": { + "analog_outputs": {}, + "analog_inputs": {}, + } + } + } + + generate_config_final_actions(cfg) + + assert cfg == { + "controllers": { + "con1": { + "analog_outputs": {}, + "analog_inputs": {}, + } + } + } From d933af3b2434cdbe595fa980bc0295491aa5f83c Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Tue, 2 Jul 2024 07:00:54 +0200 Subject: [PATCH 3/9] docs: exclude Python 3.12 from allowed versions --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index fab721f9..31bdf4e7 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -4,7 +4,7 @@ /// tab | For Windows - Windows 10 (build 1809 and later), or Windows 11 -- Python 3.9 or higher, we recommend Python 3.10 or higher +- 3.8 ≤ Python ≤ 3.11, we recommend Python 3.10 or 3.11 - [Git version control system](https://git-scm.com/), or a Git GUI such as [GitHub Desktop](https://desktop.github.com/) or [GitKraken](https://www.gitkraken.com/) From b7eade81b87e40538d0fda8e9060cb00ff9f20e1 Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 4 Jul 2024 04:47:01 +0800 Subject: [PATCH 4/9] add converter for int keys --- quam/serialisation/json.py | 16 +++++++++++- .../serialisation/test_json_serialisation.py | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/quam/serialisation/json.py b/quam/serialisation/json.py index 3d7b9306..e2d56655 100644 --- a/quam/serialisation/json.py +++ b/quam/serialisation/json.py @@ -166,7 +166,7 @@ def load( metadata["default_filename"] = path.name with open(path, "r") as f: - contents = json.load(f) + contents = json.load(f, object_hook=convert_int_keys) elif path.is_dir(): metadata["default_foldername"] = str(path) for file in path.iterdir(): @@ -183,3 +183,17 @@ def load( metadata["content_mapping"][file.name] = list(file_contents.keys()) return contents, metadata + + +def convert_int_keys(obj): + """Convert dictionary keys to integers if possible.""" + if not isinstance(obj, dict): + return obj + + new_obj = {} + for key, value in obj.items(): + if key.isdigit(): + key = int(key) + new_obj[key] = value + + return new_obj diff --git a/tests/serialisation/test_json_serialisation.py b/tests/serialisation/test_json_serialisation.py index b7695e5f..b2ff446a 100644 --- a/tests/serialisation/test_json_serialisation.py +++ b/tests/serialisation/test_json_serialisation.py @@ -1,4 +1,5 @@ import json +from typing import Dict import pytest from quam.serialisation import JSONSerialiser @@ -107,3 +108,28 @@ def test_component_mamping_ignore(tmp_path): "a": 4, } } + + +@quam_dataclass +class QuAMWithIntDict(QuamRoot): + a: int + d: Dict[int, str] + + +def test_serialise_int_dict_keys(tmp_path): + quam_root = QuAMWithIntDict(a=1, d={1: "a", 2: "b"}) + + serialiser = JSONSerialiser() + path = tmp_path / "quam_root.json" + serialiser.save(quam_root, path) + + d, _ = serialiser.load(path) + + assert d == { + "a": 1, + "d": { + "1": "a", + "2": "b", + }, + "__class__": "test_json_serialisation.QuAMWithIntDict", + } From 0ed74b5078a53c450ffc59bdade604ced902c36c Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 4 Jul 2024 04:58:07 +0800 Subject: [PATCH 5/9] feat: Add support for instantiating union types The code changes in `quam_instantiation.py` and `test_instantiation.py` introduce support for instantiating union types. This allows the `instantiate_attr` function to handle union types correctly when creating instances of classes. The changes include importing the `types` module and checking for `types.UnionType` in addition to `typing.Union` when determining if a type is a union type. This commit message follows the established convention of using a prefix to indicate the type of change (`feat` for feature). --- quam/core/quam_instantiation.py | 3 ++- tests/instantiation/test_instantiation.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/quam/core/quam_instantiation.py b/quam/core/quam_instantiation.py index 57680cf4..eee55f36 100644 --- a/quam/core/quam_instantiation.py +++ b/quam/core/quam_instantiation.py @@ -1,4 +1,5 @@ from __future__ import annotations +import types import typing from typing import TYPE_CHECKING, Dict, Any from inspect import isclass @@ -224,7 +225,7 @@ def instantiate_attr( ) if typing.get_origin(expected_type) == dict: expected_type = dict - elif typing.get_origin(expected_type) == typing.Union: + elif typing.get_origin(expected_type) in [typing.Union, types.UnionType]: for union_type in typing.get_args(expected_type): try: instantiated_attr = instantiate_attr( diff --git a/tests/instantiation/test_instantiation.py b/tests/instantiation/test_instantiation.py index 7aa293ff..eca643cb 100644 --- a/tests/instantiation/test_instantiation.py +++ b/tests/instantiation/test_instantiation.py @@ -357,3 +357,18 @@ class TestQuamUnion(QuamComponent): with pytest.raises(TypeError): instantiate_quam_class(TestQuamUnion, {"union_val": {"a": "42"}}) + + +def test_instantiation_pipe_union_type(): + @quam_dataclass + class TestQuamUnion(QuamComponent): + union_val: int | TestQuamComponent + + obj = instantiate_quam_class(TestQuamUnion, {"union_val": 42}) + assert obj.union_val == 42 + + obj = instantiate_quam_class(TestQuamUnion, {"union_val": {"a": 42}}) + assert obj.union_val.a == 42 + + with pytest.raises(TypeError): + instantiate_quam_class(TestQuamUnion, {"union_val": {"a": "42"}}) \ No newline at end of file From 075416de2669e829a5fd52f2b2754d1f9400ce89 Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 4 Jul 2024 05:02:07 +0800 Subject: [PATCH 6/9] add conditional to test --- CHANGELOG.md | 4 ++++ tests/instantiation/test_instantiation.py | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f46e16..c873f676 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ ### Changed - Allow `QuamBase.get_reference(attr)` to return a reference of one of its attributes +### Fixed +- Fix quam object instantiation error when a parameter type uses pipe operator + + ## [0.3.3] ### Added - Added the following parameters to `IQChannel`: `RF_frequency`, `LO_frequency`, `intermediate_frequency` diff --git a/tests/instantiation/test_instantiation.py b/tests/instantiation/test_instantiation.py index eca643cb..f6c0da63 100644 --- a/tests/instantiation/test_instantiation.py +++ b/tests/instantiation/test_instantiation.py @@ -1,6 +1,8 @@ import pytest from typing import List, Literal, Optional, Tuple, Union +from pytest_cov.engine import sys + from quam.core import QuamRoot, QuamComponent, quam_dataclass from quam.core.quam_classes import QuamDict from quam.examples.superconducting_qubits.components import Transmon @@ -339,6 +341,7 @@ def test_instantiate_dict_referenced(): assert attrs == {"test_attr": "#./reference"} + @quam_dataclass class TestQuamComponent(QuamComponent): a: int @@ -359,6 +362,7 @@ class TestQuamUnion(QuamComponent): instantiate_quam_class(TestQuamUnion, {"union_val": {"a": "42"}}) +@pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher") def test_instantiation_pipe_union_type(): @quam_dataclass class TestQuamUnion(QuamComponent): @@ -371,4 +375,4 @@ class TestQuamUnion(QuamComponent): assert obj.union_val.a == 42 with pytest.raises(TypeError): - instantiate_quam_class(TestQuamUnion, {"union_val": {"a": "42"}}) \ No newline at end of file + instantiate_quam_class(TestQuamUnion, {"union_val": {"a": "42"}}) From abff173cd9658cba0a6a9cd0a95791b1b53ef394 Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 4 Jul 2024 05:04:04 +0800 Subject: [PATCH 7/9] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f46e16..dc3496ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ ### Changed - Allow `QuamBase.get_reference(attr)` to return a reference of one of its attributes +### Fixed +- Allow int keys to be serialised / loaded in QuAM using JSONSerialiser + ## [0.3.3] ### Added - Added the following parameters to `IQChannel`: `RF_frequency`, `LO_frequency`, `intermediate_frequency` From 099359e0f2827a56d21498405323eb46e1505050 Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 4 Jul 2024 06:26:37 +0800 Subject: [PATCH 8/9] remove types.uniontype for python < 3.10 --- quam/core/quam_instantiation.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/quam/core/quam_instantiation.py b/quam/core/quam_instantiation.py index eee55f36..e1bd0720 100644 --- a/quam/core/quam_instantiation.py +++ b/quam/core/quam_instantiation.py @@ -1,4 +1,5 @@ from __future__ import annotations +import sys import types import typing from typing import TYPE_CHECKING, Dict, Any @@ -17,6 +18,12 @@ from quam.core import QuamBase +if sys.version_info < (3, 10): + union_types = (typing.Union,) +else: + union_types = [typing.Union, types.UnionType] + + def instantiate_attrs_from_dict( attr_dict: dict, required_type: type, @@ -225,7 +232,7 @@ def instantiate_attr( ) if typing.get_origin(expected_type) == dict: expected_type = dict - elif typing.get_origin(expected_type) in [typing.Union, types.UnionType]: + elif typing.get_origin(expected_type) in union_types: for union_type in typing.get_args(expected_type): try: instantiated_attr = instantiate_attr( From ee64c8c7d5f3d72516924f82758cb723a70a8aef Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 4 Jul 2024 06:29:39 +0800 Subject: [PATCH 9/9] test fix --- tests/serialisation/test_json_serialisation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/serialisation/test_json_serialisation.py b/tests/serialisation/test_json_serialisation.py index b2ff446a..167f4c4d 100644 --- a/tests/serialisation/test_json_serialisation.py +++ b/tests/serialisation/test_json_serialisation.py @@ -128,8 +128,8 @@ def test_serialise_int_dict_keys(tmp_path): assert d == { "a": 1, "d": { - "1": "a", - "2": "b", + 1: "a", + 2: "b", }, "__class__": "test_json_serialisation.QuAMWithIntDict", }