Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to find instruments from parameter chain #6616

Merged
merged 7 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changes/newsfragments/6599.new
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Added a new feature to find links in a parameter chain by parameter type
Added new methods to find instruments in a parameter chain by parameter type
picarro-yren marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 4 additions & 0 deletions src/qcodes/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
InferAttrs,
InferError,
get_chain_links_of_type,
get_instrument_from_chain,
get_instrument_from_param,
get_parameter_chain,
get_root_parameter,
get_sole_chain_link_of_type,
get_sole_instrument_from_chain,
infer_channel,
infer_instrument,
infer_instrument_module,
Expand All @@ -30,6 +32,8 @@
"get_parameter_chain",
"get_root_parameter",
"get_sole_chain_link_of_type",
"get_instrument_from_chain",
"get_sole_instrument_from_chain",
"infer_channel",
"infer_instrument",
"infer_instrument_module",
Expand Down
36 changes: 36 additions & 0 deletions src/qcodes/extensions/infer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
DOES_NOT_EXIST = "Does not exist"

C = TypeVar("C", bound=ParameterBase)
TInstrument = TypeVar("TInstrument", bound=InstrumentBase)


class InferError(AttributeError): ...
Expand Down Expand Up @@ -253,3 +254,38 @@ def get_sole_chain_link_of_type(

raise ValueError(error_msg_1 + f"{[link.name for link in chain_links]}")
return chain_links[0]


def get_instrument_from_chain(
picarro-yren marked this conversation as resolved.
Show resolved Hide resolved
instrument_type: type[TInstrument] | tuple[type[TInstrument], ...],
parameter: Parameter,
) -> tuple[TInstrument, ...]:
"""Gets all instruments in a chain of linked parameters that match a given type"""

param_chain = get_parameter_chain(parameter)
return tuple(
[
cast(TInstrument, param.instrument)
for param in param_chain
if isinstance(param.instrument, instrument_type)
]
)


def get_sole_instrument_from_chain(
instrument_type: type[TInstrument] | tuple[type[TInstrument], ...],
parameter: Parameter,
) -> TInstrument:
"""Gets the one instruments in a chain of linked parameters that match a given type"""
instruments = get_instrument_from_chain(
instrument_type=instrument_type, parameter=parameter
)
if len(instruments) != 1:
if isinstance(instrument_type, type):
error_msg_1 = f"Expected only a single instrument of type {instrument_type.__name__} but found {len(instruments)}: \n"
elif isinstance(instrument_type, tuple):
type_strs = [instr_type.__name__ for instr_type in instrument_type]
error_msg_1 = f"Expected only a single instrument of types {type_strs} but found {len(instruments)}: \n"

raise ValueError(error_msg_1 + f"{[instr.name for instr in instruments]}")
picarro-yren marked this conversation as resolved.
Show resolved Hide resolved
return instruments[0]
66 changes: 66 additions & 0 deletions tests/extensions/test_infer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
InferError,
_merge_user_and_class_attrs,
get_chain_links_of_type,
get_instrument_from_chain,
get_parameter_chain,
get_root_parameter,
get_sole_chain_link_of_type,
get_sole_instrument_from_chain,
infer_channel,
infer_instrument,
)
Expand All @@ -39,6 +41,11 @@ def __init__(self, name: str):
self.module = DummyModule(name="module", parent=self)


class DummyInstrument2(DummyInstrument):
def __init__(self, name: str):
super().__init__(name=name)


class DummyDelegateInstrument(InstrumentBase):
def __init__(self, name: str):
super().__init__(name=name)
Expand Down Expand Up @@ -77,6 +84,16 @@ def make_instrument_fixture():
inst.close()


@pytest.fixture(name="instrument_fixture2")
def make_instrument_fixture2():
inst = DummyInstrument2("dummy_instrument2")
InferAttrs.clear()
try:
yield inst
finally:
inst.close()


@pytest.fixture(name="good_inst_delegates")
def make_good_delegate_parameters(instrument_fixture):
inst = instrument_fixture
Expand All @@ -90,6 +107,24 @@ def make_good_delegate_parameters(instrument_fixture):
return good_inst_del_1, good_inst_del_2, good_inst_del_3


@pytest.fixture(name="multi_inst_chain")
def make_multi_instrument_chain(instrument_fixture, instrument_fixture2):
inst = instrument_fixture
inst2 = instrument_fixture2
good_inst_del_1 = DelegateParameter(
"good_inst_del_1", source=inst.good_inst_parameter
)
good_inst_del_2 = DelegateParameter(
"good_inst_del_2",
source=good_inst_del_1,
instrument=inst2,
)
good_inst_del_3 = UserLinkingParameter(
"good_inst_del_3", linked_parameter=good_inst_del_2
)
return good_inst_del_1, good_inst_del_2, good_inst_del_3


def test_get_root_parameter_valid(instrument_fixture, good_inst_delegates):
inst = instrument_fixture
good_inst_del_1, good_inst_del_2, good_inst_del_3 = good_inst_delegates
Expand Down Expand Up @@ -314,3 +349,34 @@ def test_sole_chain_link_of_type():
(UserLinkingParameter, DelegateParameter), user_link_2
)
assert "Expected only a single chain link of types" in str(exc_info.value)


def test_get_instrument_from_chain(
instrument_fixture, instrument_fixture2, multi_inst_chain
):
inst = instrument_fixture
inst2 = instrument_fixture2
good_inst_del_1, good_inst_del_2, good_inst_del_3 = multi_inst_chain

InferAttrs.add("linked_parameter")
instruments = get_instrument_from_chain(DummyInstrument, good_inst_del_3)
assert set(instruments) == set([inst, inst2])


def test_get_sole_instrument_from_chain(instrument_fixture2, multi_inst_chain):
inst2 = instrument_fixture2
good_inst_del_1, good_inst_del_2, good_inst_del_3 = multi_inst_chain

InferAttrs.add("linked_parameter")
sole_instrument = get_sole_instrument_from_chain(DummyInstrument2, good_inst_del_3)
assert sole_instrument == inst2

with pytest.raises(ValueError) as exc_info:
_ = get_sole_instrument_from_chain(DummyInstrument, good_inst_del_3)
assert "Expected only a single instrument of type" in str(exc_info.value)

with pytest.raises(ValueError) as exc_info:
_ = get_sole_instrument_from_chain(
(DummyInstrument, DummyInstrument2), good_inst_del_3
)
assert "Expected only a single instrument of types" in str(exc_info.value)
Loading