419 lines
12 KiB
Python
419 lines
12 KiB
Python
import easygraph as eg
|
|
import pytest
|
|
|
|
|
|
class TestClustering:
|
|
@classmethod
|
|
def setup_class(cls):
|
|
pytest.importorskip("numpy")
|
|
|
|
def test_clustering(self):
|
|
G = eg.DiGraph()
|
|
G.add_edge("1", "2", weight=16)
|
|
G.add_edge("2", "3", weight=16)
|
|
G.add_edge("4", "3", weight=16)
|
|
G.add_edge("3", "4", weight=23)
|
|
G.add_edge("3", "5", weight=16)
|
|
G.add_edge("4", "2", weight=20)
|
|
print("clustering" in dir(eg))
|
|
assert eg.clustering(G) == {
|
|
"1": 0,
|
|
"2": 0.3333333333333333,
|
|
"3": 0.2,
|
|
"4": 0.5,
|
|
"5": 0,
|
|
}
|
|
|
|
def test_path(self):
|
|
G = eg.path_graph(10)
|
|
assert list(eg.clustering(G).values()) == [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
]
|
|
assert eg.clustering(G) == {
|
|
0: 0,
|
|
1: 0,
|
|
2: 0,
|
|
3: 0,
|
|
4: 0,
|
|
5: 0,
|
|
6: 0,
|
|
7: 0,
|
|
8: 0,
|
|
9: 0,
|
|
}
|
|
|
|
def test_k5(self):
|
|
G = eg.complete_graph(5)
|
|
assert list(eg.clustering(G).values()) == [1, 1, 1, 1, 1]
|
|
assert eg.average_clustering(G) == 1
|
|
G.remove_edge(1, 2)
|
|
assert list(eg.clustering(G).values()) == [
|
|
5 / 6,
|
|
1,
|
|
1,
|
|
5 / 6,
|
|
5 / 6,
|
|
]
|
|
assert eg.clustering(G, [1, 4]) == {1: 1, 4: 0.83333333333333337}
|
|
|
|
def test_k5_signed(self):
|
|
G = eg.complete_graph(5)
|
|
assert list(eg.clustering(G).values()) == [1, 1, 1, 1, 1]
|
|
assert eg.average_clustering(G) == 1
|
|
G.remove_edge(1, 2)
|
|
G.add_edge(0, 1, weight=-1)
|
|
assert list(eg.clustering(G, weight="weight").values()) == [
|
|
1 / 6,
|
|
-1 / 3,
|
|
1,
|
|
3 / 6,
|
|
3 / 6,
|
|
]
|
|
|
|
|
|
class TestDirectedClustering:
|
|
def test_clustering(self):
|
|
G = eg.DiGraph()
|
|
assert list(eg.clustering(G).values()) == []
|
|
assert eg.clustering(G) == {}
|
|
|
|
def test_path(self):
|
|
G = eg.path_graph(10, create_using=eg.DiGraph())
|
|
assert list(eg.clustering(G).values()) == [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
]
|
|
assert eg.clustering(G) == {
|
|
0: 0,
|
|
1: 0,
|
|
2: 0,
|
|
3: 0,
|
|
4: 0,
|
|
5: 0,
|
|
6: 0,
|
|
7: 0,
|
|
8: 0,
|
|
9: 0,
|
|
}
|
|
assert eg.clustering(G, 0) == 0
|
|
|
|
def test_k5(self):
|
|
G = eg.complete_graph(5, create_using=eg.DiGraph())
|
|
assert list(eg.clustering(G).values()) == [1, 1, 1, 1, 1]
|
|
assert eg.average_clustering(G) == 1
|
|
G.remove_edge(1, 2)
|
|
assert list(eg.clustering(G).values()) == [
|
|
11 / 12,
|
|
1,
|
|
1,
|
|
11 / 12,
|
|
11 / 12,
|
|
]
|
|
assert eg.clustering(G, [1, 4]) == {1: 1, 4: 11 / 12}
|
|
G.remove_edge(2, 1)
|
|
assert list(eg.clustering(G).values()) == [
|
|
5 / 6,
|
|
1,
|
|
1,
|
|
5 / 6,
|
|
5 / 6,
|
|
]
|
|
assert eg.clustering(G, [1, 4]) == {1: 1, 4: 0.83333333333333337}
|
|
assert eg.clustering(G, 4) == 5 / 6
|
|
|
|
def test_triangle_and_edge(self):
|
|
G = eg.empty_graph(range(3), eg.DiGraph())
|
|
G.add_edges_from(eg.pairwise(range(3), cyclic=True))
|
|
G.add_edge(0, 4)
|
|
assert eg.clustering(G)[0] == 1 / 6
|
|
|
|
|
|
class TestDirectedAverageClustering:
|
|
@classmethod
|
|
def setup_class(cls):
|
|
pytest.importorskip("numpy")
|
|
|
|
def test_empty(self):
|
|
G = eg.DiGraph()
|
|
with pytest.raises(ZeroDivisionError):
|
|
eg.average_clustering(G)
|
|
|
|
def test_average_clustering(self):
|
|
G = eg.empty_graph(range(3), eg.DiGraph())
|
|
G.add_edges_from(eg.pairwise(range(3), cyclic=True))
|
|
G.add_edge(2, 3)
|
|
assert eg.average_clustering(G) == (1 + 1 + 1 / 3) / 8
|
|
assert eg.average_clustering(G, count_zeros=True) == (1 + 1 + 1 / 3) / 8
|
|
assert eg.average_clustering(G, count_zeros=False) == (1 + 1 + 1 / 3) / 6
|
|
assert eg.average_clustering(G, [1, 2, 3]) == (1 + 1 / 3) / 6
|
|
assert eg.average_clustering(G, [1, 2, 3], count_zeros=True) == (1 + 1 / 3) / 6
|
|
assert eg.average_clustering(G, [1, 2, 3], count_zeros=False) == (1 + 1 / 3) / 4
|
|
|
|
|
|
class TestAverageClustering:
|
|
@classmethod
|
|
def setup_class(cls):
|
|
pytest.importorskip("numpy")
|
|
|
|
def test_empty(self):
|
|
G = eg.Graph()
|
|
with pytest.raises(ZeroDivisionError):
|
|
eg.average_clustering(G)
|
|
|
|
def test_average_clustering(self):
|
|
G = eg.complete_graph(3)
|
|
G.add_edge(2, 3)
|
|
|
|
assert eg.average_clustering(G) == (1 + 1 + 1 / 3) / 4
|
|
assert eg.average_clustering(G, count_zeros=True) == (1 + 1 + 1 / 3) / 4
|
|
assert eg.average_clustering(G, count_zeros=False) == (1 + 1 + 1 / 3) / 3
|
|
assert eg.average_clustering(G, [1, 2, 3]) == (1 + 1 / 3) / 3
|
|
assert eg.average_clustering(G, [1, 2, 3], count_zeros=True) == (1 + 1 / 3) / 3
|
|
assert eg.average_clustering(G, [1, 2, 3], count_zeros=False) == (1 + 1 / 3) / 2
|
|
|
|
def test_average_clustering_signed(self):
|
|
G = eg.complete_graph(3)
|
|
G.add_edge(2, 3)
|
|
G.add_edge(0, 1, weight=-1)
|
|
assert eg.average_clustering(G, weight="weight") == (-1 - 1 - 1 / 3) / 4
|
|
assert (
|
|
eg.average_clustering(G, weight="weight", count_zeros=True)
|
|
== (-1 - 1 - 1 / 3) / 4
|
|
)
|
|
assert (
|
|
eg.average_clustering(G, weight="weight", count_zeros=False)
|
|
== (-1 - 1 - 1 / 3) / 3
|
|
)
|
|
|
|
|
|
class TestDirectedWeightedClustering:
|
|
@classmethod
|
|
def setup_class(cls):
|
|
global np
|
|
np = pytest.importorskip("numpy")
|
|
|
|
def test_clustering(self):
|
|
G = eg.DiGraph()
|
|
assert list(eg.clustering(G, weight="weight").values()) == []
|
|
assert eg.clustering(G) == {}
|
|
|
|
def test_path(self):
|
|
G = eg.path_graph(10, create_using=eg.DiGraph())
|
|
print("type:", eg.clustering(G, weight="weight"))
|
|
assert list(eg.clustering(G, weight="weight").values()) == [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
]
|
|
assert eg.clustering(G, weight="weight") == {
|
|
0: 0,
|
|
1: 0,
|
|
2: 0,
|
|
3: 0,
|
|
4: 0,
|
|
5: 0,
|
|
6: 0,
|
|
7: 0,
|
|
8: 0,
|
|
9: 0,
|
|
}
|
|
|
|
def test_k5(self):
|
|
G = eg.complete_graph(5, create_using=eg.DiGraph())
|
|
assert list(eg.clustering(G, weight="weight").values()) == [1, 1, 1, 1, 1]
|
|
assert eg.average_clustering(G, weight="weight") == 1
|
|
G.remove_edge(1, 2)
|
|
assert list(eg.clustering(G, weight="weight").values()) == [
|
|
11 / 12,
|
|
1,
|
|
1,
|
|
11 / 12,
|
|
11 / 12,
|
|
]
|
|
assert eg.clustering(G, [1, 4], weight="weight") == {1: 1, 4: 11 / 12}
|
|
G.remove_edge(2, 1)
|
|
assert list(eg.clustering(G, weight="weight").values()) == [
|
|
5 / 6,
|
|
1,
|
|
1,
|
|
5 / 6,
|
|
5 / 6,
|
|
]
|
|
assert eg.clustering(G, [1, 4], weight="weight") == {
|
|
1: 1,
|
|
4: 0.83333333333333337,
|
|
}
|
|
|
|
def test_triangle_and_edge(self):
|
|
G = eg.empty_graph(range(3), create_using=eg.DiGraph())
|
|
G.add_edges_from(eg.pairwise(range(3), cyclic=True))
|
|
G.add_edge(0, 4, weight=2)
|
|
assert eg.clustering(G)[0] == 1 / 6
|
|
# Relaxed comparisons to allow graphblas-algorithms to pass tests
|
|
np.testing.assert_allclose(eg.clustering(G, weight="weight")[0], 1 / 12)
|
|
np.testing.assert_allclose(eg.clustering(G, 0, weight="weight"), 1 / 12)
|
|
|
|
|
|
class TestWeightedClustering:
|
|
@classmethod
|
|
def setup_class(cls):
|
|
global np
|
|
np = pytest.importorskip("numpy")
|
|
|
|
def test_clustering(self):
|
|
G = eg.Graph()
|
|
assert list(eg.clustering(G, weight="weight").values()) == []
|
|
assert eg.clustering(G) == {}
|
|
|
|
def test_path(self):
|
|
G = eg.path_graph(10)
|
|
assert list(eg.clustering(G, weight="weight").values()) == [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
]
|
|
assert eg.clustering(G, weight="weight") == {
|
|
0: 0,
|
|
1: 0,
|
|
2: 0,
|
|
3: 0,
|
|
4: 0,
|
|
5: 0,
|
|
6: 0,
|
|
7: 0,
|
|
8: 0,
|
|
9: 0,
|
|
}
|
|
|
|
def test_cubical(self):
|
|
G = eg.from_dict_of_lists(
|
|
{
|
|
0: [1, 3, 4],
|
|
1: [0, 2, 7],
|
|
2: [1, 3, 6],
|
|
3: [0, 2, 5],
|
|
4: [0, 5, 7],
|
|
5: [3, 4, 6],
|
|
6: [2, 5, 7],
|
|
7: [1, 4, 6],
|
|
},
|
|
create_using=None,
|
|
)
|
|
assert list(eg.clustering(G, weight="weight").values()) == [
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
]
|
|
assert eg.clustering(G, 1) == 0
|
|
assert list(eg.clustering(G, [1, 2], weight="weight").values()) == [0, 0]
|
|
assert eg.clustering(G, 1, weight="weight") == 0
|
|
assert eg.clustering(G, [1, 2], weight="weight") == {1: 0, 2: 0}
|
|
|
|
def test_k5(self):
|
|
G = eg.complete_graph(5)
|
|
assert list(eg.clustering(G, weight="weight").values()) == [1, 1, 1, 1, 1]
|
|
assert eg.average_clustering(G, weight="weight") == 1
|
|
G.remove_edge(1, 2)
|
|
assert list(eg.clustering(G, weight="weight").values()) == [
|
|
5 / 6,
|
|
1,
|
|
1,
|
|
5 / 6,
|
|
5 / 6,
|
|
]
|
|
assert eg.clustering(G, [1, 4], weight="weight") == {
|
|
1: 1,
|
|
4: 0.83333333333333337,
|
|
}
|
|
|
|
def test_triangle_and_edge(self):
|
|
G = eg.empty_graph(range(3), None)
|
|
G.add_edges_from(eg.pairwise(range(3), cyclic=True))
|
|
G.add_edge(0, 4, weight=2)
|
|
assert eg.clustering(G)[0] == 1 / 3
|
|
np.testing.assert_allclose(eg.clustering(G, weight="weight")[0], 1 / 6)
|
|
np.testing.assert_allclose(eg.clustering(G, 0, weight="weight"), 1 / 6)
|
|
|
|
def test_triangle_and_signed_edge(self):
|
|
G = eg.empty_graph(range(3), None)
|
|
G.add_edges_from(eg.pairwise(range(3), cyclic=True))
|
|
G.add_edge(0, 1, weight=-1)
|
|
G.add_edge(3, 0, weight=0)
|
|
assert eg.clustering(G)[0] == 1 / 3
|
|
assert eg.clustering(G, weight="weight")[0] == -1 / 3
|
|
|
|
|
|
class TestAdditionalClusteringCases:
|
|
def test_self_loops_ignored(self):
|
|
G = eg.Graph()
|
|
G.add_edges_from([(0, 1), (1, 2), (2, 0)])
|
|
G.add_edge(0, 0) # self-loop
|
|
assert eg.clustering(G, 0) == 1.0
|
|
|
|
def test_isolated_node(self):
|
|
G = eg.Graph()
|
|
G.add_node(1)
|
|
assert eg.clustering(G) == {1: 0}
|
|
|
|
def test_degree_one_node(self):
|
|
G = eg.Graph()
|
|
G.add_edge(1, 2)
|
|
assert eg.clustering(G) == {1: 0, 2: 0}
|
|
|
|
def test_custom_weight_name(self):
|
|
G = eg.Graph()
|
|
G.add_edge(0, 1, strength=2)
|
|
G.add_edge(1, 2, strength=2)
|
|
G.add_edge(2, 0, strength=2)
|
|
result = eg.clustering(G, weight="strength")
|
|
assert result[0] > 0
|
|
|
|
def test_negative_weights_mixed(self):
|
|
G = eg.complete_graph(3)
|
|
G[0][1]["weight"] = -1
|
|
G[1][2]["weight"] = 1
|
|
G[2][0]["weight"] = 1
|
|
assert eg.clustering(G, 0, weight="weight") < 0
|
|
|
|
def test_directed_reciprocal_edges(self):
|
|
G = eg.DiGraph()
|
|
G.add_edges_from([(0, 1), (1, 0), (0, 2), (2, 0), (1, 2), (2, 1)])
|
|
result = eg.clustering(G)
|
|
assert all(0 <= v <= 1 for v in result.values())
|