test_graph_laplacian.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # Author: Gael Varoquaux <gael.varoquaux@normalesup.org>
  2. # Jake Vanderplas <vanderplas@astro.washington.edu>
  3. # License: BSD
  4. from __future__ import division, print_function, absolute_import
  5. import numpy as np
  6. from numpy.testing import assert_allclose, assert_array_almost_equal
  7. from pytest import raises as assert_raises
  8. from scipy import sparse
  9. from scipy.sparse import csgraph
  10. def _explicit_laplacian(x, normed=False):
  11. if sparse.issparse(x):
  12. x = x.todense()
  13. x = np.asarray(x)
  14. y = -1.0 * x
  15. for j in range(y.shape[0]):
  16. y[j,j] = x[j,j+1:].sum() + x[j,:j].sum()
  17. if normed:
  18. d = np.diag(y).copy()
  19. d[d == 0] = 1.0
  20. y /= d[:,None]**.5
  21. y /= d[None,:]**.5
  22. return y
  23. def _check_symmetric_graph_laplacian(mat, normed):
  24. if not hasattr(mat, 'shape'):
  25. mat = eval(mat, dict(np=np, sparse=sparse))
  26. if sparse.issparse(mat):
  27. sp_mat = mat
  28. mat = sp_mat.todense()
  29. else:
  30. sp_mat = sparse.csr_matrix(mat)
  31. laplacian = csgraph.laplacian(mat, normed=normed)
  32. n_nodes = mat.shape[0]
  33. if not normed:
  34. assert_array_almost_equal(laplacian.sum(axis=0), np.zeros(n_nodes))
  35. assert_array_almost_equal(laplacian.T, laplacian)
  36. assert_array_almost_equal(laplacian,
  37. csgraph.laplacian(sp_mat, normed=normed).todense())
  38. assert_array_almost_equal(laplacian,
  39. _explicit_laplacian(mat, normed=normed))
  40. def test_laplacian_value_error():
  41. for t in int, float, complex:
  42. for m in ([1, 1],
  43. [[[1]]],
  44. [[1, 2, 3], [4, 5, 6]],
  45. [[1, 2], [3, 4], [5, 5]]):
  46. A = np.array(m, dtype=t)
  47. assert_raises(ValueError, csgraph.laplacian, A)
  48. def test_symmetric_graph_laplacian():
  49. symmetric_mats = ('np.arange(10) * np.arange(10)[:, np.newaxis]',
  50. 'np.ones((7, 7))',
  51. 'np.eye(19)',
  52. 'sparse.diags([1, 1], [-1, 1], shape=(4,4))',
  53. 'sparse.diags([1, 1], [-1, 1], shape=(4,4)).todense()',
  54. 'np.asarray(sparse.diags([1, 1], [-1, 1], shape=(4,4)).todense())',
  55. 'np.vander(np.arange(4)) + np.vander(np.arange(4)).T')
  56. for mat_str in symmetric_mats:
  57. for normed in True, False:
  58. _check_symmetric_graph_laplacian(mat_str, normed)
  59. def _assert_allclose_sparse(a, b, **kwargs):
  60. # helper function that can deal with sparse matrices
  61. if sparse.issparse(a):
  62. a = a.toarray()
  63. if sparse.issparse(b):
  64. b = a.toarray()
  65. assert_allclose(a, b, **kwargs)
  66. def _check_laplacian(A, desired_L, desired_d, normed, use_out_degree):
  67. for arr_type in np.array, sparse.csr_matrix, sparse.coo_matrix:
  68. for t in int, float, complex:
  69. adj = arr_type(A, dtype=t)
  70. L = csgraph.laplacian(adj, normed=normed, return_diag=False,
  71. use_out_degree=use_out_degree)
  72. _assert_allclose_sparse(L, desired_L, atol=1e-12)
  73. L, d = csgraph.laplacian(adj, normed=normed, return_diag=True,
  74. use_out_degree=use_out_degree)
  75. _assert_allclose_sparse(L, desired_L, atol=1e-12)
  76. _assert_allclose_sparse(d, desired_d, atol=1e-12)
  77. def test_asymmetric_laplacian():
  78. # adjacency matrix
  79. A = [[0, 1, 0],
  80. [4, 2, 0],
  81. [0, 0, 0]]
  82. # Laplacian matrix using out-degree
  83. L = [[1, -1, 0],
  84. [-4, 4, 0],
  85. [0, 0, 0]]
  86. d = [1, 4, 0]
  87. _check_laplacian(A, L, d, normed=False, use_out_degree=True)
  88. # normalized Laplacian matrix using out-degree
  89. L = [[1, -0.5, 0],
  90. [-2, 1, 0],
  91. [0, 0, 0]]
  92. d = [1, 2, 1]
  93. _check_laplacian(A, L, d, normed=True, use_out_degree=True)
  94. # Laplacian matrix using in-degree
  95. L = [[4, -1, 0],
  96. [-4, 1, 0],
  97. [0, 0, 0]]
  98. d = [4, 1, 0]
  99. _check_laplacian(A, L, d, normed=False, use_out_degree=False)
  100. # normalized Laplacian matrix using in-degree
  101. L = [[1, -0.5, 0],
  102. [-2, 1, 0],
  103. [0, 0, 0]]
  104. d = [2, 1, 1]
  105. _check_laplacian(A, L, d, normed=True, use_out_degree=False)
  106. def test_sparse_formats():
  107. for fmt in ('csr', 'csc', 'coo', 'lil', 'dok', 'dia', 'bsr'):
  108. mat = sparse.diags([1, 1], [-1, 1], shape=(4,4), format=fmt)
  109. for normed in True, False:
  110. _check_symmetric_graph_laplacian(mat, normed)