-
Notifications
You must be signed in to change notification settings - Fork 0
/
preflow_push.ex
115 lines (93 loc) · 4.16 KB
/
preflow_push.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
defmodule PreflowPush do
# Public function to find the maximum flow in a flow network
def max_flow(graph, source, sink) do
# Initialize the preflow and excess arrays
preflow = initialize_preflow(graph, source)
excess = initialize_excess(graph, source)
# Initialize the heights array using the highest-label-first rule
heights = initialize_heights(graph, source, sink)
# Push excess flow until no vertices are active
active_vertices = get_active_vertices(preflow, excess)
while !Enum.empty?(active_vertices) do
v = get_highest_label_vertex(active_vertices, heights)
push_or_relabel(preflow, excess, heights, v, graph, sink)
active_vertices = get_active_vertices(preflow, excess)
end
# Return the final maximum flow
excess[sink]
end
# Private function to initialize the preflow array
defp initialize_preflow(graph, source) do
Enum.reduce(graph, %{}, fn {u, v, capacity}, preflow ->
Map.put(preflow, {u, v}, 0)
end)
end
# Private function to initialize the excess array
defp initialize_excess(graph, source) do
Enum.reduce(graph, %{}, fn {u, v, capacity}, excess ->
if u == source, do: Map.put(excess, u, capacity), else: excess
end)
end
# Private function to initialize the heights array using the highest-label-first rule
defp initialize_heights(graph, source, sink) do
# Initialize heights to 0 for all vertices except the source
heights = Enum.reduce(graph, %{}, fn {u, v, _}, heights ->
Map.put(heights, u, 0)
end)
# Set the height of the source to the number of vertices
Map.put(heights, source, length(graph))
# Initialize the queue with vertices at height n-1
queue = Enum.filter(graph, fn {u, _, _} -> u != source end)
# BFS to set heights in the highest-label-first order
while !Enum.empty?(queue) do
{current, _, _} = Enum.at(queue, 0)
queue = Enum.drop(queue, 1)
neighbors = Enum.filter(graph, fn {u, v, _} -> u == current and Map.get(heights, v, 0) == 0 end)
new_vertices = Enum.map(neighbors, fn {_, v, _} ->
Map.put(heights, v, Map.get(heights, current) - 1)
end)
queue = queue ++ new_vertices
end
heights
end
# Private function to get active vertices with excess flow
defp get_active_vertices(preflow, excess) do
Enum.filter(excess, fn {v, e} -> e > 0 end)
end
# Private function to get the highest label vertex among active vertices
defp get_highest_label_vertex(active_vertices, heights) do
Enum.reduce(active_vertices, {nil, -1}, fn {v, _}, {highest_vertex, highest_label} ->
label = Map.get(heights, v, 0)
if label > highest_label, do: {v, label}, else: {highest_vertex, highest_label}
end) |> elem(0)
end
# Private function to push flow or relabel a vertex
defp push_or_relabel(preflow, excess, heights, u, graph, sink) do
neighbors = Enum.filter(graph, fn {uu, v, _} -> uu == u and excess[uu] > 0 and Map.get(heights, uu) == Map.get(heights, v) + 1 end)
if !Enum.empty?(neighbors) do
{_, v, capacity} = Enum.random(neighbors)
push(preflow, excess, u, v, capacity)
else
relabel(heights, u)
end
end
# Private function to push flow from u to v
defp push(preflow, excess, u, v, capacity) do
bottleneck_capacity = min(excess[u], capacity - Map.get(preflow, {u, v}))
# Update preflow and excess
preflow = Map.update(preflow, {u, v}, bottleneck_capacity, &(&1 + bottleneck_capacity))
preflow = Map.update(preflow, {v, u}, -bottleneck_capacity, &(&1 - bottleneck_capacity))
excess = Map.update(excess, u, &(&1 - bottleneck_capacity))
excess = Map.update(excess, v, &(&1 + bottleneck_capacity))
end
# Private function to relabel the height of a vertex u
defp relabel(heights, u) do
# Find the minimum height of neighbors of u
neighbors = Enum.filter(graph, fn {uu, v, _} -> uu == u end)
min_height = Enum.reduce(neighbors, Float.infinity, fn {uu, v, _}, min_height ->
min(min_height, Map.get(heights, v, 0))
end)
# Set the height of u to one more than the minimum height
heights = Map.put(heights, u, min_height + 1)
end
end