Skip to content

Commit

Permalink
DOC: sparse.csgraph: update csgraph docs to use sparray (scipy#21873)
Browse files Browse the repository at this point in the history
* update csgraph docs to use sparray [docs only]

* docs for __init__.py [docs only]

* make input type descriptions consistent [docs only]
  • Loading branch information
dschult authored Nov 12, 2024
1 parent 862fce8 commit e923211
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 152 deletions.
6 changes: 3 additions & 3 deletions scipy/sparse/csgraph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@
... [2, 0, 0],
... [1, 0, 0]])
>>> G_masked = np.ma.masked_values(G_dense, 0)
>>> from scipy.sparse import csr_matrix
>>> G_sparse = csr_matrix(G_dense)
>>> from scipy.sparse import csr_array
>>> G_sparse = csr_array(G_dense)
This becomes more difficult when zero edges are significant. For example,
consider the situation when we slightly modify the above graph::
Expand All @@ -109,7 +109,7 @@
... [0, np.inf, np.inf]])
>>> G2_masked = np.ma.masked_invalid(G2_data)
>>> from scipy.sparse.csgraph import csgraph_from_dense
>>> # G2_sparse = csr_matrix(G2_data) would give the wrong result
>>> # G2_sparse = csr_array(G2_data) would give the wrong result
>>> G2_sparse = csgraph_from_dense(G2_data, null_value=np.inf)
>>> G2_sparse.data
array([ 2., 0., 2., 0.])
Expand Down
32 changes: 16 additions & 16 deletions scipy/sparse/csgraph/_flow.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class MaximumFlowResult:
----------
flow_value : int
The value of the maximum flow.
flow : csr_matrix
flow : csr_array
The maximum flow.
"""

Expand All @@ -43,7 +43,7 @@ def maximum_flow(csgraph, source, sink, *, method='dinic'):
Parameters
----------
csgraph : csr_matrix
csgraph : csr_array
The square matrix representing a directed graph whose (i, j)'th entry
is an integer representing the capacity of the edge between
vertices i and j.
Expand Down Expand Up @@ -132,9 +132,9 @@ def maximum_flow(csgraph, source, sink, *, method='dinic'):
Here, the maximum flow is simply the capacity of the edge:
>>> import numpy as np
>>> from scipy.sparse import csr_matrix
>>> from scipy.sparse import csr_array
>>> from scipy.sparse.csgraph import maximum_flow
>>> graph = csr_matrix([[0, 5], [0, 0]])
>>> graph = csr_array([[0, 5], [0, 0]])
>>> maximum_flow(graph, 0, 1).flow_value
5
>>> maximum_flow(graph, 0, 1, method='edmonds_karp').flow_value
Expand All @@ -145,18 +145,18 @@ def maximum_flow(csgraph, source, sink, *, method='dinic'):
(0) --5--> (1) --3--> (2)
>>> graph = csr_matrix([[0, 5, 0], [0, 0, 3], [0, 0, 0]])
>>> graph = csr_array([[0, 5, 0], [0, 0, 3], [0, 0, 0]])
>>> maximum_flow(graph, 0, 2).flow_value
3
A less trivial example is given in [2]_, Chapter 26.1:
>>> graph = csr_matrix([[0, 16, 13, 0, 0, 0],
... [0, 0, 10, 12, 0, 0],
... [0, 4, 0, 0, 14, 0],
... [0, 0, 9, 0, 0, 20],
... [0, 0, 0, 7, 0, 4],
... [0, 0, 0, 0, 0, 0]])
>>> graph = csr_array([[0, 16, 13, 0, 0, 0],
... [0, 0, 10, 12, 0, 0],
... [0, 4, 0, 0, 14, 0],
... [0, 0, 9, 0, 0, 20],
... [0, 0, 0, 7, 0, 4],
... [0, 0, 0, 0, 0, 0]])
>>> maximum_flow(graph, 0, 5).flow_value
23
Expand All @@ -175,7 +175,7 @@ def maximum_flow(csgraph, source, sink, *, method='dinic'):
by :func:`maximum_bipartite_matching`. Then the CSR representation of the
graph constructed above contains this matrix as a block. Here's an example:
>>> graph = csr_matrix([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 1, 0]])
>>> graph = csr_array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 1, 0]])
>>> print(graph.toarray())
[[0 1 0 1]
[1 0 1 0]
Expand All @@ -191,7 +191,7 @@ def maximum_flow(csgraph, source, sink, *, method='dinic'):
... np.repeat(i + j + 1, j)])
>>> data = np.ones(n + i + j, dtype=int)
>>>
>>> graph_flow = csr_matrix((data, indices, indptr))
>>> graph_flow = csr_array((data, indices, indptr))
>>> print(graph_flow.toarray())
[[0 1 1 1 0 0 0 0 0]
[0 0 0 0 0 1 0 1 0]
Expand Down Expand Up @@ -292,12 +292,12 @@ def _add_reverse_edges(a):
Parameters
----------
a : csr_matrix
a : csr_array
The square matrix in CSR format representing a directed graph
Returns
-------
res : csr_matrix
res : csr_array
A new matrix in CSR format in which the missing edges are represented
by explicit zeros.
Expand All @@ -320,7 +320,7 @@ def _add_reverse_edges(a):
# allocate twice the number of non-zeros in `a` for the data, which
# will always be enough. It might be too many entries in case `a` has
# some reverse edges already; in that case, over-allocating is not
# a problem since csr_matrix implicitly truncates elements of data
# a problem since csr_array implicitly truncates elements of data
# and indices that go beyond the indices given by indptr.
res_data = np.zeros(2 * a.nnz, ITYPE)
cdef ITYPE_t[:] res_data_view = res_data
Expand Down
17 changes: 9 additions & 8 deletions scipy/sparse/csgraph/_laplacian.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def laplacian(
Parameters
----------
csgraph : array_like or sparse matrix, 2 dimensions
csgraph : array_like or sparse array or matrix, 2 dimensions
compressed-sparse graph, with shape (N, N).
normed : bool, optional
If True, then compute symmetrically normalized Laplacian.
Expand Down Expand Up @@ -72,9 +72,9 @@ def laplacian(
Returns
-------
lap : ndarray, or sparse matrix, or `LinearOperator`
lap : ndarray, or sparse array or matrix, or `LinearOperator`
The N x N Laplacian of csgraph. It will be a NumPy array (dense)
if the input was dense, or a sparse matrix otherwise, or
if the input was dense, or a sparse array otherwise, or
the format of a function or `LinearOperator` if
`form` equals 'function' or 'lo', respectively.
diag : ndarray, optional
Expand Down Expand Up @@ -237,8 +237,9 @@ def laplacian(
in a symmetric Laplacian matrix if and only if its graph is symmetric
and has all non-negative degrees, like in the examples above.
The output Laplacian matrix is by default a dense array or a sparse matrix
inferring its shape, format, and dtype from the input graph matrix:
The output Laplacian matrix is by default a dense array or a sparse
array or matrix inferring its class, shape, format, and dtype from
the input graph matrix:
>>> G = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]).astype(np.float32)
>>> G
Expand Down Expand Up @@ -276,19 +277,19 @@ def laplacian(
Our final example illustrates the latter
for a noisy directed linear graph.
>>> from scipy.sparse import diags, random
>>> from scipy.sparse import diags_array, random_array
>>> from scipy.sparse.linalg import lobpcg
Create a directed linear graph with ``N=35`` vertices
using a sparse adjacency matrix ``G``:
>>> N = 35
>>> G = diags(np.ones(N-1), 1, format="csr")
>>> G = diags_array(np.ones(N - 1), offsets=1, format="csr")
Fix a random seed ``rng`` and add a random sparse noise to the graph ``G``:
>>> rng = np.random.default_rng()
>>> G += 1e-2 * random(N, N, density=0.1, random_state=rng)
>>> G += 1e-2 * random_array((N, N), density=0.1, random_state=rng)
Set initial approximations for eigenvectors:
Expand Down
46 changes: 23 additions & 23 deletions scipy/sparse/csgraph/_matching.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def maximum_bipartite_matching(graph, perm_type='row'):
Parameters
----------
graph : sparse matrix
graph : sparse array or matrix
Input sparse in CSR format whose rows represent one partition of the
graph and whose columns represent the other partition. An edge between
two vertices is indicated by the corresponding entry in the matrix
Expand Down Expand Up @@ -72,20 +72,20 @@ def maximum_bipartite_matching(graph, perm_type='row'):
Examples
--------
>>> from scipy.sparse import csr_matrix
>>> from scipy.sparse import csr_array
>>> from scipy.sparse.csgraph import maximum_bipartite_matching
As a simple example, consider a bipartite graph in which the partitions
contain 2 and 3 elements respectively. Suppose that one partition contains
vertices labelled 0 and 1, and that the other partition contains vertices
labelled A, B, and C. Suppose that there are edges connecting 0 and C,
1 and A, and 1 and B. This graph would then be represented by the following
sparse matrix:
sparse array:
>>> graph = csr_matrix([[0, 0, 1], [1, 1, 0]])
>>> graph = csr_array([[0, 0, 1], [1, 1, 0]])
Here, the 1s could be anything, as long as they end up being stored as
elements in the sparse matrix. We can now calculate maximum matchings as
elements in the sparse array. We can now calculate maximum matchings as
follows:
>>> print(maximum_bipartite_matching(graph, perm_type='column'))
Expand All @@ -104,7 +104,7 @@ def maximum_bipartite_matching(graph, perm_type='row'):
>>> data = [0, 0, 0]
>>> indices = [2, 0, 1]
>>> indptr = [0, 1, 3]
>>> graph = csr_matrix((data, indices, indptr))
>>> graph = csr_array((data, indices, indptr))
>>> print(maximum_bipartite_matching(graph, perm_type='column'))
[2 0]
>>> print(maximum_bipartite_matching(graph, perm_type='row'))
Expand All @@ -113,20 +113,20 @@ def maximum_bipartite_matching(graph, perm_type='row'):
When one or both of the partitions are empty, the matching is empty as
well:
>>> graph = csr_matrix((2, 0))
>>> graph = csr_array((2, 0))
>>> print(maximum_bipartite_matching(graph, perm_type='column'))
[-1 -1]
>>> print(maximum_bipartite_matching(graph, perm_type='row'))
[]
When the input matrix is square, and the graph is known to admit a perfect
When the input array is square, and the graph is known to admit a perfect
matching, i.e. a matching with the property that every vertex in the graph
belongs to some edge in the matching, then one can view the output as the
permutation of rows (or columns) turning the input matrix into one with the
permutation of rows (or columns) turning the input array into one with the
property that all diagonal elements are non-empty:
>>> a = [[0, 1, 2, 0], [1, 0, 0, 1], [2, 0, 0, 3], [0, 1, 3, 0]]
>>> graph = csr_matrix(a)
>>> graph = csr_array(a)
>>> perm = maximum_bipartite_matching(graph, perm_type='row')
>>> print(graph[perm].toarray())
[[1 0 0 1]
Expand Down Expand Up @@ -297,8 +297,8 @@ def min_weight_full_bipartite_matching(biadjacency, maximize=False):
Parameters
----------
biadjacency : sparse matrix
Biadjacency matrix of the bipartite graph: A sparse matrix in CSR, CSC,
biadjacency : sparse array or matrix
Biadjacency matrix of the bipartite graph: A sparse array in CSR, CSC,
or COO format whose rows represent one partition of the graph and whose
columns represent the other partition. An edge between two vertices is
indicated by the corresponding entry in the matrix, and the weight of
Expand Down Expand Up @@ -373,12 +373,12 @@ def min_weight_full_bipartite_matching(biadjacency, maximize=False):
Examples
--------
>>> from scipy.sparse import csr_matrix
>>> from scipy.sparse import csr_array
>>> from scipy.sparse.csgraph import min_weight_full_bipartite_matching
Let us first consider an example in which all weights are equal:
>>> biadjacency = csr_matrix([[1, 1, 1], [1, 0, 0], [0, 1, 0]])
>>> biadjacency = csr_array([[1, 1, 1], [1, 0, 0], [0, 1, 0]])
Here, all we get is a perfect matching of the graph:
Expand All @@ -394,14 +394,14 @@ def min_weight_full_bipartite_matching(biadjacency, maximize=False):
:func:`maximum_bipartite_matching`:
>>> from scipy.sparse.csgraph import maximum_bipartite_matching
>>> biadjacency = csr_matrix([[1, 1, 1], [1, 0, 0], [0, 1, 0]])
>>> biadjacency = csr_array([[1, 1, 1], [1, 0, 0], [0, 1, 0]])
>>> print(maximum_bipartite_matching(biadjacency, perm_type='column'))
[2 0 1]
When multiple edges are available, the ones with lowest weights are
preferred:
>>> biadjacency = csr_matrix([[3, 3, 6], [4, 3, 5], [10, 1, 8]])
>>> biadjacency = csr_array([[3, 3, 6], [4, 3, 5], [10, 1, 8]])
>>> row_ind, col_ind = min_weight_full_bipartite_matching(biadjacency)
>>> print(col_ind)
[0 2 1]
Expand All @@ -415,32 +415,32 @@ def min_weight_full_bipartite_matching(biadjacency, maximize=False):
cardinalities, the matching is as large as the smaller of the two
partitions:
>>> biadjacency = csr_matrix([[0, 1, 1], [0, 2, 3]])
>>> biadjacency = csr_array([[0, 1, 1], [0, 2, 3]])
>>> row_ind, col_ind = min_weight_full_bipartite_matching(biadjacency)
>>> print(row_ind, col_ind)
[0 1] [2 1]
>>> biadjacency = csr_matrix([[0, 1], [3, 1], [1, 4]])
>>> biadjacency = csr_array([[0, 1], [3, 1], [1, 4]])
>>> row_ind, col_ind = min_weight_full_bipartite_matching(biadjacency)
>>> print(row_ind, col_ind)
[0 2] [1 0]
When one or both of the partitions are empty, the matching is empty as
well:
>>> biadjacency = csr_matrix((2, 0))
>>> biadjacency = csr_array((2, 0))
>>> row_ind, col_ind = min_weight_full_bipartite_matching(biadjacency)
>>> print(row_ind, col_ind)
[] []
In general, we will always reach the same sum of weights as if we had used
:func:`scipy.optimize.linear_sum_assignment` but note that for that one,
missing edges are represented by a matrix entry of ``float('inf')``. Let us
generate a random sparse matrix with integer entries between 1 and 10:
missing edges are represented by a array entry of ``float('inf')``. Let us
generate a random sparse array with integer entries between 1 and 10:
>>> import numpy as np
>>> from scipy.sparse import random
>>> from scipy.sparse import random_array
>>> from scipy.optimize import linear_sum_assignment
>>> sparse = random(10, 10, random_state=42, density=.5, format='coo') * 10
>>> sparse = random_array((10, 10), random_state=42, density=.5, format='coo') * 10
>>> sparse.data = np.ceil(sparse.data)
>>> dense = sparse.toarray()
>>> dense = np.full(sparse.shape, np.inf)
Expand Down
12 changes: 6 additions & 6 deletions scipy/sparse/csgraph/_min_spanning_tree.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def minimum_spanning_tree(csgraph, overwrite=False):
Parameters
----------
csgraph : array_like or sparse matrix, 2 dimensions
csgraph : array_like or sparse array or matrix, 2 dimensions
The N x N matrix representing an undirected graph over N nodes
(see notes below).
overwrite : bool, optional
Expand Down Expand Up @@ -79,12 +79,12 @@ def minimum_spanning_tree(csgraph, overwrite=False):
removing the edges with weights 8 and 6. In compressed sparse
representation, the solution looks like this:
>>> from scipy.sparse import csr_matrix
>>> from scipy.sparse import csr_array
>>> from scipy.sparse.csgraph import minimum_spanning_tree
>>> X = csr_matrix([[0, 8, 0, 3],
... [0, 0, 2, 5],
... [0, 0, 0, 6],
... [0, 0, 0, 0]])
>>> X = csr_array([[0, 8, 0, 3],
... [0, 0, 2, 5],
... [0, 0, 0, 6],
... [0, 0, 0, 0]])
>>> Tcsr = minimum_spanning_tree(X)
>>> Tcsr.toarray().astype(int)
array([[0, 0, 0, 3],
Expand Down
Loading

0 comments on commit e923211

Please sign in to comment.