Source code for pacman.operations.router_compressors.abstract_compressor
# Copyright (c) 2017-2019 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
based on https://github.com/project-rig/
"""
from abc import abstractmethod
import logging
from spinn_utilities.progress_bar import ProgressBar
from pacman.model.routing_tables import (
MulticastRoutingTable, MulticastRoutingTables)
from pacman.exceptions import MinimisationFailedError
logger = logging.getLogger(__name__)
[docs]class AbstractCompressor(object):
MAX_SUPPORTED_LENGTH = 1023
__slots__ = [
# Max length below which the algorithm should stop compressing
"_target_length",
# String of problems detected. Must be "" to finish
"_problems",
# Flag to say if the results can be order dependent
"_ordered",
]
def __init__(self, ordered=True):
self._ordered = ordered
def __call__(self, router_tables, target_length=None):
if target_length is None:
self._target_length = 0 # Compress as much as you can
else:
self._target_length = target_length
# create progress bar
progress = ProgressBar(
router_tables.routing_tables,
"Compressing routing Tables using {}".format(
self.__class__.__name__))
return self.compress_tables(router_tables, progress)
[docs] @staticmethod
def intersect(key_a, mask_a, key_b, mask_b):
"""
Return if key-mask pairs intersect (i.e., would both match some of
the same keys).
For example, the key-mask pairs ``00XX`` and ``001X`` both match the
keys``0010`` and ``0011`` (i.e., they do intersect)::
>>> intersect(0b0000, 0b1100, 0b0010, 0b1110)
True
But the key-mask pairs ``00XX`` and ``11XX`` do not match any of the
same keys (i.e., they do not intersect)::
>>> intersect(0b0000, 0b1100, 0b1100, 0b1100)
False
:param key_a: The key of first key-mask pair
:type key_a: int
:param mask_a: The mask of first key-mask pair
:type key_b: int
:param key_b: The key of second key-mask pair
:type key_b: int
:param mask_b: The mask of second key-mask pair
:type key_b: int
:return: True if the two key-mask pairs intersect otherwise False.
:rtype: bool
"""
return (key_a & mask_b) == (key_b & mask_a)
[docs] def merge(self, entry1, entry2):
"""
Merges two entries/triples into one that covers both
The assumption is that they both have the same known spinnaker_route
:param entry1: Key, Mask, defaultable from the first entry
:type entry1: ~pacman.operations.router_compressors.Entry
:param entry2: Key, Mask, defaultable from the second entry
:type entry2: ~pacman.operations.router_compressors.Entry
:return: Key, Mask, defaultable from merged entry
:rtype: (int, int, bool)
"""
any_ones = entry1.key | entry2.key
all_ones = entry1.key & entry2.key
all_selected = entry1.mask & entry2.mask
# Compute the new mask and key
any_zeros = ~all_ones
new_xs = any_ones ^ any_zeros
mask = all_selected & new_xs # Combine existing and new Xs
key = all_ones & mask
return key, mask, entry1.defaultable and entry2.defaultable
[docs] @abstractmethod
def compress_table(self, router_table):
pass
[docs] def compress_tables(self, router_tables, progress):
"""
Compress all the unordered routing tables
Tables who start of smaller than target_length are not compressed
:param router_tables: Routing tables
:type router_tables: ~pacman.model.routing_tables.MulticastRoutingTable
:param progress: Progress bar to show while working
:tpye progress: ProgressBar
:return: The compressed but still unordered routing tables
"""
compressed_tables = MulticastRoutingTables()
self._problems = ""
for table in progress.over(router_tables.routing_tables):
if table.number_of_entries < self._target_length:
new_table = table
else:
compressed_table = self.compress_table(table)
new_table = MulticastRoutingTable(table.x, table.y)
for entry in compressed_table:
new_table.add_multicast_routing_entry(
entry.to_MulticastRoutingEntry())
if new_table.number_of_entries > self.MAX_SUPPORTED_LENGTH:
self._problems += "(x:{},y:{})={} ".format(
new_table.x, new_table.y, new_table.number_of_entries)
compressed_tables.add_routing_table(new_table)
if len(self._problems) > 0:
if self._ordered:
raise MinimisationFailedError(
"The routing table after compression will still not fit"
" within the machines router: {}".format(self._problems))
else:
logger.warning(self._problems)
return compressed_tables
@property
def ordered(self):
return self._ordered