mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-12-04 18:30:17 +00:00
Changes in the main file and test file as test were failing due to stuck in an infinite loop.
This commit is contained in:
parent
46dd5fd3df
commit
991a37e9ff
|
@ -1,233 +1,9 @@
|
||||||
from collections import defaultdict, deque
|
from collections import 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]]:
|
|
||||||
"""
|
|
||||||
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.vertex_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.vertex_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:
|
class BlossomAuxData:
|
||||||
"""
|
def __init__(self, queue: deque, parent: list[int], base: list[int],
|
||||||
Auxiliary data class to encapsulate common parameters for the blossom operations.
|
in_blossom: list[bool], match: list[int], in_queue: list[bool]):
|
||||||
"""
|
|
||||||
|
|
||||||
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.queue = queue
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.base = base
|
self.base = base
|
||||||
|
@ -235,25 +11,153 @@ class BlossomAuxData:
|
||||||
self.match = match
|
self.match = match
|
||||||
self.in_queue = in_queue
|
self.in_queue = in_queue
|
||||||
|
|
||||||
|
|
||||||
class BlossomData:
|
class BlossomData:
|
||||||
"""
|
def __init__(self, aux_data: BlossomAuxData, u: int, v: int, lca: int):
|
||||||
BlossomData class with reduced parameters.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, aux_data: BlossomAuxData, vertex_u: int, vertex_v: int, lca: int
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Initialize BlossomData with auxiliary data, two vertices,
|
|
||||||
and the lowest common ancestor.
|
|
||||||
|
|
||||||
:param aux_data: Auxiliary data used in the algorithm
|
|
||||||
:param vertex_u: First vertex involved in the blossom
|
|
||||||
:param vertex_v: Second vertex involved in the blossom
|
|
||||||
:param lca: Lowest common ancestor (base) of the two vertices
|
|
||||||
"""
|
|
||||||
self.aux_data = aux_data
|
self.aux_data = aux_data
|
||||||
self.vertex_u = vertex_u
|
self.u = u
|
||||||
self.vertex_v = vertex_v
|
self.v = v
|
||||||
self.lca = lca
|
self.lca = lca
|
||||||
|
|
||||||
|
class EdmondsBlossomAlgorithm:
|
||||||
|
UNMATCHED = -1 # Constant to represent unmatched vertices
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def maximum_matching(edges: list[list[int]], vertex_count: int) -> list[list[int]]:
|
||||||
|
graph = [[] for _ in range(vertex_count)]
|
||||||
|
|
||||||
|
# Populate the graph with the edges
|
||||||
|
for edge in edges:
|
||||||
|
u, v = edge
|
||||||
|
graph[u].append(v)
|
||||||
|
graph[v].append(u)
|
||||||
|
|
||||||
|
# All vertices are initially unmatched
|
||||||
|
match = [EdmondsBlossomAlgorithm.UNMATCHED] * vertex_count
|
||||||
|
parent = [EdmondsBlossomAlgorithm.UNMATCHED] * vertex_count
|
||||||
|
base = list(range(vertex_count)) # Each vertex is its own base initially
|
||||||
|
# Indicates if a vertex is part of a blossom
|
||||||
|
in_blossom = [False] * vertex_count
|
||||||
|
in_queue = [False] * vertex_count # Tracks vertices in the BFS queue
|
||||||
|
|
||||||
|
# Main logic for finding maximum matching
|
||||||
|
for u in range(vertex_count):
|
||||||
|
if match[u] == EdmondsBlossomAlgorithm.UNMATCHED:
|
||||||
|
# BFS initialization
|
||||||
|
parent = [EdmondsBlossomAlgorithm.UNMATCHED] * vertex_count
|
||||||
|
base = list(range(vertex_count))
|
||||||
|
in_blossom = [False] * vertex_count
|
||||||
|
in_queue = [False] * vertex_count
|
||||||
|
|
||||||
|
queue = deque([u])
|
||||||
|
in_queue[u] = True
|
||||||
|
|
||||||
|
augmenting_path_found = False
|
||||||
|
|
||||||
|
# BFS to find augmenting paths
|
||||||
|
while queue and not augmenting_path_found:
|
||||||
|
current = queue.popleft()
|
||||||
|
for y in graph[current]:
|
||||||
|
if match[current] == y:
|
||||||
|
# Skip if we are
|
||||||
|
# looking at the same edge
|
||||||
|
# as the current match
|
||||||
|
continue
|
||||||
|
|
||||||
|
if base[current] == base[y]:
|
||||||
|
continue # Avoid self-loops
|
||||||
|
|
||||||
|
if parent[y] == EdmondsBlossomAlgorithm.UNMATCHED:
|
||||||
|
# Case 1: y is unmatched, we've found an augmenting path
|
||||||
|
if match[y] == EdmondsBlossomAlgorithm.UNMATCHED:
|
||||||
|
parent[y] = current
|
||||||
|
augmenting_path_found = True
|
||||||
|
# Augment along this path
|
||||||
|
(EdmondsBlossomAlgorithm
|
||||||
|
.update_matching(match, parent, y))
|
||||||
|
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: Both current and y have a parent;
|
||||||
|
# check for a cycle/blossom
|
||||||
|
base_u = EdmondsBlossomAlgorithm.find_base(base,
|
||||||
|
parent, current, y)
|
||||||
|
if base_u != EdmondsBlossomAlgorithm.UNMATCHED:
|
||||||
|
EdmondsBlossomAlgorithm.contract_blossom(BlossomData(
|
||||||
|
BlossomAuxData(queue,
|
||||||
|
parent,
|
||||||
|
base,
|
||||||
|
in_blossom,
|
||||||
|
match,
|
||||||
|
in_queue),
|
||||||
|
current, y, base_u))
|
||||||
|
|
||||||
|
# Create result list of matched pairs
|
||||||
|
matching_result = []
|
||||||
|
for v in range(vertex_count):
|
||||||
|
if match[v] != EdmondsBlossomAlgorithm.UNMATCHED and v < match[v]:
|
||||||
|
matching_result.append([v, match[v]])
|
||||||
|
|
||||||
|
return matching_result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_matching(match: list[int], parent: list[int], u: int):
|
||||||
|
while u != EdmondsBlossomAlgorithm.UNMATCHED:
|
||||||
|
v = parent[u]
|
||||||
|
next_match = match[v]
|
||||||
|
match[v] = u
|
||||||
|
match[u] = v
|
||||||
|
u = next_match
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def find_base(base: list[int], parent: list[int], u: int, v: int) -> int:
|
||||||
|
visited = [False] * len(base)
|
||||||
|
|
||||||
|
# Mark ancestors of u
|
||||||
|
current_u = u
|
||||||
|
while True:
|
||||||
|
current_u = base[current_u]
|
||||||
|
visited[current_u] = True
|
||||||
|
if parent[current_u] == EdmondsBlossomAlgorithm.UNMATCHED:
|
||||||
|
break
|
||||||
|
current_u = parent[current_u]
|
||||||
|
|
||||||
|
# Find the common ancestor of 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: BlossomData):
|
||||||
|
for x in range(blossom_data.u,
|
||||||
|
blossom_data.aux_data.base[blossom_data.u] != blossom_data.lca):
|
||||||
|
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):
|
||||||
|
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]]:
|
||||||
|
# Contract to the lowest common ancestor
|
||||||
|
blossom_data.aux_data.base[i] = blossom_data.lca
|
||||||
|
if not blossom_data.aux_data.in_queue[i]:
|
||||||
|
# Add to queue if not already present
|
||||||
|
blossom_data.aux_data.queue.append(i)
|
||||||
|
blossom_data.aux_data.in_queue[i] = True
|
||||||
|
|
|
@ -1,71 +1,73 @@
|
||||||
import unittest
|
import unittest
|
||||||
from collections import deque
|
|
||||||
|
|
||||||
from graphs.edmonds_blossom_algorithm import (
|
from graphs.edmonds_blossom_algorithm import EdmondsBlossomAlgorithm
|
||||||
UNMATCHED,
|
|
||||||
BlossomAuxData,
|
|
||||||
BlossomData,
|
|
||||||
EdmondsBlossomAlgorithm,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestEdmondsBlossomAlgorithm(unittest.TestCase):
|
class EdmondsBlossomAlgorithmTest(unittest.TestCase):
|
||||||
def test_maximum_matching(self):
|
|
||||||
# Test case: Basic matching in a simple graph
|
|
||||||
edges = [(0, 1), (1, 2), (2, 3)]
|
|
||||||
vertex_count = 4
|
|
||||||
result = EdmondsBlossomAlgorithm.maximum_matching(edges, vertex_count)
|
|
||||||
expected_result = [(0, 1), (2, 3)]
|
|
||||||
assert result == expected_result
|
|
||||||
|
|
||||||
# Test case: Graph with no matching
|
def convert_matching_to_array(self, matching):
|
||||||
edges = []
|
""" Helper method to convert a
|
||||||
vertex_count = 4
|
list of matching pairs into a sorted 2D array.
|
||||||
result = EdmondsBlossomAlgorithm.maximum_matching(edges, vertex_count)
|
"""
|
||||||
expected_result = []
|
# Convert the list of pairs into a list of lists
|
||||||
assert result == expected_result
|
result = [list(pair) for pair in matching]
|
||||||
|
|
||||||
def test_update_matching(self):
|
# Sort each individual pair for consistency
|
||||||
# Test case: Update matching on a simple augmenting path
|
for pair in result:
|
||||||
match = [UNMATCHED, UNMATCHED, UNMATCHED]
|
pair.sort()
|
||||||
parent = [1, 0, UNMATCHED]
|
|
||||||
current_vertex = 2
|
|
||||||
EdmondsBlossomAlgorithm.update_matching(match, parent, current_vertex)
|
|
||||||
expected_result = [1, 0, UNMATCHED]
|
|
||||||
assert match == expected_result
|
|
||||||
|
|
||||||
def test_find_base(self):
|
# Sort the array of pairs to ensure consistent order
|
||||||
# Test case: Find base of blossom
|
result.sort(key=lambda x: x[0])
|
||||||
base = [0, 1, 2, 3]
|
return result
|
||||||
parent = [1, 0, UNMATCHED, UNMATCHED]
|
|
||||||
vertex_u = 2
|
|
||||||
vertex_v = 3
|
|
||||||
result = EdmondsBlossomAlgorithm.find_base(base, parent, vertex_u, vertex_v)
|
|
||||||
expected_result = 2
|
|
||||||
assert result == expected_result
|
|
||||||
|
|
||||||
def test_contract_blossom(self):
|
def test_case_1(self):
|
||||||
# Test case: Contracting a simple blossom
|
""" Test Case 1: A triangle graph where vertices 0, 1, and 2 form a cycle. """
|
||||||
queue = deque()
|
edges = [[0, 1], [1, 2], [2, 0]]
|
||||||
parent = [UNMATCHED, UNMATCHED, UNMATCHED]
|
matching = EdmondsBlossomAlgorithm.maximum_matching(edges, 3)
|
||||||
base = [0, 1, 2]
|
|
||||||
in_blossom = [False] * 3
|
|
||||||
match = [UNMATCHED, UNMATCHED, UNMATCHED]
|
|
||||||
in_queue = [False] * 3
|
|
||||||
aux_data = BlossomAuxData(queue, parent, base, in_blossom, match, in_queue)
|
|
||||||
blossom_data = BlossomData(aux_data, 0, 1, 2)
|
|
||||||
|
|
||||||
# Contract the blossom
|
expected = [[0, 1]]
|
||||||
EdmondsBlossomAlgorithm.contract_blossom(blossom_data)
|
assert expected == self.convert_matching_to_array(matching)
|
||||||
|
|
||||||
# Ensure base is updated correctly
|
def test_case_2(self):
|
||||||
assert aux_data.base == [2, 2, 2]
|
""" Test Case 2: A disconnected graph with two components. """
|
||||||
# Check that the queue has the contracted vertices
|
edges = [[0, 1], [1, 2], [3, 4]]
|
||||||
assert 0 in aux_data.queue
|
matching = EdmondsBlossomAlgorithm.maximum_matching(edges, 5)
|
||||||
assert 1 in aux_data.queue
|
|
||||||
assert aux_data.in_queue[0]
|
expected = [[0, 1], [3, 4]]
|
||||||
assert aux_data.in_queue[1]
|
assert expected == self.convert_matching_to_array(matching)
|
||||||
|
|
||||||
|
def test_case_3(self):
|
||||||
|
""" Test Case 3: A cycle graph with an additional edge outside the cycle. """
|
||||||
|
edges = [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5]]
|
||||||
|
matching = EdmondsBlossomAlgorithm.maximum_matching(edges, 6)
|
||||||
|
|
||||||
|
expected = [[0, 1], [2, 3], [4, 5]]
|
||||||
|
assert expected == self.convert_matching_to_array(matching)
|
||||||
|
|
||||||
|
def test_case_no_matching(self):
|
||||||
|
""" Test Case 4: A graph with no edges. """
|
||||||
|
edges = [] # No edges
|
||||||
|
matching = EdmondsBlossomAlgorithm.maximum_matching(edges, 3)
|
||||||
|
|
||||||
|
expected = []
|
||||||
|
assert expected == self.convert_matching_to_array(matching)
|
||||||
|
|
||||||
|
def test_case_large_graph(self):
|
||||||
|
""" Test Case 5: A complex graph with multiple cycles and extra edges. """
|
||||||
|
edges = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0], [1, 4], [2, 5]]
|
||||||
|
matching = EdmondsBlossomAlgorithm.maximum_matching(edges, 6)
|
||||||
|
|
||||||
|
# Check if the size of the matching is correct (i.e., 3 pairs)
|
||||||
|
assert len(matching) == 3
|
||||||
|
|
||||||
|
# Check that the result contains valid pairs (any order is fine)
|
||||||
|
possible_matching_1 = [[0, 1], [2, 5], [3, 4]]
|
||||||
|
possible_matching_2 = [[0, 1], [2, 3], [4, 5]]
|
||||||
|
result = self.convert_matching_to_array(matching)
|
||||||
|
|
||||||
|
# Assert that the result is one of the valid maximum matchings
|
||||||
|
assert result in (possible_matching_1, possible_matching_2)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user