Matrix Utilities

driada.network.matrix_utils.get_neighbors_from_adj(a, node)[source]

Get outgoing neighbors of a node from adjacency matrix.

Parameters:
  • a (numpy.ndarray or scipy sparse matrix) – Adjacency matrix of the graph. Must support matrix indexing with a[[node], :] and .nonzero() method.

  • node (int) – Node index to find neighbors for (0-based). Negative indices are supported following Python’s indexing convention.

Returns:

Array of neighbor node indices where a[node, :] is non-zero. Includes self-loops if present. Returns indices in column order.

Return type:

numpy.ndarray

Raises:

IndexError – From underlying array if node index is out of bounds.

Notes

For directed graphs, returns out-neighbors only (nodes that can be reached from this node).

driada.network.matrix_utils.get_ccs_from_adj(adj)[source]

Find all connected components in an undirected graph.

Parameters:

adj (array-like or sparse matrix) – Adjacency matrix of the graph. Should be symmetric for undirected graphs.

Yields:

set – Set of node indices in each connected component. Components are yielded in order of lowest node index. Isolated nodes are included as single-node components.

Notes

For directed graphs, this finds weakly connected components (treating edges as undirected). For strongly connected components in directed graphs, use get_sccs_from_adj instead.

driada.network.matrix_utils.get_sccs_from_adj(adj)[source]

Get strongly connected components using Tarjan’s algorithm.

Adapted from networkx.algorithms.components.strongly_connected.strongly_connected_components

Parameters:

adj (numpy.ndarray or scipy sparse matrix) – Adjacency matrix representing a directed graph. Must support .shape attribute and work with get_neighbors_from_adj() function.

Yields:

set – Set of nodes in each strongly connected component. Components are yielded as they are discovered.

Notes

Implements non-recursive version of Tarjan’s algorithm. A strongly connected component is a maximal set of nodes where every node is reachable from every other node following directed edges.

driada.network.matrix_utils.get_giant_cc_from_adj(adj)[source]

Extract the giant (largest) connected component from a graph.

Parameters:

adj (sparse matrix) – Adjacency matrix of the graph. Must be a scipy sparse matrix.

Returns:

  • gcc_adj (sparse matrix) – Adjacency matrix of the giant connected component.

  • node_mapping (dict) – Mapping from new node indices to original node indices.

Raises:
  • ValueError – If the graph is empty (no nodes).

  • AttributeError – If adj is not a sparse matrix (missing tocsc/tocsr methods).

Notes

For directed graphs, this finds the giant weakly connected component.

driada.network.matrix_utils.get_giant_scc_from_adj(adj)[source]

Extract the giant (largest) strongly connected component from a directed graph.

Parameters:

adj (sparse matrix) – Adjacency matrix of the directed graph. Must be a scipy sparse matrix.

Returns:

  • gscc_adj (sparse matrix) – Adjacency matrix of the giant strongly connected component.

  • node_mapping (dict) – Mapping from new node indices to original node indices.

Raises:
  • ValueError – If the graph is empty (no nodes).

  • AttributeError – If adj is not a sparse matrix (missing tocsc/tocsr methods).

Notes

For undirected graphs, each node will be its own SCC and the function will return an arbitrary single-node component.

driada.network.matrix_utils.assign_random_weights(A)[source]

Assign random weights to edges in an adjacency matrix.

Parameters:

A (array-like or sparse matrix) – Binary adjacency matrix. Can be dense numpy array or scipy sparse matrix.

Returns:

Weighted adjacency matrix with random weights in [0,1], symmetrized to ensure undirected graph. Returns same format as input (dense if A is dense, sparse if A is sparse).

Return type:

array-like

Notes

  • The function generates uniform random weights in [0,1]

  • Weights are symmetrized by averaging: (W + W.T) / 2

  • Directed edges will get averaged weights

  • Zero entries in A remain zero in the output

  • To control randomness, set numpy’s random seed before calling

  • Sparse matrices are handled efficiently without densification

driada.network.matrix_utils.turn_to_partially_directed(mat, directed=0.0, weighted=0)[source]

Convert a symmetric matrix to partially directed by randomly removing edges.

This function supports both dense (numpy) and sparse (scipy.sparse) matrices. Sparse format is preferred for memory efficiency with large graphs.

Parameters:
  • mat (np.ndarray or scipy.sparse matrix) – Input adjacency matrix (should be symmetric)

  • directed (float or None, default=0.0) – Fraction of edges to make directed. Must be in range [0.0, 1.0] or None. 0.0 = fully undirected, 1.0 = fully directed. None is converted to 0.0.

  • weighted (int, default=0) – Whether the matrix represents a weighted graph (0=binary, 1=weighted)

Returns:

Partially directed adjacency matrix in sparse format (always returns sparse)

Return type:

scipy.sparse.csr_matrix

Raises:

ValueError – If directed is not in range [0.0, 1.0] (after None conversion)

Notes

  • Self-loops are always removed

  • Only symmetric edge pairs are affected

  • Asymmetric edges remain unchanged

  • Uses uniform random distribution for edge direction selection

driada.network.matrix_utils.get_symmetry_index(a)[source]

Calculate symmetry index of a matrix.

The symmetry index measures what fraction of edges in a directed graph have their reciprocal edge present. It quantifies the degree of bidirectionality in the network.

Parameters:

a (array-like or sparse matrix) – Input adjacency matrix to analyze. Can be weighted or binary.

Returns:

Symmetry index in range [0, 1]: - 1.0: Perfectly symmetric (all edges are bidirectional) - 0.0: Completely asymmetric (no reciprocal edges) - 0.5: Half of edges have reciprocal counterparts

Return type:

float

Notes

The symmetry index is calculated as: (number of edges with symmetric counterpart) / (total number of edges)

An edge a[i,j] has a symmetric counterpart if a[j,i] is also non-zero. Self-loops (diagonal elements) always have their symmetric counterpart (themselves) and thus contribute to symmetry.

Empty matrices (no edges) are considered perfectly symmetric and return 1.0.

This metric differs from counting symmetric pairs: it counts each edge individually and checks if its reverse exists, rather than counting unique bidirectional pairs.

Examples

>>> # Perfectly symmetric matrix
>>> A = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]])
>>> get_symmetry_index(A)
1.0
>>> # Directed cycle (no reciprocal edges)
>>> B = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]])
>>> get_symmetry_index(B)
0.0
>>> # Partially symmetric
>>> C = np.array([[0, 1, 1], [1, 0, 0], [0, 0, 0]])
>>> get_symmetry_index(C)  # 2 edges have counterparts out of 3 total
0.6666...
driada.network.matrix_utils.symmetric_component(A, is_weighted)[source]

Extract the symmetric component of a matrix.

Finds edges that exist in both directions (i,j) and (j,i). For weighted graphs, preserves the original weights.

Parameters:
  • A (square sparse matrix) – Input adjacency matrix. Must be square (n×n) as the function computes A ∩ A.T. Supports scipy sparse matrix formats.

  • is_weighted (bool) – If True, preserve edge weights; if False, return binary matrix.

Returns:

Symmetric component of the input matrix. Returns sparse matrix if input is sparse, dense matrix if input is dense.

Return type:

sparse matrix or numpy.matrix

Raises:

ValueError – If A is not a square matrix (due to broadcasting error in A.T operation).

Notes

The symmetric component contains only edges that exist bidirectionally. This is useful for analyzing reciprocal connections in directed networks.

Mathematical relationship: A = symmetric_component(A) + non_symmetric_component(A)

driada.network.matrix_utils.non_symmetric_component(A, is_weighted)[source]

Extract the non-symmetric (asymmetric) component of a matrix.

Finds edges that exist in only one direction, i.e., (i,j) exists but (j,i) does not, or vice versa.

Parameters:
  • A (square sparse matrix or array) – Input adjacency matrix. Must be square (n×n) for transpose operation.

  • is_weighted (bool) – If True, preserve edge weights; if False, work with binary matrix.

Returns:

Non-symmetric component of the input matrix. Returns same format as input (sparse if input is sparse, dense if input is dense).

Return type:

sparse matrix or array

Raises:

ValueError – If A is not a square matrix.

Notes

The non-symmetric component represents unidirectional connections in directed networks. Mathematical relationship: A = symmetric_component(A) + non_symmetric_component(A).

This function now preserves sparsity to avoid memory issues with large matrices.

driada.network.matrix_utils.remove_duplicates(coo)[source]

Remove duplicate entries from a COO-format sparse matrix.

When multiple values exist for the same (i,j) position, keeps only the last occurrence. This is useful for cleaning malformed sparse matrices.

Parameters:

coo (scipy.sparse.coo_matrix) – COO-format sparse matrix potentially containing duplicates. Must be in COO format (not CSR, CSC, etc.).

Returns:

COO matrix with duplicates removed.

Return type:

scipy.sparse.coo_matrix

Raises:

AttributeError – If input is not in COO format (missing .row, .col, .data attributes).

Notes

COO format allows duplicate entries, but most algorithms expect unique (i,j) pairs. This function ensures data integrity by keeping the last value for each (i,j) position. The “last” value depends on the order of data in the COO arrays.

Note: This uses DOK intermediate format which has O(nnz) time complexity but high memory overhead. For large matrices, consider using scipy’s built-in duplicate summing behavior instead.

driada.network.matrix_utils.adj_input_to_csr_sparse_matrix(a)[source]

Convert various matrix formats to CSR sparse format.

Handles numpy arrays and different scipy sparse formats, ensuring the output is always in CSR format for efficient row operations.

Parameters:

a (np.ndarray or scipy.sparse matrix) – Input matrix in various formats (dense, COO, CSC, CSR). Other sparse formats (LIL, DIA, BSR) are not supported.

Returns:

Matrix in CSR (Compressed Sparse Row) format. Note: returns csr_array (not csr_matrix) for consistency.

Return type:

scipy.sparse.csr_array

Raises:

Exception – If input format is not recognized.

Notes

CSR format is efficient for row slicing and matrix arithmetic. COO matrices are cleaned of duplicates before conversion. CSC matrices are properly converted to CSR format.

driada.network.matrix_utils.remove_selfloops_from_adj(a)[source]

Remove self-loops (diagonal elements) from adjacency matrix.

Self-loops are edges from a node to itself. This function sets all diagonal elements to zero.

Parameters:

a (array-like or sparse matrix) – Input adjacency matrix.

Returns:

Adjacency matrix with self-loops removed (diagonal = 0). Always returns a copy, even if no self-loops exist.

Return type:

array-like or sparse matrix

Notes

Only modifies the matrix if self-loops exist (trace != 0). For sparse matrices, explicitly removes zeros after diagonal clearing. Always returns a copy to ensure safety.

driada.network.matrix_utils.remove_isolates_from_adj(a)[source]

Remove isolated nodes (nodes with no connections) from adjacency matrix.

Isolated nodes have zero in-degree and zero out-degree. This function removes such nodes and returns the cleaned matrix with a mapping.

Parameters:

a (array-like or sparse matrix) – Input adjacency matrix.

Returns:

  • cleared_matrix (scipy.sparse.csr_array) – Adjacency matrix with isolated nodes removed.

  • node_mapping (dict) – Mapping from new node indices to original indices.

Notes

Degree calculation is binary (ignores edge weights).

driada.network.matrix_utils.sausage_index(A, nn)[source]

Calculate the sausage index of a network.

The sausage index measures the proportion of edges that connect nodes within a distance nn along the main diagonal. High values indicate a “sausage-like” or chain-like network structure.

Parameters:
  • A (array-like) – Adjacency matrix (typically symmetric).

  • nn (int) – Maximum diagonal distance to consider as “sausage edges”.

Returns:

Sausage index value between 0 and 1. Returns 0.0 for empty graphs (no edges).

Return type:

float

Notes

Sausage index = (edges within nn diagonals) / (total edges). Values close to 1 indicate strong linear/chain structure. Values close to 0 indicate more random connectivity. Only counts upper diagonals (assumes symmetric matrix).

driada.network.matrix_utils.get_laplacian(A)[source]

Compute the Laplacian matrix L = D - A.

Parameters:

A (array_like or sparse matrix) – Adjacency matrix

Returns:

L – Laplacian matrix (same type as input). Always returns float type.

Return type:

array_like or sparse matrix

Notes

Uses out-degree (row sums) for directed graphs. Isolated nodes have L[i,i] = 0.

driada.network.matrix_utils.get_inv_sqrt_diag_matrix(a)[source]

Compute D^(-1/2) where D is the degree matrix.

Parameters:

a (array_like or sparse matrix) – Adjacency matrix

Returns:

DH – Inverse square root of degree matrix (same type as input). Zero-degree nodes have 0 on diagonal.

Return type:

array_like or sparse matrix

driada.network.matrix_utils.get_norm_laplacian(a)[source]

Compute the normalized Laplacian L = I - D^(-1/2) A D^(-1/2).

Parameters:

a (array_like or sparse matrix) – Adjacency matrix (must be symmetric)

Returns:

matrix – Normalized Laplacian (same type as input)

Return type:

array_like or sparse matrix

Raises:

Exception – If adjacency matrix is not symmetric.

Notes

The normalized Laplacian is L = I - D^(-1/2) A D^(-1/2).

driada.network.matrix_utils.get_inv_diag_matrix(a)[source]

Compute D^(-1) where D is the degree matrix.

Parameters:

a (array_like or sparse matrix) – Adjacency matrix

Returns:

Dinv – Inverse of degree matrix (same type as input). Zero-degree nodes have 0 on diagonal.

Return type:

array_like or sparse matrix

driada.network.matrix_utils.get_rw_laplacian(a)[source]

Compute the random walk Laplacian L_rw = I - D^(-1)A.

Parameters:

a (array_like or sparse matrix) – Adjacency matrix

Returns:

matrix – Random walk Laplacian (same type as input). Always returns float dtype.

Return type:

array_like or sparse matrix

Notes

For random walks, L_rw represents “staying probabilities”. Isolated nodes have L_rw[i,i] = 1 (100% probability of staying) since they have no outgoing edges.

driada.network.matrix_utils.get_trans_matrix(a)[source]

Compute the transition matrix T = D^(-1)A.

Parameters:

a (array_like or sparse matrix) – Adjacency matrix

Returns:

T – Transition matrix (same type as input). Always returns float dtype.

Return type:

array_like or sparse matrix

Notes

Row-stochastic matrix where rows sum to 1 (or 0 for isolated nodes). Uses out-degree normalization for directed graphs.

This module provides utilities for working with adjacency matrices and other matrix operations in network analysis.