From 6918d4c6228a1cf17f44947d12f1cd26fecf0e2e Mon Sep 17 00:00:00 2001 From: Tarun Vishwakarma Date: Mon, 14 Oct 2024 08:17:52 +0530 Subject: [PATCH 1/6] Added edmonds_blossom_algorithm.py --- graphs/edmonds_blossom_algorithm.py | 211 ++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 graphs/edmonds_blossom_algorithm.py diff --git a/graphs/edmonds_blossom_algorithm.py b/graphs/edmonds_blossom_algorithm.py new file mode 100644 index 000000000000..59316e57c568 --- /dev/null +++ b/graphs/edmonds_blossom_algorithm.py @@ -0,0 +1,211 @@ +from collections import deque, defaultdict +from typing import List, Tuple, Dict + + +UNMATCHED = -1 # Constant to represent unmatched vertices + + +class EdmondsBlossomAlgorithm: + @staticmethod + def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tuple[int, int]]: + """ + Finds the maximum matching in a general graph using Edmonds' Blossom Algorithm. + + :param edges: List of edges in the graph. + :param vertex_count: Number of vertices in the graph. + :return: A list of matched pairs of vertices. + + >>> EdmondsBlossomAlgorithm.maximum_matching([(0, 1), (1, 2), (2, 3)], 4) + [(0, 1), (2, 3)] + """ + graph: Dict[int, List[int]] = defaultdict(list) + + # Populate the graph with the edges + for vertex_u, vertex_v in edges: + graph[vertex_u].append(vertex_v) + graph[vertex_v].append(vertex_u) + + # Initial matching array and auxiliary data structures + match = [UNMATCHED] * vertex_count + parent = [UNMATCHED] * vertex_count + base = list(range(vertex_count)) + in_blossom = [False] * vertex_count + in_queue = [False] * vertex_count + + # Main logic for finding maximum matching + for vertex_u in range(vertex_count): + if match[vertex_u] == UNMATCHED: + # BFS initialization + parent = [UNMATCHED] * vertex_count + base = list(range(vertex_count)) + in_blossom = [False] * vertex_count + in_queue = [False] * vertex_count + + queue = deque([vertex_u]) + in_queue[vertex_u] = True + + augmenting_path_found = False + + # BFS to find augmenting paths + while queue and not augmenting_path_found: + current_vertex = queue.popleft() + for neighbor in graph[current_vertex]: + if match[current_vertex] == neighbor: + continue + + if base[current_vertex] == base[neighbor]: + continue # Avoid self-loops + + if parent[neighbor] == UNMATCHED: + # Case 1: neighbor is unmatched, we've found an augmenting path + if match[neighbor] == UNMATCHED: + parent[neighbor] = current_vertex + augmenting_path_found = True + EdmondsBlossomAlgorithm.update_matching(match, parent, neighbor) + break + + # Case 2: neighbor is matched, add neighbor's match to the queue + matched_vertex = match[neighbor] + parent[neighbor] = current_vertex + parent[matched_vertex] = neighbor + if not in_queue[matched_vertex]: + queue.append(matched_vertex) + in_queue[matched_vertex] = True + else: + # Case 3: Both current_vertex and neighbor have a parent; check for a cycle/blossom + base_vertex = EdmondsBlossomAlgorithm.find_base(base, parent, current_vertex, neighbor) + if base_vertex != UNMATCHED: + EdmondsBlossomAlgorithm.contract_blossom(BlossomData( + BlossomAuxData(queue, parent, base, in_blossom, match, in_queue), + current_vertex, neighbor, base_vertex + )) + + # Create result list of matched pairs + matching_result = [] + for vertex in range(vertex_count): + if match[vertex] != UNMATCHED and vertex < match[vertex]: + matching_result.append((vertex, match[vertex])) + + return matching_result + + @staticmethod + def update_matching(match: List[int], parent: List[int], current_vertex: int) -> None: + """ + Updates the matching along the augmenting path found. + + :param match: The matching array. + :param parent: The parent array used during the BFS. + :param current_vertex: The starting node of the augmenting path. + + >>> match = [UNMATCHED, UNMATCHED, UNMATCHED] + >>> parent = [1, 0, UNMATCHED] + >>> EdmondsBlossomAlgorithm.update_matching(match, parent, 2) + >>> match + [1, 0, -1] + """ + while current_vertex != UNMATCHED: + matched_vertex = parent[current_vertex] + next_vertex = match[matched_vertex] + match[matched_vertex] = current_vertex + match[current_vertex] = matched_vertex + current_vertex = next_vertex + + @staticmethod + def find_base(base: List[int], parent: List[int], vertex_u: int, vertex_v: int) -> int: + """ + Finds the base of a node in the blossom. + + :param base: The base array. + :param parent: The parent array. + :param vertex_u: One end of the edge. + :param vertex_v: The other end of the edge. + :return: The base of the node or UNMATCHED. + + >>> base = [0, 1, 2, 3] + >>> parent = [1, 0, UNMATCHED, UNMATCHED] + >>> EdmondsBlossomAlgorithm.find_base(base, parent, 2, 3) + 2 + """ + visited = [False] * len(base) + + # Mark ancestors of vertex_u + current_vertex_u = vertex_u + while True: + current_vertex_u = base[current_vertex_u] + visited[current_vertex_u] = True + if parent[current_vertex_u] == UNMATCHED: + break + current_vertex_u = parent[current_vertex_u] + + # Find the common ancestor of vertex_v + current_vertex_v = vertex_v + while True: + current_vertex_v = base[current_vertex_v] + if visited[current_vertex_v]: + return current_vertex_v + current_vertex_v = parent[current_vertex_v] + + @staticmethod + def contract_blossom(blossom_data: 'BlossomData') -> None: + """ + Contracts a blossom in the graph, modifying the base array + and marking the vertices involved. + + :param blossom_data: An object containing the necessary data + to perform the contraction. + + >>> aux_data = BlossomAuxData(deque(), [], [], [], [], []) + >>> blossom_data = BlossomData(aux_data, 0, 1, 2) + >>> EdmondsBlossomAlgorithm.contract_blossom(blossom_data) + """ + # Mark all vertices in the blossom + current_vertex_u = blossom_data.u + while blossom_data.aux_data.base[current_vertex_u] != blossom_data.lca: + base_u = blossom_data.aux_data.base[current_vertex_u] + match_base_u = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_u]] + blossom_data.aux_data.in_blossom[base_u] = True + blossom_data.aux_data.in_blossom[match_base_u] = True + current_vertex_u = blossom_data.aux_data.parent[blossom_data.aux_data.match[current_vertex_u]] + + current_vertex_v = blossom_data.v + while blossom_data.aux_data.base[current_vertex_v] != blossom_data.lca: + base_v = blossom_data.aux_data.base[current_vertex_v] + match_base_v = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_v]] + blossom_data.aux_data.in_blossom[base_v] = True + blossom_data.aux_data.in_blossom[match_base_v] = True + current_vertex_v = blossom_data.aux_data.parent[blossom_data.aux_data.match[current_vertex_v]] + + # Update the base for all marked vertices + for i in range(len(blossom_data.aux_data.base)): + if blossom_data.aux_data.in_blossom[blossom_data.aux_data.base[i]]: + blossom_data.aux_data.base[i] = blossom_data.lca + if not blossom_data.aux_data.in_queue[i]: + blossom_data.aux_data.queue.append(i) + blossom_data.aux_data.in_queue[i] = True + + +class BlossomAuxData: + """ + Auxiliary data class to encapsulate common parameters for the blossom operations. + """ + + def __init__(self, queue: deque, parent: List[int], base: List[int], + in_blossom: List[bool], match: List[int], in_queue: List[bool]) -> None: + self.queue = queue + self.parent = parent + self.base = base + self.in_blossom = in_blossom + self.match = match + self.in_queue = in_queue + + +class BlossomData: + """ + BlossomData class with reduced parameters. + """ + + def __init__(self, aux_data: BlossomAuxData, u: int, v: int, lca: int) -> None: + self.aux_data = aux_data + self.u = u + self.v = v + self.lca = lca From 5c6ca3d08b3d2ea78cc3d15e16ba53fb553c7436 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 02:53:03 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- graphs/edmonds_blossom_algorithm.py | 68 ++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/graphs/edmonds_blossom_algorithm.py b/graphs/edmonds_blossom_algorithm.py index 59316e57c568..7d5b6f5b2215 100644 --- a/graphs/edmonds_blossom_algorithm.py +++ b/graphs/edmonds_blossom_algorithm.py @@ -7,7 +7,9 @@ class EdmondsBlossomAlgorithm: @staticmethod - def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tuple[int, int]]: + def maximum_matching( + edges: List[Tuple[int, int]], vertex_count: int + ) -> List[Tuple[int, int]]: """ Finds the maximum matching in a general graph using Edmonds' Blossom Algorithm. @@ -61,7 +63,9 @@ def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tu if match[neighbor] == UNMATCHED: parent[neighbor] = current_vertex augmenting_path_found = True - EdmondsBlossomAlgorithm.update_matching(match, parent, neighbor) + EdmondsBlossomAlgorithm.update_matching( + match, parent, neighbor + ) break # Case 2: neighbor is matched, add neighbor's match to the queue @@ -73,12 +77,25 @@ def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tu in_queue[matched_vertex] = True else: # Case 3: Both current_vertex and neighbor have a parent; check for a cycle/blossom - base_vertex = EdmondsBlossomAlgorithm.find_base(base, parent, current_vertex, neighbor) + base_vertex = EdmondsBlossomAlgorithm.find_base( + base, parent, current_vertex, neighbor + ) if base_vertex != UNMATCHED: - EdmondsBlossomAlgorithm.contract_blossom(BlossomData( - BlossomAuxData(queue, parent, base, in_blossom, match, in_queue), - current_vertex, neighbor, base_vertex - )) + EdmondsBlossomAlgorithm.contract_blossom( + BlossomData( + BlossomAuxData( + queue, + parent, + base, + in_blossom, + match, + in_queue, + ), + current_vertex, + neighbor, + base_vertex, + ) + ) # Create result list of matched pairs matching_result = [] @@ -89,7 +106,9 @@ def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tu return matching_result @staticmethod - def update_matching(match: List[int], parent: List[int], current_vertex: int) -> None: + def update_matching( + match: List[int], parent: List[int], current_vertex: int + ) -> None: """ Updates the matching along the augmenting path found. @@ -111,7 +130,9 @@ def update_matching(match: List[int], parent: List[int], current_vertex: int) -> current_vertex = next_vertex @staticmethod - def find_base(base: List[int], parent: List[int], vertex_u: int, vertex_v: int) -> int: + def find_base( + base: List[int], parent: List[int], vertex_u: int, vertex_v: int + ) -> int: """ Finds the base of a node in the blossom. @@ -146,7 +167,7 @@ def find_base(base: List[int], parent: List[int], vertex_u: int, vertex_v: int) current_vertex_v = parent[current_vertex_v] @staticmethod - def contract_blossom(blossom_data: 'BlossomData') -> None: + def contract_blossom(blossom_data: "BlossomData") -> None: """ Contracts a blossom in the graph, modifying the base array and marking the vertices involved. @@ -162,18 +183,26 @@ def contract_blossom(blossom_data: 'BlossomData') -> None: current_vertex_u = blossom_data.u while blossom_data.aux_data.base[current_vertex_u] != blossom_data.lca: base_u = blossom_data.aux_data.base[current_vertex_u] - match_base_u = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_u]] + match_base_u = blossom_data.aux_data.base[ + blossom_data.aux_data.match[current_vertex_u] + ] blossom_data.aux_data.in_blossom[base_u] = True blossom_data.aux_data.in_blossom[match_base_u] = True - current_vertex_u = blossom_data.aux_data.parent[blossom_data.aux_data.match[current_vertex_u]] + current_vertex_u = blossom_data.aux_data.parent[ + blossom_data.aux_data.match[current_vertex_u] + ] current_vertex_v = blossom_data.v while blossom_data.aux_data.base[current_vertex_v] != blossom_data.lca: base_v = blossom_data.aux_data.base[current_vertex_v] - match_base_v = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_v]] + match_base_v = blossom_data.aux_data.base[ + blossom_data.aux_data.match[current_vertex_v] + ] blossom_data.aux_data.in_blossom[base_v] = True blossom_data.aux_data.in_blossom[match_base_v] = True - current_vertex_v = blossom_data.aux_data.parent[blossom_data.aux_data.match[current_vertex_v]] + current_vertex_v = blossom_data.aux_data.parent[ + blossom_data.aux_data.match[current_vertex_v] + ] # Update the base for all marked vertices for i in range(len(blossom_data.aux_data.base)): @@ -189,8 +218,15 @@ class BlossomAuxData: Auxiliary data class to encapsulate common parameters for the blossom operations. """ - def __init__(self, queue: deque, parent: List[int], base: List[int], - in_blossom: List[bool], match: List[int], in_queue: List[bool]) -> None: + def __init__( + self, + queue: deque, + parent: List[int], + base: List[int], + in_blossom: List[bool], + match: List[int], + in_queue: List[bool], + ) -> None: self.queue = queue self.parent = parent self.base = base From b60e18e501794a5183658aebfa7ca2f564858623 Mon Sep 17 00:00:00 2001 From: Tarun Vishwakarma Date: Mon, 14 Oct 2024 11:08:56 +0530 Subject: [PATCH 3/6] Corrected ruff checks --- graphs/edmonds_blossom_algorithm.py | 47 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/graphs/edmonds_blossom_algorithm.py b/graphs/edmonds_blossom_algorithm.py index 59316e57c568..14c4542f05f7 100644 --- a/graphs/edmonds_blossom_algorithm.py +++ b/graphs/edmonds_blossom_algorithm.py @@ -1,13 +1,11 @@ from collections import deque, defaultdict -from typing import List, Tuple, Dict - UNMATCHED = -1 # Constant to represent unmatched vertices class EdmondsBlossomAlgorithm: @staticmethod - def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tuple[int, int]]: + def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tuple[int, int]]: """ Finds the maximum matching in a general graph using Edmonds' Blossom Algorithm. @@ -18,7 +16,7 @@ def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tu >>> EdmondsBlossomAlgorithm.maximum_matching([(0, 1), (1, 2), (2, 3)], 4) [(0, 1), (2, 3)] """ - graph: Dict[int, List[int]] = defaultdict(list) + graph: dict[int, list[int]] = defaultdict(list) # Populate the graph with the edges for vertex_u, vertex_v in edges: @@ -61,7 +59,9 @@ def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tu if match[neighbor] == UNMATCHED: parent[neighbor] = current_vertex augmenting_path_found = True - EdmondsBlossomAlgorithm.update_matching(match, parent, neighbor) + EdmondsBlossomAlgorithm.update_matching( + match, parent, neighbor + ) break # Case 2: neighbor is matched, add neighbor's match to the queue @@ -73,12 +73,19 @@ def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tu in_queue[matched_vertex] = True else: # Case 3: Both current_vertex and neighbor have a parent; check for a cycle/blossom - base_vertex = EdmondsBlossomAlgorithm.find_base(base, parent, current_vertex, neighbor) + base_vertex = EdmondsBlossomAlgorithm.find_base( + base, parent, current_vertex, neighbor + ) if base_vertex != UNMATCHED: - EdmondsBlossomAlgorithm.contract_blossom(BlossomData( - BlossomAuxData(queue, parent, base, in_blossom, match, in_queue), - current_vertex, neighbor, base_vertex - )) + EdmondsBlossomAlgorithm.contract_blossom( + BlossomData( + BlossomAuxData( + queue, parent, base, in_blossom, + match, in_queue + ), + current_vertex, neighbor, base_vertex + ) + ) # Create result list of matched pairs matching_result = [] @@ -89,7 +96,7 @@ def maximum_matching(edges: List[Tuple[int, int]], vertex_count: int) -> List[Tu return matching_result @staticmethod - def update_matching(match: List[int], parent: List[int], current_vertex: int) -> None: + def update_matching(match: list[int], parent: list[int], current_vertex: int) -> None: """ Updates the matching along the augmenting path found. @@ -111,7 +118,9 @@ def update_matching(match: List[int], parent: List[int], current_vertex: int) -> current_vertex = next_vertex @staticmethod - def find_base(base: List[int], parent: List[int], vertex_u: int, vertex_v: int) -> int: + def find_base( + base: list[int], parent: list[int], vertex_u: int, vertex_v: int + ) -> int: """ Finds the base of a node in the blossom. @@ -165,7 +174,9 @@ def contract_blossom(blossom_data: 'BlossomData') -> None: match_base_u = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_u]] blossom_data.aux_data.in_blossom[base_u] = True blossom_data.aux_data.in_blossom[match_base_u] = True - current_vertex_u = blossom_data.aux_data.parent[blossom_data.aux_data.match[current_vertex_u]] + current_vertex_u = blossom_data.aux_data.parent[ + blossom_data.aux_data.match[current_vertex_u] + ] current_vertex_v = blossom_data.v while blossom_data.aux_data.base[current_vertex_v] != blossom_data.lca: @@ -173,7 +184,9 @@ def contract_blossom(blossom_data: 'BlossomData') -> None: match_base_v = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_v]] blossom_data.aux_data.in_blossom[base_v] = True blossom_data.aux_data.in_blossom[match_base_v] = True - current_vertex_v = blossom_data.aux_data.parent[blossom_data.aux_data.match[current_vertex_v]] + current_vertex_v = blossom_data.aux_data.parent[ + blossom_data.aux_data.match[current_vertex_v] + ] # Update the base for all marked vertices for i in range(len(blossom_data.aux_data.base)): @@ -189,8 +202,10 @@ class BlossomAuxData: Auxiliary data class to encapsulate common parameters for the blossom operations. """ - def __init__(self, queue: deque, parent: List[int], base: List[int], - in_blossom: List[bool], match: List[int], in_queue: List[bool]) -> None: + def __init__( + self, queue: deque, parent: list[int], base: list[int], in_blossom: list[bool], + match: list[int], in_queue: list[bool] + ) -> None: self.queue = queue self.parent = parent self.base = base From b4cf6eb6af8632476674d3c9d067b2f2e0109b9c Mon Sep 17 00:00:00 2001 From: Tarun Vishwakarma Date: Mon, 14 Oct 2024 11:12:02 +0530 Subject: [PATCH 4/6] Corrected ruff checks --- graphs/edmonds_blossom_algorithm.py | 45 ++++++++--------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/graphs/edmonds_blossom_algorithm.py b/graphs/edmonds_blossom_algorithm.py index 7d5b6f5b2215..14c4542f05f7 100644 --- a/graphs/edmonds_blossom_algorithm.py +++ b/graphs/edmonds_blossom_algorithm.py @@ -1,15 +1,11 @@ from collections import deque, defaultdict -from typing import List, Tuple, Dict - UNMATCHED = -1 # Constant to represent unmatched vertices class EdmondsBlossomAlgorithm: @staticmethod - def maximum_matching( - edges: List[Tuple[int, int]], vertex_count: int - ) -> List[Tuple[int, int]]: + def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tuple[int, int]]: """ Finds the maximum matching in a general graph using Edmonds' Blossom Algorithm. @@ -20,7 +16,7 @@ def maximum_matching( >>> EdmondsBlossomAlgorithm.maximum_matching([(0, 1), (1, 2), (2, 3)], 4) [(0, 1), (2, 3)] """ - graph: Dict[int, List[int]] = defaultdict(list) + graph: dict[int, list[int]] = defaultdict(list) # Populate the graph with the edges for vertex_u, vertex_v in edges: @@ -84,16 +80,10 @@ def maximum_matching( EdmondsBlossomAlgorithm.contract_blossom( BlossomData( BlossomAuxData( - queue, - parent, - base, - in_blossom, - match, - in_queue, + queue, parent, base, in_blossom, + match, in_queue ), - current_vertex, - neighbor, - base_vertex, + current_vertex, neighbor, base_vertex ) ) @@ -106,9 +96,7 @@ def maximum_matching( return matching_result @staticmethod - def update_matching( - match: List[int], parent: List[int], current_vertex: int - ) -> None: + def update_matching(match: list[int], parent: list[int], current_vertex: int) -> None: """ Updates the matching along the augmenting path found. @@ -131,7 +119,7 @@ def update_matching( @staticmethod def find_base( - base: List[int], parent: List[int], vertex_u: int, vertex_v: int + base: list[int], parent: list[int], vertex_u: int, vertex_v: int ) -> int: """ Finds the base of a node in the blossom. @@ -167,7 +155,7 @@ def find_base( current_vertex_v = parent[current_vertex_v] @staticmethod - def contract_blossom(blossom_data: "BlossomData") -> None: + def contract_blossom(blossom_data: 'BlossomData') -> None: """ Contracts a blossom in the graph, modifying the base array and marking the vertices involved. @@ -183,9 +171,7 @@ def contract_blossom(blossom_data: "BlossomData") -> None: current_vertex_u = blossom_data.u while blossom_data.aux_data.base[current_vertex_u] != blossom_data.lca: base_u = blossom_data.aux_data.base[current_vertex_u] - match_base_u = blossom_data.aux_data.base[ - blossom_data.aux_data.match[current_vertex_u] - ] + match_base_u = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_u]] blossom_data.aux_data.in_blossom[base_u] = True blossom_data.aux_data.in_blossom[match_base_u] = True current_vertex_u = blossom_data.aux_data.parent[ @@ -195,9 +181,7 @@ def contract_blossom(blossom_data: "BlossomData") -> None: current_vertex_v = blossom_data.v while blossom_data.aux_data.base[current_vertex_v] != blossom_data.lca: base_v = blossom_data.aux_data.base[current_vertex_v] - match_base_v = blossom_data.aux_data.base[ - blossom_data.aux_data.match[current_vertex_v] - ] + match_base_v = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_v]] blossom_data.aux_data.in_blossom[base_v] = True blossom_data.aux_data.in_blossom[match_base_v] = True current_vertex_v = blossom_data.aux_data.parent[ @@ -219,13 +203,8 @@ class BlossomAuxData: """ def __init__( - self, - queue: deque, - parent: List[int], - base: List[int], - in_blossom: List[bool], - match: List[int], - in_queue: List[bool], + self, queue: deque, parent: list[int], base: list[int], in_blossom: list[bool], + match: list[int], in_queue: list[bool] ) -> None: self.queue = queue self.parent = parent From 9e1d5f30e023f72648e7621789d0f2ab4d5fe7bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 05:42:54 +0000 Subject: [PATCH 5/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- graphs/edmonds_blossom_algorithm.py | 39 +++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/graphs/edmonds_blossom_algorithm.py b/graphs/edmonds_blossom_algorithm.py index 14c4542f05f7..55217d7730b9 100644 --- a/graphs/edmonds_blossom_algorithm.py +++ b/graphs/edmonds_blossom_algorithm.py @@ -5,7 +5,9 @@ class EdmondsBlossomAlgorithm: @staticmethod - def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tuple[int, int]]: + def maximum_matching( + edges: list[tuple[int, int]], vertex_count: int + ) -> list[tuple[int, int]]: """ Finds the maximum matching in a general graph using Edmonds' Blossom Algorithm. @@ -80,10 +82,16 @@ def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tu EdmondsBlossomAlgorithm.contract_blossom( BlossomData( BlossomAuxData( - queue, parent, base, in_blossom, - match, in_queue + queue, + parent, + base, + in_blossom, + match, + in_queue, ), - current_vertex, neighbor, base_vertex + current_vertex, + neighbor, + base_vertex, ) ) @@ -96,7 +104,9 @@ def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tu return matching_result @staticmethod - def update_matching(match: list[int], parent: list[int], current_vertex: int) -> None: + def update_matching( + match: list[int], parent: list[int], current_vertex: int + ) -> None: """ Updates the matching along the augmenting path found. @@ -155,7 +165,7 @@ def find_base( current_vertex_v = parent[current_vertex_v] @staticmethod - def contract_blossom(blossom_data: 'BlossomData') -> None: + def contract_blossom(blossom_data: "BlossomData") -> None: """ Contracts a blossom in the graph, modifying the base array and marking the vertices involved. @@ -171,7 +181,9 @@ def contract_blossom(blossom_data: 'BlossomData') -> None: current_vertex_u = blossom_data.u while blossom_data.aux_data.base[current_vertex_u] != blossom_data.lca: base_u = blossom_data.aux_data.base[current_vertex_u] - match_base_u = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_u]] + match_base_u = blossom_data.aux_data.base[ + blossom_data.aux_data.match[current_vertex_u] + ] blossom_data.aux_data.in_blossom[base_u] = True blossom_data.aux_data.in_blossom[match_base_u] = True current_vertex_u = blossom_data.aux_data.parent[ @@ -181,7 +193,9 @@ def contract_blossom(blossom_data: 'BlossomData') -> None: current_vertex_v = blossom_data.v while blossom_data.aux_data.base[current_vertex_v] != blossom_data.lca: base_v = blossom_data.aux_data.base[current_vertex_v] - match_base_v = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_v]] + match_base_v = blossom_data.aux_data.base[ + blossom_data.aux_data.match[current_vertex_v] + ] blossom_data.aux_data.in_blossom[base_v] = True blossom_data.aux_data.in_blossom[match_base_v] = True current_vertex_v = blossom_data.aux_data.parent[ @@ -203,8 +217,13 @@ class BlossomAuxData: """ def __init__( - self, queue: deque, parent: list[int], base: list[int], in_blossom: list[bool], - match: list[int], in_queue: list[bool] + self, + queue: deque, + parent: list[int], + base: list[int], + in_blossom: list[bool], + match: list[int], + in_queue: list[bool], ) -> None: self.queue = queue self.parent = parent From 872469c9a7d6142414123a4872d871c0de44a290 Mon Sep 17 00:00:00 2001 From: Tarun Vishwakarma Date: Mon, 14 Oct 2024 11:26:16 +0530 Subject: [PATCH 6/6] Corrected ruff checks Again --- graphs/edmonds_blossom_algorithm.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/graphs/edmonds_blossom_algorithm.py b/graphs/edmonds_blossom_algorithm.py index 14c4542f05f7..f1e1aa65d700 100644 --- a/graphs/edmonds_blossom_algorithm.py +++ b/graphs/edmonds_blossom_algorithm.py @@ -1,11 +1,13 @@ -from collections import deque, defaultdict +from collections import defaultdict, deque UNMATCHED = -1 # Constant to represent unmatched vertices class EdmondsBlossomAlgorithm: @staticmethod - def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tuple[int, int]]: + def maximum_matching( + edges: list[tuple[int, int]], vertex_count: int + ) -> list[tuple[int, int]]: """ Finds the maximum matching in a general graph using Edmonds' Blossom Algorithm. @@ -55,7 +57,8 @@ def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tu continue # Avoid self-loops if parent[neighbor] == UNMATCHED: - # Case 1: neighbor is unmatched, we've found an augmenting path + # Case 1: neighbor is unmatched, we've found + # an augmenting path if match[neighbor] == UNMATCHED: parent[neighbor] = current_vertex augmenting_path_found = True @@ -64,7 +67,8 @@ def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tu ) break - # Case 2: neighbor is matched, add neighbor's match to the queue + # Case 2: neighbor is matched, add neighbor's + # match to the queue matched_vertex = match[neighbor] parent[neighbor] = current_vertex parent[matched_vertex] = neighbor @@ -72,7 +76,8 @@ def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tu queue.append(matched_vertex) in_queue[matched_vertex] = True else: - # Case 3: Both current_vertex and neighbor have a parent; check for a cycle/blossom + # Case 3: Both current_vertex and neighbor + # have a parent; check for a cycle/blossom base_vertex = EdmondsBlossomAlgorithm.find_base( base, parent, current_vertex, neighbor ) @@ -96,7 +101,9 @@ def maximum_matching(edges: list[tuple[int, int]], vertex_count: int) -> list[tu return matching_result @staticmethod - def update_matching(match: list[int], parent: list[int], current_vertex: int) -> None: + def update_matching( + match: list[int], parent: list[int], current_vertex: int + ) -> None: """ Updates the matching along the augmenting path found. @@ -171,7 +178,9 @@ def contract_blossom(blossom_data: 'BlossomData') -> None: current_vertex_u = blossom_data.u while blossom_data.aux_data.base[current_vertex_u] != blossom_data.lca: base_u = blossom_data.aux_data.base[current_vertex_u] - match_base_u = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_u]] + match_base_u = blossom_data.aux_data.base[ + blossom_data.aux_data.match[current_vertex_u] + ] blossom_data.aux_data.in_blossom[base_u] = True blossom_data.aux_data.in_blossom[match_base_u] = True current_vertex_u = blossom_data.aux_data.parent[ @@ -181,7 +190,9 @@ def contract_blossom(blossom_data: 'BlossomData') -> None: current_vertex_v = blossom_data.v while blossom_data.aux_data.base[current_vertex_v] != blossom_data.lca: base_v = blossom_data.aux_data.base[current_vertex_v] - match_base_v = blossom_data.aux_data.base[blossom_data.aux_data.match[current_vertex_v]] + match_base_v = blossom_data.aux_data.base[ + blossom_data.aux_data.match[current_vertex_v] + ] blossom_data.aux_data.in_blossom[base_v] = True blossom_data.aux_data.in_blossom[match_base_v] = True current_vertex_v = blossom_data.aux_data.parent[