Source code for pacman.operations.partition_algorithms.basic_partitioner

# 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/>.

import logging
from spinn_utilities.progress_bar import ProgressBar
from pacman.exceptions import PacmanPartitionException
from pacman.model.constraints.partitioner_constraints import (
    AbstractPartitionerConstraint, MaxVertexAtomsConstraint,
    FixedVertexAtomsConstraint)
from pacman.model.graphs.common import GraphMapper, Slice
from pacman.model.graphs.machine import MachineGraph
from pacman.utilities import utility_calls
from pacman.utilities.algorithm_utilities.partition_algorithm_utilities \
    import (generate_machine_edges, get_remaining_constraints)
from pacman.utilities.utility_objs import ResourceTracker

logger = logging.getLogger(__name__)


[docs]class BasicPartitioner(object): """ An basic algorithm that can partition an application graph based\ on the number of atoms in the vertices. """ __slots__ = [] @staticmethod def _get_ratio(top, bottom): if bottom == 0: return 1.0 return top / bottom # inherited from AbstractPartitionAlgorithm def __call__(self, graph, machine, plan_n_timesteps): """ :param graph: The application_graph to partition :type graph:\ :py:class:`pacman.model.graphs.application.ApplicationGraph` :param machine:\ The machine with respect to which to partition the application\ graph :type machine: :py:class:`spinn_machine.Machine` :param plan_n_timesteps: number of timesteps to plan for :type plan_n_timesteps: int :return: A machine graph :rtype:\ :py:class:`pacman.model.graphs.machine.MachineGraph` :raise pacman.exceptions.PacmanPartitionException:\ If something goes wrong with the partitioning """ ResourceTracker.check_constraints(graph.vertices) utility_calls.check_algorithm_can_support_constraints( constrained_vertices=graph.vertices, supported_constraints=[ MaxVertexAtomsConstraint, FixedVertexAtomsConstraint], abstract_constraint_type=AbstractPartitionerConstraint) # start progress bar progress = ProgressBar(graph.n_vertices, "Partitioning graph vertices") machine_graph = MachineGraph("Machine graph for " + graph.label) graph_mapper = GraphMapper() resource_tracker = ResourceTracker(machine, plan_n_timesteps) # Partition one vertex at a time for vertex in progress.over(graph.vertices): self._partition_one_application_vertex( vertex, resource_tracker, machine_graph, graph_mapper, plan_n_timesteps) generate_machine_edges(machine_graph, graph_mapper, graph) return machine_graph, graph_mapper, resource_tracker.chips_used def _partition_one_application_vertex( self, vertex, res_tracker, m_graph, mapper, plan_n_timesteps): """ Partitions a single application vertex. """ # Compute how many atoms of this vertex we can put on one core atoms_per_core = self._compute_atoms_per_core( vertex, res_tracker, plan_n_timesteps) if atoms_per_core < 1.0: raise PacmanPartitionException( "Not enough resources available to create vertex") # Partition into vertices for first in range(0, vertex.n_atoms, int(atoms_per_core)): # Determine vertex size last = min(first + atoms_per_core, vertex.n_atoms) - 1 if first < 0 or last < 0: raise PacmanPartitionException( "Not enough resources available to create vertex") # Create and store new vertex, and increment elements first vertex_slice = Slice(first, last) resources = vertex.get_resources_used_by_atoms(vertex_slice) m_vertex = vertex.create_machine_vertex( vertex_slice, resources, "{}:{}:{}".format(vertex.label, first, last), get_remaining_constraints(vertex)) m_graph.add_vertex(m_vertex) mapper.add_vertex_mapping(m_vertex, vertex_slice, vertex) # update allocated resources res_tracker.allocate_constrained_resources( resources, vertex.constraints) def _compute_atoms_per_core(self, vertex, res_tracker, plan_n_timesteps): """ Work out how many atoms per core are required for the given\ vertex. Assumes that the first atom of the vertex is fully\ representative. :rtype: float """ # Get the usage of the first atom, then assume that this will be the # usage of all the atoms. requirements = vertex.get_resources_used_by_atoms(Slice(0, 1)) # Locate the maximum resources available limits = res_tracker.get_maximum_constrained_resources_available( requirements, vertex.constraints) # Find the ratio of each of the resources - if 0 is required, # assume the ratio is the max available atoms_per_sdram = self._get_ratio( limits.sdram.get_total_sdram(plan_n_timesteps), requirements.sdram.get_total_sdram(plan_n_timesteps)) atoms_per_dtcm = self._get_ratio( limits.dtcm.get_value(), requirements.dtcm.get_value()) atoms_per_cpu = self._get_ratio( limits.cpu_cycles.get_value(), requirements.cpu_cycles.get_value()) n_atoms = None for fa_constraint in utility_calls.locate_constraints_of_type( vertex.constraints, FixedVertexAtomsConstraint): if n_atoms is not None and n_atoms != fa_constraint.size: raise PacmanPartitionException( "Vertex has multiple contradictory fixed atom constraints" " - cannot be both {} and {}".format( n_atoms, fa_constraint.size)) n_atoms = fa_constraint.size max_atom_values = [atoms_per_sdram, atoms_per_dtcm, atoms_per_cpu] for max_atom_constraint in utility_calls.locate_constraints_of_type( vertex.constraints, MaxVertexAtomsConstraint): max_atom_values.append(float(max_atom_constraint.size)) max_atoms = min(max_atom_values) if n_atoms is not None and max_atoms < n_atoms: raise PacmanPartitionException( "Max size of {} is incompatible with fixed size of {}".format( max_atoms, n_atoms)) return n_atoms if n_atoms is not None else max_atoms