123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- """
- Laplacian of a compressed-sparse graph
- """
- # Authors: Aric Hagberg <hagberg@lanl.gov>
- # Gael Varoquaux <gael.varoquaux@normalesup.org>
- # Jake Vanderplas <vanderplas@astro.washington.edu>
- # License: BSD
- from __future__ import division, print_function, absolute_import
- import numpy as np
- from scipy.sparse import isspmatrix
- ###############################################################################
- # Graph laplacian
- def laplacian(csgraph, normed=False, return_diag=False, use_out_degree=False):
- """
- Return the Laplacian matrix of a directed graph.
- Parameters
- ----------
- csgraph : array_like or sparse matrix, 2 dimensions
- compressed-sparse graph, with shape (N, N).
- normed : bool, optional
- If True, then compute normalized Laplacian.
- return_diag : bool, optional
- If True, then also return an array related to vertex degrees.
- use_out_degree : bool, optional
- If True, then use out-degree instead of in-degree.
- This distinction matters only if the graph is asymmetric.
- Default: False.
- Returns
- -------
- lap : ndarray or sparse matrix
- The N x N laplacian matrix of csgraph. It will be a numpy array (dense)
- if the input was dense, or a sparse matrix otherwise.
- diag : ndarray, optional
- The length-N diagonal of the Laplacian matrix.
- For the normalized Laplacian, this is the array of square roots
- of vertex degrees or 1 if the degree is zero.
- Notes
- -----
- The Laplacian matrix of a graph is sometimes referred to as the
- "Kirchoff matrix" or the "admittance matrix", and is useful in many
- parts of spectral graph theory. In particular, the eigen-decomposition
- of the laplacian matrix can give insight into many properties of the graph.
- Examples
- --------
- >>> from scipy.sparse import csgraph
- >>> G = np.arange(5) * np.arange(5)[:, np.newaxis]
- >>> G
- array([[ 0, 0, 0, 0, 0],
- [ 0, 1, 2, 3, 4],
- [ 0, 2, 4, 6, 8],
- [ 0, 3, 6, 9, 12],
- [ 0, 4, 8, 12, 16]])
- >>> csgraph.laplacian(G, normed=False)
- array([[ 0, 0, 0, 0, 0],
- [ 0, 9, -2, -3, -4],
- [ 0, -2, 16, -6, -8],
- [ 0, -3, -6, 21, -12],
- [ 0, -4, -8, -12, 24]])
- """
- if csgraph.ndim != 2 or csgraph.shape[0] != csgraph.shape[1]:
- raise ValueError('csgraph must be a square matrix or array')
- if normed and (np.issubdtype(csgraph.dtype, np.signedinteger)
- or np.issubdtype(csgraph.dtype, np.uint)):
- csgraph = csgraph.astype(float)
- create_lap = _laplacian_sparse if isspmatrix(csgraph) else _laplacian_dense
- degree_axis = 1 if use_out_degree else 0
- lap, d = create_lap(csgraph, normed=normed, axis=degree_axis)
- if return_diag:
- return lap, d
- return lap
- def _setdiag_dense(A, d):
- A.flat[::len(d)+1] = d
- def _laplacian_sparse(graph, normed=False, axis=0):
- if graph.format in ('lil', 'dok'):
- m = graph.tocoo()
- needs_copy = False
- else:
- m = graph
- needs_copy = True
- w = m.sum(axis=axis).getA1() - m.diagonal()
- if normed:
- m = m.tocoo(copy=needs_copy)
- isolated_node_mask = (w == 0)
- w = np.where(isolated_node_mask, 1, np.sqrt(w))
- m.data /= w[m.row]
- m.data /= w[m.col]
- m.data *= -1
- m.setdiag(1 - isolated_node_mask)
- else:
- if m.format == 'dia':
- m = m.copy()
- else:
- m = m.tocoo(copy=needs_copy)
- m.data *= -1
- m.setdiag(w)
- return m, w
- def _laplacian_dense(graph, normed=False, axis=0):
- m = np.array(graph)
- np.fill_diagonal(m, 0)
- w = m.sum(axis=axis)
- if normed:
- isolated_node_mask = (w == 0)
- w = np.where(isolated_node_mask, 1, np.sqrt(w))
- m /= w
- m /= w[:, np.newaxis]
- m *= -1
- _setdiag_dense(m, 1 - isolated_node_mask)
- else:
- m *= -1
- _setdiag_dense(m, w)
- return m, w
|