Skip to content

Commit bab603f

Browse files
authored
Add dinic's max flow algorithm (#534)
1 parent 6f563df commit bab603f

File tree

5 files changed

+82
-4
lines changed

5 files changed

+82
-4
lines changed

pydatastructs/graphs/adjacency_list.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ def is_adjacent(self, node1, node2):
3535
node1 = self.__getattribute__(node1)
3636
return hasattr(node1, node2)
3737

38+
def num_vertices(self):
39+
return len(self.vertices)
40+
41+
def num_edges(self):
42+
return sum(len(self.neighbors(v)) for v in self.vertices)
43+
3844
def neighbors(self, node):
3945
node = self.__getattribute__(node)
4046
return [self.__getattribute__(name) for name in node.adjacent]

pydatastructs/graphs/adjacency_matrix.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ def is_adjacent(self, node1, node2):
4040
row = self.matrix.get(node1, {})
4141
return row.get(node2, False) is not False
4242

43+
def num_vertices(self):
44+
return len(self.vertices)
45+
46+
def num_edges(self):
47+
return sum(len(v) for v in self.matrix.values())
48+
4349
def neighbors(self, node):
4450
node = str(node)
4551
neighbors = []

pydatastructs/graphs/algorithms.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,10 +1067,9 @@ def _job(graph: Graph, u: str):
10671067
return L
10681068

10691069

1070-
def _breadth_first_search_max_flow(graph: Graph, source_node, sink_node, flow_passed):
1070+
def _breadth_first_search_max_flow(graph: Graph, source_node, sink_node, flow_passed, for_dinic=False):
10711071
bfs_queue = Queue()
10721072
parent, currentPathC = {}, {}
1073-
parent[source_node] = -2
10741073
currentPathC[source_node] = float('inf')
10751074
bfs_queue.append(source_node)
10761075
while len(bfs_queue) != 0:
@@ -1080,11 +1079,11 @@ def _breadth_first_search_max_flow(graph: Graph, source_node, sink_node, flow_pa
10801079
for next_node in next_nodes:
10811080
capacity = graph.get_edge(curr_node, next_node.name).value
10821081
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:
1082+
if capacity and parent.get(next_node.name, False) is False and capacity - fp > 0:
10841083
parent[next_node.name] = curr_node
10851084
next_flow = min(currentPathC[curr_node], capacity - fp)
10861085
currentPathC[next_node.name] = next_flow
1087-
if next_node.name == sink_node:
1086+
if next_node.name == sink_node and not for_dinic:
10881087
return (next_flow, parent)
10891088
bfs_queue.append(next_node.name)
10901089
return (0, parent)
@@ -1107,6 +1106,51 @@ def _max_flow_edmonds_karp_(graph: Graph, source, sink):
11071106
new_flow, parent = _breadth_first_search_max_flow(graph, source, sink, flow_passed)
11081107
return m_flow
11091108

1109+
1110+
def _depth_first_search_max_flow_dinic(graph: Graph, u, parent, sink_node, flow, flow_passed):
1111+
if u == sink_node:
1112+
return flow
1113+
1114+
next_nodes = graph.neighbors(u)
1115+
if len(next_nodes) != 0:
1116+
for next_node in next_nodes:
1117+
capacity = graph.get_edge(u, next_node.name).value
1118+
fp = flow_passed.get((u, next_node.name), 0)
1119+
parent_cond = parent.get(next_node.name, None)
1120+
if parent_cond and parent_cond == u and capacity - fp > 0:
1121+
path_flow = _depth_first_search_max_flow_dinic(graph,
1122+
next_node.name,
1123+
parent, sink_node,
1124+
min(flow, capacity - fp), flow_passed)
1125+
if path_flow > 0:
1126+
fp = flow_passed.get((u, next_node.name), 0)
1127+
flow_passed[(u, next_node.name)] = fp + path_flow
1128+
fp = flow_passed.get((next_node.name, u), 0)
1129+
flow_passed[(next_node.name, u)] = fp - path_flow
1130+
return path_flow
1131+
return 0
1132+
1133+
1134+
def _max_flow_dinic_(graph: Graph, source, sink):
1135+
max_flow = 0
1136+
flow_passed = {}
1137+
while True:
1138+
next_flow, parent = _breadth_first_search_max_flow(graph, source, sink, flow_passed, True)
1139+
if parent.get(sink, False) is False:
1140+
break
1141+
1142+
while True:
1143+
path_flow = _depth_first_search_max_flow_dinic(graph, source,
1144+
parent, sink,
1145+
float('inf'),
1146+
flow_passed)
1147+
if path_flow <= 0:
1148+
break
1149+
max_flow += path_flow
1150+
1151+
return max_flow
1152+
1153+
11101154
def max_flow(graph, source, sink, algorithm='edmonds_karp', **kwargs):
11111155
raise_if_backend_is_not_python(
11121156
max_flow, kwargs.get('backend', Backend.PYTHON))

pydatastructs/graphs/graph.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,17 @@ def remove_edge(self, source, target):
147147
"""
148148
raise NotImplementedError(
149149
"This is an abstract method.")
150+
151+
def num_vertices(self):
152+
"""
153+
Number of vertices
154+
"""
155+
raise NotImplementedError(
156+
"This is an abstract method.")
157+
158+
def num_edges(self):
159+
"""
160+
Number of edges
161+
"""
162+
raise NotImplementedError(
163+
"This is an abstract method.")

pydatastructs/graphs/tests/test_algorithms.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ def _test_breadth_first_search(ds):
1717

1818
G1 = Graph(V1, V2, V3)
1919

20+
assert G1.num_vertices() == 3
21+
2022
edges = [
2123
(V1.name, V2.name),
2224
(V2.name, V3.name),
@@ -26,6 +28,8 @@ def _test_breadth_first_search(ds):
2628
for edge in edges:
2729
G1.add_edge(*edge)
2830

31+
assert G1.num_edges() == len(edges)
32+
2933
parent = {}
3034
def bfs_tree(curr_node, next_node, parent):
3135
if next_node != "":
@@ -55,6 +59,8 @@ def bfs_tree(curr_node, next_node, parent):
5559
for edge in edges:
5660
G2.add_edge(*edge)
5761

62+
assert G2.num_edges() == len(edges)
63+
5864
path = []
5965
def path_finder(curr_node, next_node, dest_node, parent, path):
6066
if next_node != "":
@@ -436,3 +442,5 @@ def _test_max_flow(ds, algorithm):
436442

437443
_test_max_flow("List", "edmonds_karp")
438444
_test_max_flow("Matrix", "edmonds_karp")
445+
_test_max_flow("List", "dinic")
446+
_test_max_flow("Matrix", "dinic")

0 commit comments

Comments
 (0)