Skip to content

New reduction rule: BinaryMatrixFactorization to BicliqueCover #140

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 3 additions & 2 deletions src/ProblemReductions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
24 changes: 23 additions & 1 deletion src/models/BMF.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

30 changes: 29 additions & 1 deletion src/models/BicliqueCover.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
# 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

Check warning on line 38 in src/models/BicliqueCover.jl

View check run for this annotation

Codecov / codecov/patch

src/models/BicliqueCover.jl#L38

Added line #L38 was not covered by tests

# constraints interface
function constraints(c::BicliqueCover)
Expand Down Expand Up @@ -94,3 +94,31 @@
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
51 changes: 51 additions & 0 deletions src/rules/BMF_BicliqueCover.jl
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion src/rules/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,5 @@ include("sat_dominatingset.jl")
include("independentset_setpacking.jl")
include("circuit_sat.jl")
include("vertexcovering_independentset.jl")
include("matching_setpacking.jl")
include("matching_setpacking.jl")
include("BMF_BicliqueCover.jl")
39 changes: 22 additions & 17 deletions test/models/BMF.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions test/models/BicliqueCover.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
29 changes: 29 additions & 0 deletions test/rules/BMF_BicliqueCover.jl
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions test/rules/rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading