# Source code for pacman.model.graphs.abstract_edge_partition

# Copyright (c) 2017-2019 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# 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/>.
from spinn_utilities.abstract_base import (
AbstractBase, abstractmethod, abstractproperty)
from spinn_utilities.ordered_set import OrderedSet
from pacman.exceptions import (
PacmanConfigurationException, PacmanInvalidParameterException)
from pacman.model.graphs.common import ConstrainedObject

_REPR_TEMPLATE = "{}(identifier={}, edges={}, constraints={}, label={})"

[docs]class AbstractEdgePartition(ConstrainedObject, metaclass=AbstractBase):
""" A collection of edges which start at a single vertex which have the\
same semantics and so can share a single key or block of SDRAM\
(depending on edge type).
"""

__slots__ = [
# The partition identifier
"_identifier",
# The edges in the partition
"_edges",
# The type of edges to accept
"_allowed_edge_types",
# The weight of traffic going down this partition
"_traffic_weight",
# The label of the graph
"_label",
# class name
"_class_name",
# Safety code generated by the graph when added to that graph
"_graph_code"
]

def __init__(
self, identifier, allowed_edge_types, constraints,
label, traffic_weight, class_name):
"""
:param str identifier: The identifier of the partition
:param allowed_edge_types: The types of edges allowed
:type allowed_edge_types: type or tuple(type, ...)
:param iterable(AbstractConstraint) constraints:
Any initial constraints
:param str label: An optional label of the partition
:param int traffic_weight:
The weight of traffic going down this partition
"""
super().__init__(constraints)
self._label = label
self._identifier = identifier
self._edges = OrderedSet()
self._allowed_edge_types = allowed_edge_types
self._traffic_weight = traffic_weight
self._class_name = class_name
self._graph_code = None

@property
def label(self):
""" The label of the edge partition.

:rtype: str
"""
return self._label

""" Add an edge to the edge partition.

.. note::
This method should only be called by the add_edge method of
the graph that owns the partition. Calling it from anywhere else,
even with the correct graph_code, will lead to unsupported
inconsistency.

:param AbstractEdge edge: the edge to add
:param int graph_code:
A code to check the correct graph is calling this method
:raises PacmanInvalidParameterException:
If the edge does not belong in this edge partition
"""
if graph_code != self._graph_code:
raise PacmanConfigurationException(
"Only one graph should add edges")
if self._graph_code is None:
raise PacmanConfigurationException(
"Only Graphs can add edges to partitions")

# Check for an incompatible edge
if not isinstance(edge, self._allowed_edge_types):
raise PacmanInvalidParameterException(
"edge", str(edge.__class__),
"Edges of this graph must be one of the following types:"
" {}".format(self._allowed_edge_types))

[docs]    def register_graph_code(self, graph_code):
"""
Allows the graph to register its code when the partition is added
"""
if self._graph_code is not None:
raise PacmanConfigurationException(
"Illegal attempt to add partition {} to a second "
"graph".format(self))
self._graph_code = graph_code

@property
def identifier(self):
""" The identifier of this edge partition.

:rtype: str
"""
return self._identifier

@property
def edges(self):
""" The edges in this edge partition.

.. note::
The order in which the edges are added is preserved for when they
are requested later. If not, please talk to the software team.

:rtype: iterable(AbstractEdge)
"""
return self._edges

@property
def n_edges(self):
""" The number of edges in the edge partition.

:rtype: int
"""
return len(self._edges)

@property
def traffic_weight(self):
""" The weight of the traffic in this edge partition compared to\
other partitions.

:rtype: int
"""
return self._traffic_weight

def __repr__(self):
edges = ""
for edge in self._edges:
if edge.label is not None:
edges += edge.label + ","
else:
edges += str(edge) + ","
return _REPR_TEMPLATE.format(
self._class_name, self._identifier, edges, self.constraints,
self.label)

def __str__(self):
return self.__repr__()

def __contains__(self, edge):
""" Check if the edge is contained within this partition

:param AbstractEdge edge: the edge to search for.
:rtype: bool
"""
return edge in self._edges

[docs]    @abstractmethod
def clone_without_edges(self):
""" Make a copy of this edge partition without any of the edges in it

This follows the design pattern that only the graph adds edges to

:return: The copied edge partition but excluding edges
"""

@abstractproperty
def pre_vertices(self):
"""
Provides the vertices associated with this partition

.. note::
Most edge partitions will be
:py:class:AbstractSingleSourcePartition
and therefore provide the pre_vertex method.

:rtype: iter(AbstractVertex)
"""