Skip to content

Commit 83c25b4

Browse files
authored
Dominating sets (#21)
* new dominating sets * update
1 parent 24d6de4 commit 83c25b4

File tree

11 files changed

+179
-9
lines changed

11 files changed

+179
-9
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ makedocs(;
3636
"Matching problem" => "tutorials/Matching.md",
3737
"Binary paint shop problem" => "tutorials/PaintShop.md",
3838
"Coloring problem" => "tutorials/Coloring.md",
39+
"Dominating set problem" => "tutorials/DominatingSet.md",
3940
"Satisfiability problem" => "tutorials/Satisfiability.md",
4041
"Other problems" => "tutorials/Others.md",
4142
],

docs/src/ref.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ IndependentSet
77
MaximalIS
88
Matching
99
Coloring
10+
DominatingSet
1011
MaxCut
1112
PaintShop
1213
Satisfiability
@@ -37,6 +38,8 @@ mis_compactify!
3738
3839
is_maximal_independent_set
3940
41+
is_dominating_set
42+
4043
cut_size
4144
4245
num_paint_shop_color_switch

examples/DominatingSet.jl

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# # Dominating set problem
2+
3+
# !!! note
4+
# It is recommended to read the [Independent set problem](@ref) tutorial first to know more about
5+
# * how to optimize the tensor network contraction order,
6+
# * what graph properties are available and how to select correct method to compute graph properties,
7+
# * how to compute weighted graphs and handle open vertices.
8+
9+
# ## Problem definition
10+
11+
# In graph theory, a [dominating set](https://en.wikipedia.org/wiki/Dominating_set) for a graph ``G = (V, E)`` is a subset ``D`` of ``V`` such that every vertex not in ``D`` is adjacent to at least one member of ``D``.
12+
# The domination number ``\gamma(G)`` is the number of vertices in a smallest dominating set for ``G``.
13+
# The decision version of finding the minimum dominating set is an NP-complete.
14+
# In the following, we are going to solve the dominating set problem on the Petersen graph.
15+
16+
using GraphTensorNetworks, Graphs, Compose
17+
18+
graph = Graphs.smallgraph(:petersen)
19+
20+
# We can visualize this graph using the following function
21+
rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a
22+
23+
locations = [[rot15(0.0, 1.0, i) for i=0:4]..., [rot15(0.0, 0.6, i) for i=0:4]...]
24+
25+
show_graph(graph; locs=locations)
26+
27+
# ## Tensor network representation
28+
# Let ``G=(V,E)`` be the target graph that we want to solve.
29+
# The tensor network representation map a vertex ``v\in V`` to a boolean degree of freedom ``s_v\in\{0, 1\}``.
30+
# We defined the restriction on a vertex and its neighbouring vertices ``N(v)``:
31+
# ```math
32+
# T(x_v)_{s_1,s_2,\ldots,s_{|N(v)|},s_v} = \begin{cases}
33+
# 0 & s_1=s_2=\ldots=s_{|N(v)|}=s_v=0,\\
34+
# 1 & s_v=0,\\
35+
# x_v^{w_v} & \text{otherwise},
36+
# \end{cases}
37+
# ```
38+
# where ``w_v`` is the weight of vertex ``v``.
39+
# This tensor means if both ``v`` and its neighbouring vertices are not in ``D``, i.e., ``s_1=s_2=\ldots=s_{|N(v)|}=s_v=0``,
40+
# this configuration is forbidden because ``v`` is not adjacent to any member in the set.
41+
# otherwise, if ``v`` is in ``D``, it has a contribution ``x_v^{w_v}`` to the final result.
42+
# We can use [`DominatingSet`](@ref) to construct the tensor network for solving the dominating set problem as
43+
problem = DominatingSet(graph; optimizer=TreeSA());
44+
45+
# One can check the contraction time space complexity of a [`DominatingSet`](@ref) instance by typing:
46+
47+
timespacereadwrite_complexity(problem)
48+
49+
# Results are `log2` values of time (number of iterations),
50+
# space (number of items in the largest tensor)
51+
# and read-write (number of read-write of operations to elements).
52+
53+
# ## Solving properties
54+
55+
# ### Counting properties
56+
# ##### Domination polynomial
57+
# The graph polynomial for the dominating set problem is known as the domination polynomial (see [arXiv:0905.2251](https://arxiv.org/abs/0905.2251)).
58+
# It is defined as
59+
# ```math
60+
# D(G, x) = \sum_{k=0}^{\gamma(G)} d_k x^k,
61+
# ```
62+
# where ``d_k`` is the number of dominating sets of size ``k`` in graph ``G=(V, E)``.
63+
64+
domination_polynomial = solve(problem, GraphPolynomial())[]
65+
66+
# The domination number ``\gamma(G)`` can be computed with the [`SizeMin`](@ref) property:
67+
domination_number = solve(problem, SizeMin())[]
68+
69+
# Similarly, we have its counting [`CountingMin`](@ref):
70+
counting_min_dominating_set = solve(problem, CountingMin())[]
71+
72+
# ### Configuration properties
73+
# ##### finding all dominating set
74+
# One can enumerate all minimum dominating sets with the [`ConfigsMin`](@ref) property in the program.
75+
min_configs = solve(problem, ConfigsMin())[].c
76+
77+
all(c->is_dominating_set(graph, c), min_configs)
78+
79+
# ##### finding minimum dominating set
80+
81+
imgs = ntuple(k->show_graph(graph;
82+
locs=locations, scale=0.25,
83+
vertex_colors=[iszero(min_configs[k][i]) ? "white" : "red"
84+
for i=1:nv(graph)]), length(min_configs));
85+
86+
Compose.set_default_graphic_size(18cm, 8cm); Compose.compose(context(),
87+
ntuple(k->(context((mod1(k,5)-1)/5, ((k-1)÷5)/2, 1.2/5, 1.0/2), imgs[k]), 10)...)
88+
89+
# Similarly, if one is only interested in computing one of the minimum dominating sets,
90+
# one can use the graph property [`SingleConfigMin`](@ref).

examples/MaximalIS.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ show_graph(graph; locs=locations)
2626
# ## Tensor network representation
2727
# Let ``G=(V,E)`` be the target graph that we want to solve.
2828
# The tensor network representation map a vertex ``v\in V`` to a boolean degree of freedom ``s_v\in\{0, 1\}``.
29-
# We defined the restriction on its neighbourhood ``N[v]``:
29+
# We defined the restriction on its neighbourhood ``N(v)``:
3030
# ```math
3131
# T(x_v)_{s_1,s_2,\ldots,s_{|N(v)|},s_v} = \begin{cases}
3232
# s_vx_v & s_1=s_2=\ldots=s_{|N(v)|}=0,\\

examples/PaintShop.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ max_config = solve(problem, GraphPolynomial())[]
7979
# ##### graph polynomial
8080
# The graph polynomial of the binary paint shop problem in our convension is defined as
8181
# ```math
82-
# D(G, x) = \sum_{k=0}^{\delta(G)} d_k x^k
82+
# P(G, x) = \sum_{k=0}^{\delta(G)} p_k x^k
8383
# ```
84-
# where ``d_k`` is the number of possible coloring with number of color changes ``2m-1-k``.
84+
# where ``p_k`` is the number of possible coloring with number of color changes ``2m-1-k``.
8585
paint_polynomial = solve(problem, GraphPolynomial())[]
8686

8787
# ### Configuration properties

src/GraphTensorNetworks.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ export line_graph
3030

3131
# Tensor Networks (Graph problems)
3232
export GraphProblem, IndependentSet, MaximalIS, Matching,
33-
Coloring, optimize_code, set_packing, MaxCut, PaintShop,
33+
Coloring, optimize_code, set_packing, MaxCut, PaintShop, DominatingSet,
3434
paintshop_from_pairs, NoWeight, Satisfiability
3535
export flavors, labels, terms, nflavor, get_weights
3636
export mis_compactify!, cut_size, num_paint_shop_color_switch, paint_shop_coloring_from_config
3737
export is_good_vertex_coloring
3838
export CNF, CNFClause, BoolVar, satisfiable, @bools, , ¬,
39+
export is_dominating_set
3940

4041
# Interfaces
4142
export solve, SizeMax, SizeMin, CountingAll, CountingMax, CountingMin, GraphPolynomial, SingleConfigMax, SingleConfigMin, ConfigsAll, ConfigsMax, ConfigsMin

src/networks/DominatingSet.jl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""
2+
DominatingSet{CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphProblem
3+
DominatingSet(graph; weights=NoWeight(), openvertices=(),
4+
optimizer=GreedyMethod(), simplifier=nothing)
5+
6+
The [dominating set](https://psychic-meme-f4d866f8.pages.github.io/dev/tutorials/DominatingSet.html) problem.
7+
In the constructor, `weights` are the weights of vertices.
8+
`optimizer` and `simplifier` are for tensor network optimization, check [`optimize_code`](@ref) for details.
9+
"""
10+
struct DominatingSet{CT<:AbstractEinsum,WT<:Union{NoWeight, Vector}} <: GraphProblem
11+
code::CT
12+
weights::WT
13+
end
14+
15+
function DominatingSet(g::SimpleGraph; weights=NoWeight(), openvertices=(), optimizer=GreedyMethod(), simplifier=nothing)
16+
@assert weights isa NoWeight || length(weights) == nv(g)
17+
rawcode = EinCode(([[Graphs.neighbors(g, v)..., v] for v in Graphs.vertices(g)]...,), collect(Int, openvertices))
18+
DominatingSet(_optimize_code(rawcode, uniformsize(rawcode, 2), optimizer, simplifier), weights)
19+
end
20+
21+
flavors(::Type{<:DominatingSet}) = [0, 1]
22+
get_weights(gp::DominatingSet, i::Int) = [0, gp.weights[i]]
23+
terms(gp::DominatingSet) = getixsv(gp.code)
24+
labels(gp::DominatingSet) = [1:length(getixsv(gp.code))...]
25+
26+
function generate_tensors(x::T, mi::DominatingSet) where T
27+
ixs = getixsv(mi.code)
28+
isempty(ixs) && return []
29+
return add_labels!(map(enumerate(ixs)) do (i, ix)
30+
dominating_set_tensor((Ref(x) .^ get_weights(mi, i))..., length(ix))
31+
end, ixs, labels(mi))
32+
end
33+
function dominating_set_tensor(a::T, b::T, d::Int) where T
34+
t = zeros(T, fill(2, d)...)
35+
for i = 2:1<<(d-1)
36+
t[i] = a
37+
end
38+
t[1<<(d-1)+1:end] .= Ref(b)
39+
return t
40+
end
41+
42+
"""
43+
is_dominating_set(g::SimpleGraph, config)
44+
45+
Return true if `config` (a vector of boolean numbers as the mask of vertices) is a dominating set of graph `g`.
46+
"""
47+
is_dominating_set(g::SimpleGraph, config) = all(w->config[w] == 1 || any(v->!iszero(config[v]), neighbors(g, w)), Graphs.vertices(g))

src/networks/MaximalIS.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@ function generate_tensors(x::T, mi::MaximalIS) where T
2626
ixs = getixsv(mi.code)
2727
isempty(ixs) && return []
2828
return add_labels!(map(enumerate(ixs)) do (i, ix)
29-
neighbortensor((Ref(x) .^ get_weights(mi, i))..., length(ix))
29+
maximal_independent_set_tensor((Ref(x) .^ get_weights(mi, i))..., length(ix))
3030
end, ixs, labels(mi))
3131
end
32-
function neighbortensor(a::T, b::T, d::Int) where T
32+
function maximal_independent_set_tensor(a::T, b::T, d::Int) where T
3333
t = zeros(T, fill(2, d)...)
3434
for i = 2:1<<(d-1)
35-
t[i] = one(T)
35+
t[i] = a
3636
end
37-
t[1<<(d-1)+1] = a
3837
t[1<<(d-1)+1] = b
3938
return t
4039
end

src/networks/networks.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ include("Matching.jl")
106106
include("Coloring.jl")
107107
include("PaintShop.jl")
108108
include("Satisfiability.jl")
109+
include("DominatingSet.jl")
109110

110111
# forward the time, space and readwrite complexity
111112
OMEinsum.timespacereadwrite_complexity(gp::GraphProblem) = timespacereadwrite_complexity(gp.code, uniformsize(gp.code, nflavor(gp)))

test/networks/DominatingSet.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using GraphTensorNetworks, Test, Graphs
2+
3+
@testset "dominating set basic" begin
4+
g = smallgraph(:petersen)
5+
mask = trues(10)
6+
mask[neighbors(g, 1)] .= false
7+
@test is_dominating_set(g, mask)
8+
mask[1] = false
9+
@test !is_dominating_set(g, mask)
10+
11+
@test GraphTensorNetworks.dominating_set_tensor(TropicalF64(0), TropicalF64(1), 3)[:,:,1] == TropicalF64[-Inf 0.0; 0 0]
12+
@test GraphTensorNetworks.dominating_set_tensor(TropicalF64(0), TropicalF64(1), 3)[:,:,2] == TropicalF64[1.0 1.0; 1.0 1.0]
13+
end
14+
15+
@testset "dominating set v.s. maximal IS" begin
16+
g = smallgraph(:petersen)
17+
gp1 = DominatingSet(g)
18+
@test solve(gp1, SizeMax())[].n == 10
19+
res1 = solve(gp1, ConfigsMin())[].c
20+
gp2 = MaximalIS(g)
21+
res2 = solve(gp2, ConfigsMin())[].c
22+
@test res1 == res2
23+
configs = solve(gp2, ConfigsAll())[]
24+
for config in configs
25+
@test is_dominating_set(g, config)
26+
end
27+
end

0 commit comments

Comments
 (0)