def set(self, name, value): """ Set connection attributes for all connections on the local MPI node. `name` -- attribute name `value` -- the attribute numeric value, or a list/1D array of such values of the same length as the number of local connections, or a 2D array with the same dimensions as the connectivity matrix (as returned by `get(format='array')`). """ if not (common.is_number(value) or common.is_listlike(value)): raise TypeError( "Argument should be a numeric type (int, float...), a list, or a numpy array." ) if isinstance(value, numpy.ndarray) and len(value.shape) == 2: offset = (self.parent.pre.first_id, self.parent.post.first_id) value_list = [] connection_parameters = nest.GetStatus(self.connections) for conn in connection_parameters: addr = (conn['source'] - offset[0], conn['target'] - offset[1]) try: val = value[addr] except IndexError, e: raise IndexError("%s. addr=%s" % (e, addr)) if numpy.isnan(val): raise Exception( "Array contains no value for synapse from %d to %d" % (c.source, c.target)) else: value_list.append(val) value = value_list
def _set_spiketimes(self, spiketimes): assert common.is_listlike(spiketimes) if len(spiketimes) == 0 or common.is_number(spiketimes[0]): spiketimes = [spiketimes for i in xrange(len(self))] assert len(spiketimes) == len( self ), "spiketimes (length %d) must contain as many iterables as there are cells in the group (%d)." % ( len(spiketimes), len(self)) self._threshold.set_spike_times(spiketimes)
def connect(self, source, targets, weights, delays): """ Connect a neuron to one or more other neurons. `source` -- the ID of the pre-synaptic cell. `targets` -- a list/1D array of post-synaptic cell IDs, or a single ID. `weight` -- a list/1D array of connection weights, or a single weight. Must have the same length as `targets`. `delays` -- a list/1D array of connection delays, or a single delay. Must have the same length as `targets`. """ # are we sure the targets are all on the current node? if common.is_listlike(source): assert len(source) == 1 source = source[0] if not common.is_listlike(targets): targets = [targets] assert len(targets) > 0 if self.synapse_type not in ('excitatory', 'inhibitory', None): raise common.ConnectionError( "synapse_type must be 'excitatory', 'inhibitory', or None (equivalent to 'excitatory')" ) weights = weights * 1000.0 # weights should be in nA or uS, but iaf_neuron uses pA and iaf_cond_neuron uses nS. # Using convention in this way is not ideal. We should # be able to look up the units used by each model somewhere. if self.synapse_type == 'inhibitory' and common.is_conductance( targets[0]): weights = -1 * weights # NEST wants negative values for inhibitory weights, even if these are conductances if isinstance(weights, numpy.ndarray): weights = weights.tolist() elif isinstance(weights, float): weights = [weights] if isinstance(delays, numpy.ndarray): delays = delays.tolist() elif isinstance(delays, float): delays = [delays] try: nest.DivergentConnect([source], targets, weights, delays, self.synapse_model) except nest.NESTError, e: raise common.ConnectionError( "%s. source=%s, targets=%s, weights=%s, delays=%s, synapse model='%s'" % (e, source, targets, weights, delays, self.synapse_model))
def _probabilistic_connect(self, projection, p): """ Connect-up a Projection with connection probability p, where p may be either a float 0<=p<=1, or a dict containing a float array for each pre-synaptic cell, the array containing the connection probabilities for all the local targets of that pre-synaptic cell. """ if isinstance(projection.rng, random.NativeRNG): raise Exception("Use of NativeRNG not implemented.") else: rng = projection.rng local = projection.post._mask_local.flatten() is_conductance = common.is_conductance(projection.post.index(0)) for src in projection.pre.all(): # ( the following two lines are a nice idea, but this needs some thought for # the parallel case, to ensure reproducibility when varying the number # of processors # N = rng.binomial(npost,self.p_connect,1)[0] # targets = sample(postsynaptic_neurons, N) # ) N = projection.post.size # if running in parallel, rng.next(N) will not return N values, but only # as many as are needed on this node, as determined by mask_local. # Over the simulation as a whole (all nodes), N values will indeed be # returned. rarr = rng.next(N, 'uniform', (0, 1), mask_local=local) if not common.is_listlike(rarr) and common.is_number( rarr): # if N=1, rarr will be a single number rarr = numpy.array([rarr]) if common.is_number(p): create = rarr < p else: create = rarr < p[src][local] if create.shape != projection.post.local_cells.shape: logger.warning( "Too many random numbers. Discarding the excess. Did you specify MPI rank and number of processes when you created the random number generator?" ) create = create[:projection.post.local_cells.size] targets = projection.post.local_cells[create].tolist() weights = self.get_weights(N, local)[create] weights = common.check_weight(weights, projection.synapse_type, is_conductance) delays = self.get_delays(N, local)[create] if not self.allow_self_connections and projection.pre == projection.post and src in targets: assert len(targets) == len(weights) == len(delays) i = targets.index(src) weights = numpy.delete(weights, i) delays = numpy.delete(delays, i) targets.remove(src) if len(targets) > 0: projection.connection_manager.connect(src, targets, weights, delays)
def connect(self, source, targets, weights, delays): """ Connect a neuron to one or more other neurons with a static connection. `source` -- the ID of the pre-synaptic cell. `targets` -- a list/1D array of post-synaptic cell IDs, or a single ID. `weight` -- a list/1D array of connection weights, or a single weight. Must have the same length as `targets`. `delays` -- a list/1D array of connection delays, or a single delay. Must have the same length as `targets`. """ if not isinstance(source, (int, long)) or source < 0: errmsg = "Invalid source ID: %s" % source raise common.ConnectionError(errmsg) if not common.is_listlike(targets): targets = [targets] if isinstance(weights, float): weights = [weights] if isinstance(delays, float): delays = [delays] assert len(targets) > 0 for target in targets: if not isinstance(target, common.IDMixin): raise common.ConnectionError("Invalid target ID: %s" % target) assert len(targets) == len(weights) == len(delays), "%s %s %s" % ( len(targets), len(weights), len(delays)) if common.is_conductance(targets[0]): weight_scale_factor = 1e-6 # Convert from µS to S else: weight_scale_factor = 1e-9 # Convert from nA to A synapse_type = self.synapse_type or "excitatory" if isinstance(synapse_type, basestring): syn_target_id = ConnectionManager.synapse_target_ids[synapse_type] syn_factory = pypcsim.SimpleScalingSpikingSynapse( syn_target_id, weights[0], delays[0]) elif isinstance(synapse_type, pypcsim.SimObject): syn_factory = synapse_type else: raise common.ConnectionError( "synapse_type must be a string or a PCSIM synapse factory. Actual type is %s" % type(synapse_type)) for target, weight, delay in zip(targets, weights, delays): syn_factory.W = weight * weight_scale_factor syn_factory.delay = delay * 0.001 # ms --> s try: c = net.connect(source, target, syn_factory) except RuntimeError, e: raise common.ConnectionError(e) if target.local: self.connections.append( Connection(source, target, net.object(c), 1.0 / weight_scale_factor))
def set(self, name, value): """ Set connection attributes for all connections in this manager. `name` -- attribute name `value` -- the attribute numeric value, or a list/1D array of such values of the same length as the number of local connections, or a 2D array with the same dimensions as the connectivity matrix (as returned by `get(format='array')`). """ if self.parent is None: raise Exception( "Only implemented for connections created via a Projection object, not using connect()" ) synapse_obj = self.parent.post.celltype.synapses[self.parent.target or "excitatory"] weight_units = ("cond" in self.parent.post.celltype.__class__.__name__ ) and uS or nA ###print "in ConnectionManager.set(), weight_units = %s" % weight_units bc = self._get_brian_connection(self.parent.pre.brian_cells, self.parent.post.brian_cells, synapse_obj, weight_units) if name == 'weight': M = bc.W units = weight_units elif name == 'delay': M = bc.delay units = ms else: raise Exception( "Setting parameters other than weight and delay not yet supported." ) if common.is_number(value): for row in M.data: for i in range(len(row)): row[i] = value * units elif isinstance(value, numpy.ndarray) and len(value.shape) == 2: address_gen = ((i, j) for i, row in enumerate(bc.W.rows) for j in row) for (i, j) in address_gen: M[i, j] = value[i, j] * units elif common.is_listlike(value): assert len(value) == M.getnnz() address_gen = ((i, j) for i, row in enumerate(bc.W.rows) for j in row) for ((i, j), val) in izip(address_gen, value): M[i, j] = val * units else: raise Exception("Values must be scalars or lists/arrays")
def connect(self, source, targets, weights, delays): """ Connect a neuron to one or more other neurons with a static connection. `source` -- the ID of the pre-synaptic cell. `targets` -- a list/1D array of post-synaptic cell IDs, or a single ID. `weight` -- a list/1D array of connection weights, or a single weight. Must have the same length as `targets`. `delays` -- a list/1D array of connection delays, or a single delay. Must have the same length as `targets`. """ if not isinstance(source, int) or source > state.gid_counter or source < 0: errmsg = "Invalid source ID: %s (gid_counter=%d)" % ( source, state.gid_counter) raise common.ConnectionError(errmsg) if not common.is_listlike(targets): targets = [targets] if isinstance(weights, float): weights = [weights] if isinstance(delays, float): delays = [delays] assert len(targets) > 0 for target in targets: if not isinstance(target, common.IDMixin): raise common.ConnectionError("Invalid target ID: %s" % target) assert len(targets) == len(weights) == len(delays), "%s %s %s" % ( len(targets), len(weights), len(delays)) for target, weight, delay in zip(targets, weights, delays): if target.local: if self.synapse_type is None: self.synapse_type = weight >= 0 and 'excitatory' or 'inhibitory' if self.synapse_model == 'Tsodyks-Markram' and 'TM' not in self.synapse_type: self.synapse_type += '_TM' synapse_object = getattr(target._cell, self.synapse_type) nc = state.parallel_context.gid_connect( int(source), synapse_object) nc.weight[0] = weight nc.delay = delay # nc.threshold is supposed to be set by ParallelContext.threshold, called in _build_cell(), above, but this hasn't been tested self.connections.append(Connection(source, target, nc))
def connect(self, source, targets, weights, delays): """ Connect a neuron to one or more other neurons with a static connection. `source` -- the ID of the pre-synaptic cell. `targets` -- a list/1D array of post-synaptic cell IDs, or a single ID. `weight` -- a list/1D array of connection weights, or a single weight. Must have the same length as `targets`. `delays` -- a list/1D array of connection delays, or a single delay. Must have the same length as `targets`. """ #print "connecting", source, "to", targets, "with weights", weights, "and delays", delays if not common.is_listlike(targets): targets = [targets] if isinstance(weights, float): weights = [weights] if isinstance(delays, float): delays = [delays] assert len(targets) > 0 if not isinstance(source, common.IDMixin): raise common.ConnectionError( "source should be an ID object, actually %s" % type(source)) for target in targets: if not isinstance(target, common.IDMixin): raise common.ConnectionError("Invalid target ID: %s" % target) assert len(targets) == len(weights) == len(delays), "%s %s %s" % ( len(targets), len(weights), len(delays)) if common.is_conductance(targets[0]): units = uS else: units = nA synapse_type = self.synapse_type or "excitatory" synapse_obj = targets[0].cellclass.synapses[synapse_type] try: source_group = source.parent_group except AttributeError, errmsg: raise common.ConnectionError( "%s. Maybe trying to connect from non-existing cell (ID=%s)." % (errmsg, source))
def __init__(self, presynaptic_population, postsynaptic_population, method, source=None, target=None, synapse_dynamics=None, label=None, rng=None): """ presynaptic_population and postsynaptic_population - Population objects. source - string specifying which attribute of the presynaptic cell signals action potentials target - string specifying which synapse on the postsynaptic cell to connect to If source and/or target are not given, default values are used. method - a Connector object, encapsulating the algorithm to use for connecting the neurons. synapse_dynamics - a `SynapseDynamics` object specifying which synaptic plasticity mechanisms to use. rng - specify an RNG object to be used by the Connector.. """ """ PCSIM implementation specific comments: - source parameter does not have any meaning in context of PyPCSIM interface. Action potential signals are predefined by the neuron model and each cell has only one source, so there is no need to name a source since is implicitly known. - rng parameter is also not currently not applicable. For connection making only internal random number generators can be used. - The semantics of the target parameter is slightly changed: If it is a string then it represents a pcsim synapse class. If it is an integer then it represents which target(synapse) on the postsynaptic cell to connect to. It can be also a pcsim SimObjectFactory object which will be used for creation of the synapse objects associated to the created connections. """ common.Projection.__init__(self, presynaptic_population, postsynaptic_population, method, source, target, synapse_dynamics, label, rng) self.is_conductance = self.post.celltype.__class__.conductance_based self.synapse_shape = ("alpha" in self.post.celltype.__class__.__name__ ) and "alpha" or "exp" ### Determine connection decider ##decider, wiring_method, weight, delay = method.connect(self) ## ##weight = self.getWeight(weight) ##self.is_conductance = hasattr(self.post.pcsim_population.object(0),'ErevExc') ## ##if isinstance(weight, pyNN.random.RandomDistribution) or hasattr(weight, '__len__'): ## w = 1. ##else: ## w = self.convertWeight(weight, self.is_conductance) ## ##delay = self.getDelay(delay) ##if isinstance(delay, pyNN.random.RandomDistribution) or hasattr(delay, '__len__'): ## d = simulator.state.min_delay/1000. ##else: ## d = self.convertDelay(delay) ## # handle synapse dynamics if common.is_listlike(method.weights): w = method.weights[0] elif hasattr(method.weights, "next"): # random distribution w = 0.0 # actual value used here shouldn't matter. Actual values will be set in the Connector. else: w = method.weights if common.is_listlike(method.delays): d = min(method.delays) elif hasattr(method.delays, "next"): # random distribution d = get_min_delay( ) # actual value used here shouldn't matter. Actual values will be set in the Connector. else: d = method.delays plasticity_parameters = {} if self.synapse_dynamics: # choose the right model depending on whether we have conductance- or current-based synapses if self.is_conductance: possible_models = get_synapse_models("Cond") else: possible_models = get_synapse_models("Curr").union( get_synapse_models("CuBa")) if self.synapse_shape == 'alpha': possible_models = possible_models.intersection( get_synapse_models("Alpha")) else: possible_models = possible_models.intersection( get_synapse_models("Exp")).difference( get_synapse_models("DoubleExp")) if not self.is_conductance and self.synapse_shape is "exp": possible_models.add("StaticStdpSynapse") possible_models.add("StaticSpikingSynapse") possible_models.add("DynamicStdpSynapse") possible_models.add("DynamicSpikingSynapse") # we need to know the synaptic time constant, which is a property of the # post-synaptic cell in PyNN. Here, we get it from the Population initial # value, but this is a problem if tau_syn varies from cell to cell if target in (None, 'excitatory'): tau_syn = self.post.celltype.parameters['TauSynExc'] if self.is_conductance: e_syn = self.post.celltype.parameters['ErevExc'] elif target == 'inhibitory': tau_syn = self.post.celltype.parameters['TauSynInh'] if self.is_conductance: e_syn = self.post.celltype.parameters['ErevInh'] else: raise Exception( "Currently, target must be one of 'excitatory', 'inhibitory' with dynamic synapses" ) if self.is_conductance: plasticity_parameters.update(Erev=e_syn) weight_scale_factor = 1e-6 else: weight_scale_factor = 1e-9 if self.synapse_dynamics.fast: possible_models = possible_models.intersection( self.short_term_plasticity_mechanism) plasticity_parameters.update( self._short_term_plasticity_parameters) # perhaps need to ensure that STDP is turned off here, to be turned back on by the next block else: possible_models = possible_models.difference( dynamic_synapse_models) # imported from synapses module if self.synapse_dynamics.slow: possible_models = possible_models.intersection( self.long_term_plasticity_mechanism) plasticity_parameters.update(self._stdp_parameters) dendritic_delay = self.synapse_dynamics.slow.dendritic_delay_fraction * d transmission_delay = d - dendritic_delay plasticity_parameters.update({ 'back_delay': 2 * 0.001 * dendritic_delay, 'Winit': w * weight_scale_factor }) # hack to work around the limitations of the translation method if self.is_conductance: for name in self.synapse_dynamics.slow.weight_dependence.scales_with_weight: plasticity_parameters[ name] *= 1e3 # a scale factor of 1e-9 is always applied in the translation stage else: possible_models = possible_models.difference( stdp_synapse_models) plasticity_parameters.update({'W': w * weight_scale_factor}) if len(possible_models) == 0: raise common.NoModelAvailableError( "The synapse model requested is not available.") synapse_type = getattr(pypcsim, list(possible_models)[0]) try: self.syn_factory = synapse_type(delay=d, tau=tau_syn, **plasticity_parameters) except Exception, err: err.args = ( "%s\nActual arguments were: delay=%g, tau=%g, plasticity_parameters=%s" % (err.message, d, tau_syn, plasticity_parameters), ) + err.args[1:] raise
if common.is_number(value): for c in self: setattr(c, name, value) elif isinstance(value, numpy.ndarray) and len(value.shape) == 2: offset = (self.parent.pre.first_id, self.parent.post.first_id) for c in self.connections: addr = (c.source - offset[0], c.target - offset[1]) try: val = value[addr] except IndexError, e: raise IndexError("%s. addr=%s" % (e, addr)) if numpy.isnan(val): raise Exception( "Array contains no value for synapse from %d to %d" % (c.source, c.target)) else: setattr(c, name, val) elif common.is_listlike(value): for c, val in zip(self.connections, value): setattr(c, name, val) else: raise TypeError( "Argument should be a numeric type (int, float...), a list, or a numpy array." ) # --- Initialization, and module attributes ------------------------------------ net = None state = _State() del _State
class ConnectionManager(object): """ Manage synaptic connections, providing methods for creating, listing, accessing individual connections. """ def __init__(self, synapse_type, synapse_model=None, parent=None): """ Create a new ConnectionManager. `synapse_model` -- either None or 'Tsodyks-Markram'. `parent` -- the parent `Projection`, if any. """ global connection_managers self.connections = [] self.parent = parent self.synapse_type = synapse_type self.synapse_model = synapse_model connection_managers.append(self) def __getitem__(self, i): """Return the `i`th connection on the local MPI node.""" return self.connections[i] def __len__(self): """Return the number of connections on the local MPI node.""" return len(self.connections) def __iter__(self): """Return an iterator over all connections on the local MPI node.""" return iter(self.connections) def connect(self, source, targets, weights, delays): """ Connect a neuron to one or more other neurons with a static connection. `source` -- the ID of the pre-synaptic cell. `targets` -- a list/1D array of post-synaptic cell IDs, or a single ID. `weight` -- a list/1D array of connection weights, or a single weight. Must have the same length as `targets`. `delays` -- a list/1D array of connection delays, or a single delay. Must have the same length as `targets`. """ if not isinstance(source, int) or source > state.gid_counter or source < 0: errmsg = "Invalid source ID: %s (gid_counter=%d)" % ( source, state.gid_counter) raise common.ConnectionError(errmsg) if not common.is_listlike(targets): targets = [targets] if isinstance(weights, float): weights = [weights] if isinstance(delays, float): delays = [delays] assert len(targets) > 0 for target in targets: if not isinstance(target, common.IDMixin): raise common.ConnectionError("Invalid target ID: %s" % target) assert len(targets) == len(weights) == len(delays), "%s %s %s" % ( len(targets), len(weights), len(delays)) for target, weight, delay in zip(targets, weights, delays): if target.local: if self.synapse_type is None: self.synapse_type = weight >= 0 and 'excitatory' or 'inhibitory' if self.synapse_model == 'Tsodyks-Markram' and 'TM' not in self.synapse_type: self.synapse_type += '_TM' synapse_object = getattr(target._cell, self.synapse_type) nc = state.parallel_context.gid_connect( int(source), synapse_object) nc.weight[0] = weight nc.delay = delay # nc.threshold is supposed to be set by ParallelContext.threshold, called in _build_cell(), above, but this hasn't been tested self.connections.append(Connection(source, target, nc)) def get(self, parameter_name, format, offset=(0, 0)): """ Get the values of a given attribute (weight, delay, etc) for all connections on the local MPI node. `parameter_name` -- name of the attribute whose values are wanted. `format` -- "list" or "array". Array format implicitly assumes that all connections belong to a single Projection. `offset` -- an (i,j) tuple giving the offset to be used in converting source and target IDs to array indices. Return a list or a 2D Numpy array. The array element X_ij contains the attribute value for the connection from the ith neuron in the pre- synaptic Population to the jth neuron in the post-synaptic Population, if a single such connection exists. If there are no such connections, X_ij will be NaN. If there are multiple such connections, the summed value will be given, which makes some sense for weights, but is pretty meaningless for delays. """ if format == 'list': values = [getattr(c, parameter_name) for c in self.connections] elif format == 'array': values = numpy.nan * numpy.ones( (self.parent.pre.size, self.parent.post.size)) for c in self.connections: value = getattr(c, parameter_name) addr = (c.source - offset[0], c.target - offset[1]) if numpy.isnan(values[addr]): values[addr] = value else: values[addr] += value else: raise Exception("format must be 'list' or 'array'") return values def set(self, name, value): """ Set connection attributes for all connections on the local MPI node. `name` -- attribute name `value` -- the attribute numeric value, or a list/1D array of such values of the same length as the number of local connections, or a 2D array with the same dimensions as the connectivity matrix (as returned by `get(format='array')`). """ if common.is_number(value): for c in self: setattr(c, name, value) elif isinstance(value, numpy.ndarray) and len(value.shape) == 2: offset = (self.parent.pre.first_id, self.parent.post.first_id) for c in self.connections: addr = (c.source - offset[0], c.target - offset[1]) try: val = value[addr] except IndexError, e: raise IndexError("%s. addr=%s" % (e, addr)) if numpy.isnan(val): raise Exception( "Array contains no value for synapse from %d to %d" % (c.source, c.target)) else: setattr(c, name, val) elif common.is_listlike(value): for c, val in zip(self.connections, value): setattr(c, name, val)