123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657 |
- import pytest
- import networkx as nx
- from networkx.generators import directed
- # Unit tests for the :func:`~networkx.weisfeiler_lehman_graph_hash` function
- def test_empty_graph_hash():
- """
- empty graphs should give hashes regardless of other params
- """
- G1 = nx.empty_graph()
- G2 = nx.empty_graph()
- h1 = nx.weisfeiler_lehman_graph_hash(G1)
- h2 = nx.weisfeiler_lehman_graph_hash(G2)
- h3 = nx.weisfeiler_lehman_graph_hash(G2, edge_attr="edge_attr1")
- h4 = nx.weisfeiler_lehman_graph_hash(G2, node_attr="node_attr1")
- h5 = nx.weisfeiler_lehman_graph_hash(
- G2, edge_attr="edge_attr1", node_attr="node_attr1"
- )
- h6 = nx.weisfeiler_lehman_graph_hash(G2, iterations=10)
- assert h1 == h2
- assert h1 == h3
- assert h1 == h4
- assert h1 == h5
- assert h1 == h6
- def test_directed():
- """
- A directed graph with no bi-directional edges should yield different a graph hash
- to the same graph taken as undirected if there are no hash collisions.
- """
- r = 10
- for i in range(r):
- G_directed = nx.gn_graph(10 + r, seed=100 + i)
- G_undirected = nx.to_undirected(G_directed)
- h_directed = nx.weisfeiler_lehman_graph_hash(G_directed)
- h_undirected = nx.weisfeiler_lehman_graph_hash(G_undirected)
- assert h_directed != h_undirected
- def test_reversed():
- """
- A directed graph with no bi-directional edges should yield different a graph hash
- to the same graph taken with edge directions reversed if there are no hash collisions.
- Here we test a cycle graph which is the minimal counterexample
- """
- G = nx.cycle_graph(5, create_using=nx.DiGraph)
- nx.set_node_attributes(G, {n: str(n) for n in G.nodes()}, name="label")
- G_reversed = G.reverse()
- h = nx.weisfeiler_lehman_graph_hash(G, node_attr="label")
- h_reversed = nx.weisfeiler_lehman_graph_hash(G_reversed, node_attr="label")
- assert h != h_reversed
- def test_isomorphic():
- """
- graph hashes should be invariant to node-relabeling (when the output is reindexed
- by the same mapping)
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=200 + i)
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g1_hash = nx.weisfeiler_lehman_graph_hash(G1)
- g2_hash = nx.weisfeiler_lehman_graph_hash(G2)
- assert g1_hash == g2_hash
- def test_isomorphic_edge_attr():
- """
- Isomorphic graphs with differing edge attributes should yield different graph
- hashes if the 'edge_attr' argument is supplied and populated in the graph,
- and there are no hash collisions.
- The output should still be invariant to node-relabeling
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=300 + i)
- for a, b in G1.edges:
- G1[a][b]["edge_attr1"] = f"{a}-{b}-1"
- G1[a][b]["edge_attr2"] = f"{a}-{b}-2"
- g1_hash_with_edge_attr1 = nx.weisfeiler_lehman_graph_hash(
- G1, edge_attr="edge_attr1"
- )
- g1_hash_with_edge_attr2 = nx.weisfeiler_lehman_graph_hash(
- G1, edge_attr="edge_attr2"
- )
- g1_hash_no_edge_attr = nx.weisfeiler_lehman_graph_hash(G1, edge_attr=None)
- assert g1_hash_with_edge_attr1 != g1_hash_no_edge_attr
- assert g1_hash_with_edge_attr2 != g1_hash_no_edge_attr
- assert g1_hash_with_edge_attr1 != g1_hash_with_edge_attr2
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g2_hash_with_edge_attr1 = nx.weisfeiler_lehman_graph_hash(
- G2, edge_attr="edge_attr1"
- )
- g2_hash_with_edge_attr2 = nx.weisfeiler_lehman_graph_hash(
- G2, edge_attr="edge_attr2"
- )
- assert g1_hash_with_edge_attr1 == g2_hash_with_edge_attr1
- assert g1_hash_with_edge_attr2 == g2_hash_with_edge_attr2
- def test_missing_edge_attr():
- """
- If the 'edge_attr' argument is supplied but is missing from an edge in the graph,
- we should raise a KeyError
- """
- G = nx.Graph()
- G.add_edges_from([(1, 2, {"edge_attr1": "a"}), (1, 3, {})])
- pytest.raises(KeyError, nx.weisfeiler_lehman_graph_hash, G, edge_attr="edge_attr1")
- def test_isomorphic_node_attr():
- """
- Isomorphic graphs with differing node attributes should yield different graph
- hashes if the 'node_attr' argument is supplied and populated in the graph, and
- there are no hash collisions.
- The output should still be invariant to node-relabeling
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=400 + i)
- for u in G1.nodes():
- G1.nodes[u]["node_attr1"] = f"{u}-1"
- G1.nodes[u]["node_attr2"] = f"{u}-2"
- g1_hash_with_node_attr1 = nx.weisfeiler_lehman_graph_hash(
- G1, node_attr="node_attr1"
- )
- g1_hash_with_node_attr2 = nx.weisfeiler_lehman_graph_hash(
- G1, node_attr="node_attr2"
- )
- g1_hash_no_node_attr = nx.weisfeiler_lehman_graph_hash(G1, node_attr=None)
- assert g1_hash_with_node_attr1 != g1_hash_no_node_attr
- assert g1_hash_with_node_attr2 != g1_hash_no_node_attr
- assert g1_hash_with_node_attr1 != g1_hash_with_node_attr2
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g2_hash_with_node_attr1 = nx.weisfeiler_lehman_graph_hash(
- G2, node_attr="node_attr1"
- )
- g2_hash_with_node_attr2 = nx.weisfeiler_lehman_graph_hash(
- G2, node_attr="node_attr2"
- )
- assert g1_hash_with_node_attr1 == g2_hash_with_node_attr1
- assert g1_hash_with_node_attr2 == g2_hash_with_node_attr2
- def test_missing_node_attr():
- """
- If the 'node_attr' argument is supplied but is missing from a node in the graph,
- we should raise a KeyError
- """
- G = nx.Graph()
- G.add_nodes_from([(1, {"node_attr1": "a"}), (2, {})])
- G.add_edges_from([(1, 2), (2, 3), (3, 1), (1, 4)])
- pytest.raises(KeyError, nx.weisfeiler_lehman_graph_hash, G, node_attr="node_attr1")
- def test_isomorphic_edge_attr_and_node_attr():
- """
- Isomorphic graphs with differing node attributes should yield different graph
- hashes if the 'node_attr' and 'edge_attr' argument is supplied and populated in
- the graph, and there are no hash collisions.
- The output should still be invariant to node-relabeling
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=500 + i)
- for u in G1.nodes():
- G1.nodes[u]["node_attr1"] = f"{u}-1"
- G1.nodes[u]["node_attr2"] = f"{u}-2"
- for a, b in G1.edges:
- G1[a][b]["edge_attr1"] = f"{a}-{b}-1"
- G1[a][b]["edge_attr2"] = f"{a}-{b}-2"
- g1_hash_edge1_node1 = nx.weisfeiler_lehman_graph_hash(
- G1, edge_attr="edge_attr1", node_attr="node_attr1"
- )
- g1_hash_edge2_node2 = nx.weisfeiler_lehman_graph_hash(
- G1, edge_attr="edge_attr2", node_attr="node_attr2"
- )
- g1_hash_edge1_node2 = nx.weisfeiler_lehman_graph_hash(
- G1, edge_attr="edge_attr1", node_attr="node_attr2"
- )
- g1_hash_no_attr = nx.weisfeiler_lehman_graph_hash(G1)
- assert g1_hash_edge1_node1 != g1_hash_no_attr
- assert g1_hash_edge2_node2 != g1_hash_no_attr
- assert g1_hash_edge1_node1 != g1_hash_edge2_node2
- assert g1_hash_edge1_node2 != g1_hash_edge2_node2
- assert g1_hash_edge1_node2 != g1_hash_edge1_node1
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g2_hash_edge1_node1 = nx.weisfeiler_lehman_graph_hash(
- G2, edge_attr="edge_attr1", node_attr="node_attr1"
- )
- g2_hash_edge2_node2 = nx.weisfeiler_lehman_graph_hash(
- G2, edge_attr="edge_attr2", node_attr="node_attr2"
- )
- assert g1_hash_edge1_node1 == g2_hash_edge1_node1
- assert g1_hash_edge2_node2 == g2_hash_edge2_node2
- def test_digest_size():
- """
- The hash string lengths should be as expected for a variety of graphs and
- digest sizes
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G = nx.erdos_renyi_graph(n, p * i, seed=1000 + i)
- h16 = nx.weisfeiler_lehman_graph_hash(G)
- h32 = nx.weisfeiler_lehman_graph_hash(G, digest_size=32)
- assert h16 != h32
- assert len(h16) == 16 * 2
- assert len(h32) == 32 * 2
- # Unit tests for the :func:`~networkx.weisfeiler_lehman_hash_subgraphs` function
- def is_subiteration(a, b):
- """
- returns True if that each hash sequence in 'a' is a prefix for
- the corresponding sequence indexed by the same node in 'b'.
- """
- return all(b[node][: len(hashes)] == hashes for node, hashes in a.items())
- def hexdigest_sizes_correct(a, digest_size):
- """
- returns True if all hex digest sizes are the expected length in a node:subgraph-hashes
- dictionary. Hex digest string length == 2 * bytes digest length since each pair of hex
- digits encodes 1 byte (https://docs.python.org/3/library/hashlib.html)
- """
- hexdigest_size = digest_size * 2
- list_digest_sizes_correct = lambda l: all(len(x) == hexdigest_size for x in l)
- return all(list_digest_sizes_correct(hashes) for hashes in a.values())
- def test_empty_graph_subgraph_hash():
- """ "
- empty graphs should give empty dict subgraph hashes regardless of other params
- """
- G = nx.empty_graph()
- subgraph_hashes1 = nx.weisfeiler_lehman_subgraph_hashes(G)
- subgraph_hashes2 = nx.weisfeiler_lehman_subgraph_hashes(G, edge_attr="edge_attr")
- subgraph_hashes3 = nx.weisfeiler_lehman_subgraph_hashes(G, node_attr="edge_attr")
- subgraph_hashes4 = nx.weisfeiler_lehman_subgraph_hashes(G, iterations=2)
- subgraph_hashes5 = nx.weisfeiler_lehman_subgraph_hashes(G, digest_size=64)
- assert subgraph_hashes1 == {}
- assert subgraph_hashes2 == {}
- assert subgraph_hashes3 == {}
- assert subgraph_hashes4 == {}
- assert subgraph_hashes5 == {}
- def test_directed_subgraph_hash():
- """
- A directed graph with no bi-directional edges should yield different subgraph hashes
- to the same graph taken as undirected, if all hashes don't collide.
- """
- r = 10
- for i in range(r):
- G_directed = nx.gn_graph(10 + r, seed=100 + i)
- G_undirected = nx.to_undirected(G_directed)
- directed_subgraph_hashes = nx.weisfeiler_lehman_subgraph_hashes(G_directed)
- undirected_subgraph_hashes = nx.weisfeiler_lehman_subgraph_hashes(G_undirected)
- assert directed_subgraph_hashes != undirected_subgraph_hashes
- def test_reversed_subgraph_hash():
- """
- A directed graph with no bi-directional edges should yield different subgraph hashes
- to the same graph taken with edge directions reversed if there are no hash collisions.
- Here we test a cycle graph which is the minimal counterexample
- """
- G = nx.cycle_graph(5, create_using=nx.DiGraph)
- nx.set_node_attributes(G, {n: str(n) for n in G.nodes()}, name="label")
- G_reversed = G.reverse()
- h = nx.weisfeiler_lehman_subgraph_hashes(G, node_attr="label")
- h_reversed = nx.weisfeiler_lehman_subgraph_hashes(G_reversed, node_attr="label")
- assert h != h_reversed
- def test_isomorphic_subgraph_hash():
- """
- the subgraph hashes should be invariant to node-relabeling when the output is reindexed
- by the same mapping and all hashes don't collide.
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=200 + i)
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g1_subgraph_hashes = nx.weisfeiler_lehman_subgraph_hashes(G1)
- g2_subgraph_hashes = nx.weisfeiler_lehman_subgraph_hashes(G2)
- assert g1_subgraph_hashes == {-1 * k: v for k, v in g2_subgraph_hashes.items()}
- def test_isomorphic_edge_attr_subgraph_hash():
- """
- Isomorphic graphs with differing edge attributes should yield different subgraph
- hashes if the 'edge_attr' argument is supplied and populated in the graph, and
- all hashes don't collide.
- The output should still be invariant to node-relabeling
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=300 + i)
- for a, b in G1.edges:
- G1[a][b]["edge_attr1"] = f"{a}-{b}-1"
- G1[a][b]["edge_attr2"] = f"{a}-{b}-2"
- g1_hash_with_edge_attr1 = nx.weisfeiler_lehman_subgraph_hashes(
- G1, edge_attr="edge_attr1"
- )
- g1_hash_with_edge_attr2 = nx.weisfeiler_lehman_subgraph_hashes(
- G1, edge_attr="edge_attr2"
- )
- g1_hash_no_edge_attr = nx.weisfeiler_lehman_subgraph_hashes(G1, edge_attr=None)
- assert g1_hash_with_edge_attr1 != g1_hash_no_edge_attr
- assert g1_hash_with_edge_attr2 != g1_hash_no_edge_attr
- assert g1_hash_with_edge_attr1 != g1_hash_with_edge_attr2
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g2_hash_with_edge_attr1 = nx.weisfeiler_lehman_subgraph_hashes(
- G2, edge_attr="edge_attr1"
- )
- g2_hash_with_edge_attr2 = nx.weisfeiler_lehman_subgraph_hashes(
- G2, edge_attr="edge_attr2"
- )
- assert g1_hash_with_edge_attr1 == {
- -1 * k: v for k, v in g2_hash_with_edge_attr1.items()
- }
- assert g1_hash_with_edge_attr2 == {
- -1 * k: v for k, v in g2_hash_with_edge_attr2.items()
- }
- def test_missing_edge_attr_subgraph_hash():
- """
- If the 'edge_attr' argument is supplied but is missing from an edge in the graph,
- we should raise a KeyError
- """
- G = nx.Graph()
- G.add_edges_from([(1, 2, {"edge_attr1": "a"}), (1, 3, {})])
- pytest.raises(
- KeyError, nx.weisfeiler_lehman_subgraph_hashes, G, edge_attr="edge_attr1"
- )
- def test_isomorphic_node_attr_subgraph_hash():
- """
- Isomorphic graphs with differing node attributes should yield different subgraph
- hashes if the 'node_attr' argument is supplied and populated in the graph, and
- all hashes don't collide.
- The output should still be invariant to node-relabeling
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=400 + i)
- for u in G1.nodes():
- G1.nodes[u]["node_attr1"] = f"{u}-1"
- G1.nodes[u]["node_attr2"] = f"{u}-2"
- g1_hash_with_node_attr1 = nx.weisfeiler_lehman_subgraph_hashes(
- G1, node_attr="node_attr1"
- )
- g1_hash_with_node_attr2 = nx.weisfeiler_lehman_subgraph_hashes(
- G1, node_attr="node_attr2"
- )
- g1_hash_no_node_attr = nx.weisfeiler_lehman_subgraph_hashes(G1, node_attr=None)
- assert g1_hash_with_node_attr1 != g1_hash_no_node_attr
- assert g1_hash_with_node_attr2 != g1_hash_no_node_attr
- assert g1_hash_with_node_attr1 != g1_hash_with_node_attr2
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g2_hash_with_node_attr1 = nx.weisfeiler_lehman_subgraph_hashes(
- G2, node_attr="node_attr1"
- )
- g2_hash_with_node_attr2 = nx.weisfeiler_lehman_subgraph_hashes(
- G2, node_attr="node_attr2"
- )
- assert g1_hash_with_node_attr1 == {
- -1 * k: v for k, v in g2_hash_with_node_attr1.items()
- }
- assert g1_hash_with_node_attr2 == {
- -1 * k: v for k, v in g2_hash_with_node_attr2.items()
- }
- def test_missing_node_attr_subgraph_hash():
- """
- If the 'node_attr' argument is supplied but is missing from a node in the graph,
- we should raise a KeyError
- """
- G = nx.Graph()
- G.add_nodes_from([(1, {"node_attr1": "a"}), (2, {})])
- G.add_edges_from([(1, 2), (2, 3), (3, 1), (1, 4)])
- pytest.raises(
- KeyError, nx.weisfeiler_lehman_subgraph_hashes, G, node_attr="node_attr1"
- )
- def test_isomorphic_edge_attr_and_node_attr_subgraph_hash():
- """
- Isomorphic graphs with differing node attributes should yield different subgraph
- hashes if the 'node_attr' and 'edge_attr' argument is supplied and populated in
- the graph, and all hashes don't collide
- The output should still be invariant to node-relabeling
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G1 = nx.erdos_renyi_graph(n, p * i, seed=500 + i)
- for u in G1.nodes():
- G1.nodes[u]["node_attr1"] = f"{u}-1"
- G1.nodes[u]["node_attr2"] = f"{u}-2"
- for a, b in G1.edges:
- G1[a][b]["edge_attr1"] = f"{a}-{b}-1"
- G1[a][b]["edge_attr2"] = f"{a}-{b}-2"
- g1_hash_edge1_node1 = nx.weisfeiler_lehman_subgraph_hashes(
- G1, edge_attr="edge_attr1", node_attr="node_attr1"
- )
- g1_hash_edge2_node2 = nx.weisfeiler_lehman_subgraph_hashes(
- G1, edge_attr="edge_attr2", node_attr="node_attr2"
- )
- g1_hash_edge1_node2 = nx.weisfeiler_lehman_subgraph_hashes(
- G1, edge_attr="edge_attr1", node_attr="node_attr2"
- )
- g1_hash_no_attr = nx.weisfeiler_lehman_subgraph_hashes(G1)
- assert g1_hash_edge1_node1 != g1_hash_no_attr
- assert g1_hash_edge2_node2 != g1_hash_no_attr
- assert g1_hash_edge1_node1 != g1_hash_edge2_node2
- assert g1_hash_edge1_node2 != g1_hash_edge2_node2
- assert g1_hash_edge1_node2 != g1_hash_edge1_node1
- G2 = nx.relabel_nodes(G1, {u: -1 * u for u in G1.nodes()})
- g2_hash_edge1_node1 = nx.weisfeiler_lehman_subgraph_hashes(
- G2, edge_attr="edge_attr1", node_attr="node_attr1"
- )
- g2_hash_edge2_node2 = nx.weisfeiler_lehman_subgraph_hashes(
- G2, edge_attr="edge_attr2", node_attr="node_attr2"
- )
- assert g1_hash_edge1_node1 == {
- -1 * k: v for k, v in g2_hash_edge1_node1.items()
- }
- assert g1_hash_edge2_node2 == {
- -1 * k: v for k, v in g2_hash_edge2_node2.items()
- }
- def test_iteration_depth():
- """
- All nodes should have the correct number of subgraph hashes in the output when
- using degree as initial node labels
- Subsequent iteration depths for the same graph should be additive for each node
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G = nx.erdos_renyi_graph(n, p * i, seed=600 + i)
- depth3 = nx.weisfeiler_lehman_subgraph_hashes(G, iterations=3)
- depth4 = nx.weisfeiler_lehman_subgraph_hashes(G, iterations=4)
- depth5 = nx.weisfeiler_lehman_subgraph_hashes(G, iterations=5)
- assert all(len(hashes) == 3 for hashes in depth3.values())
- assert all(len(hashes) == 4 for hashes in depth4.values())
- assert all(len(hashes) == 5 for hashes in depth5.values())
- assert is_subiteration(depth3, depth4)
- assert is_subiteration(depth4, depth5)
- assert is_subiteration(depth3, depth5)
- def test_iteration_depth_edge_attr():
- """
- All nodes should have the correct number of subgraph hashes in the output when
- setting initial node labels empty and using an edge attribute when aggregating
- neighborhoods.
- Subsequent iteration depths for the same graph should be additive for each node
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G = nx.erdos_renyi_graph(n, p * i, seed=700 + i)
- for a, b in G.edges:
- G[a][b]["edge_attr1"] = f"{a}-{b}-1"
- depth3 = nx.weisfeiler_lehman_subgraph_hashes(
- G, edge_attr="edge_attr1", iterations=3
- )
- depth4 = nx.weisfeiler_lehman_subgraph_hashes(
- G, edge_attr="edge_attr1", iterations=4
- )
- depth5 = nx.weisfeiler_lehman_subgraph_hashes(
- G, edge_attr="edge_attr1", iterations=5
- )
- assert all(len(hashes) == 3 for hashes in depth3.values())
- assert all(len(hashes) == 4 for hashes in depth4.values())
- assert all(len(hashes) == 5 for hashes in depth5.values())
- assert is_subiteration(depth3, depth4)
- assert is_subiteration(depth4, depth5)
- assert is_subiteration(depth3, depth5)
- def test_iteration_depth_node_attr():
- """
- All nodes should have the correct number of subgraph hashes in the output when
- setting initial node labels to an attribute.
- Subsequent iteration depths for the same graph should be additive for each node
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G = nx.erdos_renyi_graph(n, p * i, seed=800 + i)
- for u in G.nodes():
- G.nodes[u]["node_attr1"] = f"{u}-1"
- depth3 = nx.weisfeiler_lehman_subgraph_hashes(
- G, node_attr="node_attr1", iterations=3
- )
- depth4 = nx.weisfeiler_lehman_subgraph_hashes(
- G, node_attr="node_attr1", iterations=4
- )
- depth5 = nx.weisfeiler_lehman_subgraph_hashes(
- G, node_attr="node_attr1", iterations=5
- )
- assert all(len(hashes) == 3 for hashes in depth3.values())
- assert all(len(hashes) == 4 for hashes in depth4.values())
- assert all(len(hashes) == 5 for hashes in depth5.values())
- assert is_subiteration(depth3, depth4)
- assert is_subiteration(depth4, depth5)
- assert is_subiteration(depth3, depth5)
- def test_iteration_depth_node_edge_attr():
- """
- All nodes should have the correct number of subgraph hashes in the output when
- setting initial node labels to an attribute and also using an edge attribute when
- aggregating neighborhoods.
- Subsequent iteration depths for the same graph should be additive for each node
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G = nx.erdos_renyi_graph(n, p * i, seed=900 + i)
- for u in G.nodes():
- G.nodes[u]["node_attr1"] = f"{u}-1"
- for a, b in G.edges:
- G[a][b]["edge_attr1"] = f"{a}-{b}-1"
- depth3 = nx.weisfeiler_lehman_subgraph_hashes(
- G, edge_attr="edge_attr1", node_attr="node_attr1", iterations=3
- )
- depth4 = nx.weisfeiler_lehman_subgraph_hashes(
- G, edge_attr="edge_attr1", node_attr="node_attr1", iterations=4
- )
- depth5 = nx.weisfeiler_lehman_subgraph_hashes(
- G, edge_attr="edge_attr1", node_attr="node_attr1", iterations=5
- )
- assert all(len(hashes) == 3 for hashes in depth3.values())
- assert all(len(hashes) == 4 for hashes in depth4.values())
- assert all(len(hashes) == 5 for hashes in depth5.values())
- assert is_subiteration(depth3, depth4)
- assert is_subiteration(depth4, depth5)
- assert is_subiteration(depth3, depth5)
- def test_digest_size_subgraph_hash():
- """
- The hash string lengths should be as expected for a variety of graphs and
- digest sizes
- """
- n, r = 100, 10
- p = 1.0 / r
- for i in range(1, r + 1):
- G = nx.erdos_renyi_graph(n, p * i, seed=1000 + i)
- digest_size16_hashes = nx.weisfeiler_lehman_subgraph_hashes(G)
- digest_size32_hashes = nx.weisfeiler_lehman_subgraph_hashes(G, digest_size=32)
- assert digest_size16_hashes != digest_size32_hashes
- assert hexdigest_sizes_correct(digest_size16_hashes, 16)
- assert hexdigest_sizes_correct(digest_size32_hashes, 32)
|