Source code for pacman.model.graphs.application.application_graph
# 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 collections import defaultdict
from .application_edge import ApplicationEdge
from .application_vertex import ApplicationVertex
from .application_edge_partition import ApplicationEdgePartition
from spinn_utilities.ordered_set import OrderedSet
from pacman.exceptions import (
PacmanAlreadyExistsException, PacmanInvalidParameterException)
[docs]class ApplicationGraph(object):
"""
An application-level abstraction of a graph.
"""
__slots__ = [
# The sets of edge partitions by pre-vertex
"_outgoing_edge_partitions_by_pre_vertex",
# The total number of outgoing edge partitions
"_n_outgoing_edge_partitions",
# count of vertex which had a None or already used label
"_unlabelled_vertex_count",
# map between labels and vertex
"_vertex_by_label"
]
def __init__(self):
self._outgoing_edge_partitions_by_pre_vertex = defaultdict(OrderedSet)
self._n_outgoing_edge_partitions = 0
self._unlabelled_vertex_count = 0
self._vertex_by_label = dict()
[docs] def add_vertex(self, vertex):
"""
Add a vertex to the graph.
:param ~pacman.model.graphs.application.ApplicationVertex vertex:
The vertex to add
:raises PacmanInvalidParameterException:
If the vertex is not of a valid type
:raises PacmanConfigurationException:
If there is an attempt to add the same vertex more than once
"""
if not isinstance(vertex, ApplicationVertex):
raise PacmanInvalidParameterException(
"vertex", str(vertex.__class__),
"Only an ApplicationVertex can be added")
if not vertex.label:
vertex.set_label(
vertex.__class__.__name__ + "_" + self._label_postfix())
elif vertex.label in self._vertex_by_label:
if self._vertex_by_label[vertex.label] == vertex:
raise PacmanAlreadyExistsException("vertex", vertex.label)
vertex.set_label(vertex.label + self._label_postfix())
vertex.addedToGraph()
self._vertex_by_label[vertex.label] = vertex
@property
def vertices(self):
"""
The vertices in the graph.
:rtype: iterable(~pacman.model.graphs.AbstractVertex)
"""
return self._vertex_by_label.values()
[docs] def vertex_by_label(self, label):
return self._vertex_by_label[label]
@property
def n_vertices(self):
"""
The number of vertices in the graph.
:rtype: int
"""
return len(self._vertex_by_label)
[docs] def add_edge(self, edge, outgoing_edge_partition_name):
"""
Add an edge to the graph and its partition.
If required and possible will create a new partition in the graph
:param ~pacman.model.graphs.application.ApplicationEdge edge:
The edge to add
:param str outgoing_edge_partition_name:
The name of the edge partition to add the edge to; each edge
partition is the partition of edges that start at the same vertex
:return: The partition the edge was added to.
:rtype: ~pacman.model.graphs.AbstractEdgePartition
:raises PacmanInvalidParameterException:
If the edge is not of a valid type or if edges have already been
added to this partition that start at a different vertex to this
one
"""
self._check_edge(edge)
# Add the edge to the partition
partition = self.get_outgoing_edge_partition_starting_at_vertex(
edge.pre_vertex, outgoing_edge_partition_name)
if partition is None:
partition = ApplicationEdgePartition(
identifier=outgoing_edge_partition_name,
pre_vertex=edge.pre_vertex)
self._add_outgoing_edge_partition(partition)
partition.add_edge(edge)
return partition
def _check_edge(self, edge):
"""
Add an edge to the graph.
:param ~pacman.model.graphs.application.ApplicationEdge edge:
The edge to add
:raises PacmanInvalidParameterException:
If the edge is not of a valid type or if edges have already been
added to this partition that start at a different vertex to this
one
"""
# verify that the edge is one suitable for this graph
if not isinstance(edge, ApplicationEdge):
raise PacmanInvalidParameterException(
"edge", edge.__class__,
"Only ApplicationEdges can be added")
if edge.pre_vertex.label not in self._vertex_by_label:
raise PacmanInvalidParameterException(
"Edge", str(edge.pre_vertex),
"Pre-vertex must be known in graph")
if edge.post_vertex.label not in self._vertex_by_label:
raise PacmanInvalidParameterException(
"Edge", str(edge.post_vertex),
"Post-vertex must be known in graph")
@property
def edges(self):
# pylint: disable=not-an-iterable
# https://github.com/PyCQA/pylint/issues/3105
"""
The edges in the graph.
:rtype: iterable(~pacman.model.graphs.AbstractEdge)
"""
return [
edge
for partition in self.outgoing_edge_partitions
for edge in partition.edges]
def _add_outgoing_edge_partition(self, edge_partition):
"""
Add an edge partition to the graph.
Will also add any edges already in the partition as well
:param edge_partition:
:type edge_partition:
~pacman.model.graphs.application.ApplicationEdgePartition
"""
self._outgoing_edge_partitions_by_pre_vertex[
edge_partition.pre_vertex].add(edge_partition)
self._n_outgoing_edge_partitions += 1
@property
def outgoing_edge_partitions(self):
"""
The edge partitions in the graph.
:rtype: iterable(~pacman.model.graphs.AbstractEdgePartition)
"""
for partitions in \
self._outgoing_edge_partitions_by_pre_vertex.values():
for partition in partitions:
yield partition
@property
def n_outgoing_edge_partitions(self):
"""
The number of outgoing edge partitions in the graph.
:rtype: int
"""
return self._n_outgoing_edge_partitions
[docs] def get_outgoing_edge_partitions_starting_at_vertex(self, vertex):
"""
Get all the edge partitions that start at the given vertex.
:param ~pacman.model.graphs.AbstractVertex vertex:
The vertex at which the edge partitions to find starts
:rtype: iterable(~pacman.model.graphs.AbstractEdgePartition)
"""
return self._outgoing_edge_partitions_by_pre_vertex[vertex]
[docs] def get_outgoing_edge_partition_starting_at_vertex(
self, vertex, outgoing_edge_partition_name):
"""
Get the given outgoing edge partition that starts at the
given vertex, or `None` if no such edge partition exists.
:param ~pacman.model.graphs.AbstractVertex vertex:
The vertex at the start of the edges in the partition
:param str outgoing_edge_partition_name:
The name of the edge partition
:return:
The named edge partition, or `None` if no such partition exists
:rtype: ~pacman.model.graphs.AbstractEdgePartition or None
"""
# In general, very few partitions start at a given vertex, so iteration
# isn't going to be as onerous as it looks
parts = self.get_outgoing_edge_partitions_starting_at_vertex(vertex)
for partition in parts:
if partition.identifier == outgoing_edge_partition_name:
return partition
return None
[docs] def reset(self):
"""
Reset all the application vertices.
"""
for vertex in self.vertices:
vertex.reset()
def _label_postfix(self):
self._unlabelled_vertex_count += 1
return str(self._unlabelled_vertex_count)