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
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
223 changes: 223 additions & 0 deletions graphs/edmond_blossom_algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
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
parent = [
UNMATCHED
] * vertex_count # Tracks the parent of each vertex in the augmenting path
base = list(
range(vertex_count)
) # Base of each vertex (initially the vertex itself)
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

# 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
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
)

# 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
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
)
if base_u != UNMATCHED:
aux_data = BlossomAuxData(
queue, parent, base, in_blossom, match, in_queue
)
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.

Check failure on line 157 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:157:89: E501 Line too long (101 > 88)

:param blossom_data: An object containing the necessary data to perform the contraction.

Check failure on line 159 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

graphs/edmond_blossom_algorithm.py:159:89: E501 Line too long (96 > 88)
"""
# Mark all vertices in the blossom
for x in range(

Check failure on line 162 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (B020)

graphs/edmond_blossom_algorithm.py:162:13: B020 Loop control variable `x` overrides iterable it iterates
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]],

Check failure on line 165 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F821)

graphs/edmond_blossom_algorithm.py:165:70: F821 Undefined name `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(

Check failure on line 172 in graphs/edmond_blossom_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (B020)

graphs/edmond_blossom_algorithm.py:172:13: B020 Loop control variable `x` overrides iterable it iterates
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)
Loading
Loading