153 lines
4.8 KiB
Python
153 lines
4.8 KiB
Python
|
from itertools import chain
|
||
|
from itertools import combinations
|
||
|
|
||
|
import pytest
|
||
|
|
||
|
import networkx as nx
|
||
|
from networkx.algorithms.community import label_propagation_communities
|
||
|
from networkx.algorithms.community import asyn_lpa_communities
|
||
|
|
||
|
|
||
|
def test_directed_not_supported():
|
||
|
with pytest.raises(nx.NetworkXNotImplemented):
|
||
|
# not supported for directed graphs
|
||
|
test = nx.DiGraph()
|
||
|
test.add_edge('a', 'b')
|
||
|
test.add_edge('a', 'c')
|
||
|
test.add_edge('b', 'd')
|
||
|
result = label_propagation_communities(test)
|
||
|
|
||
|
|
||
|
def test_one_node():
|
||
|
test = nx.Graph()
|
||
|
test.add_node('a')
|
||
|
|
||
|
# The expected communities are:
|
||
|
ground_truth = set([frozenset(['a'])])
|
||
|
|
||
|
communities = label_propagation_communities(test)
|
||
|
result = {frozenset(c) for c in communities}
|
||
|
assert result == ground_truth
|
||
|
|
||
|
|
||
|
def test_unconnected_communities():
|
||
|
test = nx.Graph()
|
||
|
# community 1
|
||
|
test.add_edge('a', 'c')
|
||
|
test.add_edge('a', 'd')
|
||
|
test.add_edge('d', 'c')
|
||
|
# community 2
|
||
|
test.add_edge('b', 'e')
|
||
|
test.add_edge('e', 'f')
|
||
|
test.add_edge('f', 'b')
|
||
|
|
||
|
# The expected communities are:
|
||
|
ground_truth = set([frozenset(['a', 'c', 'd']),
|
||
|
frozenset(['b', 'e', 'f'])])
|
||
|
|
||
|
communities = label_propagation_communities(test)
|
||
|
result = {frozenset(c) for c in communities}
|
||
|
assert result == ground_truth
|
||
|
|
||
|
|
||
|
def test_connected_communities():
|
||
|
test = nx.Graph()
|
||
|
# community 1
|
||
|
test.add_edge('a', 'b')
|
||
|
test.add_edge('c', 'a')
|
||
|
test.add_edge('c', 'b')
|
||
|
test.add_edge('d', 'a')
|
||
|
test.add_edge('d', 'b')
|
||
|
test.add_edge('d', 'c')
|
||
|
test.add_edge('e', 'a')
|
||
|
test.add_edge('e', 'b')
|
||
|
test.add_edge('e', 'c')
|
||
|
test.add_edge('e', 'd')
|
||
|
# community 2
|
||
|
test.add_edge('1', '2')
|
||
|
test.add_edge('3', '1')
|
||
|
test.add_edge('3', '2')
|
||
|
test.add_edge('4', '1')
|
||
|
test.add_edge('4', '2')
|
||
|
test.add_edge('4', '3')
|
||
|
test.add_edge('5', '1')
|
||
|
test.add_edge('5', '2')
|
||
|
test.add_edge('5', '3')
|
||
|
test.add_edge('5', '4')
|
||
|
# edge between community 1 and 2
|
||
|
test.add_edge('a', '1')
|
||
|
# community 3
|
||
|
test.add_edge('x', 'y')
|
||
|
# community 4 with only a single node
|
||
|
test.add_node('z')
|
||
|
|
||
|
# The expected communities are:
|
||
|
ground_truth1 = set([frozenset(['a', 'b', 'c', 'd', 'e']),
|
||
|
frozenset(['1', '2', '3', '4', '5']),
|
||
|
frozenset(['x', 'y']),
|
||
|
frozenset(['z'])])
|
||
|
ground_truth2 = set([frozenset(['a', 'b', 'c', 'd', 'e',
|
||
|
'1', '2', '3', '4', '5']),
|
||
|
frozenset(['x', 'y']),
|
||
|
frozenset(['z'])])
|
||
|
ground_truth = (ground_truth1, ground_truth2)
|
||
|
|
||
|
communities = label_propagation_communities(test)
|
||
|
result = {frozenset(c) for c in communities}
|
||
|
assert result in ground_truth
|
||
|
|
||
|
|
||
|
def test_termination():
|
||
|
# ensure termination of asyn_lpa_communities in two cases
|
||
|
# that led to an endless loop in a previous version
|
||
|
test1 = nx.karate_club_graph()
|
||
|
test2 = nx.caveman_graph(2, 10)
|
||
|
test2.add_edges_from([(0, 20), (20, 10)])
|
||
|
asyn_lpa_communities(test1)
|
||
|
asyn_lpa_communities(test2)
|
||
|
|
||
|
|
||
|
class TestAsynLpaCommunities(object):
|
||
|
def _check_communities(self, G, expected):
|
||
|
"""Checks that the communities computed from the given graph ``G``
|
||
|
using the :func:`~networkx.asyn_lpa_communities` function match
|
||
|
the set of nodes given in ``expected``.
|
||
|
|
||
|
``expected`` must be a :class:`set` of :class:`frozenset`
|
||
|
instances, each element of which is a node in the graph.
|
||
|
|
||
|
"""
|
||
|
communities = asyn_lpa_communities(G)
|
||
|
result = {frozenset(c) for c in communities}
|
||
|
assert result == expected
|
||
|
|
||
|
def test_null_graph(self):
|
||
|
G = nx.null_graph()
|
||
|
ground_truth = set()
|
||
|
self._check_communities(G, ground_truth)
|
||
|
|
||
|
def test_single_node(self):
|
||
|
G = nx.empty_graph(1)
|
||
|
ground_truth = {frozenset([0])}
|
||
|
self._check_communities(G, ground_truth)
|
||
|
|
||
|
def test_simple_communities(self):
|
||
|
# This graph is the disjoint union of two triangles.
|
||
|
G = nx.Graph(['ab', 'ac', 'bc', 'de', 'df', 'fe'])
|
||
|
ground_truth = {frozenset('abc'), frozenset('def')}
|
||
|
self._check_communities(G, ground_truth)
|
||
|
|
||
|
def test_seed_argument(self):
|
||
|
G = nx.Graph(['ab', 'ac', 'bc', 'de', 'df', 'fe'])
|
||
|
ground_truth = {frozenset('abc'), frozenset('def')}
|
||
|
communities = asyn_lpa_communities(G, seed=1)
|
||
|
result = {frozenset(c) for c in communities}
|
||
|
assert result == ground_truth
|
||
|
|
||
|
def test_several_communities(self):
|
||
|
# This graph is the disjoint union of five triangles.
|
||
|
ground_truth = {frozenset(range(3 * i, 3 * (i + 1))) for i in range(5)}
|
||
|
edges = chain.from_iterable(combinations(c, 2) for c in ground_truth)
|
||
|
G = nx.Graph(edges)
|
||
|
self._check_communities(G, ground_truth)
|