subgraph_alg.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. """
  2. Subraph centrality and communicability betweenness.
  3. """
  4. import networkx as nx
  5. from networkx.utils import not_implemented_for
  6. __all__ = [
  7. "subgraph_centrality_exp",
  8. "subgraph_centrality",
  9. "communicability_betweenness_centrality",
  10. "estrada_index",
  11. ]
  12. @not_implemented_for("directed")
  13. @not_implemented_for("multigraph")
  14. def subgraph_centrality_exp(G):
  15. r"""Returns the subgraph centrality for each node of G.
  16. Subgraph centrality of a node `n` is the sum of weighted closed
  17. walks of all lengths starting and ending at node `n`. The weights
  18. decrease with path length. Each closed walk is associated with a
  19. connected subgraph ([1]_).
  20. Parameters
  21. ----------
  22. G: graph
  23. Returns
  24. -------
  25. nodes:dictionary
  26. Dictionary of nodes with subgraph centrality as the value.
  27. Raises
  28. ------
  29. NetworkXError
  30. If the graph is not undirected and simple.
  31. See Also
  32. --------
  33. subgraph_centrality:
  34. Alternative algorithm of the subgraph centrality for each node of G.
  35. Notes
  36. -----
  37. This version of the algorithm exponentiates the adjacency matrix.
  38. The subgraph centrality of a node `u` in G can be found using
  39. the matrix exponential of the adjacency matrix of G [1]_,
  40. .. math::
  41. SC(u)=(e^A)_{uu} .
  42. References
  43. ----------
  44. .. [1] Ernesto Estrada, Juan A. Rodriguez-Velazquez,
  45. "Subgraph centrality in complex networks",
  46. Physical Review E 71, 056103 (2005).
  47. https://arxiv.org/abs/cond-mat/0504730
  48. Examples
  49. --------
  50. (Example from [1]_)
  51. >>> G = nx.Graph(
  52. ... [
  53. ... (1, 2),
  54. ... (1, 5),
  55. ... (1, 8),
  56. ... (2, 3),
  57. ... (2, 8),
  58. ... (3, 4),
  59. ... (3, 6),
  60. ... (4, 5),
  61. ... (4, 7),
  62. ... (5, 6),
  63. ... (6, 7),
  64. ... (7, 8),
  65. ... ]
  66. ... )
  67. >>> sc = nx.subgraph_centrality_exp(G)
  68. >>> print([f"{node} {sc[node]:0.2f}" for node in sorted(sc)])
  69. ['1 3.90', '2 3.90', '3 3.64', '4 3.71', '5 3.64', '6 3.71', '7 3.64', '8 3.90']
  70. """
  71. # alternative implementation that calculates the matrix exponential
  72. import scipy as sp
  73. import scipy.linalg # call as sp.linalg
  74. nodelist = list(G) # ordering of nodes in matrix
  75. A = nx.to_numpy_array(G, nodelist)
  76. # convert to 0-1 matrix
  77. A[A != 0.0] = 1
  78. expA = sp.linalg.expm(A)
  79. # convert diagonal to dictionary keyed by node
  80. sc = dict(zip(nodelist, map(float, expA.diagonal())))
  81. return sc
  82. @not_implemented_for("directed")
  83. @not_implemented_for("multigraph")
  84. def subgraph_centrality(G):
  85. r"""Returns subgraph centrality for each node in G.
  86. Subgraph centrality of a node `n` is the sum of weighted closed
  87. walks of all lengths starting and ending at node `n`. The weights
  88. decrease with path length. Each closed walk is associated with a
  89. connected subgraph ([1]_).
  90. Parameters
  91. ----------
  92. G: graph
  93. Returns
  94. -------
  95. nodes : dictionary
  96. Dictionary of nodes with subgraph centrality as the value.
  97. Raises
  98. ------
  99. NetworkXError
  100. If the graph is not undirected and simple.
  101. See Also
  102. --------
  103. subgraph_centrality_exp:
  104. Alternative algorithm of the subgraph centrality for each node of G.
  105. Notes
  106. -----
  107. This version of the algorithm computes eigenvalues and eigenvectors
  108. of the adjacency matrix.
  109. Subgraph centrality of a node `u` in G can be found using
  110. a spectral decomposition of the adjacency matrix [1]_,
  111. .. math::
  112. SC(u)=\sum_{j=1}^{N}(v_{j}^{u})^2 e^{\lambda_{j}},
  113. where `v_j` is an eigenvector of the adjacency matrix `A` of G
  114. corresponding to the eigenvalue `\lambda_j`.
  115. Examples
  116. --------
  117. (Example from [1]_)
  118. >>> G = nx.Graph(
  119. ... [
  120. ... (1, 2),
  121. ... (1, 5),
  122. ... (1, 8),
  123. ... (2, 3),
  124. ... (2, 8),
  125. ... (3, 4),
  126. ... (3, 6),
  127. ... (4, 5),
  128. ... (4, 7),
  129. ... (5, 6),
  130. ... (6, 7),
  131. ... (7, 8),
  132. ... ]
  133. ... )
  134. >>> sc = nx.subgraph_centrality(G)
  135. >>> print([f"{node} {sc[node]:0.2f}" for node in sorted(sc)])
  136. ['1 3.90', '2 3.90', '3 3.64', '4 3.71', '5 3.64', '6 3.71', '7 3.64', '8 3.90']
  137. References
  138. ----------
  139. .. [1] Ernesto Estrada, Juan A. Rodriguez-Velazquez,
  140. "Subgraph centrality in complex networks",
  141. Physical Review E 71, 056103 (2005).
  142. https://arxiv.org/abs/cond-mat/0504730
  143. """
  144. import numpy as np
  145. nodelist = list(G) # ordering of nodes in matrix
  146. A = nx.to_numpy_array(G, nodelist)
  147. # convert to 0-1 matrix
  148. A[np.nonzero(A)] = 1
  149. w, v = np.linalg.eigh(A)
  150. vsquare = np.array(v) ** 2
  151. expw = np.exp(w)
  152. xg = vsquare @ expw
  153. # convert vector dictionary keyed by node
  154. sc = dict(zip(nodelist, map(float, xg)))
  155. return sc
  156. @not_implemented_for("directed")
  157. @not_implemented_for("multigraph")
  158. def communicability_betweenness_centrality(G):
  159. r"""Returns subgraph communicability for all pairs of nodes in G.
  160. Communicability betweenness measure makes use of the number of walks
  161. connecting every pair of nodes as the basis of a betweenness centrality
  162. measure.
  163. Parameters
  164. ----------
  165. G: graph
  166. Returns
  167. -------
  168. nodes : dictionary
  169. Dictionary of nodes with communicability betweenness as the value.
  170. Raises
  171. ------
  172. NetworkXError
  173. If the graph is not undirected and simple.
  174. Notes
  175. -----
  176. Let `G=(V,E)` be a simple undirected graph with `n` nodes and `m` edges,
  177. and `A` denote the adjacency matrix of `G`.
  178. Let `G(r)=(V,E(r))` be the graph resulting from
  179. removing all edges connected to node `r` but not the node itself.
  180. The adjacency matrix for `G(r)` is `A+E(r)`, where `E(r)` has nonzeros
  181. only in row and column `r`.
  182. The subraph betweenness of a node `r` is [1]_
  183. .. math::
  184. \omega_{r} = \frac{1}{C}\sum_{p}\sum_{q}\frac{G_{prq}}{G_{pq}},
  185. p\neq q, q\neq r,
  186. where
  187. `G_{prq}=(e^{A}_{pq} - (e^{A+E(r)})_{pq}` is the number of walks
  188. involving node r,
  189. `G_{pq}=(e^{A})_{pq}` is the number of closed walks starting
  190. at node `p` and ending at node `q`,
  191. and `C=(n-1)^{2}-(n-1)` is a normalization factor equal to the
  192. number of terms in the sum.
  193. The resulting `\omega_{r}` takes values between zero and one.
  194. The lower bound cannot be attained for a connected
  195. graph, and the upper bound is attained in the star graph.
  196. References
  197. ----------
  198. .. [1] Ernesto Estrada, Desmond J. Higham, Naomichi Hatano,
  199. "Communicability Betweenness in Complex Networks"
  200. Physica A 388 (2009) 764-774.
  201. https://arxiv.org/abs/0905.4102
  202. Examples
  203. --------
  204. >>> G = nx.Graph([(0, 1), (1, 2), (1, 5), (5, 4), (2, 4), (2, 3), (4, 3), (3, 6)])
  205. >>> cbc = nx.communicability_betweenness_centrality(G)
  206. >>> print([f"{node} {cbc[node]:0.2f}" for node in sorted(cbc)])
  207. ['0 0.03', '1 0.45', '2 0.51', '3 0.45', '4 0.40', '5 0.19', '6 0.03']
  208. """
  209. import numpy as np
  210. import scipy as sp
  211. import scipy.linalg # call as sp.linalg
  212. nodelist = list(G) # ordering of nodes in matrix
  213. n = len(nodelist)
  214. A = nx.to_numpy_array(G, nodelist)
  215. # convert to 0-1 matrix
  216. A[np.nonzero(A)] = 1
  217. expA = sp.linalg.expm(A)
  218. mapping = dict(zip(nodelist, range(n)))
  219. cbc = {}
  220. for v in G:
  221. # remove row and col of node v
  222. i = mapping[v]
  223. row = A[i, :].copy()
  224. col = A[:, i].copy()
  225. A[i, :] = 0
  226. A[:, i] = 0
  227. B = (expA - sp.linalg.expm(A)) / expA
  228. # sum with row/col of node v and diag set to zero
  229. B[i, :] = 0
  230. B[:, i] = 0
  231. B -= np.diag(np.diag(B))
  232. cbc[v] = B.sum()
  233. # put row and col back
  234. A[i, :] = row
  235. A[:, i] = col
  236. # rescale when more than two nodes
  237. order = len(cbc)
  238. if order > 2:
  239. scale = 1.0 / ((order - 1.0) ** 2 - (order - 1.0))
  240. for v in cbc:
  241. cbc[v] *= scale
  242. return cbc
  243. def estrada_index(G):
  244. r"""Returns the Estrada index of a the graph G.
  245. The Estrada Index is a topological index of folding or 3D "compactness" ([1]_).
  246. Parameters
  247. ----------
  248. G: graph
  249. Returns
  250. -------
  251. estrada index: float
  252. Raises
  253. ------
  254. NetworkXError
  255. If the graph is not undirected and simple.
  256. Notes
  257. -----
  258. Let `G=(V,E)` be a simple undirected graph with `n` nodes and let
  259. `\lambda_{1}\leq\lambda_{2}\leq\cdots\lambda_{n}`
  260. be a non-increasing ordering of the eigenvalues of its adjacency
  261. matrix `A`. The Estrada index is ([1]_, [2]_)
  262. .. math::
  263. EE(G)=\sum_{j=1}^n e^{\lambda _j}.
  264. References
  265. ----------
  266. .. [1] E. Estrada, "Characterization of 3D molecular structure",
  267. Chem. Phys. Lett. 319, 713 (2000).
  268. https://doi.org/10.1016/S0009-2614(00)00158-5
  269. .. [2] José Antonio de la Peñaa, Ivan Gutman, Juan Rada,
  270. "Estimating the Estrada index",
  271. Linear Algebra and its Applications. 427, 1 (2007).
  272. https://doi.org/10.1016/j.laa.2007.06.020
  273. Examples
  274. --------
  275. >>> G = nx.Graph([(0, 1), (1, 2), (1, 5), (5, 4), (2, 4), (2, 3), (4, 3), (3, 6)])
  276. >>> ei = nx.estrada_index(G)
  277. >>> print(f"{ei:0.5}")
  278. 20.55
  279. """
  280. return sum(subgraph_centrality(G).values())