Skip to content

Commit 046f59d

Browse files
committed
Pass weights to METIS. Add elimination algorithm IND.
1 parent bc50862 commit 046f59d

4 files changed

Lines changed: 299 additions & 11 deletions

File tree

ext/MetisExt.jl

Lines changed: 280 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,87 @@
11
module MetisExt
22

3+
using Base: oneto
34
using CliqueTrees
5+
using CliqueTrees: EliminationAlgorithm
6+
using CliqueTrees.Utilities
47
using Graphs
58
using Metis: Metis
69

7-
const IDX = Metis.idx_t
10+
const INT = Metis.idx_t
11+
12+
function CliqueTrees.permutation(weights::AbstractVector, graph, alg::METIS)
13+
return permutation(weights, BipartiteGraph(graph), alg)
14+
end
815

916
function CliqueTrees.permutation(graph, alg::METIS)
1017
return permutation(BipartiteGraph(graph), alg)
1118
end
1219

20+
function CliqueTrees.permutation(weights::AbstractVector, graph::AbstractGraph{V}, alg::METIS) where {V}
21+
m = ne(graph) * (2 - is_directed(graph))
22+
n = nv(graph)
23+
24+
# construct options
25+
options = Vector{INT}(undef, Metis.METIS_NOPTIONS)
26+
options .= -1 # null
27+
options[Metis.METIS_OPTION_CTYPE + 1] = alg.ctype
28+
options[Metis.METIS_OPTION_RTYPE + 1] = alg.rtype
29+
options[Metis.METIS_OPTION_NSEPS + 1] = alg.nseps
30+
options[Metis.METIS_OPTION_NUMBERING + 1] = 1
31+
options[Metis.METIS_OPTION_NITER + 1] = alg.niter
32+
options[Metis.METIS_OPTION_SEED + 1] = alg.seed
33+
options[Metis.METIS_OPTION_COMPRESS + 1] = alg.compress
34+
options[Metis.METIS_OPTION_CCORDER + 1] = alg.ccorder
35+
options[Metis.METIS_OPTION_PFACTOR + 1] = alg.pfactor
36+
options[Metis.METIS_OPTION_UFACTOR + 1] = alg.ufactor
37+
38+
# construct METIS graph
39+
xadj = Vector{INT}(undef, n + 1)
40+
adjncy = Vector{INT}(undef, m)
41+
vwght = Vector{INT}(undef, n)
42+
xadj[begin] = p = one(INT)
43+
44+
@inbounds for j in vertices(graph)
45+
vwght[j] = trunc(INT, weights[j])
46+
47+
for i in neighbors(graph, j)
48+
if i != j
49+
adjncy[p] = i
50+
p += one(INT)
51+
end
52+
end
53+
54+
xadj[j + one(V)] = p
55+
end
56+
57+
resize!(adjncy, p - one(INT))
58+
59+
# construct permutation
60+
metisorder = Vector{INT}(undef, n)
61+
metisindex = Vector{INT}(undef, n)
62+
63+
Metis.@check Metis.METIS_NodeND(
64+
Ref{INT}(n),
65+
xadj,
66+
adjncy,
67+
vwght,
68+
options,
69+
metisorder,
70+
metisindex,
71+
)
72+
73+
# restore vertex type
74+
order::Vector{V} = metisorder
75+
index::Vector{V} = metisindex
76+
return order, index
77+
end
78+
1379
function CliqueTrees.permutation(graph::AbstractGraph{V}, alg::METIS) where {V}
80+
m = ne(graph) * (2 - is_directed(graph))
81+
n = nv(graph)
82+
1483
# construct options
15-
options = Vector{IDX}(undef, Metis.METIS_NOPTIONS)
84+
options = Vector{INT}(undef, Metis.METIS_NOPTIONS)
1685
options .= -1 # null
1786
options[Metis.METIS_OPTION_CTYPE + 1] = alg.ctype
1887
options[Metis.METIS_OPTION_RTYPE + 1] = alg.rtype
@@ -26,29 +95,29 @@ function CliqueTrees.permutation(graph::AbstractGraph{V}, alg::METIS) where {V}
2695
options[Metis.METIS_OPTION_UFACTOR + 1] = alg.ufactor
2796

2897
# construct METIS graph
29-
xadj = Vector{IDX}(undef, nv(graph) + 1)
30-
adjncy = Vector{IDX}(undef, ne(graph) * (2 - is_directed(graph)))
31-
xadj[begin] = p = one(IDX)
98+
xadj = Vector{INT}(undef, n + 1)
99+
adjncy = Vector{INT}(undef, m)
100+
xadj[begin] = p = one(INT)
32101

33102
@inbounds for j in vertices(graph)
34103
for i in neighbors(graph, j)
35104
if i != j
36105
adjncy[p] = i
37-
p += one(IDX)
106+
p += one(INT)
38107
end
39108
end
40109

41110
xadj[j + one(V)] = p
42111
end
43112

44-
resize!(adjncy, p - one(IDX))
113+
resize!(adjncy, p - one(INT))
45114

46115
# construct permutation
47-
metisorder = Vector{IDX}(undef, nv(graph))
48-
metisindex = Vector{IDX}(undef, nv(graph))
116+
metisorder = Vector{INT}(undef, n)
117+
metisindex = Vector{INT}(undef, n)
49118

50119
Metis.@check Metis.METIS_NodeND(
51-
Ref{IDX}(nv(graph)),
120+
Ref{INT}(n),
52121
xadj,
53122
adjncy,
54123
C_NULL,
@@ -63,4 +132,205 @@ function CliqueTrees.permutation(graph::AbstractGraph{V}, alg::METIS) where {V}
63132
return order, index
64133
end
65134

135+
function CliqueTrees.permutation(weights::AbstractVector, graph, alg::IND)
136+
return permutation(weights, BipartiteGraph(graph), alg)
137+
end
138+
139+
function CliqueTrees.permutation(graph, alg::IND)
140+
return permutation(BipartiteGraph(graph), alg)
141+
end
142+
143+
function CliqueTrees.permutation(graph::AbstractGraph{V}, alg::IND) where {V}
144+
weights = ones(V, nv(graph))
145+
return permutation(weights, graph, alg)
146+
end
147+
148+
function CliqueTrees.permutation(weights::AbstractVector, graph::AbstractGraph{V}, alg::IND) where {V}
149+
n = nv(graph)
150+
m = ne(graph) * (2 - is_directed(graph))
151+
new = BipartiteGraph{INT, INT}(n, n, m)
152+
pointers(new)[begin] = p = one(INT)
153+
154+
@inbounds for v in vertices(graph)
155+
for w in neighbors(graph, v)
156+
if v != w
157+
targets(new)[p] = INT(w)
158+
p += one(INT)
159+
end
160+
end
161+
162+
pointers(new)[v + one(V)] = p
163+
end
164+
165+
resize!(targets(new), p - one(INT))
166+
order::Vector{V} = indissect(vertices(new), oneto(zero(INT)), zero(INT), weights, new, INT(alg.limit), alg.alg)
167+
return order, invperm(order)
168+
end
169+
170+
function indissect(label::AbstractVector{INT}, clique::AbstractVector{INT}, delta::INT, weights::AbstractVector, graph::AbstractGraph{INT}, limit::INT, alg::EliminationAlgorithm)
171+
n = nv(graph)
172+
173+
if n <= limit + delta
174+
order = first(permutation(weights, graph; alg = CompositeRotations(clique, alg)))
175+
else
176+
project = separator(weights, graph)
177+
project0 = Vector{INT}(undef, n)
178+
project1 = Vector{INT}(undef, n)
179+
n0 = n1 = n2 = zero(INT)
180+
m0 = m1 = zero(INT)
181+
182+
@inbounds for v in vertices(graph)
183+
vv = project[v]
184+
185+
if iszero(vv)
186+
project0[v] = n0 += one(INT)
187+
m0 += eltypedegree(graph, v)
188+
elseif isone(vv)
189+
project1[v] = n1 += one(INT)
190+
m1 += eltypedegree(graph, v)
191+
else
192+
project0[v] = n0 += one(INT)
193+
project1[v] = n1 += one(INT)
194+
n2 += one(INT)
195+
196+
for w in outneighbors(graph, v)
197+
ww = project[w]
198+
199+
if iszero(ww)
200+
m0 += one(INT)
201+
elseif isone(ww)
202+
m1 += one(INT)
203+
end
204+
end
205+
end
206+
end
207+
208+
m0 += n2 * n2 - n2
209+
m1 += n2 * n2 - n2
210+
label0 = Vector{INT}(undef, n0)
211+
label1 = Vector{INT}(undef, n1)
212+
label2 = Vector{INT}(undef, n2)
213+
t0 = t1 = t2 = zero(INT)
214+
215+
@inbounds for v in vertices(graph)
216+
vv = project[v]
217+
218+
if iszero(vv)
219+
t0 += one(INT)
220+
label0[t0] = v
221+
elseif isone(vv)
222+
t1 += one(INT)
223+
label1[t1] = v
224+
else
225+
t0 += one(INT)
226+
t1 += one(INT)
227+
t2 += one(INT)
228+
label0[t0] = label1[t1] = label2[t2] = v
229+
end
230+
end
231+
232+
t0 = t1 = one(INT)
233+
weights0 = Vector{INT}(undef, n0)
234+
weights1 = Vector{INT}(undef, n1)
235+
graph0 = BipartiteGraph{INT, INT}(n0, n0, m0)
236+
graph1 = BipartiteGraph{INT, INT}(n1, n1, m1)
237+
pointers(graph0)[t0] = u0 = one(INT)
238+
pointers(graph1)[t1] = u1 = one(INT)
239+
240+
@inbounds for v in vertices(graph)
241+
vv = project[v]
242+
243+
if iszero(vv)
244+
weights0[t0] = weights[v]
245+
246+
for w in neighbors(graph, v)
247+
targets(graph0)[u0] = project0[w]
248+
u0 += one(INT)
249+
end
250+
251+
t0 += one(INT)
252+
pointers(graph0)[t0] = u0
253+
elseif isone(vv)
254+
weights1[t1] = weights[v]
255+
256+
for w in neighbors(graph, v)
257+
targets(graph1)[u1] = project1[w]
258+
u1 += one(INT)
259+
end
260+
261+
t1 += one(INT)
262+
pointers(graph1)[t1] = u1
263+
else
264+
weights0[t0] = weights1[t1] = weights[v]
265+
266+
for w in neighbors(graph, v)
267+
ww = project[w]
268+
269+
if iszero(ww)
270+
targets(graph0)[u0] = project0[w]
271+
u0 += one(INT)
272+
elseif isone(ww)
273+
targets(graph1)[u1] = project1[w]
274+
u1 += one(INT)
275+
end
276+
end
277+
278+
for w in label2
279+
if v != w
280+
targets(graph0)[u0] = project0[w]
281+
targets(graph1)[u1] = project1[w]
282+
u0 += one(INT)
283+
u1 += one(INT)
284+
end
285+
end
286+
287+
t0 += one(INT)
288+
t1 += one(INT)
289+
pointers(graph0)[t0] = u0
290+
pointers(graph1)[t1] = u1
291+
end
292+
end
293+
294+
order0 = indissect(label0, view(project0, label2), n2, weights0, graph0, limit + delta, alg)
295+
order1 = indissect(label1, view(project1, label2), n2, weights1, graph1, limit + delta, alg)
296+
order = first(permutation(graph; alg = CompositeRotations(clique, [order0; order1; label2])))
297+
end
298+
299+
@inbounds for i in oneto(n - delta)
300+
order[i] = label[order[i]]
301+
end
302+
303+
return resize!(order, n - delta)
304+
end
305+
306+
function separator(weights::AbstractVector, graph::BipartiteGraph{INT, INT, Vector{INT}, Vector{INT}})
307+
n = nv(graph)
308+
309+
# construct options
310+
options = Vector{INT}(undef, Metis.METIS_NOPTIONS)
311+
options .= -1 # null
312+
options[Metis.METIS_OPTION_NUMBERING + 1] = 1
313+
314+
# construct METIS graph
315+
xadj = pointers(graph) .- one(INT)
316+
adjncy = targets(graph) .- one(INT)
317+
vwght = trunc.(INT, weights)
318+
319+
# construct separator
320+
part = Vector{INT}(undef, n)
321+
sepsize = fill(zero(INT), 1)
322+
323+
Metis.@check Metis.METIS_ComputeVertexSeparator(
324+
Ref{INT}(n),
325+
xadj,
326+
adjncy,
327+
vwght,
328+
options,
329+
sepsize,
330+
part,
331+
)
332+
333+
return part
334+
end
335+
66336
end

src/CliqueTrees.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export BFS,
5151
AMD,
5252
SymAMD,
5353
METIS,
54+
IND,
5455
Spectral,
5556
FlowCutter,
5657
BT,

src/elimination_algorithms.jl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,19 @@ julia> treewidth(graph; alg)
645645
ufactor::Int = -1
646646
end
647647

648+
struct IND{A <: EliminationAlgorithm} <: EliminationAlgorithm
649+
alg::A
650+
limit::Int
651+
end
652+
653+
function IND(alg::EliminationAlgorithm)
654+
return IND(alg, 200)
655+
end
656+
657+
function IND()
658+
return IND(DEFAULT_ELIMINATION_ALGORITHM)
659+
end
660+
648661
"""
649662
Spectral <: EliminationAlgorithm
650663
@@ -2265,7 +2278,8 @@ function mf!(weights::AbstractVector{W}, graph::Graph{V}) where {W, V}
22652278
end
22662279

22672280
# update heap
2268-
for w in take(stack, snum)
2281+
for j in oneto(snum)
2282+
w = stack[j]
22692283
heap[w] -= (nweight - sweight) * (nweights[w] - sweight)
22702284
hrise!(heap, w)
22712285
hfall!(heap, w)

test/core.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ end
416416
MF(),
417417
MMD(),
418418
METIS(),
419+
IND(),
419420
Spectral(),
420421
# FlowCutter(),
421422
BT(),
@@ -526,6 +527,7 @@ end
526527
MF(),
527528
MMD(),
528529
METIS(),
530+
IND(),
529531
# Spectral,
530532
# FlowCutter(),
531533
BT(),
@@ -771,6 +773,7 @@ end
771773
MF(),
772774
MMD(),
773775
METIS(),
776+
IND(),
774777
Spectral(),
775778
# FlowCutter(),
776779
BT(),

0 commit comments

Comments
 (0)