# pacman imports
from pacman.model.constraints.key_allocator_constraints \
import ContiguousKeyRangeContraint
from pacman.model.constraints.key_allocator_constraints \
import FixedKeyFieldConstraint
from pacman.model.constraints.key_allocator_constraints \
import FixedKeyAndMaskConstraint
from pacman.model.constraints.key_allocator_constraints \
import FixedMaskConstraint
from pacman.model.constraints.key_allocator_constraints \
import FlexiKeyFieldConstraint
from pacman.model.routing_info \
import RoutingInfo, PartitionRoutingInfo, BaseKeyAndMask
from pacman.utilities import utility_calls
from pacman.utilities.algorithm_utilities import \
routing_info_allocator_utilities
from pacman import exceptions
from pacman.utilities.utility_objs import FlexiField
from pacman.utilities.utility_objs.flexi_field import SUPPORTED_TAGS
from pacman.utilities.algorithm_utilities import \
field_based_system_utilities as field_utilities
from spinn_utilities.progress_bar import ProgressBar
# general imports
import math
from rig.bitfield import BitField
# hard coded values
APPLICATION_MASK_BIT = 0
# flag for usage for detecting the application field
APPLICATION_DIVIDER_FIELD_NAME = "APPLICATION_DIVIDER"
FIXED_MASK_NAME = \
field_utilities.TYPES_OF_FIELDS.FIXED_MASK.name # @UndefinedVariable
FIXED_KEY_NAME = \
field_utilities.TYPES_OF_FIELDS.FIXED_KEY.name # @UndefinedVariable
FIXED_FIELD_NAME = \
field_utilities.TYPES_OF_FIELDS.FIXED_FIELD.name # @UndefinedVariable
[docs]class VertexBasedRoutingInfoAllocator(object):
""" allocator of routing keys based off the vertex requirements
"""
__slots__ = [
# the field id used for a fixed key field.
"_fixed_key_application_field_value",
# the field id used for a fixed mask
"_fixed_mask_application_field_value",
# the id used for a flexible field
"_flexi_field_application_field_values",
# the id used for a fixed field
"_fixed_field_application_field_value",
# dictionary containing all fields that have been mapped during the
# process
"_field_mapper"
]
def __init__(self):
# separate holders for the application flag if needed
self._fixed_key_application_field_value = None
self._fixed_mask_application_field_value = None
self._flexi_field_application_field_values = dict()
self._fixed_field_application_field_value = None
# mapper between fields and constraints based data
# TODO: FIX THIS BIT OF HORRIBLE CODE USAGE
self._field_mapper = dict()
def __call__(self, application_graph, graph_mapper, machine_graph,
n_keys_map):
"""
:param application_graph: The application graph
:param graph_mapper: the mapping between graphs
:param machine_graph: the machine graph
:param n_keys_map: the mapping between edges and n keys
:return: routing information objects
"""
progress_bar = ProgressBar(
machine_graph.n_outgoing_edge_partitions * 3,
"Allocating routing keys")
# ensure groups are stable and correct
self._determine_groups(
machine_graph, graph_mapper, application_graph, n_keys_map,
progress_bar)
# define the key space
bit_field_space = BitField(32)
field_positions = set()
# locate however many types of constraints there are
seen_fields = field_utilities.deduce_types(machine_graph)
progress_bar.update(machine_graph.n_outgoing_edge_partitions)
if len(seen_fields) > 1:
self._adds_application_field_to_the_fields(seen_fields)
# handle the application space
self._create_application_space_in_the_bit_field_space(
bit_field_space, seen_fields, field_positions)
# assign fields to positions in the space
bit_field_space.assign_fields()
# get positions of the flexible fields:
self._assign_flexi_field_positions(
bit_field_space, seen_fields, field_positions)
# create routing_info_allocator
routing_info = RoutingInfo()
seen_mask_instances = 0
# extract keys and masks for each edge from the bitfield
for partition in machine_graph.outgoing_edge_partitions:
# get keys and masks
keys_and_masks, seen_mask_instances = \
self._extract_keys_and_masks_from_bit_field(
partition, bit_field_space, n_keys_map,
seen_mask_instances)
# update routing info for each edge in the partition
partition_info = PartitionRoutingInfo(keys_and_masks, partition)
routing_info.add_partition_info(partition_info)
# update the progress bar again
progress_bar.update()
progress_bar.end()
return routing_info, field_positions
def _assign_flexi_field_positions(
self, bit_field_space, seen_fields, field_positions):
""" Searches though seen fields and if there's a flexible field
:param bit_field_space: the bit field space system
:param seen_fields: the fields been seen
:param field_positions: the set of fields that have been allocated\
over the entire key space
:rtype: None
"""
# locate the application field if it exists
application_field, _ = self._locate_application_field(seen_fields)
for field in seen_fields:
if (field != FIXED_MASK_NAME and
field != FIXED_KEY_NAME and
field != FIXED_FIELD_NAME):
if application_field is None:
field_positions = \
self._assign_flexi_field_positions_recursive(
bit_field_space, field, seen_fields,
field_positions)
else:
inputs = dict()
inputs[APPLICATION_DIVIDER_FIELD_NAME] = \
self._flexi_field_application_field_values[field]
this_bit_field_space = bit_field_space(**inputs)
field_positions = \
self._assign_flexi_field_positions_recursive(
this_bit_field_space, field, seen_fields,
field_positions)
def _assign_flexi_field_positions_recursive(
self, bit_field_space, field, fields, field_positions):
low, length = bit_field_space.get_location_and_length(field)
hi = low + length
field_positions.add((hi, low))
for field_instance in fields[field]:
# only carry on if there's more to create
if len(fields[field][field_instance]) > 0:
inputs = dict()
inputs[field] = field_instance.value
this_bit_field_space = bit_field_space(**inputs)
# deal with next level hierarchy
for nested_field in fields[field][field_instance]:
new_field_positions = \
self._assign_flexi_field_positions_recursive(
this_bit_field_space, nested_field,
fields[field][field_instance], field_positions)
for field_position in new_field_positions:
field_positions.add(field_position)
return field_positions
def _adds_application_field_to_the_fields(self, seen_fields):
""" Determine what the field spaces and values are for the\
application field needed to separate the different hierarchy\
fields and adds it to the fields in current existence.
:param seen_fields: the types of fields needed to get this simulation\
operating correctly
"""
application_field = None
success = False
required_bits = int(math.ceil(math.log(len(seen_fields), 2)))
if FIXED_KEY_NAME in seen_fields:
success = self._fixed_key_application_field_allocation(
seen_fields, required_bits)
else:
if FIXED_MASK_NAME in seen_fields:
success = self._fixed_mask_application_field_allocation(
seen_fields, application_field, required_bits)
return success
def _fixed_key_application_field_allocation(
self, seen_fields, required_bits):
fixed_keys = seen_fields[FIXED_KEY_NAME]
if len(fixed_keys) == 1:
found = False
searching = True
# generate the bit generator for the fixed key
fixed_key = fixed_keys[0]
fields = field_utilities.convert_mask_into_fields(fixed_key.mask)
bit_generator = self._generate_bits_that_satisfy_constraints(
fields, required_bits)
# search for a set of bits which will work for the fixed key and
# any fixed masks that exist
while searching:
# use the fixed mask functionality to generate the fields for
# the fixed key
application_field, new_fields = \
self._update_fixed_mask_field_set(
bit_generator, fixed_key.key)
seen_fields[FIXED_KEY_NAME] = list()
seen_fields[FIXED_KEY_NAME].append((fixed_key, new_fields))
# if there's fixed masks as well, ensure they work with the
field_name = FIXED_MASK_NAME
if field_name in seen_fields:
success = self._fixed_mask_application_field_allocation(
seen_fields, application_field, required_bits)
if success:
return True
else:
return True
return found
else:
# more than 1 fixed key exists
# generate the bit generator for the first key, as this is the
# premise of all the other keys to meet
new_fixed_keys = list()
# generate the bit generator for the fixed key
fixed_key = fixed_keys[0]
fields = field_utilities.convert_mask_into_fields(fixed_key.mask)
bit_generator = self._generate_bits_that_satisfy_constraints(
fields, required_bits)
# search till we find a application field which meets all
# requirements
while True:
application_field, new_fields = \
self._update_fixed_mask_field_set(bit_generator)
# check that all the other fields can meet the value for this
# field's application field value
valid = True
for fixed_key_index in range(1, len(fixed_keys)):
fixed_key = fixed_keys[fixed_key_index]
fields = field_utilities.convert_mask_into_fields(
fixed_key.mask)
application_field_value = \
self._determine_fixed_mask_application_field_value(
fields[0].value, application_field.hi,
application_field.lo)
if application_field_value != application_field.value:
valid = False
# if valid, update all fields
if valid:
# set the first one which has had its already generated
new_fixed_keys.append((fixed_keys[0], new_fields))
# set the rest of the fields which need computing
for fixed_key_index in range(1, len(fixed_keys)):
fixed_key = fixed_keys[fixed_key_index]
fields = field_utilities.convert_mask_into_fields(
fixed_key.mask)
# create new fields
new_fields = self.\
_adjust_fixed_mask_fields_for_application_field(
fields[0].value, application_field.hi,
application_field.value)
# update the fields for this fixed mask
new_fixed_keys.append((fixed_key, new_fields))
field_name = FIXED_MASK_NAME
if field_name in seen_fields:
success = \
self._fixed_mask_application_field_allocation(
seen_fields, application_field, required_bits)
if success:
# update the fixed keys fields
seen_fields[FIXED_KEY_NAME] = new_fixed_keys
return True
else:
# update the fixed keys fields
seen_fields[FIXED_KEY_NAME] = new_fixed_keys
return True
def _fixed_mask_application_field_allocation(
self, seen_fields, application_field, required_bits):
"""
:param seen_fields:
:param application_field:
:param required_bits:
:return: bool true if it was able to adjust to the field,\
false otherwise
"""
fixed_mask_masks = \
seen_fields[FIXED_MASK_NAME]
if len(fixed_mask_masks) > 1:
# generate the bit generator for the first field, as this is the
# premise of all the other fields to meet
keys = list(fixed_mask_masks.keys)
fields = fixed_mask_masks[keys[0]]
searching = True
found = False
bit_generator = self._generate_bits_that_satisfy_constraints(
fields, required_bits)
# search till we find a application field which meets all
# requirements
while searching:
application_field, new_fields = \
self._update_fixed_mask_field_set(bit_generator)
# check that all the other fields can meet the value for this
# field's application field value
valid = True
keys = list(fixed_mask_masks.keys)
no_keys = len(keys)
for field_key_index in range(1, no_keys):
fields = fixed_mask_masks[keys[field_key_index]]
application_field_value = \
self._determine_fixed_mask_application_field_value(
fields[0].value, application_field.hi,
application_field.lo)
if application_field_value != application_field.value:
valid = False
# if valid, update all fields
if valid:
# set the first one which has had its already generated
keys = list(fixed_mask_masks.keys())
fixed_mask_masks[keys[0]] = new_fields
# set the rest of the fields which need computing
for field_key_index in range(1, no_keys):
fields = fixed_mask_masks[keys[field_key_index]]
# create new fields
new_fields = self.\
_adjust_fixed_mask_fields_for_application_field(
fields[0].value, application_field.hi,
application_field.value)
# update the fields for this fixed mask
fixed_mask_masks[keys[field_key_index]] = new_fields
# update searcher to state that we'vefound what
# we've been looking for
searching = False
found = True
# if exited but not found a solution, tell parent function
if not found:
return False
else: # one fixed mask field
if application_field is None: # no fixed keys seen
keys = list(fixed_mask_masks.keys())
fields = fixed_mask_masks[keys[0]]
bit_generator = self._generate_bits_that_satisfy_constraints(
fields, required_bits)
_, new_fields = self._update_fixed_mask_field_set(
bit_generator)
# update the seen fields object with new fields
keys = list(fixed_mask_masks.keys())
fixed_mask_masks[keys[0]] = new_fields
else:
# fixed key seen so application field exists
keys = list(fixed_mask_masks.keys())
fields = fixed_mask_masks[keys[0]]
# get the application field value for a application field
application_field_value = \
self._determine_fixed_mask_application_field_value(
fields[0].value, application_field.hi,
application_field.lo)
# check that the application for the fixed mask is different
# than the fixed key mask, if not return false stating it could
# not carry on with this application field
if application_field_value == application_field.value:
return False
# create new fields
new_fields = \
self._adjust_fixed_mask_fields_for_application_field(
fields[0].value, application_field.hi,
application_field_value)
# update the fields for this fixed mask
keys = list(fixed_mask_masks.keys())
fixed_mask_masks[keys[0]] = new_fields
# state that we did fix the fixed mask stuff with this application
# field
return True
def _update_fixed_mask_field_set(self, bit_generator, fixed_key=None):
""" Update the first mask's field set so that it has a application\
field with the correct value and the old fields are adjusted to\
take into account the loss of a number of bits
:param bit_generator: the generator which can provide different sets\
of bits usable for the application field
:return: the application field that was created
"""
# get next attempt
(bit_hi, bit_lo, original_mask) = bit_generator.next()
# determine value of the application field
if fixed_key is None:
application_field_value = \
self._determine_fixed_mask_application_field_value(
original_mask, bit_hi, bit_lo)
else:
application_field_value = \
self._determine_fixed_mask_application_field_value(
fixed_key, bit_hi, bit_lo)
# adjust the fields to reflect new application field scopes
fields = self._adjust_fixed_mask_fields_for_application_field(
original_mask, bit_hi, application_field_value)
# locate the application field bit in the new fields and adjust the
# tags to be routing, name to APPLICATION_DIVIDER_FIELD_NAME and the
# value to the located application value
distance_in_bits = bit_hi - bit_lo
application_field = None
if distance_in_bits == 1:
for field in fields:
if field.hi == bit_hi and field.lo == bit_hi:
field.value = application_field_value
field.tag = SUPPORTED_TAGS.ROUTING.name
field.name = APPLICATION_DIVIDER_FIELD_NAME
application_field = field
else:
for field in fields:
if field.hi == bit_hi and field.lo == bit_lo:
field.value = application_field_value
field.tag = SUPPORTED_TAGS.ROUTING.name
field.name = APPLICATION_DIVIDER_FIELD_NAME
application_field = field
return application_field, fields
@staticmethod
def _adjust_fixed_mask_fields_for_application_field(
original_mask, bit_hi, bit_values):
""" Take the old mask and the application field specs and make a set\
of new fields to compensate for the placement of the application\
field
:param original_mask: the old mask used by the fields
:param bit_hi: the point where the application space starts
:param bit_values: the value being used by fixed masks for the\
application field
:return: the adjusted fields
"""
if bit_values == 0:
bit_values = 1
value = bit_values << bit_hi
# invert the mask
inverted_value = ~value
# make new mask from inverted and original mask
new_mask = original_mask & inverted_value
# convert new mask into fields
fields = field_utilities.convert_mask_into_fields(new_mask)
# adjust the fields to link to the original mask (needed for mapping
# later on)
for field in fields:
field.value = original_mask
return fields
def _extract_keys_and_masks_from_bit_field(
self, partition, bit_field_space, n_keys_map, seen_mask_instances):
""" Take the bit field space and a partition and locate the keys and\
masks for that partition given all its constraints
:param partition: the partition to extract keys and masks for
:param bit_field_space: the bit field space
:param n_keys_map: the edge to n_keys map
:param seen_mask_instances: a count of how many fixed mask instances\
have been seen, so that it can increment the routing part\
of the fixed mask fields.
:return: the routing keys and masks for the partition and the number\
of seen fixed masks instances
"""
routing_keys_and_masks = list()
application_keys_and_masks = list()
fixed_key_constraints = utility_calls.locate_constraints_of_type(
partition.constraints, FixedKeyAndMaskConstraint)
fixed_mask_constraints = utility_calls.locate_constraints_of_type(
partition.constraints, FixedMaskConstraint)
fixed_field_constraints = utility_calls.locate_constraints_of_type(
partition.constraints, FixedKeyFieldConstraint)
flexi_field_constraints = utility_calls.locate_constraints_of_type(
partition.constraints, FlexiKeyFieldConstraint)
continuous_constraints = utility_calls.locate_constraints_of_type(
partition.constraints, ContiguousKeyRangeContraint)
if len(fixed_key_constraints) > 0:
fixed_keys_fields = \
self._field_mapper[fixed_key_constraints[0].keys_and_masks[0]]
# tracker for iterator fields
range_based_fixed_key_fields = list()
# add the app field (must exist in this situation)
inputs = dict()
inputs[APPLICATION_DIVIDER_FIELD_NAME] = \
self._fixed_key_application_field_value
# handle the inputs for static parts of the mask
for fixed_key_field in fixed_keys_fields:
app_field_space = bit_field_space(**inputs)
tag = list(app_field_space.get_tags(
str(fixed_key_field.name)))[0]
if tag == SUPPORTED_TAGS.ROUTING.name:
inputs[str(fixed_key_field.name)] = fixed_key_field.value
elif tag == SUPPORTED_TAGS.APPLICATION.name:
range_based_fixed_key_fields.append(fixed_key_field)
else:
raise exceptions.PacmanConfigurationException(
"Don't know this tag field, sorry")
if len(range_based_fixed_key_fields) > 1:
raise exceptions.PacmanConfigurationException(
"Multiple fixed key fields are not supported")
# get n keys from n_keys_map for the range based mask part
n_keys = n_keys_map.n_keys_for_partition(partition)
# generate keys
for key_index in range(0, n_keys):
inputs[str(range_based_fixed_key_fields[0].name)] = key_index
# routing keys and masks
routing_key = bit_field_space(**inputs).get_value(
tag=SUPPORTED_TAGS.ROUTING.name)
routing_mask = bit_field_space(**inputs).get_mask(
tag=SUPPORTED_TAGS.ROUTING.name)
routing_keys_and_masks.append(BaseKeyAndMask(routing_key,
routing_mask))
# application keys and masks
application_key = bit_field_space(**inputs).get_value(
tag=SUPPORTED_TAGS.APPLICATION.name)
application_mask = bit_field_space(**inputs).get_mask(
tag=SUPPORTED_TAGS.APPLICATION.name)
application_keys_and_masks.append(BaseKeyAndMask(
application_key, application_mask))
elif len(fixed_mask_constraints) > 0:
# get constraint and its fields
fixed_mask_constraint_mask = fixed_mask_constraints[0].mask
fixed_mask_fields = self._field_mapper[fixed_mask_constraint_mask]
# tracker for iterator fields
range_based_fixed_mask_fields = list()
# add the app field (must exist in this situation)
inputs = dict()
inputs[APPLICATION_DIVIDER_FIELD_NAME] = \
self._fixed_mask_application_field_value
# handle the inputs for static parts of the mask
for fixed_mask_field in fixed_mask_fields:
app_field_space = bit_field_space(**inputs)
tag = list(app_field_space.get_tags(str(fixed_mask_field)))[0]
if tag == SUPPORTED_TAGS.ROUTING.name:
inputs[str(fixed_mask_field)] = seen_mask_instances
elif tag == SUPPORTED_TAGS.APPLICATION.name:
range_based_fixed_mask_fields.append(fixed_mask_field)
else:
raise exceptions.PacmanConfigurationException(
"I don't recognise this tag. sorry")
if len(range_based_fixed_mask_fields) > 1:
raise exceptions.PacmanConfigurationException(
"Multiple fixed mask fields are not supported")
# get n keys from n_keys_map for the range based mask part
n_keys = n_keys_map.n_keys_for_partition(partition)
# generate keys
for key_index in range(0, n_keys):
inputs[str(range_based_fixed_mask_fields[0])] = key_index
# routing keys and masks
routing_key = bit_field_space(**inputs).get_value(
tag=SUPPORTED_TAGS.ROUTING.name)
routing_mask = bit_field_space(**inputs).get_mask(
tag=SUPPORTED_TAGS.ROUTING.name)
routing_keys_and_masks.append(BaseKeyAndMask(routing_key,
routing_mask))
# application keys and masks
application_key = bit_field_space(**inputs).get_value(
tag=SUPPORTED_TAGS.APPLICATION.name)
application_mask = bit_field_space(**inputs).get_mask(
tag=SUPPORTED_TAGS.APPLICATION.name)
application_keys_and_masks.append(BaseKeyAndMask(
application_key, application_mask))
# update the seen mask instances so the next one gets a new key
seen_mask_instances += 1
elif len(fixed_field_constraints) > 0:
# TODO: need to fill this out
raise exceptions.PacmanConfigurationException(
"Fixed field constraints are not supported")
elif len(flexi_field_constraints) > 0:
inputs = dict()
# if there's a application field, add the value
if len(self._flexi_field_application_field_values) != 0:
inputs[APPLICATION_DIVIDER_FIELD_NAME] = \
self._flexi_field_application_field_values[
flexi_field_constraints[0].fields[0].name]
# collect flexible fields by group
range_based_fixed_mask_fields = list()
for field in flexi_field_constraints[0].fields:
if field.value is not None:
inputs[field.name] = field.value
else:
range_based_fixed_mask_fields.append(field)
# if the set contains a range_based flexible field do search
if len(range_based_fixed_mask_fields) != 0:
routing_keys_and_masks, application_keys_and_masks = \
self._handle_set_of_flexi_range_fields(
range_based_fixed_mask_fields, bit_field_space,
routing_keys_and_masks, application_keys_and_masks,
inputs, 0)
else: # no range, just grab the key and value
key = bit_field_space(**inputs).get_value()
mask = bit_field_space(**inputs).get_mask()
routing_keys_and_masks.append(BaseKeyAndMask(key, mask))
# if there's a continuous constraint check, check the keys for
# continuity
if len(continuous_constraints) != 0:
are_continuous = self._check_keys_are_continuous(
application_keys_and_masks)
if not are_continuous:
raise exceptions.PacmanConfigurationException(
"These keys returned from the bitfield are"
"not continuous. Therefore cannot be used")
# if continuous, we only need the first key, so drop the rest
routing_keys_and_masks = routing_keys_and_masks[0:1]
# return keys and masks
return routing_keys_and_masks, seen_mask_instances
@staticmethod
def _check_keys_are_continuous(keys_and_masks):
""" Searches though the keys and masks and checks if they are all\
continuous
:param keys_and_masks: the keys to check if they are continuous
:return: true if they are continuous, false otherwise
"""
last_key = None
for keys_and_mask in keys_and_masks:
if last_key is None:
last_key = keys_and_mask.key
else:
if last_key + 1 != keys_and_mask.key:
return False
last_key = keys_and_mask.key
return True
def _handle_set_of_flexi_range_fields(
self, range_based_flexi_fields, bit_field_space,
routing_keys_and_masks, application_keys_and_masks, inputs,
position):
""" Take a set of flexible fields which are range based and deduce\
the routing keys and masks for the set
:param range_based_flexi_fields: the set of range based flexible fields
:param bit_field_space: the bit field space
:param routing_keys_and_masks: set of routing keys and masks built\
from previous iterations
:param application_keys_and_masks: the application keys and masks\
from previous iterations
:param inputs: parameters used by the bit field to get keys
:param position: the position within the set (used for exit condition)
:return: routing keys and application keys for the flexible field set
"""
for value in range(0,
range_based_flexi_fields[position].instance_n_keys):
inputs[range_based_flexi_fields[position].name] = value
if position < len(range_based_flexi_fields):
# routing keys and masks
routing_key = bit_field_space(**inputs).get_value(
tag=SUPPORTED_TAGS.ROUTING.name)
routing_mask = bit_field_space(**inputs).get_mask(
tag=SUPPORTED_TAGS.ROUTING.name)
routing_keys_and_masks.append(BaseKeyAndMask(routing_key,
routing_mask))
# application keys and masks
application_key = bit_field_space(**inputs).get_value(
tag=SUPPORTED_TAGS.APPLICATION.name)
application_mask = bit_field_space(**inputs).get_mask(
tag=SUPPORTED_TAGS.APPLICATION.name)
application_keys_and_masks.append(BaseKeyAndMask(
application_key, application_mask))
else:
# not at the end, keep iterating but add the results from
# the iterations before going back upwards
position += 1
other_routing_keys_and_masks, \
other_application_keys_and_masks = \
self._handle_set_of_flexi_range_fields(
range_based_flexi_fields, bit_field_space,
routing_keys_and_masks, application_keys_and_masks,
inputs, position)
# add keys before going upwards
routing_keys_and_masks.extend(other_routing_keys_and_masks)
application_keys_and_masks.extend(
other_application_keys_and_masks)
# exit this iteration
return routing_keys_and_masks, application_keys_and_masks
def _create_application_space_in_the_bit_field_space(
self, bit_field_space, fields, field_positions):
""" Take the fields seen and adjusted for bitfield and the bitfield\
object and builds the fields for being assigned to keys and masks
:param bit_field_space: th bit field space
:param fields: the fields which have been adjusted accordingly to\
work in the bit field scope
:param field_positions: The positions of all fields seen
"""
# locate the application field if it exists
application_field, application_field_spare_values = \
self._locate_application_field(fields)
# create the application based field if its not none
if application_field is not None:
length = (application_field.hi - application_field.lo) + 1
# add field to positions
field_positions.add((application_field.hi, application_field.lo))
# add field to bit field
bit_field_space.add_field(
application_field.name, length, application_field.lo,
tags=SUPPORTED_TAGS.ROUTING.name)
# iterate though the fields, adding fields to the system
for field in fields:
# handle fixed mask fields
if field == FIXED_MASK_NAME:
fixed_fields = fields[FIXED_MASK_NAME]
for fixed_field_key in fixed_fields:
# create the top level bit field space for this set
fixed_field_list = fixed_fields[fixed_field_key]
fixed_field_application_field_value = \
self._locate_field_value_by_name(
fixed_field_list, APPLICATION_DIVIDER_FIELD_NAME)
internal_field_space = self._create_internal_field_space(
bit_field_space, fixed_field_application_field_value,
application_field)
# iterate though rest of the fields adding them to the
# lower position field space
for fixed_field in fixed_field_list:
if fixed_field.name != APPLICATION_DIVIDER_FIELD_NAME:
length = (fixed_field.hi - fixed_field.lo) + 1
# add to field positions
field_positions.add(
(fixed_field.hi, fixed_field.lo))
# add to bit field
internal_field_space.add_field(
"{}".format(fixed_field.name), length,
fixed_field.lo, fixed_field.tag)
if fixed_field.value not in self._field_mapper:
self._field_mapper[fixed_field.value] = list()
self._field_mapper[fixed_field.value].append(
fixed_field.name)
else:
self._fixed_mask_application_field_value = \
fixed_field.value
# handle fixed field
elif field == FIXED_FIELD_NAME:
# TODO: need to check this bit out
raise exceptions.PacmanConfigurationException(
"Fixed Fields are not currently supported")
# handle fixed key fields
elif field == FIXED_KEY_NAME:
fixed_keys = \
fields[FIXED_KEY_NAME]
# handle application field
first_fields = fixed_keys[0][1]
application_field_value = self._locate_field_value_by_name(
first_fields, APPLICATION_DIVIDER_FIELD_NAME)
internal_bit_map_space = self._create_internal_field_space(
bit_field_space, application_field_value,
application_field)
# record the fixed key application field value for future use
self._fixed_key_application_field_value = \
application_field_value
# handle the rest of the fields
for (key_and_mask, fixed_key_fields) in fixed_keys:
for fixed_key_field in fixed_key_fields:
if (fixed_key_field.name !=
APPLICATION_DIVIDER_FIELD_NAME):
length = (fixed_key_field.hi -
fixed_key_field.lo) + 1
# add field to field positions
field_positions.add((fixed_key_field.ki,
fixed_key_field.lo))
# add field to bit field space
internal_bit_map_space.add_field(
str(fixed_key_field.name), length,
fixed_key_field.lo, fixed_key_field.tag)
if key_and_mask not in self._field_mapper:
self._field_mapper[key_and_mask] = list()
# add field for the field mapper
self._field_mapper[key_and_mask].append(
fixed_key_field)
# set the value for the field
inputs = dict()
# deduce the value for the key for this field
value = self._deduce_key_value_for_field(
key_and_mask.key, fixed_key_field)
fixed_key_field.value = value
inputs[str(fixed_key_field.name)] = value
# update value in bit field space
internal_bit_map_space(**inputs)
else: # handle flexible field stuff
if application_field is not None:
# create a new bit field where everything is linked off a
# internal app field
internal_value = application_field_spare_values[0]
application_field_spare_values.remove(internal_value)
internal_bit_field_space = \
self._create_internal_field_space(
bit_field_space, internal_value, application_field)
self._flexi_field_application_field_values[field] = \
internal_value
# handle the flexible fields
self._create_flexi_field_space_in_the_bit_field_space(
internal_bit_field_space, field, fields)
else:
self._create_flexi_field_space_in_the_bit_field_space(
bit_field_space, field, fields)
def _locate_application_field(self, fields):
""" Search through the fields looking for the application field, and\
when its found, deduces what's left for space space for other\
fields
:param fields: the set of field types and the fields
:return: the application field and the spare values of the field
"""
application_field = None
application_field_spare_values = None
# search for the field with the correct tag
for field in fields:
# if a fixed mask field, locate correct application field and value
if field == FIXED_MASK_NAME:
fixed_fields = \
fields[FIXED_MASK_NAME]
for fixed_field_key in fixed_fields:
fixed_field_list = fixed_fields[fixed_field_key]
for fixed_field in fixed_field_list:
# if the field is the correct field, mark and deduce
# usable values
if fixed_field.name == APPLICATION_DIVIDER_FIELD_NAME:
if application_field is None:
application_field = fixed_field
application_field_spare_values = \
self._deduce_application_field_space(
application_field)
else:
application_field_spare_values.remove(
fixed_field.value)
elif field == FIXED_KEY_NAME:
fixed_keys_fields = \
fields[FIXED_KEY_NAME]
for (_, fixed_key_fields) in fixed_keys_fields:
for fixed_key_field in fixed_key_fields:
if (fixed_key_field.name ==
APPLICATION_DIVIDER_FIELD_NAME):
if application_field is None:
application_field = fixed_key_field
application_field_spare_values = \
self._deduce_application_field_space(
application_field)
else:
application_field_spare_values.remove(
fixed_key_field.value)
elif field == FIXED_FIELD_NAME:
# TODO: NEED TO COMPLETE THIS BIT
raise exceptions.PacmanConfigurationException(
"Fixed fields are not currently supported")
return application_field, application_field_spare_values
def _create_flexi_field_space_in_the_bit_field_space(
self, bit_field_space, field, fields):
""" Create the fields in the bit field space for the flexible fields
:param bit_field_space: the bit field space
:param field: the field that needs placing into the bitfield space
:param fields: ???????
:rtype: None
"""
# field must be a flexible field, work accordingly
example_entry = self._check_entries_are_tag_consistent(fields, field)
if example_entry.tag is None:
bit_field_space.add_field(field)
else:
bit_field_space.add_field(field, tags=example_entry.tag)
for field_instance in fields[field]:
# only carry on if there's more to create
if len(fields[field][field_instance]) > 0:
# create next level
internal_bit_field = self._create_internal_field_space(
bit_field_space, field_instance.value, field_instance)
# deal with next level hierarchy
for nested_field in fields[field][field_instance]:
self._create_flexi_field_space_in_the_bit_field_space(
internal_bit_field, nested_field,
fields[field][field_instance])
# bottom level
if field_instance.instance_n_keys is not None:
for value in range(0, field_instance.instance_n_keys):
self._create_internal_field_space(
bit_field_space, value, field_instance)
def _determine_groups(self, machine_graph, graph_mapper, graph,
n_keys_map, progress_bar):
routing_info_allocator_utilities.check_types_of_edge_constraint(
machine_graph)
for partition in machine_graph.outgoing_edge_partitions:
fixed_key_constraints = \
utility_calls.locate_constraints_of_type(
partition.constraints,
FixedKeyAndMaskConstraint)
fixed_mask_constraints = \
utility_calls.locate_constraints_of_type(
partition.constraints,
FixedMaskConstraint)
fixed_field_constraints = \
utility_calls.locate_constraints_of_type(
partition.constraints,
FixedKeyFieldConstraint)
if (len(fixed_key_constraints) == 0 and
len(fixed_mask_constraints) == 0 and
len(fixed_field_constraints) == 0):
self.add_field_constraints(
partition, graph_mapper, graph, n_keys_map)
progress_bar.update()
@staticmethod
def _determine_fixed_mask_application_field_value(mask, bit_hi, bit_lo):
""" Determine the value of the application field for a given mask
:param mask: the mask to deduce the value for
:param bit_hi: the bit high index for where the application field\
finishes
:param bit_lo: the bit low index for where the application field starts
:return: the value for the application for this mask
"""
bit_value = mask >> int(bit_lo)
mask = int(math.pow((bit_hi - bit_lo), 2))
bit_value &= mask
return bit_value
@staticmethod
def _generate_bits_that_satisfy_constraints(
fixed_mask_fields, required_bits):
""" Generator for getting valid bits from the first fixed mask
:param fixed_mask_fields: the fields from this fixed mask
:param required_bits: the number of bits required to match the types
:type required_bits: int
:return: the high and low bit index for where the region can exist,\
as well as the original mask this generator is working on.
"""
routing_fields = list()
# locate fields valid for generating collections
field = None
for field in fixed_mask_fields:
if field.tag == SUPPORTED_TAGS.ROUTING.name:
routing_fields.append(field)
# sort fields based on high value
routing_fields.sort(key=lambda rout_field: rout_field.hi)
# locate next set of bits to yield to higher function
for routing_field in routing_fields:
if routing_field.hi - routing_field.lo >= required_bits:
current_hi = routing_field.hi
while (current_hi - required_bits) > routing_field.lo:
yield (current_hi, current_hi - required_bits, field.value)
current_hi -= 1
@staticmethod
def _deduce_key_value_for_field(key, fixed_key_field):
new_key = key >> fixed_key_field.lo
mask = int(math.pow(2, ((fixed_key_field.hi -
fixed_key_field.lo) + 1))) - 1
new_key &= mask
return new_key
@staticmethod
def _deduce_application_field_space(application_field):
""" Given an application field, deduce the spare spaces available to it
:param application_field: the application field in question
:return: the spare values
"""
# get length of field
length = (application_field.hi - application_field.lo) + 1
application_field_spare_values = list()
# build available values
for value in range(0, ((2 ^ length) - 1)):
application_field_spare_values.append(value)
# remove the one this application field is built from
application_field_spare_values.remove(application_field.value)
return application_field_spare_values
@staticmethod
def _check_entries_are_tag_consistent(fields, field):
""" Check that the tags of the fields are consistent with each other
:param fields: the set of fields which could have tags
:param field: field its comparing against
:return: the first field
"""
first = None
for field_instance in fields[field]:
if first is None:
first = field_instance
elif field_instance.tag != first.tag:
raise exceptions.PacmanConfigurationException(
"Two fields with the same id, but with different tags. "
"This is deemed an error and therefore please fix before"
"trying again. thanks you")
return first
[docs] @staticmethod
def add_field_constraints(
partition, graph_mapper, graph, n_keys_map):
""" Search though the graph adding field constraints for the key\
allocator
"""
fields = list()
verts = list(graph.vertices)
vertex = partition.pre_vertex
app_vertex = graph_mapper.get_application_vertex(vertex)
vertices = list(graph_mapper.get_machine_vertices(app_vertex))
# pop based flexible field
fields.append(FlexiField(
flexi_field_name="Population", value=verts.index(app_vertex),
tag=SUPPORTED_TAGS.ROUTING.name, nested_level=0))
# part-population flexible field
fields.append(FlexiField(
flexi_field_name="PartPopulation{}".format(
verts.index(app_vertex)),
tag=SUPPORTED_TAGS.ROUTING.name, value=vertices.index(vertex),
nested_level=1))
fields.append(FlexiField(
flexi_field_name="POP({}:{})Keys".format(
verts.index(app_vertex), vertices.index(vertex)),
tag=SUPPORTED_TAGS.APPLICATION.name,
instance_n_keys=n_keys_map.n_keys_for_partition(partition),
nested_level=2))
# add constraint to the edge
partition.add_constraint(FlexiKeyFieldConstraint(fields))
@staticmethod
def _create_internal_field_space(higher_space, field_value, field):
inputs = dict()
inputs[field.name] = field_value
return higher_space(**inputs)
@staticmethod
def _locate_field_value_by_name(field_list, field_name):
for field in field_list:
if field.name == field_name:
return field.value
return None