diff --git a/src/ProblemReductions.jl b/src/ProblemReductions.jl index 0ee2489..9344b2b 100644 --- a/src/ProblemReductions.jl +++ b/src/ProblemReductions.jl @@ -30,8 +30,8 @@ export Factoring, is_factoring export Matching, is_matching export MaximalIS export PaintShop -export BinaryMatrixFactorization, is_binary_matrix_factorization -export BicliqueCover, is_biclique_cover +export BinaryMatrixFactorization, is_binary_matrix_factorization, read_solution +export BicliqueCover, is_biclique_cover, read_solution, biclique_cover_from_matrix # rules export target_problem, AbstractProblem, ConstraintSatisfactionProblem, solution_size, solution_size_multiple, SolutionSize, objectives, constraints, energy_mode @@ -48,6 +48,7 @@ export ReductionIndependentSetToSetPacking, ReductionSetPackingToIndependentSet export ReductionSATToCircuit export ReductionIndependentSetToVertexCovering export ReductionMatchingToSetPacking +export ReductionBMFToBicliqueCover, ReductionBicliqueCoverToBMF # reduction path export ReductionGraph, reduction_graph, reduction_paths, ConcatenatedReduction diff --git a/src/models/BMF.jl b/src/models/BMF.jl index c17fa53..7b28ccd 100644 --- a/src/models/BMF.jl +++ b/src/models/BMF.jl @@ -18,7 +18,7 @@ The Boolean Matrix Factorization (BMF) problem is defined on a binary matrix A i struct BinaryMatrixFactorization<: AbstractProblem A::BitMatrix k::Int - function BinaryMatrixFactorization(A::BitMatrix, k::Int) where K + function BinaryMatrixFactorization(A::BitMatrix, k::Int) new(A, k) end end @@ -49,5 +49,27 @@ function is_binary_matrix_factorization(bmf::BinaryMatrixFactorization, b::BitMa return solution_size(bmf,b,c) == 0 end +""" + + read_solution(bmf::BinaryMatrixFactorization, solution::Vector{BitMatrix}) + Read the solution of the BinaryMatrixFactorization problem from the solution of the BMF problem, return a vector of cliques +""" +function read_solution(bmf::BinaryMatrixFactorization, a::BitMatrix, b::BitMatrix) + @assert size(a,2) == size(b,1) == bmf.k "Dimension mismatch" + cliques = [zeros(Int64,size(a,1)+size(b,2)) for i in range(1,bmf.k)] + for i in range(1,bmf.k) + for j in range(1,size(a,1)) + if a[j,i] + cliques[i][j] = 1 + end + end + for j in range(1,size(b,2)) + if b[i,j] + cliques[i][j+size(a,1)] = 1 + end + end + end + return cliques +end diff --git a/src/models/BicliqueCover.jl b/src/models/BicliqueCover.jl index edc1c38..77d7418 100644 --- a/src/models/BicliqueCover.jl +++ b/src/models/BicliqueCover.jl @@ -35,7 +35,7 @@ problem_size(c::BicliqueCover) = (; num_vertices=nv(c.graph), num_edges=ne(c.gra # Variables Interface # each vertex is assigned to a biclique, with k bicliques, variables(c::BicliqueCover) = fill(1,c.k * nv(c.graph)) num_variables(c::BicliqueCover) = nv(c.graph) * c.k -num_flavors(c::BicliqueCover) = 2 +num_flavors(::Type{<:BicliqueCover}) = 2 # constraints interface function constraints(c::BicliqueCover) @@ -94,3 +94,31 @@ function is_biclique(config,bc::BicliqueCover) end return true end + +""" + + read_solution(bicliquecover::BicliqueCover, solution::Vector{Vector{Int64}}) + +Read the solution of the BicliqueCover problem from the solution of the BMF problem, return a vector of two bit matrix +""" +function read_solution(bicliquecover::BicliqueCover, solution::Vector{Vector{Int64}}) + len_part1 = length(bicliquecover.part1) + len_part2 = nv(bicliquecover.graph) - len_part1 + k = bicliquecover.k + B = falses((len_part1,k)) + C = falses((k,len_part1)) + # each iteration, we update the a-th column of B and the a-th row of C + for a in range(1,k) + for i in range(1,len_part1) + if solution[a][i] == 1 + B[i,a] = true + end + end + for j in range(1,len_part2) + if solution[a][j+len_part1] == 1 + C[a,j] = true + end + end + end + return [B,C] +end diff --git a/src/rules/BMF_BicliqueCover.jl b/src/rules/BMF_BicliqueCover.jl new file mode 100644 index 0000000..b29e5e7 --- /dev/null +++ b/src/rules/BMF_BicliqueCover.jl @@ -0,0 +1,51 @@ +""" +$TYPEDEF + +The reduction result from BinaryMatrixFactorization to BicliqueCover. + +### Fields +- `bicliquecover`: The BicliqueCover problem. +""" +struct ReductionBMFToBicliqueCover{Int64} <: AbstractReductionResult + bicliquecover::BicliqueCover{Int64} +end +Base.:(==)(a::ReductionBMFToBicliqueCover, b::ReductionBMFToBicliqueCover) = a.bicliquecover == b.bicliquecover +target_problem(res::ReductionBMFToBicliqueCover) = res.bicliquecover + +function reduceto(::Type{<:BicliqueCover}, bmf::BinaryMatrixFactorization) + bc = biclique_cover_from_matrix(Int.(bmf.A), bmf.k) + return ReductionBMFToBicliqueCover(bc) +end + +function extract_solution(res::ReductionBMFToBicliqueCover{Int64}, solution) + return solution +end + +""" +$TYPEDEF + +The reduction result from BicliqueCover to BinaryMatrixFactorization. + +### Fields +- `BMF`: The BinaryMatrixFactorization problem. +""" +struct ReductionBicliqueCoverToBMF <: AbstractReductionResult + BMF::BinaryMatrixFactorization +end +Base.:(==)(a::ReductionBicliqueCoverToBMF, b::ReductionBicliqueCoverToBMF) = a.BMF == b.BMF +target_problem(res::ReductionBicliqueCoverToBMF) = res.BMF + +function reduceto(::Type{<:BinaryMatrixFactorization}, biclique::BicliqueCover) + len_part1 = length(biclique.part1) + A = falses((len_part1,nv(biclique.graph)-len_part1)) + for e in edges(biclique.graph) + if e.src in biclique.part1 + A[e.src, e.dst-len_part1] = true + end + end + return ReductionBicliqueCoverToBMF(BinaryMatrixFactorization(A, biclique.k)) +end + +function extract_solution(res::ReductionBicliqueCoverToBMF, solution) + return solution +end \ No newline at end of file diff --git a/src/rules/rules.jl b/src/rules/rules.jl index a81b74d..9c851f7 100644 --- a/src/rules/rules.jl +++ b/src/rules/rules.jl @@ -88,4 +88,5 @@ include("sat_dominatingset.jl") include("independentset_setpacking.jl") include("circuit_sat.jl") include("vertexcovering_independentset.jl") -include("matching_setpacking.jl") \ No newline at end of file +include("matching_setpacking.jl") +include("BMF_BicliqueCover.jl") \ No newline at end of file diff --git a/test/models/BMF.jl b/test/models/BMF.jl index 86346f6..348d8d1 100644 --- a/test/models/BMF.jl +++ b/test/models/BMF.jl @@ -4,32 +4,37 @@ using Test, ProblemReductions # test 1 A = trues(3, 3) k = 2 - bmf = BinaryMatrixFactorization(A, k) + bmf1 = BinaryMatrixFactorization(A, k) - @test variables(bmf) == [i for i in 1:12] - @test num_variables(bmf) == 12 + @test variables(bmf1) == [i for i in 1:12] + @test num_variables(bmf1) == 12 @test flavors(BinaryMatrixFactorization) == (0, 1) @test num_flavors(BinaryMatrixFactorization) == 2 - @test problem_size(bmf) == (num_rows=3, num_cols=3, k=2) - @test solution_size(bmf, falses(3, 2), falses(2, 3)) == 9 + @test problem_size(bmf1) == (num_rows=3, num_cols=3, k=2) + @test solution_size(bmf1, falses(3, 2), falses(2, 3)) == 9 @test energy_mode(BinaryMatrixFactorization) == SmallerSizeIsBetter() - @test is_binary_matrix_factorization(bmf, falses(3, 2), falses(2, 3)) == false - @test is_binary_matrix_factorization(bmf,trues(3, 2), trues(2, 3)) == true - bmf1 = BinaryMatrixFactorization(trues(3,3), 3) - @test bmf != bmf1 - bmf2 = BinaryMatrixFactorization(trues(3,3), 2) - @test bmf == bmf2 + @test is_binary_matrix_factorization(bmf1, falses(3, 2), falses(2, 3)) == false + @test is_binary_matrix_factorization(bmf1,trues(3, 2), trues(2, 3)) == true + bmf_a = BinaryMatrixFactorization(trues(3,3), 3) + @test bmf1 != bmf_a + bmf_b = BinaryMatrixFactorization(trues(3,3), 2) + @test bmf1 == bmf_b # test 2 A = trues(3, 3) k = 2 - bmf = BinaryMatrixFactorization(A, k) + bmf2 = BinaryMatrixFactorization(A, k) - @test num_variables(bmf) == 12 + @test num_variables(bmf2) == 12 @test flavors(BinaryMatrixFactorization) == (0, 1) - @test problem_size(bmf) == (num_rows=3, num_cols=3, k=2) - @test solution_size(bmf, falses(3, 2), falses(2, 3)) == 9 + @test problem_size(bmf2) == (num_rows=3, num_cols=3, k=2) + @test solution_size(bmf2, falses(3, 2), falses(2, 3)) == 9 @test energy_mode(BinaryMatrixFactorization) == SmallerSizeIsBetter() - @test is_binary_matrix_factorization(bmf, falses(3, 2), falses(2, 3)) == false - @test is_binary_matrix_factorization(bmf,trues(3, 2), trues(2, 3)) == true + @test is_binary_matrix_factorization(bmf2, falses(3, 2), falses(2, 3)) == false + @test is_binary_matrix_factorization(bmf2,trues(3, 2), trues(2, 3)) == true + + # test 3 + A = BitMatrix([1 0;1 1]) + bmf3 = BinaryMatrixFactorization(A, 2) + @test read_solution(bmf3, BitMatrix([1 0 ; 1 1]), BitMatrix([1 0 ; 0 1])) == [[1,1,1,0],[0,1,0,1]] end \ No newline at end of file diff --git a/test/models/BicliqueCover.jl b/test/models/BicliqueCover.jl index 711fa53..c49f1fe 100644 --- a/test/models/BicliqueCover.jl +++ b/test/models/BicliqueCover.jl @@ -10,6 +10,7 @@ using Test, ProblemReductions, Graphs # variable and weight interfaces @test num_variables(bc) == 12 @test num_flavors(bc) == 2 + @test num_flavors(BicliqueCover{Int64}) == 2 @test problem_size(bc) == (num_vertices=6, num_edges=5, k=2) bc_matrix = ProblemReductions.biclique_cover_from_matrix([1 1 0;1 1 0;0 0 1],2) @test bc_matrix == bc @@ -24,4 +25,6 @@ using Test, ProblemReductions, Graphs @test ProblemReductions.is_satisfied(bc,[[1,1,0,1,1,0],[0,0,1,0,0,1]]) == true @test solution_size(bc, [[1,1,0,1,1,0],[0,0,1,0,0,1]]) == ProblemReductions.SolutionSize(6, true) @test solution_size(bc, [[1,1,0,1,1,0],[0,0,1,0,0,0]]) == ProblemReductions.SolutionSize(5, false) + B,C = BitMatrix([1 0;1 0;0 1]), BitMatrix([1 1 0;0 0 1]) + @test read_solution(bc, [[1,1,0,1,1,0],[0,0,1,0,0,1]]) == [B,C] end diff --git a/test/rules/BMF_BicliqueCover.jl b/test/rules/BMF_BicliqueCover.jl new file mode 100644 index 0000000..3335a58 --- /dev/null +++ b/test/rules/BMF_BicliqueCover.jl @@ -0,0 +1,29 @@ +using Test, ProblemReductions, Graphs + +@testset "BMF_BicliqueCover" begin + A = [1 0 ; 1 1] + A = BitMatrix(A) + bmf1 = BinaryMatrixFactorization(A, 2) + bc1 = ProblemReductions.biclique_cover_from_matrix(Int.(A), 2) + res = reduceto(BicliqueCover, bmf1) + res1 = ReductionBMFToBicliqueCover(bc1) + @test res == res1 + @test res.bicliquecover == bc1 + @test res.bicliquecover.part1 == [i for i in 1:size(A,1)] + @test target_problem(res) == bc1 + @test extract_solution(res,[[1,1,1,0],[0,1,0,1]]) == [[1,1,1,0],[0,1,0,1]] +end + +@testset "BicliqueCover_BMF" begin + g = SimpleGraph(4) + for (i,j) in [(1,3),(2,3),(2,4)] + add_edge!(g,i,j) + end + bc = BicliqueCover(g,[1,2], 2) + res = reduceto(BinaryMatrixFactorization, bc) + res1 = ReductionBicliqueCoverToBMF(BinaryMatrixFactorization(BitMatrix([1 0; 1 1]), 2)) + @test res == res1 + @test res.BMF == BinaryMatrixFactorization(BitMatrix([1 0; 1 1]), 2) + @test target_problem(res) == BinaryMatrixFactorization(BitMatrix([1 0; 1 1]), 2) + @test extract_solution(res,[[1,0,1,0],[0,1,0,1]]) == [[1,0,1,0],[0,1,0,1]] +end \ No newline at end of file diff --git a/test/rules/rules.jl b/test/rules/rules.jl index 8a24877..32ead72 100644 --- a/test/rules/rules.jl +++ b/test/rules/rules.jl @@ -52,6 +52,10 @@ end include("matching_setpacking.jl") end +@testset "BMF_BicliqueCover" begin + include("BMF_BicliqueCover.jl") +end + @testset "rules" begin circuit = CircuitSAT(@circuit begin x = a ∨ ¬b