diff --git a/spynnaker/gsyn_tools.py b/spynnaker/gsyn_tools.py index 6cbcf077b6e..80ec836511a 100644 --- a/spynnaker/gsyn_tools.py +++ b/spynnaker/gsyn_tools.py @@ -54,7 +54,7 @@ def check_sister_gysn(sister, n_neurons, runtime, gsyn): """ Compare an arrays of conductances with baseline data from a file next\ to a specified module. For testing. - :param sister: A module. The file read from will be ``gsyn.data`` \ + :param sister: A module. The file read from will be ``gsyn.data`` adjacent to this module. :param n_neurons: The number of neurons that produced the data. :param runtime: The length of time that the generated data represents. diff --git a/spynnaker/pyNN/abstract_spinnaker_common.py b/spynnaker/pyNN/abstract_spinnaker_common.py index 1c55c489501..a029f488eb1 100644 --- a/spynnaker/pyNN/abstract_spinnaker_common.py +++ b/spynnaker/pyNN/abstract_spinnaker_common.py @@ -17,28 +17,44 @@ import math import os from six import with_metaclass - -from spinn_front_end_common.utilities.constants import \ - MICRO_TO_MILLISECOND_CONVERSION from spinn_utilities.abstract_base import AbstractBase from spinn_utilities.log import FormatAdapter +from spinn_front_end_common.utilities.constants import ( + MICRO_TO_MILLISECOND_CONVERSION) from spinn_front_end_common.interface.abstract_spinnaker_base import ( AbstractSpinnakerBase) from spinn_front_end_common.utilities.exceptions import ConfigurationException from spinn_front_end_common.utility_models import CommandSender from spinn_front_end_common.utilities.utility_objs import ExecutableFinder -from spinn_front_end_common.utilities import globals_variables +from spinn_front_end_common.utilities.globals_variables import unset_simulator from spinn_front_end_common.utilities.helpful_functions import read_config -from spynnaker.pyNN import extra_algorithms, model_binaries -from spynnaker.pyNN.utilities import constants +from spynnaker.pyNN.utilities.constants import ( + MAX_DELAY_BLOCKS, MAX_SUPPORTED_DELAY_TICS, + MAX_TIMER_TICS_SUPPORTED_PER_BLOCK, MIN_SUPPORTED_DELAY) from spynnaker.pyNN.spynnaker_simulator_interface import ( SpynnakerSimulatorInterface) from spynnaker.pyNN.utilities.extracted_data import ExtractedData from spynnaker import __version__ as version +from spynnaker.pyNN.utilities.utility_calls import round_up +from spynnaker.pyNN import model_binaries logger = FormatAdapter(logging.getLogger(__name__)) +def _extra_algo_path(): + """ List containing the names of the extra algorithms config files. + + .. note:: + Need to `import` within this to avoid an import loop. + + :rtype: list(str) + """ + import spynnaker.pyNN.extra_algorithms + return [os.path.join( + os.path.dirname(spynnaker.pyNN.extra_algorithms.__file__), + "algorithms_metadata.xml")] + + class AbstractSpiNNakerCommon(with_metaclass( AbstractBase, AbstractSpinnakerBase, SpynnakerSimulatorInterface)): """ Main interface for neural code. @@ -85,7 +101,7 @@ def __init__( :param user_extra_algorithms_pre_run: :type user_extra_algorithms_pre_run: list(str) or None :param time_scale_factor: - :type time_scale_factor: + :type time_scale_factor: int or None :param extra_post_run_algorithms: :type extra_post_run_algorithms: list(str) or None :param extra_mapping_algorithms: @@ -93,7 +109,7 @@ def __init__( :param extra_load_algorithms: :type extra_load_algorithms: list(str) or None :param front_end_versions: - :type front_end_versions: + :type front_end_versions: list(tuple(str,str)) or None """ # pylint: disable=too-many-arguments, too-many-locals @@ -114,10 +130,7 @@ def __init__( # create XML path for where to locate sPyNNaker related functions when # using auto pause and resume - extra_algorithm_xml_path = list() - extra_algorithm_xml_path.append(os.path.join( - os.path.dirname(extra_algorithms.__file__), - "algorithms_metadata.xml")) + extra_algorithm_xml_path = _extra_algo_path() if user_extra_algorithm_xml_path is not None: extra_algorithm_xml_path.extend(user_extra_algorithm_xml_path) @@ -238,7 +251,7 @@ def _set_up_timings( self.set_up_timings(timestep, time_scale_factor) else: self.set_up_timings( - math.ceil(timestep * MICRO_TO_MILLISECOND_CONVERSION), + round_up(timestep * MICRO_TO_MILLISECOND_CONVERSION), time_scale_factor) # Sort out the minimum delay @@ -248,7 +261,7 @@ def _set_up_timings( raise ConfigurationException( "Pacman does not support min delays below {} ms with the " "current machine time step".format( - constants.MIN_SUPPORTED_DELAY * self.machine_time_step)) + MIN_SUPPORTED_DELAY * self.machine_time_step)) if min_delay is not None: self.__min_delay = min_delay else: @@ -256,11 +269,9 @@ def _set_up_timings( self.machine_time_step / MICRO_TO_MILLISECOND_CONVERSION) # Sort out the maximum delay - natively_supported_delay_for_models = \ - constants.MAX_SUPPORTED_DELAY_TICS + natively_supported_delay_for_models = MAX_SUPPORTED_DELAY_TICS delay_extension_max_supported_delay = ( - constants.MAX_DELAY_BLOCKS * - constants.MAX_TIMER_TICS_SUPPORTED_PER_BLOCK) + MAX_DELAY_BLOCKS * MAX_TIMER_TICS_SUPPORTED_PER_BLOCK) max_delay_tics_supported = \ natively_supported_delay_for_models + \ delay_extension_max_supported_delay @@ -276,8 +287,7 @@ def _set_up_timings( else: self.__max_delay = ( max_delay_tics_supported * ( - self.machine_time_step / - MICRO_TO_MILLISECOND_CONVERSION)) + self.machine_time_step / MICRO_TO_MILLISECOND_CONVERSION)) # Sort out the time scale factor if not user specified # (including config) @@ -387,7 +397,6 @@ def stop(self, turn_off_machine=None, clear_routing_tables=None, :param clear_tags: informs the tool chain if it should clear the tags off the machine at stop :type clear_tags: bool or None - :rtype: None """ # pylint: disable=protected-access for population in self._populations: @@ -396,14 +405,13 @@ def stop(self, turn_off_machine=None, clear_routing_tables=None, super(AbstractSpiNNakerCommon, self).stop( turn_off_machine, clear_routing_tables, clear_tags) self.reset_number_of_neurons_per_core() - globals_variables.unset_simulator(self) + unset_simulator(self) def run(self, run_time): """ Run the model created. :param run_time: the time (in milliseconds) to run the simulation for :type run_time: float or int - :rtype: None """ # pylint: disable=protected-access @@ -428,7 +436,6 @@ def register_binary_search_path(search_path): """ Register an additional binary search path for executables. :param str search_path: absolute search path for binaries - :rtype: None """ # pylint: disable=protected-access AbstractSpiNNakerCommon.__EXECUTABLE_FINDER.add_path(search_path) diff --git a/spynnaker/pyNN/connections/ethernet_control_connection.py b/spynnaker/pyNN/connections/ethernet_control_connection.py index 320d8f3cc43..c74ab85be76 100644 --- a/spynnaker/pyNN/connections/ethernet_control_connection.py +++ b/spynnaker/pyNN/connections/ethernet_control_connection.py @@ -13,12 +13,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging from spinn_front_end_common.utility_models import MultiCastCommand from spinn_front_end_common.utilities.connections import LiveEventConnection -logger = logging.getLogger(__name__) - class EthernetControlConnection(LiveEventConnection): """ A connection that can translate Ethernet control messages received\ diff --git a/spynnaker/pyNN/external_devices_models/__init__.py b/spynnaker/pyNN/external_devices_models/__init__.py index 4d5871a8583..15eab73669e 100644 --- a/spynnaker/pyNN/external_devices_models/__init__.py +++ b/spynnaker/pyNN/external_devices_models/__init__.py @@ -17,7 +17,7 @@ from .abstract_ethernet_sensor import AbstractEthernetSensor from .abstract_ethernet_translator import AbstractEthernetTranslator from .abstract_multicast_controllable_device import ( - AbstractMulticastControllableDevice) + AbstractMulticastControllableDevice, SendType) from .arbitrary_fpga_device import ArbitraryFPGADevice from .external_device_lif_control import ExternalDeviceLifControl from .external_spinnaker_link_cochlea_device import ExternalCochleaDevice @@ -32,5 +32,5 @@ "AbstractEthernetTranslator", "ArbitraryFPGADevice", "AbstractMulticastControllableDevice", "ExternalDeviceLifControl", "ExternalCochleaDevice", "ExternalFPGARetinaDevice", - "MunichMotorDevice", "MunichRetinaDevice", + "MunichMotorDevice", "MunichRetinaDevice", "SendType", "ThresholdTypeMulticastDeviceControl"] diff --git a/spynnaker/pyNN/external_devices_models/abstract_ethernet_translator.py b/spynnaker/pyNN/external_devices_models/abstract_ethernet_translator.py index 45bb7846079..0e1c62bcddb 100644 --- a/spynnaker/pyNN/external_devices_models/abstract_ethernet_translator.py +++ b/spynnaker/pyNN/external_devices_models/abstract_ethernet_translator.py @@ -33,5 +33,4 @@ def translate_control_packet(self, multicast_packet): :param multicast_packet: A received multicast packet :type multicast_packet: ~spinnman.messages.eieio.data_messages.AbstractEIEIODataElement - :rtype: None """ diff --git a/spynnaker/pyNN/external_devices_models/external_device_lif_control.py b/spynnaker/pyNN/external_devices_models/external_device_lif_control.py index 7aaec28d7f0..baaaff54372 100644 --- a/spynnaker/pyNN/external_devices_models/external_device_lif_control.py +++ b/spynnaker/pyNN/external_devices_models/external_device_lif_control.py @@ -13,12 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging from spinn_utilities.overrides import overrides from spinn_front_end_common.utilities.exceptions import ConfigurationException from spynnaker.pyNN.models.neuron import AbstractPyNNNeuronModelStandard -from spynnaker.pyNN.models.defaults import default_initial_values,\ - default_parameters +from spynnaker.pyNN.models.defaults import ( + default_initial_values, default_parameters) from spynnaker.pyNN.models.neuron.input_types import InputTypeCurrent from spynnaker.pyNN.models.neuron.neuron_models import ( NeuronModelLeakyIntegrateAndFire) @@ -27,8 +26,6 @@ from .threshold_type_multicast_device_control import ( ThresholdTypeMulticastDeviceControl) -logger = logging.getLogger(__name__) - class ExternalDeviceLifControl(AbstractPyNNNeuronModelStandard): """ Abstract control module for the PushBot, based on the LIF neuron,\ diff --git a/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py b/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py index 34c6ae6e234..8b7977c276a 100644 --- a/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py +++ b/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py @@ -14,7 +14,6 @@ # along with this program. If not, see . from collections import OrderedDict -import logging from spinn_utilities.overrides import overrides from pacman.model.constraints.key_allocator_constraints import ( FixedKeyAndMaskConstraint) @@ -26,8 +25,6 @@ from spynnaker.pyNN.models.neuron import AbstractPopulationVertex from .abstract_ethernet_controller import AbstractEthernetController -logger = logging.getLogger(__name__) - class ExternalDeviceLifControlVertex( AbstractPopulationVertex, diff --git a/spynnaker/pyNN/external_devices_models/external_spinnaker_link_fpga_retina_device.py b/spynnaker/pyNN/external_devices_models/external_spinnaker_link_fpga_retina_device.py index 2855636d2a7..f4e775fb65b 100644 --- a/spynnaker/pyNN/external_devices_models/external_spinnaker_link_fpga_retina_device.py +++ b/spynnaker/pyNN/external_devices_models/external_spinnaker_link_fpga_retina_device.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spinn_utilities.overrides import overrides from pacman.model.constraints.key_allocator_constraints import ( FixedKeyAndMaskConstraint) @@ -28,8 +26,6 @@ from spinn_front_end_common.utility_models import MultiCastCommand from spynnaker.pyNN.exceptions import SpynnakerException -logger = logging.getLogger(__name__) - def get_y_from_fpga_retina(key, mode): if mode == 128: diff --git a/spynnaker/pyNN/external_devices_models/munich_spinnaker_link_motor_device.py b/spynnaker/pyNN/external_devices_models/munich_spinnaker_link_motor_device.py index 81bc0e15856..0cfe3d8e376 100644 --- a/spynnaker/pyNN/external_devices_models/munich_spinnaker_link_motor_device.py +++ b/spynnaker/pyNN/external_devices_models/munich_spinnaker_link_motor_device.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging from spinn_utilities.overrides import overrides from pacman.executor.injection_decorator import inject_items from pacman.model.constraints.key_allocator_constraints import ( @@ -36,7 +35,6 @@ from spynnaker.pyNN.exceptions import SpynnakerException from spynnaker.pyNN.models.defaults import defaults -logger = logging.getLogger(__name__) MOTOR_PARTITION_ID = "MOTOR" diff --git a/spynnaker/pyNN/external_devices_models/push_bot/abstract_push_bot_output_device.py b/spynnaker/pyNN/external_devices_models/push_bot/abstract_push_bot_output_device.py index 822edc2ad97..0e03de73860 100644 --- a/spynnaker/pyNN/external_devices_models/push_bot/abstract_push_bot_output_device.py +++ b/spynnaker/pyNN/external_devices_models/push_bot/abstract_push_bot_output_device.py @@ -14,8 +14,7 @@ # along with this program. If not, see . from enum import Enum -from spynnaker.pyNN.external_devices_models\ - .abstract_multicast_controllable_device import SendType +from spynnaker.pyNN.external_devices_models import SendType class AbstractPushBotOutputDevice(Enum): diff --git a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_ethernet.py b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_ethernet.py index a51f02d0f15..54c46d2e080 100644 --- a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_ethernet.py +++ b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_ethernet.py @@ -16,11 +16,8 @@ from spynnaker.pyNN.models.defaults import default_initial_values from spynnaker.pyNN.external_devices_models.push_bot.push_bot_ethernet \ import ( - PushBotTranslator) + PushBotTranslator, get_pushbot_wifi_connection) from spynnaker.pyNN.external_devices_models import ExternalDeviceLifControl -from spynnaker.pyNN.external_devices_models.push_bot.push_bot_ethernet \ - import ( - get_pushbot_wifi_connection) class PushBotLifEthernet(ExternalDeviceLifControl): diff --git a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_spinnaker_link.py b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_spinnaker_link.py index 737abdd1d76..7e5e6d6245f 100644 --- a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_spinnaker_link.py +++ b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_control_modules/push_bot_lif_spinnaker_link.py @@ -12,14 +12,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spynnaker.pyNN.external_devices_models import ExternalDeviceLifControl from spynnaker.pyNN.protocols import MunichIoSpiNNakerLinkProtocol from spynnaker.pyNN.models.defaults import default_initial_values -logger = logging.getLogger(__name__) - class PushBotLifSpinnakerLink(ExternalDeviceLifControl): """ Control module for a PushBot connected to a SpiNNaker Link diff --git a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_retina_connection.py b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_retina_connection.py index fe016e1f7f3..da2ce6c4f49 100644 --- a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_retina_connection.py +++ b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_retina_connection.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from threading import RLock import numpy from spinnman.connections import ConnectionListener @@ -23,7 +21,6 @@ import ( PushBotRetinaResolution) -logger = logging.getLogger(__name__) _RETINA_PACKET_SIZE = BYTES_PER_SHORT diff --git a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_wifi_connection.py b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_wifi_connection.py index 14508dcf37c..48e23f5d395 100644 --- a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_wifi_connection.py +++ b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_ethernet/push_bot_wifi_connection.py @@ -14,11 +14,11 @@ # along with this program. If not, see . import logging -import platform import select import socket -import subprocess from six import raise_from +from spinn_utilities.overrides import overrides +from spinn_utilities.ping import Ping from spinnman.connections.abstract_classes import Listenable, Connection from spinnman.exceptions import SpinnmanIOException, SpinnmanTimeoutException from spinn_front_end_common.utilities.constants import BYTES_PER_KB @@ -102,28 +102,9 @@ def __init__(self, remote_host, remote_port=56000): # Set a general timeout on the socket self.__socket.settimeout(0) + @overrides(Connection.is_connected) def is_connected(self): - """ See\ - :py:meth:`~spinnman.connections.Connection.is_connected` - """ - if platform.platform().lower().startswith("windows"): - cmd_args = "-n 1 -w 1" - else: - cmd_args = "-c 1 -W 1" - - # check if machine is active and on the network - for _ in range(5): # Try up to five times... - # Start a ping process - process = subprocess.Popen( - "ping " + cmd_args + " " + self.__remote_ip_address, - shell=True, stdout=subprocess.PIPE) - process.wait() - if process.returncode == 0: - # ping worked - return True - - # If the ping fails this number of times, the host cannot be contacted - return False + return Ping.host_is_reachable(self.__remote_ip_address) @property def local_ip_address(self): @@ -190,18 +171,18 @@ def send(self, data): except Exception as e: # pylint: disable=broad-except raise_from(SpinnmanIOException(str(e)), e) + @overrides(Connection.close) def close(self): - """ See\ - :py:meth:`spinnman.connections.Connection.close` - """ try: self.__socket.shutdown(socket.SHUT_WR) except Exception: # pylint: disable=broad-except pass self.__socket.close() + @overrides(Listenable.is_ready_to_receive, extend_defaults=True) def is_ready_to_receive(self, timeout=0): return bool(select.select([self.__socket], [], [], timeout)[0]) + @overrides(Listenable.get_receive_method) def get_receive_method(self): return self.receive diff --git a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_spinnaker_link/push_bot_spinnaker_link_retina_device.py b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_spinnaker_link/push_bot_spinnaker_link_retina_device.py index dd583c6ab58..77d25a060a4 100644 --- a/spynnaker/pyNN/external_devices_models/push_bot/push_bot_spinnaker_link/push_bot_spinnaker_link_retina_device.py +++ b/spynnaker/pyNN/external_devices_models/push_bot/push_bot_spinnaker_link/push_bot_spinnaker_link_retina_device.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spinn_utilities.overrides import overrides from pacman.executor.injection_decorator import inject, supports_injection from pacman.model.graphs.application import ApplicationSpiNNakerLinkVertex @@ -21,8 +19,6 @@ from spynnaker.pyNN.external_devices_models.push_bot import ( AbstractPushBotRetinaDevice) -logger = logging.getLogger(__name__) - @supports_injection class PushBotSpiNNakerLinkRetinaDevice( @@ -36,15 +32,12 @@ def __init__( board_address=default_parameters['board_address'], label=default_parameters['label']): """ - :param spinnaker_link_id: - :param protocol: - :type protocol: MunichIoSpiNNakerLinkProtocol - :param resolution: - :type resolution: PushBotRetinaResolution - :param board_address: - :param label: + :param int spinnaker_link_id: + :param MunichIoSpiNNakerLinkProtocol protocol: + :param PushBotRetinaResolution resolution: + :param str board_address: + :param str label: """ - AbstractPushBotRetinaDevice.__init__(self, protocol, resolution) ApplicationSpiNNakerLinkVertex.__init__( self, spinnaker_link_id=spinnaker_link_id, diff --git a/spynnaker/pyNN/extra_algorithms/graph_edge_filter.py b/spynnaker/pyNN/extra_algorithms/graph_edge_filter.py index 3c3b16ef6b6..88ca48c670b 100644 --- a/spynnaker/pyNN/extra_algorithms/graph_edge_filter.py +++ b/spynnaker/pyNN/extra_algorithms/graph_edge_filter.py @@ -33,9 +33,10 @@ def __call__(self, app_graph, machine_graph): :param app_graph: The application graph :type app_graph: ~pacman.model.graphs.application.ApplicationGraph or None - :param .MachineGraph machine_graph: + :param ~pacman.model.graphs.machine.MachineGraph machine_graph: The machine_graph whose edges are to be filtered :return: a new, filtered machine graph + :rtype: ~pacman.model.graphs.machine.MachineGraph """ new_machine_graph = MachineGraph( label=machine_graph.label, application_graph=app_graph) @@ -57,10 +58,10 @@ def __call__(self, app_graph, machine_graph): for partition in progress.over(machine_graph.outgoing_edge_partitions): for edge in partition.edges: if self._is_filterable(edge): - logger.debug("this edge was pruned {}", edge) + logger.debug("this edge was pruned: {}", edge) prune_count += 1 continue - logger.debug("this edge was not pruned {}", edge) + logger.debug("this edge was not pruned: {}", edge) no_prune_count += 1 self._add_edge_to_new_graph(edge, partition, new_machine_graph) @@ -73,9 +74,9 @@ def __call__(self, app_graph, machine_graph): @staticmethod def _add_edge_to_new_graph(edge, partition, new_graph): """ - :param .MachineEdge edge: - :param .OutgoingEdgePartition partition: - :param .MachineGraph new_graph: + :param ~.MachineEdge edge: + :param ~.OutgoingEdgePartition partition: + :param ~.MachineGraph new_graph: """ new_graph.add_edge(edge, partition.identifier) edge.associate_application_edge() @@ -90,16 +91,18 @@ def _add_edge_to_new_graph(edge, partition, new_graph): @staticmethod def _is_filterable(edge): """ - :param .MachineEdge edge: + :param ~.MachineEdge edge: :rtype: bool """ # If our associated application edge wants to say don't filter... if (isinstance(edge.app_edge, AbstractFilterableEdge) and not edge.app_edge.filter_edge()): return False + if isinstance(edge, AbstractFilterableEdge): return edge.filter_edge() elif isinstance(edge.app_edge, ApplicationEdge): return False + # Bad graph construction? raise FilterableException( "cannot figure out if edge {} is prunable or not".format(edge)) diff --git a/spynnaker/pyNN/extra_algorithms/graph_edge_weight_updater.py b/spynnaker/pyNN/extra_algorithms/graph_edge_weight_updater.py index 5c19d9a890c..03529c2a762 100644 --- a/spynnaker/pyNN/extra_algorithms/graph_edge_weight_updater.py +++ b/spynnaker/pyNN/extra_algorithms/graph_edge_weight_updater.py @@ -13,12 +13,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging from spinn_utilities.progress_bar import ProgressBar from spynnaker.pyNN.models.abstract_models import AbstractWeightUpdatable -logger = logging.getLogger(__name__) - class GraphEdgeWeightUpdater(object): """ Updates the weights of all edges. @@ -26,9 +23,10 @@ class GraphEdgeWeightUpdater(object): def __call__(self, machine_graph): """ - :param machine_graph: the machine_graph whose edges are to be updated + :param ~pacman.model.graphs.machine.MachineGraph machine_graph: + the machine_graph whose edges are to be updated + :rtype: ~pacman.model.graphs.machine.MachineGraph """ - # create progress bar progress = ProgressBar( machine_graph.n_outgoing_edge_partitions, diff --git a/spynnaker/pyNN/extra_algorithms/on_chip_bit_field_generator.py b/spynnaker/pyNN/extra_algorithms/on_chip_bit_field_generator.py index 1b02cfd1e03..854705a7384 100644 --- a/spynnaker/pyNN/extra_algorithms/on_chip_bit_field_generator.py +++ b/spynnaker/pyNN/extra_algorithms/on_chip_bit_field_generator.py @@ -13,44 +13,62 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import math -import struct -import logging -import os from collections import defaultdict - -from spinn_front_end_common.abstract_models import \ - AbstractSupportsBitFieldGeneration -from spinn_front_end_common.utilities import system_control_logic +import os +import struct +from spinn_utilities.progress_bar import ProgressBar +from spinnman.model import ExecutableTargets +from spinnman.model.enums import CPUState +from spinn_front_end_common.abstract_models import ( + AbstractSupportsBitFieldGeneration) +from spinn_front_end_common.utilities.system_control_logic import ( + run_system_application) from spinn_front_end_common.utilities.constants import BYTES_PER_WORD from spinn_front_end_common.utilities.utility_objs import ExecutableType +from spynnaker.pyNN.utilities.constants import BITS_PER_WORD -from spinn_utilities.progress_bar import ProgressBar +_ONE_WORD = struct.Struct("> bit_in_word) & self._BIT_MASK + word_id, bit_in_word = divmod(neuron_id, BITS_PER_WORD) + flag = (bit_field[word_id] >> bit_in_word) & cls._BIT_MASK return flag - def _calculate_core_data( - self, app_graph, placements, progress, - executable_finder, transceiver): + def _calculate_core_data(self, app_graph, progress): """ gets the data needed for the bit field expander for the machine - :param app_graph: app graph - :param placements: placements - :param progress: progress bar - :param executable_finder: where to find the executable - :param transceiver: spinnman instance + :param ~.ApplicationGraph app_graph: app graph + :param ~.ProgressBar progress: progress bar :return: data and expander cores + :rtype: ExecutableTargets """ - # cores to place bitfield expander expander_cores = ExecutableTargets() - # bit field expander executable file path - bit_field_expander_path = executable_finder.get_executable_path( - self._BIT_FIELD_EXPANDER_APLX) - # locate verts which can have a synaptic matrix to begin with for app_vertex in progress.over(app_graph.vertices, False): - for machine_vertex in app_vertex.machine_vertices: - if (isinstance( - machine_vertex, AbstractSupportsBitFieldGeneration)): - self.__write_single_core_data( - machine_vertex, placements, transceiver, - bit_field_expander_path, expander_cores) + for placement in self.__bitfield_placements(app_vertex): + self.__write_single_core_data(placement, expander_cores) return expander_cores - def __write_single_core_data( - self, vertex, placements, transceiver, bit_field_expander_path, - expander_cores): - placement = placements.get_placement_of_vertex(vertex) - + def __write_single_core_data(self, placement, expander_cores): + """ + :param ~.Placement placement: + The vertex must support AbstractSupportsBitFieldGeneration + :param ~.ExecutableTargets expander_cores: + """ # check if the chip being considered already. expander_cores.add_processor( - bit_field_expander_path, placement.x, placement.y, - placement.p, executable_type=ExecutableType.SYSTEM) + self.__binary, placement.x, placement.y, placement.p, + executable_type=ExecutableType.SYSTEM) - bit_field_builder_region = vertex.bit_field_builder_region( - transceiver, placement) + bit_field_builder_region = placement.vertex.bit_field_builder_region( + self.__transceiver, placement) # update user 1 with location user_1_base_address = \ - transceiver.get_user_1_register_address_from_core(placement.p) - transceiver.write_memory( + self.__transceiver.get_user_1_register_address_from_core( + placement.p) + self.__transceiver.write_memory( placement.x, placement.y, user_1_base_address, - self._ONE_WORDS.pack(bit_field_builder_region), - self._USER_BYTES) + _ONE_WORD.pack(bit_field_builder_region), _ONE_WORD.size) - def _check_for_success(self, executable_targets, transceiver): - """ Goes through the cores checking for cores that have failed to\ - expand the bitfield to the core - :param executable_targets: cores to load bitfield on - :param transceiver: SpiNNMan instance - :rtype: None - """ +def _check_for_success(executable_targets, transceiver): + """ Goes through the cores checking for cores that have failed to\ + expand the bitfield to the core - for core_subset in executable_targets.all_core_subsets: - x = core_subset.x - y = core_subset.y - for p in core_subset.processor_ids: - # Read the result from USER0 register - user_2_base_address = \ - transceiver.get_user_2_register_address_from_core(p) - result = struct.unpack( - ". +from collections import defaultdict import os import logging -from collections import defaultdict - from spinn_utilities.log import FormatAdapter from spynnaker.pyNN.models.neuron import PopulationMachineVertex @@ -24,10 +23,30 @@ class RedundantPacketCountReport(object): + """ Reports how many packets were filtered by the packet filtering. + + :param provenance_items: + the collected provenance + :type provenance_items: + list(~spinn_front_end_common.utilities.utility_objs.ProvenanceDataItem) + :param str report_default_directory: where to write reports + """ + # The name of the report we create _FILE_NAME = "redundant_packet_count.rpt" + # The provenance items we care about for a vertex + _RELEVANT = frozenset([ + PopulationMachineVertex.GHOST_SEARCHES, + PopulationMachineVertex.BIT_FIELD_FILTERED_PACKETS, + PopulationMachineVertex.INVALID_MASTER_POP_HITS, + PopulationMachineVertex.SPIKES_PROCESSED]) + def __call__(self, provenance_items, report_default_directory): + """ + :param list(~.ProvenanceDataItem) provenance_items: + :param str report_default_directory: + """ file_name = os.path.join(report_default_directory, self._FILE_NAME) try: @@ -37,67 +56,48 @@ def __call__(self, provenance_items, report_default_directory): logger.exception("Generate_placement_reports: Can't open file" " {} for writing.", self._FILE_NAME) - @staticmethod - def _write_report(output, provenance_items): + def _write_report(self, output, provenance_items): data = defaultdict(dict) - for provenance_item in provenance_items: last_name = provenance_item.names[-1] key = provenance_item.names[0] - if ((last_name == PopulationMachineVertex.GHOST_SEARCHES) or - (last_name == - PopulationMachineVertex.BIT_FIELD_FILTERED_PACKETS) or - (last_name == - PopulationMachineVertex.INVALID_MASTER_POP_HITS) or - (last_name == PopulationMachineVertex.SPIKES_PROCESSED)): - + if last_name in self._RELEVANT: # add to store data[key][last_name] = provenance_item.value - # accum enough data on a core to do summary - if len(data[key].keys()) == 4: - - # total packets received - total = ( - data[key][PopulationMachineVertex.GHOST_SEARCHES] + - data[key][ - PopulationMachineVertex. - BIT_FIELD_FILTERED_PACKETS] + - data[key][ - PopulationMachineVertex.INVALID_MASTER_POP_HITS] + - data[key][PopulationMachineVertex.SPIKES_PROCESSED]) + if len(data[key]) == len(self._RELEVANT): + self.__write_core_summary(key, data[key], output) - # total redundant packets - redundant = ( - data[key][PopulationMachineVertex.GHOST_SEARCHES] + - data[key][ - PopulationMachineVertex. - BIT_FIELD_FILTERED_PACKETS] + - data[key][ - PopulationMachineVertex.INVALID_MASTER_POP_HITS]) - - percentage = 0 - if total != 0: - percentage = (100.0 / total) * redundant - - output.write( - "core {} \n\n" - " {} packets received.\n" - " {} were detected as redundant packets by the " - "bitfield filter.\n" - " {} were detected as having no " - "targets after the DMA stage.\n" - " {} were detected as " - "packets which we should not have received in the " - "first place. \n" - " Overall this makes a redundant " - "percentage of {}\n".format( - key, total, - data[key][ - PopulationMachineVertex. - BIT_FIELD_FILTERED_PACKETS], - data[key][PopulationMachineVertex.GHOST_SEARCHES], - data[key][ - PopulationMachineVertex. - INVALID_MASTER_POP_HITS], - percentage)) + @staticmethod + def __write_core_summary(key, items, output): + """ + :param str key: The name of the core + :param dict items: The relevant items for a core + :param ~io.TextIOBase output: + """ + # Extract + ghosts = items[PopulationMachineVertex.GHOST_SEARCHES] + filtered = items[PopulationMachineVertex.BIT_FIELD_FILTERED_PACKETS] + invalid = items[PopulationMachineVertex.INVALID_MASTER_POP_HITS] + spikes = items[PopulationMachineVertex.SPIKES_PROCESSED] + + # total packets received + total = ghosts + filtered + invalid + spikes + + # total redundant packets + redundant = ghosts + filtered + invalid + + percentage = 0 + if total != 0: + percentage = (100.0 / total) * redundant + + output.write( + "core {}\n\n" + " {} packets received.\n" + " {} were detected as redundant packets by the bitfield " + "filter.\n" + " {} were detected as having no targets after the DMA stage.\n" + " {} were detected as packets which we should not have " + "received in the first place.\n" + " Overall this makes a redundant percentage of {}\n".format( + key, total, filtered, ghosts, invalid, percentage)) diff --git a/spynnaker/pyNN/extra_algorithms/spynnaker_connection_holder_generations.py b/spynnaker/pyNN/extra_algorithms/spynnaker_connection_holder_generations.py index 90f911c02ed..411342dd365 100644 --- a/spynnaker/pyNN/extra_algorithms/spynnaker_connection_holder_generations.py +++ b/spynnaker/pyNN/extra_algorithms/spynnaker_connection_holder_generations.py @@ -13,13 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging from spinn_utilities.progress_bar import ProgressBar from spynnaker.pyNN.models.neuron import ConnectionHolder from spynnaker.pyNN.models.neural_projections import ProjectionApplicationEdge -logger = logging.getLogger(__name__) - class SpYNNakerConnectionHolderGenerator(object): """ Sets up connection holders for reports to use. @@ -28,11 +25,14 @@ class SpYNNakerConnectionHolderGenerator(object): application_graph: app graph :return: the set of connection holders for after DSG generation - :rtype: dict(tuple(ProjectionApplicationEdge, SynapseInformation), \ + :rtype: dict(tuple(ProjectionApplicationEdge, SynapseInformation), ConnectionHolder) """ def __call__(self, application_graph): + """ + :param ~.ApplicationGraph application_graph: + """ progress = ProgressBar( application_graph.n_outgoing_edge_partitions, "Generating connection holders for reporting connection data.") @@ -41,7 +41,7 @@ def __call__(self, application_graph): for partition in progress.over( application_graph.outgoing_edge_partitions): for edge in partition.edges: - # add pre run generators so that reports can extract without + # add pre-run generators so that reports can extract without # going to machine. if isinstance(edge, ProjectionApplicationEdge): # build connection holders @@ -52,6 +52,13 @@ def __call__(self, application_graph): @staticmethod def _generate_holder_for_edge(edge, data_holders): + """ + :param ProjectionApplicationEdge edge: + :param data_holders: + :type data_holders: + dict(tuple(ProjectionApplicationEdge, SynapseInformation), + ConnectionHolder) + """ # build connection holders connection_holder = ConnectionHolder( None, True, edge.pre_vertex.n_atoms, edge.post_vertex.n_atoms) diff --git a/spynnaker/pyNN/extra_algorithms/spynnaker_data_specification_writer.py b/spynnaker/pyNN/extra_algorithms/spynnaker_data_specification_writer.py index c4a34f03b87..c9f6e32c25b 100644 --- a/spynnaker/pyNN/extra_algorithms/spynnaker_data_specification_writer.py +++ b/spynnaker/pyNN/extra_algorithms/spynnaker_data_specification_writer.py @@ -28,7 +28,17 @@ def __call__( self, placements, hostname, report_default_directory, write_text_specs, machine, data_n_timesteps): # pylint: disable=too-many-arguments, signature-differs + return super(SpynnakerDataSpecificationWriter, self).__call__( + placements, hostname, report_default_directory, write_text_specs, + machine, data_n_timesteps, + placement_order=self._sort_delays_to_end(placements)) + @staticmethod + def _sort_delays_to_end(placements): + """ + :param ~.Placements placements: + :rtype: list(~.Placement) + """ delay_extensions = list() placement_order = list() for placement in placements.placements: @@ -37,7 +47,4 @@ def __call__( else: placement_order.append(placement) placement_order.extend(delay_extensions) - - return super(SpynnakerDataSpecificationWriter, self).__call__( - placements, hostname, report_default_directory, write_text_specs, - machine, data_n_timesteps, placement_order) + return placement_order diff --git a/spynnaker/pyNN/extra_algorithms/spynnaker_machine_bit_field_router_compressor.py b/spynnaker/pyNN/extra_algorithms/spynnaker_machine_bit_field_router_compressor.py index 4e1fbfb3d94..450be66bd60 100644 --- a/spynnaker/pyNN/extra_algorithms/spynnaker_machine_bit_field_router_compressor.py +++ b/spynnaker/pyNN/extra_algorithms/spynnaker_machine_bit_field_router_compressor.py @@ -17,24 +17,46 @@ from six import add_metaclass from spinn_utilities.abstract_base import AbstractBase, abstractmethod from spinn_utilities.overrides import overrides -from spinn_front_end_common.abstract_models.\ - abstract_supports_bit_field_generation import \ - AbstractSupportsBitFieldGeneration +from spinnman.model import ExecutableTargets +from spinnman.model.enums import CPUState +from spinn_front_end_common.abstract_models import ( + AbstractSupportsBitFieldGeneration) from spinn_front_end_common.interface.interface_functions.\ machine_bit_field_router_compressor import ( MachineBitFieldPairRouterCompressor, MachineBitFieldUnorderedRouterCompressor) from spinn_front_end_common.utilities import system_control_logic from spinn_front_end_common.utilities.utility_objs import ExecutableType -from spinnman.model import ExecutableTargets -from spinnman.model.enums import CPUState -from spynnaker.pyNN.extra_algorithms.synapse_expander import SYNAPSE_EXPANDER +from .synapse_expander import SYNAPSE_EXPANDER logger = logging.getLogger(__name__) @add_metaclass(AbstractBase) class SpynnakerMachineBitFieldRouterCompressor(object): + """ Bitfield-aware on-machine routing table compression algorithm. + + :param ~pacman.model.routing_tables.MulticastRoutingTables routing_tables: + routing tables + :param ~spinnman.transceiver.Transceiver transceiver: spinnman instance + :param ~spinn_machine.Machine machine: spinnMachine instance + :param int app_id: app id of the application + :param str provenance_file_path: file path for prov data + :param ~pacman.model.graphs.machine.MachineGraph machine_graph: + machine graph + :param ~pacman.model.placements.Placements placements: + placements on machine + :param int threshold_percentage: + the percentage of bitfields to do on chip before its considered a + success + :param executable_finder: where are binaries are located + :type executable_finder: + ~spinn_front_end_common.utilities.utility_objs.ExecutableFinder + :param bool read_algorithm_iobuf: flag saying if read iobuf + :param bool compress_as_much_as_possible: + flag asking if should compress as much as possible + :param bool read_expander_iobuf: reads the synaptic expander iobuf. + """ def __call__( self, routing_tables, transceiver, machine, app_id, @@ -47,23 +69,19 @@ def __call__( compress_as_much_as_possible=False, provenance_data_objects=None): """ entrance for routing table compression with bit field - :param routing_tables: routing tables - :param transceiver: spinnman instance - :param machine: spinnMachine instance - :param app_id: app id of the application - :param provenance_file_path: file path for prov data - :param machine_graph: machine graph - :param placements: placements on machine - :param threshold_percentage: the percentage of bitfields to do on chip\ - before its considered a success - :param executable_finder: where are binaries are located - :param read_algorithm_iobuf: bool flag saying if read iobuf - :param compress_as_much_as_possible: bool flag asking if should \ - compress as much as possible - :param read_expander_iobuf: reads the synaptic expander iobuf. - :rtype: None + :param ~.MulticastRoutingTables routing_tables: + :param ~.Transceiver transceiver: + :param ~.Machine machine: + :param int app_id: + :param str provenance_file_path: + :param ~.MachineGraph machine_graph: + :param ~.Placements placements: + :param int threshold_percentage: + :param ~.ExecutableFinder executable_finder: + :param bool read_algorithm_iobuf: + :param bool compress_as_much_as_possible: + :param bool read_expander_iobuf: """ - # build machine compressor machine_bit_field_router_compressor = self.compressor_factory() (compressor_executable_targets, prov_items) = \ @@ -99,17 +117,21 @@ def __call__( @abstractmethod def compressor_factory(self): - "Method to call the specific compressor to use" + """ Creates the specific compressor to use. + + :rtype: + ~spinn_front_end_common.interface.interface_functions.MachineBitFieldRouterCompressor + """ - @staticmethod def _locate_synaptic_expander_cores( - cores, executable_finder, placements, machine): + self, cores, executable_finder, placements, machine): """ removes host based cores for synaptic matrix regeneration - :param cores: the cores for everything - :param executable_finder: way to get binary path - :param machine: spiNNMachine instance. + :param ~.ExecutableTargets cores: the cores for everything + :param ~.ExecutableFinder executable_finder: way to get binary path + :param ~.Machine machine: spiNNMachine instance. :return: new targets for synaptic expander + :rtype: ~.ExecutableTargets """ new_cores = ExecutableTargets() @@ -120,21 +142,33 @@ def _locate_synaptic_expander_cores( # if any ones are going to be ran on host, ignore them from the new # core setup for core_subset in cores.all_core_subsets: - chip = machine.get_chip_at(core_subset.x, core_subset.y) - for processor_id in range(0, chip.n_processors): - if placements.is_processor_occupied( - core_subset.x, core_subset.y, processor_id): - vertex = placements.get_vertex_on_processor( - core_subset.x, core_subset.y, processor_id) - app_vertex = vertex.app_vertex - if isinstance(vertex, AbstractSupportsBitFieldGeneration): - if app_vertex.gen_on_machine(vertex.vertex_slice): - new_cores.add_processor( - expander_executable_path, - core_subset.x, core_subset.y, processor_id, - executable_type=ExecutableType.SYSTEM) + x = core_subset.x + y = core_subset.y + for p in range(machine.get_chip_at(x, y).n_processors): + if self._gen_bitfield_on_machine(placements, x, y, p): + new_cores.add_processor( + expander_executable_path, x, y, p, + executable_type=ExecutableType.SYSTEM) return new_cores + @staticmethod + def _gen_bitfield_on_machine(placements, x, y, p): + """ Check if the given core's vertex has bitfields generated on\ + machine. + + :param ~.Placements placements: + :param int x: + :param int y: + :param int p: + :rtype: bool + """ + if placements.is_processor_occupied(x, y, p): + vertex = placements.get_vertex_on_processor(x, y, p) + if isinstance(vertex, AbstractSupportsBitFieldGeneration): + if vertex.app_vertex.gen_on_machine(vertex.vertex_slice): + return True + return False + @staticmethod def _rerun_synaptic_cores( synaptic_expander_rerun_cores, transceiver, @@ -142,13 +176,13 @@ def _rerun_synaptic_cores( read_expander_iobuf): """ reruns the synaptic expander - :param synaptic_expander_rerun_cores: the cores to rerun the synaptic / - matrix generator for - :param transceiver: spinnman instance - :param provenance_file_path: prov file path - :param executable_finder: finder of binary file paths - :param read_expander_iobuf: bool for reading off iobuf if needed - :rtype: None + :param ~.ExecutableTargets synaptic_expander_rerun_cores: + the cores to rerun the synaptic matrix generator for + :param ~.Transceiver transceiver: spinnman instance + :param str provenance_file_path: prov file path + :param ~.ExecutableFinder executable_finder: + finder of binary file paths + :param bool read_expander_iobuf: read off iobuf if needed """ if synaptic_expander_rerun_cores.total_processors != 0: logger.info("rerunning synaptic expander") @@ -162,6 +196,30 @@ def _rerun_synaptic_cores( class SpynnakerMachineBitFieldUnorderedRouterCompressor( SpynnakerMachineBitFieldRouterCompressor): + """ Bitfield-aware on-machine routing table compression algorithm that \ + uses the unordered routing compressor. + + :param ~pacman.model.routing_tables.MulticastRoutingTables routing_tables: + routing tables + :param ~spinnman.transceiver.Transceiver transceiver: spinnman instance + :param ~spinn_machine.Machine machine: spinnMachine instance + :param int app_id: app id of the application + :param str provenance_file_path: file path for prov data + :param ~pacman.model.graphs.machine.MachineGraph machine_graph: + machine graph + :param ~pacman.model.placements.Placements placements: + placements on machine + :param int threshold_percentage: + the percentage of bitfields to do on chip before its considered a + success + :param executable_finder: where are binaries are located + :type executable_finder: + ~spinn_front_end_common.utilities.utility_objs.ExecutableFinder + :param bool read_algorithm_iobuf: flag saying if read iobuf + :param bool compress_as_much_as_possible: + flag asking if should compress as much as possible + :param bool read_expander_iobuf: reads the synaptic expander iobuf. + """ @overrides(SpynnakerMachineBitFieldRouterCompressor.compressor_factory) def compressor_factory(self): @@ -170,6 +228,30 @@ def compressor_factory(self): class SpynnakerMachineBitFieldPairRouterCompressor( SpynnakerMachineBitFieldRouterCompressor): + """ Bitfield-aware on-machine routing table compression algorithm that \ + uses the paired routing compressor. + + :param ~pacman.model.routing_tables.MulticastRoutingTables routing_tables: + routing tables + :param ~spinnman.transceiver.Transceiver transceiver: spinnman instance + :param ~spinn_machine.Machine machine: spinnMachine instance + :param int app_id: app id of the application + :param str provenance_file_path: file path for prov data + :param ~pacman.model.graphs.machine.MachineGraph machine_graph: + machine graph + :param ~pacman.model.placements.Placements placements: + placements on machine + :param int threshold_percentage: + the percentage of bitfields to do on chip before its considered a + success + :param executable_finder: where are binaries are located + :type executable_finder: + ~spinn_front_end_common.utilities.utility_objs.ExecutableFinder + :param bool read_algorithm_iobuf: flag saying if read iobuf + :param bool compress_as_much_as_possible: + flag asking if should compress as much as possible + :param bool read_expander_iobuf: reads the synaptic expander iobuf. + """ @overrides(SpynnakerMachineBitFieldRouterCompressor.compressor_factory) def compressor_factory(self): diff --git a/spynnaker/pyNN/extra_algorithms/spynnaker_neuron_network_specification_report.py b/spynnaker/pyNN/extra_algorithms/spynnaker_neuron_network_specification_report.py index 1962a27d08b..1e45ee0b6aa 100644 --- a/spynnaker/pyNN/extra_algorithms/spynnaker_neuron_network_specification_report.py +++ b/spynnaker/pyNN/extra_algorithms/spynnaker_neuron_network_specification_report.py @@ -13,14 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging import os from spinn_utilities.progress_bar import ProgressBar from spynnaker.pyNN.exceptions import SpynnakerException from spynnaker.pyNN.models.neural_projections import ProjectionApplicationEdge -logger = logging.getLogger(__name__) - class SpYNNakerNeuronGraphNetworkSpecificationReport(object): """ Produces a report describing the graph created from the neural \ @@ -34,6 +31,10 @@ class SpYNNakerNeuronGraphNetworkSpecificationReport(object): @staticmethod def _get_diagram(label): + """ + :param str label: + :rtype: ~graphviz.Digraph + """ # pylint: disable=import-error try: import graphviz @@ -44,8 +45,12 @@ def _get_diagram(label): return graphviz.Digraph(comment=label) def __call__(self, report_folder, application_graph): + """ + :param str report_folder: + :param ~.ApplicationGraph application_graph: + """ # create holders for data - vertex_holders = dict() + vertex_names = dict() dot_diagram = self._get_diagram( "The graph of the network in graphical form") @@ -56,29 +61,25 @@ def __call__(self, report_folder, application_graph): "generating the graphical representation of the neural network") # write vertices into dot diagram - for vertex_counter, vertex in progress.over( + for vertex_index, vertex in progress.over( enumerate(application_graph.vertices), False): + vertex_names[vertex] = str(vertex_index) dot_diagram.node( - "{}".format(vertex_counter), + vertex_names[vertex], "{} ({} neurons)".format(vertex.label, vertex.n_atoms)) - vertex_holders[vertex] = vertex_counter # write edges into dot diagram for partition in progress.over( application_graph.outgoing_edge_partitions, False): for edge in partition.edges: - source_vertex_id = vertex_holders[edge.pre_vertex] - dest_vertex_id = vertex_holders[edge.post_vertex] + pre_name = vertex_names[edge.pre_vertex] + post_name = vertex_names[edge.post_vertex] if isinstance(edge, ProjectionApplicationEdge): for synapse_info in edge.synapse_information: dot_diagram.edge( - "{}".format(source_vertex_id), - "{}".format(dest_vertex_id), - "{}".format(synapse_info.connector)) + pre_name, post_name, str(synapse_info.connector)) else: - dot_diagram.edge( - "{}".format(source_vertex_id), - "{}".format(dest_vertex_id)) + dot_diagram.edge(pre_name, post_name) # write dot file and generate pdf file_to_output = os.path.join(report_folder, "network_graph.gv") diff --git a/spynnaker/pyNN/extra_algorithms/synapse_expander.py b/spynnaker/pyNN/extra_algorithms/synapse_expander.py index 566ba54f795..b867ba73c29 100644 --- a/spynnaker/pyNN/extra_algorithms/synapse_expander.py +++ b/spynnaker/pyNN/extra_algorithms/synapse_expander.py @@ -13,21 +13,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import functools -import logging from six import iteritems - -from spinn_front_end_common.utilities.system_control_logic import \ - run_system_application -from spinn_front_end_common.utilities.utility_objs import ExecutableType from spinn_utilities.progress_bar import ProgressBar from spinnman.model import ExecutableTargets from spinnman.model.enums import CPUState -from spinn_front_end_common.utilities import globals_variables +from spinn_front_end_common.utilities.system_control_logic import ( + run_system_application) +from spinn_front_end_common.utilities.utility_objs import ExecutableType +from spinn_front_end_common.utilities.globals_variables import get_simulator from spynnaker.pyNN.models.neuron import AbstractPopulationVertex from spynnaker.pyNN.models.utility_models.delays import DelayExtensionVertex -logger = logging.getLogger(__name__) - SYNAPSE_EXPANDER = "synapse_expander.aplx" DELAY_EXPANDER = "delay_expander.aplx" @@ -81,52 +77,83 @@ def _plan_expansion(app_graph, placements, synapse_expander_bin, # Add all machine vertices of the population vertex to ones # that need synapse expansion if isinstance(vertex, AbstractPopulationVertex): - gen_on_machine = False - for m_vertex in vertex.machine_vertices: - if vertex.gen_on_machine(m_vertex.vertex_slice): - placement = placements.get_placement_of_vertex(m_vertex) - expander_cores.add_processor( - synapse_expander_bin, - placement.x, placement.y, placement.p, - executable_type=ExecutableType.SYSTEM) - gen_on_machine = True + gen_on_machine = _plan_expansion_of_vertex( + vertex, placements, expander_cores, synapse_expander_bin) if gen_on_machine: expanded_pop_vertices.append(vertex) elif isinstance(vertex, DelayExtensionVertex): - for m_vertex in vertex.machine_vertices: - if vertex.gen_on_machine(m_vertex.vertex_slice): - placement = placements.get_placement_of_vertex(m_vertex) - expander_cores.add_processor( - delay_expander_bin, - placement.x, placement.y, placement.p, - executable_type=ExecutableType.SYSTEM) + _plan_expansion_of_vertex( + vertex, placements, expander_cores, delay_expander_bin) + # No post-expand actions for these return expander_cores, expanded_pop_vertices +def _plan_expansion_of_vertex( + vertex, placements, expander_cores, expander_binary): + """ + :param vertex: + :type vertex: AbstractPopulationVertex or DelayExtensionVertex + :param ~.Placements placements: + :param ~.ExecutableTargets expander_cores: + :param str expander_binary: + :return: True if any expander cores were added + :rtype: bool + """ + gen_on_machine = False + for m_vertex in vertex.machine_vertices: + if vertex.gen_on_machine(m_vertex.vertex_slice): + placement = placements.get_placement_of_vertex(m_vertex) + expander_cores.add_processor( + expander_binary, placement.x, placement.y, placement.p, + executable_type=ExecutableType.SYSTEM) + gen_on_machine = True + return gen_on_machine + + def _fill_in_connection_data( _expander_cores, transceiver, expanded_pop_vertices, placements): - """ Once expander has run, fill in the connection data + """ Once expander has run, fill in the connection data we have for the\ + expanded connection matrix. - :rtype: None + :param ~.ExecutableTargets expander_cores: + :param ~.Transceiver transceiver: + :param list(AbstractPopulationVertex) expanded_pop_vertices: + :param ~.Placements placements: """ - ctl = globals_variables.get_simulator() - use_extra_monitors = False + # pylint: disable=unused-argument + ctl = get_simulator() for vertex in expanded_pop_vertices: - conn_holders = vertex.get_connection_holders() - for (app_edge, synapse_info), conn_holder_list in iteritems( - conn_holders): + for (edge, synapse_info), conn_holders in iteritems( + vertex.get_connection_holders()): # Only do this if this synapse_info has been generated # on the machine using the expander if synapse_info.may_generate_on_machine(): - machine_edges = app_edge.machine_edges - for machine_edge in machine_edges: - placement = placements.get_placement_of_vertex( - machine_edge.post_vertex) - conns = vertex.get_connections_from_machine( - transceiver, placement, machine_edge, - ctl.routing_infos, synapse_info, ctl.machine_time_step, - use_extra_monitors) - for conn_holder in conn_holder_list: - conn_holder.add_connections(conns) + _read_connections_for_app_edge_from_machine( + vertex, placements, transceiver, ctl, + edge, synapse_info, conn_holders) + + +def _read_connections_for_app_edge_from_machine( + vertex, placements, transceiver, ctl, + app_edge, synapse_info, conn_holder_list): + """ Reads (and records for later) the connections that were actually \ + generated on the machine for a particular application edge. + + :param AbstractPopulationVertex vertex: + :param ~.Placements placements: + :param ~.Transceiver transceiver: + :param ~.SimulatorInterface ctl: + :param ~.ApplicationEdge app_edge: + :param SynapseInformation synapse_info: + :param list(ConnectionHolder) conn_holder_list: + """ + use_extra_monitors = False + for edge in app_edge.machine_edges: + conns = vertex.get_connections_from_machine( + transceiver, placements.get_placement_of_vertex(edge.post_vertex), + edge, ctl.routing_infos, synapse_info, ctl.machine_time_step, + use_extra_monitors) + for conn_holder in conn_holder_list: + conn_holder.add_connections(conns) diff --git a/spynnaker/pyNN/models/abstract_models/abstract_accepts_incoming_synapses.py b/spynnaker/pyNN/models/abstract_models/abstract_accepts_incoming_synapses.py index 1131ab4ae63..7d2b795ac8e 100644 --- a/spynnaker/pyNN/models/abstract_models/abstract_accepts_incoming_synapses.py +++ b/spynnaker/pyNN/models/abstract_models/abstract_accepts_incoming_synapses.py @@ -66,7 +66,7 @@ def get_connections_from_machine( # pylint: disable=too-many-arguments """ Get the connections from the machine post-run. - :param ~spinnman.Transceiver transceiver: + :param ~spinnman.transceiver.Transceiver transceiver: :param ~pacman.model.placements.Placement placement: :param ProjectionMachineEdge edge: :param ~pacman.model.routing_info.RoutingInfo routing_infos: @@ -76,11 +76,13 @@ def get_connections_from_machine( :param placements: :type placements: None or ~pacman.model.placements.Placements :param monitor_api: - :type monitor_api: None or \ + :type monitor_api: None or ~spinn_front_end_common.utility_models.DataSpeedUpPacketGatherMachineVertex :param fixed_routes: :param extra_monitor: the extra monitor for this - :type fixed_routes: None or \ + :type extra_monitor: + ~spinn_front_end_common.utility_models.ExtraMonitorSupportMachineVertex + :type fixed_routes: None or dict(tuple(int,int),~spinn_machine.FixedRouteEntry) """ diff --git a/spynnaker/pyNN/models/abstract_models/abstract_population_settable.py b/spynnaker/pyNN/models/abstract_models/abstract_population_settable.py index 373af12a5e5..d3643dbfa20 100644 --- a/spynnaker/pyNN/models/abstract_models/abstract_population_settable.py +++ b/spynnaker/pyNN/models/abstract_models/abstract_population_settable.py @@ -38,8 +38,8 @@ def get_value_by_selector(self, selector, key): """ Gets the value for a particular key but only for the selected\ subset. - :param selector: See \ - :py:meth:`~spinn_utilities.ranged.RangedList.get_value_by_selector`\ + :param selector: See + :py:meth:`~spinn_utilities.ranged.RangedList.get_value_by_selector` as this is just a pass through method :type selector: None or slice or int or list(bool) or list(int) :param str key: the name of the parameter to change @@ -60,8 +60,8 @@ def set_value_by_selector(self, selector, key, value): """ Sets the value for a particular key but only for the selected \ subset. - :param selector: See \ - :py:class:`~spinn_utilities.ranged.RangedList`.set_value_by_selector\ + :param selector: See + :py:meth:`~spinn_utilities.ranged.RangedList.set_value_by_selector` as this is just a pass through method :type selector: None or slice or int or list(bool) or list(int) :param str key: the name of the parameter to change diff --git a/spynnaker/pyNN/models/abstract_models/abstract_read_parameters_before_set.py b/spynnaker/pyNN/models/abstract_models/abstract_read_parameters_before_set.py index 4ed297a4769..6ebd72bee0e 100644 --- a/spynnaker/pyNN/models/abstract_models/abstract_read_parameters_before_set.py +++ b/spynnaker/pyNN/models/abstract_models/abstract_read_parameters_before_set.py @@ -35,5 +35,4 @@ def read_parameters_from_machine( the placement of a vertex :param ~pacman.model.graphs.common.Slice vertex_slice: the slice of atoms for this vertex - :rtype: None """ diff --git a/spynnaker/pyNN/models/abstract_models/abstract_settable.py b/spynnaker/pyNN/models/abstract_models/abstract_settable.py index fe91c7105f4..b049d99da7f 100644 --- a/spynnaker/pyNN/models/abstract_models/abstract_settable.py +++ b/spynnaker/pyNN/models/abstract_models/abstract_settable.py @@ -27,7 +27,7 @@ class AbstractSettable(object): @abstractmethod def get_value(self, key): - """ Get a property + """ Get a property of the model :param str key: the name of the property :rtype: Any or float or int or list(float) or list(int) @@ -35,7 +35,7 @@ def get_value(self, key): @abstractmethod def set_value(self, key, value): - """ Set a property + """ Set a property of the model :param str key: the name of the parameter to change :param value: the new value of the parameter to assign diff --git a/spynnaker/pyNN/models/abstract_pynn_model.py b/spynnaker/pyNN/models/abstract_pynn_model.py index 65048ddf662..0b78ed2212f 100644 --- a/spynnaker/pyNN/models/abstract_pynn_model.py +++ b/spynnaker/pyNN/models/abstract_pynn_model.py @@ -100,7 +100,7 @@ def has_parameter(cls, name): @abstractproperty def default_population_parameters(self): """ Get the default values for the parameters at the population level - These are parameters that can be passed in to the Population\ + These are parameters that can be passed in to the Population constructor in addition to the standard PyNN options :rtype: dict(str, Any) diff --git a/spynnaker/pyNN/models/common/abstract_neuron_recordable.py b/spynnaker/pyNN/models/common/abstract_neuron_recordable.py index c2da9491870..e32c34efe3f 100644 --- a/spynnaker/pyNN/models/common/abstract_neuron_recordable.py +++ b/spynnaker/pyNN/models/common/abstract_neuron_recordable.py @@ -28,7 +28,7 @@ class AbstractNeuronRecordable(object): @abstractmethod def get_recordable_variables(self): """ Returns a list of the PyNN names of variables this model is \ - expected to collect + expected to collect. :rtype: list(str) """ @@ -45,7 +45,7 @@ def is_recording(self, variable): @abstractmethod def set_recording(self, variable, new_state=True, sampling_interval=None, indexes=None): - """ Sets variable to being recorded + """ Sets variable to being recorded. :param str variable: PyNN name of the variable :param bool new_state: @@ -65,7 +65,6 @@ def clear_recording(self, variable, buffer_manager, placements): ~spinn_front_end_common.interface.buffer_management.BufferManager :param ~pacman.model.placements.Placements placements: the placements object - :rtype: None """ @abstractmethod diff --git a/spynnaker/pyNN/models/common/abstract_spike_recordable.py b/spynnaker/pyNN/models/common/abstract_spike_recordable.py index b18ef17c838..b24c89515b9 100644 --- a/spynnaker/pyNN/models/common/abstract_spike_recordable.py +++ b/spynnaker/pyNN/models/common/abstract_spike_recordable.py @@ -26,7 +26,7 @@ class AbstractSpikeRecordable(object): @abstractmethod def is_recording_spikes(self): - """ Determine if spikes are being recorded + """ Determine if spikes are being recorded. :return: True if spikes are being recorded, False otherwise :rtype: bool @@ -36,7 +36,7 @@ def is_recording_spikes(self): def set_recording_spikes( self, new_state=True, sampling_interval=None, indexes=None): """ Set spikes to being recorded. \ - If `new_state` is false all other parameters are ignored. + If `new_state` is False all other parameters are ignored. :param bool new_state: Set if the spikes are recording or not :param sampling_interval: The interval at which spikes are recorded. @@ -57,7 +57,6 @@ def clear_spike_recording(self, buffer_manager, placements): ~spinn_front_end_common.interface.buffer_management.BufferManager :param ~pacman.model.placements.Placements placements: the placements object - :rtype: None """ @abstractmethod @@ -71,7 +70,7 @@ def get_spikes(self, placements, buffer_manager, machine_time_step): ~spinn_front_end_common.interface.buffer_management.BufferManager :param int machine_time_step: the time step of the simulation, in microseconds - :return: A numpy array of 2-element arrays of (neuron_id, time)\ + :return: A numpy array of 2-element arrays of (neuron_id, time) ordered by time, one element per event :rtype: ~numpy.ndarray(tuple(int,int)) """ diff --git a/spynnaker/pyNN/models/common/eieio_spike_recorder.py b/spynnaker/pyNN/models/common/eieio_spike_recorder.py index 0e81829e03f..326d2082065 100644 --- a/spynnaker/pyNN/models/common/eieio_spike_recorder.py +++ b/spynnaker/pyNN/models/common/eieio_spike_recorder.py @@ -19,9 +19,9 @@ from spinn_utilities.progress_bar import ProgressBar from spinn_utilities.log import FormatAdapter from spinnman.messages.eieio.data_messages import EIEIODataHeader -from spynnaker.pyNN.models.common import recording_utils from spinn_front_end_common.utilities.constants import ( BYTES_PER_WORD, MICRO_TO_MILLISECOND_CONVERSION) +from spynnaker.pyNN.models.common.recording_utils import make_missing_string logger = FormatAdapter(logging.getLogger(__name__)) _ONE_WORD = struct.Struct(" 0: + if n_buffer_times: + placement = placements.get_placement_of_vertex(vertex) raw_spike_data, data_missing = \ buffer_manager.get_data_by_placement(placement, region) if data_missing: missing.append(placement) self._process_spike_data( - vertex_slice, raw_spike_data, ms_per_tick, + vertex.vertex_slice, raw_spike_data, ms_per_tick, base_key_function(vertex), results) if missing: - missing_str = recording_utils.make_missing_string(missing) + missing_str = make_missing_string(missing) logger.warning( "Population {} is missing spike data in region {} from the" " following cores: {}", label, region, missing_str) diff --git a/spynnaker/pyNN/models/common/multi_spike_recorder.py b/spynnaker/pyNN/models/common/multi_spike_recorder.py index c1d917560fa..c4301d69135 100644 --- a/spynnaker/pyNN/models/common/multi_spike_recorder.py +++ b/spynnaker/pyNN/models/common/multi_spike_recorder.py @@ -12,18 +12,17 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import math import logging import struct import numpy -from pacman.model.resources.constant_sdram import ConstantSDRAM from spinn_utilities.progress_bar import ProgressBar from spinn_utilities.log import FormatAdapter -from spynnaker.pyNN.models.common import recording_utils -from pacman.model.resources.variable_sdram import VariableSDRAM +from pacman.model.resources import ConstantSDRAM, VariableSDRAM from spinn_front_end_common.utilities.constants import ( - BYTES_PER_WORD, BITS_PER_WORD, MICRO_TO_MILLISECOND_CONVERSION) + BYTES_PER_WORD, MICRO_TO_MILLISECOND_CONVERSION) +from spynnaker.pyNN.models.common.recording_utils import make_missing_string +from spynnaker.pyNN.utilities.utility_calls import ceildiv +from spynnaker.pyNN.utilities.constants import BITS_PER_WORD logger = FormatAdapter(logging.getLogger(__name__)) _TWO_WORDS = struct.Struct(". from __future__ import division -import logging import struct import numpy from spinn_front_end_common.utilities.helpful_functions import ( @@ -22,7 +21,6 @@ from spinn_front_end_common.utilities.constants import BYTES_PER_WORD from spynnaker.pyNN.exceptions import MemReadException -logger = logging.getLogger(__name__) _RECORDING_COUNT = struct.Struct(". - -import logging import numpy from spinn_utilities.overrides import overrides from spinn_front_end_common.utilities.constants import BYTES_PER_WORD @@ -25,8 +23,6 @@ N_GEN_PARAMS = 1 -logger = logging.getLogger(__file__) - class AllToAllConnector(AbstractGenerateConnectorOnMachine, AbstractConnectorSupportsViewsOnMachine): diff --git a/spynnaker/pyNN/models/neural_projections/connectors/array_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/array_connector.py index 8d25c7bdf81..0ccbdde520a 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/array_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/array_connector.py @@ -12,14 +12,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging import numpy from spinn_utilities.overrides import overrides from .abstract_connector import AbstractConnector -logger = logging.getLogger(__name__) - class ArrayConnector(AbstractConnector): """ Make connections using an array of integers based on the IDs\ @@ -32,9 +28,9 @@ class ArrayConnector(AbstractConnector): def __init__(self, array, safe=True, callback=None, verbose=False): """ :param array: - An explicit boolean matrix that specifies the connections\ - between the pre- and post-populations\ - (see PyNN documentation). Must be 2D in practice. + An explicit boolean matrix that specifies the connections between + the pre- and post-populations (see PyNN documentation). + Must be 2D in practice. :type array: ~numpy.ndarray(2, ~numpy.uint8) :param bool safe: :param callable callback: Ignored diff --git a/spynnaker/pyNN/models/neural_projections/connectors/csa_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/csa_connector.py index 4c33b01e59a..1ebaa913579 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/csa_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/csa_connector.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging import numpy from spinn_utilities.overrides import overrides from .abstract_connector import AbstractConnector @@ -24,8 +22,6 @@ # Importing csa causes problems with readthedocs so allowing it to fail _csa_found = (False, _ex) -logger = logging.getLogger(__name__) - class CSAConnector(AbstractConnector): """ Make connections using a Connection Set Algebra (Djurfeldt 2012)\ diff --git a/spynnaker/pyNN/models/neural_projections/connectors/distance_dependent_probability_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/distance_dependent_probability_connector.py index 84f522378ba..da947829e16 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/distance_dependent_probability_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/distance_dependent_probability_connector.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging import math import numpy from numpy import ( @@ -22,10 +20,10 @@ minimum, e, pi) from spinn_utilities.overrides import overrides from spinn_utilities.safe_eval import SafeEval -from spynnaker.pyNN.utilities import utility_calls +from spynnaker.pyNN.utilities.utility_calls import ( + get_probable_maximum_selected, round_up) from .abstract_connector import AbstractConnector -logger = logging.getLogger(__name__) # support for arbitrary expression for the distance dependence _d_expr_context = SafeEval(math, numpy, arccos, arcsin, arctan, arctan2, ceil, cos, cosh, exp, fabs, floor, fmod, hypot, ldexp, @@ -112,8 +110,7 @@ def _set_probabilities(self, synapse_info): @overrides(AbstractConnector.get_delay_maximum) def get_delay_maximum(self, synapse_info): return self._get_delay_maximum( - synapse_info.delays, - utility_calls.get_probable_maximum_selected( + synapse_info.delays, get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons * synapse_info.n_post_neurons, numpy.amax(self.__probs))) @@ -126,12 +123,12 @@ def get_n_connections_from_pre_vertex_maximum( max_prob = numpy.amax( self.__probs[0:synapse_info.n_pre_neurons, post_vertex_slice.as_slice]) - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, post_vertex_slice.n_atoms, max_prob) if min_delay is None or max_delay is None: - return int(math.ceil(n_connections)) + return round_up(n_connections) return self._get_n_connections_from_pre_vertex_with_delay_maximum( synapse_info.delays, @@ -141,7 +138,7 @@ def get_n_connections_from_pre_vertex_maximum( @overrides(AbstractConnector.get_n_connections_to_post_vertex_maximum) def get_n_connections_to_post_vertex_maximum(self, synapse_info): # pylint: disable=too-many-arguments - return utility_calls.get_probable_maximum_selected( + return get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_post_neurons, numpy.amax(self.__probs)) @@ -150,8 +147,7 @@ def get_n_connections_to_post_vertex_maximum(self, synapse_info): def get_weight_maximum(self, synapse_info): # pylint: disable=too-many-arguments return self._get_weight_maximum( - synapse_info.weights, - utility_calls.get_probable_maximum_selected( + synapse_info.weights, get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons * synapse_info.n_post_neurons, numpy.amax(self.__probs))) diff --git a/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_post_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_post_connector.py index da9f71aee1d..463106cd2e5 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_post_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_post_connector.py @@ -14,8 +14,6 @@ # along with this program. If not, see . from __future__ import print_function -import logging -import math import numpy from spinn_utilities.overrides import overrides from spinn_front_end_common.utilities.constants import BYTES_PER_WORD @@ -24,13 +22,12 @@ AbstractGenerateConnectorOnMachine, ConnectorIDs) from .abstract_connector_supports_views_on_machine import ( AbstractConnectorSupportsViewsOnMachine) -from spynnaker.pyNN.utilities import utility_calls from spynnaker.pyNN.exceptions import SpynnakerException +from spynnaker.pyNN.utilities.utility_calls import ( + round_up, get_probable_maximum_selected) N_GEN_PARAMS = 8 -logger = logging.getLogger(__file__) - class FixedNumberPostConnector(AbstractGenerateConnectorOnMachine, AbstractConnectorSupportsViewsOnMachine): @@ -202,12 +199,12 @@ def get_n_connections_from_pre_vertex_maximum( prob_in_slice = min( post_vertex_slice.n_atoms / float( synapse_info.n_post_neurons), 1.0) - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, self.__n_post, prob_in_slice, chance=1.0/100000.0) if min_delay is None or max_delay is None: - return int(math.ceil(n_connections)) + return round_up(n_connections) return self._get_n_connections_from_pre_vertex_with_delay_maximum( synapse_info.delays, @@ -218,11 +215,11 @@ def get_n_connections_from_pre_vertex_maximum( def get_n_connections_to_post_vertex_maximum(self, synapse_info): # pylint: disable=too-many-arguments selection_prob = 1.0 / float(synapse_info.n_post_neurons) - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons, selection_prob, chance=1.0/100000.0) - return int(math.ceil(n_connections)) + return round_up(n_connections) @overrides(AbstractConnector.get_weight_maximum) def get_weight_maximum(self, synapse_info): diff --git a/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_pre_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_pre_connector.py index 4f31127f1ba..8f3960cc6b5 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_pre_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/fixed_number_pre_connector.py @@ -12,24 +12,20 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging -import math import numpy from spinn_utilities.overrides import overrides from spinn_front_end_common.utilities.constants import BYTES_PER_WORD from .abstract_connector import AbstractConnector from .abstract_generate_connector_on_machine import ( AbstractGenerateConnectorOnMachine, ConnectorIDs) -from spynnaker.pyNN.utilities import utility_calls +from spynnaker.pyNN.utilities.utility_calls import ( + get_probable_maximum_selected, round_up) from spynnaker.pyNN.exceptions import SpynnakerException from .abstract_connector_supports_views_on_machine import ( AbstractConnectorSupportsViewsOnMachine) N_GEN_PARAMS = 8 -logger = logging.getLogger(__file__) - class FixedNumberPreConnector(AbstractGenerateConnectorOnMachine, AbstractConnectorSupportsViewsOnMachine): @@ -70,7 +66,7 @@ def __init__( :type rng: ~pyNN.random.NumpyRNG or None """ # :param ~pyNN.space.Space space: - # a Space object, needed if you wish to specify distance-dependent\ + # a Space object, needed if you wish to specify distance-dependent # weights or delays - not implemented super(FixedNumberPreConnector, self).__init__(safe, callback, verbose) self.__n_pre = n @@ -201,19 +197,19 @@ def get_n_connections_from_pre_vertex_maximum( max_delay=None): # pylint: disable=too-many-arguments prob_selection = 1.0 / float(synapse_info.n_pre_neurons) - n_connections_total = utility_calls.get_probable_maximum_selected( + n_connections_total = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, self.__n_pre * synapse_info.n_post_neurons, prob_selection, chance=1.0/10000.0) prob_in_slice = min( float(post_vertex_slice.n_atoms) / float( synapse_info.n_post_neurons), 1.0) - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, n_connections_total, prob_in_slice, chance=1.0/100000.0) if min_delay is None or max_delay is None: - return int(math.ceil(n_connections)) + return round_up(n_connections) return self._get_n_connections_from_pre_vertex_with_delay_maximum( synapse_info.delays, diff --git a/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py index 75021337b2c..ce25056ab20 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/fixed_probability_connector.py @@ -12,19 +12,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import math import numpy from spinn_utilities.overrides import overrides from data_specification.enums.data_type import DataType from spinn_front_end_common.utilities.exceptions import ConfigurationException -from spynnaker.pyNN.utilities import utility_calls +from spinn_front_end_common.utilities.constants import BYTES_PER_WORD +from spynnaker.pyNN.utilities.utility_calls import ( + round_up, get_probable_maximum_selected) from .abstract_connector import AbstractConnector from .abstract_generate_connector_on_machine import ( AbstractGenerateConnectorOnMachine, ConnectorIDs) from .abstract_connector_supports_views_on_machine import ( AbstractConnectorSupportsViewsOnMachine) -from spinn_front_end_common.utilities.constants import BYTES_PER_WORD N_GEN_PARAMS = 6 @@ -43,11 +42,11 @@ def __init__( callback=None, verbose=False, rng=None): """ :param float p_connect: - a float between zero and one. Each potential connection is created\ + a float between zero and one. Each potential connection is created with this probability. :param bool allow_self_connections: - if the connector is used to connect a Population to itself, this\ - flag determines whether a neuron is allowed to connect to itself,\ + if the connector is used to connect a Population to itself, this + flag determines whether a neuron is allowed to connect to itself, or only to other neurons in the Population. :param bool safe: :param callable callback: Ignored @@ -67,7 +66,7 @@ def __init__( @overrides(AbstractConnector.get_delay_maximum) def get_delay_maximum(self, synapse_info): - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons * synapse_info.n_post_neurons, self._p_connect) @@ -78,12 +77,12 @@ def get_n_connections_from_pre_vertex_maximum( self, post_vertex_slice, synapse_info, min_delay=None, max_delay=None): # pylint: disable=too-many-arguments - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, post_vertex_slice.n_atoms, self._p_connect, chance=1.0/10000.0) if min_delay is None or max_delay is None: - return int(math.ceil(n_connections)) + return round_up(n_connections) return self._get_n_connections_from_pre_vertex_with_delay_maximum( synapse_info.delays, @@ -93,7 +92,7 @@ def get_n_connections_from_pre_vertex_maximum( @overrides(AbstractConnector.get_n_connections_to_post_vertex_maximum) def get_n_connections_to_post_vertex_maximum(self, synapse_info): # pylint: disable=too-many-arguments - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons, self._p_connect, chance=1.0/10000.0) @@ -102,7 +101,7 @@ def get_n_connections_to_post_vertex_maximum(self, synapse_info): @overrides(AbstractConnector.get_weight_maximum) def get_weight_maximum(self, synapse_info): # pylint: disable=too-many-arguments - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons * synapse_info.n_post_neurons, self._p_connect) diff --git a/spynnaker/pyNN/models/neural_projections/connectors/from_list_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/from_list_connector.py index 962391e1271..d12e4be4c5b 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/from_list_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/from_list_connector.py @@ -13,18 +13,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging import numpy - -from spinn_front_end_common.utilities.constants import \ - MICRO_TO_MILLISECOND_CONVERSION from spinn_utilities.overrides import overrides -from spinn_front_end_common.utilities import globals_variables +from spinn_front_end_common.utilities.constants import ( + MICRO_TO_MILLISECOND_CONVERSION) +from spinn_front_end_common.utilities.globals_variables import get_simulator from .abstract_connector import AbstractConnector from spynnaker.pyNN.exceptions import InvalidParameterType -logger = logging.getLogger(__name__) - # Indices of the source and target in the connection list array _SOURCE = 0 _TARGET = 1 @@ -337,8 +333,7 @@ def conn_list(self, conn_list): self.__delays = None try: delay_column = column_names.index('delay') + _FIRST_PARAM - machine_time_step = globals_variables.get_simulator( - ).machine_time_step + machine_time_step = get_simulator().machine_time_step self.__delays = (numpy.rint( numpy.array(self.__conn_list[:, delay_column]) * ( MICRO_TO_MILLISECOND_CONVERSION / machine_time_step)) * @@ -373,9 +368,11 @@ def conn_list(self, conn_list): @property def column_names(self): - """ The names of the columns in the array after the first two. \ - Of particular interest is whether ``weight`` and ``delay`` columns\ - are present. + """ The names of the columns in the array after the first two. + + .. note:: + Of particular interest is whether ``weight`` and ``delay`` columns + are present. :rtype: list(str) """ @@ -386,8 +383,8 @@ def column_names(self, column_names): self.__column_names = column_names def get_extra_parameters(self): - """ Getter for the extra parameters. Excludes ``weight`` and\ - ``delay`` columns. + """ Getter for the extra parameters. Excludes ``weight`` and + ``delay`` columns. :return: The extra parameters :rtype: ~numpy.ndarray diff --git a/spynnaker/pyNN/models/neural_projections/connectors/index_based_probability_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/index_based_probability_connector.py index 1b7a0250ae3..4e69097e6d6 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/index_based_probability_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/index_based_probability_connector.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging import math import numpy from numpy import ( @@ -22,10 +20,10 @@ minimum, e, pi) from spinn_utilities.overrides import overrides from spinn_utilities.safe_eval import SafeEval -from spynnaker.pyNN.utilities import utility_calls +from spynnaker.pyNN.utilities.utility_calls import ( + get_probable_maximum_selected, round_up) from .abstract_connector import AbstractConnector -logger = logging.getLogger(__name__) # support for arbitrary expression for the indices _index_expr_context = SafeEval(math, numpy, arccos, arcsin, arctan, arctan2, ceil, cos, cosh, exp, fabs, floor, fmod, hypot, @@ -48,9 +46,10 @@ def __init__( safe=True, callback=None, verbose=False): """ :param str index_expression: - the right-hand side of a valid python expression for - probability, involving the indices of the pre and post populations, - that can be parsed by eval(), that computes a probability dist. + the right-hand side of a valid Python expression for + probability, involving the indices of the pre- and + post-populations, that can be parsed by eval(), that computes a + probability distribution. :param bool allow_self_connections: if the connector is used to connect a Population to itself, this flag determines whether a neuron is allowed to connect to itself, @@ -84,7 +83,7 @@ def _update_probs_from_index_expression(self, synapse_info): @overrides(AbstractConnector.get_delay_maximum) def get_delay_maximum(self, synapse_info): self._update_probs_from_index_expression(synapse_info) - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons * synapse_info.n_post_neurons, numpy.amax(self.__probs)) @@ -95,12 +94,12 @@ def get_n_connections_from_pre_vertex_maximum( self, post_vertex_slice, synapse_info, min_delay=None, max_delay=None): self._update_probs_from_index_expression(synapse_info) - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, post_vertex_slice.n_atoms, numpy.amax(self.__probs)) if min_delay is None or max_delay is None: - return int(math.ceil(n_connections)) + return round_up(n_connections) return self._get_n_connections_from_pre_vertex_with_delay_maximum( synapse_info.delays, @@ -110,14 +109,14 @@ def get_n_connections_from_pre_vertex_maximum( @overrides(AbstractConnector.get_n_connections_to_post_vertex_maximum) def get_n_connections_to_post_vertex_maximum(self, synapse_info): self._update_probs_from_index_expression(synapse_info) - return utility_calls.get_probable_maximum_selected( + return get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons, numpy.amax(self.__probs)) @overrides(AbstractConnector.get_weight_maximum) def get_weight_maximum(self, synapse_info): self._update_probs_from_index_expression(synapse_info) - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( synapse_info.n_pre_neurons * synapse_info.n_post_neurons, synapse_info.n_pre_neurons * synapse_info.n_post_neurons, numpy.amax(self.__probs)) @@ -180,9 +179,9 @@ def allow_self_connections(self, new_value): @property def index_expression(self): - """ The right-hand side of a valid python expression for probability,\ - involving the indices of the pre and post populations, that can\ - be parsed by eval(), that computes a probability dist. + """ The right-hand side of a valid Python expression for probability,\ + involving the indices of the pre- and post-populations, that can\ + be parsed by eval(), that computes a probability distribution. :rtype: str """ diff --git a/spynnaker/pyNN/models/neural_projections/connectors/kernel_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/kernel_connector.py index b64d4cb1105..a5f9918e255 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/kernel_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/kernel_connector.py @@ -46,6 +46,15 @@ class KernelConnector(AbstractGenerateConnectorOnMachine): Should these include `allow_self_connections` and `with_replacement`? """ + __slots__ = ( + "_kernel_w", "_kernel_h", "_hlf_k_w", "_hlf_k_h", + "_pre_w", "_pre_h", "_post_w", "_post_h", + "_pre_start_w", "_pre_start_h", "_post_start_w", "_post_start_h", + "_pre_step_w", "_pre_step_h", "_post_step_w", "_post_step_h", + "_krn_weights", "_krn_delays", "_shape_pre", "_shape_post", + "_shape_common", "_common_w", "_common_h", "_post_as_pre") + + __KERNEL_ARRAY_TYPES = (numpy.ndarray, ConvolutionKernel) def __init__( self, shape_pre, shape_post, shape_kernel, weight_kernel, @@ -65,11 +74,11 @@ def __init__( :type shape_kernel: list(int) or tuple(int,int) :param weight_kernel: (optional) 2D matrix of size shape_kernel describing the weights - :type weight_kernel: ~numpy.ndarray or ~pyNN.random.NumpyRNG \ + :type weight_kernel: ~numpy.ndarray or ~pyNN.random.NumpyRNG or int or float or list(int) or list(float) or None :param delay_kernel: (optional) 2D matrix of size shape_kernel describing the delays - :type delay_kernel: ~numpy.ndarray or ~pyNN.random.NumpyRNG \ + :type delay_kernel: ~numpy.ndarray or ~pyNN.random.NumpyRNG or int or float or list(int) or list(float) or None :param shape_common: (optional) 2D shape of common coordinate system (for both pre and post, @@ -177,13 +186,11 @@ def __post_as_pre(self, post_vertex_slice): :param ~pacman.model.graphs.common.Slice post_vertex_slice: :rtype: tuple(~numpy.ndarray, ~numpy.ndarray) """ - # TODO: When slices become hashable, update this code to use them - # directly as the cache index - if str(post_vertex_slice) not in self._post_as_pre: + if post_vertex_slice not in self._post_as_pre: post_r, post_c = self.__to_post_coords(post_vertex_slice) - self._post_as_pre[str(post_vertex_slice)] = \ + self._post_as_pre[post_vertex_slice] = \ self.__map_to_pre_coords(post_r, post_c) - return self._post_as_pre[str(post_vertex_slice)] + return self._post_as_pre[post_vertex_slice] def __pre_as_post(self, pre_r, pre_c): """ Write pre coords as post coords. @@ -200,7 +207,7 @@ def __get_kernel_vals(self, vals): """ Convert kernel values given into the correct format. :param vals: - :type vals: int or float or ~pyNN.random.NumpyRNG or ~numpy.ndarray\ + :type vals: int or float or ~pyNN.random.NumpyRNG or ~numpy.ndarray or ConvolutionKernel :rtype: ~numpy.ndarray """ @@ -212,8 +219,8 @@ def __get_kernel_vals(self, vals): return numpy.array(vals.next(krn_size)).reshape(krn_shape) elif numpy.isscalar(vals): return vals * numpy.ones(krn_shape) - elif ((isinstance(vals, numpy.ndarray) or - isinstance(vals, ConvolutionKernel)) and + elif ( + isinstance(vals, self.__KERNEL_ARRAY_TYPES) and vals.shape[HEIGHT] == self._kernel_h and vals.shape[WIDTH] == self._kernel_w): return vals.view(ConvolutionKernel) @@ -228,10 +235,10 @@ def __compute_statistics( """ Compute the relevant information required for the connections. :param weights: - :type weights: int or float or ~pyNN.random.NumpyRNG or \ + :type weights: int or float or ~pyNN.random.NumpyRNG or ~numpy.ndarray or ConvolutionKernel :param delays: - :type delays: int or float or ~pyNN.random.NumpyRNG or ~numpy.ndarray\ + :type delays: int or float or ~pyNN.random.NumpyRNG or ~numpy.ndarray or ConvolutionKernel :param ~pacman.model.graphs.common.Slice pre_vertex_slice: :param ~pacman.model.graphs.common.Slice post_vertex_slice: diff --git a/spynnaker/pyNN/models/neural_projections/connectors/multapse_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/multapse_connector.py index f0229340b0f..060f4d8a023 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/multapse_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/multapse_connector.py @@ -12,16 +12,14 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging -import math import numpy.random from six import raise_from from spinn_utilities.abstract_base import abstractmethod from spinn_utilities.overrides import overrides from pacman.model.graphs.common import Slice from spinn_front_end_common.utilities.constants import BYTES_PER_WORD -from spynnaker.pyNN.utilities import utility_calls +from spynnaker.pyNN.utilities.utility_calls import ( + get_probable_maximum_selected, round_up) from spynnaker.pyNN.exceptions import SpynnakerException from .abstract_connector import AbstractConnector from .abstract_generate_connector_on_machine import ( @@ -31,8 +29,6 @@ N_GEN_PARAMS = 8 -logger = logging.getLogger(__name__) - class MultapseConnector(AbstractGenerateConnectorOnMachine, AbstractConnectorSupportsViewsOnMachine): @@ -148,14 +144,14 @@ def get_n_connections_from_pre_vertex_maximum( prob_in_slice = min( post_vertex_slice.n_atoms / float(synapse_info.n_post_neurons), 1.0) - max_in_slice = utility_calls.get_probable_maximum_selected( + max_in_slice = get_probable_maximum_selected( self.__num_synapses, self.__num_synapses, prob_in_slice) prob_in_row = 1.0 / synapse_info.n_pre_neurons - n_connections = utility_calls.get_probable_maximum_selected( + n_connections = get_probable_maximum_selected( self.__num_synapses, max_in_slice, prob_in_row) if min_delay is None or max_delay is None: - return int(math.ceil(n_connections)) + return round_up(n_connections) return self._get_n_connections_from_pre_vertex_with_delay_maximum( synapse_info.delays, @@ -165,7 +161,7 @@ def get_n_connections_from_pre_vertex_maximum( @overrides(AbstractConnector.get_n_connections_to_post_vertex_maximum) def get_n_connections_to_post_vertex_maximum(self, synapse_info): prob_of_choosing_post_atom = 1.0 / synapse_info.n_post_neurons - return utility_calls.get_probable_maximum_selected( + return get_probable_maximum_selected( self.__num_synapses, self.__num_synapses, prob_of_choosing_post_atom) diff --git a/spynnaker/pyNN/models/neural_projections/connectors/one_to_one_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/one_to_one_connector.py index f730ddacfb8..77b07d6403a 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/one_to_one_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/one_to_one_connector.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging import numpy from spinn_utilities.overrides import overrides from .abstract_connector import AbstractConnector @@ -22,8 +20,6 @@ from .abstract_connector_supports_views_on_machine import ( AbstractConnectorSupportsViewsOnMachine) -logger = logging.getLogger(__name__) - class OneToOneConnector(AbstractGenerateConnectorOnMachine, AbstractConnectorSupportsViewsOnMachine): @@ -113,8 +109,7 @@ def create_synaptic_block( def __repr__(self): return "OneToOneConnector()" - def _get_pre_post_limits( - self, pre_slice, post_slice, synapse_info): + def _get_pre_post_limits(self, pre_slice, post_slice, synapse_info): """ :param ~pacman.model.graphs.common.Slice pre_slice: :param ~pacman.model.graphs.common.Slice post_slice: diff --git a/spynnaker/pyNN/models/neural_projections/connectors/small_world_connector.py b/spynnaker/pyNN/models/neural_projections/connectors/small_world_connector.py index 9df9a7e4cbd..7c6cc61c870 100644 --- a/spynnaker/pyNN/models/neural_projections/connectors/small_world_connector.py +++ b/spynnaker/pyNN/models/neural_projections/connectors/small_world_connector.py @@ -12,11 +12,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import math - import numpy from spinn_utilities.overrides import overrides from .abstract_connector import AbstractConnector +from spynnaker.pyNN.utilities.utility_calls import round_up class SmallWorldConnector(AbstractConnector): @@ -85,7 +84,7 @@ def _set_n_connections(self, synapse_info): self.__mask = (d < self.__degree).astype(float) - self.__n_connections = int(math.ceil(numpy.sum(self.__mask))) + self.__n_connections = round_up(numpy.sum(self.__mask)) @overrides(AbstractConnector.get_delay_maximum) def get_delay_maximum(self, synapse_info): diff --git a/spynnaker/pyNN/models/neural_projections/delay_afferent_machine_edge.py b/spynnaker/pyNN/models/neural_projections/delay_afferent_machine_edge.py index 1d4faef5be3..8d8777c1668 100644 --- a/spynnaker/pyNN/models/neural_projections/delay_afferent_machine_edge.py +++ b/spynnaker/pyNN/models/neural_projections/delay_afferent_machine_edge.py @@ -12,15 +12,11 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spinn_utilities.overrides import overrides from pacman.model.graphs.machine import MachineEdge from spynnaker.pyNN.models.abstract_models import ( AbstractWeightUpdatable, AbstractFilterableEdge) -logger = logging.getLogger(__name__) - class DelayAfferentMachineEdge( MachineEdge, AbstractFilterableEdge, AbstractWeightUpdatable): diff --git a/spynnaker/pyNN/models/neural_projections/projection_application_edge.py b/spynnaker/pyNN/models/neural_projections/projection_application_edge.py index 1b617955077..7d21ef22cc4 100644 --- a/spynnaker/pyNN/models/neural_projections/projection_application_edge.py +++ b/spynnaker/pyNN/models/neural_projections/projection_application_edge.py @@ -12,14 +12,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spinn_utilities.overrides import overrides from pacman.model.graphs.application import ApplicationEdge from .projection_machine_edge import ProjectionMachineEdge from spynnaker.pyNN.models.abstract_models import AbstractFilterableEdge - -logger = logging.getLogger(__name__) _DynamicsStructural = None diff --git a/spynnaker/pyNN/models/neural_projections/synapse_information.py b/spynnaker/pyNN/models/neural_projections/synapse_information.py index d41264f61c5..500cc325846 100644 --- a/spynnaker/pyNN/models/neural_projections/synapse_information.py +++ b/spynnaker/pyNN/models/neural_projections/synapse_information.py @@ -17,7 +17,7 @@ from spynnaker.pyNN.models.neural_projections.connectors import ( AbstractGenerateConnectorOnMachine) from spynnaker.pyNN.models.neuron.synapse_dynamics import ( - AbstractGenerateOnMachine) + AbstractGenerateOnMachine, AbstractSynapseDynamicsStructural) class SynapseInformation(object): @@ -47,8 +47,8 @@ def __init__(self, connector, pre_population, post_population, The population sending spikes to the synapse :param PyNNPopulationCommon post_population: The population hosting the synapse - :param bool prepop_is_view: Whether the prepopulation is a view - :param bool postpop_is_view: Whether the postpopulation is a view + :param bool prepop_is_view: Whether the pre-population is a view + :param bool postpop_is_view: Whether the post-population is a view :param rng: Seeded random number generator :type rng: ~pyNN.random.NumpyRNG or None :param AbstractSynapseDynamics synapse_dynamics: @@ -96,7 +96,7 @@ def post_population(self): @property def n_pre_neurons(self): - """ The number of neurons in the prepopulation + """ The number of neurons in the pre-population :rtype: int """ @@ -104,7 +104,7 @@ def n_pre_neurons(self): @property def n_post_neurons(self): - """ The number of neurons in the postpopulation + """ The number of neurons in the post-population :rtype: int """ @@ -112,7 +112,7 @@ def n_post_neurons(self): @property def prepop_is_view(self): - """ Whether the prepopulation is a view + """ Whether the pre-population is a view :rtype: bool """ @@ -120,7 +120,7 @@ def prepop_is_view(self): @property def postpop_is_view(self): - """ Whether the postpopulation is a view + """ Whether the post-population is a view :rtype: bool """ @@ -142,6 +142,16 @@ def synapse_dynamics(self): """ return self.__synapse_dynamics + @property + def is_structural(self): + """ Whether the dynamic behaviour of the synapse is structurally \ + plastic. + + :rtype: bool + """ + return isinstance(self.__synapse_dynamics, + AbstractSynapseDynamicsStructural) + @property def synapse_type(self): """ The type of the synapse diff --git a/spynnaker/pyNN/models/neural_properties/neural_parameter.py b/spynnaker/pyNN/models/neural_properties/neural_parameter.py index bc1887e6e84..757d591c976 100644 --- a/spynnaker/pyNN/models/neural_properties/neural_parameter.py +++ b/spynnaker/pyNN/models/neural_properties/neural_parameter.py @@ -111,8 +111,8 @@ def __init__(self, value, datatype, slice_start, slice_stop, spec): """ Iterator that repeats the single values the required number of\ times. - Allows a single Value parameter to be treated the same as parameters\ - with len. \ + Allows a single Value parameter to be treated the same as parameters + with len. Caches `cmd_word_list` and `cmd_string` so they are only created once. :param value: The simple value that is the data for each element @@ -146,10 +146,10 @@ class NeuronParameter(object): def __init__(self, value, data_type): """ - :param value: what the value of the parameter is; if a list or array,\ + :param value: what the value of the parameter is; if a list or array, potentially provides a different value for each neuron - :type value: int or float or bool or list(int) or list(float) or \ - list(bool) or ~numpy.ndarray or \ + :type value: int or float or bool or list(int) or list(float) or + list(bool) or ~numpy.ndarray or ~spinn_utilities.ranged.AbstractList :param ~data_specification.enums.DataType data_type: The serialization type of the parameter in the neuron model. @@ -164,8 +164,8 @@ def get_value(self): """ What the value of the parameter is; if a list or array,\ potentially provides a different value for each neuron. - :rtype: int or float or bool or list(int) or list(float) or \ - list(bool) or ~numpy.ndarray or \ + :rtype: int or float or bool or list(int) or list(float) or + list(bool) or ~numpy.ndarray or ~spinn_utilities.ranged.AbstractList """ return self.__value @@ -186,7 +186,7 @@ def iterator_by_slice(self, slice_start, slice_stop, spec): :param ~data_specification.DataSpecificationGenerator spec: The data specification to eventually write to. (Note that this does not actually do the write). - :return: Iterator that produces a command to write to the\ + :return: Iterator that produces a command to write to the specification for each element in the slice. :rtype: six.Iterator(tuple(bytearray, str)) """ diff --git a/spynnaker/pyNN/models/neuron/abstract_population_vertex.py b/spynnaker/pyNN/models/neuron/abstract_population_vertex.py index bea13ea7574..5be782f0ce6 100644 --- a/spynnaker/pyNN/models/neuron/abstract_population_vertex.py +++ b/spynnaker/pyNN/models/neuron/abstract_population_vertex.py @@ -12,10 +12,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging import os -import math from spinn_utilities.overrides import overrides from pacman.model.constraints.key_allocator_constraints import ( ContiguousKeyRangeContraint) @@ -23,6 +20,9 @@ from pacman.model.graphs.application import ApplicationVertex from pacman.model.resources import ( ConstantSDRAM, CPUCyclesPerTickResource, DTCMResource, ResourceContainer) +from pacman.utilities.algorithm_utilities.partition_algorithm_utilities \ + import ( + determine_max_atoms_for_vertex) from spinn_front_end_common.abstract_models import ( AbstractChangableAfterRun, AbstractProvidesIncomingPartitionConstraints, AbstractProvidesOutgoingPartitionConstraints, AbstractHasAssociatedBinary, @@ -30,29 +30,38 @@ AbstractCanReset) from spinn_front_end_common.abstract_models.impl import ( ProvidesKeyToAtomMappingImpl) -from spinn_front_end_common.utilities import ( - constants as common_constants, helpful_functions, globals_variables) from spinn_front_end_common.utilities.constants import ( - BYTES_PER_WORD, SYSTEM_BYTES_REQUIREMENT) + BYTES_PER_WORD, SIMULATION_N_BYTES, SYSTEM_BYTES_REQUIREMENT) +from spinn_front_end_common.utilities.helpful_functions import ( + read_config_int, locate_memory_region_for_placement) +from spinn_front_end_common.utilities.globals_variables import get_simulator from spinn_front_end_common.utilities.utility_objs import ExecutableType -from spinn_front_end_common.interface.simulation import simulation_utilities -from spinn_front_end_common.interface.profiling import profile_utils -from spynnaker.pyNN.utilities.constants import POPULATION_BASED_REGIONS -from spynnaker.pyNN.models.common import ( - AbstractSpikeRecordable, AbstractNeuronRecordable, NeuronRecorder) -from spynnaker.pyNN.utilities import constants, bit_field_utilities +from spinn_front_end_common.interface.simulation.simulation_utilities import ( + get_simulation_header_array) +from spinn_front_end_common.interface.profiling.profile_utils import ( + get_profile_region_size, reserve_profile_region, write_profile_region_data) +from spynnaker.pyNN.utilities.constants import ( + POPULATION_BASED_REGIONS, SPIKE_PARTITION_ID, BITS_PER_WORD) +from spynnaker.pyNN.utilities.ranged import ( + SpynnakerRangeDictionary, SpynnakerRangedList) +from spynnaker.pyNN.utilities.utility_calls import ceildiv +from spynnaker.pyNN.utilities.bit_field_utilities import ( + exact_sdram_for_bit_field_builder_region, + get_estimated_sdram_for_key_region, + reserve_bit_field_regions, write_bitfield_init_data, + ELEMENTS_USED_IN_EACH_BIT_FIELD) +from spynnaker.pyNN.exceptions import InvalidParameterType from spynnaker.pyNN.models.abstract_models import ( AbstractPopulationInitializable, AbstractAcceptsIncomingSynapses, AbstractPopulationSettable, AbstractReadParametersBeforeSet, AbstractContainsUnits) -from spynnaker.pyNN.exceptions import InvalidParameterType -from spynnaker.pyNN.utilities.ranged import ( - SpynnakerRangeDictionary, SpynnakerRangedList) +from spynnaker.pyNN.models.common import ( + AbstractSpikeRecordable, AbstractNeuronRecordable, NeuronRecorder) from .synapse_dynamics import AbstractSynapseDynamicsStructural from .synaptic_manager import SynapticManager from .population_machine_vertex import PopulationMachineVertex - -logger = logging.getLogger(__name__) +from spynnaker.pyNN.models.utility_models.delays import DelayExtensionVertex +from spynnaker.pyNN.models.neural_projections import ProjectionApplicationEdge # TODO: Make sure these values are correct (particularly CPU cycles) _NEURON_BASE_DTCM_USAGE_IN_BYTES = 9 * BYTES_PER_WORD @@ -80,7 +89,8 @@ class AbstractPopulationVertex( AbstractReadParametersBeforeSet, AbstractAcceptsIncomingSynapses, ProvidesKeyToAtomMappingImpl, AbstractCanReset): """ Underlying vertex model for Neural Populations. - Not actually abstract. + + Not actually abstract. """ __slots__ = [ @@ -134,8 +144,8 @@ def __init__( :param spikes_per_second: Expected spike rate :type spikes_per_second: float or None :param ring_buffer_sigma: - How many SD above the mean to go for upper bound of ring buffer \ - size; a good starting choice is 5.0. Given length of simulation \ + How many SD above the mean to go for upper bound of ring buffer + size; a good starting choice is 5.0. Given length of simulation we can set this for approximate number of saturation events. :type ring_buffer_sigma: float or None :param incoming_spike_buffer_size: @@ -158,7 +168,7 @@ def __init__( self.__incoming_spike_buffer_size = incoming_spike_buffer_size # get config from simulator - config = globals_variables.get_simulator().config + config = get_simulator().config if incoming_spike_buffer_size is None: self.__incoming_spike_buffer_size = config.getint( @@ -194,7 +204,7 @@ def __init__( self.__has_reset_last = True # Set up for profiling - self.__n_profile_samples = helpful_functions.read_config_int( + self.__n_profile_samples = read_config_int( config, "Reports", "n_profile_samples") @property @@ -275,6 +285,7 @@ def get_cpu_usage_for_atoms(self, vertex_slice): def get_dtcm_usage_for_atoms(self, vertex_slice): """ :param ~pacman.model.graphs.common.Slice vertex_slice: + :rtype: int """ return ( _NEURON_BASE_DTCM_USAGE_IN_BYTES + @@ -288,6 +299,7 @@ def _get_sdram_usage_for_neuron_params(self, vertex_slice): :param ~pacman.model.graphs.common.Slice vertex_slice: the slice of atoms. :return: The SDRAM required for the neuron region + :rtype: int """ return ( self._BYTES_TILL_START_OF_GLOBAL_PARAMETERS + @@ -295,7 +307,14 @@ def _get_sdram_usage_for_neuron_params(self, vertex_slice): def _get_sdram_usage_for_atoms( self, vertex_slice, graph, machine_time_step): - sdram_requirement = ( + """ + :param ~pacman.model.graphs.common.Slice vertex_slice: + :param ~.ApplicationGraph graph: + :param int machine_time_step: + :return: number of bytes + :rtype: int + """ + return ( SYSTEM_BYTES_REQUIREMENT + self._get_sdram_usage_for_neuron_params(vertex_slice) + self._neuron_recorder.get_static_sdram_usage(vertex_slice) + @@ -303,14 +322,10 @@ def _get_sdram_usage_for_atoms( len(PopulationMachineVertex.EXTRA_PROVENANCE_DATA_ENTRIES)) + self.__synapse_manager.get_sdram_usage_in_bytes( vertex_slice, machine_time_step, graph, self) + - profile_utils.get_profile_region_size( - self.__n_profile_samples) + - bit_field_utilities.get_estimated_sdram_for_bit_field_region( - graph, self) + - bit_field_utilities.get_estimated_sdram_for_key_region( - graph, self) + - bit_field_utilities.exact_sdram_for_bit_field_builder_region()) - return sdram_requirement + get_profile_region_size(self.__n_profile_samples) + + self.__estimate_sdram_for_bit_field_region(graph) + + get_estimated_sdram_for_key_region(graph, self) + + exact_sdram_for_bit_field_builder_region()) def _reserve_memory_regions( self, spec, vertex_slice, vertex, machine_graph, n_key_map): @@ -322,15 +337,14 @@ def _reserve_memory_regions( the slice of atoms from the application vertex :param ~.MachineVertex vertex: this vertex :param ~.MachineGraph machine_graph: machine graph - :param n_key_map: nkey map - :return: None + :param ~.AbstractMachinePartitionNKeysMap n_key_map: nkey map """ spec.comment("\nReserving memory space for data regions:\n\n") # Reserve memory: spec.reserve_memory_region( region=POPULATION_BASED_REGIONS.SYSTEM.value, - size=common_constants.SIMULATION_N_BYTES, + size=SIMULATION_N_BYTES, label='System') self._reserve_neuron_params_data_region(spec, vertex_slice) @@ -340,12 +354,12 @@ def _reserve_memory_regions( size=self._neuron_recorder.get_static_sdram_usage(vertex_slice), label="neuron recording") - profile_utils.reserve_profile_region( + reserve_profile_region( spec, POPULATION_BASED_REGIONS.PROFILING.value, self.__n_profile_samples) # reserve bit field region - bit_field_utilities.reserve_bit_field_regions( + reserve_bit_field_regions( spec, machine_graph, n_key_map, vertex, POPULATION_BASED_REGIONS.BIT_FIELD_BUILDER.value, POPULATION_BASED_REGIONS.BIT_FIELD_FILTER.value, @@ -356,11 +370,10 @@ def _reserve_memory_regions( def _reserve_neuron_params_data_region(self, spec, vertex_slice): """ Reserve the neuron parameter data region. - :param ~data_specification.DataSpecificationGenerator spec: + :param ~.DataSpecificationGenerator spec: the spec to write the DSG region to :param ~pacman.model.graphs.common.Slice vertex_slice: the slice of atoms from the application vertex - :return: None """ params_size = self._get_sdram_usage_for_neuron_params(vertex_slice) spec.reserve_memory_region( @@ -369,6 +382,12 @@ def _reserve_neuron_params_data_region(self, spec, vertex_slice): @staticmethod def __copy_ranged_dict(source, merge=None, merge_keys=None): + """ + :param SpynnakerRangeDictionary source: + :param SpynnakerRangeDictionary merge: + :param set merge_keys: + :rtype: SpynnakerRangeDictionary + """ target = SpynnakerRangeDictionary(len(source)) for key in source.keys(): copy_list = SpynnakerRangedList(len(source)) @@ -386,7 +405,14 @@ def __copy_ranged_dict(source, merge=None, merge_keys=None): def _write_neuron_parameters( self, spec, key, vertex_slice, machine_time_step, time_scale_factor): - + """ + :param ~.DataSpecificationGenerator spec: + :param key: + :type key: int or None + :param ~pacman.model.graphs.common.Slice vertex_slice: + :param int machine_time_step: + :param int time_scale_factor: + """ # If resetting, reset any state variables that need to be reset if (self.__has_reset_last and self.__initial_state_variables is not None): @@ -410,15 +436,13 @@ def _write_neuron_parameters( n_atoms)) # Set the focus to the memory region 2 (neuron parameters): - spec.switch_write_focus( - region=constants.POPULATION_BASED_REGIONS.NEURON_PARAMS.value) + spec.switch_write_focus(POPULATION_BASED_REGIONS.NEURON_PARAMS.value) # Write the random back off value max_offset = ( machine_time_step * time_scale_factor) // _MAX_OFFSET_DENOMINATOR spec.write_value( - int(math.ceil(max_offset / self.__n_subvertices)) * - self.__n_data_specs) + ceildiv(max_offset, self.__n_subvertices) * self.__n_data_specs) self.__n_data_specs += 1 # Write the number of microseconds between sending spikes @@ -461,6 +485,11 @@ def _write_neuron_parameters( def regenerate_data_specification( self, spec, placement, machine_time_step, time_scale_factor, routing_info): + """ + :param int machine_time_step: (injected) + :param int time_scale_factor: (injected) + :param ~pacman.model.routing_info.RoutingInfo routing_info: (injected) + """ # pylint: disable=too-many-arguments, arguments-differ vertex_slice = placement.vertex.vertex_slice @@ -470,7 +499,7 @@ def regenerate_data_specification( # write the neuron params into the new DSG region self._write_neuron_parameters( key=routing_info.get_first_key_from_pre_vertex( - placement.vertex, constants.SPIKE_PARTITION_ID), + placement.vertex, SPIKE_PARTITION_ID), machine_time_step=machine_time_step, spec=spec, time_scale_factor=time_scale_factor, vertex_slice=vertex_slice) @@ -508,13 +537,19 @@ def generate_data_specification( application_graph, machine_graph, routing_info, data_n_time_steps, n_key_map): """ - :param machine_time_step: (injected) - :param time_scale_factor: (injected) + :param int machine_time_step: (injected) + :param int time_scale_factor: (injected) :param application_graph: (injected) + :type application_graph: + ~pacman.model.graphs.application.ApplicationGraph :param machine_graph: (injected) - :param routing_info: (injected) - :param data_n_time_steps: (injected) + :type machine_graph: + ~pacman.model.graphs.machine.MachineGraph + :param ~pacman.model.routing_info.RoutingInfo routing_info: (injected) + :param int data_n_time_steps: (injected) :param n_key_map: (injected) + :type n_key_map: + ~pacman.model.routing_info.AbstractMachinePartitionNKeysMap """ # pylint: disable=too-many-arguments, arguments-differ vertex = placement.vertex @@ -533,11 +568,11 @@ def generate_data_specification( # Get the key key = routing_info.get_first_key_from_pre_vertex( - vertex, constants.SPIKE_PARTITION_ID) + vertex, SPIKE_PARTITION_ID) # Write the setup region spec.switch_write_focus(POPULATION_BASED_REGIONS.SYSTEM.value) - spec.write_array(simulation_utilities.get_simulation_header_array( + spec.write_array(get_simulation_header_array( self.get_binary_file_name(), machine_time_step, time_scale_factor)) @@ -551,7 +586,7 @@ def generate_data_specification( spec, key, vertex_slice, machine_time_step, time_scale_factor) # write profile data - profile_utils.write_profile_region_data( + write_profile_region_data( spec, POPULATION_BASED_REGIONS.PROFILING.value, self.__n_profile_samples) @@ -568,7 +603,7 @@ def generate_data_specification( self.__synapse_manager.on_chip_written_matrix_size) # write up the bitfield builder data - bit_field_utilities.write_bitfield_init_data( + write_bitfield_init_data( spec, vertex, machine_graph, routing_info, n_key_map, POPULATION_BASED_REGIONS.BIT_FIELD_BUILDER.value, POPULATION_BASED_REGIONS.POPULATION_TABLE.value, @@ -586,7 +621,6 @@ def generate_data_specification( @overrides(AbstractHasAssociatedBinary.get_binary_file_name) def get_binary_file_name(self): - # Split binary name into title and extension binary_title, binary_extension = os.path.splitext( self.__neuron_impl.binary_name) @@ -673,6 +707,11 @@ def initialize_parameters(self): return self.__pynn_model.default_initial_values.keys() def _get_parameter(self, variable): + """ + :param str variable: + :rtype: str + :raises KeyError: If the param with that name doesn't exist + """ if variable.endswith("_init"): # method called with "V_init" key = variable[:-5] @@ -725,8 +764,6 @@ def conductance_based(self): @overrides(AbstractPopulationSettable.get_value) def get_value(self, key): - """ Get a property of the overall model. - """ if key not in self._parameters: raise InvalidParameterType( "Population {} does not have parameter {}".format( @@ -735,8 +772,6 @@ def get_value(self, key): @overrides(AbstractPopulationSettable.set_value) def set_value(self, key, value): - """ Set a property of the overall model. - """ if key not in self._parameters: raise InvalidParameterType( "Population {} does not have parameter {}".format( @@ -747,12 +782,10 @@ def set_value(self, key, value): @overrides(AbstractReadParametersBeforeSet.read_parameters_from_machine) def read_parameters_from_machine( self, transceiver, placement, vertex_slice): - # locate SDRAM address to where the neuron parameters are stored - neuron_region_sdram_address = \ - helpful_functions.locate_memory_region_for_placement( - placement, POPULATION_BASED_REGIONS.NEURON_PARAMS.value, - transceiver) + neuron_region_sdram_address = locate_memory_region_for_placement( + placement, POPULATION_BASED_REGIONS.NEURON_PARAMS.value, + transceiver) # shift past the extra stuff before neuron parameters that we don't # need to read @@ -783,6 +816,9 @@ def weight_scale(self): @property def ring_buffer_sigma(self): + """ + :rtype: float or None + """ return self.__synapse_manager.ring_buffer_sigma @ring_buffer_sigma.setter @@ -794,6 +830,9 @@ def reset_ring_buffer_shifts(self): @property def spikes_per_second(self): + """ + :rtype: float or None + """ return self.__synapse_manager.spikes_per_second @spikes_per_second.setter @@ -821,7 +860,7 @@ def add_pre_run_connection_holder( def get_connection_holders(self): """ - :rtype: dict(tuple(ProjectionApplicationEdge,SynapseInformation),\ + :rtype: dict(tuple(ProjectionApplicationEdge,SynapseInformation), ConnectionHolder) """ return self.__synapse_manager.get_connection_holders() @@ -848,21 +887,11 @@ def get_maximum_delay_supported_in_ms(self, machine_time_step): @overrides(AbstractProvidesIncomingPartitionConstraints. get_incoming_partition_constraints) def get_incoming_partition_constraints(self, partition): - """ Gets the constraints for partitions going into this vertex. - - :param partition: partition that goes into this vertex - :return: list of constraints - """ return self.__synapse_manager.get_incoming_partition_constraints() @overrides(AbstractProvidesOutgoingPartitionConstraints. get_outgoing_partition_constraints) def get_outgoing_partition_constraints(self, partition): - """ Gets the constraints for partitions going out of this vertex. - - :param partition: the partition that leaves this vertex - :return: list of constraints - """ return [ContiguousKeyRangeContraint()] @overrides( @@ -888,7 +917,6 @@ def _clear_recording_region( :param buffer_manager: the buffer manager object :param placements: the placements object :param recording_region_id: the recorded region ID for clearing - :rtype: None """ for machine_vertex in self.machine_vertices: placement = placements.get_placement_of_vertex(machine_vertex) @@ -909,11 +937,11 @@ def get_units(self, variable): def describe(self): """ Get a human-readable description of the cell or synapse type. - The output may be customised by specifying a different template\ - together with an associated template engine\ + The output may be customised by specifying a different template + together with an associated template engine (see :py:mod:`pyNN.descriptions`). - If template is None, then a dictionary containing the template context\ + If template is None, then a dictionary containing the template context will be returned. :rtype: dict(str, ...) @@ -931,6 +959,11 @@ def describe(self): return context def get_synapse_id_by_target(self, target): + """ Get the ID of a synapse given the name + + :param str target: The name of the synapse + :rtype: int + """ return self.__neuron_impl.get_synapse_id_by_target(target) def __str__(self): @@ -957,3 +990,34 @@ def reset_to_first_timestep(self): if self.__synapse_manager.changes_during_run: self.__change_requires_data_generation = True self.__change_requires_neuron_parameters_reload = False + + def __estimate_sdram_for_bit_field_region(self, app_graph): + """ Estimates the SDRAM for the bit field region of this vertex + + :param ~.ApplicationGraph app_graph: the app graph + :return: the estimated number of bytes used by the bit field region + :rtype: int + """ + return sum( + self.__estimate_sdram_for_edge_bit_fields(incoming_edge) + for incoming_edge in app_graph.get_edges_ending_at_vertex(self) + if isinstance(incoming_edge, ProjectionApplicationEdge)) + + @staticmethod + def __estimate_sdram_for_edge_bit_fields(edge): + """ + :param ProjectionApplicationEdge edge: + :rtype: int + """ + n_atoms = edge.pre_vertex.n_atoms + max_atoms = min( + determine_max_atoms_for_vertex(edge.pre_vertex), n_atoms) + + # Get the number of likely vertices + n_machine_vertices = ceildiv(n_atoms, max_atoms) + n_atoms_per_machine_vertex = ceildiv(n_atoms, n_machine_vertices) + if isinstance(edge.pre_vertex, DelayExtensionVertex): + n_atoms_per_machine_vertex *= edge.pre_vertex.n_delay_stages + n_words_for_atoms = ceildiv(n_atoms_per_machine_vertex, BITS_PER_WORD) + return BYTES_PER_WORD * (ELEMENTS_USED_IN_EACH_BIT_FIELD + ( + n_words_for_atoms * n_machine_vertices)) diff --git a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py index f69a8b131ba..f143cb39561 100644 --- a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py +++ b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py @@ -38,15 +38,26 @@ def __init__(self, model): @property def _model(self): + """ + :rtype: AbstractNeuronImpl + """ return self.__model @classmethod def set_model_max_atoms_per_core(cls, n_atoms=DEFAULT_MAX_ATOMS_PER_CORE): + """ Set the maximum number of atoms per core for this model. + + :param int n_atoms: The new maximum + """ super(AbstractPyNNNeuronModel, cls).set_model_max_atoms_per_core( n_atoms) @classmethod def get_max_atoms_per_core(cls): + """ Get the maximum number of atoms per core for this model. + + :rtype: int + """ if cls not in super(AbstractPyNNNeuronModel, cls)._max_atoms_per_core: return DEFAULT_MAX_ATOMS_PER_CORE return super(AbstractPyNNNeuronModel, cls).get_max_atoms_per_core() @@ -56,6 +67,11 @@ def get_max_atoms_per_core(cls): def create_vertex( self, n_neurons, label, constraints, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size): + """ + :param float spikes_per_second: + :param float ring_buffer_sigma: + :param int incoming_spike_buffer_size: + """ # pylint: disable=arguments-differ max_atoms = self.get_max_atoms_per_core() return AbstractPopulationVertex( diff --git a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py index dbe3dc24265..78374fbe7c6 100644 --- a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py +++ b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py @@ -13,9 +13,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from spinn_utilities.overrides import overrides from .abstract_pynn_neuron_model import AbstractPyNNNeuronModel from spynnaker.pyNN.models.neuron.implementations import NeuronImplStandard -from spinn_utilities.overrides import overrides _population_parameters = dict( AbstractPyNNNeuronModel.default_population_parameters) diff --git a/spynnaker/pyNN/models/neuron/additional_inputs/additional_input_ca2_adaptive.py b/spynnaker/pyNN/models/neuron/additional_inputs/additional_input_ca2_adaptive.py index 0c23bafe620..3525cf9cab0 100644 --- a/spynnaker/pyNN/models/neuron/additional_inputs/additional_input_ca2_adaptive.py +++ b/spynnaker/pyNN/models/neuron/additional_inputs/additional_input_ca2_adaptive.py @@ -73,11 +73,6 @@ def has_variable(self, variable): @overrides(AbstractAdditionalInput.get_values) def get_values(self, parameters, state_variables, vertex_slice, ts): - """ - :param int ts: machine time step - """ - # pylint: disable=arguments-differ - # Add the rest of the data return [parameters[TAU_CA2].apply_operation( operation=lambda x: numpy.exp(float(-ts) / (1000.0 * x))), diff --git a/spynnaker/pyNN/models/neuron/implementations/abstract_standard_neuron_component.py b/spynnaker/pyNN/models/neuron/implementations/abstract_standard_neuron_component.py index 9b656e18530..c971e5da948 100644 --- a/spynnaker/pyNN/models/neuron/implementations/abstract_standard_neuron_component.py +++ b/spynnaker/pyNN/models/neuron/implementations/abstract_standard_neuron_component.py @@ -87,7 +87,8 @@ def add_state_variables(self, state_variables): @abstractmethod def get_values(self, parameters, state_variables, vertex_slice, ts): - """ Get the values to be written to the machine for this model + # The way this docstring is built *is* a hack, but nothing else works + (r""" Get the values to be written to the machine for this model :param ~spinn_utilities.ranged.RangeDictionary parameters: The holder of the parameters @@ -97,10 +98,14 @@ def get_values(self, parameters, state_variables, vertex_slice, ts): The slice of variables being retrieved :param float ts: The time to be advanced in one call to the update of this component - :return: A list with the same length as self.struct.field_types + :return: A list with the same length as + `self.`\ """ + ":py:attr:`~spynnaker.pyNN.models.neuron.implementations." + r"AbstractStandardNeuronComponent.struct`\ " + """`.`\\ :py:attr:`~spynnaker.pyNN.utilities.struct.Struct.field_types` :rtype: list(int or float or list(int) or list(float) or ~spinn_utilities.ranged.RangedList) - """ + """) def get_data(self, parameters, state_variables, vertex_slice, ts): """ Get the data *to be written to the machine* for this model. diff --git a/spynnaker/pyNN/models/neuron/implementations/neuron_impl_standard.py b/spynnaker/pyNN/models/neuron/implementations/neuron_impl_standard.py index 43023543386..b48e26cee27 100644 --- a/spynnaker/pyNN/models/neuron/implementations/neuron_impl_standard.py +++ b/spynnaker/pyNN/models/neuron/implementations/neuron_impl_standard.py @@ -19,7 +19,7 @@ from spinn_utilities.overrides import overrides from spynnaker.pyNN.models.neuron.input_types import InputTypeConductance from .abstract_neuron_impl import AbstractNeuronImpl -from spinn_front_end_common.utilities import globals_variables +from spinn_front_end_common.utilities.globals_variables import get_simulator from spinn_front_end_common.utilities.constants import BYTES_PER_WORD # The size of the n_steps_per_timestep parameter @@ -187,8 +187,7 @@ def add_state_variables(self, state_variables): @overrides(AbstractNeuronImpl.get_data) def get_data(self, parameters, state_variables, vertex_slice): # Work out the time step per step - ts = globals_variables.get_simulator().machine_time_step - ts /= self.__n_steps_per_timestep + ts = get_simulator().machine_time_step / self.__n_steps_per_timestep items = [numpy.array([self.__n_steps_per_timestep], dtype="uint32")] items.extend( component.get_data(parameters, state_variables, vertex_slice, ts) diff --git a/spynnaker/pyNN/models/neuron/master_pop_table.py b/spynnaker/pyNN/models/neuron/master_pop_table.py index 078c04856c2..84ce2033a15 100644 --- a/spynnaker/pyNN/models/neuron/master_pop_table.py +++ b/spynnaker/pyNN/models/neuron/master_pop_table.py @@ -12,19 +12,16 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging -import math import struct import numpy -from spinn_front_end_common.utilities.constants import BYTES_PER_WORD, \ - SARK_PER_MALLOC_SDRAM_USAGE +from spinn_front_end_common.utilities.constants import ( + BYTES_PER_WORD, SARK_PER_MALLOC_SDRAM_USAGE) from spynnaker.pyNN.models.neural_projections import ( ProjectionApplicationEdge, ProjectionMachineEdge) from spynnaker.pyNN.exceptions import ( SynapseRowTooBigException, SynapticConfigurationException) +from spynnaker.pyNN.utilities.utility_calls import ceildiv -logger = logging.getLogger(__name__) _TWO_WORDS = struct.Struct(". -from spinn_front_end_common.utilities.constants import \ - MICRO_TO_MILLISECOND_CONVERSION from spinn_utilities.overrides import overrides from data_specification.enums import DataType +from spinn_front_end_common.utilities.constants import ( + MICRO_TO_MILLISECOND_CONVERSION) from .abstract_neuron_model import AbstractNeuronModel from spynnaker.pyNN.models.neuron.implementations import ( AbstractStandardNeuronComponent) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/common.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/common.py index c67456a6cda..62b44754fa7 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/common.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/common.py @@ -14,10 +14,8 @@ # along with this program. If not, see . import math -import logging import numpy -logger = logging.getLogger(__name__) # Default value of fixed-point one for STDP STDP_FIXED_POINT_ONE = (1 << 11) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/abstract_timing_dependence.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/abstract_timing_dependence.py index a11db4e3526..02946be2d5e 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/abstract_timing_dependence.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/abstract_timing_dependence.py @@ -89,7 +89,7 @@ def get_provenance_data(self, pre_population_label, post_population_label): :param str pre_population_label: label of pre. :param str post_population_label: label of post. - :rtype: \ + :rtype: list(~spinn_front_end_common.utilities.utility_objs.ProvenanceDataItem) """ # pylint: disable=unused-argument diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_pfister_spike_triplet.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_pfister_spike_triplet.py index 1052c4659a0..00aa32619fe 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_pfister_spike_triplet.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_pfister_spike_triplet.py @@ -13,19 +13,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import logging from spinn_utilities.overrides import overrides from spinn_front_end_common.utilities.constants import ( BYTES_PER_WORD, MICRO_TO_MILLISECOND_CONVERSION) +from spinn_front_end_common.utilities.globals_variables import get_simulator from spynnaker.pyNN.models.neuron.plasticity.stdp.common import ( get_exp_lut_array) -from spynnaker.pyNN.models.neuron.plasticity.stdp.timing_dependence\ - import AbstractTimingDependence -from spynnaker.pyNN.models.neuron.plasticity.stdp.synapse_structure\ - import SynapseStructureWeightOnly -from spinn_front_end_common.utilities.globals_variables import get_simulator - -logger = logging.getLogger(__name__) +from spynnaker.pyNN.models.neuron.plasticity.stdp.synapse_structure import ( + SynapseStructureWeightOnly) +from .abstract_timing_dependence import AbstractTimingDependence class TimingDependencePfisterSpikeTriplet(AbstractTimingDependence): @@ -112,19 +108,13 @@ def is_same_as(self, timing_dependence): (self.__tau_y == timing_dependence.tau_y)) @property + @overrides(AbstractTimingDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ return "pfister_triplet" @property + @overrides(AbstractTimingDependence.pre_trace_n_bytes) def pre_trace_n_bytes(self): - """ The number of bytes used by the pre-trace of the rule per neuron - - :rtype: int - """ # Triplet rule trace entries consists of two 16-bit traces - R1 and R2 return BYTES_PER_WORD @@ -136,16 +126,12 @@ def get_parameters_sdram_usage_in_bytes(self): return lut_array_words * BYTES_PER_WORD @property + @overrides(AbstractTimingDependence.n_weight_terms) def n_weight_terms(self): - """ The number of weight terms expected by this timing rule - - :rtype: int - """ return 2 @overrides(AbstractTimingDependence.write_parameters) def write_parameters(self, spec, machine_time_step, weight_scales): - # Write lookup tables spec.write_array(self.__tau_plus_data) spec.write_array(self.__tau_minus_data) @@ -153,11 +139,8 @@ def write_parameters(self, spec, machine_time_step, weight_scales): spec.write_array(self.__tau_y_data) @property + @overrides(AbstractTimingDependence.synaptic_structure) def synaptic_structure(self): - """ Get the synaptic structure of the plastic part of the rows - - :rtype: AbstractSynapseStructure - """ return self.__synapse_structure @overrides(AbstractTimingDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_recurrent.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_recurrent.py index 1e6d1e9277e..add3beb1820 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_recurrent.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_recurrent.py @@ -14,12 +14,10 @@ # along with this program. If not, see . import numpy -from spinn_front_end_common.utilities.constants import \ - MICRO_TO_MILLISECOND_CONVERSION from spinn_utilities.overrides import overrides from data_specification.enums import DataType from spinn_front_end_common.utilities.constants import ( - BYTES_PER_WORD, BYTES_PER_SHORT) + BYTES_PER_WORD, BYTES_PER_SHORT, MICRO_TO_MILLISECOND_CONVERSION) from .abstract_timing_dependence import AbstractTimingDependence from spynnaker.pyNN.models.neuron.plasticity.stdp.synapse_structure import ( SynapseStructureWeightAccumulator) @@ -50,6 +48,13 @@ def __init__( mean_pre_window=default_parameters['mean_pre_window'], mean_post_window=default_parameters['mean_post_window'], dual_fsm=default_parameters['dual_fsm']): + """ + :param int accumulator_depression: + :param int accumulator_potentiation: + :param float mean_pre_window: + :param float mean_post_window: + :param bool dual_fsm: + """ # pylint: disable=too-many-arguments self.__accumulator_depression_plus_one = accumulator_depression + 1 self.__accumulator_potentiation_minus_one = \ @@ -75,21 +80,15 @@ def is_same_as(self, timing_dependence): timing_dependence.mean_post_window)) @property + @overrides(AbstractTimingDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ if self.__dual_fsm: return "recurrent_dual_fsm" return "recurrent_pre_stochastic" @property + @overrides(AbstractTimingDependence.pre_trace_n_bytes) def pre_trace_n_bytes(self): - """ The number of bytes used by the pre-trace of the rule per neuron - - :rtype: int - """ # When using the separate FSMs, pre-trace contains window length, # otherwise it's in the synapse return BYTES_PER_SHORT if self.__dual_fsm else 0 @@ -102,16 +101,12 @@ def get_parameters_sdram_usage_in_bytes(self): 2 * STDP_FIXED_POINT_ONE * BYTES_PER_SHORT) @property + @overrides(AbstractTimingDependence.n_weight_terms) def n_weight_terms(self): - """ The number of weight terms expected by this timing rule - - :rtype: int - """ return 1 @overrides(AbstractTimingDependence.write_parameters) def write_parameters(self, spec, machine_time_step, weight_scales): - # Write parameters spec.write_value(data=self.__accumulator_depression_plus_one, data_type=DataType.INT32) @@ -142,11 +137,8 @@ def _write_exp_dist_lut(spec, mean): inv_cdf.astype(numpy.uint16), data_type=DataType.UINT16) @property + @overrides(AbstractTimingDependence.synaptic_structure) def synaptic_structure(self): - """ Get the synaptic structure of the plastic part of the rows - - :rtype: AbstractSynapseStructure - """ return self.__synapse_structure @overrides(AbstractTimingDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_nearest_pair.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_nearest_pair.py index f58d2a826d6..fd0f58afc19 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_nearest_pair.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_nearest_pair.py @@ -12,20 +12,15 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spinn_utilities.overrides import overrides from spinn_front_end_common.utilities.constants import ( BYTES_PER_WORD, MICRO_TO_MILLISECOND_CONVERSION) - +from spinn_front_end_common.utilities.globals_variables import get_simulator from spynnaker.pyNN.models.neuron.plasticity.stdp.common import ( get_exp_lut_array) from spynnaker.pyNN.models.neuron.plasticity.stdp.synapse_structure import ( SynapseStructureWeightOnly) from .abstract_timing_dependence import AbstractTimingDependence -from spinn_front_end_common.utilities.globals_variables import get_simulator - -logger = logging.getLogger(__name__) class TimingDependenceSpikeNearestPair(AbstractTimingDependence): @@ -81,19 +76,13 @@ def is_same_as(self, timing_dependence): self.__tau_minus == timing_dependence.tau_minus) @property + @overrides(AbstractTimingDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ return "nearest_pair" @property + @overrides(AbstractTimingDependence.pre_trace_n_bytes) def pre_trace_n_bytes(self): - """ The number of bytes used by the pre-trace of the rule per neuron - - :rtype: int - """ # Pair rule requires no pre-synaptic trace when only the nearest # Neighbours are considered and, a single 16-bit R1 trace return 0 @@ -104,11 +93,8 @@ def get_parameters_sdram_usage_in_bytes(self): len(self.__tau_minus_data)) @property + @overrides(AbstractTimingDependence.n_weight_terms) def n_weight_terms(self): - """ The number of weight terms expected by this timing rule - - :rtype: int - """ return 1 @overrides(AbstractTimingDependence.write_parameters) @@ -119,11 +105,8 @@ def write_parameters(self, spec, machine_time_step, weight_scales): spec.write_array(self.__tau_minus_data) @property + @overrides(AbstractTimingDependence.synaptic_structure) def synaptic_structure(self): - """ Get the synaptic structure of the plastic part of the rows - - :rtype: AbstractSynapseStructure - """ return self.__synapse_structure @overrides(AbstractTimingDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_pair.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_pair.py index 40cb9ee532b..b1671c20205 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_pair.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_spike_pair.py @@ -12,23 +12,20 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spinn_utilities.overrides import overrides from spinn_front_end_common.utilities.constants import ( BYTES_PER_SHORT, BYTES_PER_WORD, MICRO_TO_MILLISECOND_CONVERSION) +from spinn_front_end_common.utilities.globals_variables import get_simulator from spynnaker.pyNN.models.neuron.plasticity.stdp.common import ( get_exp_lut_array) from .abstract_timing_dependence import AbstractTimingDependence from spynnaker.pyNN.models.neuron.plasticity.stdp.synapse_structure import ( SynapseStructureWeightOnly) -from spinn_front_end_common.utilities.globals_variables import get_simulator - -logger = logging.getLogger(__name__) class TimingDependenceSpikePair(AbstractTimingDependence): - """ A basic timing dependence STDP rule. + """ A basic timing dependence STDP rule where relevance decays \ + exponentially. """ __slots__ = [ "__synapse_structure", @@ -77,19 +74,13 @@ def is_same_as(self, timing_dependence): self.__tau_minus == timing_dependence.tau_minus) @property + @overrides(AbstractTimingDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ return "pair" @property + @overrides(AbstractTimingDependence.pre_trace_n_bytes) def pre_trace_n_bytes(self): - """ The number of bytes used by the pre-trace of the rule per neuron - - :rtype: int - """ # Pair rule requires no pre-synaptic trace when only the nearest # Neighbours are considered and, a single 16-bit R1 trace return BYTES_PER_SHORT @@ -100,26 +91,19 @@ def get_parameters_sdram_usage_in_bytes(self): len(self.__tau_minus_data)) @property + @overrides(AbstractTimingDependence.n_weight_terms) def n_weight_terms(self): - """ The number of weight terms expected by this timing rule - - :rtype: int - """ return 1 @overrides(AbstractTimingDependence.write_parameters) def write_parameters(self, spec, machine_time_step, weight_scales): - # Write lookup tables spec.write_array(self.__tau_plus_data) spec.write_array(self.__tau_minus_data) @property + @overrides(AbstractTimingDependence.synaptic_structure) def synaptic_structure(self): - """ Get the synaptic structure of the plastic part of the rows - - :rtype: AbstractSynapseStructure - """ return self.__synapse_structure @overrides(AbstractTimingDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_vogels_2011.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_vogels_2011.py index c75f8672b0e..a5d7efb0e81 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_vogels_2011.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/timing_dependence/timing_dependence_vogels_2011.py @@ -12,21 +12,16 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import logging from spinn_utilities.overrides import overrides from data_specification.enums import DataType from spinn_front_end_common.utilities.constants import ( BYTES_PER_WORD, BYTES_PER_SHORT, MICRO_TO_MILLISECOND_CONVERSION) -from spynnaker.pyNN.models.neuron.plasticity.stdp.timing_dependence import ( - AbstractTimingDependence) +from spinn_front_end_common.utilities.globals_variables import get_simulator from spynnaker.pyNN.models.neuron.plasticity.stdp.synapse_structure import ( SynapseStructureWeightOnly) -from spinn_front_end_common.utilities.globals_variables import get_simulator from spynnaker.pyNN.models.neuron.plasticity.stdp.common import ( float_to_fixed, get_exp_lut_array) - -logger = logging.getLogger(__name__) +from .abstract_timing_dependence import AbstractTimingDependence class TimingDependenceVogels2011(AbstractTimingDependence): @@ -80,19 +75,13 @@ def is_same_as(self, timing_dependence): self.__alpha == timing_dependence.alpha) @property + @overrides(AbstractTimingDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ return "vogels_2011" @property + @overrides(AbstractTimingDependence.pre_trace_n_bytes) def pre_trace_n_bytes(self): - """ The number of bytes used by the pre-trace of the rule per neuron - - :rtype: int - """ # Trace entries consist of a single 16-bit number return BYTES_PER_SHORT @@ -101,16 +90,12 @@ def get_parameters_sdram_usage_in_bytes(self): return BYTES_PER_WORD + BYTES_PER_WORD * len(self.__tau_data) @property + @overrides(AbstractTimingDependence.n_weight_terms) def n_weight_terms(self): - """ The number of weight terms expected by this timing rule - - :rtype: int - """ return 1 @overrides(AbstractTimingDependence.write_parameters) def write_parameters(self, spec, machine_time_step, weight_scales): - # Write alpha to spec fixed_point_alpha = float_to_fixed(self.__alpha) spec.write_value(data=fixed_point_alpha, data_type=DataType.INT32) @@ -119,11 +104,8 @@ def write_parameters(self, spec, machine_time_step, weight_scales): spec.write_array(self.__tau_data) @property + @overrides(AbstractTimingDependence.synaptic_structure) def synaptic_structure(self): - """ Get the synaptic structure of the plastic part of the rows - - :rtype: AbstractSynapseStructure - """ return self.__synapse_structure @overrides(AbstractTimingDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/abstract_weight_dependence.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/abstract_weight_dependence.py index 109ae9ce866..ec47c17ccc8 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/abstract_weight_dependence.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/abstract_weight_dependence.py @@ -28,7 +28,7 @@ def get_provenance_data(self, pre_population_label, post_population_label): :param str pre_population_label: label of pre. :param str post_population_label: label of post. :return: the provenance data of the weight dependency - :rtype: \ + :rtype: list(~spinn_front_end_common.utilities.utility_objs.ProvenanceDataItem) """ # pylint: disable=unused-argument diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive.py index 24099adaf4c..08cf9ea586a 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive.py @@ -68,11 +68,8 @@ def is_same_as(self, weight_dependence): (self.A_minus == weight_dependence.A_minus)) @property + @overrides(AbstractWeightDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ return "additive" @overrides(AbstractWeightDependence.get_parameters_sdram_usage_in_bytes) @@ -106,12 +103,8 @@ def write_parameters( data_type=DataType.INT32) @property + @overrides(AbstractWeightDependence.weight_maximum) def weight_maximum(self): - """ The maximum weight that will ever be set in a synapse as a result\ - of this rule - - :rtype: float - """ return self.__w_max @overrides(AbstractWeightDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive_triplet.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive_triplet.py index e2a112411b7..23322ffca7d 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive_triplet.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_additive_triplet.py @@ -98,11 +98,8 @@ def is_same_as(self, weight_dependence): (self.__a3_minus == weight_dependence.A3_minus)) @property + @overrides(AbstractWeightDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ return "additive" @overrides(AbstractWeightDependence.get_parameters_sdram_usage_in_bytes) @@ -110,16 +107,14 @@ def get_parameters_sdram_usage_in_bytes( self, n_synapse_types, n_weight_terms): if n_weight_terms != 2: raise NotImplementedError( - "Additive weight dependence only supports one or two terms") + "Additive weight dependence only supports two terms") return _SPACE_PER_SYNAPSE_TYPE * n_synapse_types @overrides(AbstractWeightDependence.write_parameters) def write_parameters( self, spec, machine_time_step, weight_scales, n_weight_terms): - # Loop through each synapse type's weight scale for w in weight_scales: - # Scale the weights spec.write_value( data=int(round(self.__w_min * w)), data_type=DataType.INT32) @@ -143,12 +138,8 @@ def write_parameters( data_type=DataType.INT32) @property + @overrides(AbstractWeightDependence.weight_maximum) def weight_maximum(self): - """ The maximum weight that will ever be set in a synapse as a result\ - of this rule - - :rtype: float - """ return self.__w_max @overrides(AbstractWeightDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_multiplicative.py b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_multiplicative.py index 5b5ccce85e2..3aee0581c23 100644 --- a/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_multiplicative.py +++ b/spynnaker/pyNN/models/neuron/plasticity/stdp/weight_dependence/weight_dependence_multiplicative.py @@ -67,11 +67,8 @@ def is_same_as(self, weight_dependence): (self.A_minus == weight_dependence.A_minus)) @property + @overrides(AbstractWeightDependence.vertex_executable_suffix) def vertex_executable_suffix(self): - """ The suffix to be appended to the vertex executable for this rule - - :rtype: str - """ return "multiplicative" @overrides(AbstractWeightDependence.get_parameters_sdram_usage_in_bytes) @@ -103,12 +100,8 @@ def write_parameters( data=int(round(self.A_minus * w)), data_type=DataType.INT32) @property + @overrides(AbstractWeightDependence.weight_maximum) def weight_maximum(self): - """ The maximum weight that will ever be set in a synapse as a result\ - of this rule - - :rtype: float - """ return self.__w_max @overrides(AbstractWeightDependence.get_parameter_names) diff --git a/spynnaker/pyNN/models/neuron/population_machine_vertex.py b/spynnaker/pyNN/models/neuron/population_machine_vertex.py index 30f6f79aab7..89d60a4e07a 100644 --- a/spynnaker/pyNN/models/neuron/population_machine_vertex.py +++ b/spynnaker/pyNN/models/neuron/population_machine_vertex.py @@ -37,6 +37,8 @@ class PopulationMachineVertex( ProvidesProvenanceDataFromMachineImpl, AbstractRecordable, AbstractHasProfileData, AbstractSupportsBitFieldGeneration, AbstractSupportsBitFieldRoutingCompression): + """ A machine vertex that implements a neural population. + """ __slots__ = [ "__recorded_region_ids", diff --git a/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/formation/distance_dependent_formation.py b/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/formation/distance_dependent_formation.py index d8460cff3ea..7eb543f7c0f 100644 --- a/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/formation/distance_dependent_formation.py +++ b/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/formation/distance_dependent_formation.py @@ -15,9 +15,9 @@ import numpy from spinn_utilities.overrides import overrides -from .abstract_formation import AbstractFormation from data_specification.enums.data_type import DataType from spinn_front_end_common.utilities.constants import BYTES_PER_WORD +from .abstract_formation import AbstractFormation # 6 32-bit words (grid_x, grid_y, grid_x_recip, grid_y_recep, ff_prob_size, # lat_prob_size) @@ -48,13 +48,13 @@ def __init__( :param float p_form_forward: The peak probability of formation on feed-forward connections :param float sigma_form_forward: - The spread of probability with distance of formation on\ + The spread of probability with distance of formation on feed-forward connections :param float p_form_lateral: The peak probability of formation on lateral connections :param float sigma_form_lateral: - The spread of probability with distance of formation on\ - lateral connections + The spread of probability with distance of formation on lateral + connections """ self.__grid = numpy.asarray(grid, dtype=int) self.__p_form_forward = p_form_forward @@ -63,10 +63,10 @@ def __init__( self.__sigma_form_lateral = sigma_form_lateral self.__ff_distance_probabilities = \ - self.generate_distance_probability_array( + self._generate_distance_probability_array( self.__p_form_forward, self.__sigma_form_forward) self.__lat_distance_probabilities = \ - self.generate_distance_probability_array( + self._generate_distance_probability_array( self.__p_form_lateral, self.__sigma_form_lateral) @property @@ -80,7 +80,7 @@ def get_parameters_sdram_usage_in_bytes(self): len(self.__ff_distance_probabilities) * 2 + len(self.__lat_distance_probabilities) * 2) - def generate_distance_probability_array(self, probability, sigma): + def _generate_distance_probability_array(self, probability, sigma): """ Generate the exponentially decaying probability LUTs. :param float probability: peak probability diff --git a/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/partner_selection/last_neuron_selection.py b/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/partner_selection/last_neuron_selection.py index e2b03a3b39b..15864a00b1a 100644 --- a/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/partner_selection/last_neuron_selection.py +++ b/spynnaker/pyNN/models/neuron/structural_plasticity/synaptogenesis/partner_selection/last_neuron_selection.py @@ -26,7 +26,7 @@ class LastNeuronSelection(AbstractPartnerSelection): def __init__(self, spike_buffer_size=64): """ - :param spike_buffer_size: The size of the buffer for holding spikes + :param int spike_buffer_size: The size of the buffer for holding spikes """ self.__spike_buffer_size = spike_buffer_size diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/__init__.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/__init__.py index 3ea25b6de04..81437512c6d 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/__init__.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/__init__.py @@ -22,7 +22,8 @@ from .pynn_synapse_dynamics import PyNNSynapseDynamics from .synapse_dynamics_static import SynapseDynamicsStatic from .synapse_dynamics_stdp import SynapseDynamicsSTDP -from .synapse_dynamics_structural_common import SynapseDynamicsStructuralCommon +from .synapse_dynamics_structural_common import ( + StructuralPlasticityCommon as SynapseDynamicsStructuralCommon) from .synapse_dynamics_structural_static import SynapseDynamicsStructuralStatic from .synapse_dynamics_structural_stdp import SynapseDynamicsStructuralSTDP diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_plastic_synapse_dynamics.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_plastic_synapse_dynamics.py index 40be1c50f7b..b87aaf58a1f 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_plastic_synapse_dynamics.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_plastic_synapse_dynamics.py @@ -42,13 +42,13 @@ def get_plastic_synaptic_data( and lengths for the fixed_plastic and plastic-plastic parts of\ each row. - Data is returned as an array made up of an array of 32-bit words for\ - each row, for each of the fixed-plastic and plastic-plastic data\ - regions. The row into which connection should go is given by\ - `connection_row_indices`, and the total number of rows is given by\ + Data is returned as an array made up of an array of 32-bit words for + each row, for each of the fixed-plastic and plastic-plastic data + regions. The row into which connection should go is given by + `connection_row_indices`, and the total number of rows is given by `n_rows`. - Lengths are returned as an array made up of an integer for each row,\ + Lengths are returned as an array made up of an integer for each row, for each of the fixed-plastic and plastic-plastic regions. :param ~numpy.ndarray connections: diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_static_synapse_dynamics.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_static_synapse_dynamics.py index 3edd43b9210..b6e8233df15 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_static_synapse_dynamics.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_static_synapse_dynamics.py @@ -41,12 +41,12 @@ def get_static_synaptic_data( """ Get the fixed-fixed data for each row, and lengths for the\ fixed-fixed parts of each row. - Data is returned as an array made up of an array of 32-bit words for\ - each row for the fixed-fixed region. The row into which connection\ - should go is given by `connection_row_indices`, and the total number\ + Data is returned as an array made up of an array of 32-bit words for + each row for the fixed-fixed region. The row into which connection + should go is given by `connection_row_indices`, and the total number of rows is given by `n_rows`. - Lengths are returned as an array made up of an integer for each row,\ + Lengths are returned as an array made up of an integer for each row, for the fixed-fixed region. :param ~numpy.ndarray connections: diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics.py index ca31ddaf4bc..4bd162baa2f 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics.py @@ -13,11 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import math import numpy from six import add_metaclass from spinn_utilities.abstract_base import ( AbstractBase, abstractmethod, abstractproperty) +from spynnaker.pyNN.utilities.utility_calls import ceildiv @add_metaclass(AbstractBase) @@ -196,7 +196,7 @@ def get_n_items(self, rows, item_size): :rtype: ~numpy.ndarray """ return numpy.array([ - int(math.ceil(float(row.size) / float(item_size))) + ceildiv(row.size, item_size) for row in rows], dtype="uint32").reshape((-1, 1)) def get_words(self, rows): diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics_structural.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics_structural.py index 624e4229d39..abfa0cf52f4 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics_structural.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/abstract_synapse_dynamics_structural.py @@ -86,6 +86,8 @@ def s_max(self): @abstractproperty def seed(self): """ The seed to control the randomness + + :rtype: int """ @abstractproperty @@ -122,3 +124,16 @@ def elimination(self): :rtype: AbstractElimination """ + + def get_per_edge_parameters_sdram_usage_in_bytes(self): + """ Get the amount of SDRAM used by the parameters of the rules per \ + structurally-plastic synapse. + + .. note:: + The partner selection rule is shared. + + :rtype: int + """ + # pylint: disable=no-member + return (self.formation.get_parameters_sdram_usage_in_bytes() + + self.elimination.get_parameters_sdram_usage_in_bytes()) diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py index 6975734fc3b..7028b34c44c 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py @@ -179,7 +179,8 @@ def requires_mapping(self): @overrides(AbstractChangableAfterRun.mark_no_changes, extend_doc=False) def mark_no_changes(self): """ Marks the point after which changes are reported. Immediately\ - after calling this method, requires_mapping should return False. + after calling this method, :py:meth:`requires_mapping` should \ + return False. """ self.__change_requires_mapping = False diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_stdp.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_stdp.py index 310f096d082..895c6590a10 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_stdp.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_stdp.py @@ -12,8 +12,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import math import numpy from spinn_utilities.overrides import overrides from spinn_front_end_common.abstract_models import AbstractChangableAfterRun @@ -21,16 +19,17 @@ BYTES_PER_WORD, BYTES_PER_SHORT) from spynnaker.pyNN.models.abstract_models import AbstractSettable from .abstract_plastic_synapse_dynamics import AbstractPlasticSynapseDynamics -from .abstract_synapse_dynamics_structural \ - import AbstractSynapseDynamicsStructural +from .abstract_synapse_dynamics_structural import ( + AbstractSynapseDynamicsStructural) from .abstract_generate_on_machine import ( AbstractGenerateOnMachine, MatrixGeneratorID) -from spynnaker.pyNN.exceptions import InvalidParameterType,\ - SynapticConfigurationException -from spynnaker.pyNN.utilities.utility_calls import get_n_bits +from spynnaker.pyNN.exceptions import ( + InvalidParameterType, SynapticConfigurationException) +from spynnaker.pyNN.utilities.utility_calls import get_n_bits, ceildiv # How large are the time-stamps stored with each event TIME_STAMP_BYTES = BYTES_PER_WORD +_HALF_WORDS_PER_WORD = 2 class SynapseDynamicsSTDP( @@ -281,30 +280,25 @@ def _n_header_bytes(self): # The actual number of bytes is in a word-aligned struct, so work out # the number of bytes as a number of words - return int(math.ceil(float(n_bytes) / BYTES_PER_WORD)) * BYTES_PER_WORD + return ceildiv(n_bytes, BYTES_PER_WORD) * BYTES_PER_WORD def __get_n_connections(self, n_connections, check_length_padded=True): """ :param int n_connections: - :rtype: int :param bool check_length_padded: - :rtype: bool + :rtype: int """ synapse_structure = self.__timing_dependence.synaptic_structure if self.__pad_to_length is not None and check_length_padded: n_connections = max(n_connections, self.__pad_to_length) if n_connections == 0: return 0 - # 2 == two half words per word - fp_size_words = ( - n_connections // 2 if n_connections % 2 == 0 - else (n_connections + 1) // 2) - pp_size_bytes = ( - self._n_header_bytes + - (synapse_structure.get_n_half_words_per_connection() * - BYTES_PER_SHORT * n_connections)) - pp_size_words = int(math.ceil(float(pp_size_bytes) / BYTES_PER_WORD)) + fp_size_words = ceildiv(n_connections, _HALF_WORDS_PER_WORD) + pp_size_bytes = self._n_header_bytes + ( + synapse_structure.get_n_half_words_per_connection() * + BYTES_PER_SHORT * n_connections) + pp_size_words = ceildiv(pp_size_bytes, BYTES_PER_WORD) return fp_size_words + pp_size_words def get_n_words_for_plastic_connections(self, n_connections): @@ -476,7 +470,7 @@ def get_provenance_data(self, pre_population_label, post_population_label): """ :param str pre_population_label: :param str post_population_label: - :rtype: \ + :rtype: list(~spinn_front_end_common.utilities.utility_objs.ProvenanceDataItem) """ prov_data = list() diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_common.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_common.py index 5ed01456971..77b8f5c1095 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_common.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_common.py @@ -12,21 +12,18 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - import collections -import math import numpy from data_specification.enums.data_type import DataType from spinn_front_end_common.utilities.constants import ( MICRO_TO_MILLISECOND_CONVERSION, MICRO_TO_SECOND_CONVERSION, BYTES_PER_WORD, BYTES_PER_SHORT) from spynnaker.pyNN.models.neural_projections import ProjectionApplicationEdge -from .abstract_synapse_dynamics_structural import ( - AbstractSynapseDynamicsStructural) from spynnaker.pyNN.exceptions import SynapticConfigurationException +from spynnaker.pyNN.utilities.utility_calls import ceildiv -class SynapseDynamicsStructuralCommon(object): +class StructuralPlasticityCommon(object): """ Utility class that holds properties of synaptic rewiring\ both in the presence and absence of STDP. @@ -106,7 +103,7 @@ def __init__( :param float initial_weight: Initial weight assigned to a newly formed connection :param initial_delay: Delay assigned to a newly formed connection - :type initial_delay: int or (int, int) + :type initial_delay: int or float or tuple(int or float, int or float) :param int s_max: Maximum fan-in per target layer neuron :param int seed: seed the random number generators """ @@ -157,7 +154,7 @@ def p_rew(self): """ The period of rewiring. :return: The period of rewiring - :rtype: int + :rtype: float """ return self.__p_rew @@ -225,17 +222,18 @@ def write_parameters( def __get_structural_edges(self, app_graph, app_vertex): """ - :param ~pacman.model.graphs.application.ApplicationGraph app_graph: - :param ~pacman.model.graphs.application.ApplicationVertex app_vertex: + :param ~.ApplicationGraph app_graph: + :param ~.ApplicationVertex app_vertex: :rtype: list(tuple(ProjectionApplicationEdge, SynapseInformation)) + :raises SynapticConfigurationException: + if there are two structurally-plastic projections on an edge """ structural_edges = list() for app_edge in app_graph.get_edges_ending_at_vertex(app_vertex): if isinstance(app_edge, ProjectionApplicationEdge): found = False for synapse_info in app_edge.synapse_information: - if isinstance(synapse_info.synapse_dynamics, - AbstractSynapseDynamicsStructural): + if synapse_info.is_structural: if found: raise SynapticConfigurationException( "Only one Projection between each pair of " @@ -248,16 +246,13 @@ def __write_common_rewiring_data( self, spec, app_vertex, post_slice, machine_time_step, n_pre_pops): """ Write the non-sub-population synapse parameters to the spec. - :param ~data_specification.DataSpecificationGenerator spec: - the data spec + :param ~.DataSpecificationGenerator spec: the data spec :param AbstractPopulationVertex app_vertex: the highest level object of the post-synaptic population :param ~pacman.model.graphs.common.Slice post_slice: the slice of the app vertex corresponding to this machine vertex :param int machine_time_step: the duration of a machine time step (ms) :param int n_pre_pops: the number of pre-populations - :return: None - :rtype: None """ if (self.__p_rew * MICRO_TO_MILLISECOND_CONVERSION < machine_time_step / MICRO_TO_MILLISECOND_CONVERSION): @@ -305,10 +300,11 @@ def __write_prepopulation_info( routing_info, weight_scales, post_slice, synapse_indices, machine_time_step): """ - :param ~data_specification.DataSpecificationGenerator spec: + :param ~.DataSpecificationGenerator spec: :param AbstractPopulationVertex app_vertex: - :param list(tuple(ProjectionApplicationEdge,SynapseInformation)) \ - structural_edges: + :param structural_edges: + :type structural_edges: + list(tuple(ProjectionApplicationEdge,SynapseInformation)) :param RoutingInfo routing_info: :param dict(AbstractSynapseType,float) weight_scales: :param ~pacman.model.graphs.common.Slice post_slice: @@ -367,9 +363,10 @@ def __write_post_to_pre_table( """ Post to pre table is basically the transpose of the synaptic\ matrix. - :param ~data_specification.DataSpecificationGenerator spec: - :param dict(tuple(AbstractPopulationVertex,SynapseInformation),int) \ - pop_index: + :param ~.DataSpecificationGenerator spec: + :param pop_index: + :type pop_index: + dict(tuple(AbstractPopulationVertex,SynapseInformation),int) :param AbstractPopulationVertex app_vertex: :param ~pacman.model.graphs.common.Slice post_slice: """ @@ -438,22 +435,18 @@ def get_parameters_sdram_usage_in_bytes( param_sizes = self.__partner_selection\ .get_parameters_sdram_usage_in_bytes() for (in_edge, synapse_info) in structural_edges: - max_atoms = in_edge.pre_vertex.get_max_atoms_per_core() - if in_edge.pre_vertex.n_atoms < max_atoms: - max_atoms = in_edge.pre_vertex.n_atoms - n_sub_edges += int(math.ceil( - float(in_edge.pre_vertex.n_atoms) / float(max_atoms))) - dynamics = synapse_info.synapse_dynamics - param_sizes += dynamics.formation\ - .get_parameters_sdram_usage_in_bytes() - param_sizes += dynamics.elimination\ - .get_parameters_sdram_usage_in_bytes() - - return int((self._REWIRING_DATA_SIZE + - (self._PRE_POP_INFO_BASE_SIZE * len(structural_edges)) + - (self._KEY_ATOM_INFO_SIZE * n_sub_edges) + - (self._POST_TO_PRE_ENTRY_SIZE * n_neurons * self.__s_max) + - param_sizes)) + n_sub_edges += ceildiv(in_edge.pre_vertex.n_atoms, min( + in_edge.pre_vertex.get_max_atoms_per_core(), + in_edge.pre_vertex.n_atoms)) + param_sizes += synapse_info.synapse_dynamics.\ + get_per_edge_parameters_sdram_usage_in_bytes() + + return int( + self._REWIRING_DATA_SIZE + + (self._PRE_POP_INFO_BASE_SIZE * len(structural_edges)) + + (self._KEY_ATOM_INFO_SIZE * n_sub_edges) + + (self._POST_TO_PRE_ENTRY_SIZE * n_neurons * self.__s_max) + + param_sizes) def synaptic_data_update( self, connections, post_vertex_slice, app_edge, synapse_info, @@ -466,8 +459,7 @@ def synaptic_data_update( :param SynapseInformation synapse_info: :param ProjectionMachineEdge machine_edge: """ - if not isinstance(synapse_info.synapse_dynamics, - AbstractSynapseDynamicsStructural): + if not synapse_info.is_structural: return collector = self.__connections.setdefault( (app_edge.post_vertex, post_vertex_slice.lo_atom), []) @@ -500,7 +492,7 @@ def get_vertex_executable_suffix(self): def is_same_as(self, synapse_dynamics): """ - :param SynapseDynamicsStructuralCommon synapse_dynamics: + :param StructuralPlasticityCommon synapse_dynamics: :rtype: bool """ # Note noqa because exact type comparison is required here @@ -518,41 +510,64 @@ def is_same_as(self, synapse_dynamics): @property def initial_weight(self): + """ Initial weight assigned to a newly formed connection + + :rtype: float + """ return self.__initial_weight @property def initial_delay(self): + """ Delay assigned to a newly formed connection + + :rtype: int or float or tuple(int or float, int or float) + """ return self.__initial_delay @property def f_rew(self): + """ Frequency of rewiring (Hz) + + :rtype: float + """ return self.__f_rew @property def s_max(self): + """ Maximum fan-in per target layer neuron + + :rtype: int + """ return self.__s_max @property def seed(self): + """ seed for the random number generators + + :rtype: int + """ return self.__seed @property def partner_selection(self): - """ + """ The partner selection rule + :rtype: AbstractPartnerSelection """ return self.__partner_selection @property def formation(self): - """ + """ The formation rule + :rtype: AbstractFormation """ return self.__formation @property def elimination(self): - """ + """ The elimination rule + :rtype: AbstractElimination """ return self.__elimination diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_static.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_static.py index a15b60fc2ad..b4311afc5af 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_static.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_static.py @@ -16,8 +16,7 @@ from spinn_utilities.overrides import overrides from .abstract_synapse_dynamics_structural import ( AbstractSynapseDynamicsStructural) -from .synapse_dynamics_structural_common import ( - SynapseDynamicsStructuralCommon as CommonSP) +from .synapse_dynamics_structural_common import StructuralPlasticityCommon from .synapse_dynamics_static import SynapseDynamicsStatic from .synapse_dynamics_stdp import SynapseDynamicsSTDP from .synapse_dynamics_structural_stdp import SynapseDynamicsStructuralSTDP @@ -28,8 +27,8 @@ class SynapseDynamicsStructuralStatic( SynapseDynamicsStatic, AbstractSynapseDynamicsStructural): """ Class that enables synaptic rewiring in the absence of STDP. - It acts as a wrapper around SynapseDynamicsStatic, meaning that \ - rewiring can operate in parallel with static synapses. + It acts as a wrapper around :py:class:`SynapseDynamicsStatic`, meaning + that rewiring can operate in parallel with static synapses. Written by Petrut Bogdan. """ @@ -37,10 +36,10 @@ class SynapseDynamicsStructuralStatic( def __init__( self, partner_selection, formation, elimination, - f_rew=CommonSP.DEFAULT_F_REW, - initial_weight=CommonSP.DEFAULT_INITIAL_WEIGHT, - initial_delay=CommonSP.DEFAULT_INITIAL_DELAY, - s_max=CommonSP.DEFAULT_S_MAX, seed=None, + f_rew=StructuralPlasticityCommon.DEFAULT_F_REW, + initial_weight=StructuralPlasticityCommon.DEFAULT_INITIAL_WEIGHT, + initial_delay=StructuralPlasticityCommon.DEFAULT_INITIAL_DELAY, + s_max=StructuralPlasticityCommon.DEFAULT_S_MAX, seed=None, weight=0.0, delay=1.0): """ :param AbstractPartnerSelection partner_selection: @@ -55,7 +54,7 @@ def __init__( a fixed delay value, or a tuple of two values means the delay will be chosen at random from a uniform distribution between the given values - :type initial_delay: float or (float, float) + :type initial_delay: int or float or tuple(int or float, int or float) :param int s_max: Maximum fan-in per target layer neuron :param int seed: seed the random number generators :param float weight: The weight of connections formed by the connector @@ -64,7 +63,7 @@ def __init__( super(SynapseDynamicsStructuralStatic, self).__init__( weight=weight, delay=delay, pad_to_length=s_max) - self.__common_sp = CommonSP( + self.__common_sp = StructuralPlasticityCommon( partner_selection, formation, elimination, f_rew, initial_weight, initial_delay, s_max, seed) diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_stdp.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_stdp.py index e0e65182360..d0739801d5b 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_stdp.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_structural_stdp.py @@ -17,9 +17,7 @@ from .synapse_dynamics_stdp import SynapseDynamicsSTDP from .abstract_synapse_dynamics_structural import ( AbstractSynapseDynamicsStructural) -from .synapse_dynamics_structural_common import ( - SynapseDynamicsStructuralCommon as - CommonSP) +from .synapse_dynamics_structural_common import StructuralPlasticityCommon from spynnaker.pyNN.exceptions import SynapticConfigurationException @@ -27,10 +25,10 @@ class SynapseDynamicsStructuralSTDP( SynapseDynamicsSTDP, AbstractSynapseDynamicsStructural): """ Class that enables synaptic rewiring in the presence of STDP. - It acts as a wrapper around SynapseDynamicsSTDP, meaning rewiring can\ - operate in parallel with STDP synapses. + It acts as a wrapper around :py:class:`SynapseDynamicsSTDP`, meaning + rewiring can operate in parallel with STDP synapses. - Written by Petrut Bogdan. + :codeauthor: Petrut Bogdan """ __slots__ = ["__common_sp"] @@ -38,10 +36,10 @@ def __init__( self, partner_selection, formation, elimination, timing_dependence=None, weight_dependence=None, voltage_dependence=None, dendritic_delay_fraction=1.0, - f_rew=CommonSP.DEFAULT_F_REW, - initial_weight=CommonSP.DEFAULT_INITIAL_WEIGHT, - initial_delay=CommonSP.DEFAULT_INITIAL_DELAY, - s_max=CommonSP.DEFAULT_S_MAX, seed=None, + f_rew=StructuralPlasticityCommon.DEFAULT_F_REW, + initial_weight=StructuralPlasticityCommon.DEFAULT_INITIAL_WEIGHT, + initial_delay=StructuralPlasticityCommon.DEFAULT_INITIAL_DELAY, + s_max=StructuralPlasticityCommon.DEFAULT_S_MAX, seed=None, weight=0.0, delay=1.0, backprop_delay=True): """ :param AbstractPartnerSelection partner_selection: @@ -63,7 +61,7 @@ def __init__( a fixed delay value, or a tuple of two values means the delay will be chosen at random from a uniform distribution between the given values - :type initial_delay: float or tuple(float, float) + :type initial_delay: int or float or tuple(int or float, int or float) :param int s_max: Maximum fan-in per target layer neuron :param int seed: seed the random number generators :param float weight: The weight of connections formed by the connector @@ -73,7 +71,7 @@ def __init__( timing_dependence, weight_dependence, voltage_dependence, dendritic_delay_fraction, weight, delay, pad_to_length=s_max, backprop_delay=backprop_delay) - self.__common_sp = CommonSP( + self.__common_sp = StructuralPlasticityCommon( partner_selection, formation, elimination, f_rew, initial_weight, initial_delay, s_max, seed) diff --git a/spynnaker/pyNN/models/neuron/synapse_io.py b/spynnaker/pyNN/models/neuron/synapse_io.py index acef9f55c7c..5557bcc640e 100644 --- a/spynnaker/pyNN/models/neuron/synapse_io.py +++ b/spynnaker/pyNN/models/neuron/synapse_io.py @@ -13,12 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import math import numpy from six import raise_from - -from spinn_front_end_common.utilities.constants import \ - MICRO_TO_MILLISECOND_CONVERSION, BYTES_PER_WORD +from spinn_front_end_common.utilities.constants import ( + MICRO_TO_MILLISECOND_CONVERSION, BYTES_PER_WORD) from spynnaker.pyNN.models.neural_projections.connectors import ( AbstractConnector) from spynnaker.pyNN.utilities.constants import MAX_SUPPORTED_DELAY_TICS @@ -26,6 +24,7 @@ from spynnaker.pyNN.models.neuron.synapse_dynamics import ( AbstractStaticSynapseDynamics, AbstractSynapseDynamicsStructural, AbstractSynapseDynamics) +from spynnaker.pyNN.utilities.utility_calls import ceildiv _N_HEADER_WORDS = 3 # There are 16 slots, one per time step @@ -133,7 +132,7 @@ def _n_words(n_bytes): :param int n_bytes: :rtype: int """ - return math.ceil(float(n_bytes) / BYTES_PER_WORD) + return ceildiv(n_bytes, BYTES_PER_WORD) @staticmethod def _get_max_row_length( @@ -324,7 +323,7 @@ def get_synapses( :return: (row_data, max_row_length, delayed_row_data, max_delayed_row_length, delayed_source_ids, stages) :rtype: - tuple(~numpy.ndarray, int, ~numpy.ndarray, int, ~numpy.ndarray,\ + tuple(~numpy.ndarray, int, ~numpy.ndarray, int, ~numpy.ndarray, ~numpy.ndarray) """ # pylint: disable=too-many-arguments, too-many-locals @@ -501,6 +500,11 @@ def __convert_delayed_data( self, n_synapses, pre_vertex_slice, delayed_connections): """ Take the delayed_connections and convert the source ids and delay\ values + + :param int n_synapses: + :param ~pacman.model.graphs.common.Slice pre_vertex_slice: + :param ~numpy.ndarray delayed_connections: + :rtype: ~numpy.ndarray """ synapse_ids = range(len(n_synapses)) row_stage = numpy.array([ diff --git a/spynnaker/pyNN/models/neuron/synapse_types/synapse_type_semd.py b/spynnaker/pyNN/models/neuron/synapse_types/synapse_type_semd.py index 3e7a73cb27e..c3cd1f94b27 100644 --- a/spynnaker/pyNN/models/neuron/synapse_types/synapse_type_semd.py +++ b/spynnaker/pyNN/models/neuron/synapse_types/synapse_type_semd.py @@ -42,6 +42,8 @@ class SynapseTypeSEMD(AbstractSynapseType): + """ Synaptic type for a spiking elementary motion detector. + """ __slots__ = [ "__tau_syn_E", "__tau_syn_E2", @@ -141,7 +143,6 @@ def init(x): @overrides(AbstractSynapseType.update_values) def update_values(self, values, parameters, state_variables): - # Read the data (_decay_E, _init_E, isyn_exc, _decay_E2, _init_E2, isyn_exc2, _decay_I, _init_I, isyn_inh, _multiplicator, exc2_old) = values diff --git a/spynnaker/pyNN/models/neuron/synaptic_manager.py b/spynnaker/pyNN/models/neuron/synaptic_manager.py index f03a3774f0d..4cf4264317c 100644 --- a/spynnaker/pyNN/models/neuron/synaptic_manager.py +++ b/spynnaker/pyNN/models/neuron/synaptic_manager.py @@ -30,8 +30,7 @@ AbstractGenerateConnectorOnMachine) from spynnaker.pyNN.models.neural_projections import ProjectionApplicationEdge from .synapse_dynamics import ( - AbstractSynapseDynamicsStructural, - AbstractGenerateOnMachine, SynapseDynamicsStructuralSTDP) + AbstractSynapseDynamicsStructural, AbstractGenerateOnMachine) from spynnaker.pyNN.models.neuron.synapse_io import SynapseIORowBased from spynnaker.pyNN.models.spike_source.spike_source_poisson_vertex import ( SpikeSourcePoissonVertex) @@ -39,7 +38,7 @@ from spynnaker.pyNN.utilities.constants import ( POPULATION_BASED_REGIONS, POSSION_SIGMA_SUMMATION_LIMIT) from spynnaker.pyNN.utilities.utility_calls import ( - get_maximum_probable_value, get_n_bits) + get_maximum_probable_value, get_n_bits, ceildiv, round_up) from spynnaker.pyNN.utilities.running_stats import RunningStats from spynnaker.pyNN.models.neuron.master_pop_table import ( MasterPopTableAsBinarySearch) @@ -102,7 +101,8 @@ class SynapticManager(object): "_direct_matrix_region"] # TODO make this right - FUDGE = 0 + _CPU_FUDGE = 0 + _DTCM_FUDGE = 0 def __init__(self, n_synapse_types, ring_buffer_sigma, spikes_per_second, config, population_table_type=None, synapse_io=None): @@ -127,6 +127,7 @@ def __init__(self, n_synapse_types, ring_buffer_sigma, spikes_per_second, self.__n_synapse_types = n_synapse_types self.__ring_buffer_sigma = ring_buffer_sigma self.__spikes_per_second = spikes_per_second + # Overridable (for testing only) region IDs self._synapse_params_region = \ POPULATION_BASED_REGIONS.SYNAPSE_PARAMS.value self._pop_table_region = \ @@ -209,22 +210,6 @@ def synapse_dynamics(self): """ return self.__synapse_dynamics - @staticmethod - def __combine_structural_stdp_dynamics(structural, stdp): - """ - :param AbstractSynapseDynamicsStructural structural: - :param SynapseDynamicsSTDP stdp: - :rtype: SynapseDynamicsStructuralSTDP - """ - return SynapseDynamicsStructuralSTDP( - structural.partner_selection, structural.formation, - structural.elimination, - stdp.timing_dependence, stdp.weight_dependence, - # voltage dependence is not supported - None, stdp.dendritic_delay_fraction, - structural.f_rew, structural.initial_weight, - structural.initial_delay, structural.s_max, structural.seed) - @synapse_dynamics.setter def synapse_dynamics(self, synapse_dynamics): if self.__synapse_dynamics is None: @@ -259,6 +244,7 @@ def spikes_per_second(self, spikes_per_second): def get_maximum_delay_supported_in_ms(self, machine_time_step): """ + :param int machine_time_step: :rtype: int or None """ return self.__synapse_io.get_maximum_delay_supported_in_ms( @@ -287,7 +273,7 @@ def add_pre_run_connection_holder( def get_connection_holders(self): """ - :rtype: dict(tuple(ProjectionApplicationEdge,SynapseInformation),\ + :rtype: dict(tuple(ProjectionApplicationEdge,SynapseInformation), ConnectionHolder) """ return self.__pre_run_connection_holders @@ -297,14 +283,14 @@ def get_n_cpu_cycles(self): :rtype: int """ # TODO: Calculate this correctly - return self.FUDGE + return self._CPU_FUDGE def get_dtcm_usage_in_bytes(self): """ :rtype: int """ # TODO: Calculate this correctly - return self.FUDGE + return self._DTCM_FUDGE def _get_synapse_params_size(self): """ @@ -383,7 +369,7 @@ def __add_synapse_size(self, memory_size, synapse_info, post_vertex_slice, def _get_size_of_generator_information(self, in_edges): """ Get the size of the synaptic expander parameters - :param list(.ApplicationEdge) in_edges: + :param list(~.ApplicationEdge) in_edges: :rtype: int """ gen_on_machine = False @@ -397,8 +383,8 @@ def _get_size_of_generator_information(self, in_edges): max_atoms = in_edge.pre_vertex.get_max_atoms_per_core() if in_edge.pre_vertex.n_atoms < max_atoms: max_atoms = in_edge.pre_vertex.n_atoms - n_edge_vertices = int(math.ceil( - float(in_edge.pre_vertex.n_atoms) / float(max_atoms))) + n_edge_vertices = ceildiv( + in_edge.pre_vertex.n_atoms, max_atoms) # Get the size if synapse_info.may_generate_on_machine(): @@ -531,20 +517,20 @@ def _ring_buffer_expected_upper_bound( Requires an assessment of maximum Poisson input rate. - Assumes knowledge of mean and SD of weight distribution, fan-in\ + Assumes knowledge of mean and SD of weight distribution, fan-in and timestep. - All arguments should be assumed real values except n_synapses_in\ + All arguments should be assumed real values except n_synapses_in which will be an integer. - :param float weight_mean: Mean of weight distribution (in either nA or\ + :param float weight_mean: Mean of weight distribution (in either nA or microSiemens as required) :param float weight_std_dev: SD of weight distribution :param float spikes_per_second: Maximum expected Poisson rate in Hz :param int machine_timestep: in us :param int n_synapses_in: No of connected synapses - :param float sigma: How many SD above the mean to go for upper bound;\ - a good starting choice is 5.0. Given length of simulation we can\ + :param float sigma: How many SD above the mean to go for upper bound; + a good starting choice is 5.0. Given length of simulation we can set this for approximate number of saturation events. :rtype: float """ @@ -692,7 +678,7 @@ def _get_ring_buffer_to_input_left_shifts( # Convert these to powers; we could use int.bit_length() for this if # they were integers, but they aren't... max_weight_powers = ( - 0 if w <= 0 else int(math.ceil(max(0, math.log(w, 2)))) + 0 if w <= 0 else round_up(max(0, math.log(w, 2))) for w in max_weights) # If 2^max_weight_power equals the max weight, we have to add another @@ -718,7 +704,7 @@ def __get_weight_scale(ring_buffer_to_input_left_shift): :param int ring_buffer_to_input_left_shift: :rtype: float """ - return float(math.pow(2, 16 - (ring_buffer_to_input_left_shift + 1))) + return 2.0 ** (16 - (ring_buffer_to_input_left_shift + 1)) def _write_synapse_parameters( self, spec, ring_buffer_shifts, weight_scale): @@ -1097,7 +1083,7 @@ def __is_direct( self, single_addr, connector, pre_vertex_slice, post_vertex_slice, app_edge, synapse_info): """ Determine if the given connection can be done with a "direct"\ - synaptic matrix - this must have an exactly 1 entry per row + synaptic matrix. This must have an exactly 1 entry per row. :param int single_addr: :param AbstractConnector connector: diff --git a/spynnaker/pyNN/models/pynn_population_common.py b/spynnaker/pyNN/models/pynn_population_common.py index de06723c126..53bd02b32d0 100644 --- a/spynnaker/pyNN/models/pynn_population_common.py +++ b/spynnaker/pyNN/models/pynn_population_common.py @@ -24,7 +24,8 @@ MaxVertexAtomsConstraint) from pacman.model.graphs.application.application_vertex import ( ApplicationVertex) -from spinn_front_end_common.utilities import globals_variables +from spinn_front_end_common.utilities.globals_variables import ( + get_simulator, get_not_running_simulator) from spinn_front_end_common.utilities.exceptions import ConfigurationException from spinn_front_end_common.abstract_models import AbstractChangableAfterRun from spynnaker.pyNN.models.abstract_models import ( @@ -71,7 +72,8 @@ def __init__( :param spinnaker_control: The simulator engine core. :type spinnaker_control: ~spinn_front_end_common.interface.abstract_spinnaker_base.AbstractSpinnakerBase - :param size: The size of the population; external devices may use None + :param size: The size of the population; external devices may use + `None` :type size: int or float or None :param label: The label for the population, or None for a default :type label: str or None @@ -86,7 +88,7 @@ def __init__( :param initial_values: Initialisation for model variables. :type initial_values: dict(str, Any) or None :param additional_parameters: - Any extra parameters to pass to the model's vertex creation \ + Any extra parameters to pass to the model's vertex creation function. :type additional_parameters: dict(str, Any) or None """ @@ -165,14 +167,13 @@ def __init__( # things for pynn demands self._all_ids = numpy.arange( - globals_variables.get_simulator().id_counter, - globals_variables.get_simulator().id_counter + size) + get_simulator().id_counter, get_simulator().id_counter + size) self.__first_id = self._all_ids[0] self.__last_id = self._all_ids[-1] # update the simulators id_counter for giving a unique ID for every # atom - globals_variables.get_simulator().id_counter += size + get_simulator().id_counter += size # set up initial values if given if initial_values is not None: @@ -247,13 +248,13 @@ def get(self, parameter_names, gather=True, simplify=True): """ Get the values of a parameter for every local cell in the\ population. - :param parameter_names: Name of parameter. This is either a single\ + :param parameter_names: Name of parameter. This is either a single string or a list of strings :type parameter_names: str or iterable(str) :param bool gather: pointless on sPyNNaker :param bool simplify: ignored - :return: A single list of values (or possibly a single value) if\ - paramter_names is a string, or a dict of these if parameter names\ + :return: A single list of values (or possibly a single value) if + paramter_names is a string, or a dict of these if parameter names is a list. :rtype: str or list(str) or dict(str,str) or dict(str,list(str)) """ @@ -278,15 +279,15 @@ def get_by_selector(self, selector, parameter_names): """ Get the values of a parameter for the selected cell in the\ population. - :param selector: a description of the subrange to accept. \ - Or None for all. See: \ + :param selector: a description of the subrange to accept. + Or `None` for all. See :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: slice or int or iterable(bool) or iterable(int) - :param parameter_names: Name of parameter. This is either a\ + :param parameter_names: Name of parameter. This is either a single string or a list of strings :type parameter_names: str or iterable(str) - :return: A single list of values (or possibly a single value) if\ - paramter_names is a string or a dict of these if parameter names\ + :return: A single list of values (or possibly a single value) if + paramter_names is a string or a dict of these if parameter names is a list. :rtype: str or list(str) or dict(str,str) or dict(str,list(str)) """ @@ -365,7 +366,7 @@ def _initialize(self, variable, value): raise KeyError( "Population does not support the initialisation of {}".format( variable)) - if globals_variables.get_not_running_simulator().has_ran \ + if get_not_running_simulator().has_ran \ and not self._vertex_changeable_after_run: raise Exception("Population does not support changes after run") self._read_parameters_before_set() @@ -415,7 +416,7 @@ def _set_check(self, parameter, value): raise KeyError("Population does not have property {}".format( parameter)) - if globals_variables.get_not_running_simulator().has_ran \ + if get_not_running_simulator().has_ran \ and not self._vertex_changeable_after_run: raise Exception( " run has been called") @@ -433,9 +434,9 @@ def _set_check(self, parameter, value): def set(self, parameter, value=None): """ Set one or more parameters for every cell in the population. - param can be a dict, in which case value should not be supplied, or a\ - string giving the parameter name, in which case value is the parameter\ - value. value can be a numeric value, or list of such\ + param can be a dict, in which case value should not be supplied, or a + string giving the parameter name, in which case value is the parameter + value. value can be a numeric value, or list of such (e.g. for setting spike times):: p.set("tau_m", 20.0). @@ -460,8 +461,8 @@ def set(self, parameter, value=None): def set_by_selector(self, selector, parameter, value=None): """ Set one or more parameters for selected cell in the population. - param can be a dict, in which case value should not be supplied, or a\ - string giving the parameter name, in which case value is the parameter\ + param can be a dict, in which case value should not be supplied, or a + string giving the parameter name, in which case value is the parameter value. value can be a numeric value, or list of such (e.g. for setting spike times):: @@ -486,27 +487,24 @@ def set_by_selector(self, selector, parameter, value=None): self.__vertex.set_value_by_selector(selector, key, value) def _read_parameters_before_set(self): - """ Reads parameters from the machine before "set" completes - - :return: None + """ Reads parameters from the machine before "set" completes. """ # If the tools have run before, and not reset, and the read # hasn't already been done, read back the data - if globals_variables.get_simulator().has_ran \ + if get_simulator().has_ran \ and self._vertex_read_parameters_before_set \ and not self.__has_read_neuron_parameters_this_run \ - and not globals_variables.get_simulator().use_virtual_board: + and not get_simulator().use_virtual_board: + placements = get_simulator().placements # go through each machine vertex and read the neuron parameters # it contains for machine_vertex in self.__vertex.machine_vertices: # tell the core to rewrite neuron params back to the # SDRAM space. - placement = globals_variables.get_simulator().placements.\ - get_placement_of_vertex(machine_vertex) - self.__vertex.read_parameters_from_machine( - globals_variables.get_simulator().transceiver, placement, + get_simulator().transceiver, + placements.get_placement_of_vertex(machine_vertex), machine_vertex.vertex_slice) self.__has_read_neuron_parameters_this_run = True @@ -559,7 +557,7 @@ def set_constraint(self, constraint): :param ~pacman.model.constraints.AbstractConstraint constraint: """ - globals_variables.get_simulator().verify_not_running() + get_simulator().verify_not_running() if not isinstance(constraint, AbstractConstraint): raise ConfigurationException( "the constraint entered is not a recognised constraint") @@ -576,7 +574,7 @@ def add_placement_constraint(self, x, y, p=None): :param int y: The y-coordinate of the placement constraint :param int p: The processor ID of the placement constraint (optional) """ - globals_variables.get_simulator().verify_not_running() + get_simulator().verify_not_running() self.__vertex.add_constraint(ChipAndCoreConstraint(x, y, p)) # state that something has changed in the population, @@ -590,7 +588,7 @@ def set_mapping_constraint(self, constraint_dict): A dictionary containing "x", "y" and optionally "p" as keys, and ints as values """ - globals_variables.get_simulator().verify_not_running() + get_simulator().verify_not_running() self.add_placement_constraint(**constraint_dict) # state that something has changed in the population, @@ -603,7 +601,7 @@ def set_max_atoms_per_core(self, max_atoms_per_core): :param int max_atoms_per_core: the new value for the max atoms per core. """ - globals_variables.get_simulator().verify_not_running() + get_simulator().verify_not_running() self.__vertex.add_constraint( MaxVertexAtomsConstraint(max_atoms_per_core)) # state that something has changed in the population diff --git a/spynnaker/pyNN/models/pynn_projection_common.py b/spynnaker/pyNN/models/pynn_projection_common.py index 2aadb28c0a3..44a16dd0036 100644 --- a/spynnaker/pyNN/models/pynn_projection_common.py +++ b/spynnaker/pyNN/models/pynn_projection_common.py @@ -14,7 +14,6 @@ # along with this program. If not, see . import logging -import math import numpy from pyNN.random import RandomDistribution from spinn_utilities.progress_bar import ProgressBar @@ -31,14 +30,16 @@ DelayedApplicationEdge, SynapseInformation, ProjectionApplicationEdge, DelayAfferentApplicationEdge) from spynnaker.pyNN.models.utility_models.delays import DelayExtensionVertex -from spynnaker.pyNN.utilities import constants +from spynnaker.pyNN.utilities.constants import ( + MAX_DELAY_BLOCKS, MAX_TIMER_TICS_SUPPORTED_PER_BLOCK, SPIKE_PARTITION_ID) from spynnaker.pyNN.models.neuron import ConnectionHolder +from spynnaker.pyNN.utilities.utility_calls import ceildiv # pylint: disable=protected-access logger = logging.getLogger(__name__) _delay_extension_max_supported_delay = ( - constants.MAX_DELAY_BLOCKS * constants.MAX_TIMER_TICS_SUPPORTED_PER_BLOCK) + MAX_DELAY_BLOCKS * MAX_TIMER_TICS_SUPPORTED_PER_BLOCK) # The maximum delay supported by the Delay extension, in ticks. @@ -181,7 +182,7 @@ def __init__( # add edge to the graph spinnaker_control.add_application_edge( - self.__projection_edge, constants.SPIKE_PARTITION_ID) + self.__projection_edge, SPIKE_PARTITION_ID) # If the delay exceeds the post vertex delay, add a delay extension if max_delay > post_vertex_max_supported_delay_ms: @@ -248,8 +249,8 @@ def _find_existing_edge(self, pre_synaptic_vertex, post_synaptic_vertex): :param post_synaptic_vertex: The destination vertex of the multapse :type post_synaptic_vertex: ~pacman.model.graphs.application.ApplicationVertex - :return: None or the edge going to these vertices. - :rtype: ~.ApplicationEdge + :return: the edge going to these vertices, if one exists. + :rtype: ~.ApplicationEdge or None """ # Find edges ending at the postsynaptic vertex @@ -297,13 +298,13 @@ def _add_delay_extension( pre_vertex, delay_vertex, label="{}_to_DelayExtension".format( pre_vertex.label)) self.__spinnaker_control.add_application_edge( - delay_afferent_edge, constants.SPIKE_PARTITION_ID) + delay_afferent_edge, SPIKE_PARTITION_ID) # Ensure that the delay extension knows how many states it will # support - n_stages = int(math.ceil( - float(max_delay_for_projection - max_delay_per_neuron) / - float(max_delay_per_neuron))) + n_stages = ceildiv( + max_delay_for_projection - max_delay_per_neuron, + max_delay_per_neuron) if n_stages > delay_vertex.n_delay_stages: delay_vertex.n_delay_stages = n_stages @@ -315,7 +316,7 @@ def _add_delay_extension( label="{}_delayed_to_{}".format( pre_vertex.label, post_vertex.label)) self.__spinnaker_control.add_application_edge( - delay_edge, constants.SPIKE_PARTITION_ID) + delay_edge, SPIKE_PARTITION_ID) else: delay_edge.add_synapse_information(self.__synapse_information) return delay_edge diff --git a/spynnaker/pyNN/models/recording_common.py b/spynnaker/pyNN/models/recording_common.py index 5ff82ef5278..bfc86ad9984 100644 --- a/spynnaker/pyNN/models/recording_common.py +++ b/spynnaker/pyNN/models/recording_common.py @@ -60,12 +60,11 @@ def _record(self, variable, sampling_interval=None, to_file=None, indexes=None): """ Tell the vertex to record data. - :param str variable: the variable to record, valued variables to\ + :param str variable: the variable to record, valued variables to record are: 'gsyn_exc', 'gsyn_inh', 'v', 'spikes' :param int sampling_interval: the interval to record them :param indexes: List of indexes to record or None for all :type indexes: list(int) or None - :return: None """ get_simulator().verify_not_running() @@ -206,8 +205,6 @@ def _get_spikes(self): def _turn_off_all_recording(self, indexes=None): """ Turns off recording, is used by a pop saying `.record()` - - :rtype: None """ # check for standard record which includes spikes if isinstance(self.__population._vertex, AbstractNeuronRecordable): diff --git a/spynnaker/pyNN/models/spike_source/spike_source_array_vertex.py b/spynnaker/pyNN/models/spike_source/spike_source_array_vertex.py index ad91188f3a3..cb034913176 100644 --- a/spynnaker/pyNN/models/spike_source/spike_source_array_vertex.py +++ b/spynnaker/pyNN/models/spike_source/spike_source_array_vertex.py @@ -20,10 +20,10 @@ from spinn_front_end_common.abstract_models import AbstractChangableAfterRun from spinn_front_end_common.abstract_models.impl import ( ProvidesKeyToAtomMappingImpl) -from spinn_front_end_common.utilities import globals_variables +from spinn_front_end_common.utilities.globals_variables import get_simulator from spynnaker.pyNN.models.common import ( AbstractSpikeRecordable, EIEIOSpikeRecorder, SimplePopulationSettable) -from spynnaker.pyNN.utilities import constants +from spynnaker.pyNN.utilities.constants import SPIKE_PARTITION_ID logger = logging.getLogger(__name__) @@ -66,7 +66,7 @@ def __init__( n_keys=n_neurons, label=label, constraints=constraints, max_atoms_per_core=max_atoms_per_core, send_buffer_times=_send_buffer_times(spike_times, time_step), - send_buffer_partition_id=constants.SPIKE_PARTITION_ID) + send_buffer_partition_id=SPIKE_PARTITION_ID) # handle recording self.__spike_recorder = EIEIOSpikeRecorder() @@ -118,7 +118,7 @@ def set_recording_spikes( @overrides(AbstractSpikeRecordable.get_spikes_sampling_interval) def get_spikes_sampling_interval(self): - return globals_variables.get_simulator().machine_time_step + return get_simulator().machine_time_step @overrides(AbstractSpikeRecordable.get_spikes) def get_spikes(self, placements, buffer_manager, machine_time_step): @@ -141,11 +141,11 @@ def clear_spike_recording(self, buffer_manager, placements): def describe(self): """ Returns a human-readable description of the cell or synapse type. - The output may be customised by specifying a different template\ - together with an associated template engine\ + The output may be customised by specifying a different template + together with an associated template engine (see :py:mod:`pyNN.descriptions`). - If template is None, then a dictionary containing the template\ + If template is `None`, then a dictionary containing the template context will be returned. """ diff --git a/spynnaker/pyNN/models/spike_source/spike_source_poisson_vertex.py b/spynnaker/pyNN/models/spike_source/spike_source_poisson_vertex.py index 9b3a403ea94..7ba1a6b40c8 100644 --- a/spynnaker/pyNN/models/spike_source/spike_source_poisson_vertex.py +++ b/spynnaker/pyNN/models/spike_source/spike_source_poisson_vertex.py @@ -14,11 +14,9 @@ # along with this program. If not, see . import logging -import math import numpy import scipy.stats import struct - from spinn_utilities.overrides import overrides from data_specification.enums import DataType from pacman.executor.injection_decorator import inject_items @@ -33,29 +31,34 @@ AbstractRewritesDataSpecification) from spinn_front_end_common.abstract_models.impl import ( ProvidesKeyToAtomMappingImpl) -from spinn_front_end_common.interface.simulation import simulation_utilities -from spinn_front_end_common.interface.buffer_management import ( - recording_utilities) -from spinn_front_end_common.utilities import ( - helpful_functions, globals_variables) +from spinn_front_end_common.interface.simulation.simulation_utilities import ( + get_simulation_header_array) +from spinn_front_end_common.interface.buffer_management.recording_utilities \ + import ( + get_recording_header_size, get_recording_data_constant_size, + get_recording_header_array) from spinn_front_end_common.utilities.constants import ( SYSTEM_BYTES_REQUIREMENT, SIMULATION_N_BYTES, BYTES_PER_WORD) +from spinn_front_end_common.utilities.globals_variables import get_simulator +from spinn_front_end_common.utilities.helpful_functions import ( + locate_memory_region_for_placement, read_config_int) from spinn_front_end_common.utilities.utility_objs import ExecutableType from spinn_front_end_common.utilities.exceptions import ConfigurationException -from spinn_front_end_common.interface.profiling import profile_utils +from spinn_front_end_common.interface.profiling.profile_utils import ( + get_profile_region_size, reserve_profile_region, write_profile_region_data) from spynnaker.pyNN.models.common import ( AbstractSpikeRecordable, MultiSpikeRecorder, SimplePopulationSettable) -from spynnaker.pyNN.utilities import constants +from spynnaker.pyNN.utilities.constants import ( + LIVE_POISSON_CONTROL_PARTITION_ID, SPIKE_PARTITION_ID) from spynnaker.pyNN.models.abstract_models import ( AbstractReadParametersBeforeSet) +from spynnaker.pyNN.utilities.utility_calls import ( + validate_mars_kiss_64_seed, ceildiv, round_up) +from spynnaker.pyNN.utilities.struct import Struct +from spynnaker.pyNN.utilities.ranged import ( + SpynnakerRangeDictionary, SpynnakerRangedList) from .spike_source_poisson_machine_vertex import ( SpikeSourcePoissonMachineVertex) -from spynnaker.pyNN.utilities.utility_calls import validate_mars_kiss_64_seed -from spynnaker.pyNN.utilities.struct import Struct -from spynnaker.pyNN.utilities.ranged.spynnaker_ranged_dict \ - import SpynnakerRangeDictionary -from spynnaker.pyNN.utilities.ranged.spynnaker_ranged_list \ - import SpynnakerRangedList logger = logging.getLogger(__name__) @@ -154,14 +157,18 @@ def __init__( max_rate=None): """ :param int n_neurons: - :param constraints: + :param list(~pacman.model.constrants.AbstractConstraint) constraints: :param str label: :param float seed: :param int max_atoms_per_core: - :param model: - :param iterable of float rate: - :param iterable of int start: - :param iterable of int duration: + :param AbstractPyNNModel model: + :param float rate: + :param int start: + :param int duration: + :param iterable(float) rates: + :param iterable(int) starts: + :param iterable(int) durations: + :param float max_rate: """ # pylint: disable=too-many-arguments super(SpikeSourcePoissonVertex, self).__init__( @@ -302,8 +309,8 @@ def __init__( self.__machine_time_step = None # get config from simulator - config = globals_variables.get_simulator().config - self.__n_profile_samples = helpful_functions.read_config_int( + config = get_simulator().config + self.__n_profile_samples = read_config_int( config, "Reports", "n_profile_samples") # Prepare for recording, and to get spikes @@ -446,6 +453,7 @@ def set_value(self, key, value): def _max_spikes_per_ts(self, machine_time_step): """ :param int machine_time_step: + :rtype: int or float """ ts_per_second = MICROSECONDS_PER_SECOND / float(machine_time_step) if float(self.__max_rate) / ts_per_second < \ @@ -457,7 +465,7 @@ def _max_spikes_per_ts(self, machine_time_step): max_spikes_per_ts = scipy.stats.poisson.ppf( 1.0 - (1.0 / float(chance_ts)), float(self.__max_rate) / ts_per_second) - return int(math.ceil(max_spikes_per_ts)) + 1.0 + return round_up(max_spikes_per_ts) + 1.0 def get_recording_sdram_usage(self, vertex_slice, machine_time_step): """ @@ -479,8 +487,7 @@ def get_recording_sdram_usage(self, vertex_slice, machine_time_step): ) def get_resources_used_by_atoms(self, vertex_slice, machine_time_step): """ - :param ~pacman.model.graphs.common.Slice vertex_slice: - :param int machine_time_step: + :param int machine_time_step: (injected) """ # pylint: disable=arguments-differ poisson_params_sz = self.get_rates_bytes(vertex_slice) @@ -488,9 +495,9 @@ def get_resources_used_by_atoms(self, vertex_slice, machine_time_step): SYSTEM_BYTES_REQUIREMENT + SpikeSourcePoissonMachineVertex.get_provenance_data_size(0) + poisson_params_sz + - recording_utilities.get_recording_header_size(1) + - recording_utilities.get_recording_data_constant_size(1) + - profile_utils.get_profile_region_size(self.__n_profile_samples)) + get_recording_header_size(1) + + get_recording_data_constant_size(1) + + get_profile_region_size(self.__n_profile_samples)) recording = self.get_recording_sdram_usage( vertex_slice, machine_time_step) @@ -546,11 +553,10 @@ def reserve_memory_regions(self, spec, placement): """ Reserve memory regions for Poisson source parameters and output\ buffer. - :param ~data_specification.DataSpecification spec: + :param ~data_specification.DataSpecificationGenerator spec: the data specification writer - :param ~pacman.models.placements.Placement placement: + :param ~pacman.model.placements.Placement placement: the location this vertex resides on in the machine - :return: None """ spec.comment("\nReserving memory space for data regions:\n\n") @@ -565,10 +571,10 @@ def reserve_memory_regions(self, spec, placement): spec.reserve_memory_region( region=_REGIONS.SPIKE_HISTORY_REGION.value, - size=recording_utilities.get_recording_header_size(1), + size=get_recording_header_size(1), label="Recording") - profile_utils.reserve_profile_region( + reserve_profile_region( spec, _REGIONS.PROFILER_REGION.value, self.__n_profile_samples) placement.vertex.reserve_provenance_data_region(spec) @@ -577,10 +583,8 @@ def _reserve_poisson_params_rates_region(self, placement, spec): """ Allocate space for the Poisson parameters and rates regions as\ they can be reused for setters after an initial run - :param ~pacman.models.placements.Placement placement: - the location on machine for this vertex - :param ~data_specification.DataSpecification spec: the DSG writer - :return: None + :param ~.Placement placement: the location on machine for this vertex + :param ~.DataSpecificationGenerator spec: the DSG writer """ spec.reserve_memory_region( region=_REGIONS.POISSON_PARAMS_REGION.value, @@ -595,11 +599,10 @@ def _write_poisson_parameters( vertex_slice, machine_time_step, time_scale_factor): """ Generate Parameter data for Poisson spike sources - :param ~data_specification.DataSpecification spec: - the data specification writer - :param ~pacman.model.graphs.machine.MachineGraph graph: - :param ~pacman.model.placements.Placement placement: - :param ~pacman.model.routing_info.RoutingInfo routing_info: + :param ~.DataSpecificationGenerator spec: the data specification writer + :param ~.MachineGraph graph: + :param ~.Placement placement: + :param ~.RoutingInfo routing_info: :param ~pacman.model.graphs.common.Slice vertex_slice: the slice of atoms a machine vertex holds from its application vertex @@ -616,13 +619,13 @@ def _write_poisson_parameters( # Write Key info for this core: key = routing_info.get_first_key_from_pre_vertex( - placement.vertex, constants.SPIKE_PARTITION_ID) + placement.vertex, SPIKE_PARTITION_ID) spec.write_value(data=1 if key is not None else 0) spec.write_value(data=key if key is not None else 0) # Write the incoming mask if there is one in_edges = graph.get_edges_ending_at_vertex_with_partition_name( - placement.vertex, constants.LIVE_POISSON_CONTROL_PARTITION_ID) + placement.vertex, LIVE_POISSON_CONTROL_PARTITION_ID) if len(in_edges) > 1: raise ConfigurationException( "Only one control edge can end at a Poisson vertex") @@ -640,8 +643,7 @@ def _write_poisson_parameters( max_offset = ( machine_time_step * time_scale_factor) // _MAX_OFFSET_DENOMINATOR spec.write_value( - int(math.ceil(max_offset / self.__n_subvertices)) * - self.__n_data_specs) + ceildiv(max_offset, self.__n_subvertices) * self.__n_data_specs) self.__n_data_specs += 1 if self.__max_spikes > 0: @@ -702,7 +704,7 @@ def _write_poisson_rates(self, spec, vertex_slice, machine_time_step, first_machine_time_step): """ Generate Rate data for Poisson spike sources - :param ~data_specification.DataSpecification spec: + :param ~.DataSpecificationGenerator spec: the data specification writer :param ~pacman.model.graphs.common.Slice vertex_slice: the slice of atoms a machine vertex holds from its application @@ -809,15 +811,15 @@ def _write_poisson_rates(self, spec, vertex_slice, machine_time_step, @staticmethod def _convert_ms_to_n_timesteps(value, machine_time_step): + """ + :param ~numpy.ndarray value: + :param int machine_time_step: + :rtype: ~numpy.ndarray + """ return numpy.round( value * (MICROSECONDS_PER_MILLISECOND / float(machine_time_step))).astype("uint32") - @staticmethod - def _convert_n_timesteps_to_ms(value, machine_time_step): - return ( - value / (MICROSECONDS_PER_MILLISECOND / float(machine_time_step))) - @overrides(AbstractSpikeRecordable.is_recording_spikes) def is_recording_spikes(self): return self.__spike_recorder.record @@ -837,14 +839,20 @@ def set_recording_spikes( @overrides(AbstractSpikeRecordable.get_spikes_sampling_interval) def get_spikes_sampling_interval(self): - return globals_variables.get_simulator().machine_time_step + return get_simulator().machine_time_step @staticmethod def get_dtcm_usage_for_atoms(): + """ + :rtype: int + """ return 0 @staticmethod def get_cpu_usage_for_atoms(): + """ + :rtype: int + """ return 0 @inject_items({ @@ -862,11 +870,11 @@ def regenerate_data_specification( self, spec, placement, machine_time_step, time_scale_factor, routing_info, graph, first_machine_time_step): """ - :param int machine_time_step: - :param int time_scale_factor: - :param ~pacman.model.routing_info.RoutingInfo routing_info: - :param ~pacman.model.graphs.machine.MachineGraph graph: - :param int first_machine_time_step: + :param int machine_time_step: (injected) + :param int time_scale_factor: (injected) + :param ~pacman.model.routing_info.RoutingInfo routing_info: (injected) + :param ~pacman.model.graphs.machine.MachineGraph graph: (injected) + :param int first_machine_time_step: (injected) """ # pylint: disable=too-many-arguments, arguments-differ @@ -907,18 +915,16 @@ def read_parameters_from_machine( self, transceiver, placement, vertex_slice): # locate SDRAM address where parameters are stored - poisson_params = \ - helpful_functions.locate_memory_region_for_placement( - placement, _REGIONS.POISSON_PARAMS_REGION.value, transceiver) + poisson_params = locate_memory_region_for_placement( + placement, _REGIONS.POISSON_PARAMS_REGION.value, transceiver) seed_array = transceiver.read_memory( placement.x, placement.y, poisson_params + SEED_OFFSET_BYTES, SEED_SIZE_BYTES) self.__kiss_seed[vertex_slice] = struct.unpack_from("<4I", seed_array) # locate SDRAM address where the rates are stored - poisson_rate_region_sdram_address = \ - helpful_functions.locate_memory_region_for_placement( - placement, _REGIONS.RATES_REGION.value, transceiver) + poisson_rate_region_sdram_address = locate_memory_region_for_placement( + placement, _REGIONS.RATES_REGION.value, transceiver) # get size of poisson params size_of_region = self.get_rates_bytes(vertex_slice) @@ -985,12 +991,12 @@ def generate_data_specification( self, spec, placement, machine_time_step, time_scale_factor, routing_info, data_n_time_steps, graph, first_machine_time_step): """ - :param int machine_time_step: - :param int time_scale_factor: - :param ~pacman.model.routing_info.RoutingInfo routing_info: - :param int data_n_time_steps: - :param ~pacman.model.graphs.machine.MachineGraph graph: - :param int first_machine_time_step: + :param int machine_time_step: (injected) + :param int time_scale_factor: (injected) + :param ~pacman.model.routing_info.RoutingInfo routing_info: (injected) + :param int data_n_time_steps: (injected) + :param ~pacman.model.graphs.machine.MachineGraph graph: (injected) + :param int first_machine_time_step: (injected) """ # pylint: disable=too-many-arguments, arguments-differ self.__machine_time_step = machine_time_step @@ -1003,7 +1009,7 @@ def generate_data_specification( # write setup data spec.switch_write_focus(_REGIONS.SYSTEM_REGION.value) - spec.write_array(simulation_utilities.get_simulation_header_array( + spec.write_array(get_simulation_header_array( self.get_binary_file_name(), machine_time_step, time_scale_factor)) @@ -1011,8 +1017,7 @@ def generate_data_specification( spec.switch_write_focus(_REGIONS.SPIKE_HISTORY_REGION.value) sdram = self.get_recording_sdram_usage(vertex_slice, machine_time_step) recorded_region_sizes = [sdram.get_total_sdram(data_n_time_steps)] - spec.write_array(recording_utilities.get_recording_header_array( - recorded_region_sizes)) + spec.write_array(get_recording_header_array(recorded_region_sizes)) # write parameters self._write_poisson_parameters( @@ -1024,9 +1029,8 @@ def generate_data_specification( first_machine_time_step) # write profile data - profile_utils.write_profile_region_data( - spec, _REGIONS.PROFILER_REGION.value, - self.__n_profile_samples) + write_profile_region_data( + spec, _REGIONS.PROFILER_REGION.value, self.__n_profile_samples) # End-of-Spec: spec.end_specification() @@ -1062,12 +1066,12 @@ def clear_spike_recording(self, buffer_manager, placements): def describe(self): """ Return a human-readable description of the cell or synapse type. - The output may be customised by specifying a different template\ - together with an associated template engine\ + The output may be customised by specifying a different template + together with an associated template engine (see :py:mod:`pyNN.descriptions`). - If template is None, then a dictionary containing the template context\ - will be returned. + If template is `None`, then a dictionary containing the template + context will be returned. :rtype: dict(str, ...) """ diff --git a/spynnaker/pyNN/models/utility_models/delays/delay_block.py b/spynnaker/pyNN/models/utility_models/delays/delay_block.py index feea65f6759..d0de1f08422 100644 --- a/spynnaker/pyNN/models/utility_models/delays/delay_block.py +++ b/spynnaker/pyNN/models/utility_models/delays/delay_block.py @@ -12,9 +12,9 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -import math import numpy +from spynnaker.pyNN.utilities.utility_calls import ceildiv +from spynnaker.pyNN.utilities.constants import BITS_PER_WORD class DelayBlock(object): @@ -33,7 +33,7 @@ def __init__(self, n_delay_stages, delay_per_stage, vertex_slice): """ self.__delay_per_stage = delay_per_stage self.__n_delay_stages = n_delay_stages - n_words_per_row = int(math.ceil(vertex_slice.n_atoms / 32.0)) + n_words_per_row = ceildiv(vertex_slice.n_atoms, BITS_PER_WORD) self.__delay_block = numpy.zeros( (n_delay_stages, n_words_per_row), dtype="uint32") diff --git a/spynnaker/pyNN/models/utility_models/delays/delay_extension_machine_vertex.py b/spynnaker/pyNN/models/utility_models/delays/delay_extension_machine_vertex.py index 23c7aef4053..f02ca1c6d9c 100644 --- a/spynnaker/pyNN/models/utility_models/delays/delay_extension_machine_vertex.py +++ b/spynnaker/pyNN/models/utility_models/delays/delay_extension_machine_vertex.py @@ -23,6 +23,8 @@ class DelayExtensionMachineVertex( MachineVertex, ProvidesProvenanceDataFromMachineImpl): + """ Implements long delays of spikes. + """ __slots__ = [ "__resources"] @@ -48,11 +50,13 @@ def __init__(self, resources_required, label, constraints=None, :param ~pacman.model.resources.ResourceContainer resources_required: The resources required by the vertex :param str label: The optional name of the vertex - :param iterable(AbstractConstraint) constraints: + :param constraints: The optional initial constraints of the vertex - :param ~pacman.model.graphs.application.ApplicationVertex app_vertex: + :type constraints: + iterable(~pacman.model.constraints.AbstractConstraint) + :param DelayExtensionVertex app_vertex: The application vertex that caused this machine vertex to be - created. If None, there is no such application vertex. + created. :param ~pacman.model.graphs.common.Slice vertex_slice: The slice of the application vertex that this machine vertex implements. diff --git a/spynnaker/pyNN/models/utility_models/delays/delay_extension_vertex.py b/spynnaker/pyNN/models/utility_models/delays/delay_extension_vertex.py index 21cd5f1a6c6..1e71dd26592 100644 --- a/spynnaker/pyNN/models/utility_models/delays/delay_extension_vertex.py +++ b/spynnaker/pyNN/models/utility_models/delays/delay_extension_vertex.py @@ -12,10 +12,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - from collections import defaultdict -import logging -import math from spinn_utilities.overrides import overrides from pacman.executor.injection_decorator import inject_items from pacman.model.constraints.key_allocator_constraints import ( @@ -30,20 +27,19 @@ AbstractProvidesOutgoingPartitionConstraints, AbstractHasAssociatedBinary) from spinn_front_end_common.interface.simulation import simulation_utilities from spinn_front_end_common.utilities.constants import ( - SYSTEM_BYTES_REQUIREMENT, SIMULATION_N_BYTES, BITS_PER_WORD, - BYTES_PER_WORD) + SYSTEM_BYTES_REQUIREMENT, SIMULATION_N_BYTES, BYTES_PER_WORD) from spinn_front_end_common.utilities.utility_objs import ExecutableType from .delay_block import DelayBlock from .delay_extension_machine_vertex import DelayExtensionMachineVertex from .delay_generator_data import DelayGeneratorData -from spynnaker.pyNN.utilities.constants import SPIKE_PARTITION_ID +from spynnaker.pyNN.utilities.constants import ( + SPIKE_PARTITION_ID, BITS_PER_WORD) from spynnaker.pyNN.models.neural_projections import DelayedApplicationEdge from spynnaker.pyNN.models.neural_projections.connectors import ( AbstractGenerateConnectorOnMachine) from spynnaker.pyNN.models.neuron.synapse_dynamics import ( AbstractGenerateOnMachine) - -logger = logging.getLogger(__name__) +from spynnaker.pyNN.utilities.utility_calls import ceildiv _DELAY_PARAM_HEADER_WORDS = 8 # pylint: disable=protected-access @@ -87,9 +83,10 @@ def __init__(self, n_neurons, delay_per_stage, source_vertex, where messages are coming from :param int machine_time_step: how long is the machine time step :param int time_scale_factor: what slowdown factor has been applied - :param iterable(~pacman.model.constraints.AbstractConstraint) \ - constraints: + :param constraints: the vertex constraints + :type constraints: + iterable(~pacman.model.constraints.AbstractConstraint) :param str label: the vertex label """ # pylint: disable=too-many-arguments @@ -225,8 +222,7 @@ def generate_data_specification( # ################################################################### # Reserve SDRAM space for memory areas: vertex_slice = vertex.vertex_slice - n_words_per_stage = int( - math.ceil(vertex_slice.n_atoms / BITS_PER_WORD)) + n_words_per_stage = ceildiv(vertex_slice.n_atoms, BITS_PER_WORD) delay_params_sz = BYTES_PER_WORD * ( _DELAY_PARAM_HEADER_WORDS + (self.__n_delay_stages * n_words_per_stage)) @@ -289,7 +285,7 @@ def generate_data_specification( def _write_setup_info(self, spec, machine_time_step, time_scale_factor): """ - :param ~data_specification.DataSpecificationGenerator spec: + :param ~.DataSpecificationGenerator spec: :param int machine_time_step: :param int time_scale_factor: """ @@ -340,8 +336,7 @@ def write_delay_parameters( max_offset = ( machine_time_step * time_scale_factor) // _MAX_OFFSET_DENOMINATOR spec.write_value( - int(math.ceil(max_offset / total_n_vertices)) * - self.__n_data_specs) + ceildiv(max_offset, total_n_vertices) * self.__n_data_specs) self.__n_data_specs += 1 # Write the time between spikes @@ -384,6 +379,7 @@ def _get_edge_generator_size(self, synapse_info): """ Get the size of the generator data for a given synapse info object :param SynapseInformation synapse_info: + :rtype: int """ connector = synapse_info.connector dynamics = synapse_info.synapse_dynamics @@ -394,42 +390,44 @@ def _get_edge_generator_size(self, synapse_info): synapse_gen = isinstance( dynamics, AbstractGenerateOnMachine) if connector_gen and synapse_gen: - return sum(( - DelayGeneratorData.BASE_SIZE, - connector.gen_delay_params_size_in_bytes( - synapse_info.delays), - connector.gen_connector_params_size_in_bytes, - )) + return ( + DelayGeneratorData.BASE_SIZE + + connector.gen_delay_params_size_in_bytes(synapse_info.delays) + + connector.gen_connector_params_size_in_bytes) return 0 def _get_size_of_generator_information(self, out_edges): """ Get the size of the generator data for all edges - :param list(.ApplicationEdge) out_edges: + :param list(~.ApplicationEdge) out_edges: :rtype: int """ - gen_on_machine = False - size = 0 - for out_edge in out_edges: - if isinstance(out_edge, DelayedApplicationEdge): - for synapse_info in out_edge.synapse_information: - - # Get the number of likely vertices - max_atoms = out_edge.post_vertex.get_max_atoms_per_core() - if out_edge.post_vertex.n_atoms < max_atoms: - max_atoms = out_edge.post_vertex.n_atoms - n_edge_vertices = int(math.ceil(float( - out_edge.post_vertex.n_atoms) / float(max_atoms))) - - # Get the size - gen_size = self._get_edge_generator_size(synapse_info) - if gen_size > 0: - gen_on_machine = True - size += gen_size * n_edge_vertices - if gen_on_machine: + size = sum( + self.__size_of_edge_generator_info(edge) + for edge in out_edges + if isinstance(edge, DelayedApplicationEdge)) + # If there's a non-zero size, we will be generating on machine and we + # should add the baseline overhead + if size: size += _EXPANDER_BASE_PARAMS_SIZE return size + def __size_of_edge_generator_info(self, edge): + """ + :param DelayedApplicationEdge edge: + :rtype: int + """ + # Get the number of likely vertices + max_atoms = min( + edge.post_vertex.get_max_atoms_per_core(), + edge.post_vertex.n_atoms) + n_edge_vertices = ceildiv(edge.post_vertex.n_atoms, max_atoms) + + # Get the size for each synapse type + return sum( + self._get_edge_generator_size(synapse_info) * n_edge_vertices + for synapse_info in edge.synapse_information) + def get_dtcm_usage_for_atoms(self, vertex_slice): """ :param ~pacman.model.graphs.common.Slice vertex_slice: diff --git a/spynnaker/pyNN/models/utility_models/spike_injector/spike_injector_vertex.py b/spynnaker/pyNN/models/utility_models/spike_injector/spike_injector_vertex.py index 1825924a93a..18aba38ba42 100644 --- a/spynnaker/pyNN/models/utility_models/spike_injector/spike_injector_vertex.py +++ b/spynnaker/pyNN/models/utility_models/spike_injector/spike_injector_vertex.py @@ -131,8 +131,8 @@ def describe(self): together with an associated template engine (see :py:mod:`pyNN.descriptions`). - If template is None, then a dictionary containing the template context - will be returned. + If template is `None`, then a dictionary containing the template + context will be returned. """ parameters = dict() diff --git a/spynnaker/pyNN/protocols/munich_io_spinnaker_link_protocol.py b/spynnaker/pyNN/protocols/munich_io_spinnaker_link_protocol.py index 638ef8dff91..77242bcea5b 100644 --- a/spynnaker/pyNN/protocols/munich_io_spinnaker_link_protocol.py +++ b/spynnaker/pyNN/protocols/munich_io_spinnaker_link_protocol.py @@ -12,14 +12,10 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - from enum import Enum -import logging from spinn_front_end_common.utility_models import MultiCastCommand from spinn_front_end_common.utilities.exceptions import ConfigurationException -logger = logging.getLogger(__name__) - # structure of command is KKKKKKKKKKKKKKKKKKKKK-IIIIIII-F-DDD # K = ignored key at the top of the command # I = instruction diff --git a/spynnaker/pyNN/spynnaker_external_device_plugin_manager.py b/spynnaker/pyNN/spynnaker_external_device_plugin_manager.py index 0df662a5f42..9d80087bc57 100644 --- a/spynnaker/pyNN/spynnaker_external_device_plugin_manager.py +++ b/spynnaker/pyNN/spynnaker_external_device_plugin_manager.py @@ -16,7 +16,8 @@ from spinn_utilities.socket_address import SocketAddress from pacman.model.graphs.application import ApplicationEdge from spinnman.messages.eieio import EIEIOType -from spinn_front_end_common.utilities import helpful_functions +from spinn_front_end_common.utilities.helpful_functions import ( + read_config, read_config_int) from spinn_front_end_common.utilities.globals_variables import get_simulator from spinn_front_end_common.utility_models import ( ReverseIpTagMultiCastSource) @@ -50,15 +51,15 @@ def add_database_socket_address( """ config = get_simulator().config if database_notify_port_num is None: - database_notify_port_num = helpful_functions.read_config_int( + database_notify_port_num = read_config_int( config, "Database", "notify_port") if database_notify_host is None: - database_notify_host = helpful_functions.read_config( + database_notify_host = read_config( config, "Database", "notify_hostname") elif database_notify_host == "0.0.0.0": database_notify_host = "localhost" if database_ack_port_num is None: - database_ack_port_num = helpful_functions.read_config_int( + database_ack_port_num = read_config_int( config, "Database", "listen_port") # build the database socket address used by the notification interface @@ -293,23 +294,31 @@ def add_edge(vertex, device_vertex, partition_id): :param ~pacman.model.graphs.application.ApplicationVertex vertex: the pre-vertex to connect the edge from - :param device_vertex: the post vertex to connect the edge to + :param device_vertex: the post-vertex to connect the edge to :type device_vertex: ~pacman.model.graphs.application.ApplicationVertex :param str partition_id: the partition identifier for making nets """ - _spinnaker = get_simulator() edge = ApplicationEdge(vertex, device_vertex) - _spinnaker.add_application_edge(edge, partition_id) + get_simulator().add_application_edge(edge, partition_id) @staticmethod def add_application_vertex(vertex): + """ + :param ~pacman.model.graphs.application.ApplicationVertex vertex: + """ get_simulator().add_application_vertex(vertex) @staticmethod def machine_time_step(): + """ + :rtype: int + """ return get_simulator().machine_time_step @staticmethod def time_scale_factor(): + """ + :rtype: int + """ return get_simulator().time_scale_factor diff --git a/spynnaker/pyNN/utilities/bit_field_utilities.py b/spynnaker/pyNN/utilities/bit_field_utilities.py index 4fbe52da452..3e4528cbb44 100644 --- a/spynnaker/pyNN/utilities/bit_field_utilities.py +++ b/spynnaker/pyNN/utilities/bit_field_utilities.py @@ -13,14 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import math - from pacman.utilities.constants import FULL_MASK +from pacman.utilities.algorithm_utilities.partition_algorithm_utilities \ + import ( + determine_max_atoms_for_vertex) from spinn_front_end_common.utilities.constants import BYTES_PER_WORD -from pacman.utilities.algorithm_utilities. \ - partition_algorithm_utilities import determine_max_atoms_for_vertex -from spynnaker.pyNN.models.neural_projections import ProjectionApplicationEdge -from spynnaker.pyNN.models.utility_models.delays import DelayExtensionVertex +from .utility_calls import ceildiv # number of elements ELEMENTS_USED_IN_EACH_BIT_FIELD = 3 # n words, key, pointer to bitfield @@ -43,47 +41,16 @@ BIT_IN_A_WORD = 32.0 -def get_estimated_sdram_for_bit_field_region(app_graph, vertex): - """ estimates the sdram for the bit field region - :param app_graph: the app graph - :param vertex: machine vertex - :return: the estimated number of bytes used by the bit field region - """ - sdram = 0 - for incoming_edge in app_graph.get_edges_ending_at_vertex(vertex): - if isinstance(incoming_edge, ProjectionApplicationEdge): - edge_pre_vertex = incoming_edge.pre_vertex - max_atoms = determine_max_atoms_for_vertex(edge_pre_vertex) - if incoming_edge.pre_vertex.n_atoms < max_atoms: - max_atoms = incoming_edge.pre_vertex.n_atoms - - # Get the number of likely vertices - n_machine_vertices = int(math.ceil( - float(incoming_edge.pre_vertex.n_atoms) / - float(max_atoms))) - n_atoms_per_machine_vertex = int(math.ceil( - float(incoming_edge.pre_vertex.n_atoms) / - n_machine_vertices)) - if isinstance(edge_pre_vertex, DelayExtensionVertex): - n_atoms_per_machine_vertex *= \ - edge_pre_vertex.n_delay_stages - n_words_for_atoms = int(math.ceil( - n_atoms_per_machine_vertex / BIT_IN_A_WORD)) - sdram += ( - (ELEMENTS_USED_IN_EACH_BIT_FIELD + ( - n_words_for_atoms * n_machine_vertices)) * - BYTES_PER_WORD) - return sdram - - def get_estimated_sdram_for_key_region(app_graph, vertex): - """ gets an estimate of the bitfield builder region - - :param app_graph: the app graph - :param vertex: machine vertex - :return: sdram needed + """ gets an estimate of the bitfield builder region size + + :param ~pacman.model.graphs.application.ApplicationGraph app_graph: + the app graph + :param ~pacman.model.graphs.application.ApplicationVertex vertex: + app vertex + :return: SDRAM needed + :rtype: int """ - # basic sdram sdram = N_KEYS_DATA_SET_IN_WORDS * BYTES_PER_WORD for in_edge in app_graph.get_edges_ending_at_vertex(vertex): @@ -93,28 +60,27 @@ def get_estimated_sdram_for_key_region(app_graph, vertex): max_atoms = determine_max_atoms_for_vertex(edge_pre_vertex) if in_edge.pre_vertex.n_atoms < max_atoms: max_atoms = in_edge.pre_vertex.n_atoms - n_edge_vertices = int(math.ceil( - float(in_edge.pre_vertex.n_atoms) / float(max_atoms))) + n_edge_vertices = ceildiv(in_edge.pre_vertex.n_atoms, max_atoms) sdram += (n_edge_vertices * N_ELEMENTS_IN_EACH_KEY_N_ATOM_MAP * BYTES_PER_WORD) return sdram -def _exact_sdram_for_bit_field_region( - machine_graph, vertex, n_key_map): - """ calculates the correct sdram for the bitfield region based off \ - the machine graph and graph mapper +def _exact_sdram_for_bit_field_region(machine_graph, vertex, n_key_map): + """ Gets the correct SDRAM for the bitfield region based off \ + the machine graph - :param machine_graph: machine graph - :param vertex: the machine vertex - :param n_key_map: n keys map + :param ~.MachineGraph machine_graph: machine graph + :param ~.MachineVertex vertex: the machine vertex + :param ~.AbstractMachinePartitionNKeysMap n_key_map: n keys map :return: sdram in bytes + :rtype: int """ sdram = ELEMENTS_USED_IN_BIT_FIELD_HEADER * BYTES_PER_WORD for incoming_edge in machine_graph.get_edges_ending_at_vertex(vertex): n_keys = n_key_map.n_keys_for_partition( machine_graph.get_outgoing_partition_for_edge(incoming_edge)) - n_words_for_atoms = int(math.ceil(n_keys / BIT_IN_A_WORD)) + n_words_for_atoms = ceildiv(n_keys, int(BIT_IN_A_WORD)) sdram += ( (ELEMENTS_USED_IN_EACH_BIT_FIELD + n_words_for_atoms) * @@ -123,23 +89,26 @@ def _exact_sdram_for_bit_field_region( def exact_sdram_for_bit_field_builder_region(): - """ returns the sdram requirement for the builder region - :return: returns the sdram requirement for the builder region + """ Gets the SDRAM requirement for the builder region + + :return: returns the SDRAM requirement for the builder region + :rtype: int """ return N_REGIONS_ADDRESSES * BYTES_PER_WORD def _exact_sdram_for_bit_field_key_region(machine_graph, vertex): - """ calcs the exact sdram for the bitfield key region + """ Calculates the exact SDRAM for the bitfield key region - :param machine_graph: machine graph - :param vertex: machine vertex - :return: bytes + :param ~.MachineGraph machine_graph: machine graph + :param ~.MachineVertex vertex: machine vertex + :return: number of bytes + :rtype: int """ return ( - N_KEYS_DATA_SET_IN_WORDS + - len(machine_graph.get_edges_ending_at_vertex(vertex)) * - N_ELEMENTS_IN_EACH_KEY_N_ATOM_MAP) * BYTES_PER_WORD + N_KEYS_DATA_SET_IN_WORDS + + len(machine_graph.get_edges_ending_at_vertex(vertex)) * + N_ELEMENTS_IN_EACH_KEY_N_ATOM_MAP) * BYTES_PER_WORD def reserve_bit_field_regions( @@ -147,16 +116,17 @@ def reserve_bit_field_regions( bit_filter_region, bit_field_key_region): """ reserves the regions for the bitfields - :param spec: dsg file - :param machine_graph: machine graph - :param n_key_map: map between partitions and n keys - :param vertex: machine vertex - :param bit_field_builder_region: region id for the builder region - :param bit_filter_region: region id for the bitfield region - :param bit_field_key_region: region id for the key map - :rtype: None + :param ~data_specification.DataSpecificationGenerator spec: dsg file + :param ~pacman.model.graphs.machine.MachineGraph machine_graph: + machine graph + :param ~pacman.model.routing_info.AbstractMachinePartitionNKeysMap \ + n_key_map: + map between partitions and n keys + :param ~pacman.model.graphs.machine.MachineVertex vertex: machine vertex + :param int bit_field_builder_region: region id for the builder region + :param int bit_filter_region: region id for the bitfield region + :param int bit_field_key_region: region id for the key map """ - # reserve the final destination for the bitfields spec.reserve_memory_region( region=bit_filter_region, @@ -185,21 +155,24 @@ def write_bitfield_init_data( structural_dynamics_region_id, has_structural_dynamics_region): """ writes the init data needed for the bitfield generator - :param spec: data spec writer - :param machine_vertex: machine vertex - :param machine_graph: machine graph - :param routing_info: keys - :param n_key_map: map for edge to n keys - :param bit_field_builder_region: the region id for the bitfield builder - :param master_pop_region_id: the region id for the master pop table - :param synaptic_matrix_region_id: the region id for the synaptic matrix - :param direct_matrix_region_id: the region id for the direct matrix - :param bit_field_region_id: the region id for the bit-fields - :param bit_field_key_map_region_id: the region id for the key map - :param structural_dynamics_region_id: the region id for the structural - :param has_structural_dynamics_region: \ - bool saying if the core has a has_structural_dynamics_region or not - :rtype: None + :param ~data_specification.DataSpecificationGenerator spec: data spec writer + :param ~pacman.model.graphs.machine.MachineVertex machine_vertex: + machine vertex + :param ~pacman.model.graphs.machine.MachineGraph machine_graph: + machine graph + :param ~pacman.model.routing_info.RoutingInfo routing_info: keys + :param ~pacman.model.routing_info.AbstractMachinePartitionNKeysMap \ + n_key_map: + map for edge to n keys + :param int bit_field_builder_region: the region id for the bitfield builder + :param int master_pop_region_id: the region id for the master pop table + :param int synaptic_matrix_region_id: the region id for the synaptic matrix + :param int direct_matrix_region_id: the region id for the direct matrix + :param int bit_field_region_id: the region id for the bit-fields + :param int bit_field_key_map_region_id: the region id for the key map + :param int structural_dynamics_region_id: the region id for the structural + :param bool has_structural_dynamics_region: + whether the core has a structural dynamics region """ spec.switch_write_focus(bit_field_builder_region) diff --git a/spynnaker/pyNN/utilities/constants.py b/spynnaker/pyNN/utilities/constants.py index bf63e59569b..d164f78d619 100644 --- a/spynnaker/pyNN/utilities/constants.py +++ b/spynnaker/pyNN/utilities/constants.py @@ -17,6 +17,9 @@ from spinn_front_end_common.utilities.constants import ( BYTES_PER_WORD, BYTES_PER_KB) +#: The number of bits in a SpiNNaker machine word +BITS_PER_WORD = 32 + POSSION_SIGMA_SUMMATION_LIMIT = 3.0 BLOCK_INDEX_HEADER_WORDS = 3 diff --git a/spynnaker/pyNN/utilities/extracted_data.py b/spynnaker/pyNN/utilities/extracted_data.py index 9252403c1fd..7ca817c9e9a 100644 --- a/spynnaker/pyNN/utilities/extracted_data.py +++ b/spynnaker/pyNN/utilities/extracted_data.py @@ -50,6 +50,5 @@ def set(self, projection, attribute, data): :type attribute: list(int) or tuple(int) or None :param ConnectionHolder data: attribute data in a connection holder - :rtype: None """ self.__data[projection][attribute] = data diff --git a/spynnaker/pyNN/utilities/random_stats/abstract_random_stats.py b/spynnaker/pyNN/utilities/random_stats/abstract_random_stats.py index 154e4b804ef..a93382bd329 100644 --- a/spynnaker/pyNN/utilities/random_stats/abstract_random_stats.py +++ b/spynnaker/pyNN/utilities/random_stats/abstract_random_stats.py @@ -50,12 +50,12 @@ def var(self, dist): @abstractmethod def high(self, dist): - """ Return the high cutoff value of the distribution, or None if the\ + """ Return the high cutoff value of the distribution, or `None` if the\ distribution is unbounded """ @abstractmethod def low(self, dist): - """ Return the low cutoff value of the distribution, or None if the\ + """ Return the low cutoff value of the distribution, or `None` if the\ distribution is unbounded """ diff --git a/spynnaker/pyNN/utilities/struct.py b/spynnaker/pyNN/utilities/struct.py index 567047fb80a..81bd4cf6687 100644 --- a/spynnaker/pyNN/utilities/struct.py +++ b/spynnaker/pyNN/utilities/struct.py @@ -65,7 +65,8 @@ def get_size_in_whole_words(self, array_size=1): return (size_in_bytes + (BYTES_PER_WORD - 1)) // BYTES_PER_WORD def get_data(self, values, offset=0, array_size=1): - """ Get a numpy array of uint32 of data for the given values + """ Get a numpy array of uint32 of data for the given values. \ + The array will be suitable for being written to SpiNNaker. :param values: A list of values with length the same size as the number of fields diff --git a/spynnaker/pyNN/utilities/utility_calls.py b/spynnaker/pyNN/utilities/utility_calls.py index 4cc881fffa8..53b3428b419 100644 --- a/spynnaker/pyNN/utilities/utility_calls.py +++ b/spynnaker/pyNN/utilities/utility_calls.py @@ -14,11 +14,10 @@ # along with this program. If not, see . """ -utility class containing simple helper methods +Utility package containing simple helper functions. """ -import os -import logging import math +import os import numpy from pyNN.random import RandomDistribution from scipy.stats import binom @@ -30,7 +29,6 @@ RandomStatsPoissonImpl, RandomStatsRandIntImpl, RandomStatsUniformImpl, RandomStatsVonmisesImpl, RandomStatsBinomialImpl) - MAX_RATE = 2 ** 32 - 1 # To allow a unit32_t to be used to store the rate STATS_BY_NAME = { @@ -46,8 +44,6 @@ 'randint': RandomStatsRandIntImpl(), 'vonmises': RandomStatsVonmisesImpl()} -logger = logging.getLogger(__name__) - def check_directory_exists_and_create_if_not(filename): """ Create a parent directory for a file if it doesn't exist @@ -69,7 +65,6 @@ def convert_param_to_numpy(param, no_atoms): :return: the converted param as an array of floats :rtype: ~numpy.ndarray(float) """ - # Deal with random distributions by generating values if isinstance(param, RandomDistribution): @@ -205,6 +200,8 @@ def get_probable_maximum_selected( """ Get the likely maximum number of items that will be selected from a\ set of n_trials from a total set of n_total_trials\ with a probability of selection of selection_prob + + :rtype: float """ prob = 1.0 - (chance / float(n_total_trials)) return binom.ppf(prob, n_trials, selection_prob) @@ -213,6 +210,8 @@ def get_probable_maximum_selected( def get_probability_within_range(dist, lower, upper): """ Get the probability that a value will fall within the given range for\ a given RandomDistribution + + :rtype: float """ stats = STATS_BY_NAME[dist.name] return (stats.cdf(dist, upper) - stats.cdf(dist, lower)) @@ -221,6 +220,8 @@ def get_probability_within_range(dist, lower, upper): def get_maximum_probable_value(dist, n_items, chance=(1.0 / 100.0)): """ Get the likely maximum value of a RandomDistribution given a\ number of draws + + :rtype: float """ stats = STATS_BY_NAME[dist.name] prob = 1.0 - (chance / float(n_items)) @@ -230,6 +231,8 @@ def get_maximum_probable_value(dist, n_items, chance=(1.0 / 100.0)): def get_minimum_probable_value(dist, n_items, chance=(1.0 / 100.0)): """ Get the likely minimum value of a RandomDistribution given a\ number of draws + + :rtype: float """ stats = STATS_BY_NAME[dist.name] prob = chance / float(n_items) @@ -238,6 +241,8 @@ def get_minimum_probable_value(dist, n_items, chance=(1.0 / 100.0)): def get_mean(dist): """ Get the mean of a RandomDistribution + + :rtype: float """ stats = STATS_BY_NAME[dist.name] return stats.mean(dist) @@ -245,6 +250,8 @@ def get_mean(dist): def get_standard_deviation(dist): """ Get the standard deviation of a RandomDistribution + + :rtype: float """ stats = STATS_BY_NAME[dist.name] return stats.std(dist) @@ -252,6 +259,8 @@ def get_standard_deviation(dist): def get_variance(dist): """ Get the variance of a RandomDistribution + + :rtype: float """ stats = STATS_BY_NAME[dist.name] return stats.var(dist) @@ -261,6 +270,8 @@ def high(dist): """ Gets the high or max boundary value for this distribution Could return None + + :rtype: float or None """ stats = STATS_BY_NAME[dist.name] return stats.high(dist) @@ -270,6 +281,8 @@ def low(dist): """ Gets the high or min boundary value for this distribution Could return None + + :rtype: float or None """ stats = STATS_BY_NAME[dist.name] return stats.low(dist) @@ -287,8 +300,17 @@ def validate_mars_kiss_64_seed(seed): return seed +def round_up(value): + """ Rounds a value up to the nearest integer. + + :param float value: + :rtype: int + """ + return int(math.ceil(value)) + + def get_n_bits(n_values): - """ Determine how many bits are required for the given number of values + """ Determine how many bits are required for the given number of values. :param int n_values: the number of values (starting at 0) :return: the number of bits required to express that many values @@ -298,4 +320,13 @@ def get_n_bits(n_values): return 0 if n_values == 1: return 1 - return int(math.ceil(math.log(n_values, 2))) + return (n_values - 1).bit_length() + + +def ceildiv(x, y): + """ Divide `x` by `y` and round *up*. + + :rtype: int + """ + d, m = divmod(x, y) + return int(d) + (m != 0) diff --git a/unittests/mocks.py b/unittests/mocks.py index 299d83d3179..230e347b587 100644 --- a/unittests/mocks.py +++ b/unittests/mocks.py @@ -15,7 +15,8 @@ import configparser import numpy -from spinn_front_end_common.utilities import globals_variables +from spinn_front_end_common.utilities.globals_variables import ( + set_failed_state, set_simulator) from spynnaker.pyNN.utilities.spynnaker_failed_state import ( SpynnakerFailedState) from builtins import property @@ -132,8 +133,8 @@ def id_counter(self, value): @classmethod def setup(cls): simulator = MockSimulator() - globals_variables.set_failed_state(SpynnakerFailedState()) - globals_variables.set_simulator(simulator) + set_failed_state(SpynnakerFailedState()) + set_simulator(simulator) return simulator @property