From fcc0b397e4ab3ebfe5c1254228166b1bb5818eb3 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 27 Mar 2024 06:34:36 +0000 Subject: [PATCH 01/13] create a single RoutingEntry class --- spinn_machine/__init__.py | 4 +- spinn_machine/routing_entry.py | 210 ++++++++++++++++++++++++++++++++ unittests/test_routing_entry.py | 145 ++++++++++++++++++++++ 3 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 spinn_machine/routing_entry.py create mode 100644 unittests/test_routing_entry.py diff --git a/spinn_machine/__init__.py b/spinn_machine/__init__.py index 57c9644d..be678296 100644 --- a/spinn_machine/__init__.py +++ b/spinn_machine/__init__.py @@ -84,6 +84,7 @@ from .machine import Machine from .multicast_routing_entry import MulticastRoutingEntry from .router import Router +from .routing_entry import RoutingEntry from .spinnaker_triad_geometry import SpiNNakerTriadGeometry from .virtual_machine import virtual_machine from .fixed_route_entry import FixedRouteEntry @@ -91,4 +92,5 @@ __all__ = ["Chip", "CoreSubset", "CoreSubsets", "FixedRouteEntry", "FrozenCoreSubsets", "Link", "Machine", "MulticastRoutingEntry", - "Router", "SpiNNakerTriadGeometry", "virtual_machine"] + "Router", "RoutingEntry", "SpiNNakerTriadGeometry", + "virtual_machine"] diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py new file mode 100644 index 00000000..232d3524 --- /dev/null +++ b/spinn_machine/routing_entry.py @@ -0,0 +1,210 @@ +# Copyright (c) 2024 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations +from typing import Any, FrozenSet, Iterable, Optional, Tuple, overload +from spinn_machine.router import Router +from .exceptions import SpinnMachineAlreadyExistsException + + +class RoutingEntry(object): + """ + Represents an entry in a SpiNNaker chip's multicast routing table. + """ + + __slots__ = ( + "_defaultable", "_processor_ids", + "_link_ids", "_spinnaker_route", "__repr") + + @overload + def __init__(self, *, processor_ids: Iterable[int], + link_ids: Iterable[int], + defaultable: bool = False, + spinnaker_route: None = None): + ... + + @overload + def __init__(self, int, *, processor_ids: None = None, + link_ids: None = None, + defaultable: bool = False, + spinnaker_route: int): + ... + + # pylint: disable=too-many-arguments + def __init__(self, *, processor_ids: Optional[Iterable[int]] = None, + link_ids: Optional[Iterable[int]] = None, + defaultable: bool = False, + spinnaker_route: Optional[int] = None) -> None: + """ + .. note:: + The processor_ids and link_ids parameters are only optional if a + spinnaker_route is provided. If a spinnaker_route is provided + the processor_ids and link_ids parameters are ignored. + + :param processor_ids: The destination processor IDs + :type processor_ids: iterable(int) or None + :param link_ids: The destination link IDs + :type link_ids: iterable(int) or None + :param bool defaultable: + If this entry is defaultable (it receives packets + from its directly opposite route position) + :param spinnaker_route: + The processor_ids and link_ids expressed as a single int. + :type spinnaker_route: int or None + :raise spinn_machine.exceptions.SpinnMachineAlreadyExistsException: + * If processor_ids contains the same ID more than once + * If link_ids contains the same ID more than once + :raise TypeError: if no spinnaker_route provided and either + processor_ids or link_ids is missing or `None` + """ + self._defaultable: bool = defaultable + self.__repr: Optional[str] = None + + # Add processor IDs, ignore duplicates + if spinnaker_route is None: + assert processor_ids is not None + assert link_ids is not None + self._processor_ids: Optional[FrozenSet[int]] = \ + frozenset(processor_ids) + self._link_ids: Optional[FrozenSet[int]] = frozenset(link_ids) + self._spinnaker_route: int = self._calc_spinnaker_route() + else: + self._spinnaker_route = spinnaker_route + self._processor_ids = None + self._link_ids = None + + # TODO from FixedRouteEntry use or loose + @staticmethod + def __check_dupes(sequence: Iterable[int], name: str): + check = set() + for _id in sequence: + if _id in check: + raise SpinnMachineAlreadyExistsException(name, str(_id)) + check.add(_id) + + @property + def processor_ids(self) -> FrozenSet[int]: + """ + The destination processor IDs. + + :rtype: frozenset(int) + """ + if self._processor_ids is None: + self._processor_ids, self._link_ids = self._calc_routing_ids() + return self._processor_ids + + @property + def link_ids(self) -> FrozenSet[int]: + """ + The destination link IDs. + + :rtype: frozenset(int) + """ + if self._link_ids is None: + self._processor_ids, self._link_ids = self._calc_routing_ids() + return self._link_ids + + @property + def defaultable(self) -> bool: + """ + Whether this entry is a defaultable entry. An entry is defaultable if + it is duplicating the default behaviour of the SpiNNaker router (to + pass a message out on the link opposite from where it was received, + without routing it to any processors; source and destination chips for + a message cannot be defaultable). + + :rtype: bool + """ + return self._defaultable + + @property + def spinnaker_route(self) -> int: + """ + The encoded SpiNNaker route. + + :rtype: int + """ + return self._spinnaker_route + + def merge(self, other: RoutingEntry) -> RoutingEntry: + """ + Merges together two routing entries. The merge will join the + processor IDs and link IDs from both the entries. This could be + used to add a new destination to an existing route in a + routing table. It is also possible to use the add (`+`) operator + or the or (`|`) operator with the same effect. + + :param ~spinn_machine.MulticastRoutingEntry other: + The multicast entry to merge with this entry + :return: A new multicast routing entry with merged destinations + :rtype: ~spinn_machine.MulticastRoutingEntry + :raise spinn_machine.exceptions.SpinnMachineInvalidParameterException: + If the key and mask of the other entry do not match + """ + # 2 different merged routes can NEVER be defaultable + return RoutingEntry( + spinnaker_route=self.spinnaker_route | other.spinnaker_route, + defaultable=self.defaultable and other.defaultable) + + def __eq__(self, other_entry: Any) -> bool: + if not isinstance(other_entry, RoutingEntry): + return False + if self._spinnaker_route != other_entry._spinnaker_route: + return False + return (self._defaultable == other_entry.defaultable) + + def __hash__(self) -> int: + return self._spinnaker_route * 29 * int(self._defaultable) * 131 + + def __ne__(self, other: Any) -> bool: + return not self.__eq__(other) + + def __repr__(self) -> str: + if not self.__repr: + self.__repr = ( + f"{{{', '.join(map(str, sorted(self.processor_ids)))}}}:" + f"{{{', '.join(map(str, sorted(self.link_ids)))}}}") + return self.__repr + + def __str__(self) -> str: + return self.__repr__() + + def _calc_spinnaker_route(self) -> int: + """ + Convert a routing table entry represented in software to a + binary routing table entry usable on the machine. + + :rtype: int + """ + route_entry = 0 + assert self._processor_ids is not None + for processor_id in self._processor_ids: + route_entry |= (1 << (Router.MAX_LINKS_PER_ROUTER + processor_id)) + assert self._link_ids is not None + for link_id in self._link_ids: + route_entry |= (1 << link_id) + return route_entry + + def _calc_routing_ids(self) -> Tuple[FrozenSet[int], FrozenSet[int]]: + """ + Convert a binary routing table entry usable on the machine to lists of + route IDs usable in a routing table entry represented in software. + + :rtype: tuple(frozenset(int), frozenset(int)) + """ + processor_ids = (pi for pi in range(0, Router.MAX_CORES_PER_ROUTER) + if self._spinnaker_route & 1 << + (Router.MAX_LINKS_PER_ROUTER + pi)) + link_ids = (li for li in range(0, Router.MAX_LINKS_PER_ROUTER) + if self._spinnaker_route & 1 << li) + return frozenset(processor_ids), frozenset(link_ids) diff --git a/unittests/test_routing_entry.py b/unittests/test_routing_entry.py new file mode 100644 index 00000000..9dd98fb9 --- /dev/null +++ b/unittests/test_routing_entry.py @@ -0,0 +1,145 @@ +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pickle +import unittest +from spinn_machine import RoutingEntry +from spinn_machine.config_setup import unittest_setup +from spinn_machine.exceptions import SpinnMachineInvalidParameterException + + +class TestRoutingEntry(unittest.TestCase): + + def setUp(self): + unittest_setup() + + def test_creating_new_routing_entry(self): + link_ids = list() + proc_ids = list() + for i in range(6): + link_ids.append(i) + for i in range(18): + proc_ids.append(i) + a_multicast = RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, + defaultable=True) + + self.assertEqual(a_multicast.link_ids, set(link_ids)) + self.assertEqual(a_multicast.processor_ids, set(proc_ids)) + # While we're here, let's check a few other basic operations + self.assertEqual(str(a_multicast), + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11," + " 12, 13, 14, 15, 16, 17}:{0, 1, 2, 3, 4, 5}") + self.assertEqual( + a_multicast, + pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) + hash(a_multicast) + + def test_spinnaker_route(self): + multicast1 = RoutingEntry(processor_ids=[1, 3, 4, 16], + link_ids=[2, 3, 5]) + self.assertEqual(4196012, multicast1.spinnaker_route) + multicast2 = RoutingEntry(spinnaker_route=4196012) + self.assertEqual(multicast2.processor_ids, {1, 3, 4, 16}) + # Third test getting the link_ids first + multicast3 = RoutingEntry(spinnaker_route=4196012) + self.assertEqual(multicast3.link_ids, {2, 3, 5}) + self.assertEqual(multicast3.processor_ids, {1, 3, 4, 16}) + + def test_merger(self): + link_ids = list() + link_ids2 = list() + proc_ids = list() + proc_ids2 = list() + for i in range(3): + link_ids.append(i) + for i in range(3, 6): + link_ids2.append(i) + for i in range(9): + proc_ids.append(i) + for i in range(9, 18): + proc_ids2.append(i) + a_multicast = RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, + defaultable=True) + b_multicast = RoutingEntry( + processor_ids=proc_ids2, link_ids=link_ids2, + defaultable=True) + + result_multicast = a_multicast.merge(b_multicast) + comparison_link_ids = list() + comparison_proc_ids = list() + for i in range(6): + comparison_link_ids.append(i) + self.assertEqual(link_ids + link_ids2, comparison_link_ids) + for i in range(18): + comparison_proc_ids.append(i) + self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) + + self.assertEqual(result_multicast.link_ids, set(comparison_link_ids)) + self.assertEqual(result_multicast.processor_ids, + set(comparison_proc_ids)) + + def test_merger_with_different_defaultable(self): + link_ids = list() + link_ids2 = list() + proc_ids = list() + proc_ids2 = list() + for i in range(3): + link_ids.append(i) + for i in range(3, 6): + link_ids2.append(i) + for i in range(9): + proc_ids.append(i) + for i in range(9, 18): + proc_ids2.append(i) + a_multicast = RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, + defaultable=True) + b_multicast = RoutingEntry( + processor_ids=proc_ids2, link_ids=link_ids2, + defaultable=False) + + result_multicast = a_multicast.merge(b_multicast) + comparison_link_ids = list() + comparison_proc_ids = list() + for i in range(6): + comparison_link_ids.append(i) + self.assertEqual(link_ids + link_ids2, comparison_link_ids) + for i in range(18): + comparison_proc_ids.append(i) + self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) + + self.assertEqual(result_multicast.link_ids, set(comparison_link_ids)) + self.assertEqual(result_multicast.processor_ids, + set(comparison_proc_ids)) + self.assertNotEqual(result_multicast, a_multicast) + self.assertNotEqual(result_multicast, b_multicast) + + """ + From FixedRouteEntry use or loose + def test_fixed_route_errors(self): + with self.assertRaises(SpinnMachineAlreadyExistsException) as e: + FixedRouteEntry([1, 2, 2], [2, 3, 4]) + self.assertEqual(e.exception.item, "processor ID") + self.assertEqual(e.exception.value, "2") + with self.assertRaises(SpinnMachineAlreadyExistsException) as e: + FixedRouteEntry([1, 2, 3], [2, 3, 2]) + self.assertEqual(e.exception.item, "link ID") + self.assertEqual(e.exception.value, "2") + """ + + +if __name__ == '__main__': + unittest.main() From 01308ef7d29c9d690de60fcd55c2304e5cc749c2 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 27 Mar 2024 07:16:52 +0000 Subject: [PATCH 02/13] replace FixedRoueEntry with Shared RoutingEntry class --- spinn_machine/__init__.py | 3 +- spinn_machine/fixed_route_entry.py | 75 ----------------------------- spinn_machine/router.py | 6 +-- unittests/test_fixed_route_entry.py | 44 ----------------- 4 files changed, 4 insertions(+), 124 deletions(-) delete mode 100644 spinn_machine/fixed_route_entry.py delete mode 100644 unittests/test_fixed_route_entry.py diff --git a/spinn_machine/__init__.py b/spinn_machine/__init__.py index be678296..4dc33a33 100644 --- a/spinn_machine/__init__.py +++ b/spinn_machine/__init__.py @@ -87,10 +87,9 @@ from .routing_entry import RoutingEntry from .spinnaker_triad_geometry import SpiNNakerTriadGeometry from .virtual_machine import virtual_machine -from .fixed_route_entry import FixedRouteEntry -__all__ = ["Chip", "CoreSubset", "CoreSubsets", "FixedRouteEntry", +__all__ = ["Chip", "CoreSubset", "CoreSubsets", "FrozenCoreSubsets", "Link", "Machine", "MulticastRoutingEntry", "Router", "RoutingEntry", "SpiNNakerTriadGeometry", "virtual_machine"] diff --git a/spinn_machine/fixed_route_entry.py b/spinn_machine/fixed_route_entry.py deleted file mode 100644 index 6fe6f87e..00000000 --- a/spinn_machine/fixed_route_entry.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2017 The University of Manchester -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from typing import FrozenSet, Iterable, Optional -from .exceptions import SpinnMachineAlreadyExistsException - - -class FixedRouteEntry(object): - """ - Describes the sole entry in a SpiNNaker chip's fixed route routing table. - There is only one fixed route entry per chip. - """ - __slots__ = ( - # the processors IDs for this route - "_processor_ids", - # the link IDs for this route - "_link_ids", - "__repr") - - def __init__(self, processor_ids: Iterable[int], link_ids: Iterable[int]): - """ - :param iterable(int) processor_ids: - :param iterable(int) link_ids: - """ - self.__repr: Optional[str] = None - # Add processor IDs, checking that there is only one of each - self._processor_ids = frozenset(processor_ids) - self.__check_dupes(processor_ids, "processor ID") - - # Add link IDs, checking that there is only one of each - self._link_ids = frozenset(link_ids) - self.__check_dupes(link_ids, "link ID") - - @staticmethod - def __check_dupes(sequence: Iterable[int], name: str): - check = set() - for _id in sequence: - if _id in check: - raise SpinnMachineAlreadyExistsException(name, str(_id)) - check.add(_id) - - @property - def processor_ids(self) -> FrozenSet[int]: - """ - The destination processor IDs. - - :rtype: frozenset(int) - """ - return self._processor_ids - - @property - def link_ids(self) -> FrozenSet[int]: - """ - The destination link IDs. - - :rtype: frozenset(int) - """ - return self._link_ids - - def __repr__(self) -> str: - if not self.__repr: - self.__repr = ("{%s}:{%s}" % ( - ", ".join(map(str, sorted(self._link_ids))), - ", ".join(map(str, sorted(self._processor_ids))))) - return self.__repr diff --git a/spinn_machine/router.py b/spinn_machine/router.py index 38c6eafa..c5b24fce 100644 --- a/spinn_machine/router.py +++ b/spinn_machine/router.py @@ -18,8 +18,8 @@ SpinnMachineAlreadyExistsException, SpinnMachineInvalidParameterException) if TYPE_CHECKING: from .link import Link - from .fixed_route_entry import FixedRouteEntry from .multicast_routing_entry import MulticastRoutingEntry + from .routing_entry import RoutingEntry class Router(object): @@ -146,7 +146,7 @@ def n_available_multicast_entries(self) -> int: @staticmethod def convert_routing_table_entry_to_spinnaker_route( routing_table_entry: Union[ - MulticastRoutingEntry, FixedRouteEntry]) -> int: + MulticastRoutingEntry, RoutingEntry]) -> int: """ Convert a routing table entry represented in software to a binary routing table entry usable on the machine. @@ -154,7 +154,7 @@ def convert_routing_table_entry_to_spinnaker_route( :param routing_table_entry: The entry to convert :type routing_table_entry: ~spinn_machine.MulticastRoutingEntry or - ~spinn_machine.FixedRouteEntry + ~spinn_machine.RoutingEntry :rtype: int """ route_entry = 0 diff --git a/unittests/test_fixed_route_entry.py b/unittests/test_fixed_route_entry.py deleted file mode 100644 index 8ee483cc..00000000 --- a/unittests/test_fixed_route_entry.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2017 The University of Manchester -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -from spinn_machine import FixedRouteEntry -from spinn_machine.config_setup import unittest_setup -from spinn_machine.exceptions import SpinnMachineAlreadyExistsException - - -class TestingFixedRouteEntries(unittest.TestCase): - - def setUp(self): - unittest_setup() - - def test_fixed_route_creation(self): - fre = FixedRouteEntry([1, 2, 3], [2, 3, 4]) - self.assertEqual(fre.__repr__(), "{2, 3, 4}:{1, 2, 3}") - self.assertEqual(frozenset(fre.processor_ids), frozenset([1, 2, 3])) - self.assertEqual(frozenset(fre.link_ids), frozenset([2, 3, 4])) - - def test_fixed_route_errors(self): - with self.assertRaises(SpinnMachineAlreadyExistsException) as e: - FixedRouteEntry([1, 2, 2], [2, 3, 4]) - self.assertEqual(e.exception.item, "processor ID") - self.assertEqual(e.exception.value, "2") - with self.assertRaises(SpinnMachineAlreadyExistsException) as e: - FixedRouteEntry([1, 2, 3], [2, 3, 2]) - self.assertEqual(e.exception.item, "link ID") - self.assertEqual(e.exception.value, "2") - - -if __name__ == '__main__': - unittest.main() From 71ba06ec216dc5cf5810bfef075acc254d7f27f4 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Wed, 27 Mar 2024 17:24:58 +0000 Subject: [PATCH 03/13] replace MulticastRoutingTableByPartitionEntry with RoutingEntry --- spinn_machine/routing_entry.py | 59 ++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index 232d3524..42f87a70 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations -from typing import Any, FrozenSet, Iterable, Optional, Tuple, overload +from typing import Any, FrozenSet, Iterable, Optional, Tuple, overload, Union from spinn_machine.router import Router -from .exceptions import SpinnMachineAlreadyExistsException +from .exceptions import (SpinnMachineInvalidParameterException) class RoutingEntry(object): @@ -27,23 +27,30 @@ class RoutingEntry(object): "_link_ids", "_spinnaker_route", "__repr") @overload - def __init__(self, *, processor_ids: Iterable[int], - link_ids: Iterable[int], - defaultable: bool = False, + def __init__(self, *, processor_ids: Union[int, Iterable[int]], + link_ids: Union[int, Iterable[int]], + incoming_processor: Optional[int] = None, + incoming_link: Optional[int] = None, + defaultable: None = None, spinnaker_route: None = None): ... @overload def __init__(self, int, *, processor_ids: None = None, link_ids: None = None, - defaultable: bool = False, + incoming_processor: None = None, + incoming_link: None = None, + defaultable: Optional[bool] = False, spinnaker_route: int): ... # pylint: disable=too-many-arguments - def __init__(self, *, processor_ids: Optional[Iterable[int]] = None, - link_ids: Optional[Iterable[int]] = None, - defaultable: bool = False, + def __init__(self, *, + processor_ids: Optional[Union[int, Iterable[int]]] = None, + link_ids: Optional[Union[int, Iterable[int]]] = None, + incoming_processor: Optional[int] = None, + incoming_link: Optional[int] = None, + defaultable: Optional[bool] = None, spinnaker_route: Optional[int] = None) -> None: """ .. note:: @@ -72,26 +79,38 @@ def __init__(self, *, processor_ids: Optional[Iterable[int]] = None, # Add processor IDs, ignore duplicates if spinnaker_route is None: - assert processor_ids is not None - assert link_ids is not None + if isinstance(processor_ids, int): + processor_ids = [processor_ids] + if isinstance(link_ids, int): + link_ids = [link_ids] self._processor_ids: Optional[FrozenSet[int]] = \ frozenset(processor_ids) self._link_ids: Optional[FrozenSet[int]] = frozenset(link_ids) self._spinnaker_route: int = self._calc_spinnaker_route() + if incoming_link is None: + self._defaultable = False + else: + if incoming_processor is None: + if len(self.link_ids) != 1: + self._defaultable = False + else: + link_id = next(iter(link_ids)) + self._defaultable = ( + ((incoming_link + 3) % 6) == link_id) + else: + raise SpinnMachineInvalidParameterException( + "incoming_processor", incoming_processor, + "The incoming direction for a path can only be from " + "either one link or one processors, not both") else: + assert processor_ids is None + assert link_ids is None + assert incoming_link is None + assert incoming_processor is None self._spinnaker_route = spinnaker_route self._processor_ids = None self._link_ids = None - # TODO from FixedRouteEntry use or loose - @staticmethod - def __check_dupes(sequence: Iterable[int], name: str): - check = set() - for _id in sequence: - if _id in check: - raise SpinnMachineAlreadyExistsException(name, str(_id)) - check.add(_id) - @property def processor_ids(self) -> FrozenSet[int]: """ From ee530f5f6369a8eea03cc0f75cad093c29fc8a97 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 07:41:07 +0000 Subject: [PATCH 04/13] MulticastRoutingEntry wraps a RoutingEntry --- spinn_machine/multicast_routing_entry.py | 177 +++------------------- spinn_machine/routing_entry.py | 7 +- unittests/test_multicast_routing_entry.py | 86 +++++------ unittests/test_router.py | 20 +-- unittests/test_routing_entry.py | 4 +- 5 files changed, 70 insertions(+), 224 deletions(-) diff --git a/spinn_machine/multicast_routing_entry.py b/spinn_machine/multicast_routing_entry.py index 755a9fff..a6812896 100644 --- a/spinn_machine/multicast_routing_entry.py +++ b/spinn_machine/multicast_routing_entry.py @@ -13,71 +13,29 @@ # limitations under the License. from __future__ import annotations from typing import Any, FrozenSet, Iterable, Optional, Tuple, overload -from spinn_machine.router import Router from .exceptions import SpinnMachineInvalidParameterException +from .routing_entry import RoutingEntry class MulticastRoutingEntry(object): """ - Represents an entry in a SpiNNaker chip's multicast routing table. - There can be up to 1024 such entries per chip, but some may be reserved - for system purposes. + Represents an entry in a SpiNNaker chip's routing table, + including the key and mask """ __slots__ = ( - "_routing_entry_key", "_mask", "_defaultable", "_processor_ids", - "_link_ids", "_spinnaker_route", "__repr") + "_routing_entry_key", "_mask", "_routing_entry") - @overload - def __init__(self, routing_entry_key: int, mask: int, *, - processor_ids: Iterable[int], - link_ids: Iterable[int], - defaultable: bool = False, - spinnaker_route: None = None): - ... - - @overload - def __init__(self, routing_entry_key: int, mask: int, *, - processor_ids: None = None, - link_ids: None = None, - defaultable: bool = False, - spinnaker_route: int): - ... - - # pylint: disable=too-many-arguments - def __init__(self, routing_entry_key: int, mask: int, *, - processor_ids: Optional[Iterable[int]] = None, - link_ids: Optional[Iterable[int]] = None, - defaultable: bool = False, - spinnaker_route: Optional[int] = None) -> None: + def __init__(self, routing_entry_key: int, mask: int, + routing_entry: RoutingEntry): """ - .. note:: - The processor_ids and link_ids parameters are only optional if a - spinnaker_route is provided. If a spinnaker_route is provided - the processor_ids and link_ids parameters are ignored. - :param int routing_entry_key: The routing key_combo :param int mask: The route key_combo mask - :param processor_ids: The destination processor IDs - :type processor_ids: iterable(int) or None - :param link_ids: The destination link IDs - :type link_ids: iterable(int) or None - :param bool defaultable: - If this entry is defaultable (it receives packets - from its directly opposite route position) - :param spinnaker_route: - The processor_ids and link_ids expressed as a single int. - :type spinnaker_route: int or None - :raise spinn_machine.exceptions.SpinnMachineAlreadyExistsException: - * If processor_ids contains the same ID more than once - * If link_ids contains the same ID more than once - :raise TypeError: if no spinnaker_route provided and either - processor_ids or link_ids is missing or `None` + :param RoutingEntry: routing_entry """ self._routing_entry_key: int = routing_entry_key self._mask: int = mask - self._defaultable: bool = defaultable - self.__repr: Optional[str] = None + self._routing_entry = routing_entry if (routing_entry_key & mask) != routing_entry_key: raise SpinnMachineInvalidParameterException( @@ -87,19 +45,6 @@ def __init__(self, routing_entry_key: int, mask: int, *, " is determined to be an error in the tool chain. Please " "correct this and try again.") - # Add processor IDs, ignore duplicates - if spinnaker_route is None: - assert processor_ids is not None - assert link_ids is not None - self._processor_ids: Optional[FrozenSet[int]] = \ - frozenset(processor_ids) - self._link_ids: Optional[FrozenSet[int]] = frozenset(link_ids) - self._spinnaker_route: int = self._calc_spinnaker_route() - else: - self._spinnaker_route = spinnaker_route - self._processor_ids = None - self._link_ids = None - @property def routing_entry_key(self) -> int: """ @@ -118,28 +63,6 @@ def mask(self) -> int: """ return self._mask - @property - def processor_ids(self) -> FrozenSet[int]: - """ - The destination processor IDs. - - :rtype: frozenset(int) - """ - if self._processor_ids is None: - self._processor_ids, self._link_ids = self._calc_routing_ids() - return self._processor_ids - - @property - def link_ids(self) -> FrozenSet[int]: - """ - The destination link IDs. - - :rtype: frozenset(int) - """ - if self._link_ids is None: - self._processor_ids, self._link_ids = self._calc_routing_ids() - return self._link_ids - @property def defaultable(self) -> bool: """ @@ -151,7 +74,7 @@ def defaultable(self) -> bool: :rtype: bool """ - return self._defaultable + return self._routing_entry.defaultable @property def spinnaker_route(self) -> int: @@ -160,7 +83,11 @@ def spinnaker_route(self) -> int: :rtype: int """ - return self._spinnaker_route + return self._routing_entry.spinnaker_route + + @property + def entry(self) -> RoutingEntry: + return self._routing_entry def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: """ @@ -187,25 +114,9 @@ def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: "other.mask", hex(other.mask), f"The mask does not match 0x{self.mask:x}") + entry = self.entry.merge(other.entry) return MulticastRoutingEntry( - self.routing_entry_key, self.mask, - processor_ids=(self.processor_ids | other.processor_ids), - link_ids=(self.link_ids | other.link_ids), - defaultable=(self._defaultable and other.defaultable)) - - def __add__(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: - """ - Allows overloading of `+` to merge two entries together. - See :py:meth:`merge` - """ - return self.merge(other) - - def __or__(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: - """ - Allows overloading of `|` to merge two entries together. - See :py:meth:`merge` - """ - return self.merge(other) + self.routing_entry_key, self.mask, entry) def __eq__(self, other_entry: Any) -> bool: if not isinstance(other_entry, MulticastRoutingEntry): @@ -214,66 +125,18 @@ def __eq__(self, other_entry: Any) -> bool: return False if self.mask != other_entry.mask: return False - if self._spinnaker_route != other_entry._spinnaker_route: - return False - return (self._defaultable == other_entry.defaultable) + return (self.entry == other_entry.entry) def __hash__(self) -> int: return (self.routing_entry_key * 13 + self.mask * 19 + - self._spinnaker_route * 29 * int(self._defaultable) * 131) + hash(self._routing_entry)) def __ne__(self, other: Any) -> bool: return not self.__eq__(other) def __repr__(self) -> str: - if not self.__repr: - self.__repr = ( - f"{self._routing_entry_key}:{self._mask}:{self._defaultable}" - f":{{{', '.join(map(str, sorted(self.processor_ids)))}}}:" - f"{{{', '.join(map(str, sorted(self.link_ids)))}}}") - - return self.__repr + return (f"{self._routing_entry_key}:{self._mask}:" + f"{repr(self._routing_entry)}") def __str__(self) -> str: return self.__repr__() - - def __getstate__(self): - return dict( - (slot, getattr(self, slot)) - for slot in self.__slots__ - if hasattr(self, slot) - ) - - def __setstate__(self, state): - for slot, value in state.items(): - setattr(self, slot, value) - - def _calc_spinnaker_route(self) -> int: - """ - Convert a routing table entry represented in software to a - binary routing table entry usable on the machine. - - :rtype: int - """ - route_entry = 0 - assert self._processor_ids is not None - for processor_id in self._processor_ids: - route_entry |= (1 << (Router.MAX_LINKS_PER_ROUTER + processor_id)) - assert self._link_ids is not None - for link_id in self._link_ids: - route_entry |= (1 << link_id) - return route_entry - - def _calc_routing_ids(self) -> Tuple[FrozenSet[int], FrozenSet[int]]: - """ - Convert a binary routing table entry usable on the machine to lists of - route IDs usable in a routing table entry represented in software. - - :rtype: tuple(frozenset(int), frozenset(int)) - """ - processor_ids = (pi for pi in range(0, Router.MAX_CORES_PER_ROUTER) - if self._spinnaker_route & 1 << - (Router.MAX_LINKS_PER_ROUTER + pi)) - link_ids = (li for li in range(0, Router.MAX_LINKS_PER_ROUTER) - if self._spinnaker_route & 1 << li) - return frozenset(processor_ids), frozenset(link_ids) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index 42f87a70..253abddd 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -74,7 +74,6 @@ def __init__(self, *, :raise TypeError: if no spinnaker_route provided and either processor_ids or link_ids is missing or `None` """ - self._defaultable: bool = defaultable self.__repr: Optional[str] = None # Add processor IDs, ignore duplicates @@ -102,6 +101,9 @@ def __init__(self, *, "incoming_processor", incoming_processor, "The incoming direction for a path can only be from " "either one link or one processors, not both") + # TODO remove this GIGO! + if defaultable is not None: + self._defaultable: bool = defaultable else: assert processor_ids is None assert link_ids is None @@ -110,6 +112,7 @@ def __init__(self, *, self._spinnaker_route = spinnaker_route self._processor_ids = None self._link_ids = None + self._defaultable = defaultable @property def processor_ids(self) -> FrozenSet[int]: @@ -193,6 +196,8 @@ def __repr__(self) -> str: self.__repr = ( f"{{{', '.join(map(str, sorted(self.processor_ids)))}}}:" f"{{{', '.join(map(str, sorted(self.link_ids)))}}}") + if self._defaultable: + self.__repr += "(defaultable)" return self.__repr def __str__(self) -> str: diff --git a/unittests/test_multicast_routing_entry.py b/unittests/test_multicast_routing_entry.py index 9df22106..482a66bb 100644 --- a/unittests/test_multicast_routing_entry.py +++ b/unittests/test_multicast_routing_entry.py @@ -14,7 +14,7 @@ import pickle import unittest -from spinn_machine import MulticastRoutingEntry +from spinn_machine import MulticastRoutingEntry, RoutingEntry from spinn_machine.config_setup import unittest_setup from spinn_machine.exceptions import SpinnMachineInvalidParameterException @@ -34,17 +34,18 @@ def test_creating_new_multicast_routing_entry(self): key = 1 mask = 1 a_multicast = MulticastRoutingEntry( - key, mask, processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) + key, mask, RoutingEntry(processor_ids=proc_ids, link_ids=link_ids, + defaultable=True)) self.assertEqual(a_multicast.routing_entry_key, key) - self.assertEqual(a_multicast.link_ids, set(link_ids)) + self.assertEqual(a_multicast.entry.link_ids, set(link_ids)) self.assertEqual(a_multicast.mask, mask) - self.assertEqual(a_multicast.processor_ids, set(proc_ids)) + self.assertEqual(a_multicast.entry.processor_ids, set(proc_ids)) # While we're here, let's check a few other basic operations - self.assertEqual(str(a_multicast), - "1:1:True:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11," - " 12, 13, 14, 15, 16, 17}:{0, 1, 2, 3, 4, 5}") + self.assertEqual( + str(a_multicast), + "1:1:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}:" + "{0, 1, 2, 3, 4, 5}(defaultable)") self.assertEqual( a_multicast, pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) @@ -52,18 +53,20 @@ def test_creating_new_multicast_routing_entry(self): def test_bad_key_mask(self): with self.assertRaises(SpinnMachineInvalidParameterException): - MulticastRoutingEntry(1, 2) + MulticastRoutingEntry(1, 2, None) def test_spinnaker_route(self): - multicast1 = MulticastRoutingEntry(1, 1, processor_ids=[1, 3, 4, 16], - link_ids=[2, 3, 5]) + multicast1 = MulticastRoutingEntry(1, 1, RoutingEntry( + processor_ids=[1, 3, 4, 16], link_ids=[2, 3, 5])) self.assertEqual(4196012, multicast1.spinnaker_route) - multicast2 = MulticastRoutingEntry(1, 1, spinnaker_route=4196012) - self.assertEqual(multicast2.processor_ids, {1, 3, 4, 16}) + multicast2 = MulticastRoutingEntry( + 1, 1, RoutingEntry(spinnaker_route=4196012)) + self.assertEqual(multicast2.entry.processor_ids, {1, 3, 4, 16}) # Third test getting the link_ids first - multicast3 = MulticastRoutingEntry(1, 1, spinnaker_route=4196012) - self.assertEqual(multicast3.link_ids, {2, 3, 5}) - self.assertEqual(multicast3.processor_ids, {1, 3, 4, 16}) + multicast3 = MulticastRoutingEntry(1, 1, RoutingEntry( + spinnaker_route=4196012)) + self.assertEqual(multicast3.entry.link_ids, {2, 3, 5}) + self.assertEqual(multicast3.entry.processor_ids, {1, 3, 4, 16}) def test_merger(self): link_ids = list() @@ -81,11 +84,10 @@ def test_merger(self): key = 1 mask = 1 a_multicast = MulticastRoutingEntry( - key, mask, processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) - b_multicast = MulticastRoutingEntry( - key, mask, processor_ids=proc_ids2, link_ids=link_ids2, - defaultable=True) + key, mask, RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) + b_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( + processor_ids=proc_ids2, link_ids=link_ids2, defaultable=True)) result_multicast = a_multicast.merge(b_multicast) comparison_link_ids = list() @@ -98,9 +100,10 @@ def test_merger(self): self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) self.assertEqual(result_multicast.routing_entry_key, key) - self.assertEqual(result_multicast.link_ids, set(comparison_link_ids)) + self.assertEqual(result_multicast.entry.link_ids, + set(comparison_link_ids)) self.assertEqual(result_multicast.mask, mask) - self.assertEqual(result_multicast.processor_ids, + self.assertEqual(result_multicast.entry.processor_ids, set(comparison_proc_ids)) def test_merger_with_different_defaultable(self): @@ -118,12 +121,10 @@ def test_merger_with_different_defaultable(self): proc_ids2.append(i) key = 1 mask = 1 - a_multicast = MulticastRoutingEntry( - key, mask, processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) - b_multicast = MulticastRoutingEntry( - key, mask, processor_ids=proc_ids2, link_ids=link_ids2, - defaultable=False) + a_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) + b_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( + processor_ids=proc_ids2, link_ids=link_ids2, defaultable=False)) result_multicast = a_multicast.merge(b_multicast) comparison_link_ids = list() @@ -136,12 +137,11 @@ def test_merger_with_different_defaultable(self): self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) self.assertEqual(result_multicast.routing_entry_key, key) - self.assertEqual(result_multicast.link_ids, set(comparison_link_ids)) + self.assertEqual(result_multicast.entry.link_ids, + set(comparison_link_ids)) self.assertEqual(result_multicast.mask, mask) - self.assertEqual(result_multicast.processor_ids, + self.assertEqual(result_multicast.entry.processor_ids, set(comparison_proc_ids)) - self.assertEqual(result_multicast, a_multicast + b_multicast) - self.assertEqual(result_multicast, a_multicast | b_multicast) self.assertNotEqual(result_multicast, a_multicast) self.assertNotEqual(result_multicast, b_multicast) @@ -160,12 +160,10 @@ def test_merger_with_invalid_parameter_key(self): proc_ids2.append(i) key_combo = 1 mask = 1 - a_multicast = MulticastRoutingEntry( - key_combo, mask, processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) - b_multicast = MulticastRoutingEntry( - key_combo + 1, mask + 1, processor_ids=proc_ids2, - link_ids=link_ids2, defaultable=True) + entry = RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, defaultable=True) + a_multicast = MulticastRoutingEntry(key_combo, mask, entry) + b_multicast = MulticastRoutingEntry(key_combo + 1, mask + 1, entry) with self.assertRaises(SpinnMachineInvalidParameterException) as e: a_multicast.merge(b_multicast) self.assertEqual(e.exception.parameter, "other.key") @@ -187,12 +185,10 @@ def test_merger_with_invalid_parameter_mask(self): proc_ids2.append(i) key = 0 mask = 2 - a_multicast = MulticastRoutingEntry( - key, mask, processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) - b_multicast = MulticastRoutingEntry( - key, 4, processor_ids=proc_ids2, link_ids=link_ids2, - defaultable=True) + a_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) + b_multicast = MulticastRoutingEntry(key, 4, RoutingEntry( + processor_ids=proc_ids2, link_ids=link_ids2, defaultable=True)) self.assertNotEqual(a_multicast, b_multicast) self.assertNotEqual(a_multicast, "foo") with self.assertRaises(SpinnMachineInvalidParameterException): diff --git a/unittests/test_router.py b/unittests/test_router.py index c96be8f4..696f64a5 100644 --- a/unittests/test_router.py +++ b/unittests/test_router.py @@ -13,7 +13,7 @@ # limitations under the License. import unittest -from spinn_machine import Router, Link, MulticastRoutingEntry +from spinn_machine import Router, Link from spinn_machine.config_setup import unittest_setup from spinn_machine.exceptions import ( SpinnMachineAlreadyExistsException, SpinnMachineInvalidParameterException) @@ -72,24 +72,6 @@ def test_creating_new_router_with_duplicate_links(self): with self.assertRaises(SpinnMachineAlreadyExistsException): Router(links, 1024) - def test_convert_to_route(self): - e = MulticastRoutingEntry(28, 60, processor_ids=[4, 5, 7], - link_ids=[1, 3, 5], defaultable=True) - r = Router.convert_routing_table_entry_to_spinnaker_route(e) - self.assertEqual(r, 11306) - - def test_bad_processor(self): - e = MulticastRoutingEntry(28, 60, processor_ids=[4, 5, -1], - link_ids=[1, 3, 5], defaultable=True) - with self.assertRaises(SpinnMachineInvalidParameterException): - Router.convert_routing_table_entry_to_spinnaker_route(e) - - def test_bad_link(self): - e = MulticastRoutingEntry(28, 60, processor_ids=[4, 5, 7], - link_ids=[1, 3, 15], defaultable=True) - with self.assertRaises(SpinnMachineInvalidParameterException): - Router.convert_routing_table_entry_to_spinnaker_route(e) - if __name__ == '__main__': unittest.main() diff --git a/unittests/test_routing_entry.py b/unittests/test_routing_entry.py index 9dd98fb9..84b816de 100644 --- a/unittests/test_routing_entry.py +++ b/unittests/test_routing_entry.py @@ -39,8 +39,8 @@ def test_creating_new_routing_entry(self): self.assertEqual(a_multicast.processor_ids, set(proc_ids)) # While we're here, let's check a few other basic operations self.assertEqual(str(a_multicast), - "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11," - " 12, 13, 14, 15, 16, 17}:{0, 1, 2, 3, 4, 5}") + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, " + "15, 16, 17}:{0, 1, 2, 3, 4, 5}(defaultable)") self.assertEqual( a_multicast, pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) From 9f257fd067a1ae671531a893f516f7f9cbd3b3c0 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 10:51:46 +0000 Subject: [PATCH 05/13] use MulticastRoutingEntry instead of RTEntry --- spinn_machine/multicast_routing_entry.py | 30 +++++++++++------------ unittests/test_multicast_routing_entry.py | 6 ++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/spinn_machine/multicast_routing_entry.py b/spinn_machine/multicast_routing_entry.py index a6812896..95f661ec 100644 --- a/spinn_machine/multicast_routing_entry.py +++ b/spinn_machine/multicast_routing_entry.py @@ -24,35 +24,35 @@ class MulticastRoutingEntry(object): """ __slots__ = ( - "_routing_entry_key", "_mask", "_routing_entry") + "_key", "_mask", "_routing_entry") - def __init__(self, routing_entry_key: int, mask: int, + def __init__(self, key: int, mask: int, routing_entry: RoutingEntry): """ - :param int routing_entry_key: The routing key_combo + :param int key: The routing key_combo :param int mask: The route key_combo mask :param RoutingEntry: routing_entry """ - self._routing_entry_key: int = routing_entry_key + self._key: int = key self._mask: int = mask self._routing_entry = routing_entry - if (routing_entry_key & mask) != routing_entry_key: + if (key & mask) != key: raise SpinnMachineInvalidParameterException( "key_mask_combo and mask", - f"{routing_entry_key} and {mask}", + f"{key} and {mask}", "The key combo is changed when masked with the mask. This" " is determined to be an error in the tool chain. Please " "correct this and try again.") @property - def routing_entry_key(self) -> int: + def key(self) -> int: """ The routing key. :rtype: int """ - return self._routing_entry_key + return self._key @property def mask(self) -> int: @@ -105,10 +105,10 @@ def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: :raise spinn_machine.exceptions.SpinnMachineInvalidParameterException: If the key and mask of the other entry do not match """ - if other.routing_entry_key != self.routing_entry_key: + if other.key != self.key: raise SpinnMachineInvalidParameterException( - "other.key", hex(other.routing_entry_key), - f"The key does not match 0x{self.routing_entry_key:x}") + "other.key", hex(other.key), + f"The key does not match 0x{self.key:x}") if other.mask != self.mask: raise SpinnMachineInvalidParameterException( "other.mask", hex(other.mask), @@ -116,26 +116,26 @@ def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: entry = self.entry.merge(other.entry) return MulticastRoutingEntry( - self.routing_entry_key, self.mask, entry) + self.key, self.mask, entry) def __eq__(self, other_entry: Any) -> bool: if not isinstance(other_entry, MulticastRoutingEntry): return False - if self.routing_entry_key != other_entry.routing_entry_key: + if self.key != other_entry.key: return False if self.mask != other_entry.mask: return False return (self.entry == other_entry.entry) def __hash__(self) -> int: - return (self.routing_entry_key * 13 + self.mask * 19 + + return (self.key * 13 + self.mask * 19 + hash(self._routing_entry)) def __ne__(self, other: Any) -> bool: return not self.__eq__(other) def __repr__(self) -> str: - return (f"{self._routing_entry_key}:{self._mask}:" + return (f"{self._key}:{self._mask}:" f"{repr(self._routing_entry)}") def __str__(self) -> str: diff --git a/unittests/test_multicast_routing_entry.py b/unittests/test_multicast_routing_entry.py index 482a66bb..d7a9b062 100644 --- a/unittests/test_multicast_routing_entry.py +++ b/unittests/test_multicast_routing_entry.py @@ -37,7 +37,7 @@ def test_creating_new_multicast_routing_entry(self): key, mask, RoutingEntry(processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) - self.assertEqual(a_multicast.routing_entry_key, key) + self.assertEqual(a_multicast.key, key) self.assertEqual(a_multicast.entry.link_ids, set(link_ids)) self.assertEqual(a_multicast.mask, mask) self.assertEqual(a_multicast.entry.processor_ids, set(proc_ids)) @@ -99,7 +99,7 @@ def test_merger(self): comparison_proc_ids.append(i) self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) - self.assertEqual(result_multicast.routing_entry_key, key) + self.assertEqual(result_multicast.key, key) self.assertEqual(result_multicast.entry.link_ids, set(comparison_link_ids)) self.assertEqual(result_multicast.mask, mask) @@ -136,7 +136,7 @@ def test_merger_with_different_defaultable(self): comparison_proc_ids.append(i) self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) - self.assertEqual(result_multicast.routing_entry_key, key) + self.assertEqual(result_multicast.key, key) self.assertEqual(result_multicast.entry.link_ids, set(comparison_link_ids)) self.assertEqual(result_multicast.mask, mask) From 2b6838d720f14ec539dfc0c384a9b0f43634581f Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 11:23:28 +0000 Subject: [PATCH 06/13] expose processor and link ids --- spinn_machine/multicast_routing_entry.py | 28 +++++++++++++++++++---- unittests/test_multicast_routing_entry.py | 26 ++++++++++----------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/spinn_machine/multicast_routing_entry.py b/spinn_machine/multicast_routing_entry.py index 95f661ec..4d263f64 100644 --- a/spinn_machine/multicast_routing_entry.py +++ b/spinn_machine/multicast_routing_entry.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations -from typing import Any, FrozenSet, Iterable, Optional, Tuple, overload +from typing import Any, FrozenSet from .exceptions import SpinnMachineInvalidParameterException from .routing_entry import RoutingEntry @@ -63,6 +63,24 @@ def mask(self) -> int: """ return self._mask + @property + def processor_ids(self) -> FrozenSet[int]: + """ + The destination processor IDs. + + :rtype: frozenset(int) + """ + return self._routing_entry.processor_ids + + @property + def link_ids(self) -> FrozenSet[int]: + """ + The destination link IDs. + + :rtype: frozenset(int) + """ + return self._routing_entry.link_ids + @property def defaultable(self) -> bool: """ @@ -114,9 +132,9 @@ def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: "other.mask", hex(other.mask), f"The mask does not match 0x{self.mask:x}") - entry = self.entry.merge(other.entry) + routing_entry = self._routing_entry.merge(other._routing_entry) return MulticastRoutingEntry( - self.key, self.mask, entry) + self.key, self.mask, routing_entry) def __eq__(self, other_entry: Any) -> bool: if not isinstance(other_entry, MulticastRoutingEntry): @@ -125,7 +143,7 @@ def __eq__(self, other_entry: Any) -> bool: return False if self.mask != other_entry.mask: return False - return (self.entry == other_entry.entry) + return (self._routing_entry == other_entry._routing_entry) def __hash__(self) -> int: return (self.key * 13 + self.mask * 19 + @@ -135,7 +153,7 @@ def __ne__(self, other: Any) -> bool: return not self.__eq__(other) def __repr__(self) -> str: - return (f"{self._key}:{self._mask}:" + return (f"{self._key}:{self._mask}:" f"{repr(self._routing_entry)}") def __str__(self) -> str: diff --git a/unittests/test_multicast_routing_entry.py b/unittests/test_multicast_routing_entry.py index d7a9b062..26489fac 100644 --- a/unittests/test_multicast_routing_entry.py +++ b/unittests/test_multicast_routing_entry.py @@ -34,18 +34,18 @@ def test_creating_new_multicast_routing_entry(self): key = 1 mask = 1 a_multicast = MulticastRoutingEntry( - key, mask, RoutingEntry(processor_ids=proc_ids, link_ids=link_ids, - defaultable=True)) + key, mask, RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) self.assertEqual(a_multicast.key, key) - self.assertEqual(a_multicast.entry.link_ids, set(link_ids)) + self.assertEqual(a_multicast.link_ids, set(link_ids)) self.assertEqual(a_multicast.mask, mask) - self.assertEqual(a_multicast.entry.processor_ids, set(proc_ids)) + self.assertEqual(a_multicast.processor_ids, set(proc_ids)) # While we're here, let's check a few other basic operations self.assertEqual( str(a_multicast), - "1:1:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}:" - "{0, 1, 2, 3, 4, 5}(defaultable)") + "1:1:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, " + "16, 17}:{0, 1, 2, 3, 4, 5}(defaultable)") self.assertEqual( a_multicast, pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) @@ -61,12 +61,12 @@ def test_spinnaker_route(self): self.assertEqual(4196012, multicast1.spinnaker_route) multicast2 = MulticastRoutingEntry( 1, 1, RoutingEntry(spinnaker_route=4196012)) - self.assertEqual(multicast2.entry.processor_ids, {1, 3, 4, 16}) + self.assertEqual(multicast2.processor_ids, {1, 3, 4, 16}) # Third test getting the link_ids first multicast3 = MulticastRoutingEntry(1, 1, RoutingEntry( spinnaker_route=4196012)) - self.assertEqual(multicast3.entry.link_ids, {2, 3, 5}) - self.assertEqual(multicast3.entry.processor_ids, {1, 3, 4, 16}) + self.assertEqual(multicast3.link_ids, {2, 3, 5}) + self.assertEqual(multicast3.processor_ids, {1, 3, 4, 16}) def test_merger(self): link_ids = list() @@ -100,10 +100,10 @@ def test_merger(self): self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) self.assertEqual(result_multicast.key, key) - self.assertEqual(result_multicast.entry.link_ids, + self.assertEqual(result_multicast.link_ids, set(comparison_link_ids)) self.assertEqual(result_multicast.mask, mask) - self.assertEqual(result_multicast.entry.processor_ids, + self.assertEqual(result_multicast.processor_ids, set(comparison_proc_ids)) def test_merger_with_different_defaultable(self): @@ -137,10 +137,10 @@ def test_merger_with_different_defaultable(self): self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) self.assertEqual(result_multicast.key, key) - self.assertEqual(result_multicast.entry.link_ids, + self.assertEqual(result_multicast.link_ids, set(comparison_link_ids)) self.assertEqual(result_multicast.mask, mask) - self.assertEqual(result_multicast.entry.processor_ids, + self.assertEqual(result_multicast.processor_ids, set(comparison_proc_ids)) self.assertNotEqual(result_multicast, a_multicast) self.assertNotEqual(result_multicast, b_multicast) From 6d4a59e3563890ab93a0e1344ca40a8a671d6cd3 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 11:41:38 +0000 Subject: [PATCH 07/13] style fixes --- spinn_machine/multicast_routing_entry.py | 5 +---- spinn_machine/routing_entry.py | 2 +- unittests/test_router.py | 3 +-- unittests/test_routing_entry.py | 1 - 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/spinn_machine/multicast_routing_entry.py b/spinn_machine/multicast_routing_entry.py index 4d263f64..7d8a6267 100644 --- a/spinn_machine/multicast_routing_entry.py +++ b/spinn_machine/multicast_routing_entry.py @@ -103,10 +103,6 @@ def spinnaker_route(self) -> int: """ return self._routing_entry.spinnaker_route - @property - def entry(self) -> RoutingEntry: - return self._routing_entry - def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: """ Merges together two multicast routing entries. The entry to merge @@ -132,6 +128,7 @@ def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: "other.mask", hex(other.mask), f"The mask does not match 0x{self.mask:x}") + # pylint disable:protected-access routing_entry = self._routing_entry.merge(other._routing_entry) return MulticastRoutingEntry( self.key, self.mask, routing_entry) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index 253abddd..1fddb82f 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -36,7 +36,7 @@ def __init__(self, *, processor_ids: Union[int, Iterable[int]], ... @overload - def __init__(self, int, *, processor_ids: None = None, + def __init__(self, *, processor_ids: None = None, link_ids: None = None, incoming_processor: None = None, incoming_link: None = None, diff --git a/unittests/test_router.py b/unittests/test_router.py index 696f64a5..ae97efb8 100644 --- a/unittests/test_router.py +++ b/unittests/test_router.py @@ -15,8 +15,7 @@ import unittest from spinn_machine import Router, Link from spinn_machine.config_setup import unittest_setup -from spinn_machine.exceptions import ( - SpinnMachineAlreadyExistsException, SpinnMachineInvalidParameterException) +from spinn_machine.exceptions import SpinnMachineAlreadyExistsException class TestingRouter(unittest.TestCase): diff --git a/unittests/test_routing_entry.py b/unittests/test_routing_entry.py index 84b816de..835c200a 100644 --- a/unittests/test_routing_entry.py +++ b/unittests/test_routing_entry.py @@ -16,7 +16,6 @@ import unittest from spinn_machine import RoutingEntry from spinn_machine.config_setup import unittest_setup -from spinn_machine.exceptions import SpinnMachineInvalidParameterException class TestRoutingEntry(unittest.TestCase): From 293c1b403dde63b0dbfd4516eb3dff461c83b82c Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 13:30:17 +0000 Subject: [PATCH 08/13] # pylint: disable=protected-access --- spinn_machine/multicast_routing_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spinn_machine/multicast_routing_entry.py b/spinn_machine/multicast_routing_entry.py index 7d8a6267..e757a814 100644 --- a/spinn_machine/multicast_routing_entry.py +++ b/spinn_machine/multicast_routing_entry.py @@ -128,7 +128,7 @@ def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry: "other.mask", hex(other.mask), f"The mask does not match 0x{self.mask:x}") - # pylint disable:protected-access + # pylint: disable=protected-access routing_entry = self._routing_entry.merge(other._routing_entry) return MulticastRoutingEntry( self.key, self.mask, routing_entry) From 529565862d131aee2009e7eb403870b87f14fb16 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 14:55:14 +0000 Subject: [PATCH 09/13] merged not equal routes are not defaultable --- spinn_machine/routing_entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index 1fddb82f..40dcc676 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -174,9 +174,11 @@ def merge(self, other: RoutingEntry) -> RoutingEntry: If the key and mask of the other entry do not match """ # 2 different merged routes can NEVER be defaultable + if self == other: + return self return RoutingEntry( spinnaker_route=self.spinnaker_route | other.spinnaker_route, - defaultable=self.defaultable and other.defaultable) + defaultable=False) def __eq__(self, other_entry: Any) -> bool: if not isinstance(other_entry, RoutingEntry): From b083b8f62e2d5574092bd51cbc879c0eee90b4a9 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 15:35:09 +0000 Subject: [PATCH 10/13] asserts for safety and typing --- spinn_machine/routing_entry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index 40dcc676..b8ac9f03 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -78,6 +78,8 @@ def __init__(self, *, # Add processor IDs, ignore duplicates if spinnaker_route is None: + assert processor_ids is not None + assert link_ids is not None if isinstance(processor_ids, int): processor_ids = [processor_ids] if isinstance(link_ids, int): @@ -90,7 +92,7 @@ def __init__(self, *, self._defaultable = False else: if incoming_processor is None: - if len(self.link_ids) != 1: + if len(link_ids) != 1: self._defaultable = False else: link_id = next(iter(link_ids)) From 5c80802ae5e55184e726bf4fe2240bb6c8110c4d Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 16:02:06 +0000 Subject: [PATCH 11/13] when given links autodetect defaultable. --- spinn_machine/routing_entry.py | 4 +- unittests/test_multicast_routing_entry.py | 73 ++++++++++++----------- unittests/test_routing_entry.py | 60 +++++++------------ 3 files changed, 61 insertions(+), 76 deletions(-) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index b8ac9f03..d9ed8e8f 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -80,6 +80,7 @@ def __init__(self, *, if spinnaker_route is None: assert processor_ids is not None assert link_ids is not None + assert defaultable is None if isinstance(processor_ids, int): processor_ids = [processor_ids] if isinstance(link_ids, int): @@ -103,9 +104,6 @@ def __init__(self, *, "incoming_processor", incoming_processor, "The incoming direction for a path can only be from " "either one link or one processors, not both") - # TODO remove this GIGO! - if defaultable is not None: - self._defaultable: bool = defaultable else: assert processor_ids is None assert link_ids is None diff --git a/unittests/test_multicast_routing_entry.py b/unittests/test_multicast_routing_entry.py index 26489fac..7b5a396f 100644 --- a/unittests/test_multicast_routing_entry.py +++ b/unittests/test_multicast_routing_entry.py @@ -34,8 +34,7 @@ def test_creating_new_multicast_routing_entry(self): key = 1 mask = 1 a_multicast = MulticastRoutingEntry( - key, mask, RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) + key, mask, RoutingEntry(processor_ids=proc_ids, link_ids=link_ids)) self.assertEqual(a_multicast.key, key) self.assertEqual(a_multicast.link_ids, set(link_ids)) @@ -45,7 +44,30 @@ def test_creating_new_multicast_routing_entry(self): self.assertEqual( str(a_multicast), "1:1:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, " - "16, 17}:{0, 1, 2, 3, 4, 5}(defaultable)") + "16, 17}:{0, 1, 2, 3, 4, 5}") + self.assertEqual( + a_multicast, + pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) + hash(a_multicast) + + def test_creating_defaulatble_multicast_routing_entry(self): + link_ids = list() + proc_ids = list() + link_ids.append(2) + key = 1 + mask = 1 + a_multicast = MulticastRoutingEntry( + key, mask, RoutingEntry( + processor_ids=proc_ids, link_ids=link_ids, incoming_link=5)) + + self.assertEqual(a_multicast.key, key) + self.assertEqual(a_multicast.link_ids, set(link_ids)) + self.assertEqual(a_multicast.mask, mask) + self.assertEqual(a_multicast.processor_ids, set(proc_ids)) + # While we're here, let's check a few other basic operations + self.assertEqual( + str(a_multicast), + "1:1:{}:{2}(defaultable)") self.assertEqual( a_multicast, pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) @@ -85,9 +107,9 @@ def test_merger(self): mask = 1 a_multicast = MulticastRoutingEntry( key, mask, RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) + processor_ids=proc_ids, link_ids=link_ids)) b_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( - processor_ids=proc_ids2, link_ids=link_ids2, defaultable=True)) + processor_ids=proc_ids2, link_ids=link_ids2)) result_multicast = a_multicast.merge(b_multicast) comparison_link_ids = list() @@ -107,43 +129,22 @@ def test_merger(self): set(comparison_proc_ids)) def test_merger_with_different_defaultable(self): - link_ids = list() - link_ids2 = list() - proc_ids = list() - proc_ids2 = list() - for i in range(3): - link_ids.append(i) - for i in range(3, 6): - link_ids2.append(i) - for i in range(9): - proc_ids.append(i) - for i in range(9, 18): - proc_ids2.append(i) key = 1 mask = 1 a_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) + processor_ids=[], link_ids=[3], incoming_link=6)) + assert a_multicast.defaultable b_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( - processor_ids=proc_ids2, link_ids=link_ids2, defaultable=False)) + processor_ids=[], link_ids=[3], incoming_link=4)) + assert not b_multicast.defaultable result_multicast = a_multicast.merge(b_multicast) - comparison_link_ids = list() - comparison_proc_ids = list() - for i in range(6): - comparison_link_ids.append(i) - self.assertEqual(link_ids + link_ids2, comparison_link_ids) - for i in range(18): - comparison_proc_ids.append(i) - self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) self.assertEqual(result_multicast.key, key) - self.assertEqual(result_multicast.link_ids, - set(comparison_link_ids)) + self.assertEqual(result_multicast.link_ids, set([3])) self.assertEqual(result_multicast.mask, mask) - self.assertEqual(result_multicast.processor_ids, - set(comparison_proc_ids)) - self.assertNotEqual(result_multicast, a_multicast) - self.assertNotEqual(result_multicast, b_multicast) + self.assertEqual(result_multicast.processor_ids, set()) + assert not result_multicast.defaultable def test_merger_with_invalid_parameter_key(self): link_ids = list() @@ -161,7 +162,7 @@ def test_merger_with_invalid_parameter_key(self): key_combo = 1 mask = 1 entry = RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, defaultable=True) + processor_ids=proc_ids, link_ids=link_ids) a_multicast = MulticastRoutingEntry(key_combo, mask, entry) b_multicast = MulticastRoutingEntry(key_combo + 1, mask + 1, entry) with self.assertRaises(SpinnMachineInvalidParameterException) as e: @@ -186,9 +187,9 @@ def test_merger_with_invalid_parameter_mask(self): key = 0 mask = 2 a_multicast = MulticastRoutingEntry(key, mask, RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, defaultable=True)) + processor_ids=proc_ids, link_ids=link_ids)) b_multicast = MulticastRoutingEntry(key, 4, RoutingEntry( - processor_ids=proc_ids2, link_ids=link_ids2, defaultable=True)) + processor_ids=proc_ids2, link_ids=link_ids2)) self.assertNotEqual(a_multicast, b_multicast) self.assertNotEqual(a_multicast, "foo") with self.assertRaises(SpinnMachineInvalidParameterException): diff --git a/unittests/test_routing_entry.py b/unittests/test_routing_entry.py index 835c200a..3cc2c59e 100644 --- a/unittests/test_routing_entry.py +++ b/unittests/test_routing_entry.py @@ -31,15 +31,28 @@ def test_creating_new_routing_entry(self): for i in range(18): proc_ids.append(i) a_multicast = RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) + processor_ids=proc_ids, link_ids=link_ids) self.assertEqual(a_multicast.link_ids, set(link_ids)) self.assertEqual(a_multicast.processor_ids, set(proc_ids)) # While we're here, let's check a few other basic operations self.assertEqual(str(a_multicast), "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, " - "15, 16, 17}:{0, 1, 2, 3, 4, 5}(defaultable)") + "15, 16, 17}:{0, 1, 2, 3, 4, 5}") + self.assertEqual( + a_multicast, + pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) + hash(a_multicast) + + def test_defualtable_routing_entry(self): + a_multicast = RoutingEntry( + processor_ids=[], link_ids=[4], incoming_link=1) + + self.assertEqual(a_multicast.link_ids, set([4])) + self.assertEqual(a_multicast.processor_ids, set()) + # While we're here, let's check a few other basic operations + self.assertEqual(str(a_multicast), + "{}:{4}(defaultable)") self.assertEqual( a_multicast, pickle.loads(pickle.dumps(a_multicast, pickle.HIGHEST_PROTOCOL))) @@ -69,12 +82,8 @@ def test_merger(self): proc_ids.append(i) for i in range(9, 18): proc_ids2.append(i) - a_multicast = RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) - b_multicast = RoutingEntry( - processor_ids=proc_ids2, link_ids=link_ids2, - defaultable=True) + a_multicast = RoutingEntry(processor_ids=proc_ids, link_ids=link_ids) + b_multicast = RoutingEntry(processor_ids=proc_ids2, link_ids=link_ids2) result_multicast = a_multicast.merge(b_multicast) comparison_link_ids = list() @@ -91,40 +100,17 @@ def test_merger(self): set(comparison_proc_ids)) def test_merger_with_different_defaultable(self): - link_ids = list() - link_ids2 = list() - proc_ids = list() - proc_ids2 = list() - for i in range(3): - link_ids.append(i) - for i in range(3, 6): - link_ids2.append(i) - for i in range(9): - proc_ids.append(i) - for i in range(9, 18): - proc_ids2.append(i) a_multicast = RoutingEntry( - processor_ids=proc_ids, link_ids=link_ids, - defaultable=True) + processor_ids=[], link_ids=[1], incoming_link=4) b_multicast = RoutingEntry( - processor_ids=proc_ids2, link_ids=link_ids2, - defaultable=False) + processor_ids=[], link_ids=[1], incoming_link=5) result_multicast = a_multicast.merge(b_multicast) - comparison_link_ids = list() - comparison_proc_ids = list() - for i in range(6): - comparison_link_ids.append(i) - self.assertEqual(link_ids + link_ids2, comparison_link_ids) - for i in range(18): - comparison_proc_ids.append(i) - self.assertEqual(proc_ids + proc_ids2, comparison_proc_ids) - self.assertEqual(result_multicast.link_ids, set(comparison_link_ids)) - self.assertEqual(result_multicast.processor_ids, - set(comparison_proc_ids)) + self.assertEqual(result_multicast.link_ids, set([1])) + self.assertEqual(result_multicast.processor_ids, set()) self.assertNotEqual(result_multicast, a_multicast) - self.assertNotEqual(result_multicast, b_multicast) + self.assertNotEqual(hash(result_multicast), hash(a_multicast)) """ From FixedRouteEntry use or loose From fc1fb81ceb1305003f72f95c04894e19324a612c Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Thu, 28 Mar 2024 16:23:41 +0000 Subject: [PATCH 12/13] typing fix --- spinn_machine/routing_entry.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index d9ed8e8f..eb998f03 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -75,6 +75,8 @@ def __init__(self, *, processor_ids or link_ids is missing or `None` """ self.__repr: Optional[str] = None + self._link_ids: Optional[FrozenSet[int]] + self._processor_ids: Optional[FrozenSet[int]] # Add processor IDs, ignore duplicates if spinnaker_route is None: @@ -85,9 +87,8 @@ def __init__(self, *, processor_ids = [processor_ids] if isinstance(link_ids, int): link_ids = [link_ids] - self._processor_ids: Optional[FrozenSet[int]] = \ - frozenset(processor_ids) - self._link_ids: Optional[FrozenSet[int]] = frozenset(link_ids) + self._processor_ids = frozenset(processor_ids) + self._link_ids = frozenset(link_ids) self._spinnaker_route: int = self._calc_spinnaker_route() if incoming_link is None: self._defaultable = False @@ -112,7 +113,10 @@ def __init__(self, *, self._spinnaker_route = spinnaker_route self._processor_ids = None self._link_ids = None - self._defaultable = defaultable + if defaultable: + self._defaultable = True + else: + self._defaultable = False @property def processor_ids(self) -> FrozenSet[int]: From aacd5266f21c1339490b9b5e96de590289b90a9b Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 2 Apr 2024 06:55:28 +0100 Subject: [PATCH 13/13] Needs to be a collection for len --- spinn_machine/routing_entry.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spinn_machine/routing_entry.py b/spinn_machine/routing_entry.py index eb998f03..f7ca5705 100644 --- a/spinn_machine/routing_entry.py +++ b/spinn_machine/routing_entry.py @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations -from typing import Any, FrozenSet, Iterable, Optional, Tuple, overload, Union +from typing import (Any, Collection, FrozenSet, Optional, overload, Tuple, + Union) from spinn_machine.router import Router from .exceptions import (SpinnMachineInvalidParameterException) @@ -27,8 +28,8 @@ class RoutingEntry(object): "_link_ids", "_spinnaker_route", "__repr") @overload - def __init__(self, *, processor_ids: Union[int, Iterable[int]], - link_ids: Union[int, Iterable[int]], + def __init__(self, *, processor_ids: Union[int, Collection[int]], + link_ids: Union[int, Collection[int]], incoming_processor: Optional[int] = None, incoming_link: Optional[int] = None, defaultable: None = None, @@ -46,8 +47,8 @@ def __init__(self, *, processor_ids: None = None, # pylint: disable=too-many-arguments def __init__(self, *, - processor_ids: Optional[Union[int, Iterable[int]]] = None, - link_ids: Optional[Union[int, Iterable[int]]] = None, + processor_ids: Optional[Union[int, Collection[int]]] = None, + link_ids: Optional[Union[int, Collection[int]]] = None, incoming_processor: Optional[int] = None, incoming_link: Optional[int] = None, defaultable: Optional[bool] = None,