Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Reduction: SAT to Dominating Set #84

Merged
merged 6 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ProblemReductions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export ReductionCircuitToSpinGlass, ReductionMaxCutToSpinGlass, ReductionSpinGla
ReductionSpinGlassToQUBO, ReductionQUBOToSpinGlass
export findbest, BruteForce
export CNF
export Reduction3SATToIndependentSet
export ReductionSATToIndependentSet, ReductionSATToDominatingSet

# reduction path
export ReductionGraph, reduction_graph, reduction_paths, implement_reduction_path
Expand Down
1 change: 1 addition & 0 deletions src/rules/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ include("sat_coloring.jl")
include("vertexcovering_setcovering.jl")
include("factoring_sat.jl")
include("sat_independentset.jl")
include("sat_dominatingset.jl")
90 changes: 90 additions & 0 deletions src/rules/sat_dominatingset.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""
$TYPEDEF

The reduction result of a general SAT problem to an Dominating Set problem.

### Fields
$TYPEDFIELDS
"""
struct ReductionSATToDominatingSet{GT<:AbstractGraph} <: AbstractReductionResult
target::DominatingSet{GT} # the target problem
num_literals::Int # number of literals in the SAT problem
num_clauses::Int # number of clauses in the SAT problem
end
target_problem(res::ReductionSATToDominatingSet) = res.target

@with_complexity 1 function reduceto(::Type{<:DominatingSet}, s::AbstractSatisfiabilityProblem)
num_clauses = length(s.cnf.clauses)
num_vertices = 3 * num_variables(s) + num_clauses
g = SimpleGraph(num_vertices)
for i in 1:num_variables(s)
for m in 3*(i-1)+1:3*i
for n in m:3*i
add_edge!(g, m, n)
end
end
end
for (i, clause_tmp) in enumerate(s.cnf.clauses)
for literal in clause_tmp.vars
literal_node = 3 * (findfirst(==(literal.name), variables(s))-1) + (literal.neg ? 2 : 1)
add_edge!(g, literal_node, 3 * num_variables(s)+i)
end
end
return ReductionSATToDominatingSet(DominatingSet(g), num_variables(s), num_clauses)
end

function extract_solution(res::ReductionSATToDominatingSet, sol)
if count(value -> value == 1, sol) > res.num_literals
return nothing
end
assignment = fill(0, res.num_literals)
for (i, value) in enumerate(sol)
if value == 1
if rem(i, 3) == 1
assignment[div(i, 3)+1] = 1
elseif rem(i, 3) == 2
assignment[div(i, 3)+1] = 0
elseif rem(i, 3) == 0
assignment[div(i, 3)] = rand([0,1])
end
end
end
return assignment
end

function extract_multiple_solutions(res::ReductionSATToDominatingSet, sol)
if count(value -> value == 1, sol[1]) > res.num_literals
return []
end
all_assignments = Vector{Vector{Int}}()
for sol_tmp in sol
assignment = fill(0, res.num_literals)
dummy_vars = Vector{Int}()
for (i, value) in enumerate(sol_tmp)
if value == 1
if rem(i, 3) == 1
assignment[div(i, 3)+1] = 1
elseif rem(i, 3) == 2
assignment[div(i, 3)+1] = 0
elseif rem(i, 3) == 0
push!(dummy_vars, div(i, 3))
end
end
end
if length(dummy_vars) > 0
for each_case in 0:( 2^( length(dummy_vars) ) - 1 )
copied_assignment = copy(assignment)
case_number = each_case
for var in dummy_vars
copied_assignment[var] = rem(case_number, 2)
case_number = div(case_number, 2)
end
push!(all_assignments, copied_assignment)
end
else
push!(all_assignments, assignment)
end
end
return unique(all_assignments)
end

7 changes: 6 additions & 1 deletion test/rules/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ end
include("sat_independentset.jl")
end

@testset "sat_dominatingset" begin
include("sat_dominatingset.jl")
end

@testset "rules" begin
circuit = CircuitSAT(@circuit begin
x = a ∨ ¬b
Expand Down Expand Up @@ -58,7 +62,8 @@ end
spinglass2 => QUBO,
sat => KSatisfiability,
ksat => Satisfiability,
sat => IndependentSet
sat => IndependentSet,
sat => DominatingSet
]
@info "Testing reduction from $(typeof(source)) to $(target_type)"
# directly solve
Expand Down
72 changes: 72 additions & 0 deletions test/rules/sat_dominatingset.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using Test, ProblemReductions, Graphs

@testset "sat_dominatingset" begin
function verify(sat)
reduction_results = reduceto(DominatingSet, sat)
DS_tmp = reduction_results |> target_problem
sol_DS = findbest(DS_tmp, BruteForce())
s1 = Set(findbest(sat, BruteForce()))
s2 = Set( unique( extract_solution.(Ref(reduction_results), sol_DS) ) )
s3 = Set(extract_multiple_solutions(reduction_results, sol_DS))
return (s2 ⊆ s1) && (s3 == s1)
end

function verify_unsatisfiable(sat)
reduction_results = reduceto(DominatingSet, sat)
DS_tmp = reduction_results |> target_problem
sol_DS = findbest(DS_tmp, BruteForce())
s2 = Set( unique( extract_solution.(Ref(reduction_results), sol_DS) ) )
s3 = Set(extract_multiple_solutions(reduction_results, sol_DS))
return (s2 == Set([nothing])) && (s3 == Set([]))
end

# Example 001: satisfiable 3-SAT
x1 = BoolVar(:x1, false)
nx1 = BoolVar(:x1, true)
x2 = BoolVar(:x2, false)
nx2 = BoolVar(:x2, true)
x3 = BoolVar(:x3, false)
nx3 = BoolVar(:x3, true)

clause1 = CNFClause([x1, nx2, x3])
clause2 = CNFClause([nx1, x2, nx3])
clause3 = CNFClause([x1, nx2, nx3])
clause4 = CNFClause([nx1, x2, x3])

clause_lst = [clause1, clause2, clause3, clause4]
sat01 = Satisfiability(CNF(clause_lst))

@test reduction_complexity(DominatingSet, sat01) == 1
@test verify(sat01)

# Example 002: satisfiable 3-SAT
clause5 = CNFClause([nx1, x2, x3])
clause6 = CNFClause([x1, nx2, x3])
clause7 = CNFClause([x1, x2, nx3])
sat02 = Satisfiability(CNF([clause5, clause6, clause7]))
@test verify(sat02)

# Example 003: satisfiable 3-SAT
clause8 = CNFClause([x1, x2, x3])
clause9 = CNFClause([nx1, nx2, nx3])
sat03 = Satisfiability(CNF([clause8, clause9]))
@test verify(sat03)

# Example 004: unsatisfiable 3-SAT (trivial example)
clause10 = CNFClause([x1, x1, x1])
clause11 = CNFClause([nx1, nx1, nx1])
sat04 = Satisfiability(CNF([clause10, clause11]))
@test verify_unsatisfiable(sat04)

# Example 005: unsatisfiable 1-SAT (equivalent with example 004)
sat05 = Satisfiability(CNF([CNFClause([x1]), CNFClause([nx1])]))
@test verify_unsatisfiable(sat05)

# Example 006: unsatisfiable 2-SAT
sat06 = Satisfiability(CNF([CNFClause([x1, x2]), CNFClause([x1, nx2]), CNFClause([nx1, x2]), CNFClause([nx1, nx2])]))
@test verify_unsatisfiable(sat06)

# Example 007: satisfiable 2-SAT
sat07 = Satisfiability(CNF([CNFClause([x1, x2]), CNFClause([x1, nx2]), CNFClause([nx1, x2])]))
@test verify(sat07)
end
Loading