Skip to content

Commit

Permalink
using timestamps when marking visited vertices in flow and matching a…
Browse files Browse the repository at this point in the history
…lgorithms
  • Loading branch information
xtof-durr committed Dec 26, 2023
1 parent 45b9fa8 commit edc548e
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 64 deletions.
35 changes: 14 additions & 21 deletions tests/test_tryalgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def isclose(a, b, rel_tol, abs_tol):
from tryalgo.bfs import bfs, bfs_implicit
from tryalgo.biconnected_components import cut_nodes_edges, cut_nodes_edges2
from tryalgo.binary_search import continuous_binary_search, discrete_binary_search, optimized_binary_search, optimized_binary_search_lower, ternary_search
from tryalgo.bipartite_matching import max_bipartite_matching, max_bipartite_matching2
from tryalgo.bipartite_matching import max_bipartite_matching
from tryalgo.bipartite_vertex_cover import bipartite_vertex_cover
from tryalgo.closest_points import closest_points
from tryalgo.closest_values import closest_values
Expand Down Expand Up @@ -416,39 +416,32 @@ def half_graph(n):
V = range(n)
return [[v for v in V if v > u] for u in V]

def random_graph(n, p):
def random_graph(n, p): # only used for performance tests
V = range(n)
return [[v for v in V if random.random() <= p] for u in V]

self.assertEqual([None], max_bipartite_matching([[]]))
self.assertEqual([], max_bipartite_matching2([[]]))
self.assertEqual([None, None], max_bipartite_matching([[], []]))
self.assertEqual([], max_bipartite_matching2([[], []]))
self.assertEqual([0, None], max_bipartite_matching([[0], [0]]))
self.assertEqual([0], max_bipartite_matching2([[0], [0]]))
self.assertEqual([], max_bipartite_matching([[]]))
self.assertEqual([], max_bipartite_matching([[], []]))
self.assertEqual([0], max_bipartite_matching([[0], [0]]))
self.assertEqual([0, 1], max_bipartite_matching([[0], [0, 1]]))
self.assertEqual([0, 1], max_bipartite_matching2([[0], [0, 1]]))
self.assertEqual([0, 1, 2, 4, None], max_bipartite_matching(
self.assertEqual([0, 1, 2, 4], max_bipartite_matching(
[[0], [0, 1], [2, 3], [1], [0, 3]]))
self.assertEqual([0, 1, 2, 4], max_bipartite_matching2(
[[0], [0, 1], [2, 3], [1], [0, 3]]))
n = 1000
G = random_graph(n, 0.9)
now = time.time()
max_bipartite_matching(G)
# self.assertEqual([None] + list(range(n-1)), max_bipartite_matching2(G))
print("Time = ", time.time() - now)
n = 100
G = half_graph(n)
self.assertEqual([None]+list(range(n-1)), max_bipartite_matching(G))


def test_bipartite_vertex_cover(self):
for n in range(
1, 10): # try different random bipartite graphs
2, 10): # try different random bipartite graphs
V = range(n)
V1 = range(n - 1) # all vertices, except n-1
bigraph = [[] for u in V]
bigraph[n - 1] = [n - 1] # add edge (n-1,n-1) to make both sides equal size
for i in range(100):
for _ in range(n * n // 8):
u = random.choice(V)
v = random.choice(V)
u = random.choice(V1)
v = random.choice(V1)
if v not in bigraph[u]:
bigraph[u].append(v)
matching = max_bipartite_matching(bigraph)
Expand Down
46 changes: 9 additions & 37 deletions tryalgo/bipartite_matching.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,24 @@
jill-jenn vie et christoph durr - 2014-2018
"""

__all__ = ["max_bipartite_matching", "max_bipartite_matching2"]
__all__ = ["max_bipartite_matching"]


# snip{
def augment(u, bigraph, visit, match):
"""augment """
for v in bigraph[u]:
if not visit[v]:
visit[v] = True
if match[v] is None or augment(match[v], bigraph,
visit, match):
match[v] = u # found an augmenting path
return True
return False


def max_bipartite_matching(bigraph):
"""Bipartie maximum matching
:param bigraph: adjacency list, index = vertex in U,
value = neighbor list in V
:assumption: U = V = {0, 1, 2, ..., n - 1} for n = len(bigraph)
:returns: matching list, match[v] == u iff (u, v) in matching
:complexity: `O(|V|*|E|)`
"""
n = len(bigraph) # same domain for U and V
match = [None] * n
for u in range(n):
if bigraph[u]: # if u is not an isolated vertex
augment(u, bigraph, [False] * n, match)
return match
# snip}

def augment2(u, bigraph, visit, timestamp, match):
"""augment """
# snip{
def augment(u, bigraph, visit, timestamp, match):
""" find augmenting path starting from u, by recursive DFS """
for v in bigraph[u]:
if visit[v] < timestamp:
visit[v] = timestamp
if match[v] is None or augment2(match[v], bigraph,
if match[v] is None or augment(match[v], bigraph,
visit, timestamp, match):
match[v] = u # found an augmenting path
return True
return False


def max_bipartite_matching2(bigraph):
def max_bipartite_matching(bigraph):
"""Bipartie maximum matching
:param bigraph: adjacency list, index = vertex in U,
Expand All @@ -60,11 +32,11 @@ def max_bipartite_matching2(bigraph):
:returns: matching list, match[v] == u iff (u, v) in matching
:complexity: `O(|V|*|E|)`
"""
nU = len(bigraph)
nU = len(bigraph) # nU = cardinality of U, nV = card. of V
nV = max(max(adjlist, default=-1) for adjlist in bigraph) + 1
match = [None] * nV
visit = [-1] * nV
visit = [-1] * nV # timestamp of last visit
for u in range(nU):
augment2(u, bigraph, visit, u, match)
augment(u, bigraph, visit, u, match)
return match
# snip}
14 changes: 8 additions & 6 deletions tryalgo/ford_fulkerson.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@

# snip{
# pylint: disable=too-many-arguments
def _augment(graph, capacity, flow, val, u, target, visit):
def _augment(graph, capacity, flow, val, u, target, visit, timestamp):
"""Find an augmenting path from u to target with value at most val"""
visit[u] = True
visit[u] = timestamp
if u == target:
return val
for v in graph[u]:
cuv = capacity[u][v]
if not visit[v] and cuv > flow[u][v]: # reachable arc
if visit[v] < timestamp and cuv > flow[u][v]: # reachable arc
res = min(val, cuv - flow[u][v])
delta = _augment(graph, capacity, flow, res, v, target, visit)
delta = _augment(graph, capacity, flow, res, v, target, visit, timestamp)
if delta > 0:
flow[u][v] += delta # augment flow
flow[v][u] -= delta
Expand All @@ -44,7 +44,9 @@ def ford_fulkerson(graph, capacity, s, t):
n = len(graph)
flow = [[0] * n for _ in range(n)]
INF = float('inf')
while _augment(graph, capacity, flow, INF, s, t, [False] * n) > 0:
pass # work already done in _augment
visit = [-1] * n
timestamp = 0
while _augment(graph, capacity, flow, INF, s, t, visit, timestamp) > 0:
timestamp += 1
return (flow, sum(flow[s])) # flow network, amount of flow
# snip}

0 comments on commit edc548e

Please sign in to comment.