307 lines
10 KiB
Python
307 lines
10 KiB
Python
"""
|
|
Tests for closeness centrality.
|
|
"""
|
|
import pytest
|
|
import networkx as nx
|
|
from networkx.testing import almost_equal
|
|
|
|
class TestClosenessCentrality:
|
|
@classmethod
|
|
def setup_class(cls):
|
|
cls.K = nx.krackhardt_kite_graph()
|
|
cls.P3 = nx.path_graph(3)
|
|
cls.P4 = nx.path_graph(4)
|
|
cls.K5 = nx.complete_graph(5)
|
|
|
|
cls.C4 = nx.cycle_graph(4)
|
|
cls.T = nx.balanced_tree(r=2, h=2)
|
|
cls.Gb = nx.Graph()
|
|
cls.Gb.add_edges_from([(0, 1), (0, 2), (1, 3), (2, 3),
|
|
(2, 4), (4, 5), (3, 5)])
|
|
|
|
F = nx.florentine_families_graph()
|
|
cls.F = F
|
|
|
|
cls.LM = nx.les_miserables_graph()
|
|
|
|
# Create random undirected, unweighted graph for testing incremental version
|
|
cls.undirected_G = nx.fast_gnp_random_graph(n=100, p=0.6, seed=123)
|
|
cls.undirected_G_cc = nx.closeness_centrality(cls.undirected_G)
|
|
|
|
def test_wf_improved(self):
|
|
G = nx.union(self.P4, nx.path_graph([4, 5, 6]))
|
|
c = nx.closeness_centrality(G)
|
|
cwf = nx.closeness_centrality(G, wf_improved=False)
|
|
res = {0: 0.25, 1: 0.375, 2: 0.375, 3: 0.25,
|
|
4: 0.222, 5: 0.333, 6: 0.222}
|
|
wf_res = {0: 0.5, 1: 0.75, 2: 0.75, 3: 0.5,
|
|
4: 0.667, 5: 1.0, 6: 0.667}
|
|
for n in G:
|
|
assert almost_equal(c[n], res[n], places=3)
|
|
assert almost_equal(cwf[n], wf_res[n], places=3)
|
|
|
|
def test_digraph(self):
|
|
G = nx.path_graph(3, create_using=nx.DiGraph())
|
|
c = nx.closeness_centrality(G)
|
|
cr = nx.closeness_centrality(G.reverse())
|
|
d = {0: 0.0, 1: 0.500, 2: 0.667}
|
|
dr = {0: 0.667, 1: 0.500, 2: 0.0}
|
|
for n in sorted(self.P3):
|
|
assert almost_equal(c[n], d[n], places=3)
|
|
assert almost_equal(cr[n], dr[n], places=3)
|
|
|
|
def test_k5_closeness(self):
|
|
c = nx.closeness_centrality(self.K5)
|
|
d = {0: 1.000,
|
|
1: 1.000,
|
|
2: 1.000,
|
|
3: 1.000,
|
|
4: 1.000}
|
|
for n in sorted(self.K5):
|
|
assert almost_equal(c[n], d[n], places=3)
|
|
|
|
def test_p3_closeness(self):
|
|
c = nx.closeness_centrality(self.P3)
|
|
d = {0: 0.667,
|
|
1: 1.000,
|
|
2: 0.667}
|
|
for n in sorted(self.P3):
|
|
assert almost_equal(c[n], d[n], places=3)
|
|
|
|
def test_krackhardt_closeness(self):
|
|
c = nx.closeness_centrality(self.K)
|
|
d = {0: 0.529,
|
|
1: 0.529,
|
|
2: 0.500,
|
|
3: 0.600,
|
|
4: 0.500,
|
|
5: 0.643,
|
|
6: 0.643,
|
|
7: 0.600,
|
|
8: 0.429,
|
|
9: 0.310}
|
|
for n in sorted(self.K):
|
|
assert almost_equal(c[n], d[n], places=3)
|
|
|
|
def test_florentine_families_closeness(self):
|
|
c = nx.closeness_centrality(self.F)
|
|
d = {'Acciaiuoli': 0.368,
|
|
'Albizzi': 0.483,
|
|
'Barbadori': 0.4375,
|
|
'Bischeri': 0.400,
|
|
'Castellani': 0.389,
|
|
'Ginori': 0.333,
|
|
'Guadagni': 0.467,
|
|
'Lamberteschi': 0.326,
|
|
'Medici': 0.560,
|
|
'Pazzi': 0.286,
|
|
'Peruzzi': 0.368,
|
|
'Ridolfi': 0.500,
|
|
'Salviati': 0.389,
|
|
'Strozzi': 0.4375,
|
|
'Tornabuoni': 0.483}
|
|
for n in sorted(self.F):
|
|
assert almost_equal(c[n], d[n], places=3)
|
|
|
|
def test_les_miserables_closeness(self):
|
|
c = nx.closeness_centrality(self.LM)
|
|
d = {'Napoleon': 0.302,
|
|
'Myriel': 0.429,
|
|
'MlleBaptistine': 0.413,
|
|
'MmeMagloire': 0.413,
|
|
'CountessDeLo': 0.302,
|
|
'Geborand': 0.302,
|
|
'Champtercier': 0.302,
|
|
'Cravatte': 0.302,
|
|
'Count': 0.302,
|
|
'OldMan': 0.302,
|
|
'Valjean': 0.644,
|
|
'Labarre': 0.394,
|
|
'Marguerite': 0.413,
|
|
'MmeDeR': 0.394,
|
|
'Isabeau': 0.394,
|
|
'Gervais': 0.394,
|
|
'Listolier': 0.341,
|
|
'Tholomyes': 0.392,
|
|
'Fameuil': 0.341,
|
|
'Blacheville': 0.341,
|
|
'Favourite': 0.341,
|
|
'Dahlia': 0.341,
|
|
'Zephine': 0.341,
|
|
'Fantine': 0.461,
|
|
'MmeThenardier': 0.461,
|
|
'Thenardier': 0.517,
|
|
'Cosette': 0.478,
|
|
'Javert': 0.517,
|
|
'Fauchelevent': 0.402,
|
|
'Bamatabois': 0.427,
|
|
'Perpetue': 0.318,
|
|
'Simplice': 0.418,
|
|
'Scaufflaire': 0.394,
|
|
'Woman1': 0.396,
|
|
'Judge': 0.404,
|
|
'Champmathieu': 0.404,
|
|
'Brevet': 0.404,
|
|
'Chenildieu': 0.404,
|
|
'Cochepaille': 0.404,
|
|
'Pontmercy': 0.373,
|
|
'Boulatruelle': 0.342,
|
|
'Eponine': 0.396,
|
|
'Anzelma': 0.352,
|
|
'Woman2': 0.402,
|
|
'MotherInnocent': 0.398,
|
|
'Gribier': 0.288,
|
|
'MmeBurgon': 0.344,
|
|
'Jondrette': 0.257,
|
|
'Gavroche': 0.514,
|
|
'Gillenormand': 0.442,
|
|
'Magnon': 0.335,
|
|
'MlleGillenormand': 0.442,
|
|
'MmePontmercy': 0.315,
|
|
'MlleVaubois': 0.308,
|
|
'LtGillenormand': 0.365,
|
|
'Marius': 0.531,
|
|
'BaronessT': 0.352,
|
|
'Mabeuf': 0.396,
|
|
'Enjolras': 0.481,
|
|
'Combeferre': 0.392,
|
|
'Prouvaire': 0.357,
|
|
'Feuilly': 0.392,
|
|
'Courfeyrac': 0.400,
|
|
'Bahorel': 0.394,
|
|
'Bossuet': 0.475,
|
|
'Joly': 0.394,
|
|
'Grantaire': 0.358,
|
|
'MotherPlutarch': 0.285,
|
|
'Gueulemer': 0.463,
|
|
'Babet': 0.463,
|
|
'Claquesous': 0.452,
|
|
'Montparnasse': 0.458,
|
|
'Toussaint': 0.402,
|
|
'Child1': 0.342,
|
|
'Child2': 0.342,
|
|
'Brujon': 0.380,
|
|
'MmeHucheloup': 0.353}
|
|
for n in sorted(self.LM):
|
|
assert almost_equal(c[n], d[n], places=3)
|
|
|
|
def test_weighted_closeness(self):
|
|
edges = ([('s', 'u', 10), ('s', 'x', 5), ('u', 'v', 1),
|
|
('u', 'x', 2), ('v', 'y', 1), ('x', 'u', 3),
|
|
('x', 'v', 5), ('x', 'y', 2), ('y', 's', 7), ('y', 'v', 6)])
|
|
XG = nx.Graph()
|
|
XG.add_weighted_edges_from(edges)
|
|
c = nx.closeness_centrality(XG, distance='weight')
|
|
d = {'y': 0.200,
|
|
'x': 0.286,
|
|
's': 0.138,
|
|
'u': 0.235,
|
|
'v': 0.200}
|
|
for n in sorted(XG):
|
|
assert almost_equal(c[n], d[n], places=3)
|
|
|
|
|
|
#
|
|
# Tests for incremental closeness centrality.
|
|
#
|
|
@staticmethod
|
|
def pick_add_edge(g):
|
|
u = nx.utils.arbitrary_element(g)
|
|
possible_nodes = set(g.nodes())
|
|
neighbors = list(g.neighbors(u)) + [u]
|
|
possible_nodes.difference_update(neighbors)
|
|
v = nx.utils.arbitrary_element(possible_nodes)
|
|
return (u, v)
|
|
|
|
@staticmethod
|
|
def pick_remove_edge(g):
|
|
u = nx.utils.arbitrary_element(g)
|
|
possible_nodes = list(g.neighbors(u))
|
|
v = nx.utils.arbitrary_element(possible_nodes)
|
|
return (u, v)
|
|
|
|
def test_directed_raises(self):
|
|
with pytest.raises(nx.NetworkXNotImplemented):
|
|
dir_G = nx.gn_graph(n=5)
|
|
prev_cc = None
|
|
edge = self.pick_add_edge(dir_G)
|
|
insert = True
|
|
nx.incremental_closeness_centrality(dir_G, edge, prev_cc, insert)
|
|
|
|
def test_wrong_size_prev_cc_raises(self):
|
|
with pytest.raises(nx.NetworkXError):
|
|
G = self.undirected_G.copy()
|
|
edge = self.pick_add_edge(G)
|
|
insert = True
|
|
prev_cc = self.undirected_G_cc.copy()
|
|
prev_cc.pop(0)
|
|
nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
|
|
|
|
def test_wrong_nodes_prev_cc_raises(self):
|
|
with pytest.raises(nx.NetworkXError):
|
|
G = self.undirected_G.copy()
|
|
edge = self.pick_add_edge(G)
|
|
insert = True
|
|
prev_cc = self.undirected_G_cc.copy()
|
|
num_nodes = len(prev_cc)
|
|
prev_cc.pop(0)
|
|
prev_cc[num_nodes] = 0.5
|
|
nx.incremental_closeness_centrality(G, edge, prev_cc, insert)
|
|
|
|
def test_zero_centrality(self):
|
|
G = nx.path_graph(3)
|
|
prev_cc = nx.closeness_centrality(G)
|
|
edge = self.pick_remove_edge(G)
|
|
test_cc = nx.incremental_closeness_centrality(
|
|
G, edge, prev_cc, insertion=False)
|
|
G.remove_edges_from([edge])
|
|
real_cc = nx.closeness_centrality(G)
|
|
shared_items = set(test_cc.items()) & set(real_cc.items())
|
|
assert len(shared_items) == len(real_cc)
|
|
assert 0 in test_cc.values()
|
|
|
|
def test_incremental(self):
|
|
# Check that incremental and regular give same output
|
|
G = self.undirected_G.copy()
|
|
prev_cc = None
|
|
for i in range(5):
|
|
if i % 2 == 0:
|
|
# Remove an edge
|
|
insert = False
|
|
edge = self.pick_remove_edge(G)
|
|
else:
|
|
# Add an edge
|
|
insert = True
|
|
edge = self.pick_add_edge(G)
|
|
|
|
# start = timeit.default_timer()
|
|
test_cc = nx.incremental_closeness_centrality(
|
|
G, edge, prev_cc, insert)
|
|
# inc_elapsed = (timeit.default_timer() - start)
|
|
# print("incremental time: {}".format(inc_elapsed))
|
|
|
|
if insert:
|
|
G.add_edges_from([edge])
|
|
else:
|
|
G.remove_edges_from([edge])
|
|
|
|
# start = timeit.default_timer()
|
|
real_cc = nx.closeness_centrality(G)
|
|
# reg_elapsed = (timeit.default_timer() - start)
|
|
# print("regular time: {}".format(reg_elapsed))
|
|
# Example output:
|
|
# incremental time: 0.208
|
|
# regular time: 0.276
|
|
# incremental time: 0.00683
|
|
# regular time: 0.260
|
|
# incremental time: 0.0224
|
|
# regular time: 0.278
|
|
# incremental time: 0.00804
|
|
# regular time: 0.208
|
|
# incremental time: 0.00947
|
|
# regular time: 0.188
|
|
|
|
assert set(test_cc.items()) == set(real_cc.items())
|
|
|
|
prev_cc = test_cc
|