Skip to content

Commit 7a6e6eb

Browse files
committed
new opt method auto
1 parent 24987de commit 7a6e6eb

File tree

9 files changed

+191
-95
lines changed

9 files changed

+191
-95
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ Viznet = "52a3aca4-6234-47fd-b74a-806bdf78ede9"
2424
[compat]
2525
CUDA = "3.3"
2626
Compose = "0.9"
27+
FFTW = "1.4"
2728
LightGraphs = "1.3"
2829
Mods = "1.3"
2930
OMEinsum = "0.4"
30-
OMEinsumContractionOrders = "0.1"
31+
OMEinsumContractionOrders = "0.2"
3132
Polynomials = "2.0"
3233
Primes = "0.5"
3334
Requires = "1"
3435
TropicalGEMM = "0.1"
3536
TropicalNumbers = "0.4"
3637
TupleTools = "1.2"
3738
Viznet = "0.3"
38-
FFTW = "1.4"
3939
julia = "1"
4040

4141
[extras]

src/arithematics.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export is_commutative_semiring
22
export Max2Poly, Polynomial, Tropical, CountingTropical, StaticBitVector, Mod, ConfigEnumerator, onehotv
3+
export bitstringset_type, bitstringsampler_type
34

45
using Polynomials: Polynomial
56
using TropicalNumbers: Tropical, CountingTropical, StaticBitVector

src/bounding.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
using TupleTools
22

3-
export bounding_contract
4-
53
"""
64
backward_tropical(mode, ixs, xs, iy, y, ymask, size_dict)
75
@@ -85,6 +83,15 @@ function masked_einsum(code::NestedEinsum, @nospecialize(xs), masks, size_dict)
8583
y[OMEinsum.asarray(.!masks.content)] .= Ref(zero(eltype(y))); y
8684
end
8785

86+
"""
87+
bounding_contract(code, xsa, ymask, xsb; size_info=nothing)
88+
89+
Contraction method with bounding.
90+
91+
* `xsa` are input tensors for bounding, e.g. tropical tensors,
92+
* `xsb` are input tensors for computing, e.g. tensors elements are counting tropical with set algebra,
93+
* `ymask` is the initial gradient mask for the output tensor.
94+
"""
8895
function bounding_contract(@nospecialize(code::EinCode), @nospecialize(xsa), ymask, @nospecialize(xsb); size_info=nothing)
8996
bounding_contract(NestedEinsum((1:length(xsa)), code), xsa, ymask, xsb; size_info=size_info)
9097
end
@@ -100,11 +107,12 @@ function bounding_contract(code::NestedEinsum, @nospecialize(xsa), ymask, @nospe
100107
masked_einsum(code, xsb, mt, size_dict)
101108
end
102109

103-
function mis_config_ad(@nospecialize(code::EinCode), @nospecialize(xsa), ymask; size_info=nothing)
104-
mis_config_ad(NestedEinsum((1:length(xsa)), code), xsa, ymask; size_info=size_info)
110+
# get the optimal solution with automatic differentiation.
111+
function solution_ad(@nospecialize(code::EinCode), @nospecialize(xsa), ymask; size_info=nothing)
112+
solution_ad(NestedEinsum((1:length(xsa)), code), xsa, ymask; size_info=size_info)
105113
end
106114

107-
function mis_config_ad(code::NestedEinsum, @nospecialize(xsa), ymask; size_info=nothing)
115+
function solution_ad(code::NestedEinsum, @nospecialize(xsa), ymask; size_info=nothing)
108116
size_dict = OMEinsum.get_size_dict(getixs(flatten(code)), xsa, size_info)
109117
# compute intermediate tensors
110118
@debug "caching einsum..."

src/configurations.jl

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1-
export getconfigs_bounded, getconfigs_direct
1+
export optimalsolutions, solutions
22

3-
function getconfigs_bounded(gp::GraphProblem; all=false, usecuda=false)
3+
"""
4+
optimalsolutions(problem; all=false, usecuda=false)
5+
6+
Find optimal solutions with bounding.
7+
8+
* When `all` is true, the program will use set for enumerate all possible solutions, otherwise, it will return one solution for each size.
9+
* `usecuda` can not be true if you want to use set to enumerate all possible solutions.
10+
"""
11+
function optimalsolutions(gp::GraphProblem; all=false, usecuda=false)
412
if all && usecuda
513
throw(ArgumentError("ConfigEnumerator can not be computed on GPU!"))
614
end
@@ -18,22 +26,34 @@ function getconfigs_bounded(gp::GraphProblem; all=false, usecuda=false)
1826
return bounding_contract(gp.code, xst, ymask, xs)
1927
else
2028
@assert ndims(ymask) == 0
21-
t, res = mis_config_ad(gp.code, xst, ymask)
29+
t, res = solution_ad(gp.code, xst, ymask)
2230
N = length(vertex_index)
2331
return fill(CountingTropical(asscalar(t).n, ConfigSampler(StaticBitVector(map(l->res[l], 1:N)))))
2432
end
2533
end
2634

27-
function getconfigs_direct(gp::GraphProblem; all=false, usecuda=false)
35+
"""
36+
solutions(problem, basetype; all=false, usecuda=false)
37+
38+
General routine to find solutions without bounding,
39+
40+
* `basetype` can be a type with counting field,
41+
* `CountingTropical{Float64,Float64}` for finding optimal solutions,
42+
* `Polynomial{Float64, 1.0}` for enumerating all solutions,
43+
* `Max2Poly{Float64, 1.0}` for optimal and suboptimal solutions.
44+
* When `all` is true, the program will use set for enumerate all possible solutions, otherwise, it will return one solution for each size.
45+
* `usecuda` can not be true if you want to use set to enumerate all possible solutions.
46+
"""
47+
function solutions(gp::GraphProblem, ::Type{BT}; all=false, usecuda=false) where BT
2848
if all && usecuda
2949
throw(ArgumentError("ConfigEnumerator can not be computed on GPU!"))
3050
end
31-
T = (all ? bitstringset_type : bitstringsampler_type)(CountingTropical{Int64}, length(labels(gp.code)))
32-
syms = labels(gp.code)
33-
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
34-
xs = generate_tensors(l->onehotv(T, vertex_index[l]), gp)
35-
if usecuda
36-
xs = CuArray.(xs)
37-
end
38-
dynamic_einsum(gp.code, xs)
51+
return contractf(fx_solutions(gp, BT, all), gp; usecuda=usecuda)
52+
end
53+
54+
# return a mapping from label to variable `x`
55+
function fx_solutions(gp::GraphProblem, ::Type{BT}, all::Bool) where BT
56+
T = (all ? bitstringset_type : bitstringsampler_type)(BT, length(labels(gp.code)))
57+
vertex_index = Dict([s=>i for (i, s) in enumerate(labels(gp.code))])
58+
return l->onehotv(T, vertex_index[l])
3959
end

src/graph_polynomials.jl

Lines changed: 91 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,47 @@ using OMEinsum: NestedEinsum, getixs, getiy
33
using FFTW
44
using LightGraphs
55

6-
export contractx, graph_polynomial, optimize_code
6+
export contractx, contractf, graph_polynomial, optimize_code
77
export Independence, MaximalIndependence, Matching, Coloring
88
const EinTypes = Union{EinCode,NestedEinsum}
99

1010
abstract type GraphProblem end
11+
"""
12+
Independence{CT<:EinTypes} <: GraphProblem
13+
Independence(graph; kwargs...)
14+
15+
Independent set problem. For `kwargs`, check `optimize_code` API.
16+
"""
1117
struct Independence{CT<:EinTypes} <: GraphProblem
1218
code::CT
1319
end
20+
21+
"""
22+
Independence{CT<:EinTypes} <: GraphProblem
23+
Independence(graph; kwargs...)
24+
25+
Independent set problem. For `kwargs`, check `optimize_code` API.
26+
"""
1427
struct MaximalIndependence{CT<:EinTypes} <: GraphProblem
1528
code::CT
1629
end
30+
31+
"""
32+
Matching{CT<:EinTypes} <: GraphProblem
33+
Matching(graph; kwargs...)
34+
35+
Vertex matching problem. For `kwargs`, check `optimize_code` API.
36+
"""
1737
struct Matching{CT<:EinTypes} <: GraphProblem
1838
code::CT
1939
end
40+
41+
"""
42+
Coloring{K,CT<:EinTypes} <: GraphProblem
43+
Coloring{K}(graph; kwargs...)
44+
45+
K-Coloring problem. For `kwargs`, check `optimize_code` API.
46+
"""
2047
struct Coloring{K,CT<:EinTypes} <: GraphProblem
2148
code::CT
2249
end
@@ -39,7 +66,31 @@ function labels(code::EinTypes)
3966
return res
4067
end
4168

42-
function graph_polynomial(gp::GraphProblem, ::Val{:fft}; usecuda=false, maxorder=graph_polynomial_maxorder(gp; usecuda=usecuda), r=1.0)
69+
"""
70+
graph_polynomial(problem, method; usecuda=false, kwargs...)
71+
72+
Computing the graph polynomial for specific problem.
73+
74+
* `problem` can be one of the following instances,
75+
* `Independence` for the independence polynomial,
76+
* `MaximalIndependence` for the maximal independence polynomial,
77+
* `Matching` for the matching polynomial,
78+
79+
* `method` can be one of the following inputs,
80+
* `Val(:finitefield)`, compute exactly with the finite field method.
81+
It consumes additional kwargs [`max_iter`, `maxorder`], where `maxorder` is maximum order of polynomial
82+
and `max_iter` is the maximum number of primes numbers to use in the finitefield algebra.
83+
`max_iter` can be determined automatically in most cases.
84+
* `Val(:polynomial)`, compute directly with `Polynomial` number type,
85+
* `Val(:fft)`, compute with the fast fourier transformation approach, fast but needs to tune the hyperparameter `r`.
86+
It Consumes additional kwargs [`maxorder`, `r`]. The larger `r` is,
87+
the more accurate the factors of high order terms, and the less accurate the factors of low order terms.
88+
* `Val(:fitting)`, compute with the polynomial fitting approach, fast but inaccurate for large graphs.
89+
"""
90+
function graph_polynomial end
91+
92+
function graph_polynomial(gp::GraphProblem, ::Val{:fft}; usecuda=false,
93+
maxorder=graph_polynomial_maxorder(gp; usecuda=usecuda), r=1.0)
4394
ω = exp(-2im*π/(maxorder+1))
4495
xs = r .* collect.^ (0:maxorder))
4596
ys = [asscalar(contractx(gp, x; usecuda=usecuda)) for x in xs]
@@ -68,7 +119,8 @@ function _polynomial_single(gp::GraphProblem, ::Type{T}; usecuda, maxorder) wher
68119
A \ T.(ys)
69120
end
70121

71-
function graph_polynomial(gp::GraphProblem, ::Val{:finitefield}; usecuda=false, maxorder=graph_polynomial_maxorder(gp; usecuda=usecuda), max_iter=100)
122+
function graph_polynomial(gp::GraphProblem, ::Val{:finitefield}; usecuda=false,
123+
maxorder=graph_polynomial_maxorder(gp; usecuda=usecuda), max_iter=100)
72124
TI = Int32 # Int 32 is faster
73125
N = typemax(TI)
74126
YS = []
@@ -93,21 +145,38 @@ function improved_counting(sequences)
93145
map(yi->Mods.CRT(yi...), zip(sequences...))
94146
end
95147

96-
function optimize_code(code; method=:kahypar, sc_target=17, max_group_size=40, nrepeat=10, imbalances=0.0:0.001:0.8)
148+
"""
149+
optimize_code(code; optmethod=:kahypar, sc_target=17, max_group_size=40, nrepeat=10, imbalances=0.0:0.001:0.8)
150+
151+
Optimize the contraction order.
152+
153+
* `optmethod` can be one of
154+
* `:kahypar`, the kahypar + greedy approach, takes kwargs [`sc_target`, `max_group_size`, `imbalances`].
155+
Check `optimize_kahypar` method in package `OMEinsumContractionOrders`.
156+
* `:auto`, also the kahypar + greedy approach, but determines `sc_target` automatically. It is slower!
157+
* `:greedy`, the greedy approach. Check in `optimize_greedy` in package `OMEinsum`.
158+
* `:raw`, do nothing and return the raw EinCode.
159+
"""
160+
function optimize_code(code::EinTypes; optmethod=:kahypar, sc_target=17, max_group_size=40, nrepeat=10, imbalances=0.0:0.001:0.8)
97161
size_dict = Dict([s=>2 for s in labels(code)])
98-
optcode = if method == :kahypar
162+
optcode = if optmethod == :kahypar
99163
optimize_kahypar(code, size_dict; sc_target=sc_target, max_group_size=max_group_size, imbalances=imbalances)
100-
elseif method == :greedy
164+
elseif optmethod == :greedy
101165
optimize_greedy(code, size_dict; nrepeat=nrepeat)
166+
elseif optmethod == :auto
167+
optimize_kahypar_auto(code, size_dict; max_group_size=max_group_size, effort=500)
168+
elseif optmethod == :raw
169+
code
102170
else
103-
ArgumentError("optimizer `$method` not defined.")
171+
ArgumentError("optimizer `$optmethod` not defined.")
104172
end
105173
println("time/space complexity is $(OMEinsum.timespace_complexity(optcode, size_dict))")
106174
return optcode
107175
end
108176

109-
function contractx(gp::GraphProblem, x::T; usecuda=false) where T
110-
xs = generate_tensors(_->x, gp)
177+
contractx(gp::GraphProblem, x; usecuda=false) = contractf(_->x, gp; usecuda=usecuda)
178+
function contractf(f, gp::GraphProblem; usecuda=false)
179+
xs = generate_tensors(f, gp)
111180
if usecuda
112181
xs = CuArray.(xs)
113182
end
@@ -116,9 +185,10 @@ end
116185

117186
############### Problem specific implementations ################
118187
### independent set ###
119-
function Independence(g::SimpleGraph)
120-
Independence(EinCode(([(i,) for i in LightGraphs.vertices(g)]..., # labels for vertex tensors
121-
[minmax(e.src,e.dst) for e in LightGraphs.edges(g)]...), ())) # labels for edge tensors
188+
function Independence(g::SimpleGraph; kwargs...)
189+
rawcode = EinCode(([(i,) for i in LightGraphs.vertices(g)]..., # labels for vertex tensors
190+
[minmax(e.src,e.dst) for e in LightGraphs.edges(g)]...), ()) # labels for edge tensors
191+
Independence(optimize_code(rawcode; kwargs...))
122192
end
123193

124194
function generate_tensors(fx, gp::Independence)
@@ -136,7 +206,7 @@ misv(val::T) where T = [one(T), val]
136206
graph_polynomial_maxorder(gp::Independence; usecuda) = Int(sum(contractx(gp, TropicalF64(1.0); usecuda=usecuda)).n)
137207

138208
### coloring ###
139-
Coloring(g::SimpleGraph) = Coloring(Independence(g).code)
209+
Coloring(g::SimpleGraph; kwargs...) = Coloring(Independence(g; kwargs...).code)
140210
function generate_tensors(fx, c::Coloring{K}) where K
141211
ixs = getixs(flatten(code))
142212
T = eltype(fx(ixs[1][1]))
@@ -159,9 +229,10 @@ end
159229
coloringv(vals::Vector{T}) where T = vals
160230

161231
### matching ###
162-
function Matching(g::SimpleGraph)
163-
Matching(EinCode(([(minmax(e.src,e.dst),) for e in LightGraphs.edges(g)]..., # labels for edge tensors
164-
[([minmax(i,j) for j in neighbors(g, i)]...,) for i in LightGraphs.vertices(g)]...,), ())) # labels for vertex tensors
232+
function Matching(g::SimpleGraph; kwargs...)
233+
rawcode = EinCode(([(minmax(e.src,e.dst),) for e in LightGraphs.edges(g)]..., # labels for edge tensors
234+
[([minmax(i,j) for j in neighbors(g, i)]...,) for i in LightGraphs.vertices(g)]...,), ()) # labels for vertex tensors
235+
Matching(optimize_code(rawcode; kwargs...))
165236
end
166237

167238
function generate_tensors(fx, m::Matching)
@@ -193,8 +264,9 @@ end
193264
graph_polynomial_maxorder(m::Matching; usecuda) = Int(sum(contractx(m, TropicalF64(1.0); usecuda=usecuda)).n)
194265

195266
### maximal independent set ###
196-
function MaximalIndependence(g::SimpleGraph)
197-
MaximalIndependence(EinCode(([(LightGraphs.neighbors(g, v)..., v) for v in LightGraphs.vertices(g)]...,), ()))
267+
function MaximalIndependence(g::SimpleGraph; kwargs...)
268+
rawcode = EinCode(([(LightGraphs.neighbors(g, v)..., v) for v in LightGraphs.vertices(g)]...,), ())
269+
MaximalIndependence(optimize_code(rawcode; kwargs...))
198270
end
199271

200272
function generate_tensors(fx, mi::MaximalIndependence)
@@ -213,8 +285,4 @@ function neighbortensor(x::T, d::Int) where T
213285
return t
214286
end
215287

216-
graph_polynomial_maxorder(mi::MaximalIndependence; usecuda) = Int(sum(contractx(mi, TropicalF64(1.0); usecuda=usecuda)).n)
217-
218-
for GP in [:Independence, :MaximalIndependence, :Matching, :Coloring]
219-
@eval optimize_code(gp::$GP; kwargs...) = $GP(optimize_code(gp.code; kwargs...))
220-
end
288+
graph_polynomial_maxorder(mi::MaximalIndependence; usecuda) = Int(sum(contractx(mi, TropicalF64(1.0); usecuda=usecuda)).n)

0 commit comments

Comments
 (0)