def __call__(self, pre_size, post_size): super(IJConn, self).__call__(pre_size, post_size) max_pre = np.max(self.pre_ids) max_post = np.max(self.post_ids) if max_pre >= self.pre_num: raise ConnectorError(f'pre_num ({self.pre_num}) should be greater than ' f'the maximum id ({max_pre}) of self.pre_ids.') if max_post >= self.post_num: raise ConnectorError(f'post_num ({self.post_num}) should be greater than ' f'the maximum id ({max_post}) of self.post_ids.') return self
def require(self, *structures): self.check(structures) assert self.pre_size == self.post_size if isinstance(self.pre_size, int) or (isinstance(self.pre_size, (tuple, list)) and len(self.pre_size) == 1): num_node = self.pre_num if self.num_neighbor > num_node: raise ConnectorError("num_neighbor > num_node, choose smaller num_neighbor or larger num_node") # If k == n, the graph is complete not Watts-Strogatz if self.num_neighbor == num_node: conn = np.ones((num_node, num_node), dtype=MAT_DTYPE) else: conn = np.zeros((num_node, num_node), dtype=MAT_DTYPE) nodes = np.array(list(range(num_node))) # nodes are labeled 0 to n-1 # connect each node to k/2 neighbors for j in range(1, self.num_neighbor // 2 + 1): targets = np.concatenate([nodes[j:], nodes[0:j]]) # first j nodes are now last in list conn[nodes, targets] = True conn[targets, nodes] = True # rewire edges from each node # loop over all nodes in order (label) and neighbors in order (distance) # no self loops or multiple edges allowed for j in range(1, self.num_neighbor // 2 + 1): # outer loop is neighbors targets = np.concatenate([nodes[j:], nodes[0:j]]) # first j nodes are now last in list if self.directed: # inner loop in node order for u, v in zip(nodes, targets): w = _smallworld_rewire(prob=self.prob, i=u, all_j=conn[u], include_self=self.include_self) if w != -1: conn[u, v] = False conn[u, w] = True w = _smallworld_rewire(prob=self.prob, i=u, all_j=conn[:, u], include_self=self.include_self) if w != -1: conn[v, u] = False conn[w, u] = True else: # inner loop in node order for u, v in zip(nodes, targets): w = _smallworld_rewire(prob=self.prob, i=u, all_j=conn[u], include_self=self.include_self) if w != -1: conn[u, v] = False conn[v, u] = False conn[u, w] = True conn[w, u] = True # conn = np.asarray(conn, dtype=MAT_DTYPE) else: raise ConnectorError('Currently only support 1D ring connection.') return self.make_returns(structures, mat=conn)
def require(self, *structures): self.check(structures) assert self.pre_num == self.post_num num_node = self.pre_num if self.m < 1 or self.m >= num_node: raise ConnectorError(f"Barabási–Albert network must have m >= 1 and " f"m < n, while m = {self.m} and n = {num_node}") # Add m initial nodes (m0 in barabasi-speak) conn = np.zeros((num_node, num_node), dtype=MAT_DTYPE) # Target nodes for new edges targets = list(range(self.m)) # List of existing nodes, with nodes repeated once for each adjacent edge repeated_nodes = [] # Start adding the other n-m nodes. The first node is m. source = self.m while source < num_node: # Add edges to m nodes from the source. origins = [source] * self.m conn[origins, targets] = True if not self.directed: conn[targets, origins] = True # Add one node to the list for each new edge just created. repeated_nodes.extend(targets) # And the new node "source" has m edges to add to the list. repeated_nodes.extend([source] * self.m) # Now choose m unique nodes from the existing nodes # Pick uniformly from repeated_nodes (preferential attachment) targets = list(_random_subset(repeated_nodes, self.m, self.rng)) source += 1 return self.make_returns(structures, mat=conn)
def require(self, *structures): self.check(structures) if len(self.pre_size) == 1: height, width = self.pre_size[0], 1 elif len(self.pre_size) == 2: height, width = self.pre_size else: raise ConnectorError( f'Currently, GridN only supports the two-dimensional geometry.' ) conn_i = [] conn_j = [] for row in range(height): res = _grid_n(height=height, width=width, row=row, n=self.N, include_self=self.include_self) conn_i.extend(res[0]) conn_j.extend(res[1]) pre_ids = np.asarray(conn_i, dtype=IDX_DTYPE) post_ids = np.asarray(conn_j, dtype=IDX_DTYPE) return self.make_returns(structures, ij=(pre_ids, post_ids))
def __init__(self, m, p, directed=False, seed=None): super(PowerLaw, self).__init__() self.m = m self.p = p if self.p > 1 or self.p < 0: raise ConnectorError(f"p must be in [0,1], while p={self.p}") self.directed = directed self.seed = seed self.rng = np.random.RandomState(seed)
def __call__(self, pre_size, post_size): try: assert self.pre_num == tools.size2num(pre_size) assert self.post_num == tools.size2num(post_size) except AssertionError: raise ConnectorError(f'(pre_size, post_size) is inconsistent with the shape of the sparse matrix.') super(SparseMatConn, self).__call__(pre_size, post_size) return self
def check(self, structures: Union[Tuple, List, str]): # check "pre_num" and "post_num" try: assert self.pre_num is not None and self.post_num is not None except AssertionError: raise ConnectorError(f'self.pre_num or self.post_num is not defined. ' f'Please use self.__call__(pre_size, post_size) ' f'before requiring properties.') # check synaptic structures if isinstance(structures, str): structures = [structures] if structures is None or len(structures) == 0: raise ConnectorError('No synaptic structure is received.') for n in structures: if n not in SUPPORTED_SYN_STRUCTURE: raise ConnectorError(f'Unknown synapse structure "{n}". ' f'Only {SUPPORTED_SYN_STRUCTURE} is supported.')
def require(self, *structures): self.check(structures) try: assert self.pre_num == self.post_num except AssertionError: raise ConnectorError( f'One2One connection must be defined in two groups with the ' f'same size, but {self.pre_num} != {self.post_num}.') ind = np.arange(self.pre_num) indptr = np.arange(self.pre_num + 1) return self.make_returns(structures, csr=(ind, indptr))
def require(self, *structures): self.check(structures) assert self.pre_num == self.post_num num_node = self.pre_num if self.m1 < 1 or self.m1 >= num_node: raise ConnectorError(f"Dual Barabási–Albert network must have m1 >= 1 and m1 < num_node, " f"while m1 = {self.m1} and num_node = {num_node}.") if self.m2 < 1 or self.m2 >= num_node: raise ConnectorError(f"Dual Barabási–Albert network must have m2 >= 1 and m2 < num_node, " f"while m2 = {self.m2} and num_node = {num_node}.") if self.p < 0 or self.p > 1: raise ConnectorError(f"Dual Barabási–Albert network must have 0 <= p <= 1, while p = {self.p}") # Add max(m1,m2) initial nodes (m0 in barabasi-speak) conn = np.zeros((num_node, num_node), dtype=MAT_DTYPE) # List of existing nodes, with nodes repeated once for each adjacent edge repeated_nodes = [] # Start adding the remaining nodes. source = max(self.m1, self.m2) # Pick which m to use first time (m1 or m2) m = self.m1 if self.rng.random() < self.p else self.m2 # Target nodes for new edges targets = list(range(m)) while source < num_node: # Add edges to m nodes from the source. origins = [source] * m conn[origins, targets] = True if not self.directed: conn[targets, origins] = True # Add one node to the list for each new edge just created. repeated_nodes.extend(targets) # And the new node "source" has m edges to add to the list. repeated_nodes.extend([source] * m) # Pick which m to use next time (m1 or m2) m = self.m1 if self.rng.random() < self.p else self.m2 # Now choose m unique nodes from the existing nodes # Pick uniformly from repeated_nodes (preferential attachment) targets = list(_random_subset(repeated_nodes, m, self.rng)) source += 1 return self.make_returns(structures, mat=conn)
def __init__(self, num, include_self=True, seed=None): super(FixedPostNum, self).__init__() if isinstance(num, int): assert num >= 0, '"num" must be a non-negative integer.' elif isinstance(num, float): assert 0. <= num <= 1., '"num" must be in [0., 1.).' else: raise ConnectorError(f'Unknown type: {type(num)}') self.num = num self.seed = seed self.include_self = include_self self.rng = np.random.RandomState(seed=seed)
def __init__(self, csr_mat): super(SparseMatConn, self).__init__() try: from scipy.sparse import csr_matrix except (ModuleNotFoundError, ImportError): raise ConnectorError(f'Using SparseMatConn requires the scipy package. ' f'Please run "pip install scipy" to install scipy.') assert isinstance(csr_mat, csr_matrix) csr_mat.data = np.asarray(csr_mat.data, dtype=MAT_DTYPE) self.csr_mat = csr_mat self.pre_num, self.post_num = csr_mat.shape
def require(self, *structures): self.check(structures) assert self.pre_num == self.post_num num_node = self.pre_num if self.m < 1 or num_node < self.m: raise ConnectorError(f"Must have m>1 and m<n, while m={self.m} and n={num_node}") # add m initial nodes (m0 in barabasi-speak) conn = np.zeros((num_node, num_node), dtype=MAT_DTYPE) repeated_nodes = list(range(self.m)) # list of existing nodes to sample from # with nodes repeated once for each adjacent edge source = self.m # next node is m while source < num_node: # Now add the other n-1 nodes possible_targets = _random_subset(repeated_nodes, self.m, self.rng) # do one preferential attachment for new node target = possible_targets.pop() conn[source, target] = True if not self.directed: conn[target, source] = True repeated_nodes.append(target) # add one node to list for each new link count = 1 while count < self.m: # add m-1 more new links if self.rng.random() < self.p: # clustering step: add triangle neighbors = np.where(conn[target])[0] neighborhood = [nbr for nbr in neighbors if not conn[source, nbr] and not nbr == source] if neighborhood: # if there is a neighbor without a link nbr = self.rng.choice(neighborhood) conn[source, nbr] = True # add triangle if not self.directed: conn[nbr, source] = True repeated_nodes.append(nbr) count = count + 1 continue # go to top of while loop # else do preferential attachment step if above fails target = possible_targets.pop() conn[source, target] = True if not self.directed: conn[target, source] = True repeated_nodes.append(target) count = count + 1 repeated_nodes.extend([source] * self.m) # add source node to list m times source += 1 return self.make_returns(structures, mat=conn)
def make_returns(self, structures, csr=None, mat=None, ij=None): """Make the desired synaptic structures and return them. """ # checking all_data = dict() if (csr is None) and (mat is None) and (ij is None): raise ConnectorError('Must provide one of "csr", "mat" or "ij".') structures = (structures,) if isinstance(structures, str) else structures assert isinstance(structures, (tuple, list)) # "csr" structure if csr is not None: assert isinstance(csr[0], np.ndarray) assert isinstance(csr[1], np.ndarray) if (PRE2POST in structures) and (PRE2POST not in all_data): all_data[PRE2POST] = (math.asarray(csr[0], dtype=IDX_DTYPE), math.asarray(csr[1], dtype=IDX_DTYPE)) self._return_by_csr(structures, csr=csr, all_data=all_data) # "mat" structure if mat is not None: assert isinstance(mat, np.ndarray) and np.ndim(mat) == 2 if (CONN_MAT in structures) and (CONN_MAT not in all_data): all_data[CONN_MAT] = math.asarray(mat, dtype=MAT_DTYPE) self._return_by_mat(structures, mat=mat, all_data=all_data) # "ij" structure if ij is not None: assert isinstance(ij[0], np.ndarray) assert isinstance(ij[1], np.ndarray) if (PRE_IDS in structures) and (PRE_IDS not in structures): all_data[PRE_IDS] = math.asarray(ij[0], dtype=IDX_DTYPE) if (POST_IDS in structures) and (POST_IDS not in structures): all_data[POST_IDS] = math.asarray(ij[1], dtype=IDX_DTYPE) self._return_by_ij(structures, ij=ij, all_data=all_data) # return if len(structures) == 1: return all_data[structures[0]] else: return tuple([all_data[n] for n in structures])
def __call__(self, pre_size, post_size=None): if post_size is None: post_size = pre_size try: assert pre_size == post_size except AssertionError: raise ConnectorError( f'The shape of pre-synaptic group should be the same with the post group. ' f'But we got {pre_size} != {post_size}.') if isinstance(pre_size, int): pre_size = (pre_size,) else: pre_size = tuple(pre_size) if isinstance(post_size, int): post_size = (post_size,) else: post_size = tuple(post_size) self.pre_size, self.post_size = pre_size, post_size self.pre_num = tools.size2num(self.pre_size) self.post_num = tools.size2num(self.post_size) return self
def require(self, *structures): self.check(structures) # value range to encode if self.encoding_values is None: value_ranges = tuple([(0, s) for s in self.pre_size]) elif isinstance(self.encoding_values, (tuple, list)): if len(self.encoding_values) == 0: raise ConnectorError(f'encoding_values has a length of 0.') elif isinstance(self.encoding_values[0], (int, float)): assert len(self.encoding_values) == 2 assert self.encoding_values[0] < self.encoding_values[1] value_ranges = tuple([self.encoding_values for _ in self.pre_size]) elif isinstance(self.encoding_values[0], (tuple, list)): if len(self.encoding_values) != len(self.pre_size): raise ConnectorError(f'The network size has {len(self.pre_size)} dimensions, while ' f'the encoded values provided only has {len(self.encoding_values)}-D. ' f'Error in {str(self)}.') for v in self.encoding_values: assert isinstance(v[0], (int, float)) assert len(v) == 2 value_ranges = tuple(self.encoding_values) else: raise ConnectorError(f'Unsupported encoding values: {self.encoding_values}') else: raise ConnectorError(f'Unsupported encoding values: {self.encoding_values}') # values values = [np.linspace(vs[0], vs[1], n + 1)[:n] for vs, n in zip(value_ranges, self.pre_size)] post_values = np.stack([v.flatten() for v in np.meshgrid(*values)]) value_sizes = np.array([v[1] - v[0] for v in value_ranges]) if value_sizes.ndim < post_values.ndim: value_sizes = np.expand_dims(value_sizes, axis=tuple([i + 1 for i in range(post_values.ndim - 1)])) # probability of connections prob_mat = [] for i in range(self.pre_num): # values for node i i_coordinate = tuple() for s in self.pre_size[:-1]: i, pos = divmod(i, s) i_coordinate += (pos,) i_coordinate += (i,) i_value = np.array([values[i][c] for i, c in enumerate(i_coordinate)]) if i_value.ndim < post_values.ndim: i_value = np.expand_dims(i_value, axis=tuple([i + 1 for i in range(post_values.ndim - 1)])) # distances dists = np.abs(i_value - post_values) if self.periodic_boundary: dists = np.where(dists > value_sizes / 2, value_sizes - dists, dists) exp_dists = np.exp(-(np.linalg.norm(dists, axis=0) / self.sigma) ** 2 / 2) prob_mat.append(exp_dists) prob_mat = np.stack(prob_mat) if self.normalize: prob_mat /= prob_mat.max() # connectivity conn_mat = prob_mat >= self.rng.random(prob_mat.shape) if not self.include_self: np.fill_diagonal(conn_mat, False) return self.make_returns(structures, mat=conn_mat)