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,411 @@
import math
import random
import easygraph as eg
from easygraph.classes.graph import Graph
__all__ = [
"erdos_renyi_M",
"erdos_renyi_P",
"fast_erdos_renyi_P",
"WS_Random",
"graph_Gnm",
]
def erdos_renyi_M(n, edge, directed=False, FilePath=None):
"""Given the number of nodes and the number of edges, return an Erdős-Rényi random graph, and store the graph in a document.
Parameters
----------
n : int
The number of nodes.
edge : int
The number of edges.
directed : bool, optional (default=False)
If True, this function returns a directed graph.
FilePath : string
The file for storing the output graph G.
Returns
-------
G : graph
an Erdős-Rényi random graph.
Examples
--------
Returns an Erdős-Rényi random graph G.
>>> erdos_renyi_M(100,180,directed=False,FilePath="/users/fudanmsn/downloads/RandomNetwork.txt")
References
----------
.. [1] P. Erdős and A. Rényi, On Random Graphs, Publ. Math. 6, 290 (1959).
.. [2] E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959).
"""
if directed:
G = eg.DiGraph()
adjacent = {}
mmax = n * (n - 1)
if edge >= mmax:
for i in range(n):
for j in range(n):
if i != j:
G.add_edge(i, j)
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(j)
else:
adjacent[i].append(j)
return G
count = 0
while count < edge:
i = random.randint(0, n - 1)
j = random.randint(0, n - 1)
if i == j or G.has_edge(i, j):
continue
else:
count = count + 1
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(j)
else:
adjacent[i].append(j)
G.add_edge(i, j)
else:
G = eg.Graph()
adjacent = {}
mmax = n * (n - 1) / 2
if edge >= mmax:
for i in range(n):
for j in range(n):
if i != j:
G.add_edge(i, j)
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(j)
else:
adjacent[i].append(j)
if j not in adjacent:
adjacent[j] = []
adjacent[j].append(i)
else:
adjacent[j].append(i)
return G
count = 0
while count < edge:
i = random.randint(0, n - 1)
j = random.randint(0, n - 1)
if i == j or G.has_edge(i, j):
continue
else:
count = count + 1
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(j)
else:
adjacent[i].append(j)
if j not in adjacent:
adjacent[j] = []
adjacent[j].append(i)
else:
adjacent[j].append(i)
G.add_edge(i, j)
writeRandomNetworkToFile(n, adjacent, FilePath)
return G
def erdos_renyi_P(n, p, directed=False, FilePath=None):
"""Given the number of nodes and the probability of edge creation, return an Erdős-Rényi random graph, and store the graph in a document.
Parameters
----------
n : int
The number of nodes.
p : float
Probability for edge creation.
directed : bool, optional (default=False)
If True, this function returns a directed graph.
FilePath : string
The file for storing the output graph G.
Returns
-------
G : graph
an Erdős-Rényi random graph.
Examples
--------
Returns an Erdős-Rényi random graph G
>>> erdos_renyi_P(100,0.5,directed=False,FilePath="/users/fudanmsn/downloads/RandomNetwork.txt")
References
----------
.. [1] P. Erdős and A. Rényi, On Random Graphs, Publ. Math. 6, 290 (1959).
.. [2] E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959).
"""
if directed:
G = eg.DiGraph()
adjacent = {}
probability = 0.0
for i in range(n):
for j in range(i + 1, n):
probability = random.random()
if probability < p:
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(j)
else:
adjacent[i].append(j)
G.add_edge(i, j)
else:
G = eg.Graph()
adjacent = {}
probability = 0.0
for i in range(n):
for j in range(i + 1, n):
probability = random.random()
if probability < p:
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(j)
else:
adjacent[i].append(j)
if j not in adjacent:
adjacent[j] = []
adjacent[j].append(i)
else:
adjacent[j].append(i)
G.add_edge(i, j)
writeRandomNetworkToFile(n, adjacent, FilePath)
return G
def fast_erdos_renyi_P(n, p, directed=False, FilePath=None):
"""Given the number of nodes and the probability of edge creation, return an Erdős-Rényi random graph, and store the graph in a document. Use this function for generating a huge scale graph.
Parameters
----------
n : int
The number of nodes.
p : float
Probability for edge creation.
directed : bool, optional (default=False)
If True, this function returns a directed graph.
FilePath : string
The file for storing the output graph G.
Returns
-------
G : graph
an Erdős-Rényi random graph.
Examples
--------
Returns an Erdős-Rényi random graph G
>>> erdos_renyi_P(100,0.5,directed=False,FilePath="/users/fudanmsn/downloads/RandomNetwork.txt")
References
----------
.. [1] P. Erdős and A. Rényi, On Random Graphs, Publ. Math. 6, 290 (1959).
.. [2] E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959).
"""
if directed:
G = eg.DiGraph()
w = -1
lp = math.log(1.0 - p)
v = 0
adjacent = {}
while v < n:
lr = math.log(1.0 - random.random())
w = w + 1 + int(lr / lp)
if v == w: # avoid self loops
w = w + 1
while v < n <= w:
w = w - n
v = v + 1
if v == w: # avoid self loops
w = w + 1
if v < n:
G.add_edge(v, w)
if v not in adjacent:
adjacent[v] = []
adjacent[v].append(w)
else:
adjacent[v].append(w)
else:
G = eg.Graph()
w = -1
lp = math.log(1.0 - p)
v = 1
adjacent = {}
while v < n:
lr = math.log(1.0 - random.random())
w = w + 1 + int(lr / lp)
while w >= v and v < n:
w = w - v
v = v + 1
if v < n:
G.add_edge(v, w)
if v not in adjacent:
adjacent[v] = []
adjacent[v].append(w)
else:
adjacent[v].append(w)
if w not in adjacent:
adjacent[w] = []
adjacent[w].append(v)
else:
adjacent[w].append(v)
writeRandomNetworkToFile(n, adjacent, FilePath)
return G
def WS_Random(n, k, p, FilePath=None):
"""Returns a small-world graph.
Parameters
----------
n : int
The number of nodes
k : int
Each node is joined with its `k` nearest neighbors in a ring
topology.
p : float
The probability of rewiring each edge
FilePath : string
The file for storing the output graph G
Returns
-------
G : graph
a small-world graph
Examples
--------
Returns a small-world graph G
>>> WS_Random(100,10,0.3,"/users/fudanmsn/downloads/RandomNetwork.txt")
"""
if k >= n:
print("k>=n, choose smaller k or larger n")
return
adjacent = {}
G = eg.Graph()
NUM1 = n
NUM2 = NUM1 - 1
K = k
K1 = K + 1
N = list(range(NUM1))
G.add_nodes(N)
for i in range(NUM1):
for j in range(1, K1):
K_add = NUM1 - K
i_add_j = i + j + 1
if i >= K_add and i_add_j > NUM1:
i_add = i + j - NUM1
G.add_edge(i, i_add)
else:
i_add = i + j
G.add_edge(i, i_add)
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(i_add)
else:
adjacent[i].append(i_add)
if i_add not in adjacent:
adjacent[i_add] = []
adjacent[i_add].append(i)
else:
adjacent[i_add].append(i)
for i in range(NUM1):
for e_del in range(i + 1, i + K1):
if e_del >= NUM1:
e_del = e_del - NUM1
P_random = random.random()
if P_random < p:
G.remove_edge(i, e_del)
adjacent[i].remove(e_del)
if adjacent[i] == []:
adjacent.pop(i)
adjacent[e_del].remove(i)
if adjacent[e_del] == []:
adjacent.pop(e_del)
e_add = random.randint(0, NUM2)
while e_add == i or G.has_edge(i, e_add) == True:
e_add = random.randint(0, NUM2)
G.add_edge(i, e_add)
if i not in adjacent:
adjacent[i] = []
adjacent[i].append(e_add)
else:
adjacent[i].append(e_add)
if e_add not in adjacent:
adjacent[e_add] = []
adjacent[e_add].append(i)
else:
adjacent[e_add].append(i)
writeRandomNetworkToFile(n, adjacent, FilePath)
return G
def writeRandomNetworkToFile(n, adjacent, FilePath):
if FilePath != None:
f = open(FilePath, "w+")
else:
f = open("RandomNetwork.txt", "w+")
adjacent = sorted(adjacent.items(), key=lambda d: d[0])
for i in adjacent:
i[1].sort()
for j in i[1]:
f.write(str(i[0]))
f.write(" ")
f.write(str(j))
f.write("\n")
f.close()
def graph_Gnm(num_v: int, num_e: int):
r"""Return a random graph with ``num_v`` vertices and ``num_e`` edges. Edges are drawn uniformly from the set of possible edges.
Args:
``num_v`` (``int``): The Number of vertices.
``num_e`` (``int``): The Number of edges.
Examples:
>>> import easygraph.randomhypergraph as rh
>>> g = rh.graph_Gnm(4, 5)
>>> g.e
([(1, 2), (0, 3), (2, 3), (0, 2), (1, 3)], [1.0, 1.0, 1.0, 1.0, 1.0])
"""
assert num_v > 1, "num_v must be greater than 1"
assert (
num_e < num_v * (num_v - 1) // 2
), "the specified num_e is larger than the possible number of edges"
v_list = list(range(num_v))
cur_num_e, e_set = 0, set()
while cur_num_e < num_e:
v = random.choice(v_list)
w = random.choice(v_list)
if v > w:
v, w = w, v
if v == w or (v, w) in e_set:
continue
e_set.add((v, w))
cur_num_e += 1
g = Graph()
g.add_nodes(list(range(0, num_v)))
for ee in list(e_set):
g.add_edge(ee[0], ee[1], weight=1.0)
return g
@@ -0,0 +1,2 @@
from .classic import *
from .RandomNetwork import *
@@ -0,0 +1,73 @@
import itertools
from easygraph.classes.graph import Graph
from easygraph.utils import nodes_or_number
from easygraph.utils import pairwise
__all__ = ["empty_graph", "path_graph", "complete_graph"]
@nodes_or_number(0)
def empty_graph(n=0, create_using=None, default=Graph):
if create_using is None:
G = default()
elif hasattr(create_using, "_adj"):
# create_using is a EasyGraph style Graph
G = create_using
else:
# try create_using as constructor
G = create_using()
n_name, nodes = n
G.add_nodes_from(nodes)
return G
@nodes_or_number(0)
def path_graph(n, create_using=None):
n_name, nodes = n
G = empty_graph(nodes, create_using)
G.add_edges_from(pairwise(nodes))
return G
@nodes_or_number(0)
def complete_graph(n, create_using=None):
"""Return the complete graph `K_n` with n nodes.
A complete graph on `n` nodes means that all pairs
of distinct nodes have an edge connecting them.
Parameters
----------
n : int or iterable container of nodes
If n is an integer, nodes are from range(n).
If n is a container of nodes, those nodes appear in the graph.
create_using : EasyGraph graph constructor, optional (default=eg.Graph)
Graph type to create. If graph instance, then cleared before populated.
Examples
--------
>>> G = eg.complete_graph(9)
>>> len(G)
9
>>> G.size()
36
>>> G = eg.complete_graph(range(11, 14))
>>> list(G.nodes())
[11, 12, 13]
>>> G = eg.complete_graph(4, eg.DiGraph())
>>> G.is_directed()
True
"""
n_name, nodes = n
G = empty_graph(n_name, create_using)
if len(nodes) > 1:
if G.is_directed():
edges = itertools.permutations(nodes, 2)
else:
edges = itertools.combinations(nodes, 2)
G.add_edges_from(edges)
return G
@@ -0,0 +1,63 @@
import unittest
import easygraph as eg
class test_random_network(unittest.TestCase):
def setUp(self):
self.G = eg.datasets.get_graph_karateclub()
def test_erdos_renyi_M(self):
print(eg.erdos_renyi_M(8, 5).edges)
def test_erdos_renyi_P(self):
print(eg.erdos_renyi_P(8, 0.2).nodes)
def test_fast_erdos_renyi_P(self):
print(eg.fast_erdos_renyi_P(8, 0.2).nodes)
def test_WS_Random(self):
print(eg.WS_Random(8, 1, 0.5).nodes)
def test_graph_Gnm(self):
print(eg.graph_Gnm(8, 5).nodes)
def test_erdos_renyi_M_max_edges(self):
n = 5
max_edges = n * (n - 1) // 2
G = eg.erdos_renyi_M(n, max_edges)
self.assertEqual(len(G.edges), max_edges)
def test_erdos_renyi_P_extreme_p(self):
G0 = eg.erdos_renyi_P(10, 0.0)
G1 = eg.erdos_renyi_P(10, 1.0)
self.assertEqual(len(G0.edges), 0)
self.assertEqual(len(G1.edges), 45) # 10 * 9 / 2
def test_fast_erdos_renyi_P_large_p(self):
G = eg.fast_erdos_renyi_P(10, 0.9)
self.assertEqual(len(G.nodes), 10)
def test_WS_Random_structure(self):
G = eg.WS_Random(10, 2, 0.1)
self.assertEqual(len(G.nodes), 10)
self.assertTrue(all(0 <= u < 10 and 0 <= v < 10 for u, v, *_ in G.edges))
def test_WS_Random_invalid_k(self):
G = eg.WS_Random(5, 5, 0.1)
self.assertIsNone(G)
def test_graph_Gnm_basic(self):
G = eg.graph_Gnm(10, 15)
self.assertEqual(len(G.nodes), 10)
self.assertEqual(len(G.edges), 15)
def test_graph_Gnm_invalid_inputs(self):
with self.assertRaises(AssertionError):
eg.graph_Gnm(1, 1)
with self.assertRaises(AssertionError):
eg.graph_Gnm(5, 11) # 5*4/2 = 10 max
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,87 @@
import unittest
import easygraph as eg
class test_classic(unittest.TestCase):
def setUp(self):
self.G = eg.datasets.get_graph_karateclub()
def test_empty_graph(self):
# print(eg.empty_graph(-1).nodes)
print(eg.empty_graph(10).nodes)
def test_path_graph(self):
eg.path_graph(10, eg.DiGraph)
def test_complete_graph(self):
eg.complete_graph(10, eg.DiGraph)
def test_empty_graph_default(self):
G = eg.empty_graph()
self.assertEqual(len(G.nodes), 0)
self.assertEqual(len(G.edges), 0)
def test_empty_graph_with_n(self):
G = eg.empty_graph(5)
self.assertEqual(set(G.nodes), set(range(5)))
self.assertEqual(len(G.edges), 0)
def test_empty_graph_with_custom_nodes(self):
G = eg.empty_graph(["a", "b", "c"])
self.assertEqual(set(G.nodes), {"a", "b", "c"})
self.assertEqual(len(G.edges), 0)
def test_empty_graph_with_existing_graph(self):
existing = eg.Graph()
existing.add_node(999)
G = eg.empty_graph(3, create_using=existing)
self.assertIn(0, G.nodes) # node 0 added
self.assertEqual(len(G.nodes), 4) # 999 is retained
self.assertEqual(len(G.edges), 0)
def test_path_graph_basic(self):
G = eg.path_graph(4)
self.assertEqual(len(G.nodes), 4)
self.assertEqual(len(G.edges), 3)
edges = {(u, v) for u, v, _ in G.edges}
self.assertTrue((0, 1) in edges and (1, 2) in edges and (2, 3) in edges)
def test_path_graph_with_custom_nodes(self):
G = eg.path_graph(["x", "y", "z"])
self.assertEqual(len(G.nodes), 3)
actual_edges = {(u, v) for u, v, _ in G.edges}
expected_edges = {("x", "y"), ("y", "z")}
self.assertEqual(actual_edges, expected_edges)
def test_complete_graph_basic(self):
G = eg.complete_graph(4)
self.assertEqual(len(G.nodes), 4)
self.assertEqual(len(G.edges), 6) # n*(n-1)/2 for undirected
def test_complete_graph_directed(self):
G = eg.complete_graph(3, create_using=eg.DiGraph())
self.assertTrue(G.is_directed())
self.assertEqual(len(G.nodes), 3)
self.assertEqual(len(G.edges), 6) # n*(n-1) for directed
def test_complete_graph_custom_nodes(self):
G = eg.complete_graph(["a", "b", "c"])
self.assertEqual(set(G.nodes), {"a", "b", "c"})
actual_edges = {(u, v) for u, v, _ in G.edges}
expected_edges = {("a", "b"), ("a", "c"), ("b", "c")}
self.assertEqual(actual_edges, expected_edges)
def test_complete_graph_one_node(self):
G = eg.complete_graph(1)
self.assertEqual(len(G.nodes), 1)
self.assertEqual(len(G.edges), 0)
def test_complete_graph_zero_nodes(self):
G = eg.complete_graph(0)
self.assertEqual(len(G.nodes), 0)
self.assertEqual(len(G.edges), 0)
if __name__ == "__main__":
unittest.main()