laplacian.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. """
  2. Laplacian centrality measures.
  3. """
  4. import networkx as nx
  5. __all__ = ["laplacian_centrality"]
  6. @nx._dispatchable(edge_attrs="weight")
  7. def laplacian_centrality(
  8. G, normalized=True, nodelist=None, weight="weight", walk_type=None, alpha=0.95
  9. ):
  10. r"""Compute the Laplacian centrality for nodes in the graph `G`.
  11. The Laplacian Centrality of a node ``i`` is measured by the drop in the
  12. Laplacian Energy after deleting node ``i`` from the graph. The Laplacian Energy
  13. is the sum of the squared eigenvalues of a graph's Laplacian matrix.
  14. .. math::
  15. C_L(u_i,G) = \frac{(\Delta E)_i}{E_L (G)} = \frac{E_L (G)-E_L (G_i)}{E_L (G)}
  16. E_L (G) = \sum_{i=0}^n \lambda_i^2
  17. Where $E_L (G)$ is the Laplacian energy of graph `G`,
  18. E_L (G_i) is the Laplacian energy of graph `G` after deleting node ``i``
  19. and $\lambda_i$ are the eigenvalues of `G`'s Laplacian matrix.
  20. This formula shows the normalized value. Without normalization,
  21. the numerator on the right side is returned.
  22. Parameters
  23. ----------
  24. G : graph
  25. A networkx graph
  26. normalized : bool (default = True)
  27. If True the centrality score is scaled so the sum over all nodes is 1.
  28. If False the centrality score for each node is the drop in Laplacian
  29. energy when that node is removed.
  30. nodelist : list, optional (default = None)
  31. The rows and columns are ordered according to the nodes in nodelist.
  32. If nodelist is None, then the ordering is produced by G.nodes().
  33. weight: string or None, optional (default=`weight`)
  34. Optional parameter `weight` to compute the Laplacian matrix.
  35. The edge data key used to compute each value in the matrix.
  36. If None, then each edge has weight 1.
  37. walk_type : string or None, optional (default=None)
  38. Optional parameter `walk_type` used when calling
  39. :func:`directed_laplacian_matrix <networkx.directed_laplacian_matrix>`.
  40. One of ``"random"``, ``"lazy"``, or ``"pagerank"``. If ``walk_type=None``
  41. (the default), then a value is selected according to the properties of `G`:
  42. - ``walk_type="random"`` if `G` is strongly connected and aperiodic
  43. - ``walk_type="lazy"`` if `G` is strongly connected but not aperiodic
  44. - ``walk_type="pagerank"`` for all other cases.
  45. alpha : real (default = 0.95)
  46. Optional parameter `alpha` used when calling
  47. :func:`directed_laplacian_matrix <networkx.directed_laplacian_matrix>`.
  48. (1 - alpha) is the teleportation probability used with pagerank.
  49. Returns
  50. -------
  51. nodes : dictionary
  52. Dictionary of nodes with Laplacian centrality as the value.
  53. Examples
  54. --------
  55. >>> G = nx.Graph()
  56. >>> edges = [(0, 1, 4), (0, 2, 2), (2, 1, 1), (1, 3, 2), (1, 4, 2), (4, 5, 1)]
  57. >>> G.add_weighted_edges_from(edges)
  58. >>> sorted((v, f"{c:0.2f}") for v, c in laplacian_centrality(G).items())
  59. [(0, '0.70'), (1, '0.90'), (2, '0.28'), (3, '0.22'), (4, '0.26'), (5, '0.04')]
  60. Notes
  61. -----
  62. The algorithm is implemented based on [1]_ with an extension to directed graphs
  63. using the ``directed_laplacian_matrix`` function.
  64. Raises
  65. ------
  66. NetworkXPointlessConcept
  67. If the graph `G` is the null graph.
  68. ZeroDivisionError
  69. If the graph `G` has no edges (is empty) and normalization is requested.
  70. References
  71. ----------
  72. .. [1] Qi, X., Fuller, E., Wu, Q., Wu, Y., and Zhang, C.-Q. (2012).
  73. Laplacian centrality: A new centrality measure for weighted networks.
  74. Information Sciences, 194:240-253.
  75. https://math.wvu.edu/~cqzhang/Publication-files/my-paper/INS-2012-Laplacian-W.pdf
  76. See Also
  77. --------
  78. :func:`~networkx.linalg.laplacianmatrix.directed_laplacian_matrix`
  79. :func:`~networkx.linalg.laplacianmatrix.laplacian_matrix`
  80. """
  81. import numpy as np
  82. import scipy as sp
  83. if len(G) == 0:
  84. raise nx.NetworkXPointlessConcept("null graph has no centrality defined")
  85. if G.size(weight=weight) == 0:
  86. if normalized:
  87. raise ZeroDivisionError("graph with no edges has zero full energy")
  88. return {n: 0 for n in G}
  89. if nodelist is not None:
  90. nodeset = set(G.nbunch_iter(nodelist))
  91. if len(nodeset) != len(nodelist):
  92. raise nx.NetworkXError("nodelist has duplicate nodes or nodes not in G")
  93. nodes = nodelist + [n for n in G if n not in nodeset]
  94. else:
  95. nodelist = nodes = list(G)
  96. if G.is_directed():
  97. lap_matrix = nx.directed_laplacian_matrix(G, nodes, weight, walk_type, alpha)
  98. else:
  99. lap_matrix = nx.laplacian_matrix(G, nodes, weight).toarray()
  100. full_energy = np.sum(lap_matrix**2)
  101. # calculate laplacian centrality
  102. laplace_centralities_dict = {}
  103. for i, node in enumerate(nodelist):
  104. # remove row and col i from lap_matrix
  105. all_but_i = list(np.arange(lap_matrix.shape[0]))
  106. all_but_i.remove(i)
  107. A_2 = lap_matrix[all_but_i, :][:, all_but_i]
  108. # Adjust diagonal for removed row
  109. new_diag = lap_matrix.diagonal() - abs(lap_matrix[:, i])
  110. np.fill_diagonal(A_2, new_diag[all_but_i])
  111. if len(all_but_i) > 0: # catches degenerate case of single node
  112. new_energy = np.sum(A_2**2)
  113. else:
  114. new_energy = 0.0
  115. lapl_cent = full_energy - new_energy
  116. if normalized:
  117. lapl_cent = lapl_cent / full_energy
  118. laplace_centralities_dict[node] = float(lapl_cent)
  119. return laplace_centralities_dict