networktools module

clabtoolkit.networktools.adjacency_matrix_to_csr(adj_matrix)[source]

Convert a dense adjacency matrix to CSR (Compressed Sparse Row) format.

This method takes a square adjacency matrix where non-zero entries represent connections between vertices and converts it to an efficient sparse representation.

Parameters:

adj_matrix (np.ndarray) – A square 2D numpy array representing the adjacency matrix. Shape should be (n_vertices, n_vertices) where n_vertices is the number of vertices in the graph. Non-zero values represent edge weights.

Returns:

A scipy sparse CSR matrix representing the same graph connectivity.

Return type:

csr_matrix

Raises:
  • ValueError – If the input matrix is not 2D or not square.

  • TypeError – If the input is not a numpy array.

Examples

>>> import numpy as np
>>> # Create a simple 4-vertex graph
>>> adj = np.array([[0, 1, 1, 0],
...                 [1, 0, 1, 1],
...                 [1, 1, 0, 1],
...                 [0, 1, 1, 0]])
>>> csr_graph = adjacency_matrix_to_csr(adj)
>>> print(csr_graph.toarray())
[[0 1 1 0]
[1 0 1 1]
[1 1 0 1]
[0 1 1 0]]
>>> # With weighted edges
>>> adj_weighted = np.array([[0, 2.5, 1.0, 0],
...                          [2.5, 0, 0, 3.2],
...                          [1.0, 0, 0, 1.8],
...                          [0, 3.2, 1.8, 0]])
>>> csr_weighted = adjacency_matrix_to_csr(adj_weighted)
>>> print(f"Non-zero values: {csr_weighted.data}")
Non-zero values: [2.5 1.  2.5 3.2 1.  1.8 3.2 1.8]
clabtoolkit.networktools.triangulated_mesh_to_csr(faces, n_vertices=None)[source]

Convert triangulated mesh faces to a CSR graph representation.

This method constructs a graph where vertices are connected if they share an edge in any triangle face. All edge weights are set to 1. The resulting graph represents the 1-ring neighborhood connectivity of the mesh.

Parameters:
  • faces (np.ndarray) – A 2D numpy array of shape (n_faces, 3) where each row contains the indices of three vertices forming a triangle. Vertex indices should be non-negative integers.

  • n_vertices (int, optional) – Total number of vertices in the mesh. If None, it will be inferred as the maximum vertex index + 1. Providing this parameter is recommended for meshes with isolated vertices.

Returns:

A scipy sparse CSR matrix of shape (n_vertices, n_vertices) where entry (i,j) is 1 if vertices i and j are connected by an edge in the mesh, and 0 otherwise. The matrix is symmetric for undirected graphs.

Return type:

csr_matrix

Raises:
  • ValueError – If faces array is not 2D, doesn’t have 3 columns, contains negative indices, or if n_vertices is less than the maximum vertex index.

  • TypeError – If faces is not a numpy array or contains non-integer values.

Examples

>>> import numpy as np
>>> # Define a simple tetrahedron (4 faces, 4 vertices)
>>> faces = np.array([[0, 1, 2],
...                   [0, 1, 3],
...                   [0, 2, 3],
...                   [1, 2, 3]])
>>> csr_graph = triangulated_mesh_to_csr(faces)
>>> print("Adjacency matrix:")
>>> print(csr_graph.toarray())
Adjacency matrix:
[[0 1 1 1]
[1 0 1 1]
[1 1 0 1]
[1 1 1 0]]
>>> # Triangle mesh with explicit vertex count
>>> faces_triangle = np.array([[0, 1, 2]])
>>> csr_triangle = triangulated_mesh_to_csr(faces_triangle, n_vertices=5)
>>> print(f"Shape: {csr_triangle.shape}")
>>> print("Connections for triangle [0,1,2]:")
>>> print(csr_triangle.toarray())
Shape: (5, 5)
Connections for triangle [0,1,2]:
[[0 1 1 0 0]
[1 0 1 0 0]
[1 1 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]]
clabtoolkit.networktools.edges_to_csr(edges, edge_values=None, n_vertices=None, symmetric=True)[source]

Convert an edge list with values to CSR graph representation.

This method constructs a graph from a list of edges and their corresponding weights/values. Useful for creating graphs from pre-computed edge lists.

Parameters:
  • edges (np.ndarray) – A 2D numpy array of shape (n_edges, 2) where each row contains the indices of two connected vertices. Vertex indices should be non-negative integers.

  • edge_values (np.ndarray) – A 1D numpy array of length n_edges containing the weight/value for each edge. Values can be any numeric type (int, float, etc.).

  • n_vertices (int, optional) – Total number of vertices in the graph. If None, it will be inferred as the maximum vertex index + 1. Providing this parameter is recommended for graphs with isolated vertices.

  • symmetric (bool, default=True) – If True, creates an undirected graph by adding reverse edges with the same values. If False, creates a directed graph using only the provided edges.

Returns:

A scipy sparse CSR matrix of shape (n_vertices, n_vertices) where entry (i,j) contains the weight of the edge from vertex i to vertex j. For undirected graphs (symmetric=True), the matrix is symmetric.

Return type:

csr_matrix

Raises:
  • ValueError – If edges array is not 2D, doesn’t have 2 columns, contains negative indices, edge_values length doesn’t match number of edges, or if n_vertices is less than the maximum vertex index.

  • TypeError – If edges contains non-integer values or edge_values is not numeric.

Examples

>>> import numpy as np
>>> # Create a simple weighted graph
>>> edges = np.array([[0, 1],
...                   [1, 2],
...                   [0, 2]])
>>> values = np.array([2.5, 1.0, 3.2])
>>> csr_graph = edges_to_csr(edges, values)
>>> print("Symmetric weighted graph:")
>>> print(csr_graph.toarray())
Symmetric weighted graph:
[[0.  2.5 3.2]
[2.5 0.  1. ]
[3.2 1.  0. ]]
>>> # Directed graph example
>>> edges_directed = np.array([[0, 1], [1, 2]])
>>> values_directed = np.array([0.8, 1.5])
>>> csr_directed = edges_to_csr(edges_directed, values_directed, symmetric=False)
>>> print("Directed graph:")
>>> print(csr_directed.toarray())
Directed graph:
[[0.  0.8 0. ]
[0.  0.  1.5]
[0.  0.  0. ]]
>>> # Handle duplicate edges (values are summed)
>>> edges_dup = np.array([[0, 1], [0, 1], [1, 0]])
>>> values_dup = np.array([1.0, 2.0, 0.5])
>>> csr_dup = edges_to_csr(edges_dup, values_dup)
>>> print("Duplicate edges (summed):")
>>> print(csr_dup.toarray())
Duplicate edges (summed):
[[0.  3.5]
[3.5 0. ]]
clabtoolkit.networktools.connected_components(csr_graph, method='union_find', return_labels=False)[source]

Find connected components in a CSR graph representation.

This method identifies all connected components in an undirected graph represented as a CSR matrix. A connected component is a maximal set of vertices such that there is a path between every pair of vertices in the set.

Parameters:
  • csr_graph (csr_matrix) – A scipy sparse CSR matrix representing the graph adjacency matrix. Should be square with shape (n_vertices, n_vertices). For undirected graphs, the matrix should be symmetric. Non-zero entries represent connections.

  • method (str, default="union_find") –

    Algorithm to use for finding connected components: - “union_find”: Disjoint Set Union with path compression and union by rank.

    Most efficient for sparse graphs. Time: O(E * α(V)), Space: O(V).

    • ”bfs”: Breadth-First Search traversal. Good memory characteristics. Time: O(V + E), Space: O(V).

    • ”dfs”: Depth-First Search traversal. Simple and intuitive. Time: O(V + E), Space: O(V).

  • return_labels (bool, default=False) – If True, also return a label array where labels[i] indicates which component vertex i belongs to. If False, only return the component lists.

Returns:

  • components (List[List[int]]) – A list of connected components, where each component is a list of vertex indices belonging to that component. Components are sorted by the smallest vertex index in each component.

  • labels (np.ndarray, optional) – Only returned if return_labels=True. A 1D array of length n_vertices where labels[i] is the component ID (0-indexed) that vertex i belongs to.

Raises:
  • TypeError – If csr_graph is not a scipy csr_matrix.

  • ValueError – If csr_graph is not square, method is not recognized, or graph is empty.

  • UserWarning – If the graph appears to be directed (non-symmetric) when undirected behavior is expected.

Return type:

List[List[int]] | Tuple[List[List[int]], ndarray]

Examples

>>> import numpy as np
>>> from scipy.sparse import csr_matrix
>>>
>>> # Create a graph with 3 components: [0,1], [2,3,4], [5]
>>> row = np.array([0, 1, 2, 2, 3, 3, 4, 4])
>>> col = np.array([1, 0, 3, 4, 2, 4, 2, 3])
>>> data = np.ones(len(row))
>>> graph = csr_matrix((data, (row, col)), shape=(6, 6))
>>>
>>> components = connected_components(graph)
>>> print("Connected components:")
>>> for i, comp in enumerate(components):
...     print(f"  Component {i}: {comp}")
Connected components:
  Component 0: [0, 1]
  Component 1: [2, 3, 4]
  Component 2: [5]
>>> # Get component labels as well
>>> components, labels = connected_components(graph, return_labels=True)
>>> print(f"Component labels: {labels}")
>>> print(f"Vertex 3 belongs to component: {labels[3]}")
Component labels: [0 0 1 1 1 2]
Vertex 3 belongs to component: 1
>>> # Using different algorithms
>>> comp_bfs = connected_components(graph, method="bfs")
>>> comp_dfs = connected_components(graph, method="dfs")
>>> # All methods should give the same result (possibly in different order)
>>> # Example with weighted edges (weights are ignored for connectivity)
>>> weighted_graph = edges_to_csr(
...     np.array([[0, 1], [1, 2]]),
...     np.array([2.5, 3.0])
... )
>>> components = connected_components(weighted_graph)
>>> print(f"Weighted graph components: {components}")
Weighted graph components: [[0, 1, 2]]

Notes

  • Edge weights are ignored; only connectivity matters.

  • Self-loops (diagonal entries) are ignored for component detection.

  • For directed graphs, this finds weakly connected components (treating

    edges as undirected).

  • Empty components (isolated vertices) are included as single-vertex components.

clabtoolkit.networktools.component_statistics(components)[source]

Compute statistics about connected components.

Parameters:

components (List[List[int]]) – List of connected components from connected_components().

Returns:

Dictionary containing component statistics: - ‘num_components’: Number of connected components - ‘largest_component_size’: Size of the largest component - ‘smallest_component_size’: Size of the smallest component - ‘average_component_size’: Average component size - ‘component_sizes’: List of all component sizes - ‘giant_component_ratio’: Ratio of largest to total vertices

Return type:

dict

Examples

>>> components = [[0, 1, 2, 3], [4], [5, 6]]
>>> stats = component_statistics(components)
>>> print(f"Number of components: {stats['num_components']}")
>>> print(f"Giant component ratio: {stats['giant_component_ratio']:.2f}")
Number of components: 3
Giant component ratio: 0.57

The networktools module provides graph analysis and network theory tools specifically designed for brain connectivity and mesh-based analysis.

Key Features

  • Graph representation creation from brain meshes

  • Sparse matrix operations for large-scale networks

  • Connectivity analysis utilities

  • Network topology analysis

  • Efficient storage and manipulation of large graphs

  • Integration with scipy.sparse for performance

Main Functions

Graph Conversion

  • triangulated_mesh_to_csr(): Convert triangulated meshes to compressed sparse row format

  • adjacency_matrix_to_csr(): Convert adjacency matrices to sparse format

  • edges_to_csr(): Convert edge lists to compressed sparse row format

Common Usage Examples

Converting mesh to graph:

from clabtoolkit.networktools import triangulated_mesh_to_csr
import nibabel as nib

# Load surface mesh
vertices, faces = nib.freesurfer.read_geometry("lh.pial")

# Convert to sparse graph representation
graph_csr = triangulated_mesh_to_csr(vertices, faces)

print(f"Graph shape: {graph_csr.shape}")
print(f"Number of edges: {graph_csr.nnz}")

Working with connectivity matrices:

from clabtoolkit.networktools import adjacency_matrix_to_csr
import numpy as np

# Convert dense connectivity matrix to sparse format
connectivity_matrix = np.random.rand(100, 100)
connectivity_matrix = (connectivity_matrix + connectivity_matrix.T) / 2  # Make symmetric

# Convert to sparse format for efficient processing
sparse_conn = adjacency_matrix_to_csr(connectivity_matrix, threshold=0.1)

# Analyze network properties
print(f"Network density: {sparse_conn.nnz / (sparse_conn.shape[0]**2):.3f}")

Edge list conversion:

# Convert edge list to sparse matrix
edges = [[0, 1], [1, 2], [2, 0]]  # Triangle connectivity
sparse_matrix = edges_to_csr(edges, n_nodes=3)

print(f"Graph shape: {sparse_matrix.shape}")
print(f"Number of edges: {sparse_matrix.nnz}")
print(f"Network density: {sparse_matrix.nnz / (sparse_matrix.shape[0]**2):.3f}")