Ejemplo n.º 1
0
  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
Ejemplo n.º 2
0
  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)
Ejemplo n.º 3
0
  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)
Ejemplo n.º 4
0
    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))
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
  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
Ejemplo n.º 7
0
  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.')
Ejemplo n.º 8
0
 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))
Ejemplo n.º 9
0
  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)
Ejemplo n.º 10
0
 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)
Ejemplo n.º 11
0
  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
Ejemplo n.º 12
0
  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)
Ejemplo n.º 13
0
  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])
Ejemplo n.º 14
0
  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
Ejemplo n.º 15
0
  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)