From 6765fbfb77aff24b96fda1f3453f60a8bb058105 Mon Sep 17 00:00:00 2001 From: mbesancon Date: Fri, 12 Jan 2018 17:41:59 -0500 Subject: [PATCH] changed w type in max matching (#5) * changed w type in max matching * test default weights * split cutoff, tests * added tests * fix stuff --- src/lp.jl | 45 ++++++++----- src/maximum_weight_matching.jl | 31 +++++---- test/runtests.jl | 113 ++++++++++++++++++++------------- 3 files changed, 119 insertions(+), 70 deletions(-) diff --git a/src/lp.jl b/src/lp.jl index 9e55ba6..74d7fcc 100644 --- a/src/lp.jl +++ b/src/lp.jl @@ -20,18 +20,11 @@ The returned object is of type `MatchingResult`. """ function maximum_weight_maximal_matching end -function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolver, w::Dict{Edge,T}, cutoff::R) where {T<:Real, R<:Real} - wnew = Dict{Edge,T}() - for (e,x) in w - if x >= cutoff - wnew[e] = x - end - end - return maximum_weight_maximal_matching(g, solver, wnew) +function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolver, w::AbstractMatrix{T}, cutoff::R) where {T<:Real, R<:Real} + return maximum_weight_maximal_matching(g, solver, cutoff_weights(w, cutoff)) end - -function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolver, w::Dict{Edge,T}) where {T<:Real} +function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolver, w::AbstractMatrix{T}) where {T<:Real} # TODO support for graphs with zero degree nodes # TODO apply separately on each connected component bpmap = bipartite_map(g) @@ -44,10 +37,15 @@ function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolve nedg = 0 edgemap = Dict{Edge,Int}() - for (e,_) in w - nedg += 1 - edgemap[e] = nedg - edgemap[reverse(e)] = nedg + + for j in 1:size(w,2) + for i in 1:size(w,1) + if w[i,j] > 0.0 + nedg += 1 + edgemap[Edge(i,j)] = nedg + edgemap[Edge(j,i)] = nedg + end + end end model = Model(solver=solver) @@ -78,7 +76,7 @@ function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolve end end - @objective(model, Max, sum(c * x[edgemap[e]] for (e,c) = w)) + @objective(model, Max, sum(w[src(e),dst(e)] * x[edgemap[e]] for e in keys(edgemap))) status = solve(model) status != :Optimal && error("JuMP solver failed to find optimal solution.") @@ -90,7 +88,7 @@ function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolve mate = fill(-1, nv(g)) for e in edges(g) - if haskey(w, e) + if w[src(e),dst(e)] > zero(T) inmatch = convert(Bool, sol[edgemap[e]]) if inmatch mate[src(e)] = dst(e) @@ -101,3 +99,18 @@ function maximum_weight_maximal_matching(g::Graph, solver::AbstractMathProgSolve return MatchingResult(cost, mate) end + +""" + cutoff_weights copies the weight matrix with all elements below cutoff set to 0 +""" +function cutoff_weights(w::AbstractMatrix{T}, cutoff::R) where {T<:Real, R<:Real} + wnew = copy(w) + for j in 1:size(w,2) + for i in 1:size(w,1) + if wnew[i,j] < cutoff + wnew[i,j] = zero(T) + end + end + end + wnew +end \ No newline at end of file diff --git a/src/maximum_weight_matching.jl b/src/maximum_weight_matching.jl index 6ee242d..05427eb 100644 --- a/src/maximum_weight_matching.jl +++ b/src/maximum_weight_matching.jl @@ -23,30 +23,30 @@ function maximum_weight_matching end function maximum_weight_matching(g::Graph, solver::AbstractMathProgSolver, - w::Dict{Edge,T} = Dict{Edge,Int64}(i => 1 for i in collect(edges(g)))) where {T <:Real} + w::AbstractMatrix{T} = default_weights(g)) where {T <:Real} model = Model(solver = solver) n = nv(g) edge_list = collect(edges(g)) # put the edge weights in w in the right order to be compatible with edge_list - for edge in keys(w) - redge = reverse(edge) - if !is_ordered(edge) && !haskey(w, redge) # replace i=>j by j=>i if necessary. - w[redge] = w[edge] + for j in 1:n + for i in 1:n + if i > j && w[i,j] > zero(T) && w[j,i] < w[i,j] + w[j,i] = w[i,j] + end + if Edge(i,j) ∉ edge_list + w[i,j] = zero(T) + end end end - - if setdiff(edge_list, keys(w)) != [] # If some edges do not have a key in w. - error("Some edge weights are missing, check that keys i => j in w satisfy i <= j") - end - + if is_bipartite(g) @variable(model, x[edge_list] >= 0) # no need to enforce integrality else @variable(model, x[edge_list] >= 0, Int) # requires MIP solver end - @objective(model, Max, sum(x[edge]*w[edge] for edge in edge_list)) + @objective(model, Max, sum(x[e]*w[src(e),dst(e)] for e in edge_list)) @constraint(model, c1[i=1:n], sum(x[Edge(i,j)] for j=filter(l -> l > i, neighbors(g,i))) + sum(x[Edge(j,i)] for j=filter(l -> l <= i, neighbors(g,i))) @@ -71,3 +71,12 @@ function dict_to_arr(n::Int64, solution::JuMP.JuMPArray{T,1,Tuple{Array{E,1}}}) end return mate end + + +function default_weights(g::G) where {G<:AbstractGraph} + m = spzeros(nv(g),nv(g)) + for e in edges(g) + m[src(e),dst(e)] = 1 + end + return m +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index e986886..135f5a1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,12 +4,36 @@ using LightGraphsMatching using Base.Test using Cbc: CbcSolver -g =CompleteBipartiteGraph(2,2) -w =Dict{Edge,Float64}() -w[Edge(1,3)] = 10. -w[Edge(1,4)] = 1. -w[Edge(2,3)] = 2. -w[Edge(2,4)] = 11. +g = CompleteGraph(4) +w = LightGraphsMatching.default_weights(g) +@test all((w + w') .≈ ones(4,4) - eye(4,4)) + +w1 = [ + 1 3 + 5 1 +] +w0 = [ + 0 3 + 5 0 +] +@test all(w0 .≈ LightGraphsMatching.cutoff_weights(w1, 2)) + +g = CompleteGraph(3) +w = [ + 1 2 1 + 1 1 1 + 3 1 1 +] +match = maximum_weight_matching(g, CbcSolver(), w) +@test match.mate[1] == 3 +@test match.weight == 3 + +g = CompleteBipartiteGraph(2,2) +w = zeros(4,4) +w[1,3] = 10. +w[1,4] = 1. +w[2,3] = 2. +w[2,4] = 11. match = maximum_weight_maximal_matching(g, CbcSolver(), w) @test match.weight == 21 @test match.mate[1] == 3 @@ -18,11 +42,11 @@ match = maximum_weight_maximal_matching(g, CbcSolver(), w) @test match.mate[4] == 2 g =CompleteBipartiteGraph(2,4) -w =Dict{Edge,Float64}() -w[Edge(1,3)] = 10 -w[Edge(1,4)] = 0.5 -w[Edge(2,3)] = 11 -w[Edge(2,4)] = 1 +w =zeros(6,6) +w[1,3] = 10 +w[1,4] = 0.5 +w[2,3] = 11 +w[2,4] = 1 match = maximum_weight_maximal_matching(g, CbcSolver(), w) @test match.weight == 11.5 @test match.mate[1] == 4 @@ -31,13 +55,13 @@ match = maximum_weight_maximal_matching(g, CbcSolver(), w) @test match.mate[3] == 2 g =CompleteBipartiteGraph(2,6) -w =Dict{Edge,Float64}() -w[Edge(1,3)] = 10 -w[Edge(1,4)] = 0.5 -w[Edge(2,3)] = 11 -w[Edge(2,4)] = 1 -w[Edge(2,5)] = -1 -w[Edge(2,6)] = -1 +w =zeros(8,8) +w[1,3] = 10 +w[1,4] = 0.5 +w[2,3] = 11 +w[2,4] = 1 +w[2,5] = -1 +w[2,6] = -1 match = maximum_weight_maximal_matching(g,CbcSolver(),w,0) @test match.weight == 11.5 @test match.mate[1] == 4 @@ -46,12 +70,12 @@ match = maximum_weight_maximal_matching(g,CbcSolver(),w,0) @test match.mate[3] == 2 g =CompleteBipartiteGraph(4,2) -w =Dict{Edge,Float64}() -w[Edge(3,5)] = 10 -w[Edge(3,6)] = 0.5 -w[Edge(2,5)] = 11 -w[Edge(1,6)] = 1 -w[Edge(1,5)] = -1 +w = zeros(6,6) +w[3,5] = 10 +w[3,6] = 0.5 +w[2,5] = 11 +w[1,6] = 1 +w[1,5] = -1 match = maximum_weight_maximal_matching(g,CbcSolver(),w,0) @test match.weight == 12 @@ -63,24 +87,24 @@ match = maximum_weight_maximal_matching(g,CbcSolver(),w,0) @test match.mate[6] == 1 g = CompleteGraph(3) -w =Dict{Edge,Float64}() -w[Edge(1,2)] = 1 -@test_throws ErrorException maximum_weight_matching(g,CbcSolver(),w) - -w[Edge(3,2)] = 1 -w[Edge(1,3)] = 1 +w = zeros(3,3) +w[1,2] = 1 +w[3,2] = 1 +w[1,3] = 1 match = maximum_weight_matching(g,CbcSolver(),w) @test match.weight == 1 g = Graph(4) -w =Dict{Edge,Float64}() add_edge!(g, 1,3) add_edge!(g, 1,4) add_edge!(g, 2,4) -w[Edge(1,3)] = 1 -w[Edge(1,4)] = 3 -w[Edge(2,4)] = 1 + +w =zeros(4,4) +w[1,3] = 1 +w[1,4] = 3 +w[2,4] = 1 + match = maximum_weight_matching(g,CbcSolver(),w) @test match.weight == 3 @test match.mate[1] == 4 @@ -100,11 +124,12 @@ match = maximum_weight_matching(g,CbcSolver()) @test match.mate[3] == 4 @test match.mate[4] == 3 -w =Dict{Edge,Float64}() -w[Edge(1,2)] = 1 -w[Edge(2,3)] = 1 -w[Edge(1,3)] = 1 -w[Edge(3,4)] = 1 +w = zeros(4,4) +w[1,2] = 1 +w[2,3] = 1 +w[1,3] = 1 +w[3,4] = 1 + match = maximum_weight_matching(g,CbcSolver(), w) @test match.weight == 2 @test match.mate[1] == 2 @@ -112,10 +137,12 @@ match = maximum_weight_matching(g,CbcSolver(), w) @test match.mate[3] == 4 @test match.mate[4] == 3 -w[Edge(1,2)] = 1 -w[Edge(2,3)] = 1 -w[Edge(1,3)] = 5 -w[Edge(3,4)] = 1 +w = zeros(4,4) +w[1,2] = 1 +w[2,3] = 1 +w[1,3] = 5 +w[3,4] = 1 + match = maximum_weight_matching(g,CbcSolver(),w) @test match.weight == 5 @test match.mate[1] == 3