import pytest import networkx as nx from networkx.algorithms.community import ( greedy_modularity_communities, naive_greedy_modularity_communities, ) @pytest.mark.parametrize( "func", (greedy_modularity_communities, naive_greedy_modularity_communities) ) def test_modularity_communities(func): G = nx.karate_club_graph() john_a = frozenset( [8, 14, 15, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33] ) mr_hi = frozenset([0, 4, 5, 6, 10, 11, 16, 19]) overlap = frozenset([1, 2, 3, 7, 9, 12, 13, 17, 21]) expected = {john_a, overlap, mr_hi} assert set(func(G, weight=None)) == expected @pytest.mark.parametrize( "func", (greedy_modularity_communities, naive_greedy_modularity_communities) ) def test_modularity_communities_categorical_labels(func): # Using other than 0-starting contiguous integers as node-labels. G = nx.Graph( [ ("a", "b"), ("a", "c"), ("b", "c"), ("b", "d"), # inter-community edge ("d", "e"), ("d", "f"), ("d", "g"), ("f", "g"), ("d", "e"), ("f", "e"), ] ) expected = {frozenset({"f", "g", "e", "d"}), frozenset({"a", "b", "c"})} assert set(func(G)) == expected def test_greedy_modularity_communities_relabeled(): # Test for gh-4966 G = nx.balanced_tree(2, 2) mapping = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 7: "h"} G = nx.relabel_nodes(G, mapping) expected = [frozenset({"e", "d", "a", "b"}), frozenset({"c", "f", "g"})] assert greedy_modularity_communities(G) == expected def test_greedy_modularity_communities_directed(): G = nx.DiGraph( [ ("a", "b"), ("a", "c"), ("b", "c"), ("b", "d"), # inter-community edge ("d", "e"), ("d", "f"), ("d", "g"), ("f", "g"), ("d", "e"), ("f", "e"), ] ) expected = [frozenset({"f", "g", "e", "d"}), frozenset({"a", "b", "c"})] assert greedy_modularity_communities(G) == expected # with loops G = nx.DiGraph() G.add_edges_from( [(1, 1), (1, 2), (1, 3), (2, 3), (1, 4), (4, 4), (5, 5), (4, 5), (4, 6), (5, 6)] ) expected = [frozenset({1, 2, 3}), frozenset({4, 5, 6})] assert greedy_modularity_communities(G) == expected @pytest.mark.parametrize( "func", (greedy_modularity_communities, naive_greedy_modularity_communities) ) def test_modularity_communities_weighted(func): G = nx.balanced_tree(2, 3) for (a, b) in G.edges: if ((a == 1) or (a == 2)) and (b != 0): G[a][b]["weight"] = 10.0 else: G[a][b]["weight"] = 1.0 expected = [{0, 1, 3, 4, 7, 8, 9, 10}, {2, 5, 6, 11, 12, 13, 14}] assert func(G, weight="weight") == expected assert func(G, weight="weight", resolution=0.9) == expected assert func(G, weight="weight", resolution=0.3) == expected assert func(G, weight="weight", resolution=1.1) != expected def test_modularity_communities_floating_point(): # check for floating point error when used as key in the mapped_queue dict. # Test for gh-4992 and gh-5000 G = nx.Graph() G.add_weighted_edges_from( [(0, 1, 12), (1, 4, 71), (2, 3, 15), (2, 4, 10), (3, 6, 13)] ) expected = [{0, 1, 4}, {2, 3, 6}] assert greedy_modularity_communities(G, weight="weight") == expected assert ( greedy_modularity_communities(G, weight="weight", resolution=0.99) == expected ) def test_modularity_communities_directed_weighted(): G = nx.DiGraph() G.add_weighted_edges_from( [ (1, 2, 5), (1, 3, 3), (2, 3, 6), (2, 6, 1), (1, 4, 1), (4, 5, 3), (4, 6, 7), (5, 6, 2), (5, 7, 5), (5, 8, 4), (6, 8, 3), ] ) expected = [frozenset({4, 5, 6, 7, 8}), frozenset({1, 2, 3})] assert greedy_modularity_communities(G, weight="weight") == expected # A large weight of the edge (2, 6) causes 6 to change group, even if it shares # only one connection with the new group and 3 with the old one. G[2][6]["weight"] = 20 expected = [frozenset({1, 2, 3, 6}), frozenset({4, 5, 7, 8})] assert greedy_modularity_communities(G, weight="weight") == expected def test_greedy_modularity_communities_multigraph(): G = nx.MultiGraph() G.add_edges_from( [ (1, 2), (1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (4, 5), (5, 6), (5, 7), (5, 7), (6, 7), (7, 8), (5, 8), ] ) expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})] assert greedy_modularity_communities(G) == expected # Converting (4, 5) into a multi-edge causes node 4 to change group. G.add_edge(4, 5) expected = [frozenset({4, 5, 6, 7, 8}), frozenset({1, 2, 3})] assert greedy_modularity_communities(G) == expected def test_greedy_modularity_communities_multigraph_weighted(): G = nx.MultiGraph() G.add_weighted_edges_from( [ (1, 2, 5), (1, 2, 3), (1, 3, 6), (1, 3, 6), (2, 3, 4), (1, 4, 1), (1, 4, 1), (2, 4, 3), (2, 4, 3), (4, 5, 1), (5, 6, 3), (5, 6, 7), (5, 6, 4), (5, 7, 9), (5, 7, 9), (6, 7, 8), (7, 8, 2), (7, 8, 2), (5, 8, 6), (5, 8, 6), ] ) expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})] assert greedy_modularity_communities(G, weight="weight") == expected # Adding multi-edge (4, 5, 16) causes node 4 to change group. G.add_edge(4, 5, weight=16) expected = [frozenset({4, 5, 6, 7, 8}), frozenset({1, 2, 3})] assert greedy_modularity_communities(G, weight="weight") == expected # Increasing the weight of edge (1, 4) causes node 4 to return to the former group. G[1][4][1]["weight"] = 3 expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})] assert greedy_modularity_communities(G, weight="weight") == expected def test_greed_modularity_communities_multidigraph(): G = nx.MultiDiGraph() G.add_edges_from( [ (1, 2), (1, 2), (3, 1), (2, 3), (2, 3), (3, 2), (1, 4), (2, 4), (4, 2), (4, 5), (5, 6), (5, 6), (6, 5), (5, 7), (6, 7), (7, 8), (5, 8), (8, 4), ] ) expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})] assert greedy_modularity_communities(G, weight="weight") == expected def test_greed_modularity_communities_multidigraph_weighted(): G = nx.MultiDiGraph() G.add_weighted_edges_from( [ (1, 2, 5), (1, 2, 3), (3, 1, 6), (1, 3, 6), (3, 2, 4), (1, 4, 2), (1, 4, 5), (2, 4, 3), (3, 2, 8), (4, 2, 3), (4, 3, 5), (4, 5, 2), (5, 6, 3), (5, 6, 7), (6, 5, 4), (5, 7, 9), (5, 7, 9), (7, 6, 8), (7, 8, 2), (8, 7, 2), (5, 8, 6), (5, 8, 6), ] ) expected = [frozenset({1, 2, 3, 4}), frozenset({5, 6, 7, 8})] assert greedy_modularity_communities(G, weight="weight") == expected def test_resolution_parameter_impact(): G = nx.barbell_graph(5, 3) gamma = 1 expected = [frozenset(range(5)), frozenset(range(8, 13)), frozenset(range(5, 8))] assert greedy_modularity_communities(G, resolution=gamma) == expected assert naive_greedy_modularity_communities(G, resolution=gamma) == expected gamma = 2.5 expected = [{0, 1, 2, 3}, {9, 10, 11, 12}, {5, 6, 7}, {4}, {8}] assert greedy_modularity_communities(G, resolution=gamma) == expected assert naive_greedy_modularity_communities(G, resolution=gamma) == expected gamma = 0.3 expected = [frozenset(range(8)), frozenset(range(8, 13))] assert greedy_modularity_communities(G, resolution=gamma) == expected assert naive_greedy_modularity_communities(G, resolution=gamma) == expected def test_n_communities_parameter(): G = nx.circular_ladder_graph(4) # No aggregation: expected = [{k} for k in range(8)] assert greedy_modularity_communities(G, n_communities=8) == expected # Aggregation to half order (number of nodes) expected = [{k, k + 1} for k in range(0, 8, 2)] assert greedy_modularity_communities(G, n_communities=4) == expected # Default aggregation case (here, 2 communities emerge) expected = [frozenset(range(0, 4)), frozenset(range(4, 8))] assert greedy_modularity_communities(G, n_communities=1) == expected