123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- """Tests for the :mod:`networkx.algorithms.triads` module."""
- import itertools
- from collections import defaultdict
- from random import sample
- import pytest
- import networkx as nx
- def test_triadic_census():
- """Tests the triadic_census function."""
- G = nx.DiGraph()
- G.add_edges_from(["01", "02", "03", "04", "05", "12", "16", "51", "56", "65"])
- expected = {
- "030T": 2,
- "120C": 1,
- "210": 0,
- "120U": 0,
- "012": 9,
- "102": 3,
- "021U": 0,
- "111U": 0,
- "003": 8,
- "030C": 0,
- "021D": 9,
- "201": 0,
- "111D": 1,
- "300": 0,
- "120D": 0,
- "021C": 2,
- }
- actual = nx.triadic_census(G)
- assert expected == actual
- def test_is_triad():
- """Tests the is_triad function"""
- G = nx.karate_club_graph()
- G = G.to_directed()
- for i in range(100):
- nodes = sample(sorted(G.nodes()), 3)
- G2 = G.subgraph(nodes)
- assert nx.is_triad(G2)
- def test_all_triplets():
- """Tests the all_triplets function."""
- G = nx.DiGraph()
- G.add_edges_from(["01", "02", "03", "04", "05", "12", "16", "51", "56", "65"])
- expected = [
- f"{i},{j},{k}"
- for i in range(7)
- for j in range(i + 1, 7)
- for k in range(j + 1, 7)
- ]
- expected = [set(x.split(",")) for x in expected]
- actual = [set(x) for x in nx.all_triplets(G)]
- assert all(any(s1 == s2 for s1 in expected) for s2 in actual)
- def test_all_triads():
- """Tests the all_triplets function."""
- G = nx.DiGraph()
- G.add_edges_from(["01", "02", "03", "04", "05", "12", "16", "51", "56", "65"])
- expected = [
- f"{i},{j},{k}"
- for i in range(7)
- for j in range(i + 1, 7)
- for k in range(j + 1, 7)
- ]
- expected = [G.subgraph(x.split(",")) for x in expected]
- actual = list(nx.all_triads(G))
- assert all(any(nx.is_isomorphic(G1, G2) for G1 in expected) for G2 in actual)
- def test_triad_type():
- """Tests the triad_type function."""
-
- G = nx.DiGraph({0: [], 1: [], 2: []})
- assert nx.triad_type(G) == "003"
-
- G = nx.DiGraph({0: [1], 1: [], 2: []})
- assert nx.triad_type(G) == "012"
-
- G = nx.DiGraph([(0, 1), (0, 2)])
- assert nx.triad_type(G) == "021D"
- G = nx.DiGraph({0: [1], 1: [0], 2: []})
- assert nx.triad_type(G) == "102"
- G = nx.DiGraph([(0, 1), (2, 1)])
- assert nx.triad_type(G) == "021U"
- G = nx.DiGraph([(0, 1), (1, 2)])
- assert nx.triad_type(G) == "021C"
-
- G = nx.DiGraph([(0, 1), (1, 0), (2, 1)])
- assert nx.triad_type(G) == "111D"
- G = nx.DiGraph([(0, 1), (1, 0), (1, 2)])
- assert nx.triad_type(G) == "111U"
- G = nx.DiGraph([(0, 1), (1, 2), (0, 2)])
- assert nx.triad_type(G) == "030T"
- G = nx.DiGraph([(0, 1), (1, 2), (2, 0)])
- assert nx.triad_type(G) == "030C"
-
- G = nx.DiGraph([(0, 1), (1, 0), (2, 0), (0, 2)])
- assert nx.triad_type(G) == "201"
- G = nx.DiGraph([(0, 1), (1, 0), (2, 0), (2, 1)])
- assert nx.triad_type(G) == "120D"
- G = nx.DiGraph([(0, 1), (1, 0), (0, 2), (1, 2)])
- assert nx.triad_type(G) == "120U"
- G = nx.DiGraph([(0, 1), (1, 0), (0, 2), (2, 1)])
- assert nx.triad_type(G) == "120C"
-
- G = nx.DiGraph([(0, 1), (1, 0), (2, 1), (1, 2), (0, 2)])
- assert nx.triad_type(G) == "210"
-
- G = nx.DiGraph([(0, 1), (1, 0), (1, 2), (2, 1), (0, 2), (2, 0)])
- assert nx.triad_type(G) == "300"
- def test_triads_by_type():
- """Tests the all_triplets function."""
- G = nx.DiGraph()
- G.add_edges_from(["01", "02", "03", "04", "05", "12", "16", "51", "56", "65"])
- all_triads = nx.all_triads(G)
- expected = defaultdict(list)
- for triad in all_triads:
- name = nx.triad_type(triad)
- expected[name].append(triad)
- actual = nx.triads_by_type(G)
- assert set(actual.keys()) == set(expected.keys())
- for tri_type, actual_Gs in actual.items():
- expected_Gs = expected[tri_type]
- for a in actual_Gs:
- assert any(nx.is_isomorphic(a, e) for e in expected_Gs)
- def test_random_triad():
- """Tests the random_triad function"""
- G = nx.karate_club_graph()
- G = G.to_directed()
- for i in range(100):
- assert nx.is_triad(nx.random_triad(G))
- def test_triadic_census_short_path_nodelist():
- G = nx.path_graph("abc", create_using=nx.DiGraph)
- expected = {"021C": 1}
- for nl in ["a", "b", "c", "ab", "ac", "bc", "abc"]:
- triad_census = nx.triadic_census(G, nodelist=nl)
- assert expected == {typ: cnt for typ, cnt in triad_census.items() if cnt > 0}
- def test_triadic_census_correct_nodelist_values():
- G = nx.path_graph(5, create_using=nx.DiGraph)
- msg = r"nodelist includes duplicate nodes or nodes not in G"
- with pytest.raises(ValueError, match=msg):
- nx.triadic_census(G, [1, 2, 2, 3])
- with pytest.raises(ValueError, match=msg):
- nx.triadic_census(G, [1, 2, "a", 3])
- def test_triadic_census_tiny_graphs():
- tc = nx.triadic_census(nx.empty_graph(0, create_using=nx.DiGraph))
- assert {} == {typ: cnt for typ, cnt in tc.items() if cnt > 0}
- tc = nx.triadic_census(nx.empty_graph(1, create_using=nx.DiGraph))
- assert {} == {typ: cnt for typ, cnt in tc.items() if cnt > 0}
- tc = nx.triadic_census(nx.empty_graph(2, create_using=nx.DiGraph))
- assert {} == {typ: cnt for typ, cnt in tc.items() if cnt > 0}
- tc = nx.triadic_census(nx.DiGraph([(1, 2)]))
- assert {} == {typ: cnt for typ, cnt in tc.items() if cnt > 0}
- def test_triadic_census_selfloops():
- GG = nx.path_graph("abc", create_using=nx.DiGraph)
- expected = {"021C": 1}
- for n in GG:
- G = GG.copy()
- G.add_edge(n, n)
- tc = nx.triadic_census(G)
- assert expected == {typ: cnt for typ, cnt in tc.items() if cnt > 0}
- GG = nx.path_graph("abcde", create_using=nx.DiGraph)
- tbt = nx.triads_by_type(GG)
- for n in GG:
- GG.add_edge(n, n)
- tc = nx.triadic_census(GG)
- assert tc == {tt: len(tbt[tt]) for tt in tc}
- def test_triadic_census_four_path():
- G = nx.path_graph("abcd", create_using=nx.DiGraph)
- expected = {"012": 2, "021C": 2}
- triad_census = nx.triadic_census(G)
- assert expected == {typ: cnt for typ, cnt in triad_census.items() if cnt > 0}
- def test_triadic_census_four_path_nodelist():
- G = nx.path_graph("abcd", create_using=nx.DiGraph)
- expected_end = {"012": 2, "021C": 1}
- expected_mid = {"012": 1, "021C": 2}
- a_triad_census = nx.triadic_census(G, nodelist=["a"])
- assert expected_end == {typ: cnt for typ, cnt in a_triad_census.items() if cnt > 0}
- b_triad_census = nx.triadic_census(G, nodelist=["b"])
- assert expected_mid == {typ: cnt for typ, cnt in b_triad_census.items() if cnt > 0}
- c_triad_census = nx.triadic_census(G, nodelist=["c"])
- assert expected_mid == {typ: cnt for typ, cnt in c_triad_census.items() if cnt > 0}
- d_triad_census = nx.triadic_census(G, nodelist=["d"])
- assert expected_end == {typ: cnt for typ, cnt in d_triad_census.items() if cnt > 0}
- def test_triadic_census_nodelist():
- """Tests the triadic_census function."""
- G = nx.DiGraph()
- G.add_edges_from(["01", "02", "03", "04", "05", "12", "16", "51", "56", "65"])
- expected = {
- "030T": 2,
- "120C": 1,
- "210": 0,
- "120U": 0,
- "012": 9,
- "102": 3,
- "021U": 0,
- "111U": 0,
- "003": 8,
- "030C": 0,
- "021D": 9,
- "201": 0,
- "111D": 1,
- "300": 0,
- "120D": 0,
- "021C": 2,
- }
- actual = {k: 0 for k in expected}
- for node in G.nodes():
- node_triad_census = nx.triadic_census(G, nodelist=[node])
- for triad_key in expected:
- actual[triad_key] += node_triad_census[triad_key]
-
- for k, v in actual.items():
- actual[k] //= 3
- assert expected == actual
- @pytest.mark.parametrize("N", [5, 10])
- def test_triandic_census_on_random_graph(N):
- G = nx.binomial_graph(N, 0.3, directed=True, seed=42)
- tc1 = nx.triadic_census(G)
- tbt = nx.triads_by_type(G)
- tc2 = {tt: len(tbt[tt]) for tt in tc1}
- assert tc1 == tc2
- for n in G:
- tc1 = nx.triadic_census(G, nodelist={n})
- tc2 = {tt: sum(1 for t in tbt.get(tt, []) if n in t) for tt in tc1}
- assert tc1 == tc2
- for ns in itertools.combinations(G, 2):
- ns = set(ns)
- tc1 = nx.triadic_census(G, nodelist=ns)
- tc2 = {
- tt: sum(1 for t in tbt.get(tt, []) if any(n in ns for n in t)) for tt in tc1
- }
- assert tc1 == tc2
- for ns in itertools.combinations(G, 3):
- ns = set(ns)
- tc1 = nx.triadic_census(G, nodelist=ns)
- tc2 = {
- tt: sum(1 for t in tbt.get(tt, []) if any(n in ns for n in t)) for tt in tc1
- }
- assert tc1 == tc2
|