diff --git a/src/ProblemReductions.jl b/src/ProblemReductions.jl index 0ee2489..65c8bf4 100644 --- a/src/ProblemReductions.jl +++ b/src/ProblemReductions.jl @@ -32,6 +32,7 @@ export MaximalIS export PaintShop export BinaryMatrixFactorization, is_binary_matrix_factorization export BicliqueCover, is_biclique_cover +export CliqueCover,is_clique_cover # rules export target_problem, AbstractProblem, ConstraintSatisfactionProblem, solution_size, solution_size_multiple, SolutionSize, objectives, constraints, energy_mode diff --git a/src/models/CliqueCover.jl b/src/models/CliqueCover.jl new file mode 100644 index 0000000..adda127 --- /dev/null +++ b/src/models/CliqueCover.jl @@ -0,0 +1,60 @@ +""" +$TYPEDEF + CliqueCover{K}(graph::SimpleGraph{Int64}, k::Int64) + +A clique cover of a graph G is a set of cliques such that all vertices in G is coverd by the vertices union of these cliques. A K clique cover is to find whether we could use only k cliques to cover all the vertices in the graph. + +""" +struct CliqueCover{Int64} <: ConstraintSatisfactionProblem{Int64} + graph::SimpleGraph{Int64} + k::Int64 + function CliqueCover(graph::SimpleGraph{Int64}, k::Int64) + new{Int64}(graph, k) + end +end +problem_size(c::CliqueCover) = (; num_vertices=nv(c.graph), num_edges=ne(c.graph), k=c.k) +num_variables(c::CliqueCover) = nv(c.graph) * c.k +num_flavors(::Type{<:CliqueCover}) = 2 + +# constraints interface +function constraints(c::CliqueCover) + return [LocalConstraint(num_flavors(c), v, [0,1]) for v in vertices(c.graph)] +end +function objectives(c::CliqueCover) + return [LocalSolutionSize(num_flavors(c), [v], [0, 1]) for v in vertices(c.graph)] +end +energy_mode(::Type{<:CliqueCover}) = SmallerSizeIsBetter() + +function is_clique_cover(configs::Vector{Vector{Int64}}, c::CliqueCover) + # check if the number of cliques is equal to k + if length(configs) != c.k + print(1) + return false + end + # check if they are all valid clique + if any(config -> !is_clique(c, config), configs) + print(2) + return false + end + vertices_covered = reduce(vcat, [findall(x-> x==1,config) for config in configs]) + # check if the vertices are covered by the cliques + if length(vertices_covered) != nv(c.graph) + print(3) + return false + end + return true +end +function is_clique(c::CliqueCover,config::Vector{Int64}) + vertices = findall(x -> x == 1, config) + for (v1, v2) in collect(Iterators.product(vertices, vertices)) + # pass if the vertices are the same + if v1 == v2 + continue + end + # check if the edge exists in the graph, if not, return false + if !has_edge(c.graph, v1, v2) + return false + end + end + return true +end \ No newline at end of file diff --git a/src/models/models.jl b/src/models/models.jl index da0dcd9..caf5a66 100644 --- a/src/models/models.jl +++ b/src/models/models.jl @@ -376,3 +376,4 @@ include("MaximalIS.jl") include("Paintshop.jl") include("BicliqueCover.jl") include("BMF.jl") +include("CliqueCover.jl") diff --git a/test/models/CliqueCover.jl b/test/models/CliqueCover.jl new file mode 100644 index 0000000..9204b80 --- /dev/null +++ b/test/models/CliqueCover.jl @@ -0,0 +1,33 @@ +using Test, ProblemReductions, Graphs + +@testset "CliqueCover" begin + g = SimpleGraph(5) + for (i,j) in [(1,3),(1,5),(2,3),(2,4),(3,5)] + add_edge!(g,i,j) + end + c = CliqueCover(g,2) + @test num_variables(c) == 5 * 2 + @test num_flavors(c) == 2 + @test problem_size(c) == (; num_vertices=5, num_edges=5, k=2) + @test Base.:(==)(c, CliqueCover(g,2)) + @test energy_mode(c) == SmallerSizeIsBetter() + @test is_clique_cover([[1,0,1,0,1],[0,1,0,1,0]],c) == true + @test is_clique_cover([[1,0,1,0,0],[0,1,0,1,0],[0,0,0,0,0]],c) == false + @test is_clique_cover([[1,0,1,0,0],[0,1,0,1,1]],c) == false + @test ProblemReductions.is_clique(c,[1,0,1,0,0]) == true + @test ProblemReductions.is_clique(c,[0,1,0,1,1]) == false + @test ProblemReductions.is_clique(c,[1,0,1,0,1]) == true + + g1 = SimpleGraph(6) + for (i,j) in [(1,3),(1,5),(1,6),(2,3),(2,4),(3,4),(3,5),(4,6),(5,6)] + add_edge!(g1,i,j) + end + c1 = CliqueCover(g1,2) + @test is_clique_cover([[1,0,0,0,1,1],[0,1,1,1,0,0]],c1) == true + @test is_clique_cover([[1,0,1,0,1,0],[0,1,0,1,0,0]],c1) == false + @test is_clique_cover([[1,0,1,0,1,0],[0,1,0,1,0,0],[0,0,0,0,0,0]],c1) == false + @test ProblemReductions.is_clique(c1,[1,0,1,0,1,0]) == true + c2 = CliqueCover(g1,3) + @test is_clique_cover([[1,0,1,0,1,0],[0,1,0,1,0,0],[0,0,0,0,0,1]],c2) == true + @test is_clique_cover([[1,0,1,0,1,0],[0,1,0,1,0,0],[0,0,0,0,0,0]],c2) == false +end \ No newline at end of file diff --git a/test/models/models.jl b/test/models/models.jl index 13bb99f..f17a516 100644 --- a/test/models/models.jl +++ b/test/models/models.jl @@ -74,3 +74,6 @@ end include("BicliqueCover.jl") end +@testset "CliqueCover" begin + include("CliqueCover.jl") +end \ No newline at end of file