def random_simple_deg_seq(sequence, brain_size=[7., 7., 7.], seed=None, tries=10): '''Wrapper function to get a SIMPLE (no parallel or self-loop edges) graph that has a given degree sequence. This graph is used conventionally as a control because it yields a random graph that accounts for degree distribution. Parameters: sequence: list of int Degree of each node to be added to the graph. brain_size: list of 3 floats Size of the brain to use when distributing node locations. Added for convenience, but does not affect connectivity pattern. seed: hashable object for random seed Seed for the random number generator. tries: int Number of attempts at creating graph (function will retry if self-loops exist. Returns: Networkx graph object, adjacency matrix, and random distances''' G = nx.random_degree_sequence_graph(sequence=sequence, seed=seed, tries=tries) A = nx.adjacency_matrix(G) N = len(sequence) centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) D = aux_tools.dist_mat(centroids) return G, A, D
def cortex_distance_matrix(lm_dir=LINEAR_MODEL_DIRECTORY, cent_dir=STRUCTURE_DIRECTORY, in_mm=True): """Compute distance matrix from centroid data. Args: lm_dir: Directory containing linear model data cent_dir: Directory containing centroid data in_mm: Set to true to return dist matrix in mm instead of 100um units Returns: distance matrix, centroid matrix""" # Get labels _, _, labels = aux.load_W_and_P(data_dir=lm_dir) # Get mask of areas in cortex mask = aux.mask_specific_structures(labels, ['CTX']) # Get new labels labels = list(np.array(labels)[mask]) # Load centroids centroids = aux.load_centroids(labels, data_dir=cent_dir, in_mm=in_mm) # Compute distance matrix dist_mat = aux_tools.dist_mat(centroids) return dist_mat, labels
def ER_distance(N=bc.num_brain_nodes, p=bc.num_brain_edges_directed, brain_size=[7., 7., 7.]): """Create an directed Erdos-Renyi random graph in which each node is assigned a position in space, so that relative positions are represented by a distance matrix.""" # Make graph & get adjacency matrix G = nx.erdos_renyi_graph(N, p, directed=True) A = nx.adjacency_matrix(G) # Randomly distribute nodes in space & compute distance matrix centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) D = aux_tools.dist_mat(centroids) return G, A.todense(), D
def ER_distance(N=426, p=.086, brain_size=[7., 7., 7.]): """Create an Erdos-Renyi random graph in which each node is assigned a position in space, so that relative positions are represented by a distance matrix.""" # Make graph & get adjacency matrix G = nx.erdos_renyi_graph(N, p) A = nx.adjacency_matrix(G) # Randomly distribute nodes in space & compute distance matrix centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) D = aux_tools.dist_mat(centroids) return G, A, D
def random_directed_deg_seq(in_sequence, out_sequence, simplify, brain_size=[7., 7., 7.], create_using=None, seed=None): '''Wrapper function to get a MULTIGRAPH (parallel or self-loop edges may exist) given degree sequence. Chance of parallel/multiedges diminish as graph has more nodes. This graph is used conventionally as a control because it yields a random graph that accounts for degree distribution. Parameters: ----------- in_sequence: list of int In degree of each node to be added to the graph. out_sequence: list of int Out degree of each node to be added to the graph. simplify: bool Whether or not to remove self-loops and parallel edges. Will change degree sequence slightly, but effect diminishes with increasing size of graphs. brain_size: list of 3 floats Size of the brain to use when distributing node locations. create_using: graph, optional Return graph of this type. The instance will be cleared. seed: hashable object for random seed Seed for the random number generator. Returns: -------- Networkx graph object, adjacency matrix, and random distances''' # Create configuration model using specified properties G = nx.directed_configuration_model(in_sequence, out_sequence, create_using=create_using, seed=seed) if simplify: G = nx.DiGraph(G) # Remove parallel edges G.remove_edges_from(G.selfloop_edges()) # Remove self loops # Get adjacency info and create random spatial locations A = nx.adjacency_matrix(G) N = len(in_sequence) centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) D = aux_tools.dist_mat(centroids) return G, A, D
def biophysical_reverse(N=bc.num_brain_nodes, N_edges=bc.num_brain_edges_directed, L=np.inf, gamma=1.75, brain_size=[7., 7., 7.]): """Create a biophysically inspired graph. Connection probabilities depend on distance & degree. Args: N: how many nodes N_edges: how many edges L: length constant gamma: power to raise degree to brain_size: size of space in which nodes are randomly placed Returns: Networkx graph object, adjacency matrix, distance matrix""" # Pick node positions & calculate distance matrix centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) # Calculate distance matrix and distance decay matrix D = aux_tools.dist_mat(centroids) D_decay = np.exp(-D / L) # Initialize diagonal adjacency matrix A = np.eye(N, dtype=float) # Make graph object G = nx.DiGraph() G.add_nodes_from(np.arange(N)) # Randomly add edges edge_ctr = 0 while edge_ctr < N_edges: # Update degree list & degree-related probability vector indegs = A.sum(0).astype(float) outdegs = A.sum(1).astype(float) degs = indegs + outdegs degs_prob = degs.copy() # Pick random node to draw edge to to_idx = np.random.randint(low=0, high=N) # Skip this node if already fully connected if outdegs[to_idx] == N: continue # Find unavailable cxns and set their probability to zero unavail_mask = A[:, to_idx] > 0 degs_prob[unavail_mask] = 0 # Set self cxn probability to zero degs_prob[to_idx] = 0 # Calculate cxn probabilities from degree & distance P = (degs_prob ** gamma) * D_decay[:, to_idx] # On the off changes that P == 0, skip if P.sum() == 0: continue # Otherwise keep going on P /= float(P.sum()) # Normalize probabilities to sum to 1 # Sample node from distribution from_idx = np.random.choice(np.arange(N), p=P) # Add edge to graph if A[from_idx, to_idx] == 0: G.add_edge(from_idx, to_idx, {'d': D[from_idx, to_idx]}) # Add edge to adjacency matrix A[from_idx, to_idx] += 1 # Increment edge counter edge_ctr += 1 # Set diagonals to zero np.fill_diagonal(A, 0) return G, A, D
def source_growth_total_degree(N=bc.num_brain_nodes, N_edges=bc.num_brain_edges_directed, L=np.inf, gamma=1., brain_size=(7., 7., 7.)): """Create a graph based in which source growth depends on total, not out-degree. Source selection probability is proportional to total degree^gamma, and target selection is either random or dependent on distance (through length constant L). Parameters: ----------- N: how many nodes N_edges: how many edges L: length constant gamma: power to raise degree to brain_size: size of space in which nodes are randomly placed Returns: -------- G, A, D: Networkx graph object, adjacency matrix, distance matrix""" # Pick node positions & calculate distance matrix centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) # Calculate distance matrix and distance decay matrix D = aux_tools.dist_mat(centroids) D_decay = np.exp(-D / L) # Initialize diagonal adjacency matrix A = np.eye(N, dtype=float) # Make graph object G = nx.DiGraph() G.add_nodes_from(np.arange(N)) G.centroids = centroids # Randomly add edges edge_ctr = 0 while edge_ctr < N_edges: # Update degree list & degree-related probability vector outdegs = A.sum(1).astype(float) indegs = A.sum(0).astype(float) total_degs = outdegs + indegs total_degs_prob = total_degs.copy() # Calculate source node probability P = total_degs_prob ** gamma # On the off chance that P == 0, skip if P.sum() == 0: continue # Otherwise keep going on P /= float(P.sum()) # Normalize probabilities to sum to 1 # Sample node from distribution src_idx = np.random.choice(np.arange(N), p=P) D_src = D_decay[src_idx, :] # Find unavailable cxns and set their probability to zero unavail_mask = A[src_idx, :] > 0 D_src[unavail_mask] = 0 # Set self-connection probability to 0 D_src[src_idx] = 0 D_src /= float(D_src.sum()) # Pick random node to draw edge to targ_idx = np.random.choice(np.arange(N), p=D_src) # Skip this node if already fully connected if outdegs[src_idx] == N: continue # Add edge to graph if A[src_idx, targ_idx] == 0: G.add_edge(src_idx, targ_idx, {'d': D[src_idx, targ_idx]}) # Add edge to adjacency matrix A[src_idx, targ_idx] += 1 # Increment edge counter edge_ctr += 1 # Set diagonals to zero np.fill_diagonal(A, 0) return G, A.astype(int), D
def pure_geometric(N, N_edges, L, brain_size=[7., 7., 7.]): """ Create a pure geometric model in which nodes are embedded in 3-D space and connect preferentially to physically nearby nodes. :param N: number of nodes :param N_edges: number of edges :param L: length constant :param brain_size: volume in which to distribute nodes in :return: graph, adjacency matrix, distance matrix """ # randomly distribute nodes in space & compute distance matrix centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) D = aux_tools.dist_mat(centroids) # make an adjacency matrix and graph A = np.zeros((N, N), dtype=float) G = nx.Graph() G.add_nodes_from(range(N)) # initialize some helper variables node_idxs = np.arange(N, dtype=int) degs = np.zeros((N, ), dtype=int) fully_connected = np.zeros((N, ), dtype=bool) # add edges edge_ctr = 0 while edge_ctr < N_edges: # pick source randomly src = np.random.choice(node_idxs[fully_connected == False]) # get distance-dependent probabilities to all available targets d_probs = np.exp(-D[src, :]/L) # unnormalized # compute which nodes are available to connect to unavailable_targs = fully_connected.copy() # no fully connected nodes unavailable_targs[src] = True # can't connect to self unavailable_targs[A[src, :] == 1] = True # can't connect to already connected nodes # set probability to zero if node unavailable d_probs[unavailable_targs] = 0. # normalize d_probs /= d_probs.sum() # randomly pick a target targ = np.random.choice(node_idxs, p=d_probs) # add edge to graph and adjacency matrix G.add_edge(src, targ) A[src, targ] = 1 A[targ, src] = 1 # update degrees degs[src] += 1 degs[targ] += 1 # update available_from if degs[src] == N: fully_connected[src] = False if degs[targ] == N: fully_connected[targ] = False edge_ctr += 1 return G, A, D
def biophysical_reverse_outdegree(N=bc.num_brain_nodes, N_edges=bc.num_brain_edges_directed, L=np.inf, gamma=1., brain_size=(7., 7., 7.)): """Create a biophysically inspired graph. Source probability depends on outdegree. Target probability depends on distance. Args: N: how many nodes N_edges: how many edges L: length constant gamma: power to raise degree to brain_size: size of space in which nodes are randomly placed Returns: Networkx graph object, adjacency matrix, distance matrix""" # Pick node positions & calculate distance matrix if brain_size == 'brain': centroids_dict = aux_random_graphs.get_coords() labels = centroids_dict.keys() centroids = np.zeros((N, 3)) for ctr, label in enumerate(labels): centroids[ctr, :] = centroids_dict[label] / 10.0 else: centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) # Calculate distance matrix and distance decay matrix D = aux_tools.dist_mat(centroids) D_decay = np.exp(-D / L) # Initialize diagonal adjacency matrix A = np.eye(N, dtype=float) # Make graph object G = nx.DiGraph() G.add_nodes_from(np.arange(N)) G.centroids = centroids # Randomly add edges edge_ctr = 0 while edge_ctr < N_edges: # Update degree list & degree-related probability vector outdegs = A.sum(1).astype(float) outdegs_prob = outdegs.copy() # Calculate source node probability P = outdegs_prob ** gamma # On the off chance that P == 0, skip if P.sum() == 0: continue # Otherwise keep going on P /= float(P.sum()) # Normalize probabilities to sum to 1 # Sample node from distribution src_idx = np.random.choice(np.arange(N), p=P) D_src = D_decay[src_idx, :] # Find unavailable cxns and set their probability to zero unavail_mask = A[src_idx, :] > 0 D_src[unavail_mask] = 0 # Set self-connection probability to 0 D_src[src_idx] = 0 D_src /= float(D_src.sum()) # Pick random node to draw edge to targ_idx = np.random.choice(np.arange(N), p=D_src) # Skip this node if already fully connected if outdegs[src_idx] == N: continue # Add edge to graph if A[src_idx, targ_idx] == 0: G.add_edge(src_idx, targ_idx, {'d': D[src_idx, targ_idx]}) # Add edge to adjacency matrix A[src_idx, targ_idx] += 1 # Increment edge counter edge_ctr += 1 # Set diagonals to zero np.fill_diagonal(A, 0) return G, A, D
def biophysical_reverse(N=bc.num_brain_nodes, N_edges=bc.num_brain_edges_directed, L=np.inf, gamma=1.75, brain_size=[7., 7., 7.]): """Create a biophysically inspired graph. Connection probabilities depend on distance & degree. Args: N: how many nodes N_edges: how many edges L: length constant gamma: power to raise degree to brain_size: size of space in which nodes are randomly placed Returns: Networkx graph object, adjacency matrix, distance matrix""" # Pick node positions & calculate distance matrix centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) # Calculate distance matrix and distance decay matrix D = aux_tools.dist_mat(centroids) D_decay = np.exp(-D / L) # Initialize diagonal adjacency matrix A = np.eye(N, dtype=float) # Make graph object G = nx.DiGraph() G.add_nodes_from(np.arange(N)) # Randomly add edges edge_ctr = 0 while edge_ctr < N_edges: # Update degree list & degree-related probability vector indegs = A.sum(0).astype(float) outdegs = A.sum(1).astype(float) degs = indegs + outdegs degs_prob = degs.copy() # Pick random node to draw edge to to_idx = np.random.randint(low=0, high=N) # Skip this node if already fully connected if outdegs[to_idx] == N: continue # Find unavailable cxns and set their probability to zero unavail_mask = A[:, to_idx] > 0 degs_prob[unavail_mask] = 0 # Set self cxn probability to zero degs_prob[to_idx] = 0 # Calculate cxn probabilities from degree & distance P = (degs_prob**gamma) * D_decay[:, to_idx] # On the off changes that P == 0, skip if P.sum() == 0: continue # Otherwise keep going on P /= float(P.sum()) # Normalize probabilities to sum to 1 # Sample node from distribution from_idx = np.random.choice(np.arange(N), p=P) # Add edge to graph if A[from_idx, to_idx] == 0: G.add_edge(from_idx, to_idx, {'d': D[from_idx, to_idx]}) # Add edge to adjacency matrix A[from_idx, to_idx] += 1 # Increment edge counter edge_ctr += 1 # Set diagonals to zero np.fill_diagonal(A, 0) return G, A, D
def source_growth_total_degree(N=bc.num_brain_nodes, N_edges=bc.num_brain_edges_directed, L=np.inf, gamma=1., brain_size=(7., 7., 7.)): """Create a graph based in which source growth depends on total, not out-degree. Source selection probability is proportional to total degree^gamma, and target selection is either random or dependent on distance (through length constant L). Parameters: ----------- N: how many nodes N_edges: how many edges L: length constant gamma: power to raise degree to brain_size: size of space in which nodes are randomly placed Returns: -------- G, A, D: Networkx graph object, adjacency matrix, distance matrix""" # Pick node positions & calculate distance matrix centroids = np.random.uniform([0, 0, 0], brain_size, (N, 3)) # Calculate distance matrix and distance decay matrix D = aux_tools.dist_mat(centroids) D_decay = np.exp(-D / L) # Initialize diagonal adjacency matrix A = np.eye(N, dtype=float) # Make graph object G = nx.DiGraph() G.add_nodes_from(np.arange(N)) G.centroids = centroids # Randomly add edges edge_ctr = 0 while edge_ctr < N_edges: # Update degree list & degree-related probability vector outdegs = A.sum(1).astype(float) indegs = A.sum(0).astype(float) total_degs = outdegs + indegs total_degs_prob = total_degs.copy() # Calculate source node probability P = total_degs_prob**gamma # On the off chance that P == 0, skip if P.sum() == 0: continue # Otherwise keep going on P /= float(P.sum()) # Normalize probabilities to sum to 1 # Sample node from distribution src_idx = np.random.choice(np.arange(N), p=P) D_src = D_decay[src_idx, :] # Find unavailable cxns and set their probability to zero unavail_mask = A[src_idx, :] > 0 D_src[unavail_mask] = 0 # Set self-connection probability to 0 D_src[src_idx] = 0 D_src /= float(D_src.sum()) # Pick random node to draw edge to targ_idx = np.random.choice(np.arange(N), p=D_src) # Skip this node if already fully connected if outdegs[src_idx] == N: continue # Add edge to graph if A[src_idx, targ_idx] == 0: G.add_edge(src_idx, targ_idx, {'d': D[src_idx, targ_idx]}) # Add edge to adjacency matrix A[src_idx, targ_idx] += 1 # Increment edge counter edge_ctr += 1 # Set diagonals to zero np.fill_diagonal(A, 0) return G, A.astype(int), D