Example #1
0
 def __init__(self, name = '', description = '',
              setup=None, chipid=None, neurontype=None):
     """
     Init a population by name and description. Population is empty.
     Name and description are used in the graph representation for
     connectivity.
     - name: string
     - description: string
     - setup: NeuroSetup to init the population
     - chipid: string id of the chip in the setup, e.g. 'ifslwta'
     - neurontype: neurontype string to init the population, e.g. 'pixel'
     """
     self.name = name
     self.description = description
     self.soma = AddrGroup('Empty group.')
     self.synapses = dict()
     self.setup = None
     self.neuronblock = None
     if setup is not None and chipid is not None and neurontype is not None:
         self.__populate_init__(setup, chipid, neurontype)
Example #2
0
 def __init__(self, name="", description="", setup=None, chipid=None, neurontype=None):
     """
     Init a population by name and description. Population is empty.
     Name and description are used in the graph representation for
     connectivity.
     - name: string
     - description: string
     - setup: NeuroSetup to init the population
     - chipid: string id of the chip in the setup, e.g. 'ifslwta'
     - neurontype: neurontype string to init the population, e.g. 'pixel'
     """
     self.name = name
     self.description = description
     self.soma = AddrGroup("Empty group.")
     self.synapses = dict()
     self.setup = None
     self.neuronblock = None
     if setup is not None and chipid is not None and neurontype is not None:
         self.__populate_init__(setup, chipid, neurontype)
Example #3
0
    def populate_by_topology(self, setup, chipid, neurontype, topology='rectangle', topology_kwargs={'p1': [0, 0], 'p2': [63, 0]}):
        """
        Takes the given number of addresses by respecting the chips natural topology (i.e. 0 to n%X+n/Y). It takes the first n addresses if offset is not set. Arguments
        are:
            - setup: a NeuroSetup
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - n: the number of neuron to allocate
        """

        self.__populate_init__(setup, chipid, neurontype)

        # all the addresses for the given topology
        S = AddrGroup(self.neuronblock.soma.id)
        getattr(S, 'populate_' + topology)(setup, chipid,
             grouptype='out', **topology_kwargs)
        addresses = _set_intersection(S.addr, self.neuronblock.soma.addresses)
        self.soma.populate_line(
            setup, chipid, grouptype='out', addresses=addresses)
        self.__populate_synapses__()
Example #4
0
    def __populate_synapses__(self):
        """
        Populate all the synapses of the population with the corresponding
        addresses for the neurons in the population.
        """
        #self.soma.sort() #sadique: is this necessary ??
        for s in self.neuronblock.synapses.keys():
            syn_id = self.neuronblock.synapses[s].id
            self.synapses[syn_id] = S = AddrGroup(self.
                neuronblock.synapses[s].id)
            self.synapses[syn_id].name = self.name + ' ' + str(s)

            ##Populate empty first to set channel and ch_addr of S
            ##Consider doing this in AddrGroup
            #S.populate_line(self.setup,
            #        self.soma.chipid,
            #        grouptype='in',
            #        addresses=[])
            addresses=self.__soma2syn__(self.soma.addr, [s])
            S.populate_line(self.setup,
                    self.soma.chipid,
                    grouptype='in',
                    addresses=addresses,)
Example #5
0
class Population(object):
    """
    Population is a set of neurons and corresponding synapses. Population can have parameters
    (efficacy of excitatory input synapses, ...).
    This is on top of synapses and is intended to be used by the user to create neural networks.
    """
    # TODO: extend to multiple chips
    # TODO: proper pickling

    def __init__(self, name = '', description = '',
                 setup=None, chipid=None, neurontype=None):
        """
        Init a population by name and description. Population is empty.
        Name and description are used in the graph representation for
        connectivity.
        - name: string
        - description: string
        - setup: NeuroSetup to init the population
        - chipid: string id of the chip in the setup, e.g. 'ifslwta'
        - neurontype: neurontype string to init the population, e.g. 'pixel'
        """
        self.name = name
        self.description = description
        self.soma = AddrGroup('Empty group.')
        self.synapses = dict()
        self.setup = None
        self.neuronblock = None
        if setup is not None and chipid is not None and neurontype is not None:
            self.__populate_init__(setup, chipid, neurontype)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
        if hasattr(self, 'soma'):
            self.soma.name = self._name + ' ' + 'soma'

        if hasattr(self, 'neuronblock'):
            for s in self.neuronblock.synapses.keys():
                self.synapses[self.neuronblock.synapses[s].
                    id].name = self._name + ' ' + str(s)

    def __copy__(self):
        '''
        Return an exact copy of the population.
        '''
        p = Population(self.name, self.description)
        p.soma = copy.deepcopy(self.soma)
        p.synapses = copy.deepcopy(self.synapses)
        p.setup = self.setup
        p.neuronblock = self.neuronblock
        return p

    def __getitem__(self, i):
        """
        x.__getitem__(i) ==> x[i]
        """
        p = Population(self.name, self.description)
        p.setup = self.setup
        p.neuronblock = self.neuronblock

        if isinstance(i, slice):
            p.soma = self.soma[i]
        else:
            if hasattr(i, '__len__'):
                p.soma = self.soma[i]
            else:
                p.soma = self.soma[i:i + 1]
        p.__populate_synapses__()
        return p

    def __getslice__(self, i, j):
        """
        x.__getslice__(i, j) ==> x[i:j]
        """
        p = Population(self.name, self.description)
        p.setup = self.setup
        p.neuronblock = self.neuronblock
        p.soma = self.soma[i:j]
        p.__populate_synapses__()
        return p

    def __len__(self):
        """
        len(x) <==> len(x.soma)

        Return the number of neurons in the population.
        """
        return len(self.soma)

    def clear(self):
        """
        Clear the population back to its original state.
        """
        self.soma = AddrGroup('Empty group')
        self.synapses = dict()

    def __populateByExplicitAddr__(self, chipid, addr):
        """
        This function is useful if you know the addresses of neurons and
        synapses. Needs consistence between chipid and addr.
        WARNING: you are supposed to use higher level functions, so use this at
        your own risk!
        """
        raise NotImplementedError

    def isinit(self):
        """
        Return True if population is initiated with setup, chipid and
        neurontype, e.g. not populated.
        """
        if self.setup is None or self.neuronblock is None:
            return False
        else:
            return True

    def union(self, population):
        """
        Add the given population's addresses to the existing one. If the
        address is already there, it doesn't add.
        WARNING: Synapses might be incorrect
        """
        if not population.isinit():
            # if the other population is still not initiated init with the same
            # parameters of self population but don't populate
            population.init(self.setup, self.soma.chipid, self.neuronblock.id)
        if population.neuronblock.neurochip.id\
           != self.neuronblock.neurochip.id:
                raise Exception(
                    'Union between different chips is not yet implemented.')
        # TODO: actually, addr should be constructed with the appropriate
        # dimensions

        if len(self.soma.addr) != 0 and len(population.soma.addr) != 0:
            self.soma.addr = np.hstack([self.soma.addr,
                                           population.soma.addr])
        else:  # self.soma is empty
            self.soma.addr = population.soma.addr.copy()

        self.soma.repopulate(self.setup)

        # At this point the order of the addresses is dictated by the order by
        # which they have been added
        # Also, synapses order may also be broken, consider adding
        # __populate_synapses__() here (too slow)
        self.soma.sort()

        return

    def add(self, addresses):
        """
        Adds a neuron (with all its synapses) to the population. Population has to
        be populated already. Address has to be of the appropriate format for
        the chip on which the population has been allocated.
        Arguments are:
            - addresses: neuron address in human format (e.g. [10, 2] for neuron
              [10,2] in a 2D chip.
        """
        for a in addresses:
            if not a in self.neuronblock.soma.addresses:
                raise Exception("At least one address is not present in the population neuronblock.")

        # add soma address(es)
        self.soma.add(self.setup, addresses)
        # add synapses
        for k in self.synapses.keys():
            ch = self.synapses[k].channel
            soma_addr = addresses
            syn_addr = self.__soma2syn__(soma_addr, synapses=[k])
            self.synapses[k].add( self.setup, syn_addr)
        #self.__unique_addresses__()

    def remove(self, address):
        """
        Removes a neuron (with all its synapses) from the population.
        Arguments are:
            - address: neuron address in human format (e.g. [10, 2] for neuron
              [10,2] in a 2D chip.
        """
        # self.soma.remove([address])...
        raise NotImplementedError

    def __getstate__(self):
        """
        Implement pickling functionalities when dumping.
        """
        d = dict(self.__dict__)
        d['neurontype'] = self.neuronblock.id
        del d['neuronblock']  # don't need to dump neuronblock
        return d

    def __setstate__(self, dict):
        """
        Implement pickling functionalities when loading.
        """
        # TODO: can we do this with __getinitargs__ instead?
        self.__dict__ = copy.copy(dict)
        chip = dict['setup'].chips[dict['soma'].chipid]
        neuronblock = chip.neuron[dict['neurontype']]
        self.neuronblock = neuronblock
        del self.neurontype

    def __soma2syn__(self, addresses, synapses=None):
        """
        Given the neurons addresses, returns all the addresses of its synapses.
        Useful for populating by giving only the soma addresses.
        If synapses is set, returns only those synapses (e.g., 'excitatory0').
        NOTE: The returned array preserves ordering of neurons (np.repeat)
        """

        ## Freshly generate addresses of synapses
        #somaddr = addresses
        #nd = len(addresses.dtype)
        #synaddr = []

        #for s in synapses:
        #    # Synapses can be multi-dimensional
        #    syn_block = self.neuronblock.synapses[s]

        #    # The following codes insures that the addresses are built it the
        #    # correct order
        #    ch = self.synapses[s].channel
        #    ch_ad = self.synapses[s].ch_addr[ch]
        #    snr = []
        #    # assumes human readable addresses are of the form neuron -
        #    # synapse. (could be anything)
        #    for field in ch_ad.addrConf:
        #        if field['type'] == -1:
        #            if field['id'] in syn_block.dims:
        #                snr.append(syn_block.dims[field['id']])
        #            else:
        #                snr.append([field['default_value']])
        #    print snr
        #    #The following three lines are a little intricate.
        #    #It first builds a grid of possible synapses (see _buildGrid)
        #    # Then combines it (column_stack) with all possible soma addresses
        #    # (which is already a grid)
        #    # Repeat and Tile make sure that the dimenstionalities are
        #    # consistent
        #    #and that all possible addresses are considered

        #    syngrid = _buildGrid(snr)
        #    pa = self.soma.addr
        #    somasyngrid = np.column_stack(
        #            [np.repeat(pa, len(syngrid), axis=0),
        #             np.tile(syngrid.T, len(pa)).T])
        #    synaddr.append(somasyngrid)

        #saddrout = np.array(_flatten(synaddr)).reshape((-1, nd + len(snr)))

        #return _sort_by_logical(saddrout)
        somaaddr = addresses
        dtp = AddrGroup._get_dtype(AddrGroup('',''), self.setup,
                                   self.soma.chipid, 'in')
        syn_dim_names = None
        synaddrs_all = None
        for s in synapses:
            syn_block = self.neuronblock.synapses[s]
            if syn_dim_names == None:
                syn_dim_names = syn_block.dims.keys()
                syndtp = np.dtype([(str(k), 'uint32') for k in syn_dim_names])
                # NOTE: the str type cast is because numpy dtype doesn't support
                # unicode strings
            synaddrs = [syn_block.dims[fld] for fld in syn_dim_names]
            synaddrs = list(it.product(*synaddrs))
            if synaddrs_all == None:
                synaddrs_all = np.array(synaddrs)
            else:
                synaddrs_all = np.concatenate((synaddrs_all, synaddrs))
        # Syn addresses only
        synaddrs_all = synaddrs_all.astype('uint32').view(syndtp).T[0]
        # Syn addresses tiled for all soma addresses
        synaddrs_full = np.tile(synaddrs_all, len(addresses)) 
        # Soma addresses repeated for all synapse addresses
        soma_addr_all = addresses.repeat(len(synaddrs_all))
        # Full addresses space
        syn_addr_full = np.empty(soma_addr_all.shape, dtype=dtp)
        for fld in soma_addr_all.dtype.names:
            syn_addr_full[fld] = soma_addr_all[fld]
        for fld in synaddrs_all.dtype.names:
            syn_addr_full[fld] = synaddrs_full[fld]
        return syn_addr_full

    def __populate_init__(self, setup, chipid, neurontype):
        """
        Basic operations common to every populate method.
        """
        self.setup = setup

        chip = setup.chips[chipid]

        # check whether neuron is available in the chip
        try:
            neuronblock = chip.neuron[neurontype]
        except KeyError:            
            raise KeyError('ERROR: %s: No such neurontype in current setup.' % neurontype)

        self.neuronblock = neuronblock  # neuronblock contains translations
                                        # for somas AND synapses biases

        self.soma.__populate__(setup, chipid, 'out')
        self.soma.name = self.name + ' ' + 'soma'

    def init(self, setup, chipid, neurontype):
        """
        self.init ==> self.__populate_init__
        """
        return self.__populate_init__(setup, chipid, neurontype)

    def __populate_synapses__(self):
        """
        Populate all the synapses of the population with the corresponding
        addresses for the neurons in the population.
        """
        #self.soma.sort() #sadique: is this necessary ??
        for s in self.neuronblock.synapses.keys():
            syn_id = self.neuronblock.synapses[s].id
            self.synapses[syn_id] = S = AddrGroup(self.
                neuronblock.synapses[s].id)
            self.synapses[syn_id].name = self.name + ' ' + str(s)

            ##Populate empty first to set channel and ch_addr of S
            ##Consider doing this in AddrGroup
            #S.populate_line(self.setup,
            #        self.soma.chipid,
            #        grouptype='in',
            #        addresses=[])
            addresses=self.__soma2syn__(self.soma.addr, [s])
            S.populate_line(self.setup,
                    self.soma.chipid,
                    grouptype='in',
                    addresses=addresses,)

    def populate_all(self, setup, chipid, neurontype):
        """
        Populate all the neurons in the given chip.
        """
        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses from the ones available in neuronblock
        addresses = self.neuronblock.soma.addresses
        self.soma.populate_line(setup, chipid, grouptype='out',
                                addresses=addresses)

        self.__populate_synapses__()

    def populate_by_number(self, setup, chipid, neurontype, n, offset=0):
        """
        Takes the given number of addresses from the neuronblock available
        addresses. It takes the first n addresses if offset is not set. Arguments
        are:
            - setup: a NeuroSetup
            - chipid: id of the chip as expressed in setup.xml
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - n: the number of neuron to allocate
            - offset: imposes not to take the first addresses
        """

        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses from the ones available in neuronblock
        addresses = self.neuronblock.soma.addresses[offset:n + offset]
        if len(addresses) != n:
            raise Exception("Not enough neurons of this type.")
        self.soma.populate_line(setup, chipid, grouptype='out',
                                addresses=addresses)

        self.__populate_synapses__()

    def populate_sparse(self, setup, chipid, neurontype, p=.3):
        """
        Populate picking random addresses from the neuronblock of all possible
        addresses with probability p. Arguments are:
            - setup: a NeuroSetup
            - chipid: id of the chip as expressed in setup.xml
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - p: probability of picking neurons in [0, 1)
        """

        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses from the ones available in neuronblock
        a = np.array(self.neuronblock.soma.addresses)
        addresses = a[np.random.random(len(a)) < p]

        self.soma.populate_line(setup, chipid, grouptype='out',
                                addresses=addresses)

        self.__populate_synapses__()

    def populate_by_topology(self, setup, chipid, neurontype, topology='rectangle', topology_kwargs={'p1': [0, 0], 'p2': [63, 0]}):
        """
        Takes the given number of addresses by respecting the chips natural topology (i.e. 0 to n%X+n/Y). It takes the first n addresses if offset is not set. Arguments
        are:
            - setup: a NeuroSetup
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - n: the number of neuron to allocate
        """

        self.__populate_init__(setup, chipid, neurontype)

        # all the addresses for the given topology
        S = AddrGroup(self.neuronblock.soma.id)
        getattr(S, 'populate_' + topology)(setup, chipid,
             grouptype='out', **topology_kwargs)
        addresses = _set_intersection(S.addr, self.neuronblock.soma.addresses)
        self.soma.populate_line(
            setup, chipid, grouptype='out', addresses=addresses)
        self.__populate_synapses__()

    def populate_by_id(self, setup, chipid, neurontype, id_list, axes=0):
        """
        Takes the given addresses (as list) from the neuronblock available
        addresses. It takes the first n addresses if offset is not set.
        Arguments are:
            - setup: a NeuroSetup
            - chipid: id of the chip as expressed in setup.xml
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - id_list: the list of ids for neurons to allocate (human addresses)
            - axes: chosse the axes by which to filter the addresses
        """

        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses
        addresses = []
        for t in self.neuronblock.soma.addresses:
            if int(t[axes]) in id_list:
                addresses.append(t)
        try:
            self.soma.populate_line(
                setup, chipid, grouptype='out', addresses=addresses)
        except:
            raise Exception, "Chip %s contains no neurons of given id_list on axes %d." % (
                chipid, axes)

        self.__populate_synapses__()
Example #6
0
    def __soma2syn__(self, addresses, synapses=None):
        """
        Given the neurons addresses, returns all the addresses of its synapses.
        Useful for populating by giving only the soma addresses.
        If synapses is set, returns only those synapses (e.g., 'excitatory0').
        NOTE: The returned array preserves ordering of neurons (np.repeat)
        """

        ## Freshly generate addresses of synapses
        #somaddr = addresses
        #nd = len(addresses.dtype)
        #synaddr = []

        #for s in synapses:
        #    # Synapses can be multi-dimensional
        #    syn_block = self.neuronblock.synapses[s]

        #    # The following codes insures that the addresses are built it the
        #    # correct order
        #    ch = self.synapses[s].channel
        #    ch_ad = self.synapses[s].ch_addr[ch]
        #    snr = []
        #    # assumes human readable addresses are of the form neuron -
        #    # synapse. (could be anything)
        #    for field in ch_ad.addrConf:
        #        if field['type'] == -1:
        #            if field['id'] in syn_block.dims:
        #                snr.append(syn_block.dims[field['id']])
        #            else:
        #                snr.append([field['default_value']])
        #    print snr
        #    #The following three lines are a little intricate.
        #    #It first builds a grid of possible synapses (see _buildGrid)
        #    # Then combines it (column_stack) with all possible soma addresses
        #    # (which is already a grid)
        #    # Repeat and Tile make sure that the dimenstionalities are
        #    # consistent
        #    #and that all possible addresses are considered

        #    syngrid = _buildGrid(snr)
        #    pa = self.soma.addr
        #    somasyngrid = np.column_stack(
        #            [np.repeat(pa, len(syngrid), axis=0),
        #             np.tile(syngrid.T, len(pa)).T])
        #    synaddr.append(somasyngrid)

        #saddrout = np.array(_flatten(synaddr)).reshape((-1, nd + len(snr)))

        #return _sort_by_logical(saddrout)
        somaaddr = addresses
        dtp = AddrGroup._get_dtype(AddrGroup('',''), self.setup,
                                   self.soma.chipid, 'in')
        syn_dim_names = None
        synaddrs_all = None
        for s in synapses:
            syn_block = self.neuronblock.synapses[s]
            if syn_dim_names == None:
                syn_dim_names = syn_block.dims.keys()
                syndtp = np.dtype([(str(k), 'uint32') for k in syn_dim_names])
                # NOTE: the str type cast is because numpy dtype doesn't support
                # unicode strings
            synaddrs = [syn_block.dims[fld] for fld in syn_dim_names]
            synaddrs = list(it.product(*synaddrs))
            if synaddrs_all == None:
                synaddrs_all = np.array(synaddrs)
            else:
                synaddrs_all = np.concatenate((synaddrs_all, synaddrs))
        # Syn addresses only
        synaddrs_all = synaddrs_all.astype('uint32').view(syndtp).T[0]
        # Syn addresses tiled for all soma addresses
        synaddrs_full = np.tile(synaddrs_all, len(addresses)) 
        # Soma addresses repeated for all synapse addresses
        soma_addr_all = addresses.repeat(len(synaddrs_all))
        # Full addresses space
        syn_addr_full = np.empty(soma_addr_all.shape, dtype=dtp)
        for fld in soma_addr_all.dtype.names:
            syn_addr_full[fld] = soma_addr_all[fld]
        for fld in synaddrs_all.dtype.names:
            syn_addr_full[fld] = synaddrs_full[fld]
        return syn_addr_full
Example #7
0
 def clear(self):
     """
     Clear the population back to its original state.
     """
     self.soma = AddrGroup('Empty group')
     self.synapses = dict()
Example #8
0
class Population(object):
    """
    Population is a set of neurons and corresponding synapses. Population can have parameters
    (efficacy of excitatory input synapses, ...).
    This is on top of synapses and is intended to be used by the user to create neural networks.
    """

    # TODO: extend to multiple chips
    # TODO: proper pickling

    def __init__(self, name="", description="", setup=None, chipid=None, neurontype=None):
        """
        Init a population by name and description. Population is empty.
        Name and description are used in the graph representation for
        connectivity.
        - name: string
        - description: string
        - setup: NeuroSetup to init the population
        - chipid: string id of the chip in the setup, e.g. 'ifslwta'
        - neurontype: neurontype string to init the population, e.g. 'pixel'
        """
        self.name = name
        self.description = description
        self.soma = AddrGroup("Empty group.")
        self.synapses = dict()
        self.setup = None
        self.neuronblock = None
        if setup is not None and chipid is not None and neurontype is not None:
            self.__populate_init__(setup, chipid, neurontype)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
        if hasattr(self, "soma"):
            self.soma.name = self._name + " " + "soma"

        if hasattr(self, "neuronblock"):
            for s in self.neuronblock.synapses.keys():
                self.synapses[self.neuronblock.synapses[s].id].name = self._name + " " + str(s)

    def __copy__(self):
        """
        Return an exact copy of the population.
        """
        p = Population(self.name, self.description)
        p.soma = copy.deepcopy(self.soma)
        p.synapses = copy.deepcopy(self.synapses)
        p.setup = self.setup
        p.neuronblock = self.neuronblock
        return p

    def __getitem__(self, i):
        """
        x.__getitem__(i) ==> x[i]
        """
        p = Population(self.name, self.description)
        p.setup = self.setup
        p.neuronblock = self.neuronblock

        if isinstance(i, slice):
            p.soma = self.soma[i]
        else:
            if hasattr(i, "__len__"):
                p.soma = self.soma[i]
            else:
                p.soma = self.soma[i : i + 1]
        p.__populate_synapses__()
        return p

    def __getslice__(self, i, j):
        """
        x.__getslice__(i, j) ==> x[i:j]
        """
        p = Population(self.name, self.description)
        p.setup = self.setup
        p.neuronblock = self.neuronblock
        p.soma = self.soma[i:j]
        p.__populate_synapses__()
        return p

    def __len__(self):
        """
        len(x) <==> len(x.soma)

        Return the number of neurons in the population.
        """
        return len(self.soma)

    def clear(self):
        """
        Clear the population back to its original state.
        """
        self.soma = AddrGroup("Empty group")
        self.synapses = dict()

    def __populateByExplicitAddr__(self, chipid, addr):
        """
        This function is useful if you know the addresses of neurons and
        synapses. Needs consistence between chipid and addr.
        WARNING: you are supposed to use higher level functions, so use this at
        your own risk!
        """
        raise NotImplementedError

    def isinit(self):
        """
        Return True if population is initiated with setup, chipid and
        neurontype, e.g. not populated.
        """
        if self.setup is None or self.neuronblock is None:
            return False
        else:
            return True

    def union(self, population):
        """
        Add the given population's addresses to the existing one. If the
        address is already there, it doesn't add.
        WARNING: Synapses might be incorrect
        """
        if not population.isinit():
            # if the other population is still not initiated init with the same
            # parameters of self population but don't populate
            population.init(self.setup, self.soma.chipid, self.neuronblock.id)
        if population.neuronblock.neurochip.id != self.neuronblock.neurochip.id:
            raise Exception("Union between different chips is not yet implemented.")
        # TODO: actually, addr should be constructed with the appropriate
        # dimensions

        if len(self.soma.addr) != 0 and len(population.soma.addr) != 0:
            self.soma.addr = np.hstack([self.soma.addr, population.soma.addr])
        else:  # self.soma is empty
            self.soma.addr = population.soma.addr.copy()

        self.soma.repopulate(self.setup)

        # At this point the order of the addresses is dictated by the order by
        # which they have been added
        # Also, synapses order may also be broken, consider adding
        # __populate_synapses__() here (too slow)
        self.soma.sort()

        return

    def add(self, addresses):
        """
        Adds a neuron (with all its synapses) to the population. Population has to
        be populated already. Address has to be of the appropriate format for
        the chip on which the population has been allocated.
        Arguments are:
            - addresses: neuron address in human format (e.g. [10, 2] for neuron
              [10,2] in a 2D chip.
        """
        for a in addresses:
            if not a in self.neuronblock.soma.addresses:
                raise Exception("At least one address is not present in the population neuronblock.")

        # add soma address(es)
        self.soma.add(self.setup, addresses)
        # add synapses
        for k in self.synapses.keys():
            ch = self.synapses[k].channel
            soma_addr = addresses
            syn_addr = self.__soma2syn__(soma_addr, synapses=[k])
            self.synapses[k].add(self.setup, syn_addr)
        # self.__unique_addresses__()

    def remove(self, address):
        """
        Removes a neuron (with all its synapses) from the population.
        Arguments are:
            - address: neuron address in human format (e.g. [10, 2] for neuron
              [10,2] in a 2D chip.
        """
        # self.soma.remove([address])...
        raise NotImplementedError

    def __getstate__(self):
        """
        Implement pickling functionalities when dumping.
        """
        d = dict(self.__dict__)
        d["neurontype"] = self.neuronblock.id
        del d["neuronblock"]  # don't need to dump neuronblock
        return d

    def __setstate__(self, dict):
        """
        Implement pickling functionalities when loading.
        """
        # TODO: can we do this with __getinitargs__ instead?
        self.__dict__ = copy.copy(dict)
        chip = dict["setup"].chips[dict["soma"].chipid]
        neuronblock = chip.neuron[dict["neurontype"]]
        self.neuronblock = neuronblock
        del self.neurontype

    def __soma2syn__(self, addresses, synapses=None):
        """
        Given the neurons addresses, returns all the addresses of its synapses.
        Useful for populating by giving only the soma addresses.
        If synapses is set, returns only those synapses (e.g., 'excitatory0').
        NOTE: The returned array preserves ordering of neurons (np.repeat)
        """

        ## Freshly generate addresses of synapses
        # somaddr = addresses
        # nd = len(addresses.dtype)
        # synaddr = []

        # for s in synapses:
        #    # Synapses can be multi-dimensional
        #    syn_block = self.neuronblock.synapses[s]

        #    # The following codes insures that the addresses are built it the
        #    # correct order
        #    ch = self.synapses[s].channel
        #    ch_ad = self.synapses[s].ch_addr[ch]
        #    snr = []
        #    # assumes human readable addresses are of the form neuron -
        #    # synapse. (could be anything)
        #    for field in ch_ad.addrConf:
        #        if field['type'] == -1:
        #            if field['id'] in syn_block.dims:
        #                snr.append(syn_block.dims[field['id']])
        #            else:
        #                snr.append([field['default_value']])
        #    print snr
        #    #The following three lines are a little intricate.
        #    #It first builds a grid of possible synapses (see _buildGrid)
        #    # Then combines it (column_stack) with all possible soma addresses
        #    # (which is already a grid)
        #    # Repeat and Tile make sure that the dimenstionalities are
        #    # consistent
        #    #and that all possible addresses are considered

        #    syngrid = _buildGrid(snr)
        #    pa = self.soma.addr
        #    somasyngrid = np.column_stack(
        #            [np.repeat(pa, len(syngrid), axis=0),
        #             np.tile(syngrid.T, len(pa)).T])
        #    synaddr.append(somasyngrid)

        # saddrout = np.array(_flatten(synaddr)).reshape((-1, nd + len(snr)))

        # return _sort_by_logical(saddrout)
        somaaddr = addresses
        dtp = AddrGroup._get_dtype(AddrGroup("", ""), self.setup, self.soma.chipid, "in")
        syn_dim_names = None
        synaddrs_all = None
        for s in synapses:
            syn_block = self.neuronblock.synapses[s]
            if syn_dim_names == None:
                syn_dim_names = syn_block.dims.keys()
                syndtp = np.dtype([(str(k), "uint32") for k in syn_dim_names])
                # NOTE: the str type cast is because numpy dtype doesn't support
                # unicode strings
            synaddrs = [syn_block.dims[fld] for fld in syn_dim_names]
            synaddrs = list(it.product(*synaddrs))
            if synaddrs_all == None:
                synaddrs_all = np.array(synaddrs)
            else:
                synaddrs_all = np.concatenate((synaddrs_all, synaddrs))
        # Syn addresses only
        synaddrs_all = synaddrs_all.astype("uint32").view(syndtp).T[0]
        # Syn addresses tiled for all soma addresses
        synaddrs_full = np.tile(synaddrs_all, len(addresses))
        # Soma addresses repeated for all synapse addresses
        soma_addr_all = addresses.repeat(len(synaddrs_all))
        # Full addresses space
        syn_addr_full = np.empty(soma_addr_all.shape, dtype=dtp)
        for fld in soma_addr_all.dtype.names:
            syn_addr_full[fld] = soma_addr_all[fld]
        for fld in synaddrs_all.dtype.names:
            syn_addr_full[fld] = synaddrs_full[fld]
        return syn_addr_full

    def __populate_init__(self, setup, chipid, neurontype):
        """
        Basic operations common to every populate method.
        """
        self.setup = setup

        chip = setup.chips[chipid]

        # check whether neuron is available in the chip
        try:
            neuronblock = chip.neuron[neurontype]
        except KeyError:
            raise KeyError("ERROR: %s: No such neurontype in current setup." % neurontype)

        self.neuronblock = neuronblock  # neuronblock contains translations
        # for somas AND synapses biases

        self.soma.__populate__(setup, chipid, "out")
        self.soma.name = self.name + " " + "soma"

    def init(self, setup, chipid, neurontype):
        """
        self.init ==> self.__populate_init__
        """
        return self.__populate_init__(setup, chipid, neurontype)

    def __populate_synapses__(self):
        """
        Populate all the synapses of the population with the corresponding
        addresses for the neurons in the population.
        """
        # self.soma.sort() #sadique: is this necessary ??
        for s in self.neuronblock.synapses.keys():
            syn_id = self.neuronblock.synapses[s].id
            self.synapses[syn_id] = S = AddrGroup(self.neuronblock.synapses[s].id)
            self.synapses[syn_id].name = self.name + " " + str(s)

            ##Populate empty first to set channel and ch_addr of S
            ##Consider doing this in AddrGroup
            # S.populate_line(self.setup,
            #        self.soma.chipid,
            #        grouptype='in',
            #        addresses=[])
            addresses = self.__soma2syn__(self.soma.addr, [s])
            S.populate_line(self.setup, self.soma.chipid, grouptype="in", addresses=addresses)

    def populate_all(self, setup, chipid, neurontype):
        """
        Populate all the neurons in the given chip.
        """
        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses from the ones available in neuronblock
        addresses = self.neuronblock.soma.addresses
        self.soma.populate_line(setup, chipid, grouptype="out", addresses=addresses)

        self.__populate_synapses__()

    def populate_by_number(self, setup, chipid, neurontype, n, offset=0):
        """
        Takes the given number of addresses from the neuronblock available
        addresses. It takes the first n addresses if offset is not set. Arguments
        are:
            - setup: a NeuroSetup
            - chipid: id of the chip as expressed in setup.xml
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - n: the number of neuron to allocate
            - offset: imposes not to take the first addresses
        """

        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses from the ones available in neuronblock
        addresses = self.neuronblock.soma.addresses[offset : n + offset]
        if len(addresses) != n:
            raise Exception("Not enough neurons of this type.")
        self.soma.populate_line(setup, chipid, grouptype="out", addresses=addresses)

        self.__populate_synapses__()

    def populate_sparse(self, setup, chipid, neurontype, p=0.3):
        """
        Populate picking random addresses from the neuronblock of all possible
        addresses with probability p. Arguments are:
            - setup: a NeuroSetup
            - chipid: id of the chip as expressed in setup.xml
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - p: probability of picking neurons in [0, 1)
        """

        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses from the ones available in neuronblock
        a = np.array(self.neuronblock.soma.addresses)
        addresses = a[np.random.random(len(a)) < p]

        self.soma.populate_line(setup, chipid, grouptype="out", addresses=addresses)

        self.__populate_synapses__()

    def populate_by_topology(
        self, setup, chipid, neurontype, topology="rectangle", topology_kwargs={"p1": [0, 0], "p2": [63, 0]}
    ):
        """
        Takes the given number of addresses by respecting the chips natural topology (i.e. 0 to n%X+n/Y). It takes the first n addresses if offset is not set. Arguments
        are:
            - setup: a NeuroSetup
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - n: the number of neuron to allocate
        """

        self.__populate_init__(setup, chipid, neurontype)

        # all the addresses for the given topology
        S = AddrGroup(self.neuronblock.soma.id)
        getattr(S, "populate_" + topology)(setup, chipid, grouptype="out", **topology_kwargs)
        addresses = _set_intersection(S.addr, self.neuronblock.soma.addresses)
        self.soma.populate_line(setup, chipid, grouptype="out", addresses=addresses)
        self.__populate_synapses__()

    def populate_by_id(self, setup, chipid, neurontype, id_list, axes=0):
        """
        Takes the given addresses (as list) from the neuronblock available
        addresses. It takes the first n addresses if offset is not set.
        Arguments are:
            - setup: a NeuroSetup
            - chipid: id of the chip as expressed in setup.xml
            - neurontype: id of neurons as expressed in chipfile.xml (e.g.
            'excitatory')
            - id_list: the list of ids for neurons to allocate (human addresses)
            - axes: chosse the axes by which to filter the addresses
        """

        self.__populate_init__(setup, chipid, neurontype)

        # filter addresses
        addresses = []
        for t in self.neuronblock.soma.addresses:
            if int(t[axes]) in id_list:
                addresses.append(t)
        try:
            self.soma.populate_line(setup, chipid, grouptype="out", addresses=addresses)
        except:
            raise Exception, "Chip %s contains no neurons of given id_list on axes %d." % (chipid, axes)

        self.__populate_synapses__()
Example #9
0
    def __soma2syn__(self, addresses, synapses=None):
        """
        Given the neurons addresses, returns all the addresses of its synapses.
        Useful for populating by giving only the soma addresses.
        If synapses is set, returns only those synapses (e.g., 'excitatory0').
        NOTE: The returned array preserves ordering of neurons (np.repeat)
        """

        ## Freshly generate addresses of synapses
        # somaddr = addresses
        # nd = len(addresses.dtype)
        # synaddr = []

        # for s in synapses:
        #    # Synapses can be multi-dimensional
        #    syn_block = self.neuronblock.synapses[s]

        #    # The following codes insures that the addresses are built it the
        #    # correct order
        #    ch = self.synapses[s].channel
        #    ch_ad = self.synapses[s].ch_addr[ch]
        #    snr = []
        #    # assumes human readable addresses are of the form neuron -
        #    # synapse. (could be anything)
        #    for field in ch_ad.addrConf:
        #        if field['type'] == -1:
        #            if field['id'] in syn_block.dims:
        #                snr.append(syn_block.dims[field['id']])
        #            else:
        #                snr.append([field['default_value']])
        #    print snr
        #    #The following three lines are a little intricate.
        #    #It first builds a grid of possible synapses (see _buildGrid)
        #    # Then combines it (column_stack) with all possible soma addresses
        #    # (which is already a grid)
        #    # Repeat and Tile make sure that the dimenstionalities are
        #    # consistent
        #    #and that all possible addresses are considered

        #    syngrid = _buildGrid(snr)
        #    pa = self.soma.addr
        #    somasyngrid = np.column_stack(
        #            [np.repeat(pa, len(syngrid), axis=0),
        #             np.tile(syngrid.T, len(pa)).T])
        #    synaddr.append(somasyngrid)

        # saddrout = np.array(_flatten(synaddr)).reshape((-1, nd + len(snr)))

        # return _sort_by_logical(saddrout)
        somaaddr = addresses
        dtp = AddrGroup._get_dtype(AddrGroup("", ""), self.setup, self.soma.chipid, "in")
        syn_dim_names = None
        synaddrs_all = None
        for s in synapses:
            syn_block = self.neuronblock.synapses[s]
            if syn_dim_names == None:
                syn_dim_names = syn_block.dims.keys()
                syndtp = np.dtype([(str(k), "uint32") for k in syn_dim_names])
                # NOTE: the str type cast is because numpy dtype doesn't support
                # unicode strings
            synaddrs = [syn_block.dims[fld] for fld in syn_dim_names]
            synaddrs = list(it.product(*synaddrs))
            if synaddrs_all == None:
                synaddrs_all = np.array(synaddrs)
            else:
                synaddrs_all = np.concatenate((synaddrs_all, synaddrs))
        # Syn addresses only
        synaddrs_all = synaddrs_all.astype("uint32").view(syndtp).T[0]
        # Syn addresses tiled for all soma addresses
        synaddrs_full = np.tile(synaddrs_all, len(addresses))
        # Soma addresses repeated for all synapse addresses
        soma_addr_all = addresses.repeat(len(synaddrs_all))
        # Full addresses space
        syn_addr_full = np.empty(soma_addr_all.shape, dtype=dtp)
        for fld in soma_addr_all.dtype.names:
            syn_addr_full[fld] = soma_addr_all[fld]
        for fld in synaddrs_all.dtype.names:
            syn_addr_full[fld] = synaddrs_full[fld]
        return syn_addr_full
Example #10
0
 def clear(self):
     """
     Clear the population back to its original state.
     """
     self.soma = AddrGroup("Empty group")
     self.synapses = dict()