Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Edmonds Blossom Algorithm #12043 #12044

Closed
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions graphs/edmond_blossom_algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
from collections import deque, defaultdict

UNMATCHED = -1 # Constant to represent unmatched vertices

Check failure on line 3 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

graphs/edmond_blossom_algorithm.py:1:1: I001 Import block is un-sorted or un-formatted

class EdmondsBlossomAlgorithm:

@staticmethod
def maximum_matching(edges, vertex_count):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: maximum_matching. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function maximum_matching

Please provide type hint for the parameter: edges

Please provide type hint for the parameter: vertex_count

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: maximum_matching. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function maximum_matching

Please provide type hint for the parameter: edges

Please provide type hint for the parameter: vertex_count

"""
Finds the maximum matching in a general graph using Edmonds' Blossom Algorithm.

:param edges: List of edges in the graph (each edge is a tuple of two vertices).
:param vertex_count: The number of vertices in the graph.
:return: A list of matched pairs of vertices (tuples).
"""
# Initialize the adjacency list for the graph
graph = defaultdict(list)

# Populate the graph with the edges
for u, v in edges:
graph[u].append(v)
graph[v].append(u)

# Initialize matching array and other auxiliary structures
match = [UNMATCHED] * vertex_count # Stores matches for each vertex, initially unmatched

Check failure on line 25 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:25:89: E501 Line too long (97 > 88)
parent = [UNMATCHED] * vertex_count # Tracks the parent of each vertex in the augmenting path

Check failure on line 26 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:26:89: E501 Line too long (102 > 88)
base = list(range(vertex_count)) # Base of each vertex (initially the vertex itself)

Check failure on line 27 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:27:89: E501 Line too long (93 > 88)
in_blossom = [False] * vertex_count # Marks if a vertex is part of a blossom
in_queue = [False] * vertex_count # Marks if a vertex is already in the BFS queue

Check failure on line 29 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:29:89: E501 Line too long (90 > 88)

# Iterate over each vertex to start the matching process
for u in range(vertex_count):
if match[u] == UNMATCHED: # Proceed only if the vertex is unmatched
# Reset auxiliary data structures for BFS
parent = [UNMATCHED] * vertex_count
base = list(range(vertex_count)) # Each vertex is its own base initially

Check failure on line 36 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:36:89: E501 Line too long (89 > 88)
in_blossom = [False] * vertex_count
in_queue = [False] * vertex_count

queue = deque([u]) # Initialize BFS queue
in_queue[u] = True # Mark u as in the queue

augmenting_path_found = False # Flag to track if an augmenting path is found

Check failure on line 43 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:43:89: E501 Line too long (93 > 88)

# BFS to find augmenting paths
while queue and not augmenting_path_found:
current = queue.popleft() # Get the next vertex from the queue

for y in graph[current]:
# Skip if this edge is the current matching
if match[current] == y:
continue

# Avoid self-loops
if base[current] == base[y]:
continue

# Case 1: y is unmatched, we found an augmenting path
if parent[y] == UNMATCHED:
if match[y] == UNMATCHED:
parent[y] = current
augmenting_path_found = True
EdmondsBlossomAlgorithm.update_matching(match, parent, y) # Augment the path

Check failure on line 63 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:63:89: E501 Line too long (109 > 88)
break

# Case 2: y is matched, add y's match to the queue
z = match[y]
parent[y] = current
parent[z] = y
if not in_queue[z]:
queue.append(z)
in_queue[z] = True
else:
# Case 3: A cycle (blossom) is detected
base_u = EdmondsBlossomAlgorithm.find_base(base, parent, current, y)

Check failure on line 75 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:75:89: E501 Line too long (96 > 88)
if base_u != UNMATCHED:
aux_data = BlossomAuxData(queue, parent, base, in_blossom, match, in_queue)

Check failure on line 77 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:77:89: E501 Line too long (107 > 88)
EdmondsBlossomAlgorithm.contract_blossom(BlossomData(aux_data, current, y, base_u))

# Collect the final matching result
matching_result = []
for v in range(vertex_count):
if match[v] != UNMATCHED and v < match[v]:
matching_result.append((v, match[v]))

return matching_result

@staticmethod
def update_matching(match, parent, u):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: update_matching. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function update_matching

Please provide type hint for the parameter: match

Please provide type hint for the parameter: parent

Please provide type hint for the parameter: u

Please provide descriptive name for the parameter: u

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: update_matching. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function update_matching

Please provide type hint for the parameter: match

Please provide type hint for the parameter: parent

Please provide type hint for the parameter: u

Please provide descriptive name for the parameter: u

"""
Updates the matching array by augmenting along the found path.

:param match: The array representing the matching for each vertex.
:param parent: The parent array, used to backtrack the augmenting path.
:param u: The end vertex of the augmenting path.
"""
while u != UNMATCHED:
v = parent[u]
next_node = match[v]
match[v] = u
match[u] = v
u = next_node

@staticmethod
def find_base(base, parent, u, v):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: find_base. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function find_base

Please provide type hint for the parameter: base

Please provide type hint for the parameter: parent

Please provide type hint for the parameter: u

Please provide descriptive name for the parameter: u

Please provide type hint for the parameter: v

Please provide descriptive name for the parameter: v

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: find_base. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function find_base

Please provide type hint for the parameter: base

Please provide type hint for the parameter: parent

Please provide type hint for the parameter: u

Please provide descriptive name for the parameter: u

Please provide type hint for the parameter: v

Please provide descriptive name for the parameter: v

"""
Finds the lowest common ancestor (LCA) of two vertices in the blossom.

:param base: The array storing the base of each vertex in the current blossom.
:param parent: The parent array used to trace the path.
:param u: One end of the edge being analyzed.
:param v: The other end of the edge being analyzed.
:return: The base of the node (LCA) or UNMATCHED if not found.
"""
visited = [False] * len(base)

# Mark all ancestors of u
current_u = u
while True:
current_u = base[current_u]
visited[current_u] = True
if parent[current_u] == UNMATCHED:
break
current_u = parent[current_u]

# Find the common ancestor with v
current_v = v
while True:
current_v = base[current_v]
if visited[current_v]:
return current_v
current_v = parent[current_v]

@staticmethod
def contract_blossom(blossom_data):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: contract_blossom. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function contract_blossom

Please provide type hint for the parameter: blossom_data

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: contract_blossom. If the function does not return a value, please provide the type hint as: def function() -> None:

As there is no test file in this pull request nor any test function or class in the file graphs/edmond_blossom_algorithm.py, please provide doctest for the function contract_blossom

Please provide type hint for the parameter: blossom_data

"""
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.
"""
# Mark all vertices in the blossom
for x in range(blossom_data.u, blossom_data.aux_data.base[blossom_data.u] != blossom_data.lca, blossom_data.aux_data.parent[blossom_data.aux_data.match[x]]):
base_x = blossom_data.aux_data.base[x]
match_base_x = blossom_data.aux_data.base[blossom_data.aux_data.match[x]]
blossom_data.aux_data.in_blossom[base_x] = True
blossom_data.aux_data.in_blossom[match_base_x] = True

for x in range(blossom_data.v, blossom_data.aux_data.base[blossom_data.v] != blossom_data.lca, blossom_data.aux_data.parent[blossom_data.aux_data.match[x]]):
base_x = blossom_data.aux_data.base[x]
match_base_x = blossom_data.aux_data.base[blossom_data.aux_data.match[x]]
blossom_data.aux_data.in_blossom[base_x] = True
blossom_data.aux_data.in_blossom[match_base_x] = True

# 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:
"""
A helper class to encapsulate auxiliary data used during blossom contraction.
"""
def __init__(self, queue, parent, base, in_blossom, match, in_queue):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: queue

Please provide type hint for the parameter: parent

Please provide type hint for the parameter: base

Please provide type hint for the parameter: in_blossom

Please provide type hint for the parameter: match

Please provide type hint for the parameter: in_queue

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: queue

Please provide type hint for the parameter: parent

Please provide type hint for the parameter: base

Please provide type hint for the parameter: in_blossom

Please provide type hint for the parameter: match

Please provide type hint for the parameter: in_queue

self.queue = queue # The BFS queue used for traversal
self.parent = parent # The parent array for each vertex
self.base = base # The base array indicating the base of each vertex
self.in_blossom = in_blossom # Indicates whether a vertex is part of a blossom
self.match = match # The matching array
self.in_queue = in_queue # Indicates whether a vertex is already in the queue


class BlossomData:
"""
A helper class to store parameters necessary for blossom contraction.
"""
def __init__(self, aux_data, u, v, lca):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: aux_data

Please provide type hint for the parameter: u

Please provide descriptive name for the parameter: u

Please provide type hint for the parameter: v

Please provide descriptive name for the parameter: v

Please provide type hint for the parameter: lca

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide return type hint for the function: __init__. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: aux_data

Please provide type hint for the parameter: u

Please provide descriptive name for the parameter: u

Please provide type hint for the parameter: v

Please provide descriptive name for the parameter: v

Please provide type hint for the parameter: lca

self.aux_data = aux_data # The auxiliary data object
self.u = u # One vertex in the current edge
self.v = v # The other vertex in the current edge
self.lca = lca # The lowest common ancestor (base) of the current blossom


# Example usage:
if __name__ == "__main__":
# Example graph with 6 vertices
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (0, 5), (2, 4)]
vertex_count = 6
result = EdmondsBlossomAlgorithm.maximum_matching(edges, vertex_count)
print("Maximum Matching Pairs:", result)