1"""Katz centrality."""
2
3import math
4
5import networkx as nx
6from networkx.utils import not_implemented_for
7
8__all__ = ["katz_centrality", "katz_centrality_numpy"]
9
10
11@not_implemented_for("multigraph")
12@nx._dispatchable(edge_attrs="weight")
13def katz_centrality(
14 G,
15 alpha=0.1,
16 beta=1.0,
17 max_iter=1000,
18 tol=1.0e-6,
19 nstart=None,
20 normalized=True,
21 weight=None,
22):
23 r"""Compute the Katz centrality for the nodes of the graph G.
24
25 Katz centrality computes the centrality for a node based on the centrality
26 of its neighbors. It is a generalization of the eigenvector centrality. The
27 Katz centrality for node $i$ is
28
29 .. math::
30
31 x_i = \alpha \sum_{j} A_{ij} x_j + \beta,
32
33 where $A$ is the adjacency matrix of graph G with eigenvalues $\lambda$.
34
35 The parameter $\beta$ controls the initial centrality and
36
37 .. math::
38
39 \alpha < \frac{1}{\lambda_{\max}}.
40
41 Katz centrality computes the relative influence of a node within a
42 network by measuring the number of the immediate neighbors (first
43 degree nodes) and also all other nodes in the network that connect
44 to the node under consideration through these immediate neighbors.
45
46 Extra weight can be provided to immediate neighbors through the
47 parameter $\beta$. Connections made with distant neighbors
48 are, however, penalized by an attenuation factor $\alpha$ which
49 should be strictly less than the inverse largest eigenvalue of the
50 adjacency matrix in order for the Katz centrality to be computed
51 correctly. More information is provided in [1]_.
52
53 Parameters
54 ----------
55 G : graph
56 A NetworkX graph.
57
58 alpha : float, optional (default=0.1)
59 Attenuation factor
60
61 beta : scalar or dictionary, optional (default=1.0)
62 Weight attributed to the immediate neighborhood. If not a scalar, the
63 dictionary must have a value for every node.
64
65 max_iter : integer, optional (default=1000)
66 Maximum number of iterations in power method.
67
68 tol : float, optional (default=1.0e-6)
69 Error tolerance used to check convergence in power method iteration.
70
71 nstart : dictionary, optional
72 Starting value of Katz iteration for each node.
73
74 normalized : bool, optional (default=True)
75 If True normalize the resulting values.
76
77 weight : None or string, optional (default=None)
78 If None, all edge weights are considered equal.
79 Otherwise holds the name of the edge attribute used as weight.
80 In this measure the weight is interpreted as the connection strength.
81
82 Returns
83 -------
84 nodes : dictionary
85 Dictionary of nodes with Katz centrality as the value.
86
87 Raises
88 ------
89 NetworkXError
90 If the parameter `beta` is not a scalar but lacks a value for at least
91 one node
92
93 PowerIterationFailedConvergence
94 If the algorithm fails to converge to the specified tolerance
95 within the specified number of iterations of the power iteration
96 method.
97
98 Examples
99 --------
100 >>> import math
101 >>> G = nx.path_graph(4)
102 >>> phi = (1 + math.sqrt(5)) / 2.0 # largest eigenvalue of adj matrix
103 >>> centrality = nx.katz_centrality(G, 1 / phi - 0.01)
104 >>> for n, c in sorted(centrality.items()):
105 ... print(f"{n} {c:.2f}")
106 0 0.37
107 1 0.60
108 2 0.60
109 3 0.37
110
111 See Also
112 --------
113 katz_centrality_numpy
114 eigenvector_centrality
115 eigenvector_centrality_numpy
116 :func:`~networkx.algorithms.link_analysis.pagerank_alg.pagerank`
117 :func:`~networkx.algorithms.link_analysis.hits_alg.hits`
118
119 Notes
120 -----
121 Katz centrality was introduced by [2]_.
122
123 This algorithm it uses the power method to find the eigenvector
124 corresponding to the largest eigenvalue of the adjacency matrix of ``G``.
125 The parameter ``alpha`` should be strictly less than the inverse of largest
126 eigenvalue of the adjacency matrix for the algorithm to converge.
127 You can use ``max(nx.adjacency_spectrum(G))`` to get $\lambda_{\max}$ the largest
128 eigenvalue of the adjacency matrix.
129 The iteration will stop after ``max_iter`` iterations or an error tolerance of
130 ``number_of_nodes(G) * tol`` has been reached.
131
132 For strongly connected graphs, as $\alpha \to 1/\lambda_{\max}$, and $\beta > 0$,
133 Katz centrality approaches the results for eigenvector centrality.
134
135 For directed graphs this finds "left" eigenvectors which corresponds
136 to the in-edges in the graph. For out-edges Katz centrality,
137 first reverse the graph with ``G.reverse()``.
138
139 References
140 ----------
141 .. [1] Mark E. J. Newman:
142 Networks: An Introduction.
143 Oxford University Press, USA, 2010, p. 720.
144 .. [2] Leo Katz:
145 A New Status Index Derived from Sociometric Index.
146 Psychometrika 18(1):39–43, 1953
147 https://link.springer.com/content/pdf/10.1007/BF02289026.pdf
148 """
149 if len(G) == 0:
150 return {}
151
152 nnodes = G.number_of_nodes()
153
154 if nstart is None:
155 # choose starting vector with entries of 0
156 x = {n: 0 for n in G}
157 else:
158 x = nstart
159
160 try:
161 b = dict.fromkeys(G, float(beta))
162 except (TypeError, ValueError, AttributeError) as err:
163 b = beta
164 if set(beta) != set(G):
165 raise nx.NetworkXError(
166 "beta dictionary must have a value for every node"
167 ) from err
168
169 # make up to max_iter iterations
170 for _ in range(max_iter):
171 xlast = x
172 x = dict.fromkeys(xlast, 0)
173 # do the multiplication y^T = Alpha * x^T A + Beta
174 for n in x:
175 for nbr in G[n]:
176 x[nbr] += xlast[n] * G[n][nbr].get(weight, 1)
177 for n in x:
178 x[n] = alpha * x[n] + b[n]
179
180 # check convergence
181 error = sum(abs(x[n] - xlast[n]) for n in x)
182 if error < nnodes * tol:
183 if normalized:
184 # normalize vector
185 try:
186 s = 1.0 / math.hypot(*x.values())
187 except ZeroDivisionError:
188 s = 1.0
189 else:
190 s = 1
191 for n in x:
192 x[n] *= s
193 return x
194 raise nx.PowerIterationFailedConvergence(max_iter)
195
196
197@not_implemented_for("multigraph")
198@nx._dispatchable(edge_attrs="weight")
199def katz_centrality_numpy(G, alpha=0.1, beta=1.0, normalized=True, weight=None):
200 r"""Compute the Katz centrality for the graph G.
201
202 Katz centrality computes the centrality for a node based on the centrality
203 of its neighbors. It is a generalization of the eigenvector centrality. The
204 Katz centrality for node $i$ is
205
206 .. math::
207
208 x_i = \alpha \sum_{j} A_{ij} x_j + \beta,
209
210 where $A$ is the adjacency matrix of graph G with eigenvalues $\lambda$.
211
212 The parameter $\beta$ controls the initial centrality and
213
214 .. math::
215
216 \alpha < \frac{1}{\lambda_{\max}}.
217
218 Katz centrality computes the relative influence of a node within a
219 network by measuring the number of the immediate neighbors (first
220 degree nodes) and also all other nodes in the network that connect
221 to the node under consideration through these immediate neighbors.
222
223 Extra weight can be provided to immediate neighbors through the
224 parameter $\beta$. Connections made with distant neighbors
225 are, however, penalized by an attenuation factor $\alpha$ which
226 should be strictly less than the inverse largest eigenvalue of the
227 adjacency matrix in order for the Katz centrality to be computed
228 correctly. More information is provided in [1]_.
229
230 Parameters
231 ----------
232 G : graph
233 A NetworkX graph
234
235 alpha : float
236 Attenuation factor
237
238 beta : scalar or dictionary, optional (default=1.0)
239 Weight attributed to the immediate neighborhood. If not a scalar the
240 dictionary must have an value for every node.
241
242 normalized : bool
243 If True normalize the resulting values.
244
245 weight : None or string, optional
246 If None, all edge weights are considered equal.
247 Otherwise holds the name of the edge attribute used as weight.
248 In this measure the weight is interpreted as the connection strength.
249
250 Returns
251 -------
252 nodes : dictionary
253 Dictionary of nodes with Katz centrality as the value.
254
255 Raises
256 ------
257 NetworkXError
258 If the parameter `beta` is not a scalar but lacks a value for at least
259 one node
260
261 Examples
262 --------
263 >>> import math
264 >>> G = nx.path_graph(4)
265 >>> phi = (1 + math.sqrt(5)) / 2.0 # largest eigenvalue of adj matrix
266 >>> centrality = nx.katz_centrality_numpy(G, 1 / phi)
267 >>> for n, c in sorted(centrality.items()):
268 ... print(f"{n} {c:.2f}")
269 0 0.37
270 1 0.60
271 2 0.60
272 3 0.37
273
274 See Also
275 --------
276 katz_centrality
277 eigenvector_centrality_numpy
278 eigenvector_centrality
279 :func:`~networkx.algorithms.link_analysis.pagerank_alg.pagerank`
280 :func:`~networkx.algorithms.link_analysis.hits_alg.hits`
281
282 Notes
283 -----
284 Katz centrality was introduced by [2]_.
285
286 This algorithm uses a direct linear solver to solve the above equation.
287 The parameter ``alpha`` should be strictly less than the inverse of largest
288 eigenvalue of the adjacency matrix for there to be a solution.
289 You can use ``max(nx.adjacency_spectrum(G))`` to get $\lambda_{\max}$ the largest
290 eigenvalue of the adjacency matrix.
291
292 For strongly connected graphs, as $\alpha \to 1/\lambda_{\max}$, and $\beta > 0$,
293 Katz centrality approaches the results for eigenvector centrality.
294
295 For directed graphs this finds "left" eigenvectors which corresponds
296 to the in-edges in the graph. For out-edges Katz centrality,
297 first reverse the graph with ``G.reverse()``.
298
299 References
300 ----------
301 .. [1] Mark E. J. Newman:
302 Networks: An Introduction.
303 Oxford University Press, USA, 2010, p. 173.
304 .. [2] Leo Katz:
305 A New Status Index Derived from Sociometric Index.
306 Psychometrika 18(1):39–43, 1953
307 https://link.springer.com/content/pdf/10.1007/BF02289026.pdf
308 """
309 import numpy as np
310
311 if len(G) == 0:
312 return {}
313 try:
314 nodelist = beta.keys()
315 if set(nodelist) != set(G):
316 raise nx.NetworkXError("beta dictionary must have a value for every node")
317 b = np.array(list(beta.values()), dtype=float)
318 except AttributeError:
319 nodelist = list(G)
320 try:
321 b = np.ones((len(nodelist), 1)) * beta
322 except (TypeError, ValueError, AttributeError) as err:
323 raise nx.NetworkXError("beta must be a number") from err
324
325 A = nx.adjacency_matrix(G, nodelist=nodelist, weight=weight).todense().T
326 n = A.shape[0]
327 centrality = np.linalg.solve(np.eye(n, n) - (alpha * A), b).squeeze()
328
329 # Normalize: rely on truediv to cast to float, then tolist to make Python numbers
330 norm = np.sign(sum(centrality)) * np.linalg.norm(centrality) if normalized else 1
331 return dict(zip(nodelist, (centrality / norm).tolist()))