Skip to content

Commit

Permalink
Add network flow keon#595 (keon#610)
Browse files Browse the repository at this point in the history
* [ADD] Added Ford_Fulkerson algorithm.

* [ADD] Added Edmonds_Karp algorithm.

* [EDIT] Added time complexity to Ford_Fulkerson and Edmonds_Karp algorithm.

* [ADD] Added test case for maximum flow algorithms.

* [EDIT] Import maximum_flow.

* [EDIT] Added link to README

* [ADD] Added Dinic algorithm.

* [ADD] Added test case for Dinic algorithm.

* [EDIT] Edited test cases for maximum flow algorithms.

* [EDIT] Edited variable name and added comment

* [EDIT] Edited format to easier to recognize.

* [EDIT] Edited variable name
  • Loading branch information
sonho00 authored and keon committed Dec 11, 2019
1 parent 3e2cccb commit ee3c595
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ If you want to uninstall algorithms, it is as simple as:
- [satisfiability](algorithms/graph/satisfiability.py)
- [tarjan](algorithms/graph/tarjan.py)
- [traversal](algorithms/graph/traversal.py)
- [maximum_flow](algorithms/graph/maximum_flow.py)
- [maximum_flow_bfs](algorithms/graph/maximum_flow_bfs.py)
- [maximum_flow_dfs](algorithms/graph/maximum_flow_dfs.py)
- [all_pairs_shortest_path](algorithms/graph/all_pairs_shortest_path.py)
Expand Down
1 change: 1 addition & 0 deletions algorithms/graph/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .tarjan import *
from .check_bipartite import *
from .maximum_flow import *
from .maximum_flow_bfs import *
from .maximum_flow_dfs import *
from .all_pairs_shortest_path import *
Expand Down
134 changes: 134 additions & 0 deletions algorithms/graph/maximum_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"""
Given the capacity, source and sink of a graph,
computes the maximum flow from source to sink.
Input : capacity, source, sink
Output : maximum flow from source to sink
Capacity is a two-dimensional array that is v*v.
capacity[i][j] implies the capacity of the edge from i to j.
If there is no edge from i to j, capacity[i][j] should be zero.
"""

import queue

def dfs(capacity, flow, visit, vertices, idx, sink, current_flow = 1 << 63):
# DFS function for ford_fulkerson algorithm.
if idx == sink:
return current_flow
visit[idx] = True
for nxt in range(vertices):
if not visit[nxt] and flow[idx][nxt] < capacity[idx][nxt]:
tmp = dfs(capacity, flow, visit, vertices, nxt, sink, min(current_flow, capacity[idx][nxt]-flow[idx][nxt]))
if tmp:
flow[idx][nxt] += tmp
flow[nxt][idx] -= tmp
return tmp
return 0

def ford_fulkerson(capacity, source, sink):
# Computes maximum flow from source to sink using DFS.
# Time Complexity : O(Ef)
# E is the number of edges and f is the maximum flow in the graph.
vertices = len(capacity)
ret = 0
flow = [[0]*vertices for i in range(vertices)]
while True:
visit = [False for i in range(vertices)]
tmp = dfs(capacity, flow, visit, vertices, source, sink)
if tmp:
ret += tmp
else:
break
return ret

def edmonds_karp(capacity, source, sink):
# Computes maximum flow from source to sink using BFS.
# Time complexity : O(V*E^2)
# V is the number of vertices and E is the number of edges.
vertices = len(capacity)
ret = 0
flow = [[0]*vertices for i in range(vertices)]
while True:
tmp = 0
q = queue.Queue()
visit = [False for i in range(vertices)]
par = [-1 for i in range(vertices)]
visit[source] = True
q.put((source, 1 << 63))
# Finds new flow using BFS.
while q.qsize():
front = q.get()
idx, current_flow = front
if idx == sink:
tmp = current_flow
break
for nxt in range(vertices):
if not visit[nxt] and flow[idx][nxt] < capacity[idx][nxt]:
visit[nxt] = True
par[nxt] = idx
q.put((nxt, min(current_flow, capacity[idx][nxt]-flow[idx][nxt])))
if par[sink] == -1:
break
ret += tmp
parent = par[sink]
idx = sink
# Update flow array following parent starting from sink.
while parent != -1:
flow[parent][idx] += tmp
flow[idx][parent] -= tmp
idx = parent
parent = par[parent]
return ret

def dinic_bfs(capacity, flow, level, source, sink):
# BFS function for Dinic algorithm.
# Check whether sink is reachable only using edges that is not full.

vertices = len(capacity)
q = queue.Queue()
q.put(source)
level[source] = 0
while q.qsize():
front = q.get()
for nxt in range(vertices):
if level[nxt] == -1 and flow[front][nxt] < capacity[front][nxt]:
level[nxt] = level[front] + 1
q.put(nxt)
return level[sink] != -1

def dinic_dfs(capacity, flow, level, idx, sink, work, current_flow = 1 << 63):
# DFS function for Dinic algorithm.
# Finds new flow using edges that is not full.
if idx == sink:
return current_flow
vertices = len(capacity)
while work[idx] < vertices:
nxt = work[idx]
if level[nxt] == level[idx] + 1 and flow[idx][nxt] < capacity[idx][nxt]:
tmp = dinic_dfs(capacity, flow, level, nxt, sink, work, min(current_flow, capacity[idx][nxt] - flow[idx][nxt]))
if tmp > 0:
flow[idx][nxt] += tmp
flow[nxt][idx] -= tmp
return tmp
work[idx] += 1
return 0

def dinic(capacity, source, sink):
# Computes maximum flow from source to sink using Dinic algorithm.
# Time complexity : O(V^2*E)
# V is the number of vertices and E is the number of edges.
vertices = len(capacity)
flow = [[0]*vertices for i in range(vertices)]
ret = 0
while True:
level = [-1 for i in range(vertices)]
work = [0 for i in range(vertices)]
if not dinic_bfs(capacity, flow, level, source, sink):
break
while True:
tmp = dinic_dfs(capacity, flow, level, source, sink, work)
if tmp > 0:
ret += tmp
else:
break
return ret

45 changes: 44 additions & 1 deletion tests/test_graph.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from algorithms.graph import Tarjan
from algorithms.graph import check_bipartite
from algorithms.graph.dijkstra import Dijkstra
from algorithms.graph import ford_fulkerson
from algorithms.graph import edmonds_karp
from algorithms.graph import dinic
from algorithms.graph import maximum_flow_bfs
from algorithms.graph import maximum_flow_dfs
from algorithms.graph import all_pairs_shortest_path
Expand Down Expand Up @@ -81,6 +84,47 @@ def test_dijkstra(self):

self.assertEqual(g.dijkstra(0), [0, 4, 12, 19, 21, 11, 9, 8, 14])

class TestMaximumFlow(unittest.TestCase):
"""
Test for the file maximum_flow.py
Arguments:
unittest {[type]} -- [description]
"""
def test_ford_fulkerson(self):
capacity = [
[0, 10, 10, 0, 0, 0, 0],
[0, 0, 2, 0, 4, 8, 0],
[0, 0, 0, 0, 0, 9, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 10],
[0, 0, 0, 0, 6, 0, 10],
[0, 0, 0, 0, 0, 0, 0]
]
self.assertEqual(19, ford_fulkerson(capacity, 0, 6))
def test_edmonds_karp(self):
capacity = [
[0, 10, 10, 0, 0, 0, 0],
[0, 0, 2, 0, 4, 8, 0],
[0, 0, 0, 0, 0, 9, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 10],
[0, 0, 0, 0, 6, 0, 10],
[0, 0, 0, 0, 0, 0, 0]
]
self.assertEqual(19, edmonds_karp(capacity, 0, 6))
def dinic(self):
capacity = [
[0, 10, 10, 0, 0, 0, 0],
[0, 0, 2, 0, 4, 8, 0],
[0, 0, 0, 0, 0, 9, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 10],
[0, 0, 0, 0, 6, 0, 10],
[0, 0, 0, 0, 0, 0, 0]
]
self.assertEqual(19, dinic(capacity, 0, 6))

class TestMaximum_Flow_Bfs(unittest.TestCase):

"""
Expand Down Expand Up @@ -164,4 +208,3 @@ def test_bellman_ford(self):
}

self.assertEqual(True, bellman_ford(graph2, 'a'))

0 comments on commit ee3c595

Please sign in to comment.