示例#1
0
 def _addDelayExtension(self, numSrcNeurons, max_delay_for_projection,
         max_delay_per_neuron, original_connector, original_synapse_list, 
         presynaptic_population, postsynaptic_population, label,
         synapse_dynamics):
     """
     Instantiate new delay extension component, connecting a new edge from 
     the source vertex to it and new edges from it to the target (given
     by numBlocks). 
     The outgoing edges cover each required block of delays, in groups of 
     MAX_DELAYS_PER_NEURON delay slots (currently 16).
     """
     global controller
     # If there are any connections with a delay of less than the maximum,
     # create a direct connection between the two populations only containing
     # these connections
     direct_synaptic_sublist = original_synapse_list.create_delay_sublist(
         0, max_delay_per_neuron)
     if (direct_synaptic_sublist.get_max_n_connections() != 0):
         direct_edge = ProjectionEdge(presynaptic_population.vertex,
                 postsynaptic_population.vertex, 
                 controller.dao.machineTimeStep,
                 synapse_list=direct_synaptic_sublist, label=label)
         controller.add_edge(direct_edge)
         self.projection_edge = direct_edge
     
     # Create a delay extension vertex to do the extra delays
     self.delay_vertex = presynaptic_population.vertex.delay_vertex
     if self.delay_vertex is None:
         sourceName = presynaptic_population.vertex.label
         delayName = "%s_delayed" % (sourceName)
         self.delay_vertex = DelayExtension(numSrcNeurons,
             max_delay_per_neuron, label = delayName)
         presynaptic_population.vertex.delay_vertex = self.delay_vertex
         #controller.add_vertex(self.delay_vertex)
     
     # Create a connection from the source population to the delay vertex
     new_label = "%s_to_DE" % (label)
     remaining_edge = DelayAfferentEdge(presynaptic_population.vertex,
             self.delay_vertex, label=new_label)
     controller.add_edge(remaining_edge)
     
     # Create a list of the connections with delay larger than that which 
     # can be handled by the neuron itself
     remaining_synaptic_sublist = original_synapse_list.create_delay_sublist(
             max_delay_per_neuron, max_delay_for_projection)
     
     
     # Create a special DelayEdge from the delay vertex to the outgoing
     # population, with the same set of connections
     delay_label = "DE to %s" % (label)
     num_blocks = int(math.ceil(float(max_delay_for_projection)
                              / float(max_delay_per_neuron))) - 1
     self.delay_edge = DelayProjectionEdge(self.delay_vertex, 
             postsynaptic_population.vertex, controller.dao.machineTimeStep,
             num_blocks, max_delay_per_neuron,
             synapse_list = remaining_synaptic_sublist,
             synapse_dynamics=synapse_dynamics, label=delay_label)
     controller.add_edge(self.delay_edge)
示例#2
0
    def _addDelayExtension(self, numSrcNeurons, max_delay_for_projection,
                           max_delay_per_neuron, original_connector,
                           original_synapse_list, presynaptic_population,
                           postsynaptic_population, label, synapse_dynamics):
        """
        Instantiate new delay extension component, connecting a new edge from 
        the source vertex to it and new edges from it to the target (given
        by numBlocks). 
        The outgoing edges cover each required block of delays, in groups of 
        MAX_DELAYS_PER_NEURON delay slots (currently 16).
        """
        global controller
        # If there are any connections with a delay of less than the maximum,
        # create a direct connection between the two populations only containing
        # these connections
        direct_synaptic_sublist = original_synapse_list.create_delay_sublist(
            0, max_delay_per_neuron)
        if (direct_synaptic_sublist.get_max_n_connections() != 0):
            direct_edge = ProjectionEdge(presynaptic_population.vertex,
                                         postsynaptic_population.vertex,
                                         controller.dao.machineTimeStep,
                                         synapse_list=direct_synaptic_sublist,
                                         label=label)
            controller.add_edge(direct_edge)
            self.projection_edge = direct_edge

        # Create a delay extension vertex to do the extra delays
        self.delay_vertex = presynaptic_population.vertex.delay_vertex
        if self.delay_vertex is None:
            sourceName = presynaptic_population.vertex.label
            delayName = "%s_delayed" % (sourceName)
            self.delay_vertex = DelayExtension(numSrcNeurons,
                                               max_delay_per_neuron,
                                               label=delayName)
            presynaptic_population.vertex.delay_vertex = self.delay_vertex
            #controller.add_vertex(self.delay_vertex)

        # Create a connection from the source population to the delay vertex
        new_label = "%s_to_DE" % (label)
        remaining_edge = DelayAfferentEdge(presynaptic_population.vertex,
                                           self.delay_vertex,
                                           label=new_label)
        controller.add_edge(remaining_edge)

        # Create a list of the connections with delay larger than that which
        # can be handled by the neuron itself
        remaining_synaptic_sublist = original_synapse_list.create_delay_sublist(
            max_delay_per_neuron, max_delay_for_projection)

        # Create a special DelayEdge from the delay vertex to the outgoing
        # population, with the same set of connections
        delay_label = "DE to %s" % (label)
        num_blocks = int(
            math.ceil(
                float(max_delay_for_projection) /
                float(max_delay_per_neuron))) - 1
        self.delay_edge = DelayProjectionEdge(
            self.delay_vertex,
            postsynaptic_population.vertex,
            controller.dao.machineTimeStep,
            num_blocks,
            max_delay_per_neuron,
            synapse_list=remaining_synaptic_sublist,
            synapse_dynamics=synapse_dynamics,
            label=delay_label)
        controller.add_edge(self.delay_edge)
示例#3
0
class Projection(object):
    """
    A container for all the connections of a given type (same synapse type
    and plasticity mechanisms) between two populations, together with methods to
    set parameters of those connections, including of plasticity mechanisms.

    :param `pacman103.front.pynn.Population` presynaptic_population:
        presynaptic Population for the Projection
    :param `pacman103.front.pynn.Population` postsynaptic_population:
        postsynaptic Population for the Projection
    :param `pacman103.front.pynn.connectors` method:
        an instance of the connection method and parameters for the Projection
    """
    def __init__(self,
                 presynaptic_population,
                 postsynaptic_population,
                 connector,
                 source=None,
                 target='excitatory',
                 synapse_dynamics=None,
                 label=None,
                 rng=None):
        """
        Instantiates a :py:object:`Projection`.
        """
        global controller
        self.projection_edge = None
        if issubclass(type(postsynaptic_population.vertex), PopulationVertex):
            # Check that the "target" is an acceptable value
            targets = postsynaptic_population.vertex.get_synapse_targets()
            if not target in targets:
                raise exceptions.PacmanException(
                    "Target {} is not available in " +
                    "the post-synaptic population (choices are {})".format(
                        target, targets))
            synapse_type = postsynaptic_population.vertex.get_synapse_id(
                target)
        else:
            raise exceptions.ConfigurationException(
                "postsynaptic_population is "
                "not a supposal reciever of"
                " synaptic projections")

        # Check that the edge doesn't already exist elsewhere
        # This would be a possible place for a merge at some point,
        # but this needs more thought
        for edge in controller.dao.get_edges():
            if (edge.prevertex == presynaptic_population.vertex
                    and edge.postvertex == postsynaptic_population.vertex):
                raise exceptions.PacmanException(
                    "More than one connection between the same pair of" +
                    " vertices is not currently supported")

        synapse_list = connector.generate_synapse_list(
            presynaptic_population.vertex, postsynaptic_population.vertex,
            1000.0 / controller.dao.machineTimeStep, synapse_type)
        self.read_synapse_list = None

        # If there are some negative weights
        if synapse_list.get_min_weight() < 0:

            # If there are mixed negative and positive weights,
            # raise an exception
            if synapse_list.get_max_weight() > 0:
                raise exceptions.PacmanException("Weights must be positive")

            # Otherwise, the weights are all negative, so invert them(!)
            else:
                synapse_list.flip()

        # check if all delays requested can fit into the natively supported
        # delays in the models
        min_delay, max_delay = synapse_list.get_min_max_delay()
        natively_supported_delay_for_models = MAX_SUPPORTED_DELAY_TICS

        delay_extention_max_supported_delay = \
            MAX_DELAY_BLOCKS * MAX_TIMER_TICS_SUPPORTED_PER_BLOCK

        if max_delay > (natively_supported_delay_for_models +
                        delay_extention_max_supported_delay):
            raise exceptions.ConfigurationException(
                "the max delay for projection {} is not "
                "supported by the pacman "
                "toolchain".format(max_delay))

        if conf.config.has_option("Model", "max_delay"):
            user_max_delay = conf.config.get("Model", "max_delay")
            if max_delay > user_max_delay:
                logger.warn(
                    "The end user entered a max delay to which the projection breaks"
                )

        if (max_delay > natively_supported_delay_for_models):
            source_sz = presynaptic_population.vertex.atoms
            self._addDelayExtension(source_sz, max_delay,
                                    natively_supported_delay_for_models,
                                    connector, synapse_list,
                                    presynaptic_population,
                                    postsynaptic_population, label,
                                    synapse_dynamics)

        else:
            self.projection_edge = ProjectionEdge(
                presynaptic_population.vertex,
                postsynaptic_population.vertex,
                controller.dao.machineTimeStep,
                synapse_list=synapse_list,
                synapse_dynamics=synapse_dynamics,
                label=label)
            self.delay_edge = None
            controller.add_edge(self.projection_edge)

    def _addDelayExtension(self, numSrcNeurons, max_delay_for_projection,
                           max_delay_per_neuron, original_connector,
                           original_synapse_list, presynaptic_population,
                           postsynaptic_population, label, synapse_dynamics):
        """
        Instantiate new delay extension component, connecting a new edge from 
        the source vertex to it and new edges from it to the target (given
        by numBlocks). 
        The outgoing edges cover each required block of delays, in groups of 
        MAX_DELAYS_PER_NEURON delay slots (currently 16).
        """
        global controller
        # If there are any connections with a delay of less than the maximum,
        # create a direct connection between the two populations only containing
        # these connections
        direct_synaptic_sublist = original_synapse_list.create_delay_sublist(
            0, max_delay_per_neuron)
        if (direct_synaptic_sublist.get_max_n_connections() != 0):
            direct_edge = ProjectionEdge(presynaptic_population.vertex,
                                         postsynaptic_population.vertex,
                                         controller.dao.machineTimeStep,
                                         synapse_list=direct_synaptic_sublist,
                                         label=label)
            controller.add_edge(direct_edge)
            self.projection_edge = direct_edge

        # Create a delay extension vertex to do the extra delays
        self.delay_vertex = presynaptic_population.vertex.delay_vertex
        if self.delay_vertex is None:
            sourceName = presynaptic_population.vertex.label
            delayName = "%s_delayed" % (sourceName)
            self.delay_vertex = DelayExtension(numSrcNeurons,
                                               max_delay_per_neuron,
                                               label=delayName)
            presynaptic_population.vertex.delay_vertex = self.delay_vertex
            #controller.add_vertex(self.delay_vertex)

        # Create a connection from the source population to the delay vertex
        new_label = "%s_to_DE" % (label)
        remaining_edge = DelayAfferentEdge(presynaptic_population.vertex,
                                           self.delay_vertex,
                                           label=new_label)
        controller.add_edge(remaining_edge)

        # Create a list of the connections with delay larger than that which
        # can be handled by the neuron itself
        remaining_synaptic_sublist = original_synapse_list.create_delay_sublist(
            max_delay_per_neuron, max_delay_for_projection)

        # Create a special DelayEdge from the delay vertex to the outgoing
        # population, with the same set of connections
        delay_label = "DE to %s" % (label)
        num_blocks = int(
            math.ceil(
                float(max_delay_for_projection) /
                float(max_delay_per_neuron))) - 1
        self.delay_edge = DelayProjectionEdge(
            self.delay_vertex,
            postsynaptic_population.vertex,
            controller.dao.machineTimeStep,
            num_blocks,
            max_delay_per_neuron,
            synapse_list=remaining_synaptic_sublist,
            synapse_dynamics=synapse_dynamics,
            label=delay_label)
        controller.add_edge(self.delay_edge)

    def describe(self, template='projection_default.txt', engine='default'):
        """
        Returns a human-readable description of the projection.

        The output may be customized by specifying a different template
        togther with an associated template engine (see ``pyNN.descriptions``).

        If template is None, then a dictionary containing the template context
        will be returned.
        """
        raise NotImplementedError

    def __getitem__(self, i):
        """Return the `i`th connection within the Projection."""
        raise NotImplementedError

    def _get_synaptic_data(self):
        if self.read_synapse_list is None:

            global controller
            synapse_list = None
            delay_synapse_list = None
            if self.projection_edge is not None:
                synapse_list = self.projection_edge.get_synaptic_data(
                    controller, MAX_SUPPORTED_DELAY_TICS)
            if self.delay_edge is not None:
                delay_synapse_list = self.delay_edge.get_synaptic_data(
                    controller, MAX_SUPPORTED_DELAY_TICS)

            # If there is both a delay and a non-delay list, merge them
            if synapse_list is not None and delay_synapse_list is not None:
                rows = synapse_list.get_rows()
                delay_rows = delay_synapse_list.get_rows()
                for i in range(len(rows)):
                    rows[i].append(delay_rows[i])
                self.read_synapse_list = synapse_list

            # If there is only a synapse list, return that
            elif synapse_list is not None:
                self.read_synapse_list = synapse_list

            # Otherwise return the delay list (there should be at least one!)
            else:
                self.read_synapse_list = delay_synapse_list

        return self.read_synapse_list

    def getDelays(self, format='list', gather=True):
        """
        Get synaptic delays for all connections in this Projection.

        Possible formats are: a list of length equal to the number of connections
        in the projection, a 2D delay array (with NaN for non-existent
        connections).
        """
        global controller
        timer = None
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer = Timer()
            timer.start_timing()
        synapse_list = self._get_synaptic_data()
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer.take_sample()

        if format == 'list':
            delays = list()
            for row in synapse_list.get_rows():
                delays.extend(row.delays *
                              (float(controller.dao.machineTimeStep) / 1000.0))
            return delays

        delays = numpy.zeros((self.projection_edge.prevertex.atoms,
                              self.projection_edge.postvertex.atoms))
        rows = synapse_list.get_rows()
        for pre_atom in range(len(rows)):
            row = rows[pre_atom]
            for i in len(row.target_indices):
                post_atom = row.target_indices[i]
                delay = (float(row.delays[i]) *
                         (float(controller.dao.machineTimeStep) / 1000.0))
                delays[pre_atom][post_atom] = delay
        return delays

    def getSynapseDynamics(self, parameter_name, format='list', gather=True):
        """
        Get parameters of the dynamic synapses for all connections in this
        Projection.
        """
        raise NotImplementedError

    def getWeights(self, format='list'):
        """
        Get synaptic weights for all connections in this Projection.
        (pyNN gather parameter not supported from the signiture
        getWeights(self, format='list', gather=True):)

        Possible formats are: a list of length equal to the number of connections
        in the projection, a 2D weight array (with NaN for non-existent
        connections). Note that for the array format, if there is more than
        one connection between two cells, the summed weight will be given.
        """
        timer = None
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer = Timer()
            timer.start_timing()
        synapse_list = self._get_synaptic_data()
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer.take_sample()

        if format == 'list':
            weights = list()
            for row in synapse_list.get_rows():
                weights.extend(row.weights)
            return weights
        elif format == 'array':
            weights = numpy.zeros((self.projection_edge.prevertex.atoms,
                                   self.projection_edge.postvertex.atoms))
            rows = synapse_list.get_rows()
        for pre_atom, row in enumerate(rows):
            for post_atom, weight in zip(row.target_indices, row.weights):
                weights[pre_atom][post_atom] = weight
        return weights

    def __len__(self):
        """Return the total number of local connections."""
        raise NotImplementedError

    def printDelays(self, file, format='list', gather=True):
        """
        Print synaptic weights to file. In the array format, zeros are printed
        for non-existent connections.
        """
        raise NotImplementedError

    def printWeights(self, file, format='list', gather=True):
        """
        Print synaptic weights to file. In the array format, zeros are printed
        for non-existent connections.
        """
        raise NotImplementedError

    def randomizeWeights(self, rand_distr):
        """
        Set weights to random values taken from rand_distr.
        """
        raise NotImplementedError

    def randomizeDelays(self, rand_distr):
        """
        Set delays to random values taken from rand_distr.
        """
        raise NotImplementedError

    def randomizeSynapseDynamics(self, param, rand_distr):
        """
        Set parameters of the synapse dynamics to values taken from rand_distr
        """
        raise NotImplementedError

    def __repr__(self):
        '''
        returns a string rep of the projection
        '''
        return "prjection {}".format(self.projection_edge.label)

    def saveConnections(self, file, gather=True, compatible_output=True):
        """
        Save connections to file in a format suitable for reading in with a
        FromFileConnector.
        """
        raise NotImplementedError

    def size(self, gather=True):
        """
        Return the total number of connections.
         - only local connections, if gather is False,
         - all connections, if gather is True (default)
        """
        raise NotImplementedError

    def setDelays(self, d):
        """
        d can be a single number, in which case all delays are set to this
        value, or a list/1D array of length equal to the number of connections
        in the projection, or a 2D array with the same dimensions as the
        connectivity matrix (as returned by `getDelays(format='array')`).
        """
        raise NotImplementedError

    def setSynapseDynamics(self, param, value):
        """
        Set parameters of the dynamic synapses for all connections in this
        projection.
        """
        raise NotImplementedError

    def setWeights(self, w):
        """
        w can be a single number, in which case all weights are set to this
        value, or a list/1D array of length equal to the number of connections
        in the projection, or a 2D array with the same dimensions as the
        connectivity matrix (as returned by `getWeights(format='array')`).
        Weights should be in nA for current-based and uS for conductance-based
        synapses.
        """
        raise NotImplementedError

    def weightHistogram(self, min=None, max=None, nbins=10):
        """
        Return a histogram of synaptic weights.
        If min and max are not given, the minimum and maximum weights are
        calculated automatically.
        """
        raise NotImplementedError
示例#4
0
class Projection(object):
    """
    A container for all the connections of a given type (same synapse type
    and plasticity mechanisms) between two populations, together with methods to
    set parameters of those connections, including of plasticity mechanisms.

    :param `pacman103.front.pynn.Population` presynaptic_population:
        presynaptic Population for the Projection
    :param `pacman103.front.pynn.Population` postsynaptic_population:
        postsynaptic Population for the Projection
    :param `pacman103.front.pynn.connectors` method:
        an instance of the connection method and parameters for the Projection
    """


    def __init__( self, presynaptic_population, postsynaptic_population,
        connector, source=None, target='excitatory', synapse_dynamics=None,
        label=None, rng=None):
        """
        Instantiates a :py:object:`Projection`.
        """
        global controller
        self.projection_edge = None
        if issubclass(type(postsynaptic_population.vertex), PopulationVertex):
            # Check that the "target" is an acceptable value
            targets = postsynaptic_population.vertex.get_synapse_targets()
            if not target in targets:
                raise exceptions.PacmanException("Target {} is not available in "
                        + "the post-synaptic population (choices are {})".format(
                                target, targets))
            synapse_type = postsynaptic_population.vertex.get_synapse_id(target)
        else:
            raise exceptions.ConfigurationException("postsynaptic_population is "
                                                    "not a supposal reciever of"
                                                    " synaptic projections")
        
        # Check that the edge doesn't already exist elsewhere
        # This would be a possible place for a merge at some point,
        # but this needs more thought
        for edge in controller.dao.get_edges():
            if (edge.prevertex == presynaptic_population.vertex and
                edge.postvertex == postsynaptic_population.vertex):
                    raise exceptions.PacmanException(
                            "More than one connection between the same pair of"
                            + " vertices is not currently supported")

        synapse_list = connector.generate_synapse_list(
                presynaptic_population.vertex, postsynaptic_population.vertex,
                1000.0 / controller.dao.machineTimeStep,
                synapse_type)
        self.read_synapse_list = None

        # If there are some negative weights
        if synapse_list.get_min_weight() < 0:
            
            # If there are mixed negative and positive weights, 
            # raise an exception
            if synapse_list.get_max_weight() > 0:
                raise exceptions.PacmanException("Weights must be positive")
            
            # Otherwise, the weights are all negative, so invert them(!)
            else:
                synapse_list.flip()
            
        # check if all delays requested can fit into the natively supported
        # delays in the models
        min_delay, max_delay = synapse_list.get_min_max_delay()
        natively_supported_delay_for_models = MAX_SUPPORTED_DELAY_TICS

        delay_extention_max_supported_delay = \
            MAX_DELAY_BLOCKS * MAX_TIMER_TICS_SUPPORTED_PER_BLOCK

        if max_delay > (natively_supported_delay_for_models 
                + delay_extention_max_supported_delay):
            raise exceptions.ConfigurationException("the max delay for projection {} is not "
                                                    "supported by the pacman "
                                                    "toolchain".format(max_delay))

        if conf.config.has_option("Model", "max_delay"):
            user_max_delay = conf.config.get("Model", "max_delay")
            if max_delay > user_max_delay:
                logger.warn("The end user entered a max delay to which the projection breaks")

        if (max_delay > natively_supported_delay_for_models):
            source_sz  = presynaptic_population.vertex.atoms
            self._addDelayExtension(source_sz, max_delay, natively_supported_delay_for_models,
                     connector, synapse_list, presynaptic_population,
                     postsynaptic_population, label, synapse_dynamics)

        else:
            self.projection_edge = ProjectionEdge(presynaptic_population.vertex,
                postsynaptic_population.vertex, controller.dao.machineTimeStep,
                synapse_list=synapse_list, synapse_dynamics=synapse_dynamics,
                label=label)
            self.delay_edge = None
            controller.add_edge(self.projection_edge)

    def _addDelayExtension(self, numSrcNeurons, max_delay_for_projection,
            max_delay_per_neuron, original_connector, original_synapse_list, 
            presynaptic_population, postsynaptic_population, label,
            synapse_dynamics):
        """
        Instantiate new delay extension component, connecting a new edge from 
        the source vertex to it and new edges from it to the target (given
        by numBlocks). 
        The outgoing edges cover each required block of delays, in groups of 
        MAX_DELAYS_PER_NEURON delay slots (currently 16).
        """
        global controller
        # If there are any connections with a delay of less than the maximum,
        # create a direct connection between the two populations only containing
        # these connections
        direct_synaptic_sublist = original_synapse_list.create_delay_sublist(
            0, max_delay_per_neuron)
        if (direct_synaptic_sublist.get_max_n_connections() != 0):
            direct_edge = ProjectionEdge(presynaptic_population.vertex,
                    postsynaptic_population.vertex, 
                    controller.dao.machineTimeStep,
                    synapse_list=direct_synaptic_sublist, label=label)
            controller.add_edge(direct_edge)
            self.projection_edge = direct_edge
        
        # Create a delay extension vertex to do the extra delays
        self.delay_vertex = presynaptic_population.vertex.delay_vertex
        if self.delay_vertex is None:
            sourceName = presynaptic_population.vertex.label
            delayName = "%s_delayed" % (sourceName)
            self.delay_vertex = DelayExtension(numSrcNeurons,
                max_delay_per_neuron, label = delayName)
            presynaptic_population.vertex.delay_vertex = self.delay_vertex
            #controller.add_vertex(self.delay_vertex)
        
        # Create a connection from the source population to the delay vertex
        new_label = "%s_to_DE" % (label)
        remaining_edge = DelayAfferentEdge(presynaptic_population.vertex,
                self.delay_vertex, label=new_label)
        controller.add_edge(remaining_edge)
        
        # Create a list of the connections with delay larger than that which 
        # can be handled by the neuron itself
        remaining_synaptic_sublist = original_synapse_list.create_delay_sublist(
                max_delay_per_neuron, max_delay_for_projection)
        
        
        # Create a special DelayEdge from the delay vertex to the outgoing
        # population, with the same set of connections
        delay_label = "DE to %s" % (label)
        num_blocks = int(math.ceil(float(max_delay_for_projection)
                                 / float(max_delay_per_neuron))) - 1
        self.delay_edge = DelayProjectionEdge(self.delay_vertex, 
                postsynaptic_population.vertex, controller.dao.machineTimeStep,
                num_blocks, max_delay_per_neuron,
                synapse_list = remaining_synaptic_sublist,
                synapse_dynamics=synapse_dynamics, label=delay_label)
        controller.add_edge(self.delay_edge)

    def describe(self, template='projection_default.txt', engine='default'):
        """
        Returns a human-readable description of the projection.

        The output may be customized by specifying a different template
        togther with an associated template engine (see ``pyNN.descriptions``).

        If template is None, then a dictionary containing the template context
        will be returned.
        """
        raise NotImplementedError

    def __getitem__(self, i):
        """Return the `i`th connection within the Projection."""
        raise NotImplementedError
    
    def _get_synaptic_data(self):
        if self.read_synapse_list is None:
        
            global controller
            synapse_list = None
            delay_synapse_list = None
            if self.projection_edge is not None:
                synapse_list = self.projection_edge.get_synaptic_data(
                        controller, MAX_SUPPORTED_DELAY_TICS)
            if self.delay_edge is not None:
                delay_synapse_list = self.delay_edge.get_synaptic_data(
                        controller, MAX_SUPPORTED_DELAY_TICS)
            
            # If there is both a delay and a non-delay list, merge them
            if synapse_list is not None and delay_synapse_list is not None:
                rows = synapse_list.get_rows()
                delay_rows = delay_synapse_list.get_rows()
                for i in range(len(rows)):
                    rows[i].append(delay_rows[i])
                self.read_synapse_list = synapse_list
            
            # If there is only a synapse list, return that
            elif synapse_list is not None:
                self.read_synapse_list = synapse_list
            
            # Otherwise return the delay list (there should be at least one!)
            else:
                self.read_synapse_list = delay_synapse_list
        
        return self.read_synapse_list

    def getDelays(self, format='list', gather=True):
        """
        Get synaptic delays for all connections in this Projection.

        Possible formats are: a list of length equal to the number of connections
        in the projection, a 2D delay array (with NaN for non-existent
        connections).
        """
        global controller
        timer = None
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer = Timer()
            timer.start_timing()
        synapse_list = self._get_synaptic_data()
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer.take_sample()
            
        if format == 'list':
            delays = list()
            for row in synapse_list.get_rows():
                delays.extend(row.delays 
                        * (float(controller.dao.machineTimeStep) / 1000.0))
            return delays
        
        delays = numpy.zeros((self.projection_edge.prevertex.atoms, 
                self.projection_edge.postvertex.atoms))
        rows = synapse_list.get_rows()
        for pre_atom in range(len(rows)):
            row = rows[pre_atom]
            for i in len(row.target_indices):
                post_atom = row.target_indices[i]
                delay = (float(row.delays[i]) 
                        * (float(controller.dao.machineTimeStep) / 1000.0))
                delays[pre_atom][post_atom] = delay
        return delays

    def getSynapseDynamics(self, parameter_name, format='list', gather=True):
        """
        Get parameters of the dynamic synapses for all connections in this
        Projection.
        """
        raise NotImplementedError

    def getWeights(self, format='list'):
        """
        Get synaptic weights for all connections in this Projection.
        (pyNN gather parameter not supported from the signiture
        getWeights(self, format='list', gather=True):)

        Possible formats are: a list of length equal to the number of connections
        in the projection, a 2D weight array (with NaN for non-existent
        connections). Note that for the array format, if there is more than
        one connection between two cells, the summed weight will be given.
        """
        timer = None
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer = Timer()
            timer.start_timing()
        synapse_list = self._get_synaptic_data()
        if conf.config.getboolean("Reports", "outputTimesForSections"):
            timer.take_sample()
            
        if format == 'list':
            weights = list()
            for row in synapse_list.get_rows():
                weights.extend(row.weights)
            return weights 
        elif format == 'array':
            weights = numpy.zeros((self.projection_edge.prevertex.atoms, 
                                   self.projection_edge.postvertex.atoms))
            rows = synapse_list.get_rows()
        for pre_atom, row in enumerate(rows):
            for post_atom, weight in zip(row.target_indices, row.weights):
                weights[pre_atom][post_atom] = weight
        return weights

    def __len__(self):
        """Return the total number of local connections."""
        raise NotImplementedError

    def printDelays(self, file, format='list', gather=True):
        """
        Print synaptic weights to file. In the array format, zeros are printed
        for non-existent connections.
        """
        raise NotImplementedError

    def printWeights(self, file, format='list', gather=True):
        """
        Print synaptic weights to file. In the array format, zeros are printed
        for non-existent connections.
        """
        raise NotImplementedError

    def randomizeWeights(self, rand_distr):
        """
        Set weights to random values taken from rand_distr.
        """
        raise NotImplementedError

    def randomizeDelays(self, rand_distr):
        """
        Set delays to random values taken from rand_distr.
        """
        raise NotImplementedError

    def randomizeSynapseDynamics(self, param, rand_distr):
        """
        Set parameters of the synapse dynamics to values taken from rand_distr
        """
        raise NotImplementedError

    def __repr__(self):
        '''
        returns a string rep of the projection
        '''
        return "prjection {}".format(self.projection_edge.label)

    def saveConnections(self, file, gather=True, compatible_output=True):
        """
        Save connections to file in a format suitable for reading in with a
        FromFileConnector.
        """
        raise NotImplementedError

    def size(self, gather=True):
        """
        Return the total number of connections.
         - only local connections, if gather is False,
         - all connections, if gather is True (default)
        """
        raise NotImplementedError

    def setDelays(self, d):
        """
        d can be a single number, in which case all delays are set to this
        value, or a list/1D array of length equal to the number of connections
        in the projection, or a 2D array with the same dimensions as the
        connectivity matrix (as returned by `getDelays(format='array')`).
        """
        raise NotImplementedError

    def setSynapseDynamics(self, param, value):
        """
        Set parameters of the dynamic synapses for all connections in this
        projection.
        """
        raise NotImplementedError

    def setWeights(self, w):
        """
        w can be a single number, in which case all weights are set to this
        value, or a list/1D array of length equal to the number of connections
        in the projection, or a 2D array with the same dimensions as the
        connectivity matrix (as returned by `getWeights(format='array')`).
        Weights should be in nA for current-based and uS for conductance-based
        synapses.
        """
        raise NotImplementedError

    def weightHistogram(self, min=None, max=None, nbins=10):
        """
        Return a histogram of synaptic weights.
        If min and max are not given, the minimum and maximum weights are
        calculated automatically.
        """
        raise NotImplementedError