Exemplo n.º 1
0
 def __add_entry(self, key_and_mask, core_mask, core_shift, n_neurons):
     if self.__n_addresses >= _MAX_ADDRESS_START:
         raise SynapticConfigurationException(
             "The table already contains {} entries;"
             " adding another is too many".format(self.__n_addresses))
     if key_and_mask.key not in self.__entries:
         entry = _MasterPopEntry(key_and_mask.key, key_and_mask.mask,
                                 core_mask, core_shift, n_neurons)
         self.__entries[key_and_mask.key] = entry
         # Need to add an extra "address" for the extra_info if needed
         if core_mask != 0:
             self.__n_addresses += 1
         return entry
     entry = self.__entries[key_and_mask.key]
     if (key_and_mask.mask != entry.mask or core_mask != entry.core_mask
             or core_shift != entry.core_shift
             or n_neurons != entry.n_neurons):
         raise SynapticConfigurationException(
             "Existing entry for key {} doesn't match one being added:"
             " Existing mask: {} core_mask: {} core_shift: {}"
             " n_neurons: {}"
             " Adding mask: {} core_mask: {} core_shift: {}"
             " n_neurons: {}".format(key_and_mask.key, entry.mask,
                                     entry.core_mask, entry.core_shift,
                                     entry.n_neurons, key_and_mask.mask,
                                     core_mask, core_shift, n_neurons))
     return entry
Exemplo n.º 2
0
    def merge(self, synapse_dynamics):
        # If the dynamics is structural, check if same as this
        if isinstance(synapse_dynamics, AbstractSynapseDynamicsStructural):
            if not self.is_same_as(synapse_dynamics):
                raise SynapticConfigurationException(
                    "Synapse dynamics must match exactly when using multiple"
                    " edges to the same population")

            # If structural part matches, return other as it might also be STDP
            return synapse_dynamics

        # If the dynamics is STDP but not Structural (as here), merge
        if isinstance(synapse_dynamics, SynapseDynamicsSTDP):
            return SynapseDynamicsStructuralSTDP(
                self.partner_selection, self.formation,
                self.elimination,
                synapse_dynamics.timing_dependence,
                synapse_dynamics.weight_dependence,
                # voltage dependence is not supported
                None, synapse_dynamics.dendritic_delay_fraction,
                self.f_rew, self.initial_weight, self.initial_delay,
                self.s_max, self.seed,
                backprop_delay=synapse_dynamics.backprop_delay)

        # Otherwise, it is static, so return ourselves
        return self
 def merge(self, synapse_dynamics):
     # This must replace something that supports neuromodulation,
     # so it can't be the first thing to be merged!
     raise SynapticConfigurationException(
         "Neuromodulation synapses can only be added where an existing"
         " projection has already been added which supports"
         " neuromodulation")
Exemplo n.º 4
0
    def __call__(self, report_folder, connection_holder, dsg_targets):
        """ Convert synaptic matrix for every application edge.
        """

        # Update the print options to display everything
        print_opts = numpy.get_printoptions()
        numpy.set_printoptions(threshold=numpy.nan)

        if dsg_targets is None:
            raise SynapticConfigurationException(
                "dsg_targets should not be none, used as a check for "
                "connection holder data to be generated")

        # generate folder for synaptic reports
        top_level_folder = os.path.join(report_folder, _DIRNAME)
        if not os.path.exists(top_level_folder):
            os.mkdir(top_level_folder)

        # create progress bar
        progress = ProgressBar(connection_holder.keys(),
                               "Generating synaptic matrix reports")

        # for each application edge, write matrix in new file
        for edge, _ in progress.over(connection_holder.keys()):
            # only write matrix's for edges which have matrix's
            if isinstance(edge, ProjectionApplicationEdge):
                # figure new file name
                file_name = os.path.join(top_level_folder,
                                         _TMPL_FILENAME.format(edge.label))
                self._write_file(file_name, connection_holder, edge)

        # Reset the print options
        numpy.set_printoptions(**print_opts)
Exemplo n.º 5
0
    def merge(self, synapse_dynamics):
        # If dynamics is STDP, test if same as
        if isinstance(synapse_dynamics, SynapseDynamicsSTDP):
            if not self.is_same_as(synapse_dynamics):
                raise SynapticConfigurationException(
                    "Synapse dynamics must match exactly when using multiple"
                    " edges to the same population")

            # If STDP part matches, return the other, as it might also be
            # structural
            return synapse_dynamics

        # If dynamics is structural but not STDP (as here), merge
        # NOTE: Import here as otherwise we get a circular dependency
        from .synapse_dynamics_structural_stdp import (
            SynapseDynamicsStructuralSTDP)
        if isinstance(synapse_dynamics, AbstractSynapseDynamicsStructural):
            return SynapseDynamicsStructuralSTDP(
                synapse_dynamics.partner_selection,
                synapse_dynamics.formation,
                synapse_dynamics.elimination,
                self.timing_dependence,
                self.weight_dependence,
                # voltage dependence is not supported
                None,
                self.dendritic_delay_fraction,
                synapse_dynamics.f_rew,
                synapse_dynamics.initial_weight,
                synapse_dynamics.initial_delay,
                synapse_dynamics.s_max,
                synapse_dynamics.seed,
                backprop_delay=self.backprop_delay)

        # Otherwise, it is static, so return ourselves
        return self
Exemplo n.º 6
0
 def merge_neuromodulation(self, neuromodulation):
     if self.__neuromodulation is None:
         self.__neuromodulation = neuromodulation
     elif not self.__neuromodulation.is_neuromodulation_same_as(
             neuromodulation):
         raise SynapticConfigurationException(
             "Neuromodulation must match exactly when using multiple"
             " edges to the same Population")
Exemplo n.º 7
0
    def merge(self, synapse_dynamics):
        # If other is structural, check structural matches
        if isinstance(synapse_dynamics, AbstractSynapseDynamicsStructural):
            if not SynapseDynamicsStructuralCommon.is_same_as(
                    self, synapse_dynamics):
                raise SynapticConfigurationException(
                    "Synapse dynamics must match exactly when using multiple"
                    " edges to the same population")
        # If other is STDP, check STDP matches
        if isinstance(synapse_dynamics, SynapseDynamicsSTDP):
            if not SynapseDynamicsSTDP.is_same_as(self, synapse_dynamics):
                raise SynapticConfigurationException(
                    "Synapse dynamics must match exactly when using multiple"
                    " edges to the same population")

        # If everything matches, return ourselves as supreme!
        return self
Exemplo n.º 8
0
    def __call__(self, report_folder, connection_holder, dsg_targets):
        """ converts synaptic matrix for every application edge.
        """

        # Update the print options to display everything
        import numpy
        print_opts = numpy.get_printoptions()
        numpy.set_printoptions(threshold=numpy.nan)

        if dsg_targets is None:
            raise SynapticConfigurationException(
                "dsg targets should not be none, used as a check for "
                "connection holder data to be generated")

        # generate folder for synaptic reports
        top_level_folder = os.path.join(report_folder,
                                        "synaptic_matrix_reports")
        if not os.path.exists(top_level_folder):
            os.mkdir(top_level_folder)

        # create progress bar
        progress = ProgressBar(len(connection_holder.keys()),
                               "Generating synaptic matrix reports")

        # for each application edge, write matrix in new file
        for application_edge, _ in connection_holder.keys():

            # only write matrix's for edges which have matrix's
            if isinstance(application_edge, ProjectionApplicationEdge):

                # figure new file name
                file_name = os.path.join(
                    top_level_folder,
                    "synaptic_matrix_for_application_edge_{}".format(
                        application_edge.label))

                # open writer
                output = None
                try:
                    output = open(file_name, "w")
                except IOError:
                    logger.error("Generate_placement_reports: Can't open file"
                                 " {} for writing.".format(file_name))

                # write all data for all synapse_information's in same file
                for info in application_edge.synapse_information:
                    this_connection_holder = connection_holder[(
                        application_edge, info)]
                    output.write("{}".format(this_connection_holder))
                output.flush()
                output.close()

            progress.update()
        progress.end()

        # Reset the print options
        numpy.set_printoptions(**print_opts)
Exemplo n.º 9
0
    def merge(self, synapse_dynamics):
        # Neuromodulation shouldn't be used without STDP
        if isinstance(synapse_dynamics, SynapseDynamicsNeuromodulation):
            raise SynapticConfigurationException(
                "Neuromodulation can only be added when an STDP projection"
                " has already been added")

        # We can always override a static synapse dynamics with a more
        # complex model
        return synapse_dynamics
    def set_sdram_partition(self, sdram_partition):
        """ Set the SDRAM partition.  Must only be called once per instance

        :param ~pacman.model.graphs.machine\
                .SourceSegmentedSDRAMMachinePartition sdram_partition:
            The SDRAM partition to receive synapses from
        """
        if self.__sdram_partition is not None:
            raise SynapticConfigurationException(
                "Trying to set SDRAM partition more than once")
        self.__sdram_partition = sdram_partition
Exemplo n.º 11
0
 def add_synapse_information(self, synapse_information):
     """
     :param SynapseInformation synapse_information:
     """
     dynamics = synapse_information.synapse_dynamics
     is_neuromodulation = are_dynamics_neuromodulation(dynamics)
     if is_neuromodulation != self.__is_neuromodulation:
         raise SynapticConfigurationException(
             "Cannot mix neuromodulated and non-neuromodulated synapses"
             f" between the same source Population {self._pre_vertex} and"
             f" target Population {self._post_vertex}")
     self.__synapse_information.append(synapse_information)
Exemplo n.º 12
0
 def get_next_allowed_address(self, next_address):
     """
     :param next_address: The next address that would be used
     :return: The next address that can be used following next_address
     """
     next_address = ((next_address + (self.ADDRESS_SCALE - 1)) //
                     self.ADDRESS_SCALE) * self.ADDRESS_SCALE
     if (next_address / self.ADDRESS_SCALE) > 0x7FFFFF:
         raise SynapticConfigurationException(
             "Address {} is out of range for this population table!".format(
                 hex(next_address)))
     return next_address
Exemplo n.º 13
0
    def __update_master_population_table(self, block_start_addr, row_length,
                                         key_and_mask, core_mask, core_shift,
                                         n_neurons, is_single):
        """ Add an entry in the binary search to deal with the synaptic matrix

        :param int block_start_addr: where the synaptic matrix block starts
        :param int row_length: how long in words each row is
        :param ~pacman.model.routing_info.BaseKeyAndMask key_and_mask:
            the key and mask for this master pop entry
        :param int core_mask:
            Mask for the part of the key that identifies the core
        :param int core_shift: The shift of the mask to get to the core_mask
        :param int n_neurons:
            The number of neurons in each machine vertex (bar the last)
        :param bool is_single:
            Flag that states if the entry is a direct entry for a single row.
        :return: The index of the entry, to be used to retrieve it
        :rtype: int
        :raises ~spynnaker.pyNN.exceptions.SynapticConfigurationException:
            If a bad address is used.
        """
        # if not single, scale the address
        start_addr = block_start_addr
        if not is_single:
            if block_start_addr % _ADDRESS_SCALE != 0:
                raise SynapticConfigurationException(
                    "Address {} is not compatible with this table".format(
                        block_start_addr))
            start_addr = block_start_addr // _ADDRESS_SCALE
            if start_addr > _MAX_ADDRESS:
                raise SynapticConfigurationException(
                    "Address {} is too big for this table".format(
                        block_start_addr))
        row_length = self.get_allowed_row_length(row_length)

        entry = self.__add_entry(key_and_mask, core_mask, core_shift,
                                 n_neurons)
        index = entry.append(start_addr, row_length - 1, is_single)
        self.__n_addresses += 1
        return index
Exemplo n.º 14
0
    def add_application_entry(self,
                              block_start_addr,
                              row_length,
                              key_and_mask,
                              core_mask,
                              core_shift,
                              n_neurons,
                              address_type=POP_TABLE_ADDRESS_TYPES.SDRAM):
        """ Add an entry for an application-edge to the population table.

        :param int block_start_addr: where the synaptic matrix block starts
        :param int row_length: how long in words each row is
        :param ~pacman.model.routing_info.BaseKeyAndMask key_and_mask:
            the key and mask for this master pop entry
        :param int core_mask:
            Mask for the part of the key that identifies the core
        :param int core_shift: The shift of the mask to get to the core_mask
        :param int n_neurons:
            The number of neurons in each machine vertex (bar the last)
        :return: The index of the entry, to be used to retrieve it
        :rtype: int
        :raises ~spynnaker.pyNN.exceptions.SynapticConfigurationException:
            If a bad address is used.
        """
        # If there are too many neurons per core, fail
        if n_neurons > _MAX_N_NEURONS:
            raise SynapticConfigurationException(
                "The parameter n_neurons of {} is too big (maximum {})".format(
                    n_neurons, _MAX_N_NEURONS))

        # If the core mask is too big, fail
        if core_mask > _MAX_CORE_MASK:
            raise SynapticConfigurationException(
                "The core mask of {} is too big (maximum {})".format(
                    core_mask, _MAX_CORE_MASK))

        return self.__update_master_population_table(block_start_addr,
                                                     row_length, key_and_mask,
                                                     core_mask, core_shift,
                                                     n_neurons, address_type)
Exemplo n.º 15
0
    def merge(self, synapse_dynamics):
        # If dynamics is Neuromodulation, merge with other neuromodulation,
        # and then return ourselves, as neuromodulation can't be used by
        # itself
        if isinstance(synapse_dynamics, SynapseDynamicsNeuromodulation):
            super().merge_neuromodulation(synapse_dynamics)
            return self
        # If other is structural, check structural matches
        if isinstance(synapse_dynamics, AbstractSynapseDynamicsStructural):
            if not SynapseDynamicsStructuralCommon.is_same_as(
                    self, synapse_dynamics):
                raise SynapticConfigurationException(
                    "Synapse dynamics must match exactly when using multiple"
                    " edges to the same population")
        # If other is STDP, check STDP matches
        if isinstance(synapse_dynamics, SynapseDynamicsSTDP):
            if not SynapseDynamicsSTDP.is_same_as(self, synapse_dynamics):
                raise SynapticConfigurationException(
                    "Synapse dynamics must match exactly when using multiple"
                    " edges to the same population")

        # If everything matches, return ourselves as supreme!
        return self
Exemplo n.º 16
0
    def get_next_allowed_address(next_address):
        """ Get the next allowed address.

        :param int next_address: The next address that would be used
        :return: The next address that can be used following next_address
        :rtype: int
        :raises ~spynnaker.pyNN.exceptions.SynapticConfigurationException:
            if the address is out of range
        """
        addr_scaled = (next_address + (_ADDRESS_SCALE - 1)) // _ADDRESS_SCALE
        if addr_scaled > _MAX_ADDRESS:
            raise SynapticConfigurationException(
                "Address {} is out of range for this population table!".format(
                    hex(addr_scaled * _ADDRESS_SCALE)))
        return addr_scaled * _ADDRESS_SCALE
Exemplo n.º 17
0
    def add_invalid_application_entry(self,
                                      key_and_mask,
                                      core_mask=0,
                                      core_shift=0,
                                      n_neurons=0):
        """ Add an entry to the table from an application vertex that doesn't
            point to anywhere.  Used to keep indices in synchronisation between
            e.g. normal and delay entries and between entries on different
            cores.

        :param ~pacman.model.routing_info.BaseKeyAndMask key_and_mask:
            a key_and_mask object used as part of describing
            an edge that will require being received to be stored in the
            master pop table; the whole edge will become multiple calls to
            this function
        :param int core_mask:
            Mask for the part of the key that identifies the core
        :param int core_shift: The shift of the mask to get to the core_mask
        :param int n_neurons:
            The number of neurons in each machine vertex (bar the last)
        :return: The index of the added entry
        :rtype: int
        """
        # If there are too many neurons per core, fail
        if n_neurons > _MAX_N_NEURONS:
            raise SynapticConfigurationException(
                "The parameter n_neurons of {} is too big (maximum {})".format(
                    n_neurons, _MAX_N_NEURONS))

        # If the core mask is too big, fail
        if core_mask > _MAX_CORE_MASK:
            raise SynapticConfigurationException(
                "The core mask of {} is too big (maximum {})".format(
                    core_mask, _MAX_CORE_MASK))
        return self.__add_invalid_entry(key_and_mask, core_mask, core_shift,
                                        n_neurons)
Exemplo n.º 18
0
    def synapse_dynamics(self, synapse_dynamics):

        # We can always override static dynamics or None
        if isinstance(self._synapse_dynamics, SynapseDynamicsStatic):
            self._synapse_dynamics = synapse_dynamics

        # We can ignore a static dynamics trying to overwrite a plastic one
        elif isinstance(synapse_dynamics, SynapseDynamicsStatic):
            pass

        # Otherwise, the dynamics must be equal
        elif not synapse_dynamics.is_same_as(self._synapse_dynamics):
            raise SynapticConfigurationException(
                "Synapse dynamics must match exactly when using multiple edges"
                "to the same population")
Exemplo n.º 19
0
 def get_next_allowed_address(self, next_address):
     """
     :param int next_address: The next address that would be used
     :return: The next address that can be used following next_address
     :rtype: int
     :raises SynapticConfigurationException: if the address is out of range
     """
     # How far is the address past an acceptable boundary?
     over = next_address % _ADDRESS_SCALE
     if over:
         next_address += _ADDRESS_SCALE - over
     if next_address // _ADDRESS_SCALE > self.TOP_MEMORY_POINT:
         raise SynapticConfigurationException(
             "Address {} is out of range for this population table!".format(
                 hex(next_address)))
     return next_address
Exemplo n.º 20
0
    def append(self, address, row_length, is_single):
        """ Add a synaptic matrix pointer to the entry

        :param int address: The address of the synaptic matrix
        :param int row_length: The length of each row in the matrix
        :param bool is_single: True if the address is to the direct matrix
        :return: The index of the pointer within the entry
        :rtype: int
        """
        index = len(self.__addresses_and_row_lengths)
        if index > _MAX_ADDRESS_COUNT:
            raise SynapticConfigurationException(
                "{} connections for the same source key (maximum {})".format(
                    index, _MAX_ADDRESS_COUNT))
        self.__addresses_and_row_lengths.append(
            (address, row_length, is_single, True))
        return index
Exemplo n.º 21
0
    def __get_structural_edges(self, application_graph, app_vertex):
        structural_application_edges = list()
        for app_edge in application_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 found:
                            raise SynapticConfigurationException(
                                "Only one Projection between each pair of"
                                " Populations can use structural plasticity ")
                        found = True
                        structural_application_edges.append(
                            (app_edge, synapse_info))

        return structural_application_edges
Exemplo n.º 22
0
 def __get_structural_projections(self, incoming_projections):
     """
     :param list(Projection) incoming_projections:
         Projections to filter to structural only
     :rtype: list(Projection)
     """
     structural_projections = list()
     seen_app_edges = set()
     for proj in incoming_projections:
         app_edge = proj._projection_edge
         for synapse_info in app_edge.synapse_information:
             if isinstance(synapse_info.synapse_dynamics,
                           AbstractSynapseDynamicsStructural):
                 if app_edge in seen_app_edges:
                     raise SynapticConfigurationException(self.PAIR_ERROR)
                 else:
                     seen_app_edges.add(app_edge)
                     structural_projections.append(proj)
     return structural_projections
 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:
     :rtype: list(tuple(ProjectionApplicationEdge, SynapseInformation))
     """
     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 found:
                         raise SynapticConfigurationException(
                             "Only one Projection between each pair of "
                             "Populations can use structural plasticity")
                     found = True
                     structural_edges.append((app_edge, synapse_info))
     return structural_edges
    def __call__(self, connection_holder, dsg_targets):
        """ Convert synaptic matrix for every application edge.

        :param connection_holder: where the synaptic matrices are stored
            (possibly after retrieval from the machine)
        :type connection_holder:
            dict(tuple(ProjectionApplicationEdge, SynapseInformation),
            ConnectionHolder)
        :param dsg_targets: used to check if connection holders are populated
        """

        # Update the print options to display everything
        print_opts = numpy.get_printoptions()
        numpy.set_printoptions(threshold=numpy.nan)

        if dsg_targets is None:
            raise SynapticConfigurationException(
                "dsg_targets should not be none, used as a check for "
                "connection holder data to be generated")

        # generate folder for synaptic reports
        top_level_folder = os.path.join(report_default_directory(), _DIRNAME)
        if not os.path.exists(top_level_folder):
            os.mkdir(top_level_folder)

        # create progress bar
        progress = ProgressBar(connection_holder.keys(),
                               "Generating synaptic matrix reports")

        # for each application edge, write matrix in new file
        for edge, _ in progress.over(connection_holder.keys()):
            # only write matrix's for edges which have matrix's
            if isinstance(edge, ProjectionApplicationEdge):
                # figure new file name
                file_name = os.path.join(top_level_folder,
                                         _TMPL_FILENAME.format(edge.label))
                self._write_file(file_name, connection_holder, edge)

        # Reset the print options
        numpy.set_printoptions(**print_opts)
    def __init__(self,
                 weight=StaticSynapse.default_parameters['weight'],
                 tau_c=1000.0,
                 tau_d=200.0,
                 w_min=0.0,
                 w_max=1.0):
        self.__weight = weight
        self.__tau_c = tau_c
        self.__tau_d = tau_d
        ts = get_simulator().machine_time_step / 1000.0
        self.__tau_c_data = get_exp_lut_array(ts,
                                              self.__tau_c,
                                              shift=LOOKUP_TAU_C_SHIFT)
        self.__tau_d_data = get_exp_lut_array(ts,
                                              self.__tau_d,
                                              shift=LOOKUP_TAU_D_SHIFT)
        self.__w_min = w_min
        self.__w_max = w_max

        if w_min < 0 or w_max < 0:
            raise SynapticConfigurationException(
                "Minimum and maximum weights must be >= 0")
    def create_machine_vertices(self, resource_tracker, machine_graph):
        app_vertex = self._governed_app_vertex
        label = app_vertex.label
        constraints = get_remaining_constraints(app_vertex)

        # Structural plasticity can only be run on a single synapse core
        if (isinstance(app_vertex.synapse_dynamics,
                       AbstractSynapseDynamicsStructural)
                and self.__n_synapse_vertices != 1):
            raise SynapticConfigurationException(
                "The current implementation of structural plasticity can only"
                " be run on a single synapse core.  Please ensure the number"
                " of synapse cores is set to 1")

        # Do some checks to make sure everything is likely to fit
        atoms_per_core = min(app_vertex.get_max_atoms_per_core(),
                             app_vertex.n_atoms)
        n_synapse_types = app_vertex.neuron_impl.get_n_synapse_types()
        if (get_n_bits(atoms_per_core) + get_n_bits(n_synapse_types) +
                get_n_bits(self.__get_max_delay)) > MAX_RING_BUFFER_BITS:
            raise SynapticConfigurationException(
                "The combination of the number of neurons per core ({}), "
                "the number of synapse types ({}), and the maximum delay per "
                "core ({}) will require too much DTCM.  Please reduce one or "
                "more of these values.".format(atoms_per_core, n_synapse_types,
                                               self.__get_max_delay))

        self.__neuron_vertices = list()
        self.__synapse_vertices = list()
        self.__synapse_verts_by_neuron = defaultdict(list)

        incoming_direct_poisson = self.__handle_poisson_sources(
            label, machine_graph)

        # Work out the ring buffer shifts based on all incoming things
        rb_shifts = app_vertex.get_ring_buffer_shifts(
            app_vertex.incoming_projections)
        weight_scales = app_vertex.get_weight_scales(rb_shifts)

        # Get resources for synapses
        independent_synapse_sdram = self.__independent_synapse_sdram()
        proj_dependent_sdram = self.__proj_dependent_synapse_sdram(
            app_vertex.incoming_projections)

        for index, vertex_slice in enumerate(self.__get_fixed_slices()):

            # Find the maximum number of cores on any chip available
            max_crs = resource_tracker.get_maximum_cores_available_on_a_chip()
            if max_crs < (self.__n_synapse_vertices + 1):
                raise ConfigurationException(
                    "No chips remaining with enough cores for"
                    f" {self.__n_synapse_vertices} synapse cores and a neuron"
                    " core")
            max_crs -= self.__n_synapse_vertices + 1

            # Create the neuron vertex for the slice
            neuron_vertex, neuron_resources = self.__add_neuron_core(
                vertex_slice, label, index, rb_shifts, weight_scales,
                machine_graph, constraints)

            # Keep track of synapse vertices for each neuron vertex and
            # resources used by each core (neuron core is added later)
            synapse_vertices = list()
            self.__synapse_verts_by_neuron[neuron_vertex] = synapse_vertices
            all_resources = []

            # Add the first vertex
            synapse_references, syn_label = self.__add_lead_synapse_core(
                vertex_slice, independent_synapse_sdram, proj_dependent_sdram,
                label, rb_shifts, weight_scales, all_resources, machine_graph,
                synapse_vertices, neuron_vertex, constraints)

            # Do the remaining synapse cores
            for i in range(1, self.__n_synapse_vertices):
                self.__add_shared_synapse_core(syn_label, i, vertex_slice,
                                               synapse_references,
                                               all_resources, machine_graph,
                                               synapse_vertices, neuron_vertex,
                                               constraints)

            # Add resources for Poisson vertices up to core limit
            poisson_vertices = incoming_direct_poisson[vertex_slice]
            remaining_poisson_vertices = list()
            added_poisson_vertices = list()
            for poisson_vertex, poisson_edge in poisson_vertices:
                if max_crs <= 0:
                    remaining_poisson_vertices.append(poisson_vertex)
                    self.__add_poisson_multicast(poisson_vertex,
                                                 synapse_vertices,
                                                 machine_graph, poisson_edge)
                else:
                    all_resources.append(
                        (poisson_vertex.resources_required, []))
                    added_poisson_vertices.append(poisson_vertex)
                    max_crs -= 1

            if remaining_poisson_vertices:
                logger.warn(
                    f"Vertex {label} is using multicast for"
                    f" {len(remaining_poisson_vertices)} one-to-one Poisson"
                    " sources as not enough cores exist to put them on the"
                    " same chip")

            # Create an SDRAM edge partition
            sdram_label = "SDRAM {} Synapses-->Neurons:{}-{}".format(
                label, vertex_slice.lo_atom, vertex_slice.hi_atom)
            source_vertices = added_poisson_vertices + synapse_vertices
            sdram_partition = SourceSegmentedSDRAMMachinePartition(
                SYNAPSE_SDRAM_PARTITION_ID, sdram_label, source_vertices)
            machine_graph.add_outgoing_edge_partition(sdram_partition)
            neuron_vertex.set_sdram_partition(sdram_partition)

            # Add SDRAM edges for synapse vertices
            for source_vertex in source_vertices:
                edge_label = "SDRAM {}-->{}".format(source_vertex.label,
                                                    neuron_vertex.label)
                machine_graph.add_edge(
                    SDRAMMachineEdge(source_vertex, neuron_vertex, edge_label),
                    SYNAPSE_SDRAM_PARTITION_ID)
                source_vertex.set_sdram_partition(sdram_partition)

            # Add SDRAM edge requirements to the neuron SDRAM, as the resource
            # tracker will otherwise try to add another core for it
            extra_sdram = MultiRegionSDRAM()
            extra_sdram.merge(neuron_resources.sdram)
            extra_sdram.add_cost(
                len(extra_sdram.regions) + 1,
                sdram_partition.total_sdram_requirements())
            neuron_resources_plus = ResourceContainer(
                sdram=extra_sdram,
                dtcm=neuron_resources.dtcm,
                cpu_cycles=neuron_resources.cpu_cycles,
                iptags=neuron_resources.iptags,
                reverse_iptags=neuron_resources.reverse_iptags)
            all_resources.append((neuron_resources_plus, constraints))

            # Allocate all the resources to ensure they all fit
            resource_tracker.allocate_constrained_group_resources(
                all_resources)

        return True
 def set_delay(self, delay):
     if delay != 1:
         raise SynapticConfigurationException(
             "Neuromodulation delay must be 0")
 def sdram_requirement(self, sdram_machine_edge):
     if isinstance(sdram_machine_edge.pre_vertex,
                   SendsSynapticInputsOverSDRAM):
         return self.n_bytes_for_transfer
     raise SynapticConfigurationException(
         "Unknown pre vertex type in edge {}".format(sdram_machine_edge))