Skip to content

Commit 6f563df

Browse files
authored
Added Edmond-Karp algorithm for computing max flow (#530)
1 parent 2482adb commit 6f563df

File tree

3 files changed

+125
-3
lines changed

3 files changed

+125
-3
lines changed

pydatastructs/graphs/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
shortest_paths,
1818
all_pair_shortest_paths,
1919
topological_sort,
20-
topological_sort_parallel
20+
topological_sort_parallel,
21+
max_flow
2122
)
2223

2324
__all__.extend(algorithms.__all__)

pydatastructs/graphs/algorithms.py

+55-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
'shortest_paths',
2323
'all_pair_shortest_paths',
2424
'topological_sort',
25-
'topological_sort_parallel'
25+
'topological_sort_parallel',
26+
'max_flow'
2627
]
2728

2829
Stack = Queue = deque
@@ -1064,3 +1065,56 @@ def _job(graph: Graph, u: str):
10641065
if len(L) != num_vertices:
10651066
raise ValueError("Graph is not acyclic.")
10661067
return L
1068+
1069+
1070+
def _breadth_first_search_max_flow(graph: Graph, source_node, sink_node, flow_passed):
1071+
bfs_queue = Queue()
1072+
parent, currentPathC = {}, {}
1073+
parent[source_node] = -2
1074+
currentPathC[source_node] = float('inf')
1075+
bfs_queue.append(source_node)
1076+
while len(bfs_queue) != 0:
1077+
curr_node = bfs_queue.popleft()
1078+
next_nodes = graph.neighbors(curr_node)
1079+
if len(next_nodes) != 0:
1080+
for next_node in next_nodes:
1081+
capacity = graph.get_edge(curr_node, next_node.name).value
1082+
fp = flow_passed.get((curr_node, next_node.name), 0)
1083+
if capacity and parent.get(next_node.name, False) is False and capacity - fp> 0:
1084+
parent[next_node.name] = curr_node
1085+
next_flow = min(currentPathC[curr_node], capacity - fp)
1086+
currentPathC[next_node.name] = next_flow
1087+
if next_node.name == sink_node:
1088+
return (next_flow, parent)
1089+
bfs_queue.append(next_node.name)
1090+
return (0, parent)
1091+
1092+
1093+
def _max_flow_edmonds_karp_(graph: Graph, source, sink):
1094+
m_flow = 0
1095+
flow_passed = {}
1096+
new_flow, parent = _breadth_first_search_max_flow(graph, source, sink, flow_passed)
1097+
while new_flow != 0:
1098+
m_flow += new_flow
1099+
current = sink
1100+
while current != source:
1101+
prev = parent[current]
1102+
fp = flow_passed.get((prev, current), 0)
1103+
flow_passed[(prev, current)] = fp + new_flow
1104+
fp = flow_passed.get((current, prev), 0)
1105+
flow_passed[(current, prev)] = fp - new_flow
1106+
current = prev
1107+
new_flow, parent = _breadth_first_search_max_flow(graph, source, sink, flow_passed)
1108+
return m_flow
1109+
1110+
def max_flow(graph, source, sink, algorithm='edmonds_karp', **kwargs):
1111+
raise_if_backend_is_not_python(
1112+
max_flow, kwargs.get('backend', Backend.PYTHON))
1113+
1114+
import pydatastructs.graphs.algorithms as algorithms
1115+
func = "_max_flow_" + algorithm + "_"
1116+
if not hasattr(algorithms, func):
1117+
raise NotImplementedError(
1118+
f"Currently {algorithm} algorithm isn't implemented for "
1119+
"performing max flow on graphs.")
1120+
return getattr(algorithms, func)(graph, source, sink)

pydatastructs/graphs/tests/test_algorithms.py

+68-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
breadth_first_search_parallel, minimum_spanning_tree,
33
minimum_spanning_tree_parallel, strongly_connected_components,
44
depth_first_search, shortest_paths, topological_sort,
5-
topological_sort_parallel)
5+
topological_sort_parallel, max_flow)
66
from pydatastructs.utils.raises_util import raises
77

88
def test_breadth_first_search():
@@ -369,3 +369,70 @@ def _test_topological_sort(func, ds, algorithm, threads=None):
369369

370370
_test_topological_sort(topological_sort, "List", "kahn")
371371
_test_topological_sort(topological_sort_parallel, "List", "kahn", 3)
372+
373+
374+
def test_max_flow():
375+
def _test_max_flow(ds, algorithm):
376+
import pydatastructs.utils.misc_util as utils
377+
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
378+
379+
a = GraphNode('a')
380+
b = GraphNode('b')
381+
c = GraphNode('c')
382+
d = GraphNode('d')
383+
e = GraphNode('e')
384+
385+
G = Graph(a, b, c, d, e)
386+
387+
G.add_edge('a', 'b', 3)
388+
G.add_edge('a', 'c', 4)
389+
G.add_edge('b', 'c', 2)
390+
G.add_edge('b', 'd', 3)
391+
G.add_edge('c', 'd', 1)
392+
G.add_edge('d', 'e', 6)
393+
394+
assert max_flow(G, 'a', 'e', algorithm) == 4
395+
assert max_flow(G, 'a', 'c', algorithm) == 6
396+
397+
a = GraphNode('a')
398+
b = GraphNode('b')
399+
c = GraphNode('c')
400+
d = GraphNode('d')
401+
e = GraphNode('e')
402+
f = GraphNode('f')
403+
404+
G2 = Graph(a, b, c, d, e, f)
405+
406+
G2.add_edge('a', 'b', 16)
407+
G2.add_edge('a', 'c', 13)
408+
G2.add_edge('b', 'c', 10)
409+
G2.add_edge('b', 'd', 12)
410+
G2.add_edge('c', 'b', 4)
411+
G2.add_edge('c', 'e', 14)
412+
G2.add_edge('d', 'c', 9)
413+
G2.add_edge('d', 'f', 20)
414+
G2.add_edge('e', 'd', 7)
415+
G2.add_edge('e', 'f', 4)
416+
417+
assert max_flow(G2, 'a', 'f', algorithm) == 23
418+
assert max_flow(G2, 'a', 'd', algorithm) == 19
419+
420+
a = GraphNode('a')
421+
b = GraphNode('b')
422+
c = GraphNode('c')
423+
d = GraphNode('d')
424+
425+
G3 = Graph(a, b, c, d)
426+
427+
G3.add_edge('a', 'b', 3)
428+
G3.add_edge('a', 'c', 2)
429+
G3.add_edge('b', 'c', 2)
430+
G3.add_edge('b', 'd', 3)
431+
G3.add_edge('c', 'd', 2)
432+
433+
assert max_flow(G3, 'a', 'd', algorithm) == 5
434+
assert max_flow(G3, 'a', 'b', algorithm) == 3
435+
436+
437+
_test_max_flow("List", "edmonds_karp")
438+
_test_max_flow("Matrix", "edmonds_karp")

0 commit comments

Comments
 (0)