diff --git a/docs/src/ref.md b/docs/src/ref.md index 3247661c..4636edfc 100644 --- a/docs/src/ref.md +++ b/docs/src/ref.md @@ -28,6 +28,7 @@ terms flavors get_weights nflavor +fixedvertices ``` #### Graph Problem Utilities diff --git a/src/GenericTensorNetworks.jl b/src/GenericTensorNetworks.jl index 632beabd..503ba28c 100644 --- a/src/GenericTensorNetworks.jl +++ b/src/GenericTensorNetworks.jl @@ -30,7 +30,7 @@ export line_graph # Tensor Networks (Graph problems) export GraphProblem, optimize_code, NoWeight -export flavors, labels, terms, nflavor, get_weights +export flavors, labels, terms, nflavor, get_weights, fixedvertices export IndependentSet, mis_compactify!, is_independent_set export MaximalIS, is_maximal_independent_set export MaxCut, cut_size diff --git a/src/networks/Coloring.jl b/src/networks/Coloring.jl index a2cbe3b9..34b829c2 100644 --- a/src/networks/Coloring.jl +++ b/src/networks/Coloring.jl @@ -10,25 +10,29 @@ struct Coloring{K,CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphProble code::CT graph::SimpleGraph{Int} weights::WT + fixedvertices::Dict{Int,Int} end -Coloring{K}(code::ET, graph::SimpleGraph, weights::Union{NoWeight, Vector}) where {K,ET<:AbstractEinsum} = Coloring{K,ET,typeof(weights)}(code, graph, weights) +Coloring{K}(code::ET, graph::SimpleGraph, weights::Union{NoWeight, Vector}, fixedvertices::Dict{Int,Int}) where {K,ET<:AbstractEinsum} = Coloring{K,ET,typeof(weights)}(code, graph, weights, fixedvertices) # same network layout as independent set. -function Coloring{K}(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) where K +function Coloring{K}(g::SimpleGraph; weights=NoWeight(), openvertices=(), fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) where K @assert weights isa NoWeight || length(weights) == ne(g) rawcode = EinCode(([[i] for i in Graphs.vertices(g)]..., # labels for vertex tensors [[minmax(e.src,e.dst)...] for e in Graphs.edges(g)]...), collect(Int, openvertices)) # labels for edge tensors - code = _optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier) - Coloring{K}(code, g, weights) + code = _optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier) + Coloring{K}(code, g, weights, fixedvertices) end flavors(::Type{<:Coloring{K}}) where K = collect(0:K-1) get_weights(c::Coloring{K}, i::Int) where K = fill(c.weights[i], K) terms(gp::Coloring) = getixsv(gp.code)[1:nv(gp.graph)] labels(gp::Coloring) = [1:nv(gp.graph)...] +fixedvertices(gp::Coloring) = gp.fixedvertices function generate_tensors(x::T, c::Coloring{K}) where {K,T} ixs = getixsv(c.code) - return vcat(add_labels!([coloringv(T, K) for i=1:nv(c.graph)], ixs[1:nv(c.graph)], labels(c)), [coloringb(x, K) .^ get_weights(c, i) for i=1:ne(c.graph)]) + return select_dims(vcat( + add_labels!([coloringv(T, K) for i=1:nv(c.graph)], ixs[1:nv(c.graph)], labels(c)), [coloringb(x, K) .^ get_weights(c, i) for i=1:ne(c.graph)] + ), ixs, fixedvertices(c)) end # coloring bond tensor diff --git a/src/networks/DominatingSet.jl b/src/networks/DominatingSet.jl index 8dfb46cc..a7f0034c 100644 --- a/src/networks/DominatingSet.jl +++ b/src/networks/DominatingSet.jl @@ -11,25 +11,27 @@ struct DominatingSet{CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphPro code::CT graph::SimpleGraph{Int} weights::WT + fixedvertices::Dict{Int,Int} end -function DominatingSet(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function DominatingSet(g::SimpleGraph; weights=NoWeight(), openvertices=(), fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) @assert weights isa NoWeight || length(weights) == nv(g) rawcode = EinCode(([[Graphs.neighbors(g, v)..., v] for v in Graphs.vertices(g)]...,), collect(Int, openvertices)) - DominatingSet(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), g, weights) + DominatingSet(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), g, weights, fixedvertices) end flavors(::Type{<:DominatingSet}) = [0, 1] get_weights(gp::DominatingSet, i::Int) = [0, gp.weights[i]] terms(gp::DominatingSet) = getixsv(gp.code) labels(gp::DominatingSet) = [1:length(getixsv(gp.code))...] +fixedvertices(gp::DominatingSet) = gp.fixedvertices function generate_tensors(x::T, mi::DominatingSet) where T ixs = getixsv(mi.code) isempty(ixs) && return [] - return add_labels!(map(enumerate(ixs)) do (i, ix) + return select_dims(add_labels!(map(enumerate(ixs)) do (i, ix) dominating_set_tensor((Ref(x) .^ get_weights(mi, i))..., length(ix)) - end, ixs, labels(mi)) + end, ixs, labels(mi)), ixs, fixedvertices(mi)) end function dominating_set_tensor(a::T, b::T, d::Int) where T t = zeros(T, fill(2, d)...) diff --git a/src/networks/IndependentSet.jl b/src/networks/IndependentSet.jl index 1cb62cf8..0c76f341 100644 --- a/src/networks/IndependentSet.jl +++ b/src/networks/IndependentSet.jl @@ -12,29 +12,31 @@ struct IndependentSet{CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphPr code::CT graph::SimpleGraph{Int} weights::WT + fixedvertices::Dict{Int,Int} end -function IndependentSet(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function IndependentSet(g::SimpleGraph; weights=NoWeight(), openvertices=(), fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) @assert weights isa NoWeight || length(weights) == nv(g) rawcode = EinCode([[[i] for i in Graphs.vertices(g)]..., # labels for vertex tensors [[minmax(e.src,e.dst)...] for e in Graphs.edges(g)]...], collect(Int, openvertices)) # labels for edge tensors - code = _optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier) - IndependentSet(code, g, weights) + code = _optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier) + IndependentSet(code, g, weights, fixedvertices) end flavors(::Type{<:IndependentSet}) = [0, 1] get_weights(gp::IndependentSet, i::Int) = [0, gp.weights[i]] terms(gp::IndependentSet) = getixsv(gp.code)[1:nv(gp.graph)] labels(gp::IndependentSet) = [1:nv(gp.graph)...] +fixedvertices(gp::IndependentSet) = gp.fixedvertices # generate tensors function generate_tensors(x::T, gp::IndependentSet) where T nv(gp.graph) == 0 && return [] ixs = getixsv(gp.code) # we only add labels at vertex tensors - return vcat(add_labels!([misv(Ref(x) .^ get_weights(gp, i)) for i=1:nv(gp.graph)], ixs[1:nv(gp.graph)], labels(gp)), + return select_dims(vcat(add_labels!([misv(Ref(x) .^ get_weights(gp, i)) for i=1:nv(gp.graph)], ixs[1:nv(gp.graph)], labels(gp)), [misb(T, length(ix)) for ix in ixs[nv(gp.graph)+1:end]] # if n!=2, it corresponds to set packing problem. - ) + ), ixs, fixedvertices(gp)) end function misb(::Type{T}, n::Integer=2) where T diff --git a/src/networks/Matching.jl b/src/networks/Matching.jl index 2861ed49..052a0f16 100644 --- a/src/networks/Matching.jl +++ b/src/networks/Matching.jl @@ -10,21 +10,23 @@ struct Matching{CT<:AbstractEinsum, WT<:Union{NoWeight,Vector}} <: GraphProblem code::CT graph::SimpleGraph{Int} weights::WT + fixedvertices::Dict{Int,Int} end -function Matching(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function Matching(g::SimpleGraph; weights=NoWeight(), openvertices=(),fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) @assert weights isa NoWeight || length(weights) == ne(g) edges = [minmax(e.src,e.dst) for e in Graphs.edges(g)] rawcode = EinCode(vcat([[s] for s in edges], # labels for edge tensors [[minmax(i,j) for j in neighbors(g, i)] for i in Graphs.vertices(g)]), collect(Tuple{Int,Int}, openvertices)) - Matching(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), g, weights) + Matching(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), g, weights, fixedvertices) end flavors(::Type{<:Matching}) = [0, 1] get_weights(m::Matching, i::Int) = [0, m.weights[i]] terms(gp::Matching) = getixsv(gp.code)[1:ne(gp.graph)] labels(gp::Matching) = getindex.(terms(gp)) +fixedvertices(gp::Matching) = gp.fixedvertices function generate_tensors(x::T, m::Matching) where T ne(m.graph) == 0 && return [] @@ -40,7 +42,7 @@ function generate_tensors(x::T, m::Matching) where T end push!(tensors, t) end - return add_labels!(tensors, ixs, labels(m)) + return select_dims(add_labels!(tensors, ixs, labels(m)), ixs, fixedvertices(m)) end function match_tensor(::Type{T}, n::Int) where T diff --git a/src/networks/MaxCut.jl b/src/networks/MaxCut.jl index b41506e8..941a7215 100644 --- a/src/networks/MaxCut.jl +++ b/src/networks/MaxCut.jl @@ -11,24 +11,28 @@ struct MaxCut{CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphProblem code::CT graph::SimpleGraph{Int} weights::WT + fixedvertices::Dict{Int,Int} end -function MaxCut(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function MaxCut(g::SimpleGraph; weights=NoWeight(), openvertices=(), fixedvertices=Dict{Int,Int}(), optimizer=GreedyMethod(), simplifier=nothing) @assert weights isa NoWeight || length(weights) == ne(g) rawcode = EinCode([[minmax(e.src,e.dst)...] for e in Graphs.edges(g)], collect(Int, openvertices)) # labels for edge tensors - MaxCut(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), g, weights) + MaxCut(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), g, weights, fixedvertices) end flavors(::Type{<:MaxCut}) = [0, 1] get_weights(gp::MaxCut, i::Int) = [0, gp.weights[i]] terms(gp::MaxCut) = getixsv(gp.code) labels(gp::MaxCut) = [1:nv(gp.graph)...] +fixedvertices(gp::MaxCut) = gp.fixedvertices function generate_tensors(x::T, gp::MaxCut) where T ixs = getixsv(gp.code) - return add_labels!(map(enumerate(ixs)) do (i, ix) + tensors = map(enumerate(ixs)) do (i, ix) maxcutb((Ref(x) .^ get_weights(gp, i)) ...) - end, ixs, labels(gp)) + end + return select_dims(add_labels!(tensors, ixs, labels(gp)), ixs, fixedvertices(gp)) end + function maxcutb(a, b) return [a b; b a] end diff --git a/src/networks/MaximalIS.jl b/src/networks/MaximalIS.jl index 6cceb2d4..c5e55be4 100644 --- a/src/networks/MaximalIS.jl +++ b/src/networks/MaximalIS.jl @@ -10,25 +10,27 @@ struct MaximalIS{CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphProblem code::CT graph::SimpleGraph weights::WT + fixedvertices::Dict{Int,Int} end -function MaximalIS(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function MaximalIS(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing, fixedvertices=Dict{Int,Int}()) @assert weights isa NoWeight || length(weights) == nv(g) rawcode = EinCode(([[Graphs.neighbors(g, v)..., v] for v in Graphs.vertices(g)]...,), collect(Int, openvertices)) - MaximalIS(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), g, weights) + MaximalIS(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), g, weights, fixedvertices) end flavors(::Type{<:MaximalIS}) = [0, 1] get_weights(gp::MaximalIS, i::Int) = [0, gp.weights[i]] terms(gp::MaximalIS) = getixsv(gp.code) labels(gp::MaximalIS) = [1:length(getixsv(gp.code))...] +fixedvertices(gp::MaximalIS) = gp.fixedvertices function generate_tensors(x::T, mi::MaximalIS) where T ixs = getixsv(mi.code) isempty(ixs) && return [] - return add_labels!(map(enumerate(ixs)) do (i, ix) + return select_dims(add_labels!(map(enumerate(ixs)) do (i, ix) maximal_independent_set_tensor((Ref(x) .^ get_weights(mi, i))..., length(ix)) - end, ixs, labels(mi)) + end, ixs, labels(mi)), ixs, fixedvertices(mi)) end function maximal_independent_set_tensor(a::T, b::T, d::Int) where T t = zeros(T, fill(2, d)...) diff --git a/src/networks/PaintShop.jl b/src/networks/PaintShop.jl index d47f8ae0..49707693 100644 --- a/src/networks/PaintShop.jl +++ b/src/networks/PaintShop.jl @@ -31,18 +31,19 @@ struct PaintShop{CT<:AbstractEinsum,LT} <: GraphProblem code::CT sequence::Vector{LT} isfirst::Vector{Bool} + fixedvertices::Dict{LT,Int} end -function paintshop_from_pairs(pairs::AbstractVector{Tuple{Int,Int}}; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function paintshop_from_pairs(pairs::AbstractVector{Tuple{Int,Int}}; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing, fixedvertices=Dict{LT,Int}()) n = length(pairs) @assert sort!(vcat(collect.(pairs)...)) == collect(1:2n) sequence = zeros(Int, 2*n) @inbounds for i=1:n sequence[pairs[i]] .= i end - return PaintShop(pairs; openvertices, optimizer, simplifier) + return PaintShop(pairs; openvertices, optimizer, simplifier, fixedvertices) end -function PaintShop(sequence::AbstractVector{T}; openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) where T +function PaintShop(sequence::AbstractVector{T}; openvertices=(), fixedvertices=Dict{T,Int}(), optimizer=GreedyMethod(), simplifier=nothing) where T @assert all(l->count(==(l), sequence)==2, sequence) n = length(sequence) isfirst = [findfirst(==(sequence[i]), sequence) == i for i=1:n] @@ -50,18 +51,19 @@ function PaintShop(sequence::AbstractVector{T}; openvertices=(), optimizer=Greed [[sequence[i], sequence[i+1]] for i=1:n-1], # labels for edge tensors ), collect(T, openvertices)) - PaintShop(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), sequence, isfirst) + PaintShop(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), sequence, isfirst, fixedvertices) end flavors(::Type{<:PaintShop}) = [0, 1] get_weights(::PaintShop, i::Int) = [0, 1] terms(gp::PaintShop) = getixsv(gp.code) labels(gp::PaintShop) = unique(gp.sequence) +fixedvertices(gp::PaintShop) = gp.fixedvertices function generate_tensors(x::T, c::PaintShop) where T ixs = getixsv(c.code) tensors = [paintshop_bond_tensor((Ref(x) .^ get_weights(c, i))...) for i=1:length(ixs)] - return add_labels!([flip_labels(tensors[i], c.isfirst[i], c.isfirst[i+1]) for i=1:length(ixs)], ixs, labels(c)) + return select_dims(add_labels!([flip_labels(tensors[i], c.isfirst[i], c.isfirst[i+1]) for i=1:length(ixs)], ixs, labels(c)), ixs, fixedvertices(c)) end function paintshop_bond_tensor(a::T, b::T) where T diff --git a/src/networks/Satisfiability.jl b/src/networks/Satisfiability.jl index 1f6ebd02..92f9ba57 100644 --- a/src/networks/Satisfiability.jl +++ b/src/networks/Satisfiability.jl @@ -120,17 +120,19 @@ struct Satisfiability{CT<:AbstractEinsum,T,WT<:Union{NoWeight, Vector}} <: Graph code::CT cnf::CNF{T} weights::WT + fixedvertices::Dict{T,Int} end -function Satisfiability(cnf::CNF{T}; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) where T +function Satisfiability(cnf::CNF{T}; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing, fixedvertices=Dict{T,Int}()) where T rawcode = EinCode([[getfield.(c.vars, :name)...] for c in cnf.clauses], collect(T, openvertices)) - Satisfiability(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), cnf, weights) + Satisfiability(_optimize_code(rawcode, uniformsize_fix(rawcode, 2, fixedvertices), optimizer, simplifier), cnf, weights, fixedvertices) end flavors(::Type{<:Satisfiability}) = [0, 1] # false, true get_weights(s::Satisfiability, i::Int) = [0, s.weights[i]] terms(gp::Satisfiability) = getixsv(gp.code) labels(gp::Satisfiability) = unique!(vcat(getixsv(gp.code)...)) +fixedvertices(gp::Satisfiability) = gp.fixedvertices """ satisfiable(cnf::CNF, config::AbstractDict) @@ -152,9 +154,14 @@ function generate_tensors(x::VT, sa::Satisfiability{CT,T}) where {CT,T,VT} cnf = sa.cnf ixs = getixsv(sa.code) isempty(cnf.clauses) && return [] - return add_labels!(map(1:length(cnf.clauses)) do i - tensor_for_clause(cnf.clauses[i], (Ref(x) .^ get_weights(sa, i))...) - end, ixs, labels(sa)) + return select_dims( + add_labels!( + map(1:length(cnf.clauses)) do i + tensor_for_clause(cnf.clauses[i], (Ref(x) .^ get_weights(sa, i))...) + end, + ixs, labels(sa)) + , ixs, fixedvertices(sa) + ) end function tensor_for_clause(c::CNFClause{T}, a, b) where T diff --git a/src/networks/SetCovering.jl b/src/networks/SetCovering.jl index eeb613c3..02f72142 100644 --- a/src/networks/SetCovering.jl +++ b/src/networks/SetCovering.jl @@ -30,9 +30,10 @@ struct SetCovering{ET, CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphP code::CT sets::Vector{Vector{ET}} weights::WT + fixedvertices::Dict{ET,Int} end -function SetCovering(sets; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) where ET +function SetCovering(sets::AbstractVector{Vector{ET}}; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing, fixedvertices=Dict{ET,Int}()) where ET nsets = length(sets) @assert weights isa NoWeight || length(weights) == nsets # get constraints @@ -40,7 +41,7 @@ function SetCovering(sets; weights=NoWeight(), openvertices=(), optimizer=Greedy code = EinCode([[[i] for i=1:nsets]..., [count[e] for e in elements]...], collect(Int,openvertices)) - SetCovering(_optimize_code(code, uniformsize(code, 2), optimizer, simplifier), sets, weights) + SetCovering(_optimize_code(code, uniformsize_fix(code, 2, fixedvertices), optimizer, simplifier), sets, weights, fixedvertices) end function cover_count(sets) @@ -69,6 +70,7 @@ flavors(::Type{<:SetCovering}) = [0, 1] get_weights(gp::SetCovering, i::Int) = [0, gp.weights[i]] terms(gp::SetCovering) = getixsv(gp.code)[1:length(gp.sets)] labels(gp::SetCovering) = [1:length(gp.sets)...] +fixedvertices(gp::SetCovering) = gp.fixedvertices # generate tensors function generate_tensors(x::T, gp::SetCovering) where T @@ -76,8 +78,8 @@ function generate_tensors(x::T, gp::SetCovering) where T nsets == 0 && return [] ixs = getixsv(gp.code) # we only add labels at vertex tensors - return vcat(add_labels!([misv(Ref(x) .^ get_weights(gp, i)) for i=1:nsets], ixs[1:nsets], labels(gp)), - [cover_tensor(T, ix) for ix in ixs[nsets+1:end]] + return select_dims(vcat(add_labels!([misv(Ref(x) .^ get_weights(gp, i)) for i=1:nsets], ixs[1:nsets], labels(gp)), + [cover_tensor(T, ix) for ix in ixs[nsets+1:end]]), ixs, fixedvertices(gp) ) end diff --git a/src/networks/SetPacking.jl b/src/networks/SetPacking.jl index ba81ea1e..12bb47d5 100644 --- a/src/networks/SetPacking.jl +++ b/src/networks/SetPacking.jl @@ -31,27 +31,29 @@ struct SetPacking{ET, CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphPr code::CT sets::Vector{Vector{ET}} weights::WT + fixedvertices::Dict{ET,Int} end -function SetPacking(sets; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing) +function SetPacking(sets::AbstractVector{Vector{ET}}; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing, fixedvertices=Dict{ET,Int}()) where ET nsets = length(sets) @assert weights isa NoWeight || length(weights) == nsets code = EinCode(vcat([[i] for i=1:nsets], [[i,j] for i=1:nsets,j=1:nsets if j>i && !isempty(sets[i] ∩ sets[j])]), collect(Int,openvertices)) - SetPacking(_optimize_code(code, uniformsize(code, 2), optimizer, simplifier), sets, weights) + SetPacking(_optimize_code(code, uniformsize_fix(code, 2, openvertices), optimizer, simplifier), sets, weights, fixedvertices) end flavors(::Type{<:SetPacking}) = [0, 1] get_weights(gp::SetPacking, i::Int) = [0, gp.weights[i]] terms(gp::SetPacking) = getixsv(gp.code)[1:length(gp.sets)] labels(gp::SetPacking) = [1:length(gp.sets)...] +fixedvertices(gp::SetPacking) = gp.fixedvertices # generate tensors function generate_tensors(x::T, gp::SetPacking) where T length(gp.sets) == 0 && return [] ixs = getixsv(gp.code) # we only add labels at vertex tensors - return vcat(add_labels!([misv(Ref(x) .^ get_weights(gp, i)) for i=1:length(gp.sets)], ixs[1:length(gp.sets)], labels(gp)), - [misb(T, length(ix)) for ix in ixs[length(gp.sets)+1:end]] + return select_dims(vcat(add_labels!([misv(Ref(x) .^ get_weights(gp, i)) for i=1:length(gp.sets)], ixs[1:length(gp.sets)], labels(gp)), + [misb(T, length(ix)) for ix in ixs[length(gp.sets)+1:end]]), ixs, fixedvertices(gp), ) end diff --git a/src/networks/networks.jl b/src/networks/networks.jl index a3dc7c8d..40ac54dd 100644 --- a/src/networks/networks.jl +++ b/src/networks/networks.jl @@ -11,7 +11,7 @@ Base.eltype(::NoWeight) = Int ######## Interfaces for graph problems ########## """ - get_weights(problem::GraphProblem, sym) + get_weights(problem::GraphProblem, sym) -> Vector The weights for the degree of freedom specified by `sym` of the graph problem, where `sym` is a symbol. In graph polynomial, integer weights are the orders of `x`. @@ -19,7 +19,7 @@ In graph polynomial, integer weights are the orders of `x`. function get_weights end """ - labels(problem::GraphProblem) + labels(problem::GraphProblem) -> Vector The labels of a graph problem is defined as the degrees of freedoms in the graph problem. e.g. for the maximum independent set problems, they are the indices of vertices: 1, 2, 3..., @@ -28,7 +28,7 @@ while for the max cut problem, they are the edges. function labels end """ - terms(problem::GraphProblem) + terms(problem::GraphProblem) -> Vector The terms of a graph problem is defined as the tensor labels that defining local energies (or weights) in the graph problem. e.g. for the maximum independent set problems, they are the vertex-tensor labels: [1], [2], [3]... @@ -37,7 +37,15 @@ The weight of a term is same as the power of `x` in the graph polynomial. function terms end """ - flavors(::Type{<:GraphProblem}) + fixedvertices(problem::GraphProblem) -> Dict + +Fix degree of freedoms in a graph problem to a certain value using a dict, where the key is a label, and the value should be in, e.g. [0, 1] in the indepenent set problem. +When a degree of freedom is fixed, its size is 1. The optimal tensor network contraction order is then different from the default case. +""" +function fixedvertices end + +""" + flavors(::Type{<:GraphProblem}) -> Vector It returns a vector of integers as the flavors of a degree of freedom. Its size is the same as the degree of freedom on a single vertex/edge. @@ -45,7 +53,7 @@ Its size is the same as the degree of freedom on a single vertex/edge. flavors(::GT) where GT<:GraphProblem = flavors(GT) """ - nflavor(::Type{<:GraphProblem}) + nflavor(::Type{<:GraphProblem}) -> Int Bond size is equal to the number of flavors. """ @@ -125,6 +133,14 @@ function contractx(gp::GraphProblem, x; usecuda=false) end end +function uniformsize_fix(code, dim, fixedvertices) + size_dict = uniformsize(code, dim) + for key in keys(fixedvertices) + size_dict[key] = 1 + end + return size_dict +end + # multiply labels vectors to the generate tensor. add_labels!(tensors::AbstractVector{<:AbstractArray}, ixs, labels) = tensors @@ -133,6 +149,7 @@ function add_labels!(tensors::AbstractVector{<:AbstractArray{T}}, ixs, labels) w for (t, ix) in zip(tensors, ixs) for (dim, l) in enumerate(ix) index = findfirst(==(l), labels) + # config = l ∈ keys(fix_config) ? (fix_config[l]+1:fix_config[l]+1) : (1:size(t, dim)) v = [_onehotv(T, index, k-1) for k=1:size(t, dim)] t .*= reshape(v, ntuple(j->dim==j ? length(v) : 1, ndims(t))) end @@ -140,6 +157,18 @@ function add_labels!(tensors::AbstractVector{<:AbstractArray{T}}, ixs, labels) w return tensors end +# select dimensions from tensors +# `tensors` is a vector of tensors, +# `ixs` is the tensor labels for `tensors`. +# `fixedvertices` is a dictionary specifying the fixed dimensions, check [`fixedvertices`](@ref) +function select_dims(tensors::AbstractVector{<:AbstractArray{T}}, ixs, fixedvertices::AbstractDict) where T + isempty(fixedvertices) && return tensors + map(tensors, ixs) do t, ix + dims = map(ixi->ixi ∉ keys(fixedvertices) ? Colon() : (fixedvertices[ixi]+1:fixedvertices[ixi]+1), ix) + t[dims...] + end +end + # TODOs: # 1. Dominating set # \exists x_i,\ldots,x_K \forall y\left[\bigwedge_{i=1}^{K}(y=x_i\wedge \textbf{adj}(y, x_i))\right] diff --git a/test/configurations.jl b/test/configurations.jl index 7ebe144e..91e9a78e 100644 --- a/test/configurations.jl +++ b/test/configurations.jl @@ -32,7 +32,7 @@ end @testset "enumerating" begin rawcode = IndependentSet(smallgraph(:petersen); optimizer=nothing) - optcode = IndependentSet(optimize_code(rawcode.code, uniformsize(rawcode.code, 2), GreedyMethod()), smallgraph(:petersen), NoWeight()) + optcode = IndependentSet(optimize_code(rawcode.code, uniformsize(rawcode.code, 2), GreedyMethod()), smallgraph(:petersen), NoWeight(), Dict{Int,Int}()) for code in [rawcode, optcode] res0 = max_size(code) _, res1 = max_size_count(code) diff --git a/test/networks/MaxCut.jl b/test/networks/MaxCut.jl index 83863c07..7c8c2a36 100644 --- a/test/networks/MaxCut.jl +++ b/test/networks/MaxCut.jl @@ -40,3 +40,14 @@ end @test cut_size(g, res.c.data[1]) == 5 end +@testset "fix vertices - max vut" begin + g = SimpleGraph(5) + for (i,j) in [(1,2),(2,3),(3,4),(4,1),(1,5),(2,4)] + add_edge!(g, i, j) + end + fixedvertices = Dict(1=>1, 4=>0) + problem = MaxCut(g, fixedvertices=fixedvertices) + optimal_config = solve(problem, ConfigsMax())[].c + @test length(optimal_config) == 1 + @test optimal_config[1] == StaticBitVector(Array{Bool, 1}([1, 0, 1, 0, 0])) +end diff --git a/test/networks/networks.jl b/test/networks/networks.jl index bea48383..a07ab0d3 100644 --- a/test/networks/networks.jl +++ b/test/networks/networks.jl @@ -1,3 +1,18 @@ +using GenericTensorNetworks: select_dims +using Test + +@testset "select dims" begin + a, b, c = randn(2), randn(2, 2), randn(2,2,2) + a_, b_, c_ = select_dims([a, b, c], [[1], [1,2], [1,2,3]], Dict(1=>1, 3=>0)) + @test a_ == a[2:2] + @test b_ == b[2:2, :] + @test c_ == c[2:2, :, 1:1] + a, b = randn(3), randn(3,3,3) + a_, b_ = select_dims([a, b], [[1], [2,3,1]], Dict(1=>1, 3=>2)) + @test a_ == a[2:2] + @test b_ == b[:,3:3,2:2] +end + include("IndependentSet.jl") include("MaximalIS.jl") include("MaxCut.jl")