def _compute_connections(num_source, num_target, density, edges, avg_deg, directed, reciprocity=-1): assert not np.allclose((density, edges, avg_deg), -1.), "At leat one " +\ "of the following entries must be specified: 'density', 'edges', " +\ "'avg_deg'." pre_recip_edges = 0 if avg_deg > 0: pre_recip_edges = int(avg_deg * num_source) elif edges > 0: pre_recip_edges = int(edges) else: pre_recip_edges = int(density * num_source * num_target) dens = pre_recip_edges / float(num_source * num_target) edges = pre_recip_edges if edges: if not directed: pre_recip_edges = edges = int(edges / 2) elif reciprocity > max(0, (2. - 1. / dens)): frac_recip = ((reciprocity - 1. + np.sqrt(1. + dens * (reciprocity - 2.))) / (2. - reciprocity)) if frac_recip < 1.: pre_recip_edges = int(edges / (1 + frac_recip)) else: raise InvalidArgument( "Such reciprocity cannot be obtained, request ignored.") elif reciprocity > 0.: raise InvalidArgument( "Reciprocity cannot be lower than 2-1/density.") return edges, pre_recip_edges
def _check_num_edges(source_ids, target_ids, num_edges, directed, multigraph): num_source, num_target = len(source_ids), len(target_ids) has_only_one_population = (False if num_source != num_target else not np.all(source_ids - target_ids)) if not has_only_one_population and not multigraph: b_d = (num_edges > num_source * num_target) b_nd = (num_edges > int(0.5 * num_source * num_target)) if (not directed and b_nd) or (directed and b_d): raise InvalidArgument("Required number of edges is too high") elif has_only_one_population and not multigraph: b_d = (num_edges > num_source * (num_target - 1)) b_nd = (num_edges > int((0.5 * num_source - 1) * num_target)) if (not directed and b_nd) or (directed and b_d): raise InvalidArgument("Required number of edges is too high") return has_only_one_population
def assortativity(graph, deg_type="in"): ''' Assortativity of the graph. Parameters ---------- graph : :class:`~nngt.Graph` or subclass Network to analyze. deg_type : string, optional (default: 'in') Type of degree to take into account (among 'in', 'out' or 'total'). Returns ------- a float quantifying the graph assortativity. ''' if nngt._config["backend"] == "igraph": deg_list = graph.get_degrees(deg_type=deg_type) return graph.assortativity(deg_list, directed=graph.is_directed()) #~ return graph.assortativity(deg_type, directed=graph.is_directed()) elif nngt._config["backend"] == "graph-tool": return nngt.analyze_graph["assortativity"](graph, deg_type)[0] else: if deg_type == 'total': raise InvalidArgument("Cannot use total degree assortativity with " "`networkx`.") return nngt.analyze_graph["assortativity"](graph, x=deg_type, y=deg_type)
def _value_psp(weight, neuron_model, di_param, timestep, simtime): nest.ResetKernel() nest.SetKernelStatus({"resolution": timestep}) # create neuron and recorder neuron = nest.Create(neuron_model, params=di_param) V_rest = nest.GetStatus(neuron)[0]["E_L"] nest.SetStatus(neuron, {"V_m": V_rest}) vm = nest.Create("voltmeter", params={"interval": timestep}) nest.Connect(vm, neuron) # send the initial spike sg = nest.Create("spike_generator", params={ 'spike_times': [timestep], 'spike_weights': weight }) nest.Connect(sg, neuron) nest.Simulate(simtime) # get the max and its time dvm = nest.GetStatus(vm)[0] da_voltage = dvm["events"]["V_m"] idx = np.argmax(da_voltage) if idx == len(da_voltage - 1): raise InvalidArgument("simtime too short: PSP maximum is out of range") else: val = da_voltage[idx] - V_rest return val
def set_param(self, param, group=None): ''' Set the groups' parameters. Parameters ---------- param : dict Dictionary containing the model type as key ("neuron" or "synapse") and the model parameter as value (e.g. {"neuron": {"C_m": 125.}}). group : list of strings, optional (default: None) List of strings containing the names of the groups which models should be updated. .. warning:: No check is performed on the validity of the parameters, which means that errors will only be detected when building the graph in NEST. ''' if group is None: group = self.keys() try: for key, val in param.items(): for name in group: if key == "neuron": self[name].neuron_param = val elif key == "synapse": self[name].syn_param = val else: raise ValueError( "Model type {} is not valid; choose among 'neuron'" " or 'synapse'.".format(key)) except: raise InvalidArgument( "Invalid param dict or group; see docstring.")
def _get_psp_list(bins, neuron_model, di_param, timestep, simtime): ''' Return the list of effective weights from a list of NEST connection weights. ''' nest.ResetKernel() nest.SetKernelStatus({"resolution": timestep}) # create neuron and recorder neuron = nest.Create(neuron_model, params=di_param) vm = nest.Create("voltmeter", params={"interval": timestep}) nest.Connect(vm, neuron) # send the spikes times = [timestep + n * simtime for n in range(len(bins))] sg = nest.Create("spike_generator", params={ 'spike_times': times, 'spike_weights': bins }) nest.Connect(sg, neuron) nest.Simulate((len(bins) + 1) * simtime) # get the max and its time dvm = nest.GetStatus(vm)[0] da_voltage = dvm["events"]["V_m"] da_times = dvm["events"]["times"] da_max_psp = da_voltage[argrelmax(da_voltage)] da_min_psp = da_voltage[argrelmin(da_voltage)] da_max_psp -= da_min_psp if len(bins) != len(da_max_psp): raise InvalidArgument("simtime too short: all PSP maxima are not in " "range.") else: return da_max_psp
def __setitem__(self, name, value): g = self.parent()._graph if name in self: size = g.num_edges() if size: dtype = super(_GtEProperty, self).__getitem__(name) # check for list value for one edge if dtype == "object" and size == 1 and isinstance(value, list): value = [value] if len(value) == size: if dtype == "string": g.edge_properties[name].set_2d_array( np.asarray(value, object)) else: g.edge_properties[name].a = np.asarray(value, dtype) else: raise ValueError("A list or a np.array with one entry per " "edge in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use " "set_attribute to create it.") self._num_values_set[name] = len(value)
def _newman_watts(source_ids, target_ids, coord_nb=-1, proba_shortcut=-1, directed=True, multigraph=False, **kwargs): ''' Returns a numpy array of dimension (num_edges,2) that describes the edge list of a Newmaan-Watts graph. ''' node_ids = np.array(source_ids, dtype=int) target_ids = np.array(target_ids, dtype=int) nodes = len(node_ids) circular_edges = nodes*coord_nb num_edges = int(circular_edges*(1+proba_shortcut)) num_edges, circular_edges = (num_edges, circular_edges if directed else (int(num_edges/2), int(circular_edges/2))) b_one_pop = _check_num_edges( source_ids, target_ids, num_edges, directed, multigraph) if not b_one_pop: raise InvalidArgument("This graph model can only be used if source " "and target populations are the same.") # generate the initial circular graph ia_edges = np.zeros((num_edges,2),dtype=int) ia_edges[:circular_edges,:] = _circular_graph(node_ids, coord_nb) # add the random connections num_test, num_ecurrent = 0, circular_edges edges_hash = {} while num_ecurrent != num_edges and num_test < MAXTESTS: ia_sources = node_ids[randint(0, nodes, num_edges-num_ecurrent)] ia_targets = node_ids[randint(0, nodes, num_edges-num_ecurrent)] ia_edges_tmp = np.array([ia_sources,ia_targets]).T ia_edges, num_ecurrent = _filter(ia_edges, ia_edges_tmp, num_ecurrent, edges_hash, b_one_pop, multigraph) num_test += 1 ia_edges = _no_self_loops(ia_edges) return ia_edges
def __getitem__(self, name): edges = None if isinstance(name, slice): edges = self.parent().edges_array[name] elif nonstring_container(name): if nonstring_container(name[0]): edges = name else: if len(name) != 2: raise InvalidArgument( "key for edge attribute must be one of the following: " "slice, list of edges, edges or attribute name.") return self.parent()[name[0]][name[1]] if isinstance(name, str): dtype = _np_dtype(super(_NxEProperty, self).__getitem__(name)) eprop = np.empty(self.parent().edge_nb(), dtype=dtype) g = self.parent() for d, eid in zip(g.edges(data=name), g.edges(data="eid")): eprop[eid[2]] = d[2] return eprop else: eprop = {k: [] for k in self.keys()} for edge in edges: data = self.parent().get_edge_data(edge[0], edge[1]) for k, v in data.items(): if k != "eid": eprop[k].append(v) for k, v in eprop.items(): dtype = _np_dtype(super(_NxEProperty, self).__getitem__(k)) eprop = {k: np.array(v, dtype)} return eprop
def set_step_currents(gids, times, currents): ''' Set step-current excitations Parameters ---------- gids : tuple NEST gids of the target neurons. times : list or :class:`numpy.ndarray` List of the times where the current will change (by default the current generator is initiated at I=0. for t=0.) currents : list or :class:`numpy.ndarray` List of the new current value after the associated time value in `times`. Returns ------- noise : tuple The NEST gid of the noise_generator. ''' if len(times) != len(currents): raise InvalidArgument('Length of `times` and `currents` must be the ' 'same') params = {"amplitude_times": times, "amplitude_values": currents} scg = nest.Create("step_current_generator", params=params, _warn=False) gids = _get_nest_gids(gids) nest.Connect(scg, gids, _warn=False) return scg
def _monitor(gids, nest_recorder, params): new_record = [] recorders = [] for i, rec in enumerate(nest_recorder): # multi/volt/conductancemeter if "meter" in rec: device = None di_spec = {"rule": "all_to_all"} if not params[i].get("to_accumulator", True): device = nest.Create(rec, len(gids)) di_spec["rule"] = "one_to_one" else: device = nest.Create(rec) recorders.append(device) device_params = nest.GetDefaults(rec) device_params.update(params[i]) new_record.append(device_params["record_from"]) nest.SetStatus(device, params[i]) nest.Connect(device, gids, conn_spec=di_spec) # event detectors elif "detector" in rec: device = nest.Create(rec) recorders.append(device) new_record.append("spikes") nest.SetStatus(device, params[i]) nest.Connect(gids, device) else: raise InvalidArgument('Invalid recorder item in `nest_recorder`: ' '{} is unknown.'.format(nest_recorder)) return tuple(recorders), new_record
def dist_rule(rule, scale, pos_src, pos_targets, dist=None): ''' DR test from one source to several targets Parameters ---------- rule : str Either 'exp', 'gaussian', or 'lin'. scale : float Characteristic scale. pos_src : array of shape (2, N) Positions of the sources. pos_targets : array of shape (2, N) Positions of the targets. dist : list, optional (default: None) List that will be filled with the distances of the edges. Returns ------- Array of size N giving the probability of the edges according to the rule. ''' vect = pos_targets - pos_src origin = np.array([(0., 0.)]) # todo correct this dist_tmp = np.squeeze(cdist(vect.T, origin), axis=1) if dist is not None: dist.extend(dist_tmp) if rule == 'exp': return np.exp(np.divide(dist_tmp, -scale)) elif rule == 'gaussian': return np.exp(-0.5 * np.square(np.divide(dist_tmp, scale))) elif rule == 'lin': return np.divide(scale - dist_tmp, scale).clip(min=0.) else: raise InvalidArgument('Unknown rule "' + rule + '".')
def lin_correlated_distrib(graph, elist=None, correl_attribute="betweenness", noise_scale=None, lower=None, upper=None, slope=None, offset=0., last_edges=False, **kwargs): if slope is not None and (lower, upper) != (None, None): raise InvalidArgument('`slope` and `lower`/`upper` parameters are not ' 'compatible, please choose one or the other.') elif (lower is not None or upper is not None) and None in (lower, upper): raise InvalidArgument('Both `lower` and `upper` should be set if one ' 'of the two is used.') ecount = _compute_num_prop(elist, graph) noise = (1. if noise_scale is None else np.abs( np.random.normal(1, noise_scale, ecount))) data = None if correl_attribute == "betweenness": data = graph.get_betweenness(kwargs["btype"], kwargs["use_weights"]) elif correl_attribute == "distance": assert 'distance' in graph.edges_attributes, \ 'Graph has no "distance" edge attribute.' if 'distance' not in kwargs: if last_edges: slc = slice(-len(elist), None) data = graph.get_edge_attributes(slc, 'distance') else: data = graph.get_edge_attributes(elist, 'distance') else: data = kwargs['distance'] else: raise NotImplementedError() if noise_scale is not None: data *= noise if len(data): if slope is None: dmax = np.max(data) dmin = np.min(data) return lower + (upper - lower) * (data - dmin) / (dmax - dmin) + offset else: return slope * data + offset return np.array([])
def __setitem__(self, name, value): if name in self: if len(value) == size: self.parent()().vs[name] = value else: raise ValueError("A list or a np.array with one entry per \ node in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use \ set_attribute to create it.")
def _init_spatial_properties(self, shape, positions=None, **kwargs): ''' Create the positions of the neurons from the graph `shape` attribute and computes the connections distances. ''' positions = None if positions is None else np.asarray(positions) self.new_edge_attribute('distance', 'double') if positions is not None and len(positions) != self.node_nb(): raise InvalidArgument("Wrong number of neurons in `positions`.") if shape is not None: shape.set_parent(self) self._shape = shape else: if positions is None or not np.any(positions): if 'height' in kwargs and 'width' in kwargs: self._shape = nngt.geometry.Shape.rectangle( kwargs['height'], kwargs['width'], parent=self) elif 'radius' in kwargs: self._shape = nngt.geometry.Shape.disk(kwargs['radius'], parent=self) elif 'radii' in kwargs: self._shape = nngt.geometry.Shape.ellipse(kwargs['radii'], parent=self) elif 'polygon' in kwargs: self._shape = nngt.geometry.Shape.from_polygon( kwargs['polygon'], min_x=kwargs.get('min_x', -5000.), max_x=kwargs.get('max_x', 5000.), unit=kwargs.get('unit', 'um'), parent=self) else: raise RuntimeError('SpatialGraph needs a `shape` or ' 'keywords arguments to build one, or ' 'at least `positions` so it can create ' 'a square containing them') else: minx, maxx = np.min(positions[:, 0]), np.max(positions[:, 0]) miny, maxy = np.min(positions[:, 1]), np.max(positions[:, 1]) height, width = 1.01 * (maxy - miny), 1.01 * (maxx - minx) centroid = (0.5 * (maxx + minx), 0.5 * (maxy + miny)) self._shape = nngt.geometry.Shape.rectangle(height, width, centroid=centroid, parent=self) b_rnd_pos = True if not self.node_nb() or positions is None else False self._pos = self._shape.seed_neurons() if b_rnd_pos else positions Connections.distances(self)
def set_model(self, model, group=None): ''' Set the groups' models. Parameters ---------- model : dict Dictionary containing the model type as key ("neuron" or "synapse") and the model name as value (e.g. {"neuron": "iaf_neuron"}). group : list of strings, optional (default: None) List of strings containing the names of the groups which models should be updated. Note ---- By default, synapses are registered as "static_synapse"s in NEST; because of this, only the ``neuron_model`` attribute is checked by the ``has_models`` function: it will answer ``True`` if all groups have a 'non-None' ``neuron_model`` attribute. Warning ------- No check is performed on the validity of the models, which means that errors will only be detected when building the graph in NEST. ''' if self._to_nest: raise RuntimeError("Models cannot be changed after the network " "has been sent to NEST!") if group is None: group = self.keys() try: for key, val in model.items(): for name in group: if key == "neuron": self[name].neuron_model = val elif key == "synapse": self[name].syn_model = val else: raise ValueError( "Model type {} is not valid; choose among 'neuron'" " or 'synapse'.".format(key)) except: if model is not None: raise InvalidArgument( "Invalid model dict or group; see docstring.") b_has_models = True if model is None: b_has_models = False for group in self.values(): b_has_models *= group.has_model self._has_models = b_has_models
def __setitem__(self, name, value): if name in self: size = self.parent().num_edges() if len(value) == size: self.parent().edge_properties[name].a = np.array(value) else: raise ValueError("A list or a np.array with one entry per \ edge in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use \ set_attribute to create it.")
def weights(graph=None, elist=None, wlist=None, distribution="constant", parameters={}, noise_scale=None): ''' Compute the weights of the graph's edges. @todo: take elist into account Parameters ---------- graph : class:`~nngt.Graph` or subclass Graph the nodes belong to. elist : class:`numpy.array`, optional (default: None) List of the edges (for user defined weights). wlist : class:`numpy.array`, optional (default: None) List of the weights (for user defined weights). distribution : class:`string`, optional (default: "constant") Type of distribution (choose among "constant", "uniform", "lognormal", "gaussian", "user_def", "lin_corr", "log_corr"). parameters : class:`dict`, optional (default: {}) Dictionary containing the distribution parameters. noise_scale : class:`int`, optional (default: None) Scale of the multiplicative Gaussian noise that should be applied on the weights. Returns ------- new_weights : class:`scipy.sparse.lil_matrix` A sparse matrix containing *ONLY* the newly-computed weights. ''' parameters["btype"] = parameters.get("btype", "edge") parameters["use_weights"] = parameters.get("use_weights", False) #~ elist = np.array(elist) if elist is not None else elist elist = None if wlist is not None: assert isinstance(wlist, np.ndarray), "numpy.ndarray required in "\ "Connections.weights" num_edges = graph.edge_nb() if elist is None else elist.shape[0] if len(wlist) != num_edges: raise InvalidArgument( '''`wlist` must have one entry per edge. For graph {}, there are {} edges while {} values where provided'''.format( graph.name, num_edges, len(wlist))) else: wlist = _eprop_distribution(graph, distribution, elist=elist, **parameters) # add to the graph container bwlist = (np.max(wlist) - wlist if np.any(wlist) else np.repeat(0, len(wlist))) if graph is not None: graph.set_edge_attribute( WEIGHT, value_type="double", values=wlist, edges=elist) graph.set_edge_attribute( BWEIGHT, value_type="double", values=bwlist, edges=elist) return wlist
def __setitem__(self, name, value): if name in self: size = self.parent().edge_nb() if len(value) == size: self.prop[name] = list(value) else: raise ValueError("A list or a np.array with one entry per " "edge in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use " "set_attribute to create it.") self._num_values_set[name] = len(value)
def __setitem__(self, name, value): size = self.parent().number_of_nodes() if name in self: if len(value) == size: for i in range(size): self.parent().node[i][name] = value[i] else: raise ValueError("A list or a np.array with one entry per " "node in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use " "set_attribute to create it.")
def __setitem__(self, name, value): if name in self: size = self.parent().number_of_edges() if len(value) == size: for e in self.parent().edges(data="eid"): self.parent().edges[e[0], e[1]][name] = value[e[2]] else: raise ValueError("A list or a np.array with one entry per " "edge in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use " "set_attribute to create it.")
def delays(graph=None, dlist=None, elist=None, distribution="constant", parameters=None, noise_scale=None): ''' Compute the delays of the neuronal connections. Parameters ---------- graph : class:`~nngt.Graph` or subclass Graph the nodes belong to. dlist : class:`numpy.array`, optional (default: None) List of user-defined delays). elist : class:`numpy.array`, optional (default: None) List of the edges which value should be updated. distribution : class:`string`, optional (default: "constant") Type of distribution (choose among "constant", "uniform", "lognormal", "gaussian", "user_def", "lin_corr", "log_corr"). parameters : class:`dict`, optional (default: {}) Dictionary containing the distribution parameters. noise_scale : class:`int`, optional (default: None) Scale of the multiplicative Gaussian noise that should be applied on the weights. Returns ------- new_delays : class:`scipy.sparse.lil_matrix` A sparse matrix containing *ONLY* the newly-computed weights. ''' elist = np.array(elist) if elist is not None else elist if dlist is not None: assert isinstance(dlist, np.ndarray), "numpy.ndarray required in "\ "Connections.delays" num_edges = graph.edge_nb() if elist is None else elist.shape[0] if len(dlist) != num_edges: raise InvalidArgument("`dlist` must have one entry per edge.") else: parameters["btype"] = parameters.get("btype", "edge") parameters["use_weights"] = parameters.get("use_weights", False) dlist = _eprop_distribution(graph, distribution, elist=elist, **parameters) # add to the graph container if graph is not None: graph.set_edge_attribute(DELAY, value_type="double", values=dlist, edges=elist) return dlist
def _get_attribute(network, attribute, nodes=None, data=None): if attribute.lower() == "b2": if data is None: from nngt.simulation import get_b2 return get_b2(network, nodes=nodes) else: if nodes is None: raise InvalidArgument( "`nodes` entry is required when using `data`.") return _b2_from_data(nodes, data) elif attribute == "betweenness": betw = network.get_betweenness("node") if nodes is not None: return betw[nodes] return betw elif attribute == "closeness": return closeness(network, nodes=nodes) elif attribute == "clustering": return local_clustering(network, nodes=nodes) elif "degree" in attribute.lower(): dtype = attribute[:attribute.index("-")] return network.get_degrees(dtype, node_list=nodes) if attribute == "firing_rate": if data is None: from nngt.simulation import get_firing_rate return get_firing_rate(network, nodes=nodes) else: if nodes is None: raise InvalidArgument( "`nodes` entry is required when using `data`.") return _fr_from_data(nodes, data) elif attribute == "subgraph_centrality": sc = subgraph_centrality(network) if nodes is not None: return sc[nodes] return sc else: raise RuntimeError( "Attribute '{}' is not available.".format(attribute))
def __setitem__(self, name, value): g = self.parent()._graph if name in self: size = g.ecount() if len(value) == size: g.es[name] = value else: raise ValueError("A list or a np.array with one entry per " "edge in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use " "set_attribute to create it.")
def new_edge(self, source, target, attributes=None): ''' Adding a connection to the graph, with optional properties. Parameters ---------- source : :class:`int/node` Source node. target : :class:`int/node` Target node. attributes : :class:`dict`, optional (default: ``{}``) Dictionary containing optional edge properties. If the graph is weighted, defaults to ``{"weight": 1.}``, the unit weight for the connection (synaptic strength in NEST). Returns ------- The new connection. ''' if attributes is None: attributes = {} connection = super(_GtGraph, self).add_edge(source, target, add_missing=True) _set_edge_attr(self, [(source, target)], attributes) for key, val in attributes: if key in self.edge_properties: self.edge_properties[key][connection] = val[0] else: raise InvalidArgument("Unknown edge property `" + key + "'.") if not self._directed: c2 = super(_GtGraph, self).add_edge(target, source) for key, val in attributes: if key in self.edge_properties: self.edge_properties[key][c2] = val[0] else: raise InvalidArgument("Unknown edge property `" + key + "'.") return connection
def new_node_attribute(self, name, value_type, values=None, val=None): num_nodes = self.vcount() if values is None: if val is not None: values = np.repeat(val, num_nodes) else: if vector in value_type: values = [[] for _ in range(num_nodes)] else: values = np.repeat(self.di_value[value_type], num_nodes) elif len(values) != num_nodes: raise InvalidArgument("'values' list must contain one element per \ node in the graph.") self.vs[name] = values
def new_edge_attribute(self, name, value_type, values=None, val=None): num_edges = self.edge_nb() if values is None: if val is not None: values = np.repeat(val, num_edges) else: if "vec" in value_type: values = [[] for _ in range(num_edges)] else: values = np.repeat(self.di_value[value_type], num_edges) elif len(values) != num_edges: raise InvalidArgument("'values' list must contain one element per \ edge in the graph.") for e, val in zip(self.edges(), values): self.edge[e[0]][e[1]][name] = val
def new_edge(self, source, target, attributes=None, ignore=False): ''' Adding a connection to the graph, with optional properties. Parameters ---------- source : :class:`int/node` Source node. target : :class:`int/node` Target node. attributes : :class:`dict`, optional (default: ``{}``) Dictionary containing optional edge properties. If the graph is weighted, defaults to ``{"weight": 1.}``, the unit weight for the connection (synaptic strength in NEST). ignore : bool, optional (default: False) If set to True, ignore attempts to add an existing edge, otherwise raises an error. Returns ------- The new connection. ''' #check attributes if attributes is None: attributes = {} # check that the edge does not already exist edge = (source, target) if edge not in self._edges: edge_id = len(self._edges) self._edges[edge] = edge_id self._out_deg[source] += 1 self._in_deg[target] += 1 # attributes self.attr_new_edges([(source, target)], attributes=attributes) # update matrix w = _get_edge_attr(self, [edge], "weight", last_edges=True) self._adj_mat[source, target] = w if not self._directed: e_recip = (target, source) self._edges[e_recip] = edge_id + 1 self._out_deg[target] += 1 self._in_deg[source] += 1 _set_edge_attr(self, [e_recip], attributes) self._adj_mat[source, target] = w else: if not ignore: raise InvalidArgument("Trying to add existing edge.") return edge
def __setitem__(self, name, value): dtype = super(_GtNProperty, self).__getitem__(name) g = self.parent()._graph if name in self: size = g.num_vertices() if len(value) == size: if dtype == "string": g.vertex_properties[name].set_2d_array(np.asarray(value)) else: g.vertex_properties[name].a = np.asarray(value) else: raise ValueError("A list or a np.array with one entry per " "node in the graph is required") else: raise InvalidArgument("Attribute does not exist yet, use " "set_attribute to create it.")
def new_edge(self, source, target, attributes=None, ignore=False): ''' Adding a connection to the graph, with optional properties. Parameters ---------- source : :class:`int/node` Source node. target : :class:`int/node` Target node. attributes : :class:`dict`, optional (default: ``{}``) Dictionary containing optional edge properties. If the graph is weighted, defaults to ``{"weight": 1.}``, the unit weight for the connection (synaptic strength in NEST). ignore : bool, optional (default: False) If set to True, ignore attempts to add an existing edge, otherwise raises an error. Returns ------- The new connection. ''' if self.has_edge(source, target): if not ignore: raise InvalidArgument("Trying to add existing edge.") else: for attr in attributes: if "_corr" in attr: raise NotImplementedError("Correlated attributes are not " "available with networkx.") if self._weighted and "weight" not in attributes: attributes["weight"] = 1. self.add_edge(source, target) self[source][target]["eid"] = self.number_of_edges() # call parent function to set the attributes self.attr_new_edges([(source, target)], attributes=attributes) if not self._directed: self.add_edge(target,source) self[source][target]["eid"] = self.number_of_edges() for key, val in attributes.items(): self[target][source][key] = val self.attr_new_edges([(target, source)], attributes=attributes) return (source, target)