def current_flow_closeness_centrality(G, weight=None, dtype=float, solver="lu"): """Compute current-flow closeness centrality for nodes. Current-flow closeness centrality is variant of closeness centrality based on effective resistance between nodes in a network. This metric is also known as information centrality. Parameters ---------- G : graph A NetworkX graph. weight : None or string, optional (default=None) If None, all edge weights are considered equal. Otherwise holds the name of the edge attribute used as weight. The weight reflects the capacity or the strength of the edge. dtype: data type (default=float) Default data type for internal matrices. Set to np.float32 for lower memory consumption. solver: string (default='lu') Type of linear solver to use for computing the flow matrix. Options are "full" (uses most memory), "lu" (recommended), and "cg" (uses least memory). Returns ------- nodes : dictionary Dictionary of nodes with current flow closeness centrality as the value. See Also -------- closeness_centrality Notes ----- The algorithm is from Brandes [1]_. See also [2]_ for the original definition of information centrality. References ---------- .. [1] Ulrik Brandes and Daniel Fleischer, Centrality Measures Based on Current Flow. Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05). LNCS 3404, pp. 533-544. Springer-Verlag, 2005. http://algo.uni-konstanz.de/publications/bf-cmbcf-05.pdf .. [2] Karen Stephenson and Marvin Zelen: Rethinking centrality: Methods and examples. Social Networks 11(1):1-37, 1989. https://doi.org/10.1016/0378-8733(89)90016-6 """ if not nx.is_connected(G): raise nx.NetworkXError("Graph not connected.") solvername = { "full": FullInverseLaplacian, "lu": SuperLUInverseLaplacian, "cg": CGInverseLaplacian, } n = G.number_of_nodes() ordering = list(reverse_cuthill_mckee_ordering(G)) # make a copy with integer labels according to rcm ordering # this could be done without a copy if we really wanted to H = nx.relabel_nodes(G, dict(zip(ordering, range(n)))) betweenness = dict.fromkeys(H, 0.0) # b[v]=0 for v in H n = H.number_of_nodes() L = laplacian_sparse_matrix(H, nodelist=range(n), weight=weight, dtype=dtype, format="csc") C2 = solvername[solver](L, width=1, dtype=dtype) # initialize solver for v in H: col = C2.get_row(v) for w in H: betweenness[v] += col[v] - 2 * col[w] betweenness[w] += col[v] for v in H: betweenness[v] = 1.0 / (betweenness[v]) return {ordering[k]: float(v) for k, v in betweenness.items()}
def approximate_current_flow_betweenness_centrality(G, normalized=True, weight=None, dtype=float, solver='full', epsilon=0.5, kmax=10000, seed=None): r"""Compute the approximate current-flow betweenness centrality for nodes. Approximates the current-flow betweenness centrality within absolute error of epsilon with high probability [1]_. Parameters ---------- G : graph A NetworkX graph normalized : bool, optional (default=True) If True the betweenness values are normalized by 2/[(n-1)(n-2)] where n is the number of nodes in G. weight : string or None, optional (default=None) Key for edge data used as the edge weight. If None, then use 1 as each edge weight. dtype : data type (float) Default data type for internal matrices. Set to np.float32 for lower memory consumption. solver : string (default='lu') Type of linear solver to use for computing the flow matrix. Options are "full" (uses most memory), "lu" (recommended), and "cg" (uses least memory). epsilon: float Absolute error tolerance. kmax: int Maximum number of sample node pairs to use for approximation. seed : integer, random_state, or None (default) Indicator of random number generation state. See :ref:`Randomness<randomness>`. Returns ------- nodes : dictionary Dictionary of nodes with betweenness centrality as the value. See Also -------- current_flow_betweenness_centrality Notes ----- The running time is $O((1/\epsilon^2)m{\sqrt k} \log n)$ and the space required is $O(m)$ for $n$ nodes and $m$ edges. If the edges have a 'weight' attribute they will be used as weights in this algorithm. Unspecified weights are set to 1. References ---------- .. [1] Ulrik Brandes and Daniel Fleischer: Centrality Measures Based on Current Flow. Proc. 22nd Symp. Theoretical Aspects of Computer Science (STACS '05). LNCS 3404, pp. 533-544. Springer-Verlag, 2005. http://algo.uni-konstanz.de/publications/bf-cmbcf-05.pdf """ try: import numpy as np except ImportError: raise ImportError( 'current_flow_betweenness_centrality requires NumPy ', 'http://scipy.org/') try: from scipy import sparse from scipy.sparse import linalg except ImportError: raise ImportError( 'current_flow_betweenness_centrality requires SciPy ', 'http://scipy.org/') if not nx.is_connected(G): raise nx.NetworkXError("Graph not connected.") solvername = { "full": FullInverseLaplacian, "lu": SuperLUInverseLaplacian, "cg": CGInverseLaplacian } n = G.number_of_nodes() ordering = list(reverse_cuthill_mckee_ordering(G)) # make a copy with integer labels according to rcm ordering # this could be done without a copy if we really wanted to H = nx.relabel_nodes(G, dict(zip(ordering, range(n)))) L = laplacian_sparse_matrix(H, nodelist=range(n), weight=weight, dtype=dtype, format='csc') C = solvername[solver](L, dtype=dtype) # initialize solver betweenness = dict.fromkeys(H, 0.0) nb = (n - 1.0) * (n - 2.0) # normalization factor cstar = n * (n - 1) / nb l = 1 # parameter in approximation, adjustable k = l * int(np.ceil((cstar / epsilon)**2 * np.log(n))) if k > kmax: msg = 'Number random pairs k>kmax (%d>%d) ' % (k, kmax) raise nx.NetworkXError(msg, 'Increase kmax or epsilon') cstar2k = cstar / (2 * k) for i in range(k): s, t = seed.sample(range(n), 2) b = np.zeros(n, dtype=dtype) b[s] = 1 b[t] = -1 p = C.solve(b) for v in H: if v == s or v == t: continue for nbr in H[v]: w = H[v][nbr].get(weight, 1.0) betweenness[v] += w * np.abs(p[v] - p[nbr]) * cstar2k if normalized: factor = 1.0 else: factor = nb / 2.0 # remap to original node names and "unnormalize" if required return dict( (ordering[k], float(v * factor)) for k, v in betweenness.items())