Skip to content

Commit

Permalink
fix mincut() (JuliaGraphs#325)
Browse files Browse the repository at this point in the history
* s-t cut weight should be adj_cost. fixes JuliaGraphs#324

* mincut(): simplify cutweight computation

* improve performance by 20x using dequeue_pair!

* _merge_vertex!: use inplace updates

saves another 20% runtime

* set u to new root in heuristic merge

* add comments

---------

Co-authored-by: Etienne dg <etienne.mace_de_gastines@insa-rouen.fr>
  • Loading branch information
axsk and etiennedeg committed Jan 25, 2024
1 parent 5878e7b commit a1ea44e
Showing 1 changed file with 14 additions and 21 deletions.
35 changes: 14 additions & 21 deletions src/traversals/maxadjvisit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ assumed to be 1.
# in which case we'll return immediately.
(nvg > one(U)) || return (Vector{Int8}([1]), zero(T))

# to avoid reallocating lists in fadjlist, we have some already merged vertices
# still appearing in fadjlist. When iterating neighbors, is_merged makes sure we
# don't consider them
is_merged = falses(nvg)
merged_vertices = IntDisjointSets(U(nvg))
graph_size = nvg
Expand Down Expand Up @@ -54,7 +57,6 @@ assumed to be 1.

is_processed = falses(nvg)
@inbounds while graph_size > 1
cutweight = zero(T)
is_processed .= false
is_processed[u] = true
# initialize pq
Expand All @@ -66,32 +68,23 @@ assumed to be 1.
for v in fadjlist[u]
(is_merged[v] || v == u) && continue
pq[v] = w[u, v]
cutweight += w[u, v]
end
# Minimum cut phase
local cutweight
while true
last_vertex = u
u, adj_cost = first(pq)
dequeue!(pq)
u, cutweight = dequeue_pair!(pq)
isempty(pq) && break
for v in fadjlist[u]
(is_merged[v] || u == v) && continue
# if the target of e is already marked then decrease cutweight
# otherwise, increase it
ew = w[u, v]
if is_processed[v]
cutweight -= ew
else
cutweight += ew
pq[v] += ew
end
(is_processed[v] || is_merged[v] || u == v) && continue
pq[v] += w[u, v]
end
is_processed[u] = true
# adj_cost is a lower bound on the cut separating the two last vertices
# encountered, so if adj_cost >= bestweight, we can already merge these
# cutweight is a lower bound on the cut separating the two last vertices
# encountered, so if cutweight >= bestweight, we can already merge these
# vertices to save one phase.
if adj_cost >= bestweight
_merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, last_vertex)
if cutweight >= bestweight
u = _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, last_vertex)
graph_size -= 1
end
end
Expand All @@ -105,14 +98,14 @@ assumed to be 1.
end

# merge u and last_vertex
root = _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, last_vertex)
u = _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, last_vertex)
graph_size -= 1
u = root # we are sure this vertex was not merged, so the next phase start from it
end
return (convert(Vector{Int8}, parities) .+ one(Int8), bestweight)
end

function _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, v)
# root is kept, non-root is discarded
root = union!(merged_vertices, u, v)
non_root = (root == u) ? v : u
is_merged[non_root] = true
Expand All @@ -122,7 +115,7 @@ function _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, v)
w[v2, root] = w[root, v2]
end
# update neighbors
fadjlist[root] = union(fadjlist[root], fadjlist[non_root])
union!(fadjlist[root], fadjlist[non_root])
for v in fadjlist[non_root]
if root fadjlist[v]
push!(fadjlist[v], root)
Expand Down

0 comments on commit a1ea44e

Please sign in to comment.