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)
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)
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_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,)
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__()
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 clear(self): """ Clear the population back to its original state. """ self.soma = AddrGroup('Empty group') self.synapses = dict()
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__()
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 clear(self): """ Clear the population back to its original state. """ self.soma = AddrGroup("Empty group") self.synapses = dict()