From f0ce0338588860e9e8c8f7cc31f7bf3a35e1195f Mon Sep 17 00:00:00 2001 From: Dean Poulos Date: Sat, 21 Dec 2024 04:43:09 +1100 Subject: [PATCH 1/3] Add setter to LO_frequency property in FrequencyConverter. --- quam/components/hardware.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quam/components/hardware.py b/quam/components/hardware.py index ff09c814..4c7a0bbf 100644 --- a/quam/components/hardware.py +++ b/quam/components/hardware.py @@ -140,6 +140,10 @@ class FrequencyConverter(BaseFrequencyConverter): def LO_frequency(self): return self.local_oscillator.frequency + @LO_frequency.setter + def LO_frequency(self, value): + self.local_oscillator.frequency = value + def configure(self): if self.local_oscillator is not None: self.local_oscillator.configure() From 37c2d55d63169cc6bcdac75bdf2ad9579910245d Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Mon, 6 Jan 2025 12:20:41 +0100 Subject: [PATCH 2/3] Use `set_at_reference` for LO frequency --- CHANGELOG.md | 1 + quam/components/hardware.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c9b8f72..9c346c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Added - Added `QuamBase.set_at_reference` to set a value at a reference - Added `string_reference.get_parent_reference` to get the parent reference of a string reference +- Added `FrequencyConverter.LO_frequency` setter which updates the local oscillator frequency ### Changed - `Pulse.integration_weights` now defaults to `#./default_integration_weights`, which returns [(1, pulse.length)] diff --git a/quam/components/hardware.py b/quam/components/hardware.py index 4c7a0bbf..80ce74f9 100644 --- a/quam/components/hardware.py +++ b/quam/components/hardware.py @@ -142,7 +142,15 @@ def LO_frequency(self): @LO_frequency.setter def LO_frequency(self, value): - self.local_oscillator.frequency = value + """Sets the frequency of the local oscillator object""" + if self.local_oscillator is None: + raise AttributeError( + f"Unable to set LO frequency for {self} as it has no local oscillator" + ) + + # Use set_at_reference to ensure the frequency is updated, even if the local + # oscillator frequency is a reference + self.local_oscillator.set_at_reference("frequency", value) def configure(self): if self.local_oscillator is not None: From cf4b82fcee85c1d76341b569f383d83b8a365a2c Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Wed, 8 Jan 2025 10:33:52 +0100 Subject: [PATCH 3/3] Fix situation where LO.frequency is None --- quam/components/hardware.py | 4 +++- quam/core/quam_classes.py | 21 ++++++++++++------ tests/quam_base/test_set_at_reference.py | 27 +++++++++++++++++++++--- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/quam/components/hardware.py b/quam/components/hardware.py index 80ce74f9..13f71656 100644 --- a/quam/components/hardware.py +++ b/quam/components/hardware.py @@ -150,7 +150,9 @@ def LO_frequency(self, value): # Use set_at_reference to ensure the frequency is updated, even if the local # oscillator frequency is a reference - self.local_oscillator.set_at_reference("frequency", value) + self.local_oscillator.set_at_reference( + "frequency", value, allow_non_reference=True + ) def configure(self): if self.local_oscillator is not None: diff --git a/quam/core/quam_classes.py b/quam/core/quam_classes.py index 2dafc739..43480207 100644 --- a/quam/core/quam_classes.py +++ b/quam/core/quam_classes.py @@ -542,24 +542,33 @@ def print_summary(self, indent: int = 0): else: print(" " * (indent + 2) + f"{attr}: {val}") - def set_at_reference(self, attr: str, value: Any): + def set_at_reference( + self, attr: str, value: Any, allow_non_reference: bool = False + ): """Follow the reference of an attribute and set the value at the reference Args: attr: The attribute to set the value at the reference of. value: The value to set. + allow_non_reference: Whether to allow the attribute to be a non-reference. + If False, the attribute must be a reference or an error is raised. Raises: - ValueError: If the attribute is not a reference. + ValueError: If the attribute is not a reference and `allow_non_reference` is + False. ValueError: If the reference is invalid, e.g. "#./" since it has no attribute. """ raw_value = self.get_unreferenced_value(attr) if not string_reference.is_reference(raw_value): - raise ValueError( - f"Cannot set at reference because attr '{attr}' is not a reference. " - f"'{attr}' = {raw_value}" - ) + if not allow_non_reference: + raise ValueError( + f"Cannot set at reference because attr '{attr}' is not a reference. " + f"'{attr}' = {raw_value}" + ) + else: + setattr(self, attr, value) + return parent_reference, ref_attr = string_reference.split_reference(raw_value) if not ref_attr: diff --git a/tests/quam_base/test_set_at_reference.py b/tests/quam_base/test_set_at_reference.py index e422a3d0..499d7be0 100644 --- a/tests/quam_base/test_set_at_reference.py +++ b/tests/quam_base/test_set_at_reference.py @@ -84,6 +84,7 @@ class DoubleChildQuam(ChildQuam): value: int = 0 child: Optional[ChildQuam] = None + def test_set_double_reference(): """Test setting a value through a double reference""" double_child = DoubleChildQuam(child=ChildQuam(value=42), value="#./child/value") @@ -108,7 +109,9 @@ def test_set_double_reference(): def test_set_nonexistent_double_reference(): """Test setting a value where the double reference does not exist""" - double_child = DoubleChildQuam(child=ChildQuam(value=42), value="#./child/nonexistent") + double_child = DoubleChildQuam( + child=ChildQuam(value=42), value="#./child/nonexistent" + ) parent = ParentQuam(child=double_child, ref_value="#./child/nonexistent") with pytest.raises(AttributeError): @@ -117,7 +120,9 @@ def test_set_nonexistent_double_reference(): def test_set_double_reference_to_nonexistent_item(): """Test setting a value through a double reference to a nonexistent item""" - double_child = DoubleChildQuam(child=ChildQuam(value=42), value="#./nonexistent/value") + double_child = DoubleChildQuam( + child=ChildQuam(value=42), value="#./nonexistent/value" + ) parent = ParentQuam(child=double_child, ref_value="#./nonexistent/value") with pytest.raises(AttributeError): @@ -132,9 +137,13 @@ def test_set_double_reference_with_invalid_reference(): with pytest.raises(AttributeError): parent.set_at_reference("ref_value", 789) + def test_set_triple_reference(): """Test setting a value through a triple reference""" - triple_child = DoubleChildQuam(child=DoubleChildQuam(child=ChildQuam(value=42), value="#./child/value"), value="#./child/value") + triple_child = DoubleChildQuam( + child=DoubleChildQuam(child=ChildQuam(value=42), value="#./child/value"), + value="#./child/value", + ) parent = ParentQuam(child=triple_child, ref_value="#./child/value") assert parent.ref_value == 42 @@ -155,3 +164,15 @@ def test_set_triple_reference(): assert parent.get_unreferenced_value("ref_value") == "#./child/value" assert triple_child.get_unreferenced_value("value") == "#./child/value" assert triple_child.child.get_unreferenced_value("value") == "#./child/value" + + +def test_set_at_reference_allow_non_reference(): + """Test setting a value through a reference with allow_non_reference=True""" + parent = ParentQuam(child=ChildQuam(value=42), ref_value="#./child/value") + parent.set_at_reference("ref_value", 789, allow_non_reference=True) + assert parent.ref_value == 789 + + with pytest.raises(ValueError): + parent.child.set_at_reference("value", 43) + + assert parent.child.value == 789