Repository: keon/algorithms
Pull request #610 (2019-12-06 16:23:15)

Add network flow #595
(Put an `X` inside the `[ ]` to denote check mark `[X]`.)


- [X] **If creating a new file :**
  - [X] added links to it in the README files ?
  - [X] included tests with it ?
  - [X] added description (overview of algorithm, time and space complexity, and possible edge case) in docstrings ?

- [ ] **if done some changes :**
  - [ ] wrote short description in the PR explaining what the changes do ?
  - [ ] Fixes #[issue number] if related to any issue

- [ ] **other**


sonho00: Changed name of variable f and p to current_flow and parent respectively. Also, fixed minor format

Patch:
diff --git a/README.md b/README.md
index 8569d3b..354eade 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/algorithms/graph/__init__.py b/algorithms/graph/__init__.py
index 43cab3d..6675e63 100644
--- a/algorithms/graph/__init__.py
+++ b/algorithms/graph/__init__.py
@@ -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 *
diff --git a/algorithms/graph/maximum_flow.py b/algorithms/graph/maximum_flow.py
new file mode 100644
index 0000000..ca78244
--- /dev/null
+++ b/algorithms/graph/maximum_flow.py
@@ -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
+


Test patch:
diff --git a/tests/test_graph.py b/tests/test_graph.py
index 47d5afa..73ef230 100644
--- a/tests/test_graph.py
+++ b/tests/test_graph.py
@@ -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
@@ -81,6 +84,47 @@ class TestDijkstra(unittest.TestCase):
 
         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):
 
     """
@@ -164,4 +208,3 @@ class TestBellmanFord(unittest.TestCase):
         } 
     
         self.assertEqual(True, bellman_ford(graph2, 'a'))
-