Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

""" 

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 

 

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 symmetric 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