def connect(self, projection): """Connect-up a Projection.""" idx = numpy.argsort(self.conn_list[:, 0]) self.sources = numpy.unique(self.conn_list[:, 0]).astype(int) self.candidates = projection.post.local_cells self.conn_list = self.conn_list[idx] self.progressbar(len(self.sources)) count = 0 left = numpy.searchsorted(self.conn_list[:, 0], self.sources, 'left') right = numpy.searchsorted(self.conn_list[:, 0], self.sources, 'right') #tests = "|".join(['(tgts == %d)' %id for id in self.candidates]) for src, l, r in zip(self.sources, left, right): targets = self.conn_list[l:r, 1].astype(int) weights = self.conn_list[l:r, 2] delays = self.conn_list[l:r, 3] try: src = projection.pre.all_cells[src] except IndexError: raise errors.ConnectionError("invalid source index %s" % src) try: tgts = projection.post.all_cells[targets] except IndexError: raise errors.ConnectionError("invalid target index or indices") ## We need to exclude the non local cells. Fastidious, need maybe ## to use a convergent_connect method, instead of a divergent_connect one #idx = eval(tests) #projection.connection_manager.connect(src, tgts[idx].tolist(), weights[idx], delays[idx]) projection.connection_manager.connect(src, tgts.tolist(), weights, delays) self.progression(count) count += 1
def __init__(self, presynaptic_neurons, postsynaptic_neurons, connector, synapse_type=None, source=None, receptor_type=None, space=Space(), label=None): """ Create a new projection, connecting the pre- and post-synaptic neurons. """ for prefix, pop in zip(("pre", "post"), (presynaptic_neurons, postsynaptic_neurons)): if not isinstance(pop, (BasePopulation, Assembly)): raise errors.ConnectionError("%ssynaptic_neurons must be a Population, PopulationView or Assembly, not a %s" % (prefix, type(pop))) if isinstance(postsynaptic_neurons, Assembly): if not postsynaptic_neurons._homogeneous_synapses: raise errors.ConnectionError('Projection to an Assembly object can be made only with homogeneous synapses types') self.pre = presynaptic_neurons # } these really self.source = source # } should be self.post = postsynaptic_neurons # } read-only self.receptor_type = receptor_type or 'excitatory' # TO FIX: if weights are negative, default should be 'inhibitory' if self.receptor_type not in postsynaptic_neurons.receptor_types: valid_types = postsynaptic_neurons.receptor_types assert len(valid_types) > 0 errmsg = "User gave synapse_type=%s, synapse_type must be one of: '%s'" raise errors.ConnectionError(errmsg % (self.receptor_type, "', '".join(valid_types))) self.label = label self.space = space self._connector = connector self.synapse_type = synapse_type or self._static_synapse_class() assert isinstance(self.synapse_type, models.BaseSynapseType), \ "The synapse_type argument must be a models.BaseSynapseType object, not a %s" % type(synapse_type) if label is None: if self.pre.label and self.post.label: self.label = "%s→%s" % (self.pre.label, self.post.label) Projection._nProj += 1
def check_weights(weights, projection): # if projection.post is an Assembly, some components might have cond-synapses, others curr, # so need a more sophisticated check here. For now, skipping check and emitting a warning if hasattr(projection.post, "_homogeneous_synapses" ) and not projection.post._homogeneous_synapses: warnings.warn( "Not checking weights due to due mixture of synapse types") if isinstance(weights, np.ndarray): all_negative = (weights <= 0).all() all_positive = (weights >= 0).all() if not (all_negative or all_positive): raise errors.ConnectionError( "Weights must be either all positive or all negative") elif np.isreal(weights): all_positive = weights >= 0 all_negative = weights <= 0 else: raise errors.ConnectionError( "Weights must be a number or an array of numbers.") if projection.post.conductance_based or projection.receptor_type == 'excitatory': if not all_positive: raise errors.ConnectionError( "Weights must be positive for conductance-based and/or excitatory synapses" ) elif projection.post.conductance_based is False and projection.receptor_type == 'inhibitory': if not all_negative: raise errors.ConnectionError( "Weights must be negative for current-based, inhibitory synapses" ) else: # This should never happen. raise Exception("Can't check weight, conductance status unknown.")
def connect(self, projection): """Connect-up a Projection.""" idx = numpy.argsort(self.conn_list[:, 1]) self.targets = numpy.unique(self.conn_list[:, 1]).astype(numpy.int) self.candidates = projection.pre.all_cells self.conn_list = self.conn_list[idx] self.progressbar(len(self.targets)) count = 0 left = numpy.searchsorted(self.conn_list[:, 1], self.targets, 'left') right = numpy.searchsorted(self.conn_list[:, 1], self.targets, 'right') for tgt, l, r in zip(self.targets, left, right): sources = self.conn_list[l:r, 0].astype(numpy.int) weights = self.conn_list[l:r, 2] delays = self.conn_list[l:r, 3] srcs = projection.pre.all_cells[sources] try: srcs = projection.pre.all_cells[sources] except IndexError: raise errors.ConnectionError("invalid sources index or indices") try: tgt = projection.post.all_cells[tgt] except IndexError: raise errors.ConnectionError("invalid target index %d" %tgt) ## We need to exclude the non local cells. Fastidious, need maybe ## to use a convergent_connect method, instead of a divergent_connect one #idx = eval(tests) #projection.connection_manager.connect(src, tgts[idx].tolist(), weights[idx], delays[idx]) projection._convergent_connect(srcs.tolist(), tgt, weights, delays) self.progression(count, projection._simulator.state.mpi_rank) count += 1
def connect(self, source, targets, weights, delays, homogeneous=False): """ 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 core.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 errors.ConnectionError("source should be an ID object, actually %s" % type(source)) for target in targets: if not isinstance(target, common.IDMixin): raise errors.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" try: source_group = source.parent_group except AttributeError, errmsg: raise errors.ConnectionError("%s. Maybe trying to connect from non-existing cell (ID=%s)." % (errmsg, source))
def __init__(self, weights=0.0, delays=None, space=Space(), safe=True, verbose=False): self.weights = weights self.space = space self.safe = safe self.verbose = verbose min_delay = common.get_min_delay() if delays is None: self.delays = min_delay else: if core.is_listlike(delays): if min(delays) < min_delay: raise errors.ConnectionError( "smallest delay (%g) is smaller than minimum delay (%g)" % (min(delays), min_delay)) elif not (isinstance(delays, basestring) or isinstance(delays, RandomDistribution)): if delays < min_delay: raise errors.ConnectionError( "delay (%g) is smaller than minimum delay (%g)" % (delays, min_delay)) self.delays = delays
def check_weights(weights, projection): # if projection.post is an Assembly, some components might have cond-synapses, others curr, so need a more sophisticated check here synapse_sign = projection.receptor_type is_conductance = projection.post.conductance_based if isinstance(weights, numpy.ndarray): all_negative = (weights <= 0).all() all_positive = (weights >= 0).all() if not (all_negative or all_positive): raise errors.ConnectionError( "Weights must be either all positive or all negative") elif numpy.isreal(weights): all_positive = weights >= 0 all_negative = weights < 0 else: raise errors.ConnectionError( "Weights must be a number or an array of numbers.") if is_conductance or synapse_sign == 'excitatory': if not all_positive: raise errors.ConnectionError( "Weights must be positive for conductance-based and/or excitatory synapses" ) elif is_conductance == False and synapse_sign == 'inhibitory': if not all_negative: raise errors.ConnectionError( "Weights must be negative for current-based, inhibitory synapses" ) else: # This should never happen. raise Exception("Can't check weight, conductance status unknown.")
def __init__(self, presynaptic_neurons, postsynaptic_neurons, connector, synapse_type=None, source=None, receptor_type=None, space=Space(), label=None): """ Create a new projection, connecting the pre- and post-synaptic neurons. """ if not hasattr(self, "_simulator"): errmsg = "`common.Projection` should not be instantiated directly. " \ "You should import Projection from a PyNN backend module, " \ "e.g. pyNN.nest or pyNN.neuron" raise Exception(errmsg) for prefix, pop in zip(("pre", "post"), (presynaptic_neurons, postsynaptic_neurons)): if not isinstance(pop, (BasePopulation, Assembly)): raise errors.ConnectionError( "%ssynaptic_neurons must be a Population, PopulationView or Assembly, not a %s" % (prefix, type(pop))) if isinstance(postsynaptic_neurons, Assembly): if not postsynaptic_neurons._homogeneous_synapses: raise errors.ConnectionError( 'Projection to an Assembly object can be made only with homogeneous synapses types' ) self.pre = presynaptic_neurons # } these really self.source = source # } should be self.post = postsynaptic_neurons # } read-only if receptor_type == "default": receptor_type = None self.receptor_type = receptor_type or sorted( postsynaptic_neurons.receptor_types)[0] # TO FIX: if weights are negative, default should be the first inhibitory receptor type, # not necessarily the first in alphabetical order. # Should perhaps explicitly specify the default type(s) if self.receptor_type not in postsynaptic_neurons.receptor_types: valid_types = postsynaptic_neurons.receptor_types assert len(valid_types) > 0 errmsg = "User gave receptor_types=%s, receptor_types must be one of: '%s'" raise errors.ConnectionError( errmsg % (self.receptor_type, "', '".join(valid_types))) self.label = label self.space = space self._connector = connector self.synapse_type = synapse_type or self._static_synapse_class() assert isinstance(self.synapse_type, models.BaseSynapseType), \ "The synapse_type argument must be a models.BaseSynapseType object, not a %s" % type(synapse_type) if label is None: if self.pre.label and self.post.label: self.label = u"%s→%s" % (self.pre.label, self.post.label) self.initial_values = {} self.annotations = {} Projection._nProj += 1
def __init__(self, presynaptic_neurons, postsynaptic_neurons, connector, synapse_type=None, source=None, receptor_type=None, space=Space(), label=None): """ Create a new projection, connecting the pre- and post-synaptic neurons. """ if not hasattr(self, "_simulator"): errmsg = "`common.Projection` should not be instantiated directly. " \ "You should import Projection from a PyNN backend module, " \ "e.g. pyNN.nest or pyNN.neuron" raise Exception(errmsg) for prefix, pop in zip(("pre", "post"), (presynaptic_neurons, postsynaptic_neurons)): if not isinstance(pop, (BasePopulation, Assembly)): raise errors.ConnectionError( "%ssynaptic_neurons must be a Population, PopulationView or Assembly, not a %s" % (prefix, type(pop))) if isinstance(postsynaptic_neurons, Assembly): if not postsynaptic_neurons._homogeneous_synapses: raise errors.ConnectionError( 'Projection to an Assembly object can be made only with homogeneous synapses types') self.pre = presynaptic_neurons # } these really self.source = source # } should be self.post = postsynaptic_neurons # } read-only self.label = label self.space = space if not isinstance(connector, Connector): raise TypeError( "The connector argument should be an instance of a subclass of Connector. " f"The argument provided was of type '{type(connector).__name__}'." ) self._connector = connector self.synapse_type = synapse_type or self._static_synapse_class() if not isinstance(self.synapse_type, models.BaseSynapseType): raise TypeError( "The synapse_type argument should be an instance of a subclass of BaseSynapseType. " f"The argument provided was of type '{type(synapse_type).__name__}'" ) self.receptor_type = receptor_type if self.receptor_type in ("default", None): self._guess_receptor_type() if self.receptor_type not in postsynaptic_neurons.receptor_types: valid_types = postsynaptic_neurons.receptor_types assert len(valid_types) > 0 errmsg = "User gave receptor_types=%s, receptor_types must be one of: '%s'" raise errors.ConnectionError(errmsg % (self.receptor_type, "', '".join(valid_types))) if label is None: if self.pre.label and self.post.label: self.label = u"%s→%s" % (self.pre.label, self.post.label) self.initial_values = {} self.annotations = {} Projection._nProj += 1
def _convergent_connect(self, sources, target, weights, delays): """ Connect a neuron to one or more other neurons with a static connection. `sources` -- a list/1D array of pre-synaptic cell IDs, or a single ID. `target` -- the ID of the post-synaptic cell. `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(target, (int, long)) or target < 0: errmsg = "Invalid target ID: %s" % target raise errors.ConnectionError(errmsg) if not core.is_listlike(sources): sources = [sources] if isinstance(weights, float): weights = [weights] if isinstance(delays, float): delays = [delays] assert len(sources) > 0 for source in sources: if not isinstance(source, common.IDMixin): raise errors.ConnectionError("Invalid source ID: %s" % source) assert len(sources) == len(weights) == len(delays), "%s %s %s" % ( len(sources), len(weights), len(delays)) if common.is_conductance(target): weight_scale_factor = 1e-6 # Convert from µS to S else: weight_scale_factor = 1e-9 # Convert from nA to A synapse_type = self.syn_factory or "excitatory" if isinstance(synapse_type, basestring): syn_target_id = Projection.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 errors.ConnectionError( "synapse_type must be a string or a PCSIM synapse factory. Actual type is %s" % type(synapse_type)) for source, weight, delay in zip(sources, weights, delays): syn_factory.W = weight * weight_scale_factor syn_factory.delay = delay * 0.001 # ms --> s try: c = simulator.net.connect(source, target, syn_factory) except RuntimeError as e: raise errors.ConnectionError(e) if target.local: self.connections.append( simulator.Connection(source, target, simulator.net.object(c), 1.0 / weight_scale_factor))
def _divergent_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 core.is_listlike(source): assert len(source) == 1 source = source[0] if not core.is_listlike(targets): targets = [targets] assert len(targets) > 0 if self.synapse_type not in targets[0].celltype.synapse_types: raise errors.ConnectionError( "User gave synapse_type=%s, synapse_type must be one of: %s" % (self.synapse_type, "'" + "', '".join(st for st in targets[0].celltype.synapse_types or ['*No connections supported*'])) + "'") weights = numpy.array( 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 # 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] if targets[0].celltype.standard_receptor_type: try: nest.DivergentConnect([source], targets, weights, delays, self.synapse_model) except nest.NESTError, e: raise errors.ConnectionError( "%s. source=%s, targets=%s, weights=%s, delays=%s, synapse model='%s'" % (e, source, targets, weights, delays, self.synapse_model))
def check_delays(delays, projection): min_delay = projection._simulator.state.min_delay max_delay = projection._simulator.state.max_delay if isinstance(delays, numpy.ndarray): below_max = (delays <= max_delay).all() above_min = (delays >= min_delay).all() in_range = below_max and above_min elif numpy.isreal(delays): in_range = min_delay <= delays <= max_delay else: raise errors.ConnectionError("Delays must be a number or an array of numbers.") if not in_range: raise errors.ConnectionError("Delay (%s) is out of range [%s, %s]" % (delays, min_delay, max_delay))
def _convergent_connect(self, presynaptic_indices, postsynaptic_index, **connection_parameters): """ Connect a neuron to one or more other neurons with a static connection. `sources` -- a 1D array of pre-synaptic cell IDs `target` -- the ID of the post-synaptic cell. TO UPDATE """ #logger.debug("Connecting to index %s from %s with %s" % (postsynaptic_index, presynaptic_indices, connection_parameters)) presynaptic_cells = self.pre[presynaptic_indices].all_cells postsynaptic_cell = self.post[postsynaptic_index] assert len(presynaptic_cells) > 0, presynaptic_cells weights = connection_parameters.pop('weight') if self.receptor_type == 'inhibitory' and self.post.conductance_based: weights *= -1 # NEST wants negative values for inhibitory weights, even if these are conductances delays = connection_parameters.pop('delay') if postsynaptic_cell.celltype.standard_receptor_type: try: nest.ConvergentConnect( presynaptic_cells.astype(int).tolist(), [int(postsynaptic_cell)], listify(weights), listify(delays), self.nest_synapse_model) except nest.NESTError, e: raise errors.ConnectionError( "%s. presynaptic_cells=%s, postsynaptic_cell=%s, weights=%s, delays=%s, synapse model='%s'" % (e, presynaptic_cells, postsynaptic_cell, weights, delays, self.nest_synapse_model))
def _convergent_connect(self, presynaptic_indices, postsynaptic_index, **connection_parameters): """ Connect a neuron to one or more other neurons with a static connection. `presynaptic_cells` -- a 1D array of pre-synaptic cell IDs `postsynaptic_cell` -- the ID of the post-synaptic cell. `connection_parameters` -- each parameter should be either a 1D array of the same length as `sources`, or a single value. """ #logger.debug("Convergent connect. Weights=%s" % connection_parameters['weight']) postsynaptic_cell = self.post[postsynaptic_index] if not isinstance( postsynaptic_cell, int ) or postsynaptic_cell > simulator.state.gid_counter or postsynaptic_cell < 0: errmsg = "Invalid post-synaptic cell: %s (gid_counter=%d)" % ( postsynaptic_cell, simulator.state.gid_counter) raise errors.ConnectionError(errmsg) for name, value in connection_parameters.items(): if isinstance(value, (float, int)): connection_parameters[name] = repeat(value) assert postsynaptic_cell.local for pre_idx, values in core.ezip(presynaptic_indices, *connection_parameters.values()): parameters = dict(zip(connection_parameters.keys(), values)) #logger.debug("Connecting neuron #%s to neuron #%s with synapse type %s, receptor type %s, parameters %s", pre_idx, postsynaptic_index, self.synapse_type, self.receptor_type, parameters) self._connections[postsynaptic_index][pre_idx].append( self.synapse_type.connection_type(self, pre_idx, postsynaptic_index, **parameters))
def connect(self, projection): """Connect-up a Projection.""" logger.debug("conn_list (original) = \n%s", self.conn_list) if numpy.any(self.conn_list[:, 0] >= projection.pre.size): raise errors.ConnectionError("source index out of range") if (self.conn_list.shape[1] < 3 or self.conn_list.shape[1] > 4 or (self.conn_list.shape[1] == 3 and projection.synapse_type.has_parameter('delay'))): raise errors.ConnectionError( "incompatible number of columns for connection list requires " "4 (3 for synapse type without delay)") # need to do some profiling, to figure out the best way to do this: # - order of sorting/filtering by local # - use numpy.unique, or just do in1d(self.conn_list)? idx = numpy.argsort(self.conn_list[:, 1]) targets = numpy.unique(self.conn_list[:, 1]).astype(numpy.int) local = numpy.in1d( targets, numpy.arange(projection.post.size)[projection.post._mask_local], assume_unique=True) local_targets = targets[local] self.conn_list = self.conn_list[idx] left = numpy.searchsorted(self.conn_list[:, 1], local_targets, 'left') right = numpy.searchsorted(self.conn_list[:, 1], local_targets, 'right') logger.debug("idx = %s", idx) logger.debug("targets = %s", targets) logger.debug("local_targets = %s", local_targets) logger.debug("conn_list (sorted by target) = \n%s", self.conn_list) logger.debug("left = %s", left) logger.debug("right = %s", right) schema = projection.synapse_type.get_schema() for tgt, l, r in zip(local_targets, left, right): sources = self.conn_list[l:r, 0].astype(numpy.int) param_dict = {'weight': self.conn_list[l:r, 2]} if self.conn_list.shape[1] == 4: param_dict['delay'] = self.conn_list[l:r, 3] connection_parameters = ParameterSpace(param_dict, schema=schema, shape=(r - l, )) if isinstance(projection.synapse_type, StandardSynapseType): connection_parameters = projection.synapse_type.translate( connection_parameters) connection_parameters.evaluate() projection._convergent_connect(sources, tgt, **connection_parameters)
def check(self, delay): all_negative = (delay <= self.max_delay).all() all_positive = (delay >= self.min_delay).all( ) # If the delay is too small , we have to throw an error if not (all_negative and all_positive): raise errors.ConnectionError( "delay (%s) is out of range [%s,%s]" % (delay, common.get_min_delay(), common.get_max_delay())) return delay
def _convergent_connect(self, sources, target, weights, delays): """ Connect one or more neurons to a single post-synaptic neuron. `sources` -- a list/1D array of pre-synaptic cell IDs, or a single ID. `target` -- the ID of the post-synaptic cell. `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 core.is_listlike(target): assert len(target) == 1 target = target[0] if not core.is_listlike(sources): sources = [sources] assert len(sources) > 0, sources if self.synapse_type not in ('excitatory', 'inhibitory', None): raise errors.ConnectionError( "synapse_type must be 'excitatory', 'inhibitory', or None (equivalent to 'excitatory')" ) weights = numpy.array( 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(target): 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.ConvergentConnect(sources, [target], weights, delays, self.synapse_model) except nest.NESTError, e: raise errors.ConnectionError( "%s. sources=%s, target=%s, weights=%s, delays=%s, synapse model='%s'" % (e, sources, target, weights, delays, self.synapse_model))
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 errors.ConnectionError(errmsg) if not core.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 errors.ConnectionError("Invalid target ID: %s" % target) assert len(targets) == len(weights) == len(delays), "%s %s %s" % ( len(targets), len(weights), len(delays)) self._resolve_synapse_type() for target, weight, delay in zip(targets, weights, delays): if target.local: if "." in self.synapse_type: section, synapse_type = self.synapse_type.split(".") synapse_object = getattr(getattr(target._cell, section), synapse_type) else: 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 check(self, delay): min_delay = self.kernel.min_delay max_delay = self.kernel.max_delay all_negative = (delay <= max_delay).all() all_positive = (delay >= min_delay).all( ) # If the delay is too small , we have to throw an error if not (all_negative and all_positive): raise errors.ConnectionError("delay (%s) is out of range [%s,%s]" % (delay, min_delay, max_delay)) return delay
def convergent_connect(self, sources, target, weights, delays): """ Connect a neuron to one or more other neurons with a static connection. `sources` -- a list/1D array of pre-synaptic cell IDs, or a single ID. `target` -- the ID of the post-synaptic cell. `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(target, int) or target > state.gid_counter or target < 0: errmsg = "Invalid target ID: %s (gid_counter=%d)" % ( target, state.gid_counter) raise errors.ConnectionError(errmsg) if not core.is_listlike(sources): sources = [sources] if isinstance(weights, float): weights = [weights] if isinstance(delays, float): delays = [delays] assert len(sources) > 0 for source in sources: if not isinstance(source, common.IDMixin): raise errors.ConnectionError("Invalid source ID: %s" % source) assert len(sources) == len(weights) == len(delays), "%s %s %s" % ( len(sources), len(weights), len(delays)) if target.local: for source, weight, delay in zip(sources, weights, delays): 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, projection): if (projection.pre != self.reference_projection.pre or projection.post != self.reference_projection.post): raise errors.ConnectionError("Pre and post populations must match between reference ({0}" " and {1}) and clone projections ({2} and {3}) for " "CloneConnector" .format(self.reference_projection.pre, self.reference_projection.post, projection.pre, projection.post)) connection_map = LazyArray(~np.isnan(self.reference_projection.get(['weight'], 'array', gather='all')[0])) self._connect_with_map(projection, connection_map)
def connect(self, projection): """Connect-up a Projection.""" logger.debug("conn_list (original) = \n%s", self.conn_list) synapse_parameter_names = projection.synapse_type.get_parameter_names() for name in self.column_names: if name not in synapse_parameter_names: raise ValueError( "%s is not a valid parameter for %s" % (name, projection.synapse_type.__class__.__name__)) if self.conn_list.size == 0: return if numpy.any(self.conn_list[:, 0] >= projection.pre.size): raise errors.ConnectionError("source index out of range") # need to do some profiling, to figure out the best way to do this: # - order of sorting/filtering by local # - use numpy.unique, or just do in1d(self.conn_list)? idx = numpy.argsort(self.conn_list[:, 1]) targets = numpy.unique(self.conn_list[:, 1]).astype(numpy.int) local = numpy.in1d( targets, numpy.arange(projection.post.size)[projection.post._mask_local], assume_unique=True) local_targets = targets[local] self.conn_list = self.conn_list[idx] left = numpy.searchsorted(self.conn_list[:, 1], local_targets, 'left') right = numpy.searchsorted(self.conn_list[:, 1], local_targets, 'right') logger.debug("idx = %s", idx) logger.debug("targets = %s", targets) logger.debug("local_targets = %s", local_targets) logger.debug("conn_list (sorted by target) = \n%s", self.conn_list) logger.debug("left = %s", left) logger.debug("right = %s", right) for tgt, l, r in zip(local_targets, left, right): sources = self.conn_list[l:r, 0].astype(numpy.int) connection_parameters = deepcopy( projection.synapse_type.parameter_space) connection_parameters.shape = (r - l, ) for col, name in enumerate(self.column_names, 2): connection_parameters.update( **{name: self.conn_list[l:r, col]}) if isinstance(projection.synapse_type, StandardSynapseType): connection_parameters = projection.synapse_type.translate( connection_parameters) connection_parameters.evaluate() projection._convergent_connect(sources, tgt, **connection_parameters)
def __init__(self, presynaptic_neurons, postsynaptic_neurons, method, source=None, target=None, synapse_dynamics=None, label=None, rng=None): """ presynaptic_neurons and postsynaptic_neurons - Population, PopulationView or Assembly objects. source - string specifying which attribute of the presynaptic cell signals action potentials. This is only needed for multicompartmental cells with branching axons or dendrodendriticsynapses. All standard cells have a single source, and this is the default. target - string specifying which synapse on the postsynaptic cell to connect to. For standard cells, this can be 'excitatory' or 'inhibitory'. For non-standard cells, it could be 'NMDA', etc. If target is not given, the default values of 'excitatory' is used. method - a Connector object, encapsulating the algorithm to use for connecting the neurons. synapse_dynamics - a `standardmodels.SynapseDynamics` object specifying which synaptic plasticity mechanisms to use. rng - specify an RNG object to be used by the Connector. """ for prefix, pop in zip(("pre", "post"), (presynaptic_neurons, postsynaptic_neurons)): if not isinstance(pop, (BasePopulation, Assembly)): raise errors.ConnectionError( "%ssynaptic_neurons must be a Population, PopulationView or Assembly, not a %s" % (prefix, type(pop))) if isinstance(postsynaptic_neurons, Assembly): if not postsynaptic_neurons._homogeneous_synapses: raise Exception( 'Projection to an Assembly object can be made only with homogeneous synapses types' ) self.pre = presynaptic_neurons # } these really self.source = source # } should be self.post = postsynaptic_neurons # } read-only self.target = target # } self.label = label if isinstance(rng, random.AbstractRNG): self.rng = rng elif rng is None: self.rng = random.NumpyRNG(seed=151985012) else: raise Exception( "rng must be either None, or a subclass of pyNN.random.AbstractRNG" ) self._method = method self.synapse_dynamics = synapse_dynamics #self.connection = None # access individual connections. To be defined by child, simulator-specific classes self.weights = [] if label is None: if self.pre.label and self.post.label: self.label = "%s→%s" % (self.pre.label, self.post.label) if self.synapse_dynamics: assert isinstance(self.synapse_dynamics, models.BaseSynapseDynamics), \ "The synapse_dynamics argument, if specified, must be a models.BaseSynapseDynamics object, not a %s" % type(synapse_dynamics)
def _convergent_connect(self, presynaptic_indices, postsynaptic_index, **connection_parameters): """ Connect a neuron to one or more other neurons with a static connection. `sources` -- a 1D array of pre-synaptic cell IDs `target` -- the ID of the post-synaptic cell. TO UPDATE """ #logger.debug("Connecting to index %s from %s with %s" % (postsynaptic_index, presynaptic_indices, connection_parameters)) presynaptic_cells = self.pre.all_cells[presynaptic_indices] postsynaptic_cell = self.post[postsynaptic_index] assert presynaptic_cells.size == presynaptic_indices.size assert len(presynaptic_cells) > 0, presynaptic_cells weights = connection_parameters.pop('weight') if self.receptor_type == 'inhibitory' and self.post.conductance_based: weights *= -1 # NEST wants negative values for inhibitory weights, even if these are conductances if hasattr(self.post, "celltype") and hasattr( self.post.celltype, "receptor_scale"): # this is a bit of a hack weights *= self.post.celltype.receptor_scale # needed for the Izhikevich model delays = connection_parameters.pop('delay') # Create connections, with weights and delays # Setting other connection parameters is done afterwards if postsynaptic_cell.celltype.standard_receptor_type: try: if not numpy.isscalar(weights): weights = numpy.array([weights]) if not numpy.isscalar(delays): delays = numpy.array([delays]) syn_dict = { 'model': self.nest_synapse_model, 'weight': weights, 'delay': delays, 'synapse_label': self.nest_synapse_label, } if 'tsodyks' in self.nest_synapse_model: if self.receptor_type == 'inhibitory': param_name = self.post.local_cells[ 0].celltype.translations['tau_syn_I'][ 'translated_name'] elif self.receptor_type == 'excitatory': param_name = self.post.local_cells[ 0].celltype.translations['tau_syn_E'][ 'translated_name'] else: raise NotImplementedError() syn_dict.update({ 'tau_psc': numpy.array([[ nest.GetStatus([postsynaptic_cell], param_name)[0] ] * len(presynaptic_cells.astype(int).tolist())]) }) nest.Connect( presynaptic_cells.astype(int).tolist(), [int(postsynaptic_cell)], 'all_to_all', syn_dict) except nest.kernel.NESTError as e: errmsg = "%s. presynaptic_cells=%s, postsynaptic_cell=%s, weights=%s, delays=%s, synapse model='%s'" % ( e, presynaptic_cells, postsynaptic_cell, weights, delays, self.nest_synapse_model) raise errors.ConnectionError(errmsg) else: receptor_type = postsynaptic_cell.celltype.get_receptor_type( self.receptor_type) if numpy.isscalar(weights): weights = repeat(weights) if numpy.isscalar(delays): delays = repeat(delays) for pre, w, d in zip(presynaptic_cells, weights, delays): syn_dict = { 'weight': w, 'delay': d, 'receptor_type': receptor_type, 'model': self.nest_synapse_model, 'synapse_label': self.nest_synapse_label } if 'tsodyks' in self.nest_synapse_model: syn_dict.update({ 'tau_psc': numpy.array([[ nest.GetStatus([postsynaptic_cell], param_name)[0] ] * len(presynaptic_cells.astype(int).tolist())]) }) nest.Connect([pre], [postsynaptic_cell], 'one_to_one', syn_dict) # Book-keeping self._connections = None # reset the caching of the connection list, since this will have to be recalculated self._sources.extend(presynaptic_cells) # Clean the connection parameters connection_parameters.pop( 'tau_minus', None) # TODO: set tau_minus on the post-synaptic cells connection_parameters.pop('dendritic_delay_fraction', None) connection_parameters.pop('w_min_always_zero_in_NEST', None) # We need to distinguish between common synapse parameters and local ones # We just get the parameters of the first connection (is there an easier way?) if self._common_synapse_property_names is None: self._identify_common_synapse_properties() # Set connection parameters other than weight and delay if connection_parameters: #logger.debug(connection_parameters) sort_indices = numpy.argsort(presynaptic_cells) connections = nest.GetConnections( source=numpy.unique(presynaptic_cells.astype(int)).tolist(), target=[int(postsynaptic_cell)], synapse_model=self.nest_synapse_model, synapse_label=self.nest_synapse_label) for name, value in connection_parameters.items(): if name not in self._common_synapse_property_names: value = make_sli_compatible(value) #logger.debug("Setting %s=%s for connections %s" % (name, value, connections)) if isinstance(value, numpy.ndarray): # the str() is to work around a bug handling unicode names in SetStatus in NEST 2.4.1 when using Python 2 nest.SetStatus(connections, str(name), value[sort_indices].tolist()) else: nest.SetStatus(connections, str(name), value) else: self._set_common_synapse_property(name, value)
def _convergent_connect(self, presynaptic_indices, postsynaptic_index, **connection_parameters): """ Connect a neuron to one or more other neurons with a static connection. `presynaptic_indices` - 1D array of presynaptic indices `postsynaptic_index` - integer - the index of the postsynaptic neuron `connection_parameters` - dict whose keys are native NEST parameter names. Values may be scalars or arrays. """ # Clean the connection parameters by removing parameters that are # used by PyNN but should not be passed to NEST connection_parameters.pop( 'tau_minus', None) # TODO: set tau_minus on the post-synaptic cells connection_parameters.pop('dendritic_delay_fraction', None) connection_parameters.pop('w_min_always_zero_in_NEST', None) syn_dict = { 'synapse_model': self.nest_synapse_model, 'synapse_label': self.nest_synapse_label, } # Weights require some special handling if self.receptor_type == 'inhibitory' and self.post.conductance_based: connection_parameters[ 'weight'] *= -1 # NEST wants negative values for inhibitory weights, even if these are conductances if "stdp" in self.nest_synapse_model: syn_dict[ "Wmax"] = -1.2345e6 # just some very large negative value to avoid # NEST complaining about weight and Wmax having different signs # (see https://github.com/NeuralEnsemble/PyNN/issues/636) # Will be overwritten below. connection_parameters["Wmax"] *= -1 if hasattr(self.post, "celltype") and hasattr( self.post.celltype, "receptor_scale"): # this is a bit of a hack connection_parameters[ 'weight'] *= self.post.celltype.receptor_scale # needed for the Izhikevich model # Prepare connections. NodeCollections can't have repeated values, so for some # connector types we need to split the presynaptic cells into groups that # don't have such repeats. # note that NEST needs sorted indices sort_indices = presynaptic_indices.argsort() presynaptic_indices = presynaptic_indices[sort_indices] for name, value in connection_parameters.items(): if isinstance(value, np.ndarray): connection_parameters[name] = value[sort_indices] try: presynaptic_cell_groups = [ self.pre.node_collection[presynaptic_indices] ] connection_parameter_groups = [connection_parameters] except ValueError as err: if "All node IDs in a NodeCollection have to be unique" in str( err): presynaptic_index_groups, connection_parameter_groups = \ split_array_to_avoid_repeats(presynaptic_indices, **connection_parameters) presynaptic_cell_groups = [ self.pre.node_collection[i] for i in presynaptic_index_groups ] else: raise postsynaptic_cell = self.post[postsynaptic_index] # Create connections and set parameters for presynaptic_cells, connection_parameter_group in zip( presynaptic_cell_groups, connection_parameter_groups): self._sources.update(presynaptic_cells.tolist()) try: weights = connection_parameter_group.pop('weight') delays = connection_parameter_group.pop('delay') # nest.Connect expects a 2D array if not np.isscalar(weights): weights = np.array([weights]) if not np.isscalar(delays): delays = np.array([delays]) syn_dict.update({'weight': weights, 'delay': delays}) if postsynaptic_cell.celltype.standard_receptor_type: # For Tsodyks-Markram synapses models we set the "tau_psc" parameter to match # the relevant "tau_syn" parameter from the post-synaptic neuron. if 'tsodyks' in self.nest_synapse_model: if self.receptor_type == 'inhibitory': param_name = postsynaptic_cell.celltype.translations[ 'tau_syn_I']['translated_name'] elif self.receptor_type == 'excitatory': param_name = postsynaptic_cell.celltype.translations[ 'tau_syn_E']['translated_name'] else: raise NotImplementedError() syn_dict["tau_psc"] = nest.GetStatus( postsynaptic_cell.node_collection, param_name)[0] else: syn_dict.update({ "receptor_type": postsynaptic_cell.celltype.get_receptor_type( self.receptor_type) }) # For parameters other than weight and delay, we need to know if they are "common" # parameters (the same for all synapses) or "local" (different synapses can have # different values), as this affects how they are set. # # To introspect which parameters are common, we need an existing connection, so # the first time we create connections we pass just the weight and delay, and set # the other parameters later. We then get the list of common parameters and cache # it so that in subsequent Connect() calls we can pass all of the local # (non-common) parameters. if self._common_synapse_property_names is None: nest.Connect(presynaptic_cells, postsynaptic_cell.node_collection, 'all_to_all', syn_dict) self._identify_common_synapse_properties() # Retrieve connections so that we can set additional # parameters using nest.SetStatus connections = nest.GetConnections( source=presynaptic_cells, target=postsynaptic_cell.node_collection, synapse_model=self.nest_synapse_model, synapse_label=self.nest_synapse_label) for name, value in connection_parameter_group.items(): if name not in self._common_synapse_property_names: value = make_sli_compatible(value) if isinstance(value, np.ndarray): nest.SetStatus(connections, name, value.tolist()) else: nest.SetStatus(connections, name, value) else: self._set_common_synapse_property(name, value) else: # Since we know which parameters are common, we can set the non-common # parameters directly in the nest.Connect call syn_dict = self._update_syn_params( syn_dict, connection_parameter_group) nest.Connect(presynaptic_cells, postsynaptic_cell.node_collection, 'all_to_all', syn_dict) # and then set the common parameters for name, value in connection_parameter_group.items(): if name in self._common_synapse_property_names: self._set_common_synapse_property(name, value) except nest.kernel.NESTError as e: errmsg = "%s. presynaptic_cells=%s, postsynaptic_cell=%s, weights=%s, delays=%s, synapse model='%s'" % ( e, presynaptic_cells, postsynaptic_cell, weights, delays, self.nest_synapse_model) raise errors.ConnectionError(errmsg) # Reset the caching of the connection list, since this will have to be recalculated self._connections = None self._simulator.state.stale_connection_cache = True