|
12 | 12 |
|
13 | 13 | Return a tuple `(parity, bestcut)`, where `parity` is a vector of integer
|
14 | 14 | values that determines the partition in `g` (1 or 2) and `bestcut` is the
|
15 |
| -weight of the cut that makes this partition. An optional `distmx` matrix may |
16 |
| -be specified; if omitted, edge distances are assumed to be 1. |
| 15 | +weight of the cut that makes this partition. An optional `distmx` matrix |
| 16 | +of non-negative weights may be specified; if omitted, edge distances are |
| 17 | +assumed to be 1. |
17 | 18 | """
|
18 |
| -function mincut(g::AbstractGraph, distmx::AbstractMatrix{T}=weights(g)) where {T<:Real} |
19 |
| - U = eltype(g) |
20 |
| - colormap = zeros(UInt8, nv(g)) ## 0 if unseen, 1 if processing and 2 if seen and closed |
21 |
| - parities = falses(nv(g)) |
22 |
| - bestweight = typemax(T) |
23 |
| - cutweight = zero(T) |
24 |
| - visited = zero(U) ## number of vertices visited |
25 |
| - pq = PriorityQueue{U,T}(Base.Order.Reverse) |
| 19 | +@traitfn function mincut(g::::(!IsDirected), distmx::AbstractMatrix{T}=weights(g)) where {T <: Real} |
26 | 20 |
|
27 |
| - # Set number of visited neighbors for all vertices to 0 |
28 |
| - for v in vertices(g) |
29 |
| - pq[v] = zero(T) |
30 |
| - end |
| 21 | + nvg = nv(g) |
| 22 | + U = eltype(g) |
31 | 23 |
|
32 | 24 | # make sure we have at least two vertices, otherwise, there's nothing to cut,
|
33 | 25 | # in which case we'll return immediately.
|
34 |
| - (haskey(pq, one(U)) && nv(g) > one(U)) || return (Vector{Int8}([1]), cutweight) |
35 |
| - |
36 |
| - # Give the starting vertex high priority |
37 |
| - pq[one(U)] = one(T) |
| 26 | + (nvg > one(U)) || return (Vector{Int8}([1]), zero(T)) |
38 | 27 |
|
39 |
| - while !isempty(pq) |
40 |
| - u = dequeue!(pq) |
41 |
| - colormap[u] = 1 |
| 28 | + is_merged = falses(nvg) |
| 29 | + merged_vertices = IntDisjointSets(U(nvg)) |
| 30 | + graph_size = nvg |
| 31 | + # We need to mutate the weight matrix, |
| 32 | + # and we need it clean (0 for non edges) |
| 33 | + w = zeros(T, nvg, nvg) |
| 34 | + size(distmx) != (nvg, nvg) && throw(ArgumentError("Adjacency / distance matrix size should match the number of vertices")) |
| 35 | + @inbounds for e in edges(g) |
| 36 | + d = distmx[src(e), dst(e)] |
| 37 | + (d < 0) && throw(DomainError(w, "weigths should be non-negative")) |
| 38 | + w[src(e), dst(e)] = d |
| 39 | + (d != distmx[dst(e), src(e)]) && throw(ArgumentError("Adjacency / distance matrix must be symmetric")) |
| 40 | + w[dst(e), src(e)] = d |
| 41 | + end |
| 42 | + # we also need to mutate neighbors when merging vertices |
| 43 | + fadjlist = [collect(outneighbors(g, v)) for v in vertices(g)] |
| 44 | + parities = falses(nvg) |
| 45 | + bestweight = typemax(T) |
| 46 | + pq = PriorityQueue{U,T}(Base.Order.Reverse) |
| 47 | + u = last_vertex = one(U) |
42 | 48 |
|
43 |
| - for v in outneighbors(g, u) |
44 |
| - # if the target of e is already marked then decrease cutweight |
45 |
| - # otherwise, increase it |
46 |
| - ew = distmx[u, v] |
47 |
| - if colormap[v] != 0 |
48 |
| - cutweight -= ew |
49 |
| - else |
50 |
| - cutweight += ew |
| 49 | + is_processed = falses(nvg) |
| 50 | + @inbounds while graph_size > 1 |
| 51 | + cutweight = zero(T) |
| 52 | + is_processed .= false |
| 53 | + is_processed[u] = true |
| 54 | + # initialize pq |
| 55 | + for v in vertices(g) |
| 56 | + is_merged[v] && continue |
| 57 | + v == u && continue |
| 58 | + pq[v] = zero(T) |
| 59 | + end |
| 60 | + for v in fadjlist[u] |
| 61 | + (is_merged[v] || v == u ) && continue |
| 62 | + pq[v] = w[u, v] |
| 63 | + cutweight += w[u, v] |
| 64 | + end |
| 65 | + # Minimum cut phase |
| 66 | + while true |
| 67 | + last_vertex = u |
| 68 | + u, adj_cost = first(pq) |
| 69 | + dequeue!(pq) |
| 70 | + isempty(pq) && break |
| 71 | + for v in fadjlist[u] |
| 72 | + (is_merged[v] || u == v) && continue |
| 73 | + # if the target of e is already marked then decrease cutweight |
| 74 | + # otherwise, increase it |
| 75 | + ew = w[u, v] |
| 76 | + if is_processed[v] |
| 77 | + cutweight -= ew |
| 78 | + else |
| 79 | + cutweight += ew |
| 80 | + pq[v] += ew |
| 81 | + end |
51 | 82 | end
|
52 |
| - if haskey(pq, v) |
53 |
| - pq[v] += distmx[u, v] |
| 83 | + is_processed[u] = true |
| 84 | + # adj_cost is a lower bound on the cut separating the two last vertices |
| 85 | + # encountered, so if adj_cost >= bestweight, we can already merge these |
| 86 | + # vertices to save one phase. |
| 87 | + if adj_cost >= bestweight |
| 88 | + _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, last_vertex) |
| 89 | + graph_size -= 1 |
54 | 90 | end
|
55 | 91 | end
|
56 | 92 |
|
57 |
| - colormap[u] = 2 |
58 |
| - visited += one(U) |
59 |
| - if cutweight < bestweight && visited < nv(g) |
| 93 | + # check if we improved the mincut |
| 94 | + if cutweight < bestweight |
60 | 95 | bestweight = cutweight
|
61 |
| - for u in vertices(g) |
62 |
| - parities[u] = (colormap[u] == 2) |
| 96 | + for v in vertices(g) |
| 97 | + parities[v] = (find_root!(merged_vertices, v) == u) |
63 | 98 | end
|
64 | 99 | end
|
| 100 | + |
| 101 | + # merge u and last_vertex |
| 102 | + root = _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, last_vertex) |
| 103 | + graph_size -= 1 |
| 104 | + u = root # we are sure this vertex was not merged, so the next phase start from it |
65 | 105 | end
|
66 | 106 | return (convert(Vector{Int8}, parities) .+ one(Int8), bestweight)
|
67 | 107 | end
|
68 | 108 |
|
| 109 | +function _merge_vertex!(merged_vertices, fadjlist, is_merged, w, u, v) |
| 110 | + root = union!(merged_vertices, u, v) |
| 111 | + non_root = (root == u) ? v : u |
| 112 | + is_merged[non_root] = true |
| 113 | + # update weights |
| 114 | + for v2 in fadjlist[non_root] |
| 115 | + w[root, v2] += w[non_root, v2] |
| 116 | + w[v2, root] = w[root, v2] |
| 117 | + end |
| 118 | + # update neighbors |
| 119 | + fadjlist[root] = union(fadjlist[root], fadjlist[non_root]) |
| 120 | + for v in fadjlist[non_root] |
| 121 | + if root ∉ fadjlist[v] |
| 122 | + push!(fadjlist[v], root) |
| 123 | + end |
| 124 | + end |
| 125 | + return root |
| 126 | +end |
| 127 | + |
69 | 128 | """
|
70 | 129 | maximum_adjacency_visit(g[, distmx][, log][, io][, s])
|
71 | 130 | maximum_adjacency_visit(g[, s])
|
@@ -106,7 +165,7 @@ function maximum_adjacency_visit(
|
106 | 165 | log && println(io, "discover vertex: $u")
|
107 | 166 | for v in outneighbors(g, u)
|
108 | 167 | log && println(io, " -- examine neighbor from $u to $v")
|
109 |
| - if has_key[v] |
| 168 | + if has_key[v] && (u != v) |
110 | 169 | ed = distmx[u, v]
|
111 | 170 | pq[v] += ed
|
112 | 171 | end
|
|
0 commit comments