From d76384563e96cd422006ff79e0d1aaedb9518471 Mon Sep 17 00:00:00 2001 From: weilycoder Date: Mon, 30 Sep 2024 20:29:16 +0800 Subject: [PATCH 1/5] Allow users to define functions that shuffle vertex and edge sets --- cyaron/graph.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/cyaron/graph.py b/cyaron/graph.py index ee6b9ff..c70af29 100644 --- a/cyaron/graph.py +++ b/cyaron/graph.py @@ -46,26 +46,33 @@ def to_str(self, **kwargs): **kwargs(Keyword args): bool shuffle = False -> whether shuffle the output or not str output(Edge) = str -> the convert function which converts object Edge to str. the default way is to use str() + list[int] node_shuffler(list[int]) + = lambda table: random.sample(table, k=len(table)) + -> the random function which shuffles the vertex sequence + list[Edge] edge_shuffler(list[int]) + -> a random function. the default is to shuffle the edge sequence, + also, if the graph is undirected, it will swap `u` and `v` randomly. """ + def _edge_shuffler_default(table): + edge_buf = random.sample(table, k=len(table)) + for edge in edge_buf: + if not self.directed and random.randint(0, 1) == 0: + (edge.start, edge.end) = (edge.end, edge.start) + return edge_buf + shuffle = kwargs.get("shuffle", False) output = kwargs.get("output", str) - buf = [] + node_shuffler = kwargs.get("node_shuffler", lambda table: random.sample(table, k=len(table))) + edge_shuffler = kwargs.get("edge_shuffler", _edge_shuffler_default) if shuffle: - new_node_id = [i for i in range(1, len(self.edges))] - random.shuffle(new_node_id) - new_node_id = [0] + new_node_id + new_node_id = [0] + node_shuffler(range(1, len(self.edges))) edge_buf = [] for edge in self.iterate_edges(): edge_buf.append( Edge(new_node_id[edge.start], new_node_id[edge.end], edge.weight)) - random.shuffle(edge_buf) - for edge in edge_buf: - if not self.directed and random.randint(0, 1) == 0: - (edge.start, edge.end) = (edge.end, edge.start) - buf.append(output(edge)) + buf = map(output, edge_shuffler(edge_buf)) else: - for edge in self.iterate_edges(): - buf.append(output(edge)) + buf = map(output, self.iterate_edges()) return "\n".join(buf) def __str__(self): From ea647b746e9e6dca7ebc5999ffefa39ebb48c255 Mon Sep 17 00:00:00 2001 From: weilycoder Date: Mon, 30 Sep 2024 20:59:04 +0800 Subject: [PATCH 2/5] Modify comments --- cyaron/graph.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cyaron/graph.py b/cyaron/graph.py index c70af29..34e89fe 100644 --- a/cyaron/graph.py +++ b/cyaron/graph.py @@ -48,7 +48,8 @@ def to_str(self, **kwargs): str output(Edge) = str -> the convert function which converts object Edge to str. the default way is to use str() list[int] node_shuffler(list[int]) = lambda table: random.sample(table, k=len(table)) - -> the random function which shuffles the vertex sequence + -> the random function which shuffles the vertex sequence. + Note that this function will actually be passed in a `range`! list[Edge] edge_shuffler(list[int]) -> a random function. the default is to shuffle the edge sequence, also, if the graph is undirected, it will swap `u` and `v` randomly. From 3ca37c8368336d4d7c225b2bdeac1294f72af7c9 Mon Sep 17 00:00:00 2001 From: weilycoder Date: Tue, 8 Oct 2024 17:52:25 +0800 Subject: [PATCH 3/5] add test_shuffle --- cyaron/graph.py | 8 +++++- cyaron/tests/graph_test.py | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/cyaron/graph.py b/cyaron/graph.py index 5748acc..66ae82d 100644 --- a/cyaron/graph.py +++ b/cyaron/graph.py @@ -46,7 +46,13 @@ def __init__(self, point_count, directed=False): """ self.directed = directed self.edges = [[] for i in range(point_count + 1)] - + + def vertex_count(self): + """edge_count(self) -> int + Return the vertex of the edges in the graph. + """ + return len(self.edges) - 1 + def edge_count(self): """edge_count(self) -> int Return the count of the edges in the graph. diff --git a/cyaron/tests/graph_test.py b/cyaron/tests/graph_test.py index 1e3f1db..f42959e 100644 --- a/cyaron/tests/graph_test.py +++ b/cyaron/tests/graph_test.py @@ -1,3 +1,5 @@ +import itertools +import random import unittest from cyaron import Graph @@ -172,3 +174,56 @@ def test_GraphMatrix(self): self.assertEqual(str(g.to_matrix(default=9, merge=merge3)), "9 9 3\n9 9 3\n9 1 1") self.assertEqual(str(g.to_matrix(default=0, merge=merge4)), "0 0 3\n0 0 1\n0 1 1") self.assertEqual(str(g.to_matrix(default=0, merge=merge5)), "0 0 3\n0 0 84\n0 1 1") + + def test_shuffle(self): + def read_graph(n, data, directed = False): + g = Graph(n, directed) + for l in data.split('\n'): + u, v, w = map(int, l.split()) + g.add_edge(u, v, weight=w) + return g + + def isomorphic(graph1, graph2, mapping = None, directed = False): + n = graph1.vertex_count() + if n != graph2.vertex_count(): + return False + if graph1.edge_count() != graph2.edge_count(): + return False + if mapping is None: + for per in itertools.permutations(range(1, n + 1)): + if isomorphic(graph1, graph2, (0, ) + per): + return True + return False + edges = {} + for e in graph2.iterate_edges(): + key, val = (e.start, e.end), e.weight + if key in edges: + edges[key].append(val) + else: + edges[key] = [val] + for e in graph1.iterate_edges(): + key, val = (mapping[e.start], mapping[e.end]), e.weight + if not directed and key[0] > key[1]: + key = key[1], key[0] + if key not in edges: + return False + if val not in edges[key]: + return False + edges[key].remove(val) + return True + + def unit_test(n, m, shuffle_kwargs = {}, check_kwargs = {}): + g = Graph.graph(n, m) + data = g.to_str(**shuffle_kwargs) + h = read_graph(n, data) + self.assertTrue(isomorphic(g, h, **check_kwargs)) + + unit_test(8, 20) + unit_test(8, 20, {"shuffle": True}) + mapping = [0] + random.sample(range(1, 8), k = 7) + shuffer = lambda seq: list(map(lambda i: mapping[i], seq)) + unit_test(7, 10, {"shuffle": True, "node_shuffler": shuffer}) + unit_test(7, 14, {"shuffle": True, "node_shuffler": shuffer}, {"mapping": mapping}) + shuffer_without_swap = lambda table: random.sample(table, k=len(table)) + unit_test(7, 12, {"shuffle": True, "edge_shuffler": shuffer_without_swap}, {"directed": True}) + From 316ebdc0a6586bc0f928ecdb244072e09bd5f51a Mon Sep 17 00:00:00 2001 From: weilycoder Date: Tue, 8 Oct 2024 18:14:09 +0800 Subject: [PATCH 4/5] Modify API --- cyaron/graph.py | 9 ++++----- cyaron/tests/graph_test.py | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cyaron/graph.py b/cyaron/graph.py index 66ae82d..48e8f0a 100644 --- a/cyaron/graph.py +++ b/cyaron/graph.py @@ -79,10 +79,9 @@ def to_str(self, **kwargs): **kwargs(Keyword args): bool shuffle = False -> whether shuffle the output or not str output(Edge) = str -> the convert function which converts object Edge to str. the default way is to use str() - list[int] node_shuffler(list[int]) - = lambda table: random.sample(table, k=len(table)) + list[int] node_shuffler(int) + = lambda n: random.sample(range(1, n + 1), k=n) -> the random function which shuffles the vertex sequence. - Note that this function will actually be passed in a `range`! list[Edge] edge_shuffler(list[Edge]) -> a random function. the default is to shuffle the edge sequence, also, if the graph is undirected, it will swap `u` and `v` randomly. @@ -96,10 +95,10 @@ def _edge_shuffler_default(table): shuffle = kwargs.get("shuffle", False) output = kwargs.get("output", str) - node_shuffler = kwargs.get("node_shuffler", lambda table: random.sample(table, k=len(table))) + node_shuffler = kwargs.get("node_shuffler", lambda n: random.sample(range(1, n + 1), k=n)) edge_shuffler = kwargs.get("edge_shuffler", _edge_shuffler_default) if shuffle: - new_node_id = [0] + node_shuffler(range(1, len(self.edges))) + new_node_id = [0] + node_shuffler(self.vertex_count()) edge_buf = [] for edge in self.iterate_edges(): edge_buf.append( diff --git a/cyaron/tests/graph_test.py b/cyaron/tests/graph_test.py index f42959e..9a2d6e6 100644 --- a/cyaron/tests/graph_test.py +++ b/cyaron/tests/graph_test.py @@ -221,9 +221,8 @@ def unit_test(n, m, shuffle_kwargs = {}, check_kwargs = {}): unit_test(8, 20) unit_test(8, 20, {"shuffle": True}) mapping = [0] + random.sample(range(1, 8), k = 7) - shuffer = lambda seq: list(map(lambda i: mapping[i], seq)) + shuffer = lambda n: list(map(lambda i: mapping[i], range(1, n + 1))) unit_test(7, 10, {"shuffle": True, "node_shuffler": shuffer}) unit_test(7, 14, {"shuffle": True, "node_shuffler": shuffer}, {"mapping": mapping}) shuffer_without_swap = lambda table: random.sample(table, k=len(table)) unit_test(7, 12, {"shuffle": True, "edge_shuffler": shuffer_without_swap}, {"directed": True}) - From 4724dd35cd301174feee4bb5d80ffd635e1a6dcc Mon Sep 17 00:00:00 2001 From: "Mr. Python" <2789762371@qq.com> Date: Sun, 27 Oct 2024 02:21:26 +0800 Subject: [PATCH 5/5] Fix typo in vertex_count method documentation --- cyaron/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cyaron/graph.py b/cyaron/graph.py index 48e8f0a..f122837 100644 --- a/cyaron/graph.py +++ b/cyaron/graph.py @@ -48,7 +48,7 @@ def __init__(self, point_count, directed=False): self.edges = [[] for i in range(point_count + 1)] def vertex_count(self): - """edge_count(self) -> int + """vertex_count(self) -> int Return the vertex of the edges in the graph. """ return len(self.edges) - 1