Skip to content

Commit 18c5d64

Browse files
committed
update
1 parent 83c25b4 commit 18c5d64

File tree

6 files changed

+111
-26
lines changed

6 files changed

+111
-26
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ end
5252

5353
## Examples
5454

55-
You can find many examples in the documentation, a good one to start with is [solving the independent set problem](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/IndependentSet/)
55+
You can find many examples in the documentation, a good one to start with is [solving the independent set problem](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/IndependentSet/).
5656

5757
## Supporting and Citing
5858

src/GraphTensorNetworks.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ using Graphs
1111
export timespace_complexity, timespacereadwrite_complexity, @ein_str, getixsv, getiyv
1212
export GreedyMethod, TreeSA, SABipartite, KaHyParBipartite, MergeVectors, MergeGreedy
1313

14+
# estimate memory
15+
export estimate_memory
16+
1417
# Algebras
1518
export StaticBitVector, StaticElementVector, @bv_str
1619
export is_commutative_semiring

src/configurations.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@ Find optimal solutions with bounding.
2020
* If `invert` is true, find the minimum.
2121
* If `tree_storage` is true, use [`TreeConfigEnumerator`](@ref) as the storage of solutions.
2222
"""
23-
function best_solutions(gp::GraphProblem; all=false, usecuda=false, invert=false, tree_storage::Bool=false)
23+
function best_solutions(gp::GraphProblem; all=false, usecuda=false, invert=false, tree_storage::Bool=false, T=Float64)
2424
if all && usecuda
2525
throw(ArgumentError("ConfigEnumerator can not be computed on GPU!"))
2626
end
27-
xst = generate_tensors(_x(TropicalF64; invert), gp)
27+
xst = generate_tensors(_x(Tropical{T}; invert), gp)
2828
ymask = trues(fill(2, length(getiyv(gp.code)))...)
2929
if usecuda
3030
xst = CuArray.(xst)
3131
ymask = CuArray(ymask)
3232
end
3333
if all
34-
# we use `Float64` types because we want to support weighted graphs.
35-
T = config_type(CountingTropical{Float64,Float64}, length(labels(gp)), nflavor(gp); all, tree_storage)
34+
# we use `Float64` as default because we want to support weighted graphs.
35+
T = config_type(CountingTropical{T,T}, length(labels(gp)), nflavor(gp); all, tree_storage)
3636
xs = generate_tensors(_x(T; invert), gp)
3737
ret = bounding_contract(AllConfigs{1}(), gp.code, xst, ymask, xs)
3838
return invert ? post_invert_exponent.(ret) : ret
@@ -71,12 +71,12 @@ end
7171
7272
Finding optimal and suboptimal solutions.
7373
"""
74-
best2_solutions(gp::GraphProblem; all=true, usecuda=false, invert::Bool=false) = solutions(gp, Max2Poly{Float64,Float64}; all, usecuda, invert)
74+
best2_solutions(gp::GraphProblem; all=true, usecuda=false, invert::Bool=false, T=Float64) = solutions(gp, Max2Poly{T,T}; all, usecuda, invert)
7575

76-
function bestk_solutions(gp::GraphProblem, k::Int; invert::Bool=false, tree_storage::Bool=false)
77-
xst = generate_tensors(_x(TropicalF64; invert), gp)
76+
function bestk_solutions(gp::GraphProblem, k::Int; invert::Bool=false, tree_storage::Bool=false, T=Float64)
77+
xst = generate_tensors(_x(Tropical{T}; invert), gp)
7878
ymask = trues(fill(2, length(getiyv(gp.code)))...)
79-
T = config_type(TruncatedPoly{k,Float64,Float64}, length(labels(gp)), nflavor(gp); all=true, tree_storage)
79+
T = config_type(TruncatedPoly{k,T,T}, length(labels(gp)), nflavor(gp); all=true, tree_storage)
8080
xs = generate_tensors(_x(T; invert), gp)
8181
ret = bounding_contract(AllConfigs{k}(), gp.code, xst, ymask, xs)
8282
return invert ? post_invert_exponent.(ret) : ret
@@ -88,7 +88,7 @@ end
8888
Finding all solutions grouped by size.
8989
e.g. when the problem is [`MaximalIS`](@ref), it computes all maximal independent sets, or the maximal cliques of it complement.
9090
"""
91-
all_solutions(gp::GraphProblem) = solutions(gp, Polynomial{Float64,:x}, all=true, usecuda=false, tree_storage=false)
91+
all_solutions(gp::GraphProblem; T=Float64) = solutions(gp, Polynomial{T,:x}, all=true, usecuda=false, tree_storage=false)
9292

9393
function _onehotv(::Type{Polynomial{BS,X}}, x, v) where {BS,X}
9494
Polynomial{BS,X}([onehotv(BS, x, v)])

src/graph_polynomials.jl

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ using FFTW
44
using Graphs
55

66
"""
7-
graph_polynomial(problem, method; usecuda=false, kwargs...)
7+
graph_polynomial(problem, method; usecuda=false, T=Float64, kwargs...)
88
99
Computing the graph polynomial for specific problem.
1010
11+
Positional Arguments
12+
----------------------------------
1113
* `problem` can be one of the following instances,
1214
* `IndependentSet` for the independence polynomial,
1315
* `MaximalIS` for the maximal independence polynomial,
@@ -23,27 +25,32 @@ Computing the graph polynomial for specific problem.
2325
It Consumes additional kwargs [`maxorder`, `r`]. The larger `r` is,
2426
the more accurate the factors of high order terms, and the less accurate the factors of low order terms.
2527
* `Val(:fitting)`, compute with the polynomial fitting approach, fast but inaccurate for large graphs.
28+
29+
Keyword Arguments
30+
----------------------------------
31+
* `usecuda` is true if one wants to compute with CUDA arrays,
32+
* `T` is the base type
2633
"""
2734
function graph_polynomial end
2835

29-
function graph_polynomial(gp::GraphProblem, ::Val{:fft}; usecuda=false,
36+
function graph_polynomial(gp::GraphProblem, ::Val{:fft}; usecuda=false, T=Float64,
3037
maxorder=max_size(gp; usecuda=usecuda), r=1.0)
3138
ω = exp(-2im*π/(maxorder+1))
3239
xs = r .* collect.^ (0:maxorder))
33-
ys = [Array(contractx(gp, x; usecuda=usecuda)) for x in xs]
40+
ys = [Array(contractx(gp, Complex{T}(x); usecuda=usecuda)) for x in xs]
3441
map(ci->Polynomial(ifft(getindex.(ys, Ref(ci))) ./ (r .^ (0:maxorder))), CartesianIndices(ys[1]))
3542
end
3643

37-
function graph_polynomial(gp::GraphProblem, ::Val{:fitting}; usecuda=false,
44+
function graph_polynomial(gp::GraphProblem, ::Val{:fitting}; usecuda=false, T=Float64,
3845
maxorder = max_size(gp; usecuda=usecuda))
3946
xs = (0:maxorder)
40-
ys = [Array(contractx(gp, x; usecuda=usecuda)) for x in xs]
47+
ys = [Array(contractx(gp, T(x); usecuda=usecuda)) for x in xs]
4148
map(ci->fit(xs, getindex.(ys, Ref(ci))), CartesianIndices(ys[1]))
4249
end
4350

44-
function graph_polynomial(gp::GraphProblem, ::Val{:polynomial}; usecuda=false)
51+
function graph_polynomial(gp::GraphProblem, ::Val{:polynomial}; usecuda=false, T=Float64)
4552
@assert !usecuda "Polynomial type can not be computed on GPU!"
46-
contractx(gp::GraphProblem, Polynomial([0, 1.0]))
53+
contractx(gp::GraphProblem, Polynomial(T[0, 1]))
4754
end
4855

4956
function _polynomial_single(gp::GraphProblem, ::Type{T}; usecuda, maxorder) where T
@@ -60,7 +67,8 @@ function _polynomial_single(gp::GraphProblem, ::Type{T}; usecuda, maxorder) wher
6067
return res
6168
end
6269

63-
function graph_polynomial(gp::GraphProblem, ::Val{:finitefield}; usecuda=false,
70+
# T is not used in finitefield approach
71+
function graph_polynomial(gp::GraphProblem, ::Val{:finitefield}; usecuda=false, T=Float64,
6472
maxorder=max_size(gp; usecuda=usecuda), max_iter=100)
6573
return map(Polynomial, big_integer_solve(T->_polynomial_single(gp, T; usecuda=usecuda, maxorder=maxorder), Int32, max_iter))
6674
end

src/interfaces.jl

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ Method Argument
9090
* It accepts keyword arguments `maxorder` (optional) and `r`,
9191
if `r > 1`, one has better precision for coefficients of large order, if `r < 1`,
9292
one has better precision for coefficients of small order.
93+
* `:fitting`, fit the polynomial directly.
94+
* The corresponding tensor element type is floating point numbers like `Base.Float64`.
95+
* It has round-off error.
96+
* BLAS and GPU are supported, it is the fastest among all methods.
9397
9498
Graph polynomials are not defined for weighted graph problems.
9599
"""
@@ -237,7 +241,7 @@ function solve(gp::GraphProblem, property::AbstractProperty; T=Float64, usecuda=
237241
elseif property isa CountingMin
238242
return post_invert_exponent.(contractx(gp, pre_invert_exponent(TruncatedPoly(ntuple(i->i == min_k(property) ? one(T) : zero(T), min_k(property)), one(T))); usecuda=usecuda))
239243
elseif property isa GraphPolynomial
240-
return graph_polynomial(gp, Val(graph_polynomial_method(property)); usecuda=usecuda, property.kwargs...)
244+
return graph_polynomial(gp, Val(graph_polynomial_method(property)); usecuda=usecuda, T=T, property.kwargs...)
241245
elseif property isa SingleConfigMax{false}
242246
return solutions(gp, CountingTropical{T,T}; all=false, usecuda=usecuda, )
243247
elseif property isa SingleConfigMin{false}
@@ -253,19 +257,19 @@ function solve(gp::GraphProblem, property::AbstractProperty; T=Float64, usecuda=
253257
elseif property isa ConfigsAll
254258
return solutions(gp, Real; all=true, usecuda=usecuda, tree_storage=tree_storage(property))
255259
elseif property isa SingleConfigMax{true}
256-
return best_solutions(gp; all=false, usecuda=usecuda)
260+
return best_solutions(gp; all=false, usecuda=usecuda, T=T)
257261
elseif property isa SingleConfigMin{true}
258-
return best_solutions(gp; all=false, usecuda=usecuda, invert=true)
262+
return best_solutions(gp; all=false, usecuda=usecuda, invert=true, T=T)
259263
elseif property isa ConfigsMax{1,true}
260-
return best_solutions(gp; all=true, usecuda=usecuda, tree_storage=tree_storage(property))
264+
return best_solutions(gp; all=true, usecuda=usecuda, tree_storage=tree_storage(property), T=T)
261265
elseif property isa ConfigsMin{1,true}
262-
return best_solutions(gp; all=true, usecuda=usecuda, invert=true, tree_storage=tree_storage(property))
266+
return best_solutions(gp; all=true, usecuda=usecuda, invert=true, tree_storage=tree_storage(property), T=T)
263267
elseif property isa (ConfigsMax{K,true} where K)
264-
return bestk_solutions(gp, max_k(property), tree_storage=tree_storage(property))
268+
return bestk_solutions(gp, max_k(property), tree_storage=tree_storage(property), T=T)
265269
elseif property isa (ConfigsMin{K,true} where K)
266-
return bestk_solutions(gp, min_k(property), invert=true, tree_storage=tree_storage(property))
270+
return bestk_solutions(gp, min_k(property), invert=true, tree_storage=tree_storage(property), T=T)
267271
else
268-
error("unknown property $property.")
272+
error("unknown property: `$property`.")
269273
end
270274
end
271275

@@ -379,3 +383,51 @@ end
379383
# convert to Matrix
380384
Base.Matrix(ce::ConfigEnumerator) = plain_matrix(ce)
381385
Base.Vector(ce::StaticElementVector) = collect(ce)
386+
387+
########## memory estimation ###############
388+
"""
389+
estimate_memory(problem, property; T=Float64)
390+
391+
Memory estimation in number of bytes to compute certain `property` of a `problem`.
392+
`T` is the base type.
393+
"""
394+
function estimate_memory(problem::GraphProblem, property::AbstractProperty; T=Float64)
395+
if property isa SingleConfigMax{true} || property isa SingleConfigMin{true}
396+
# TODO: fix this
397+
@warn "The estimation of memory might be unreliable for the bounded computation of a single solution due to the caching."
398+
end
399+
estimate_memory(tensor_element_type(T, length(labels(problem)), nflavor(problem), property), problem)
400+
end
401+
function estimate_memory(::Type{ET}, problem::GraphProblem) where ET
402+
if !isbitstype(ET) && !(ET <: Mod)
403+
@warn "Target tensor element type `$ET` is not a bits type, the estimation of memory might be unreliable."
404+
end
405+
lbs = labels(problem)
406+
nf = nflavor(problem)
407+
return peak_memory(problem.code, Dict([lb=>nf for lb in lbs])) * sizeof(ET)
408+
end
409+
410+
for (PROP, ET) in [(:SizeMax, :(Tropical{T})), (:SizeMin, :(Tropical{T})),
411+
(:(SingleConfigMax{true}), :(Tropical{T})), (:(SingleConfigMin{true}), :(Tropical{T})),
412+
(:(CountingAll), :T), (:(CountingMax{1}), :(CountingTropical{T,T})), (:(CountingMin{1}), :(CountingTropical{T,T})),
413+
(:(CountingMax{K}), :(TruncatedPoly{K,T,T})), (:(CountingMin{K}), :(TruncatedPoly{K,T,T})),
414+
(:(GraphPolynomial{:finitefield}), :(Mod{N,Int32} where N)), (:(GraphPolynomial{:fft}), :(Complex{T})),
415+
(:(GraphPolynomial{:polynomial}), :(Polynomial{T, :x})), (:(GraphPolynomial{:fitting}), :T),
416+
]
417+
@eval tensor_element_type(::Type{T}, n::Int, nflavor::Int, ::$PROP) where {T,K} = $ET
418+
end
419+
420+
for (PROP, ET) in [(:(SingleConfigMax{false}), :(CountingTropical{T,T})), (:(SingleConfigMin{false}), :(CountingTropical{T,T}))]
421+
@eval function tensor_element_type(::Type{T}, n::Int, nflavor::Int, ::$PROP) where {T}
422+
sampler_type($ET, n, nflavor)
423+
end
424+
end
425+
for (PROP, ET) in [
426+
(:(ConfigsMax{1}), :(CountingTropical{T,T})), (:(ConfigsMin{1}), :(CountingTropical{T,T})),
427+
(:(ConfigsMax{K}), :(TruncatedPoly{K,T,T})), (:(ConfigsMin{K}), :(TruncatedPoly{K,T,T})),
428+
(:(ConfigsAll), :(Real))
429+
]
430+
@eval function tensor_element_type(::Type{T}, n::Int, nflavor::Int, ::$PROP) where {T,K}
431+
set_type($ET, n, nflavor)
432+
end
433+
end

test/interfaces.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,26 @@ end
179179
@test length(res1) == length(res2)
180180
@test Set(res2 |> collect) == Set(res1 |> collect)
181181
end
182+
end
183+
184+
@testset "memory estimation" begin
185+
gp = IndependentSet(smallgraph(:petersen))
186+
for property in [
187+
SizeMax(), SizeMin(), CountingMax(), CountingMin(), CountingMax(2), CountingMin(2),
188+
ConfigsMax(;bounded=true), ConfigsMin(;bounded=true), ConfigsMax(2;bounded=true), ConfigsMin(2;bounded=true),
189+
ConfigsMax(;bounded=false), ConfigsMin(;bounded=false), ConfigsMax(2;bounded=false), ConfigsMin(2;bounded=false), SingleConfigMax(;bounded=false), SingleConfigMin(;bounded=false),
190+
CountingAll(), ConfigsAll(),
191+
]
192+
@show property
193+
ET = GraphTensorNetworks.tensor_element_type(Float32, 10, 2, property)
194+
@test eltype(solve(gp, property, T=Float32)) <: ET
195+
end
196+
@test GraphTensorNetworks.tensor_element_type(Float32, 10, 2, GraphPolynomial(method=:polynomial)) == Polynomial{Float32, :x}
197+
@test sizeof(GraphTensorNetworks.tensor_element_type(Float32, 10, 2, GraphPolynomial(method=:fitting))) == 4
198+
@test sizeof(GraphTensorNetworks.tensor_element_type(Float32, 10, 2, GraphPolynomial(method=:fft))) == 8
199+
@test sizeof(GraphTensorNetworks.tensor_element_type(Float64, 10, 2, GraphPolynomial(method=:finitefield))) == 4
200+
@test GraphTensorNetworks.tensor_element_type(Float32, 10, 2, SingleConfigMax(;bounded=true)) == Tropical{Float32}
201+
@test GraphTensorNetworks.tensor_element_type(Float32, 10, 2, SingleConfigMin(;bounded=true)) == Tropical{Float32}
202+
203+
@test estimate_memory(gp, SizeMax()) * 2 == estimate_memory(gp, CountingMax())
182204
end

0 commit comments

Comments
 (0)