Skip to content

Commit 7cd2006

Browse files
authored
Refactor the graph problems (#10)
* updat * update * fix all tests * fix tests
1 parent 2c4d157 commit 7cd2006

22 files changed

+664
-567
lines changed

docs/make.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ makedocs(;
3535
"Max-Cut Problem" => "tutorials/MaxCut.md",
3636
"Other Problems" => "tutorials/Others.md",
3737
],
38-
"Method Selection Guide" => "methodselection.md",
3938
"References" => "ref.md",
4039
],
4140
doctest=false,

docs/src/methodselection.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

docs/src/ref.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
## Graph problems
33
```@docs
44
solve
5+
GraphProblem
56
Independence
67
MaximalIndependence
78
Matching
@@ -14,6 +15,20 @@ PaintShop
1415
set_packing
1516
```
1617

18+
#### Graph Problem Interfaces
19+
```@docs
20+
generate_tensors
21+
symbols
22+
flavors
23+
get_weights
24+
nflavor
25+
```
26+
27+
To subtype [`GraphProblem`](@ref), the new type must contain a `code` field to represent the (optimized) tensor network.
28+
Interfaces [`generate_tensors`](@ref), [`symbols`](@ref), [`flavors`](@ref) and [`get_weights`] are required.
29+
[`nflavor`] is optimal.
30+
31+
1732
## Properties
1833
```@docs
1934
SizeMax
@@ -50,7 +65,10 @@ is_commutative_semiring
5065
## Tensor Network
5166
```@docs
5267
optimize_code
68+
getixsv
69+
getiyv
5370
timespace_complexity
71+
timespacereadwrite_complexity
5472
@ein_str
5573
GreedyMethod
5674
TreeSA

src/GraphTensorNetworks.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ using OMEinsum: timespace_complexity, getixsv
88
using Graphs
99

1010
# OMEinsum
11-
export timespace_complexity, @ein_str
11+
export timespace_complexity, timespacereadwrite_complexity, @ein_str, getixsv, getiyv
1212
export GreedyMethod, TreeSA, SABipartite, KaHyParBipartite, MergeVectors, MergeGreedy
1313

1414
# Algebras
@@ -27,7 +27,8 @@ export random_regular_graph, diagonal_coupled_graph, is_independent_set
2727
export square_lattice_graph, unitdisk_graph
2828

2929
# Tensor Networks (Graph problems)
30-
export Independence, MaximalIndependence, Matching, Coloring, optimize_code, set_packing, MaxCut, PaintShop, paintshop_from_pairs, UnWeighted
30+
export GraphProblem, Independence, MaximalIndependence, Matching, Coloring, optimize_code, set_packing, MaxCut, PaintShop, paintshop_from_pairs, UnWeighted
31+
export flavors, symbols, nflavor, get_weights
3132

3233
# Interfaces
3334
export solve, SizeMax, CountingAll, CountingMax, GraphPolynomial, SingleConfigMax, ConfigsAll, ConfigsMax
@@ -44,7 +45,7 @@ project_relative_path(xs...) = normpath(joinpath(dirname(dirname(pathof(@__MODUL
4445
include("utils.jl")
4546
include("bitvector.jl")
4647
include("arithematics.jl")
47-
include("networks.jl")
48+
include("networks/networks.jl")
4849
include("graph_polynomials.jl")
4950
include("configurations.jl")
5051
include("graphs.jl")

src/configurations.jl

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ function best_solutions(gp::GraphProblem; all=false, usecuda=false)
1111
throw(ArgumentError("ConfigEnumerator can not be computed on GPU!"))
1212
end
1313
syms = symbols(gp)
14-
T = (all ? set_type : sampler_type)(CountingTropical{Int64}, length(syms), bondsize(gp))
14+
T = (all ? set_type : sampler_type)(CountingTropical{Int64}, length(syms), nflavor(gp))
1515
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
16-
xst = generate_tensors(l->TropicalF64(get_weight(gp, vertex_index[l])), gp)
16+
xst = generate_tensors(l->TropicalF64.(get_weights(gp, l)), gp)
1717
ymask = trues(fill(2, length(getiyv(gp.code)))...)
1818
if usecuda
1919
xst = CuArray.(xst)
2020
ymask = CuArray(ymask)
2121
end
2222
if all
23-
xs = generate_tensors(l->_onehotv(T, vertex_index[l], 1, get_weight(gp, vertex_index[l])), gp)
23+
xs = generate_tensors(l->_onehotv.(Ref(T), vertex_index[l], flavors(gp), get_weights(gp, l)), gp)
2424
return bounding_contract(AllConfigs{1}(), gp.code, xst, ymask, xs)
2525
else
2626
@assert ndims(ymask) == 0
@@ -59,57 +59,39 @@ best2_solutions(gp::GraphProblem; all=true, usecuda=false) = solutions(gp, Max2P
5959
function bestk_solutions(gp::GraphProblem, k::Int)
6060
syms = symbols(gp)
6161
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
62-
xst = generate_tensors(l->TropicalF64(get_weight(gp, vertex_index[l])), gp)
62+
xst = generate_tensors(l->TropicalF64.(get_weights(gp, l)), gp)
6363
ymask = trues(fill(2, length(getiyv(gp.code)))...)
64-
T = set_type(TruncatedPoly{k,Float64,Float64}, length(syms), bondsize(gp))
65-
xs = generate_tensors(l->_onehotv(T, vertex_index[l], 1, get_weight(gp, vertex_index[l])), gp)
64+
T = set_type(TruncatedPoly{k,Float64,Float64}, length(syms), nflavor(gp))
65+
xs = generate_tensors(l->_onehotv.(Ref(T), vertex_index[l], flavors(gp), get_weights(gp, l)), gp)
6666
return bounding_contract(AllConfigs{k}(), gp.code, xst, ymask, xs)
6767
end
6868

6969
"""
7070
all_solutions(problem)
7171
72-
Finding all solutions.
72+
Finding all solutions grouped by size.
7373
e.g. when the problem is `MaximalIndependence`, it computes all maximal independent sets, or the maximal cliques of it complement.
7474
"""
7575
all_solutions(gp::GraphProblem) = solutions(gp, Polynomial{Float64,:x}, all=true, usecuda=false)
7676

77-
# return a mapping from label to variable `x`
78-
for GP in [:Independence, :Matching, :MaximalIndependence, :MaxCut, :PaintShop]
79-
@eval function fx_solutions(gp::$GP, ::Type{BT}, all::Bool) where BT
80-
syms = symbols(gp)
81-
T = (all ? set_type : sampler_type)(BT, length(syms), bondsize(gp))
82-
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
83-
return l->_onehotv(T, vertex_index[l], 1, get_weight(gp, vertex_index[l]))
84-
end
85-
end
86-
function fx_solutions(gp::Coloring{K}, ::Type{BT}, all::Bool) where {K,BT}
77+
# return a mapping from label to onehot bitstrings (degree of freedoms).
78+
function fx_solutions(gp::GraphProblem, ::Type{BT}, all::Bool) where {K,BT}
8779
syms = symbols(gp)
88-
T = (all ? set_type : sampler_type)(BT, length(syms), bondsize(gp))
80+
T = (all ? set_type : sampler_type)(BT, length(syms), nflavor(gp))
8981
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
9082
return function (l)
91-
map(1:K) do k
92-
_onehotv(T, vertex_index[l], k, get_weight(gp, vertex_index[l]))
93-
end
83+
_onehotv.(Ref(T), vertex_index[l], flavors(gp), get_weights(gp, l))
9484
end
9585
end
9686
function _onehotv(::Type{Polynomial{BS,X}}, x, v, w) where {BS,X}
97-
@assert isone(w)
98-
Polynomial{BS,X}([zero(BS), onehotv(BS, x, v)])
87+
Polynomial{BS,X}([zero(BS), onehotv(BS, x, v)])^w
9988
end
10089
function _onehotv(::Type{TruncatedPoly{K,BS,OS}}, x, v, w) where {K,BS,OS}
101-
@assert isone(w)
102-
TruncatedPoly{K,BS,OS}(ntuple(i->i<K ? zero(BS) : onehotv(BS, x, v), K),one(OS))
90+
TruncatedPoly{K,BS,OS}(ntuple(i->i<K ? zero(BS) : onehotv(BS, x, v), K),one(OS))^w
10391
end
10492
function _onehotv(::Type{CountingTropical{TV,BS}}, x, v, w) where {TV,BS}
10593
CountingTropical{TV,BS}(TV(w), onehotv(BS, x, v))
10694
end
10795
function _onehotv(::Type{BS}, x, v, w) where {BS<:ConfigEnumerator}
10896
onehotv(BS, x, v)
109-
end
110-
111-
for GP in [:Independence, :Matching, :MaximalIndependence, :Coloring]
112-
@eval symbols(gp::$GP) = labels(gp.code)
113-
end
114-
symbols(gp::MaxCut) = getixsv(gp.code)
115-
symbols(gp::PaintShop) = getixsv(gp.code)
97+
end

src/graph_polynomials.jl

Lines changed: 2 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -96,130 +96,6 @@ function improved_counting(ys::AbstractArray...)
9696
end
9797
improved_counting(ys::Mod...) = Mods.CRT(ys...)
9898

99-
contractx(gp::GraphProblem, x; usecuda=false) = contractf(_->x, gp; usecuda=usecuda)
100-
function contractf(f, gp::GraphProblem; usecuda=false)
101-
@debug "generating tensors ..."
102-
xs = generate_tensors(f, gp)
103-
@debug "contracting tensors ..."
104-
if usecuda
105-
gp.code([CuArray(x) for x in xs]...)
106-
else
107-
gp.code(xs...)
108-
end
109-
end
110-
111-
############### Problem specific implementations ################
112-
### independent set ###
113-
function generate_tensors(fx, gp::Independence)
114-
ixs = getixsv(gp.code)
115-
n = length(unique!(vcat(ixs...)))
116-
T = typeof(fx(ixs[1][1]))
117-
return map(enumerate(ixs)) do (i, ix)
118-
if i <= n
119-
misv(fx(ix[1]))
120-
else
121-
misb(T, length(ix)) # if n!=2, it corresponds to set packing problem.
122-
end
123-
end
124-
end
125-
126-
function misb(::Type{T}, n::Integer=2) where T
127-
res = zeros(T, fill(2, n)...)
128-
res[1] = one(T)
129-
for i=1:n
130-
res[1+1<<(i-1)] = one(T)
131-
end
132-
return res
133-
end
134-
misv(val::T) where T = [one(T), val]
135-
136-
### coloring ###
137-
function generate_tensors(fx, c::Coloring{K}) where K
138-
ixs = getixsv(c.code)
139-
T = eltype(fx(ixs[1][1]))
140-
return map(ixs) do ix
141-
# if the tensor rank is 1, create a vertex tensor.
142-
# otherwise the tensor rank must be 2, create a bond tensor.
143-
length(ix)==1 ? coloringv(fx(ix[1])) : coloringb(T, K)
144-
end
145-
end
146-
147-
# coloring bond tensor
148-
function coloringb(::Type{T}, k::Int) where T
149-
x = ones(T, k, k)
150-
for i=1:k
151-
x[i,i] = zero(T)
152-
end
153-
return x
154-
end
155-
# coloring vertex tensor
156-
coloringv(vals::Vector{T}) where T = vals
157-
158-
### matching ###
159-
function generate_tensors(fx, m::Matching)
160-
ixs = getixsv(m.code)
161-
T = typeof(fx(ixs[1][1]))
162-
n = length(unique!(vcat(ixs...))) # number of vertices
163-
tensors = []
164-
for i=1:length(ixs)
165-
if i<=n
166-
@assert length(ixs[i]) == 1
167-
t = T[one(T), fx(ixs[i][1])] # fx is defined on edges.
168-
else
169-
t = match_tensor(T, length(ixs[i]))
170-
end
171-
push!(tensors, t)
172-
end
173-
return tensors
174-
end
175-
function match_tensor(::Type{T}, n::Int) where T
176-
t = zeros(T, fill(2, n)...)
177-
for ci in CartesianIndices(t)
178-
if sum(ci.I .- 1) <= 1
179-
t[ci] = one(T)
180-
end
181-
end
182-
return t
183-
end
184-
185-
### maximal independent set ###
186-
function generate_tensors(fx, mi::MaximalIndependence)
187-
ixs = getixsv(mi.code)
188-
T = eltype(fx(ixs[1][end]))
189-
return map(ixs) do ix
190-
neighbortensor(fx(ix[end]), length(ix))
191-
end
192-
end
193-
function neighbortensor(x::T, d::Int) where T
194-
t = zeros(T, fill(2, d)...)
195-
for i = 2:1<<(d-1)
196-
t[i] = one(T)
197-
end
198-
t[1<<(d-1)+1] = x
199-
return t
200-
end
201-
202-
### max cut/spin glass problem ###
203-
function generate_tensors(fx, gp::MaxCut)
204-
ixs = getixsv(gp.code)
205-
return map(enumerate(ixs)) do (i, ix)
206-
maxcutb(fx(ix))
207-
end
208-
end
209-
function maxcutb(expJ::T) where T
210-
return T[one(T) expJ; expJ one(T)]
211-
end
212-
213-
### paint shop ###
214-
function generate_tensors(fx, c::PaintShop)
215-
ixs = getixsv(c.code)
216-
T = eltype(fx(ixs[1]))
217-
[paintshop_bond_tensor(fx(ixs[i]), c.isfirst[i], c.isfirst[i+1]) for i=1:length(ixs)]
218-
end
219-
220-
function paintshop_bond_tensor(x::T, if1::Bool, if2::Bool) where T
221-
m = T[x one(T); one(T) x]
222-
m = if1 ? m : m[[2,1],:]
223-
m = if2 ? m : m[:,[2,1]]
224-
return m
99+
for GP in [:Independence, :Matching, :MaxCut, :MaximalIndependence, :PaintShop]
100+
@eval contractx(gp::$GP, x::T; usecuda=false) where T = contractf(l->Ref(x) .^ get_weights(gp, l), gp; usecuda=usecuda)
225101
end

src/interfaces.jl

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
abstract type AbstractProperty end
2-
_support_weight(::AbstractProperty) = false
32

43
"""
54
SizeMax <: AbstractProperty
@@ -12,7 +11,6 @@ The maximum independent set size.
1211
* BLAS (on CPU) and GPU are supported,
1312
"""
1413
struct SizeMax <: AbstractProperty end
15-
_support_weight(::SizeMax) = true
1614

1715
"""
1816
CountingAll <: AbstractProperty
@@ -25,7 +23,6 @@ Counting the total number of sets. e.g. for [`Independence`](@ref) problem, it c
2523
* BLAS (GPU and CPU) and GPU are supported,
2624
"""
2725
struct CountingAll <: AbstractProperty end
28-
_support_weight(::CountingAll) = true
2926

3027
"""
3128
CountingMax{K} <: AbstractProperty
@@ -41,7 +38,6 @@ it counts independent sets of size ``\\alpha(G), \\alpha(G)-1, \\ldots, \\alpha(
4138
struct CountingMax{K} <: AbstractProperty end
4239
CountingMax(K::Int=1) = CountingMax{K}()
4340
max_k(::CountingMax{K}) where K = K
44-
_support_weight(::CountingMax{1}) = true
4541

4642
"""
4743
GraphPolynomial{METHOD} <: AbstractProperty
@@ -87,7 +83,6 @@ Finding single best solution, e.g. for [`Independence`](@ref) problem, it is one
8783
"""
8884
struct SingleConfigMax{BOUNDED} <:AbstractProperty end
8985
SingleConfigMax(; bounded::Bool=false) = SingleConfigMax{bounded}()
90-
_support_weight(::SingleConfigMax) = true
9186

9287
"""
9388
ConfigsAll <:AbstractProperty
@@ -99,7 +94,6 @@ Find all valid configurations, e.g. for [`Independence`](@ref) problem, it is fi
9994
* Weights do not take effect.
10095
"""
10196
struct ConfigsAll <:AbstractProperty end
102-
_support_weight(::ConfigsAll) = true
10397

10498
"""
10599
ConfigsMax{K, BOUNDED} <:AbstractProperty
@@ -114,7 +108,6 @@ it is finding all independent sets of sizes ``\\alpha(G), \\alpha(G)-1, \\ldots,
114108
struct ConfigsMax{K, BOUNDED} <:AbstractProperty end
115109
ConfigsMax(K::Int=1; bounded::Bool=true) = ConfigsMax{K,bounded}()
116110
max_k(::ConfigsMax{K}) where K = K
117-
_support_weight(::ConfigsMax{1}) = true
118111

119112
"""
120113
solve(problem, property; usecuda=false, T=Float64)
@@ -143,19 +136,16 @@ Keyword arguments
143136
* `T` is the "base" element type, sometimes can be used to reduce the memory cost.
144137
"""
145138
function solve(gp::GraphProblem, property::AbstractProperty; T=Float64, usecuda=false)
146-
if !_support_weight(property) && _has_weight(gp)
147-
throw(ArgumentError("weighted instance of type $(typeof(gp)) is not supported in computing $(property)"))
148-
end
149139
if property isa SizeMax
150140
syms = symbols(gp)
151141
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
152-
return contractf(x->Tropical(T(get_weight(gp, vertex_index[x]))), gp; usecuda=usecuda)
142+
return contractf(x->Tropical{T}.(get_weights(gp, vertex_index[x])), gp; usecuda=usecuda)
153143
elseif property isa CountingAll
154144
return contractx(gp, one(T); usecuda=usecuda)
155145
elseif property isa CountingMax{1}
156146
syms = symbols(gp)
157147
vertex_index = Dict([s=>i for (i, s) in enumerate(syms)])
158-
return contractf(x->CountingTropical(T(get_weight(gp, vertex_index[x])), one(T)), gp; usecuda=usecuda)
148+
return contractf(x->CountingTropical{T,T}.(get_weights(gp, vertex_index[x])), gp; usecuda=usecuda)
159149
elseif property isa CountingMax
160150
return contractx(gp, TruncatedPoly(ntuple(i->i == max_k(property) ? one(T) : zero(T), max_k(property)), one(T)); usecuda=usecuda)
161151
elseif property isa GraphPolynomial
@@ -178,7 +168,6 @@ function solve(gp::GraphProblem, property::AbstractProperty; T=Float64, usecuda=
178168
error("unknown property $property.")
179169
end
180170
end
181-
_has_weight(gp::GraphProblem) = hasfield(typeof(gp), :weights) && gp.weights isa Vector # ugly but makes life easier
182171

183172
"""
184173
max_size(problem; usecuda=false)

0 commit comments

Comments
 (0)