Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,121 @@
|
|||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms import bipartite
|
||||
|
||||
|
||||
class TestBipartiteBasic:
|
||||
def test_is_bipartite(self):
|
||||
assert bipartite.is_bipartite(nx.path_graph(4))
|
||||
assert bipartite.is_bipartite(nx.DiGraph([(1, 0)]))
|
||||
assert not bipartite.is_bipartite(nx.complete_graph(3))
|
||||
|
||||
def test_bipartite_color(self):
|
||||
G = nx.path_graph(4)
|
||||
c = bipartite.color(G)
|
||||
assert c == {0: 1, 1: 0, 2: 1, 3: 0}
|
||||
|
||||
def test_not_bipartite_color(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
c = bipartite.color(nx.complete_graph(4))
|
||||
|
||||
def test_bipartite_directed(self):
|
||||
G = bipartite.random_graph(10, 10, 0.1, directed=True)
|
||||
assert bipartite.is_bipartite(G)
|
||||
|
||||
def test_bipartite_sets(self):
|
||||
G = nx.path_graph(4)
|
||||
X, Y = bipartite.sets(G)
|
||||
assert X == {0, 2}
|
||||
assert Y == {1, 3}
|
||||
|
||||
def test_bipartite_sets_directed(self):
|
||||
G = nx.path_graph(4)
|
||||
D = G.to_directed()
|
||||
X, Y = bipartite.sets(D)
|
||||
assert X == {0, 2}
|
||||
assert Y == {1, 3}
|
||||
|
||||
def test_bipartite_sets_given_top_nodes(self):
|
||||
G = nx.path_graph(4)
|
||||
top_nodes = [0, 2]
|
||||
X, Y = bipartite.sets(G, top_nodes)
|
||||
assert X == {0, 2}
|
||||
assert Y == {1, 3}
|
||||
|
||||
def test_bipartite_sets_disconnected(self):
|
||||
with pytest.raises(nx.AmbiguousSolution):
|
||||
G = nx.path_graph(4)
|
||||
G.add_edges_from([(5, 6), (6, 7)])
|
||||
X, Y = bipartite.sets(G)
|
||||
|
||||
def test_is_bipartite_node_set(self):
|
||||
G = nx.path_graph(4)
|
||||
assert bipartite.is_bipartite_node_set(G, [0, 2])
|
||||
assert bipartite.is_bipartite_node_set(G, [1, 3])
|
||||
assert not bipartite.is_bipartite_node_set(G, [1, 2])
|
||||
G.add_edge(10, 20)
|
||||
assert bipartite.is_bipartite_node_set(G, [0, 2, 10])
|
||||
assert bipartite.is_bipartite_node_set(G, [0, 2, 20])
|
||||
assert bipartite.is_bipartite_node_set(G, [1, 3, 10])
|
||||
assert bipartite.is_bipartite_node_set(G, [1, 3, 20])
|
||||
|
||||
def test_bipartite_density(self):
|
||||
G = nx.path_graph(5)
|
||||
X, Y = bipartite.sets(G)
|
||||
density = float(len(list(G.edges()))) / (len(X) * len(Y))
|
||||
assert bipartite.density(G, X) == density
|
||||
D = nx.DiGraph(G.edges())
|
||||
assert bipartite.density(D, X) == density / 2.0
|
||||
assert bipartite.density(nx.Graph(), {}) == 0.0
|
||||
|
||||
def test_bipartite_degrees(self):
|
||||
G = nx.path_graph(5)
|
||||
X = {1, 3}
|
||||
Y = {0, 2, 4}
|
||||
u, d = bipartite.degrees(G, Y)
|
||||
assert dict(u) == {1: 2, 3: 2}
|
||||
assert dict(d) == {0: 1, 2: 2, 4: 1}
|
||||
|
||||
def test_bipartite_weighted_degrees(self):
|
||||
G = nx.path_graph(5)
|
||||
G.add_edge(0, 1, weight=0.1, other=0.2)
|
||||
X = {1, 3}
|
||||
Y = {0, 2, 4}
|
||||
u, d = bipartite.degrees(G, Y, weight="weight")
|
||||
assert dict(u) == {1: 1.1, 3: 2}
|
||||
assert dict(d) == {0: 0.1, 2: 2, 4: 1}
|
||||
u, d = bipartite.degrees(G, Y, weight="other")
|
||||
assert dict(u) == {1: 1.2, 3: 2}
|
||||
assert dict(d) == {0: 0.2, 2: 2, 4: 1}
|
||||
|
||||
def test_biadjacency_matrix_weight(self):
|
||||
scipy = pytest.importorskip("scipy")
|
||||
G = nx.path_graph(5)
|
||||
G.add_edge(0, 1, weight=2, other=4)
|
||||
X = [1, 3]
|
||||
Y = [0, 2, 4]
|
||||
M = bipartite.biadjacency_matrix(G, X, weight="weight")
|
||||
assert M[0, 0] == 2
|
||||
M = bipartite.biadjacency_matrix(G, X, weight="other")
|
||||
assert M[0, 0] == 4
|
||||
|
||||
def test_biadjacency_matrix(self):
|
||||
scipy = pytest.importorskip("scipy")
|
||||
tops = [2, 5, 10]
|
||||
bots = [5, 10, 15]
|
||||
for i in range(len(tops)):
|
||||
G = bipartite.random_graph(tops[i], bots[i], 0.2)
|
||||
top = [n for n, d in G.nodes(data=True) if d["bipartite"] == 0]
|
||||
M = bipartite.biadjacency_matrix(G, top)
|
||||
assert M.shape[0] == tops[i]
|
||||
assert M.shape[1] == bots[i]
|
||||
|
||||
def test_biadjacency_matrix_order(self):
|
||||
scipy = pytest.importorskip("scipy")
|
||||
G = nx.path_graph(5)
|
||||
G.add_edge(0, 1, weight=2)
|
||||
X = [3, 1]
|
||||
Y = [4, 2, 0]
|
||||
M = bipartite.biadjacency_matrix(G, X, Y, weight="weight")
|
||||
assert M[1, 2] == 2
|
|
@ -0,0 +1,175 @@
|
|||
import networkx as nx
|
||||
from networkx.algorithms import bipartite
|
||||
from networkx.testing import almost_equal
|
||||
|
||||
|
||||
class TestBipartiteCentrality:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.P4 = nx.path_graph(4)
|
||||
cls.K3 = nx.complete_bipartite_graph(3, 3)
|
||||
cls.C4 = nx.cycle_graph(4)
|
||||
cls.davis = nx.davis_southern_women_graph()
|
||||
cls.top_nodes = [
|
||||
n for n, d in cls.davis.nodes(data=True) if d["bipartite"] == 0
|
||||
]
|
||||
|
||||
def test_degree_centrality(self):
|
||||
d = bipartite.degree_centrality(self.P4, [1, 3])
|
||||
answer = {0: 0.5, 1: 1.0, 2: 1.0, 3: 0.5}
|
||||
assert d == answer
|
||||
d = bipartite.degree_centrality(self.K3, [0, 1, 2])
|
||||
answer = {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0, 5: 1.0}
|
||||
assert d == answer
|
||||
d = bipartite.degree_centrality(self.C4, [0, 2])
|
||||
answer = {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0}
|
||||
assert d == answer
|
||||
|
||||
def test_betweenness_centrality(self):
|
||||
c = bipartite.betweenness_centrality(self.P4, [1, 3])
|
||||
answer = {0: 0.0, 1: 1.0, 2: 1.0, 3: 0.0}
|
||||
assert c == answer
|
||||
c = bipartite.betweenness_centrality(self.K3, [0, 1, 2])
|
||||
answer = {0: 0.125, 1: 0.125, 2: 0.125, 3: 0.125, 4: 0.125, 5: 0.125}
|
||||
assert c == answer
|
||||
c = bipartite.betweenness_centrality(self.C4, [0, 2])
|
||||
answer = {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25}
|
||||
assert c == answer
|
||||
|
||||
def test_closeness_centrality(self):
|
||||
c = bipartite.closeness_centrality(self.P4, [1, 3])
|
||||
answer = {0: 2.0 / 3, 1: 1.0, 2: 1.0, 3: 2.0 / 3}
|
||||
assert c == answer
|
||||
c = bipartite.closeness_centrality(self.K3, [0, 1, 2])
|
||||
answer = {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0, 5: 1.0}
|
||||
assert c == answer
|
||||
c = bipartite.closeness_centrality(self.C4, [0, 2])
|
||||
answer = {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0}
|
||||
assert c == answer
|
||||
G = nx.Graph()
|
||||
G.add_node(0)
|
||||
G.add_node(1)
|
||||
c = bipartite.closeness_centrality(G, [0])
|
||||
assert c == {1: 0.0}
|
||||
c = bipartite.closeness_centrality(G, [1])
|
||||
assert c == {1: 0.0}
|
||||
|
||||
def test_davis_degree_centrality(self):
|
||||
G = self.davis
|
||||
deg = bipartite.degree_centrality(G, self.top_nodes)
|
||||
answer = {
|
||||
"E8": 0.78,
|
||||
"E9": 0.67,
|
||||
"E7": 0.56,
|
||||
"Nora Fayette": 0.57,
|
||||
"Evelyn Jefferson": 0.57,
|
||||
"Theresa Anderson": 0.57,
|
||||
"E6": 0.44,
|
||||
"Sylvia Avondale": 0.50,
|
||||
"Laura Mandeville": 0.50,
|
||||
"Brenda Rogers": 0.50,
|
||||
"Katherina Rogers": 0.43,
|
||||
"E5": 0.44,
|
||||
"Helen Lloyd": 0.36,
|
||||
"E3": 0.33,
|
||||
"Ruth DeSand": 0.29,
|
||||
"Verne Sanderson": 0.29,
|
||||
"E12": 0.33,
|
||||
"Myra Liddel": 0.29,
|
||||
"E11": 0.22,
|
||||
"Eleanor Nye": 0.29,
|
||||
"Frances Anderson": 0.29,
|
||||
"Pearl Oglethorpe": 0.21,
|
||||
"E4": 0.22,
|
||||
"Charlotte McDowd": 0.29,
|
||||
"E10": 0.28,
|
||||
"Olivia Carleton": 0.14,
|
||||
"Flora Price": 0.14,
|
||||
"E2": 0.17,
|
||||
"E1": 0.17,
|
||||
"Dorothy Murchison": 0.14,
|
||||
"E13": 0.17,
|
||||
"E14": 0.17,
|
||||
}
|
||||
for node, value in answer.items():
|
||||
assert almost_equal(value, deg[node], places=2)
|
||||
|
||||
def test_davis_betweenness_centrality(self):
|
||||
G = self.davis
|
||||
bet = bipartite.betweenness_centrality(G, self.top_nodes)
|
||||
answer = {
|
||||
"E8": 0.24,
|
||||
"E9": 0.23,
|
||||
"E7": 0.13,
|
||||
"Nora Fayette": 0.11,
|
||||
"Evelyn Jefferson": 0.10,
|
||||
"Theresa Anderson": 0.09,
|
||||
"E6": 0.07,
|
||||
"Sylvia Avondale": 0.07,
|
||||
"Laura Mandeville": 0.05,
|
||||
"Brenda Rogers": 0.05,
|
||||
"Katherina Rogers": 0.05,
|
||||
"E5": 0.04,
|
||||
"Helen Lloyd": 0.04,
|
||||
"E3": 0.02,
|
||||
"Ruth DeSand": 0.02,
|
||||
"Verne Sanderson": 0.02,
|
||||
"E12": 0.02,
|
||||
"Myra Liddel": 0.02,
|
||||
"E11": 0.02,
|
||||
"Eleanor Nye": 0.01,
|
||||
"Frances Anderson": 0.01,
|
||||
"Pearl Oglethorpe": 0.01,
|
||||
"E4": 0.01,
|
||||
"Charlotte McDowd": 0.01,
|
||||
"E10": 0.01,
|
||||
"Olivia Carleton": 0.01,
|
||||
"Flora Price": 0.01,
|
||||
"E2": 0.00,
|
||||
"E1": 0.00,
|
||||
"Dorothy Murchison": 0.00,
|
||||
"E13": 0.00,
|
||||
"E14": 0.00,
|
||||
}
|
||||
for node, value in answer.items():
|
||||
assert almost_equal(value, bet[node], places=2)
|
||||
|
||||
def test_davis_closeness_centrality(self):
|
||||
G = self.davis
|
||||
clos = bipartite.closeness_centrality(G, self.top_nodes)
|
||||
answer = {
|
||||
"E8": 0.85,
|
||||
"E9": 0.79,
|
||||
"E7": 0.73,
|
||||
"Nora Fayette": 0.80,
|
||||
"Evelyn Jefferson": 0.80,
|
||||
"Theresa Anderson": 0.80,
|
||||
"E6": 0.69,
|
||||
"Sylvia Avondale": 0.77,
|
||||
"Laura Mandeville": 0.73,
|
||||
"Brenda Rogers": 0.73,
|
||||
"Katherina Rogers": 0.73,
|
||||
"E5": 0.59,
|
||||
"Helen Lloyd": 0.73,
|
||||
"E3": 0.56,
|
||||
"Ruth DeSand": 0.71,
|
||||
"Verne Sanderson": 0.71,
|
||||
"E12": 0.56,
|
||||
"Myra Liddel": 0.69,
|
||||
"E11": 0.54,
|
||||
"Eleanor Nye": 0.67,
|
||||
"Frances Anderson": 0.67,
|
||||
"Pearl Oglethorpe": 0.67,
|
||||
"E4": 0.54,
|
||||
"Charlotte McDowd": 0.60,
|
||||
"E10": 0.55,
|
||||
"Olivia Carleton": 0.59,
|
||||
"Flora Price": 0.59,
|
||||
"E2": 0.52,
|
||||
"E1": 0.52,
|
||||
"Dorothy Murchison": 0.65,
|
||||
"E13": 0.52,
|
||||
"E14": 0.52,
|
||||
}
|
||||
for node, value in answer.items():
|
||||
assert almost_equal(value, clos[node], places=2)
|
|
@ -0,0 +1,83 @@
|
|||
import networkx as nx
|
||||
import pytest
|
||||
from networkx.algorithms.bipartite.cluster import cc_dot, cc_min, cc_max
|
||||
import networkx.algorithms.bipartite as bipartite
|
||||
|
||||
|
||||
def test_pairwise_bipartite_cc_functions():
|
||||
# Test functions for different kinds of bipartite clustering coefficients
|
||||
# between pairs of nodes using 3 example graphs from figure 5 p. 40
|
||||
# Latapy et al (2008)
|
||||
G1 = nx.Graph([(0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 5), (1, 6), (1, 7)])
|
||||
G2 = nx.Graph([(0, 2), (0, 3), (0, 4), (1, 3), (1, 4), (1, 5)])
|
||||
G3 = nx.Graph(
|
||||
[(0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9)]
|
||||
)
|
||||
result = {
|
||||
0: [1 / 3.0, 2 / 3.0, 2 / 5.0],
|
||||
1: [1 / 2.0, 2 / 3.0, 2 / 3.0],
|
||||
2: [2 / 8.0, 2 / 5.0, 2 / 5.0],
|
||||
}
|
||||
for i, G in enumerate([G1, G2, G3]):
|
||||
assert bipartite.is_bipartite(G)
|
||||
assert cc_dot(set(G[0]), set(G[1])) == result[i][0]
|
||||
assert cc_min(set(G[0]), set(G[1])) == result[i][1]
|
||||
assert cc_max(set(G[0]), set(G[1])) == result[i][2]
|
||||
|
||||
|
||||
def test_star_graph():
|
||||
G = nx.star_graph(3)
|
||||
# all modes are the same
|
||||
answer = {0: 0, 1: 1, 2: 1, 3: 1}
|
||||
assert bipartite.clustering(G, mode="dot") == answer
|
||||
assert bipartite.clustering(G, mode="min") == answer
|
||||
assert bipartite.clustering(G, mode="max") == answer
|
||||
|
||||
|
||||
def test_not_bipartite():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
bipartite.clustering(nx.complete_graph(4))
|
||||
|
||||
|
||||
def test_bad_mode():
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
bipartite.clustering(nx.path_graph(4), mode="foo")
|
||||
|
||||
|
||||
def test_path_graph():
|
||||
G = nx.path_graph(4)
|
||||
answer = {0: 0.5, 1: 0.5, 2: 0.5, 3: 0.5}
|
||||
assert bipartite.clustering(G, mode="dot") == answer
|
||||
assert bipartite.clustering(G, mode="max") == answer
|
||||
answer = {0: 1, 1: 1, 2: 1, 3: 1}
|
||||
assert bipartite.clustering(G, mode="min") == answer
|
||||
|
||||
|
||||
def test_average_path_graph():
|
||||
G = nx.path_graph(4)
|
||||
assert bipartite.average_clustering(G, mode="dot") == 0.5
|
||||
assert bipartite.average_clustering(G, mode="max") == 0.5
|
||||
assert bipartite.average_clustering(G, mode="min") == 1
|
||||
|
||||
|
||||
def test_ra_clustering_davis():
|
||||
G = nx.davis_southern_women_graph()
|
||||
cc4 = round(bipartite.robins_alexander_clustering(G), 3)
|
||||
assert cc4 == 0.468
|
||||
|
||||
|
||||
def test_ra_clustering_square():
|
||||
G = nx.path_graph(4)
|
||||
G.add_edge(0, 3)
|
||||
assert bipartite.robins_alexander_clustering(G) == 1.0
|
||||
|
||||
|
||||
def test_ra_clustering_zero():
|
||||
G = nx.Graph()
|
||||
assert bipartite.robins_alexander_clustering(G) == 0
|
||||
G.add_nodes_from(range(4))
|
||||
assert bipartite.robins_alexander_clustering(G) == 0
|
||||
G.add_edges_from([(0, 1), (2, 3), (3, 4)])
|
||||
assert bipartite.robins_alexander_clustering(G) == 0
|
||||
G.add_edge(1, 2)
|
||||
assert bipartite.robins_alexander_clustering(G) == 0
|
|
@ -0,0 +1,33 @@
|
|||
import networkx as nx
|
||||
import networkx.algorithms.bipartite as bipartite
|
||||
|
||||
|
||||
class TestMinEdgeCover:
|
||||
"""Tests for :func:`networkx.algorithms.bipartite.min_edge_cover`"""
|
||||
|
||||
def test_empty_graph(self):
|
||||
G = nx.Graph()
|
||||
assert bipartite.min_edge_cover(G) == set()
|
||||
|
||||
def test_graph_single_edge(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge(0, 1)
|
||||
assert bipartite.min_edge_cover(G) == {(0, 1), (1, 0)}
|
||||
|
||||
def test_bipartite_default(self):
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4], bipartite=0)
|
||||
G.add_nodes_from(["a", "b", "c"], bipartite=1)
|
||||
G.add_edges_from([(1, "a"), (1, "b"), (2, "b"), (2, "c"), (3, "c"), (4, "a")])
|
||||
min_cover = bipartite.min_edge_cover(G)
|
||||
assert nx.is_edge_cover(G, min_cover)
|
||||
assert len(min_cover) == 8
|
||||
|
||||
def test_bipartite_explicit(self):
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([1, 2, 3, 4], bipartite=0)
|
||||
G.add_nodes_from(["a", "b", "c"], bipartite=1)
|
||||
G.add_edges_from([(1, "a"), (1, "b"), (2, "b"), (2, "c"), (3, "c"), (4, "a")])
|
||||
min_cover = bipartite.min_edge_cover(G, bipartite.eppstein_matching)
|
||||
assert nx.is_edge_cover(G, min_cover)
|
||||
assert len(min_cover) == 8
|
|
@ -0,0 +1,191 @@
|
|||
"""
|
||||
Unit tests for bipartite edgelists.
|
||||
"""
|
||||
import pytest
|
||||
import io
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
import networkx as nx
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal, assert_graphs_equal
|
||||
from networkx.algorithms import bipartite
|
||||
|
||||
|
||||
class TestEdgelist:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G = nx.Graph(name="test")
|
||||
e = [("a", "b"), ("b", "c"), ("c", "d"), ("d", "e"), ("e", "f"), ("a", "f")]
|
||||
cls.G.add_edges_from(e)
|
||||
cls.G.add_nodes_from(["a", "c", "e"], bipartite=0)
|
||||
cls.G.add_nodes_from(["b", "d", "f"], bipartite=1)
|
||||
cls.G.add_node("g", bipartite=0)
|
||||
cls.DG = nx.DiGraph(cls.G)
|
||||
cls.MG = nx.MultiGraph()
|
||||
cls.MG.add_edges_from([(1, 2), (1, 2), (1, 2)])
|
||||
cls.MG.add_node(1, bipartite=0)
|
||||
cls.MG.add_node(2, bipartite=1)
|
||||
|
||||
def test_read_edgelist_1(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1 2
|
||||
# comment line
|
||||
2 3
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = bipartite.read_edgelist(bytesIO, nodetype=int)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
def test_read_edgelist_3(self):
|
||||
s = b"""\
|
||||
# comment line
|
||||
1 2 {'weight':2.0}
|
||||
# comment line
|
||||
2 3 {'weight':3.0}
|
||||
"""
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = bipartite.read_edgelist(bytesIO, nodetype=int, data=False)
|
||||
assert_edges_equal(G.edges(), [(1, 2), (2, 3)])
|
||||
|
||||
bytesIO = io.BytesIO(s)
|
||||
G = bipartite.read_edgelist(bytesIO, nodetype=int, data=True)
|
||||
assert_edges_equal(
|
||||
G.edges(data=True), [(1, 2, {"weight": 2.0}), (2, 3, {"weight": 3.0})]
|
||||
)
|
||||
|
||||
def test_write_edgelist_1(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(1, 2), (2, 3)])
|
||||
G.add_node(1, bipartite=0)
|
||||
G.add_node(2, bipartite=1)
|
||||
G.add_node(3, bipartite=0)
|
||||
bipartite.write_edgelist(G, fh, data=False)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2\n3 2\n"
|
||||
|
||||
def test_write_edgelist_2(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(1, 2), (2, 3)])
|
||||
G.add_node(1, bipartite=0)
|
||||
G.add_node(2, bipartite=1)
|
||||
G.add_node(3, bipartite=0)
|
||||
bipartite.write_edgelist(G, fh, data=True)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 {}\n3 2 {}\n"
|
||||
|
||||
def test_write_edgelist_3(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 2, weight=2.0)
|
||||
G.add_edge(2, 3, weight=3.0)
|
||||
G.add_node(1, bipartite=0)
|
||||
G.add_node(2, bipartite=1)
|
||||
G.add_node(3, bipartite=0)
|
||||
bipartite.write_edgelist(G, fh, data=True)
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 {'weight': 2.0}\n3 2 {'weight': 3.0}\n"
|
||||
|
||||
def test_write_edgelist_4(self):
|
||||
fh = io.BytesIO()
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 2, weight=2.0)
|
||||
G.add_edge(2, 3, weight=3.0)
|
||||
G.add_node(1, bipartite=0)
|
||||
G.add_node(2, bipartite=1)
|
||||
G.add_node(3, bipartite=0)
|
||||
bipartite.write_edgelist(G, fh, data=[("weight")])
|
||||
fh.seek(0)
|
||||
assert fh.read() == b"1 2 2.0\n3 2 3.0\n"
|
||||
|
||||
def test_unicode(self):
|
||||
G = nx.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
G.add_node(name1, bipartite=0)
|
||||
G.add_node("Radiohead", bipartite=1)
|
||||
fd, fname = tempfile.mkstemp()
|
||||
bipartite.write_edgelist(G, fname)
|
||||
H = bipartite.read_edgelist(fname)
|
||||
assert_graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1_issue(self):
|
||||
G = nx.Graph()
|
||||
name1 = chr(2344) + chr(123) + chr(6543)
|
||||
name2 = chr(5543) + chr(1543) + chr(324)
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
G.add_node(name1, bipartite=0)
|
||||
G.add_node("Radiohead", bipartite=1)
|
||||
fd, fname = tempfile.mkstemp()
|
||||
pytest.raises(
|
||||
UnicodeEncodeError, bipartite.write_edgelist, G, fname, encoding="latin-1"
|
||||
)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_latin1(self):
|
||||
G = nx.Graph()
|
||||
name1 = "Bj" + chr(246) + "rk"
|
||||
name2 = chr(220) + "ber"
|
||||
G.add_edge(name1, "Radiohead", **{name2: 3})
|
||||
G.add_node(name1, bipartite=0)
|
||||
G.add_node("Radiohead", bipartite=1)
|
||||
fd, fname = tempfile.mkstemp()
|
||||
bipartite.write_edgelist(G, fname, encoding="latin-1")
|
||||
H = bipartite.read_edgelist(fname, encoding="latin-1")
|
||||
assert_graphs_equal(G, H)
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_graph(self):
|
||||
G = self.G
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
bipartite.write_edgelist(G, fname)
|
||||
H = bipartite.read_edgelist(fname)
|
||||
H2 = bipartite.read_edgelist(fname)
|
||||
assert H != H2 # they should be different graphs
|
||||
G.remove_node("g") # isolated nodes are not written in edgelist
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_integers(self):
|
||||
G = nx.convert_node_labels_to_integers(self.G)
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
bipartite.write_edgelist(G, fname)
|
||||
H = bipartite.read_edgelist(fname, nodetype=int)
|
||||
# isolated nodes are not written in edgelist
|
||||
G.remove_nodes_from(list(nx.isolates(G)))
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_edgelist_multigraph(self):
|
||||
G = self.MG
|
||||
(fd, fname) = tempfile.mkstemp()
|
||||
bipartite.write_edgelist(G, fname)
|
||||
H = bipartite.read_edgelist(fname, nodetype=int, create_using=nx.MultiGraph())
|
||||
H2 = bipartite.read_edgelist(fname, nodetype=int, create_using=nx.MultiGraph())
|
||||
assert H != H2 # they should be different graphs
|
||||
assert_nodes_equal(list(H), list(G))
|
||||
assert_edges_equal(list(H.edges()), list(G.edges()))
|
||||
os.close(fd)
|
||||
os.unlink(fname)
|
||||
|
||||
def test_empty_digraph(self):
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
bytesIO = io.BytesIO()
|
||||
bipartite.write_edgelist(nx.DiGraph(), bytesIO)
|
||||
|
||||
def test_raise_attribute(self):
|
||||
with pytest.raises(AttributeError):
|
||||
G = nx.path_graph(4)
|
||||
bytesIO = io.BytesIO()
|
||||
bipartite.write_edgelist(G, bytesIO)
|
|
@ -0,0 +1,399 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from ..generators import (
|
||||
alternating_havel_hakimi_graph,
|
||||
complete_bipartite_graph,
|
||||
configuration_model,
|
||||
gnmk_random_graph,
|
||||
havel_hakimi_graph,
|
||||
preferential_attachment_graph,
|
||||
random_graph,
|
||||
reverse_havel_hakimi_graph,
|
||||
)
|
||||
|
||||
"""
|
||||
Generators - Bipartite
|
||||
----------------------
|
||||
"""
|
||||
|
||||
|
||||
class TestGeneratorsBipartite:
|
||||
def test_complete_bipartite_graph(self):
|
||||
G = complete_bipartite_graph(0, 0)
|
||||
assert nx.is_isomorphic(G, nx.null_graph())
|
||||
|
||||
for i in [1, 5]:
|
||||
G = complete_bipartite_graph(i, 0)
|
||||
assert nx.is_isomorphic(G, nx.empty_graph(i))
|
||||
G = complete_bipartite_graph(0, i)
|
||||
assert nx.is_isomorphic(G, nx.empty_graph(i))
|
||||
|
||||
G = complete_bipartite_graph(2, 2)
|
||||
assert nx.is_isomorphic(G, nx.cycle_graph(4))
|
||||
|
||||
G = complete_bipartite_graph(1, 5)
|
||||
assert nx.is_isomorphic(G, nx.star_graph(5))
|
||||
|
||||
G = complete_bipartite_graph(5, 1)
|
||||
assert nx.is_isomorphic(G, nx.star_graph(5))
|
||||
|
||||
# complete_bipartite_graph(m1,m2) is a connected graph with
|
||||
# m1+m2 nodes and m1*m2 edges
|
||||
for m1, m2 in [(5, 11), (7, 3)]:
|
||||
G = complete_bipartite_graph(m1, m2)
|
||||
assert nx.number_of_nodes(G) == m1 + m2
|
||||
assert nx.number_of_edges(G) == m1 * m2
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError, complete_bipartite_graph, 7, 3, create_using=nx.DiGraph
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError, complete_bipartite_graph, 7, 3, create_using=nx.DiGraph
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
complete_bipartite_graph,
|
||||
7,
|
||||
3,
|
||||
create_using=nx.MultiDiGraph,
|
||||
)
|
||||
|
||||
mG = complete_bipartite_graph(7, 3, create_using=nx.MultiGraph)
|
||||
assert mG.is_multigraph()
|
||||
assert sorted(mG.edges()) == sorted(G.edges())
|
||||
|
||||
mG = complete_bipartite_graph(7, 3, create_using=nx.MultiGraph)
|
||||
assert mG.is_multigraph()
|
||||
assert sorted(mG.edges()) == sorted(G.edges())
|
||||
|
||||
mG = complete_bipartite_graph(7, 3) # default to Graph
|
||||
assert sorted(mG.edges()) == sorted(G.edges())
|
||||
assert not mG.is_multigraph()
|
||||
assert not mG.is_directed()
|
||||
|
||||
# specify nodes rather than number of nodes
|
||||
G = complete_bipartite_graph([1, 2], ["a", "b"])
|
||||
has_edges = (
|
||||
G.has_edge(1, "a")
|
||||
& G.has_edge(1, "b")
|
||||
& G.has_edge(2, "a")
|
||||
& G.has_edge(2, "b")
|
||||
)
|
||||
assert has_edges
|
||||
assert G.size() == 4
|
||||
|
||||
def test_configuration_model(self):
|
||||
aseq = []
|
||||
bseq = []
|
||||
G = configuration_model(aseq, bseq)
|
||||
assert len(G) == 0
|
||||
|
||||
aseq = [0, 0]
|
||||
bseq = [0, 0]
|
||||
G = configuration_model(aseq, bseq)
|
||||
assert len(G) == 4
|
||||
assert G.number_of_edges() == 0
|
||||
|
||||
aseq = [3, 3, 3, 3]
|
||||
bseq = [2, 2, 2, 2, 2]
|
||||
pytest.raises(nx.NetworkXError, configuration_model, aseq, bseq)
|
||||
|
||||
aseq = [3, 3, 3, 3]
|
||||
bseq = [2, 2, 2, 2, 2, 2]
|
||||
G = configuration_model(aseq, bseq)
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
aseq = [2, 2, 2, 2, 2, 2]
|
||||
bseq = [3, 3, 3, 3]
|
||||
G = configuration_model(aseq, bseq)
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
aseq = [2, 2, 2, 1, 1, 1]
|
||||
bseq = [3, 3, 3]
|
||||
G = configuration_model(aseq, bseq)
|
||||
assert G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
assert sorted(d for n, d in G.degree()) == [1, 1, 1, 2, 2, 2, 3, 3, 3]
|
||||
|
||||
GU = nx.project(nx.Graph(G), range(len(aseq)))
|
||||
assert GU.number_of_nodes() == 6
|
||||
|
||||
GD = nx.project(nx.Graph(G), range(len(aseq), len(aseq) + len(bseq)))
|
||||
assert GD.number_of_nodes() == 3
|
||||
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq, create_using=nx.Graph)
|
||||
assert not G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError, configuration_model, aseq, bseq, create_using=nx.DiGraph()
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError, configuration_model, aseq, bseq, create_using=nx.DiGraph
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
configuration_model,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.MultiDiGraph,
|
||||
)
|
||||
|
||||
def test_havel_hakimi_graph(self):
|
||||
aseq = []
|
||||
bseq = []
|
||||
G = havel_hakimi_graph(aseq, bseq)
|
||||
assert len(G) == 0
|
||||
|
||||
aseq = [0, 0]
|
||||
bseq = [0, 0]
|
||||
G = havel_hakimi_graph(aseq, bseq)
|
||||
assert len(G) == 4
|
||||
assert G.number_of_edges() == 0
|
||||
|
||||
aseq = [3, 3, 3, 3]
|
||||
bseq = [2, 2, 2, 2, 2]
|
||||
pytest.raises(nx.NetworkXError, havel_hakimi_graph, aseq, bseq)
|
||||
|
||||
bseq = [2, 2, 2, 2, 2, 2]
|
||||
G = havel_hakimi_graph(aseq, bseq)
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
aseq = [2, 2, 2, 2, 2, 2]
|
||||
bseq = [3, 3, 3, 3]
|
||||
G = havel_hakimi_graph(aseq, bseq)
|
||||
assert G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
GU = nx.project(nx.Graph(G), range(len(aseq)))
|
||||
assert GU.number_of_nodes() == 6
|
||||
|
||||
GD = nx.project(nx.Graph(G), range(len(aseq), len(aseq) + len(bseq)))
|
||||
assert GD.number_of_nodes() == 4
|
||||
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq, create_using=nx.Graph)
|
||||
assert not G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError, havel_hakimi_graph, aseq, bseq, create_using=nx.DiGraph
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError, havel_hakimi_graph, aseq, bseq, create_using=nx.DiGraph
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
havel_hakimi_graph,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.MultiDiGraph,
|
||||
)
|
||||
|
||||
def test_reverse_havel_hakimi_graph(self):
|
||||
aseq = []
|
||||
bseq = []
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq)
|
||||
assert len(G) == 0
|
||||
|
||||
aseq = [0, 0]
|
||||
bseq = [0, 0]
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq)
|
||||
assert len(G) == 4
|
||||
assert G.number_of_edges() == 0
|
||||
|
||||
aseq = [3, 3, 3, 3]
|
||||
bseq = [2, 2, 2, 2, 2]
|
||||
pytest.raises(nx.NetworkXError, reverse_havel_hakimi_graph, aseq, bseq)
|
||||
|
||||
bseq = [2, 2, 2, 2, 2, 2]
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq)
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
aseq = [2, 2, 2, 2, 2, 2]
|
||||
bseq = [3, 3, 3, 3]
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq)
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
aseq = [2, 2, 2, 1, 1, 1]
|
||||
bseq = [3, 3, 3]
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq)
|
||||
assert G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
assert sorted(d for n, d in G.degree()) == [1, 1, 1, 2, 2, 2, 3, 3, 3]
|
||||
|
||||
GU = nx.project(nx.Graph(G), range(len(aseq)))
|
||||
assert GU.number_of_nodes() == 6
|
||||
|
||||
GD = nx.project(nx.Graph(G), range(len(aseq), len(aseq) + len(bseq)))
|
||||
assert GD.number_of_nodes() == 3
|
||||
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq, create_using=nx.Graph)
|
||||
assert not G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
reverse_havel_hakimi_graph,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.DiGraph,
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
reverse_havel_hakimi_graph,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.DiGraph,
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
reverse_havel_hakimi_graph,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.MultiDiGraph,
|
||||
)
|
||||
|
||||
def test_alternating_havel_hakimi_graph(self):
|
||||
aseq = []
|
||||
bseq = []
|
||||
G = alternating_havel_hakimi_graph(aseq, bseq)
|
||||
assert len(G) == 0
|
||||
|
||||
aseq = [0, 0]
|
||||
bseq = [0, 0]
|
||||
G = alternating_havel_hakimi_graph(aseq, bseq)
|
||||
assert len(G) == 4
|
||||
assert G.number_of_edges() == 0
|
||||
|
||||
aseq = [3, 3, 3, 3]
|
||||
bseq = [2, 2, 2, 2, 2]
|
||||
pytest.raises(nx.NetworkXError, alternating_havel_hakimi_graph, aseq, bseq)
|
||||
|
||||
bseq = [2, 2, 2, 2, 2, 2]
|
||||
G = alternating_havel_hakimi_graph(aseq, bseq)
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
aseq = [2, 2, 2, 2, 2, 2]
|
||||
bseq = [3, 3, 3, 3]
|
||||
G = alternating_havel_hakimi_graph(aseq, bseq)
|
||||
assert sorted(d for n, d in G.degree()) == [2, 2, 2, 2, 2, 2, 3, 3, 3, 3]
|
||||
|
||||
aseq = [2, 2, 2, 1, 1, 1]
|
||||
bseq = [3, 3, 3]
|
||||
G = alternating_havel_hakimi_graph(aseq, bseq)
|
||||
assert G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
assert sorted(d for n, d in G.degree()) == [1, 1, 1, 2, 2, 2, 3, 3, 3]
|
||||
|
||||
GU = nx.project(nx.Graph(G), range(len(aseq)))
|
||||
assert GU.number_of_nodes() == 6
|
||||
|
||||
GD = nx.project(nx.Graph(G), range(len(aseq), len(aseq) + len(bseq)))
|
||||
assert GD.number_of_nodes() == 3
|
||||
|
||||
G = reverse_havel_hakimi_graph(aseq, bseq, create_using=nx.Graph)
|
||||
assert not G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
alternating_havel_hakimi_graph,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.DiGraph,
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
alternating_havel_hakimi_graph,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.DiGraph,
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
alternating_havel_hakimi_graph,
|
||||
aseq,
|
||||
bseq,
|
||||
create_using=nx.MultiDiGraph,
|
||||
)
|
||||
|
||||
def test_preferential_attachment(self):
|
||||
aseq = [3, 2, 1, 1]
|
||||
G = preferential_attachment_graph(aseq, 0.5)
|
||||
assert G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
|
||||
G = preferential_attachment_graph(aseq, 0.5, create_using=nx.Graph)
|
||||
assert not G.is_multigraph()
|
||||
assert not G.is_directed()
|
||||
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
preferential_attachment_graph,
|
||||
aseq,
|
||||
0.5,
|
||||
create_using=nx.DiGraph(),
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
preferential_attachment_graph,
|
||||
aseq,
|
||||
0.5,
|
||||
create_using=nx.DiGraph(),
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
preferential_attachment_graph,
|
||||
aseq,
|
||||
0.5,
|
||||
create_using=nx.DiGraph(),
|
||||
)
|
||||
|
||||
def test_random_graph(self):
|
||||
n = 10
|
||||
m = 20
|
||||
G = random_graph(n, m, 0.9)
|
||||
assert len(G) == 30
|
||||
assert nx.is_bipartite(G)
|
||||
X, Y = nx.algorithms.bipartite.sets(G)
|
||||
assert set(range(n)) == X
|
||||
assert set(range(n, n + m)) == Y
|
||||
|
||||
def test_random_digraph(self):
|
||||
n = 10
|
||||
m = 20
|
||||
G = random_graph(n, m, 0.9, directed=True)
|
||||
assert len(G) == 30
|
||||
assert nx.is_bipartite(G)
|
||||
X, Y = nx.algorithms.bipartite.sets(G)
|
||||
assert set(range(n)) == X
|
||||
assert set(range(n, n + m)) == Y
|
||||
|
||||
def test_gnmk_random_graph(self):
|
||||
n = 10
|
||||
m = 20
|
||||
edges = 100
|
||||
# set seed because sometimes it is not connected
|
||||
# which raises an error in bipartite.sets(G) below.
|
||||
G = gnmk_random_graph(n, m, edges, seed=1234)
|
||||
assert len(G) == n + m
|
||||
assert nx.is_bipartite(G)
|
||||
X, Y = nx.algorithms.bipartite.sets(G)
|
||||
# print(X)
|
||||
assert set(range(n)) == X
|
||||
assert set(range(n, n + m)) == Y
|
||||
assert edges == len(list(G.edges()))
|
||||
|
||||
def test_gnmk_random_graph_complete(self):
|
||||
n = 10
|
||||
m = 20
|
||||
edges = 200
|
||||
G = gnmk_random_graph(n, m, edges)
|
||||
assert len(G) == n + m
|
||||
assert nx.is_bipartite(G)
|
||||
X, Y = nx.algorithms.bipartite.sets(G)
|
||||
# print(X)
|
||||
assert set(range(n)) == X
|
||||
assert set(range(n, n + m)) == Y
|
||||
assert edges == len(list(G.edges()))
|
|
@ -0,0 +1,316 @@
|
|||
"""Unit tests for the :mod:`networkx.algorithms.bipartite.matching` module."""
|
||||
import itertools
|
||||
|
||||
import networkx as nx
|
||||
|
||||
import pytest
|
||||
|
||||
from networkx.algorithms.bipartite.matching import eppstein_matching
|
||||
from networkx.algorithms.bipartite.matching import hopcroft_karp_matching
|
||||
from networkx.algorithms.bipartite.matching import maximum_matching
|
||||
from networkx.algorithms.bipartite.matching import minimum_weight_full_matching
|
||||
from networkx.algorithms.bipartite.matching import to_vertex_cover
|
||||
|
||||
|
||||
class TestMatching:
|
||||
"""Tests for bipartite matching algorithms."""
|
||||
|
||||
def setup(self):
|
||||
"""Creates a bipartite graph for use in testing matching algorithms.
|
||||
|
||||
The bipartite graph has a maximum cardinality matching that leaves
|
||||
vertex 1 and vertex 10 unmatched. The first six numbers are the left
|
||||
vertices and the next six numbers are the right vertices.
|
||||
|
||||
"""
|
||||
self.simple_graph = nx.complete_bipartite_graph(2, 3)
|
||||
self.simple_solution = {0: 2, 1: 3, 2: 0, 3: 1}
|
||||
|
||||
edges = [(0, 7), (0, 8), (2, 6), (2, 9), (3, 8), (4, 8), (4, 9), (5, 11)]
|
||||
self.top_nodes = set(range(6))
|
||||
self.graph = nx.Graph()
|
||||
self.graph.add_nodes_from(range(12))
|
||||
self.graph.add_edges_from(edges)
|
||||
|
||||
# Example bipartite graph from issue 2127
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from(
|
||||
[
|
||||
(1, "C"),
|
||||
(1, "B"),
|
||||
(0, "G"),
|
||||
(1, "F"),
|
||||
(1, "E"),
|
||||
(0, "C"),
|
||||
(1, "D"),
|
||||
(1, "I"),
|
||||
(0, "A"),
|
||||
(0, "D"),
|
||||
(0, "F"),
|
||||
(0, "E"),
|
||||
(0, "H"),
|
||||
(1, "G"),
|
||||
(1, "A"),
|
||||
(0, "I"),
|
||||
(0, "B"),
|
||||
(1, "H"),
|
||||
]
|
||||
)
|
||||
G.add_edge((1, "C"), (0, "A"))
|
||||
G.add_edge((1, "B"), (0, "A"))
|
||||
G.add_edge((0, "G"), (1, "I"))
|
||||
G.add_edge((0, "G"), (1, "H"))
|
||||
G.add_edge((1, "F"), (0, "A"))
|
||||
G.add_edge((1, "F"), (0, "C"))
|
||||
G.add_edge((1, "F"), (0, "E"))
|
||||
G.add_edge((1, "E"), (0, "A"))
|
||||
G.add_edge((1, "E"), (0, "C"))
|
||||
G.add_edge((0, "C"), (1, "D"))
|
||||
G.add_edge((0, "C"), (1, "I"))
|
||||
G.add_edge((0, "C"), (1, "G"))
|
||||
G.add_edge((0, "C"), (1, "H"))
|
||||
G.add_edge((1, "D"), (0, "A"))
|
||||
G.add_edge((1, "I"), (0, "A"))
|
||||
G.add_edge((1, "I"), (0, "E"))
|
||||
G.add_edge((0, "A"), (1, "G"))
|
||||
G.add_edge((0, "A"), (1, "H"))
|
||||
G.add_edge((0, "E"), (1, "G"))
|
||||
G.add_edge((0, "E"), (1, "H"))
|
||||
self.disconnected_graph = G
|
||||
|
||||
def check_match(self, matching):
|
||||
"""Asserts that the matching is what we expect from the bipartite graph
|
||||
constructed in the :meth:`setup` fixture.
|
||||
|
||||
"""
|
||||
# For the sake of brevity, rename `matching` to `M`.
|
||||
M = matching
|
||||
matched_vertices = frozenset(itertools.chain(*M.items()))
|
||||
# Assert that the maximum number of vertices (10) is matched.
|
||||
assert matched_vertices == frozenset(range(12)) - {1, 10}
|
||||
# Assert that no vertex appears in two edges, or in other words, that
|
||||
# the matching (u, v) and (v, u) both appear in the matching
|
||||
# dictionary.
|
||||
assert all(u == M[M[u]] for u in range(12) if u in M)
|
||||
|
||||
def check_vertex_cover(self, vertices):
|
||||
"""Asserts that the given set of vertices is the vertex cover we
|
||||
expected from the bipartite graph constructed in the :meth:`setup`
|
||||
fixture.
|
||||
|
||||
"""
|
||||
# By Konig's theorem, the number of edges in a maximum matching equals
|
||||
# the number of vertices in a minimum vertex cover.
|
||||
assert len(vertices) == 5
|
||||
# Assert that the set is truly a vertex cover.
|
||||
for (u, v) in self.graph.edges():
|
||||
assert u in vertices or v in vertices
|
||||
# TODO Assert that the vertices are the correct ones.
|
||||
|
||||
def test_eppstein_matching(self):
|
||||
"""Tests that David Eppstein's implementation of the Hopcroft--Karp
|
||||
algorithm produces a maximum cardinality matching.
|
||||
|
||||
"""
|
||||
self.check_match(eppstein_matching(self.graph, self.top_nodes))
|
||||
|
||||
def test_hopcroft_karp_matching(self):
|
||||
"""Tests that the Hopcroft--Karp algorithm produces a maximum
|
||||
cardinality matching in a bipartite graph.
|
||||
|
||||
"""
|
||||
self.check_match(hopcroft_karp_matching(self.graph, self.top_nodes))
|
||||
|
||||
def test_to_vertex_cover(self):
|
||||
"""Test for converting a maximum matching to a minimum vertex cover."""
|
||||
matching = maximum_matching(self.graph, self.top_nodes)
|
||||
vertex_cover = to_vertex_cover(self.graph, matching, self.top_nodes)
|
||||
self.check_vertex_cover(vertex_cover)
|
||||
|
||||
def test_eppstein_matching_simple(self):
|
||||
match = eppstein_matching(self.simple_graph)
|
||||
assert match == self.simple_solution
|
||||
|
||||
def test_hopcroft_karp_matching_simple(self):
|
||||
match = hopcroft_karp_matching(self.simple_graph)
|
||||
assert match == self.simple_solution
|
||||
|
||||
def test_eppstein_matching_disconnected(self):
|
||||
with pytest.raises(nx.AmbiguousSolution):
|
||||
match = eppstein_matching(self.disconnected_graph)
|
||||
|
||||
def test_hopcroft_karp_matching_disconnected(self):
|
||||
with pytest.raises(nx.AmbiguousSolution):
|
||||
match = hopcroft_karp_matching(self.disconnected_graph)
|
||||
|
||||
def test_issue_2127(self):
|
||||
"""Test from issue 2127"""
|
||||
# Build the example DAG
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("A", "C")
|
||||
G.add_edge("A", "B")
|
||||
G.add_edge("C", "E")
|
||||
G.add_edge("C", "D")
|
||||
G.add_edge("E", "G")
|
||||
G.add_edge("E", "F")
|
||||
G.add_edge("G", "I")
|
||||
G.add_edge("G", "H")
|
||||
|
||||
tc = nx.transitive_closure(G)
|
||||
btc = nx.Graph()
|
||||
|
||||
# Create a bipartite graph based on the transitive closure of G
|
||||
for v in tc.nodes():
|
||||
btc.add_node((0, v))
|
||||
btc.add_node((1, v))
|
||||
|
||||
for u, v in tc.edges():
|
||||
btc.add_edge((0, u), (1, v))
|
||||
|
||||
top_nodes = {n for n in btc if n[0] == 0}
|
||||
matching = hopcroft_karp_matching(btc, top_nodes)
|
||||
vertex_cover = to_vertex_cover(btc, matching, top_nodes)
|
||||
independent_set = set(G) - {v for _, v in vertex_cover}
|
||||
assert {"B", "D", "F", "I", "H"} == independent_set
|
||||
|
||||
def test_vertex_cover_issue_2384(self):
|
||||
G = nx.Graph([(0, 3), (1, 3), (1, 4), (2, 3)])
|
||||
matching = maximum_matching(G)
|
||||
vertex_cover = to_vertex_cover(G, matching)
|
||||
for u, v in G.edges():
|
||||
assert u in vertex_cover or v in vertex_cover
|
||||
|
||||
def test_unorderable_nodes(self):
|
||||
a = object()
|
||||
b = object()
|
||||
c = object()
|
||||
d = object()
|
||||
e = object()
|
||||
G = nx.Graph([(a, d), (b, d), (b, e), (c, d)])
|
||||
matching = maximum_matching(G)
|
||||
vertex_cover = to_vertex_cover(G, matching)
|
||||
for u, v in G.edges():
|
||||
assert u in vertex_cover or v in vertex_cover
|
||||
|
||||
|
||||
def test_eppstein_matching():
|
||||
"""Test in accordance to issue #1927"""
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from(["a", 2, 3, 4], bipartite=0)
|
||||
G.add_nodes_from([1, "b", "c"], bipartite=1)
|
||||
G.add_edges_from([("a", 1), ("a", "b"), (2, "b"), (2, "c"), (3, "c"), (4, 1)])
|
||||
matching = eppstein_matching(G)
|
||||
assert len(matching) == len(maximum_matching(G))
|
||||
assert all(x in set(matching.keys()) for x in set(matching.values()))
|
||||
|
||||
|
||||
class TestMinimumWeightFullMatching:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
global scipy
|
||||
scipy = pytest.importorskip("scipy")
|
||||
|
||||
def test_minimum_weight_full_matching_incomplete_graph(self):
|
||||
B = nx.Graph()
|
||||
B.add_nodes_from([1, 2], bipartite=0)
|
||||
B.add_nodes_from([3, 4], bipartite=1)
|
||||
B.add_edge(1, 4, weight=100)
|
||||
B.add_edge(2, 3, weight=100)
|
||||
B.add_edge(2, 4, weight=50)
|
||||
matching = minimum_weight_full_matching(B)
|
||||
assert matching == {1: 4, 2: 3, 4: 1, 3: 2}
|
||||
|
||||
def test_minimum_weight_full_matching_with_no_full_matching(self):
|
||||
B = nx.Graph()
|
||||
B.add_nodes_from([1, 2, 3], bipartite=0)
|
||||
B.add_nodes_from([4, 5, 6], bipartite=1)
|
||||
B.add_edge(1, 4, weight=100)
|
||||
B.add_edge(2, 4, weight=100)
|
||||
B.add_edge(3, 4, weight=50)
|
||||
B.add_edge(3, 5, weight=50)
|
||||
B.add_edge(3, 6, weight=50)
|
||||
with pytest.raises(ValueError):
|
||||
minimum_weight_full_matching(B)
|
||||
|
||||
def test_minimum_weight_full_matching_square(self):
|
||||
G = nx.complete_bipartite_graph(3, 3)
|
||||
G.add_edge(0, 3, weight=400)
|
||||
G.add_edge(0, 4, weight=150)
|
||||
G.add_edge(0, 5, weight=400)
|
||||
G.add_edge(1, 3, weight=400)
|
||||
G.add_edge(1, 4, weight=450)
|
||||
G.add_edge(1, 5, weight=600)
|
||||
G.add_edge(2, 3, weight=300)
|
||||
G.add_edge(2, 4, weight=225)
|
||||
G.add_edge(2, 5, weight=300)
|
||||
matching = minimum_weight_full_matching(G)
|
||||
assert matching == {0: 4, 1: 3, 2: 5, 4: 0, 3: 1, 5: 2}
|
||||
|
||||
def test_minimum_weight_full_matching_smaller_left(self):
|
||||
G = nx.complete_bipartite_graph(3, 4)
|
||||
G.add_edge(0, 3, weight=400)
|
||||
G.add_edge(0, 4, weight=150)
|
||||
G.add_edge(0, 5, weight=400)
|
||||
G.add_edge(0, 6, weight=1)
|
||||
G.add_edge(1, 3, weight=400)
|
||||
G.add_edge(1, 4, weight=450)
|
||||
G.add_edge(1, 5, weight=600)
|
||||
G.add_edge(1, 6, weight=2)
|
||||
G.add_edge(2, 3, weight=300)
|
||||
G.add_edge(2, 4, weight=225)
|
||||
G.add_edge(2, 5, weight=290)
|
||||
G.add_edge(2, 6, weight=3)
|
||||
matching = minimum_weight_full_matching(G)
|
||||
assert matching == {0: 4, 1: 6, 2: 5, 4: 0, 5: 2, 6: 1}
|
||||
|
||||
def test_minimum_weight_full_matching_smaller_top_nodes_right(self):
|
||||
G = nx.complete_bipartite_graph(3, 4)
|
||||
G.add_edge(0, 3, weight=400)
|
||||
G.add_edge(0, 4, weight=150)
|
||||
G.add_edge(0, 5, weight=400)
|
||||
G.add_edge(0, 6, weight=1)
|
||||
G.add_edge(1, 3, weight=400)
|
||||
G.add_edge(1, 4, weight=450)
|
||||
G.add_edge(1, 5, weight=600)
|
||||
G.add_edge(1, 6, weight=2)
|
||||
G.add_edge(2, 3, weight=300)
|
||||
G.add_edge(2, 4, weight=225)
|
||||
G.add_edge(2, 5, weight=290)
|
||||
G.add_edge(2, 6, weight=3)
|
||||
matching = minimum_weight_full_matching(G, top_nodes=[3, 4, 5, 6])
|
||||
assert matching == {0: 4, 1: 6, 2: 5, 4: 0, 5: 2, 6: 1}
|
||||
|
||||
def test_minimum_weight_full_matching_smaller_right(self):
|
||||
G = nx.complete_bipartite_graph(4, 3)
|
||||
G.add_edge(0, 4, weight=400)
|
||||
G.add_edge(0, 5, weight=400)
|
||||
G.add_edge(0, 6, weight=300)
|
||||
G.add_edge(1, 4, weight=150)
|
||||
G.add_edge(1, 5, weight=450)
|
||||
G.add_edge(1, 6, weight=225)
|
||||
G.add_edge(2, 4, weight=400)
|
||||
G.add_edge(2, 5, weight=600)
|
||||
G.add_edge(2, 6, weight=290)
|
||||
G.add_edge(3, 4, weight=1)
|
||||
G.add_edge(3, 5, weight=2)
|
||||
G.add_edge(3, 6, weight=3)
|
||||
matching = minimum_weight_full_matching(G)
|
||||
assert matching == {1: 4, 2: 6, 3: 5, 4: 1, 5: 3, 6: 2}
|
||||
|
||||
def test_minimum_weight_full_matching_negative_weights(self):
|
||||
G = nx.complete_bipartite_graph(2, 2)
|
||||
G.add_edge(0, 2, weight=-2)
|
||||
G.add_edge(0, 3, weight=0.2)
|
||||
G.add_edge(1, 2, weight=-2)
|
||||
G.add_edge(1, 3, weight=0.3)
|
||||
matching = minimum_weight_full_matching(G)
|
||||
assert matching == {0: 3, 1: 2, 2: 1, 3: 0}
|
||||
|
||||
def test_minimum_weight_full_matching_different_weight_key(self):
|
||||
G = nx.complete_bipartite_graph(2, 2)
|
||||
G.add_edge(0, 2, mass=2)
|
||||
G.add_edge(0, 3, mass=0.2)
|
||||
G.add_edge(1, 2, mass=1)
|
||||
G.add_edge(1, 3, mass=2)
|
||||
matching = minimum_weight_full_matching(G, weight="mass")
|
||||
assert matching == {0: 3, 1: 2, 2: 1, 3: 0}
|
|
@ -0,0 +1,79 @@
|
|||
import pytest
|
||||
|
||||
np = pytest.importorskip("numpy")
|
||||
sp = pytest.importorskip("scipy")
|
||||
sparse = pytest.importorskip("scipy.sparse")
|
||||
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms import bipartite
|
||||
from networkx.testing.utils import assert_edges_equal
|
||||
|
||||
|
||||
class TestBiadjacencyMatrix:
|
||||
def test_biadjacency_matrix_weight(self):
|
||||
G = nx.path_graph(5)
|
||||
G.add_edge(0, 1, weight=2, other=4)
|
||||
X = [1, 3]
|
||||
Y = [0, 2, 4]
|
||||
M = bipartite.biadjacency_matrix(G, X, weight="weight")
|
||||
assert M[0, 0] == 2
|
||||
M = bipartite.biadjacency_matrix(G, X, weight="other")
|
||||
assert M[0, 0] == 4
|
||||
|
||||
def test_biadjacency_matrix(self):
|
||||
tops = [2, 5, 10]
|
||||
bots = [5, 10, 15]
|
||||
for i in range(len(tops)):
|
||||
G = bipartite.random_graph(tops[i], bots[i], 0.2)
|
||||
top = [n for n, d in G.nodes(data=True) if d["bipartite"] == 0]
|
||||
M = bipartite.biadjacency_matrix(G, top)
|
||||
assert M.shape[0] == tops[i]
|
||||
assert M.shape[1] == bots[i]
|
||||
|
||||
def test_biadjacency_matrix_order(self):
|
||||
G = nx.path_graph(5)
|
||||
G.add_edge(0, 1, weight=2)
|
||||
X = [3, 1]
|
||||
Y = [4, 2, 0]
|
||||
M = bipartite.biadjacency_matrix(G, X, Y, weight="weight")
|
||||
assert M[1, 2] == 2
|
||||
|
||||
def test_null_graph(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
bipartite.biadjacency_matrix(nx.Graph(), [])
|
||||
|
||||
def test_empty_graph(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
bipartite.biadjacency_matrix(nx.Graph([(1, 0)]), [])
|
||||
|
||||
def test_duplicate_row(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
bipartite.biadjacency_matrix(nx.Graph([(1, 0)]), [1, 1])
|
||||
|
||||
def test_duplicate_col(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
bipartite.biadjacency_matrix(nx.Graph([(1, 0)]), [0], [1, 1])
|
||||
|
||||
def test_format_keyword(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
bipartite.biadjacency_matrix(nx.Graph([(1, 0)]), [0], format="foo")
|
||||
|
||||
def test_from_biadjacency_roundtrip(self):
|
||||
B1 = nx.path_graph(5)
|
||||
M = bipartite.biadjacency_matrix(B1, [0, 2, 4])
|
||||
B2 = bipartite.from_biadjacency_matrix(M)
|
||||
assert nx.is_isomorphic(B1, B2)
|
||||
|
||||
def test_from_biadjacency_weight(self):
|
||||
M = sparse.csc_matrix([[1, 2], [0, 3]])
|
||||
B = bipartite.from_biadjacency_matrix(M)
|
||||
assert_edges_equal(B.edges(), [(0, 2), (0, 3), (1, 3)])
|
||||
B = bipartite.from_biadjacency_matrix(M, edge_attribute="weight")
|
||||
e = [(0, 2, {"weight": 1}), (0, 3, {"weight": 2}), (1, 3, {"weight": 3})]
|
||||
assert_edges_equal(B.edges(data=True), e)
|
||||
|
||||
def test_from_biadjacency_multigraph(self):
|
||||
M = sparse.csc_matrix([[1, 2], [0, 3]])
|
||||
B = bipartite.from_biadjacency_matrix(M, create_using=nx.MultiGraph())
|
||||
assert_edges_equal(B.edges(), [(0, 2), (0, 3), (0, 3), (1, 3), (1, 3), (1, 3)])
|
|
@ -0,0 +1,392 @@
|
|||
import networkx as nx
|
||||
from networkx.algorithms import bipartite
|
||||
from networkx.testing import assert_edges_equal, assert_nodes_equal
|
||||
|
||||
|
||||
class TestBipartiteProject:
|
||||
def test_path_projected_graph(self):
|
||||
G = nx.path_graph(4)
|
||||
P = bipartite.projected_graph(G, [1, 3])
|
||||
assert_nodes_equal(list(P), [1, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 3)])
|
||||
P = bipartite.projected_graph(G, [0, 2])
|
||||
assert_nodes_equal(list(P), [0, 2])
|
||||
assert_edges_equal(list(P.edges()), [(0, 2)])
|
||||
|
||||
def test_path_projected_properties_graph(self):
|
||||
G = nx.path_graph(4)
|
||||
G.add_node(1, name="one")
|
||||
G.add_node(2, name="two")
|
||||
P = bipartite.projected_graph(G, [1, 3])
|
||||
assert_nodes_equal(list(P), [1, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 3)])
|
||||
assert P.nodes[1]["name"] == G.nodes[1]["name"]
|
||||
P = bipartite.projected_graph(G, [0, 2])
|
||||
assert_nodes_equal(list(P), [0, 2])
|
||||
assert_edges_equal(list(P.edges()), [(0, 2)])
|
||||
assert P.nodes[2]["name"] == G.nodes[2]["name"]
|
||||
|
||||
def test_path_collaboration_projected_graph(self):
|
||||
G = nx.path_graph(4)
|
||||
P = bipartite.collaboration_weighted_projected_graph(G, [1, 3])
|
||||
assert_nodes_equal(list(P), [1, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 3)])
|
||||
P[1][3]["weight"] = 1
|
||||
P = bipartite.collaboration_weighted_projected_graph(G, [0, 2])
|
||||
assert_nodes_equal(list(P), [0, 2])
|
||||
assert_edges_equal(list(P.edges()), [(0, 2)])
|
||||
P[0][2]["weight"] = 1
|
||||
|
||||
def test_directed_path_collaboration_projected_graph(self):
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, range(4))
|
||||
P = bipartite.collaboration_weighted_projected_graph(G, [1, 3])
|
||||
assert_nodes_equal(list(P), [1, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 3)])
|
||||
P[1][3]["weight"] = 1
|
||||
P = bipartite.collaboration_weighted_projected_graph(G, [0, 2])
|
||||
assert_nodes_equal(list(P), [0, 2])
|
||||
assert_edges_equal(list(P.edges()), [(0, 2)])
|
||||
P[0][2]["weight"] = 1
|
||||
|
||||
def test_path_weighted_projected_graph(self):
|
||||
G = nx.path_graph(4)
|
||||
P = bipartite.weighted_projected_graph(G, [1, 3])
|
||||
assert_nodes_equal(list(P), [1, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 3)])
|
||||
P[1][3]["weight"] = 1
|
||||
P = bipartite.weighted_projected_graph(G, [0, 2])
|
||||
assert_nodes_equal(list(P), [0, 2])
|
||||
assert_edges_equal(list(P.edges()), [(0, 2)])
|
||||
P[0][2]["weight"] = 1
|
||||
|
||||
def test_path_weighted_projected_directed_graph(self):
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, range(4))
|
||||
P = bipartite.weighted_projected_graph(G, [1, 3])
|
||||
assert_nodes_equal(list(P), [1, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 3)])
|
||||
P[1][3]["weight"] = 1
|
||||
P = bipartite.weighted_projected_graph(G, [0, 2])
|
||||
assert_nodes_equal(list(P), [0, 2])
|
||||
assert_edges_equal(list(P.edges()), [(0, 2)])
|
||||
P[0][2]["weight"] = 1
|
||||
|
||||
def test_star_projected_graph(self):
|
||||
G = nx.star_graph(3)
|
||||
P = bipartite.projected_graph(G, [1, 2, 3])
|
||||
assert_nodes_equal(list(P), [1, 2, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 2), (1, 3), (2, 3)])
|
||||
P = bipartite.weighted_projected_graph(G, [1, 2, 3])
|
||||
assert_nodes_equal(list(P), [1, 2, 3])
|
||||
assert_edges_equal(list(P.edges()), [(1, 2), (1, 3), (2, 3)])
|
||||
|
||||
P = bipartite.projected_graph(G, [0])
|
||||
assert_nodes_equal(list(P), [0])
|
||||
assert_edges_equal(list(P.edges()), [])
|
||||
|
||||
def test_project_multigraph(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge("a", 1)
|
||||
G.add_edge("b", 1)
|
||||
G.add_edge("a", 2)
|
||||
G.add_edge("b", 2)
|
||||
P = bipartite.projected_graph(G, "ab")
|
||||
assert_edges_equal(list(P.edges()), [("a", "b")])
|
||||
P = bipartite.weighted_projected_graph(G, "ab")
|
||||
assert_edges_equal(list(P.edges()), [("a", "b")])
|
||||
P = bipartite.projected_graph(G, "ab", multigraph=True)
|
||||
assert_edges_equal(list(P.edges()), [("a", "b"), ("a", "b")])
|
||||
|
||||
def test_project_collaboration(self):
|
||||
G = nx.Graph()
|
||||
G.add_edge("a", 1)
|
||||
G.add_edge("b", 1)
|
||||
G.add_edge("b", 2)
|
||||
G.add_edge("c", 2)
|
||||
G.add_edge("c", 3)
|
||||
G.add_edge("c", 4)
|
||||
G.add_edge("b", 4)
|
||||
P = bipartite.collaboration_weighted_projected_graph(G, "abc")
|
||||
assert P["a"]["b"]["weight"] == 1
|
||||
assert P["b"]["c"]["weight"] == 2
|
||||
|
||||
def test_directed_projection(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("A", 1)
|
||||
G.add_edge(1, "B")
|
||||
G.add_edge("A", 2)
|
||||
G.add_edge("B", 2)
|
||||
P = bipartite.projected_graph(G, "AB")
|
||||
assert_edges_equal(list(P.edges()), [("A", "B")])
|
||||
P = bipartite.weighted_projected_graph(G, "AB")
|
||||
assert_edges_equal(list(P.edges()), [("A", "B")])
|
||||
assert P["A"]["B"]["weight"] == 1
|
||||
|
||||
P = bipartite.projected_graph(G, "AB", multigraph=True)
|
||||
assert_edges_equal(list(P.edges()), [("A", "B")])
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("A", 1)
|
||||
G.add_edge(1, "B")
|
||||
G.add_edge("A", 2)
|
||||
G.add_edge(2, "B")
|
||||
P = bipartite.projected_graph(G, "AB")
|
||||
assert_edges_equal(list(P.edges()), [("A", "B")])
|
||||
P = bipartite.weighted_projected_graph(G, "AB")
|
||||
assert_edges_equal(list(P.edges()), [("A", "B")])
|
||||
assert P["A"]["B"]["weight"] == 2
|
||||
|
||||
P = bipartite.projected_graph(G, "AB", multigraph=True)
|
||||
assert_edges_equal(list(P.edges()), [("A", "B"), ("A", "B")])
|
||||
|
||||
|
||||
class TestBipartiteWeightedProjection:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
# Tore Opsahl's example
|
||||
# http://toreopsahl.com/2009/05/01/projecting-two-mode-networks-onto-weighted-one-mode-networks/
|
||||
cls.G = nx.Graph()
|
||||
cls.G.add_edge("A", 1)
|
||||
cls.G.add_edge("A", 2)
|
||||
cls.G.add_edge("B", 1)
|
||||
cls.G.add_edge("B", 2)
|
||||
cls.G.add_edge("B", 3)
|
||||
cls.G.add_edge("B", 4)
|
||||
cls.G.add_edge("B", 5)
|
||||
cls.G.add_edge("C", 1)
|
||||
cls.G.add_edge("D", 3)
|
||||
cls.G.add_edge("E", 4)
|
||||
cls.G.add_edge("E", 5)
|
||||
cls.G.add_edge("E", 6)
|
||||
cls.G.add_edge("F", 6)
|
||||
# Graph based on figure 6 from Newman (2001)
|
||||
cls.N = nx.Graph()
|
||||
cls.N.add_edge("A", 1)
|
||||
cls.N.add_edge("A", 2)
|
||||
cls.N.add_edge("A", 3)
|
||||
cls.N.add_edge("B", 1)
|
||||
cls.N.add_edge("B", 2)
|
||||
cls.N.add_edge("B", 3)
|
||||
cls.N.add_edge("C", 1)
|
||||
cls.N.add_edge("D", 1)
|
||||
cls.N.add_edge("E", 3)
|
||||
|
||||
def test_project_weighted_shared(self):
|
||||
edges = [
|
||||
("A", "B", 2),
|
||||
("A", "C", 1),
|
||||
("B", "C", 1),
|
||||
("B", "D", 1),
|
||||
("B", "E", 2),
|
||||
("E", "F", 1),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.weighted_projected_graph(self.G, "ABCDEF")
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
edges = [
|
||||
("A", "B", 3),
|
||||
("A", "E", 1),
|
||||
("A", "C", 1),
|
||||
("A", "D", 1),
|
||||
("B", "E", 1),
|
||||
("B", "C", 1),
|
||||
("B", "D", 1),
|
||||
("C", "D", 1),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.weighted_projected_graph(self.N, "ABCDE")
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
def test_project_weighted_newman(self):
|
||||
edges = [
|
||||
("A", "B", 1.5),
|
||||
("A", "C", 0.5),
|
||||
("B", "C", 0.5),
|
||||
("B", "D", 1),
|
||||
("B", "E", 2),
|
||||
("E", "F", 1),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.collaboration_weighted_projected_graph(self.G, "ABCDEF")
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
edges = [
|
||||
("A", "B", 11 / 6.0),
|
||||
("A", "E", 1 / 2.0),
|
||||
("A", "C", 1 / 3.0),
|
||||
("A", "D", 1 / 3.0),
|
||||
("B", "E", 1 / 2.0),
|
||||
("B", "C", 1 / 3.0),
|
||||
("B", "D", 1 / 3.0),
|
||||
("C", "D", 1 / 3.0),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.collaboration_weighted_projected_graph(self.N, "ABCDE")
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
def test_project_weighted_ratio(self):
|
||||
edges = [
|
||||
("A", "B", 2 / 6.0),
|
||||
("A", "C", 1 / 6.0),
|
||||
("B", "C", 1 / 6.0),
|
||||
("B", "D", 1 / 6.0),
|
||||
("B", "E", 2 / 6.0),
|
||||
("E", "F", 1 / 6.0),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.weighted_projected_graph(self.G, "ABCDEF", ratio=True)
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
edges = [
|
||||
("A", "B", 3 / 3.0),
|
||||
("A", "E", 1 / 3.0),
|
||||
("A", "C", 1 / 3.0),
|
||||
("A", "D", 1 / 3.0),
|
||||
("B", "E", 1 / 3.0),
|
||||
("B", "C", 1 / 3.0),
|
||||
("B", "D", 1 / 3.0),
|
||||
("C", "D", 1 / 3.0),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.weighted_projected_graph(self.N, "ABCDE", ratio=True)
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
def test_project_weighted_overlap(self):
|
||||
edges = [
|
||||
("A", "B", 2 / 2.0),
|
||||
("A", "C", 1 / 1.0),
|
||||
("B", "C", 1 / 1.0),
|
||||
("B", "D", 1 / 1.0),
|
||||
("B", "E", 2 / 3.0),
|
||||
("E", "F", 1 / 1.0),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.overlap_weighted_projected_graph(self.G, "ABCDEF", jaccard=False)
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
edges = [
|
||||
("A", "B", 3 / 3.0),
|
||||
("A", "E", 1 / 1.0),
|
||||
("A", "C", 1 / 1.0),
|
||||
("A", "D", 1 / 1.0),
|
||||
("B", "E", 1 / 1.0),
|
||||
("B", "C", 1 / 1.0),
|
||||
("B", "D", 1 / 1.0),
|
||||
("C", "D", 1 / 1.0),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.overlap_weighted_projected_graph(self.N, "ABCDE", jaccard=False)
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
def test_project_weighted_jaccard(self):
|
||||
edges = [
|
||||
("A", "B", 2 / 5.0),
|
||||
("A", "C", 1 / 2.0),
|
||||
("B", "C", 1 / 5.0),
|
||||
("B", "D", 1 / 5.0),
|
||||
("B", "E", 2 / 6.0),
|
||||
("E", "F", 1 / 3.0),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.overlap_weighted_projected_graph(self.G, "ABCDEF")
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in list(P.edges()):
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
edges = [
|
||||
("A", "B", 3 / 3.0),
|
||||
("A", "E", 1 / 3.0),
|
||||
("A", "C", 1 / 3.0),
|
||||
("A", "D", 1 / 3.0),
|
||||
("B", "E", 1 / 3.0),
|
||||
("B", "C", 1 / 3.0),
|
||||
("B", "D", 1 / 3.0),
|
||||
("C", "D", 1 / 1.0),
|
||||
]
|
||||
Panswer = nx.Graph()
|
||||
Panswer.add_weighted_edges_from(edges)
|
||||
P = bipartite.overlap_weighted_projected_graph(self.N, "ABCDE")
|
||||
assert_edges_equal(list(P.edges()), Panswer.edges())
|
||||
for u, v in P.edges():
|
||||
assert P[u][v]["weight"] == Panswer[u][v]["weight"]
|
||||
|
||||
def test_generic_weighted_projected_graph_simple(self):
|
||||
def shared(G, u, v):
|
||||
return len(set(G[u]) & set(G[v]))
|
||||
|
||||
B = nx.path_graph(5)
|
||||
G = bipartite.generic_weighted_projected_graph(
|
||||
B, [0, 2, 4], weight_function=shared
|
||||
)
|
||||
assert_nodes_equal(list(G), [0, 2, 4])
|
||||
assert_edges_equal(
|
||||
list(list(G.edges(data=True))),
|
||||
[(0, 2, {"weight": 1}), (2, 4, {"weight": 1})],
|
||||
)
|
||||
|
||||
G = bipartite.generic_weighted_projected_graph(B, [0, 2, 4])
|
||||
assert_nodes_equal(list(G), [0, 2, 4])
|
||||
assert_edges_equal(
|
||||
list(list(G.edges(data=True))),
|
||||
[(0, 2, {"weight": 1}), (2, 4, {"weight": 1})],
|
||||
)
|
||||
B = nx.DiGraph()
|
||||
nx.add_path(B, range(5))
|
||||
G = bipartite.generic_weighted_projected_graph(B, [0, 2, 4])
|
||||
assert_nodes_equal(list(G), [0, 2, 4])
|
||||
assert_edges_equal(
|
||||
list(G.edges(data=True)), [(0, 2, {"weight": 1}), (2, 4, {"weight": 1})]
|
||||
)
|
||||
|
||||
def test_generic_weighted_projected_graph_custom(self):
|
||||
def jaccard(G, u, v):
|
||||
unbrs = set(G[u])
|
||||
vnbrs = set(G[v])
|
||||
return float(len(unbrs & vnbrs)) / len(unbrs | vnbrs)
|
||||
|
||||
def my_weight(G, u, v, weight="weight"):
|
||||
w = 0
|
||||
for nbr in set(G[u]) & set(G[v]):
|
||||
w += G.edges[u, nbr].get(weight, 1) + G.edges[v, nbr].get(weight, 1)
|
||||
return w
|
||||
|
||||
B = nx.bipartite.complete_bipartite_graph(2, 2)
|
||||
for i, (u, v) in enumerate(B.edges()):
|
||||
B.edges[u, v]["weight"] = i + 1
|
||||
G = bipartite.generic_weighted_projected_graph(
|
||||
B, [0, 1], weight_function=jaccard
|
||||
)
|
||||
assert_edges_equal(list(G.edges(data=True)), [(0, 1, {"weight": 1.0})])
|
||||
G = bipartite.generic_weighted_projected_graph(
|
||||
B, [0, 1], weight_function=my_weight
|
||||
)
|
||||
assert_edges_equal(list(G.edges(data=True)), [(0, 1, {"weight": 10})])
|
||||
G = bipartite.generic_weighted_projected_graph(B, [0, 1])
|
||||
assert_edges_equal(list(G.edges(data=True)), [(0, 1, {"weight": 2})])
|
|
@ -0,0 +1,33 @@
|
|||
"""Unit tests for the :mod:`networkx.algorithms.bipartite.redundancy` module.
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from networkx import cycle_graph
|
||||
from networkx import NetworkXError
|
||||
from networkx.algorithms.bipartite import complete_bipartite_graph
|
||||
from networkx.algorithms.bipartite import node_redundancy
|
||||
|
||||
|
||||
def test_no_redundant_nodes():
|
||||
G = complete_bipartite_graph(2, 2)
|
||||
rc = node_redundancy(G)
|
||||
assert all(redundancy == 1 for redundancy in rc.values())
|
||||
|
||||
|
||||
def test_redundant_nodes():
|
||||
G = cycle_graph(6)
|
||||
edge = {0, 3}
|
||||
G.add_edge(*edge)
|
||||
redundancy = node_redundancy(G)
|
||||
for v in edge:
|
||||
assert redundancy[v] == 2 / 3
|
||||
for v in set(G) - edge:
|
||||
assert redundancy[v] == 1
|
||||
|
||||
|
||||
def test_not_enough_neighbors():
|
||||
with pytest.raises(NetworkXError):
|
||||
G = complete_bipartite_graph(1, 2)
|
||||
node_redundancy(G)
|
|
@ -0,0 +1,85 @@
|
|||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.bipartite import spectral_bipartivity as sb
|
||||
from networkx.testing import almost_equal
|
||||
|
||||
# Examples from Figure 1
|
||||
# E. Estrada and J. A. Rodríguez-Velázquez, "Spectral measures of
|
||||
# bipartivity in complex networks", PhysRev E 72, 046105 (2005)
|
||||
|
||||
|
||||
class TestSpectralBipartivity:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
global scipy
|
||||
scipy = pytest.importorskip("scipy")
|
||||
|
||||
def test_star_like(self):
|
||||
# star-like
|
||||
|
||||
G = nx.star_graph(2)
|
||||
G.add_edge(1, 2)
|
||||
assert almost_equal(sb(G), 0.843, places=3)
|
||||
|
||||
G = nx.star_graph(3)
|
||||
G.add_edge(1, 2)
|
||||
assert almost_equal(sb(G), 0.871, places=3)
|
||||
|
||||
G = nx.star_graph(4)
|
||||
G.add_edge(1, 2)
|
||||
assert almost_equal(sb(G), 0.890, places=3)
|
||||
|
||||
def test_k23_like(self):
|
||||
# K2,3-like
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(0, 1)
|
||||
assert almost_equal(sb(G), 0.769, places=3)
|
||||
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(2, 4)
|
||||
assert almost_equal(sb(G), 0.829, places=3)
|
||||
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(2, 4)
|
||||
G.add_edge(3, 4)
|
||||
assert almost_equal(sb(G), 0.731, places=3)
|
||||
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(0, 1)
|
||||
G.add_edge(2, 4)
|
||||
assert almost_equal(sb(G), 0.692, places=3)
|
||||
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(2, 4)
|
||||
G.add_edge(3, 4)
|
||||
G.add_edge(0, 1)
|
||||
assert almost_equal(sb(G), 0.645, places=3)
|
||||
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(2, 4)
|
||||
G.add_edge(3, 4)
|
||||
G.add_edge(2, 3)
|
||||
assert almost_equal(sb(G), 0.645, places=3)
|
||||
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(2, 4)
|
||||
G.add_edge(3, 4)
|
||||
G.add_edge(2, 3)
|
||||
G.add_edge(0, 1)
|
||||
assert almost_equal(sb(G), 0.597, places=3)
|
||||
|
||||
def test_single_nodes(self):
|
||||
|
||||
# single nodes
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(2, 4)
|
||||
sbn = sb(G, nodes=[1, 2])
|
||||
assert almost_equal(sbn[1], 0.85, places=2)
|
||||
assert almost_equal(sbn[2], 0.77, places=2)
|
||||
|
||||
G = nx.complete_bipartite_graph(2, 3)
|
||||
G.add_edge(0, 1)
|
||||
sbn = sb(G, nodes=[1, 2])
|
||||
assert almost_equal(sbn[1], 0.73, places=2)
|
||||
assert almost_equal(sbn[2], 0.82, places=2)
|
Loading…
Add table
Add a link
Reference in a new issue