chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:36:30 +08:00
commit 55ab4e4a73
473 changed files with 72932 additions and 0 deletions
@@ -0,0 +1,41 @@
import easygraph as eg
import pytest
from easygraph.functions.basic import average_degree
def test_average_degree_basic():
G = eg.Graph()
G.add_edges_from([(1, 2), (2, 3)])
assert average_degree(G) == pytest.approx(4 / 3)
def test_average_degree_empty_graph():
G = eg.Graph()
with pytest.raises(ZeroDivisionError):
average_degree(G)
def test_average_degree_self_loop():
G = eg.Graph()
G.add_edge(1, 1) # self-loop
# Self-loop counts as 2 towards degree of node 1
assert average_degree(G) == pytest.approx(2.0)
def test_average_degree_with_isolated_node():
G = eg.Graph()
G.add_edges_from([(1, 2), (2, 3)])
G.add_node(4) # isolated node
assert average_degree(G) == pytest.approx(1.0)
def test_average_degree_directed_graph():
G = eg.DiGraph()
G.add_edges_from([(1, 2), (2, 3), (3, 1)])
assert average_degree(G) == pytest.approx(2.0)
def test_average_degree_invalid_input():
with pytest.raises(AttributeError):
average_degree(None)
@@ -0,0 +1,418 @@
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())
@@ -0,0 +1,104 @@
import sys
import easygraph as eg
import numpy as np
import pytest
from easygraph.functions.basic.localassort import localAssort
class TestLocalAssort:
@classmethod
def setup_class(self):
self.G = eg.get_graph_karateclub()
edgelist = []
node_num = len(self.G.nodes)
for e in self.G.edges:
edgelist.append([e[0] - 1, e[1] - 1])
self.edgelist = np.int32(edgelist)
self.valuelist = np.arange(node_num, dtype=np.int32) % 6
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor <= 7,
reason="python version should higher than 3.7",
)
def test_karateclub(self):
assortM, assortT, Z = eg.localAssort(
self.edgelist, self.valuelist, pr=np.arange(0, 1, 0.1)
)
_, assortT, Z = eg.functions.basic.localassort.localAssort(
self.edgelist, self.valuelist, pr=np.array([0.9])
)
def test_localassort_small_complete_graph():
G = eg.complete_graph(4)
edgelist = np.array(list(G.edges))
node_attr = np.array([0, 0, 1, 1])
assortM, assortT, Z = localAssort(edgelist, node_attr)
assert assortM.shape == (4, 10)
assert assortT.shape == (4,)
assert Z.shape == (4,)
assert np.all(Z >= 0) and np.all(Z <= 1)
def test_localassort_with_missing_attributes():
G = eg.path_graph(5)
edgelist = np.array(list(G.edges))
node_attr = np.array([0, -1, 1, -1, 1])
assortM, assortT, Z = localAssort(edgelist, node_attr, pr=np.array([0.5]))
assert assortT.shape == (5,)
assert Z.shape == (5,)
assert np.any(np.isnan(assortT))
def test_localassort_directed_graph():
G = eg.DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3)])
edgelist = np.array(list(G.edges))
node_attr = np.array([0, 1, 0, 1])
assortM, assortT, Z = localAssort(edgelist, node_attr, undir=False)
assert assortM.shape == (4, 10)
assert assortT.shape == (4,)
assert Z.shape == (4,)
def test_localassort_single_node_graph():
edgelist = np.empty((0, 2), dtype=int)
node_attr = np.array([0])
assortM, assortT, Z = localAssort(edgelist, node_attr)
assert assortM.shape == (1, 10)
assert np.all(np.isnan(assortM)) or np.allclose(assortM, 0, atol=1e-5)
assert np.all(np.isnan(assortT)) or np.allclose(assortT, 0, atol=1e-5)
assert np.all(np.isnan(Z)) or np.allclose(Z, 0, atol=1e-5)
def test_localassort_disconnected_graph():
G = eg.Graph()
G.add_nodes_from(range(5))
edgelist = np.empty((0, 2), dtype=int)
node_attr = np.array([0, 1, 0, 1, 1])
assortM, assortT, Z = localAssort(edgelist, node_attr)
assert assortM.shape == (5, 10)
assert np.all(np.isnan(assortM)) or np.allclose(assortM, 0, atol=1e-5)
assert np.all(np.isnan(assortT)) or np.allclose(assortT, 0, atol=1e-5)
assert np.all(np.isnan(Z)) or np.allclose(Z, 0, atol=1e-5)
def test_localassort_high_restart_probabilities():
G = eg.path_graph(5)
edgelist = np.array(list(G.edges))
node_attr = np.array([1, 0, 1, 0, 1])
pr = np.array([0.95, 0.99])
assortM, assortT, Z = localAssort(edgelist, node_attr, pr=pr)
assert assortM.shape == (5, 2)
assert assortT.shape == (5,)
assert Z.shape == (5,)
def test_localassort_invalid_attribute_length():
edgelist = np.array([[0, 1], [1, 2]])
node_attr = np.array([0, 1]) # too short
with pytest.raises(ValueError):
localAssort(edgelist, node_attr)
@@ -0,0 +1,79 @@
import easygraph as eg
import pytest
class TestPredecessor:
# @classmethod
# def setup_class(self):
# pytest.importskip("numpy")
def test_predecessor(self):
G = eg.path_graph(4)
for source in G:
assert eg.predecessor(G, source) in [
{0: [], 1: [0], 2: [1], 3: [2]},
{1: [], 0: [1], 2: [1], 3: [2]},
{2: [], 1: [2], 3: [2], 0: [1]},
{3: [], 2: [3], 1: [2], 0: [1]},
]
def test_basic_predecessor(self):
G = eg.path_graph(4)
result = eg.predecessor(G, 0)
assert result == {0: [], 1: [0], 2: [1], 3: [2]}
def test_with_return_seen(self):
G = eg.path_graph(4)
pred, seen = eg.predecessor(G, 0, return_seen=True)
assert pred == {0: [], 1: [0], 2: [1], 3: [2]}
assert seen == {0: 0, 1: 1, 2: 2, 3: 3}
def test_with_target(self):
G = eg.path_graph(4)
assert eg.predecessor(G, 0, target=2) == [1]
def test_with_target_and_return_seen(self):
G = eg.path_graph(4)
pred, seen = eg.predecessor(G, 0, target=2, return_seen=True)
assert pred == [1]
assert seen == 2
def test_with_cutoff(self):
G = eg.path_graph(4)
pred = eg.predecessor(G, 0, cutoff=1)
assert pred == {0: [], 1: [0]}
def test_disconnected_graph(self):
G = eg.Graph()
G.add_edges_from([(0, 1), (2, 3)])
pred = eg.predecessor(G, 0)
assert 2 not in pred and 3 not in pred
def test_invalid_source(self):
G = eg.path_graph(4)
with pytest.raises(eg.NodeNotFound):
eg.predecessor(G, 99)
def test_no_path_to_target(self):
G = eg.Graph()
G.add_edges_from([(0, 1), (2, 3)])
assert eg.predecessor(G, 0, target=3) == []
def test_no_path_to_target_with_return_seen(self):
G = eg.Graph()
G.add_edges_from([(0, 1), (2, 3)])
pred, seen = eg.predecessor(G, 0, target=3, return_seen=True)
assert pred == []
assert seen == -1
def test_cycle_graph(self):
G = eg.Graph()
G.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 0)]) # cycled graph
pred = eg.predecessor(G, 0)
assert set(pred.keys()) == set(G.nodes)
def test_directed_graph(self):
G = eg.DiGraph()
G.add_edges_from([(0, 1), (1, 2), (2, 3)])
pred = eg.predecessor(G, 0)
assert pred == {0: [], 1: [0], 2: [1], 3: [2]}