@@ -21,34 +21,40 @@ Returns MatchingResult containing:
21
21
"""
22
22
function maximum_weight_matching end
23
23
24
- function maximum_weight_matching (g:: Graph ,
25
- optimizer,
26
- w:: AbstractMatrix{U} = default_weights (g)) where {U <: Real }
24
+ function maximum_weight_matching (
25
+ g:: Graph ,
26
+ optimizer,
27
+ w:: AbstractMatrix{U} = default_weights (g),
28
+ ) where {U<: Real }
27
29
28
30
model = Model (optimizer)
29
31
n = nv (g)
30
32
edge_list = collect (edges (g))
31
33
32
34
# put the edge weights in w in the right order to be compatible with edge_list
33
- for j in 1 : n
34
- for i in 1 : n
35
- if i > j && w[i,j] > zero (U) && w[j,i] < w[i,j]
36
- w[j,i] = w[i,j]
35
+ for j = 1 : n
36
+ for i = 1 : n
37
+ if i > j && w[i, j] > zero (U) && w[j, i] < w[i, j]
38
+ w[j, i] = w[i, j]
39
+ end
40
+ if Edge (i, j) ∉ edge_list
41
+ w[i, j] = zero (U)
42
+ end
37
43
end
38
- if Edge (i,j) ∉ edge_list
39
- w[i,j] = zero (U)
40
- end
41
- end
42
44
end
43
45
44
46
if is_bipartite (g)
45
- @variable (model, x[edge_list] >= 0 ) # no need to enforce integrality
47
+ @variable (model, x[edge_list] >= 0 ) # no need to enforce integrality
46
48
else
47
- @variable (model, x[edge_list] >= 0 , Int) # requires MIP solver
49
+ @variable (model, x[edge_list] >= 0 , Int) # requires MIP solver
48
50
end
49
- @objective (model, Max, sum (x[e]* w[src (e),dst (e)] for e in edge_list))
51
+ @objective (model, Max, sum (x[e] * w[src (e), dst (e)] for e in edge_list))
50
52
51
- @constraint (model, c1[i= 1 : n], sum (x[Edge (minmax (i,j))] for j in neighbors (g,i)) <= 1 )
53
+ @constraint (
54
+ model,
55
+ c1[i = 1 : n],
56
+ sum (x[Edge (minmax (i, j))] for j in neighbors (g, i)) <= 1
57
+ )
52
58
optimize! (model)
53
59
status = JuMP. termination_status (model)
54
60
status != MOI. OPTIMAL && error (" JuMP solver failed to find optimal solution." )
@@ -58,22 +64,69 @@ function maximum_weight_matching(g::Graph,
58
64
end
59
65
60
66
""" Returns an array of mates from a dictionary that maps edges to {0,1} """
61
- function dict_to_arr (n:: Int64 , solution:: JuMP.Containers.DenseAxisArray{U,1,Tuple{Array{E,1}}} , edge_list:: AbstractVector{E} ) where {U<: Real , E<: Edge }
62
- mate = fill (- 1 ,n)
63
- for e in edge_list
64
- if solution[e] >= 1 - 1e-5 # Some tolerance to numerical approximations by the solver.
65
- mate[src (e)] = dst (e)
66
- mate[dst (e)] = src (e)
67
+ function dict_to_arr (
68
+ n:: Int64 ,
69
+ solution:: JuMP.Containers.DenseAxisArray{U,1,Tuple{Array{E,1}}} ,
70
+ edge_list:: AbstractVector{E} ,
71
+ ) where {U<: Real ,E<: Edge }
72
+ mate = fill (- 1 , n)
73
+ for e in edge_list
74
+ if solution[e] >= 1 - 1e-5 # Some tolerance to numerical approximations by the solver.
75
+ mate[src (e)] = dst (e)
76
+ mate[dst (e)] = src (e)
77
+ end
67
78
end
68
- end
69
- return mate
79
+ return mate
70
80
end
71
81
72
82
73
83
function default_weights (g:: G ) where {G<: AbstractGraph }
74
- m = spzeros (nv (g),nv (g))
75
- for e in edges (g)
76
- m[src (e),dst (e)] = 1
77
- end
78
- return m
84
+ m = spzeros (nv (g), nv (g))
85
+ for e in edges (g)
86
+ m[src (e), dst (e)] = 1
87
+ end
88
+ return m
89
+ end
90
+
91
+ """
92
+ maximum_weight_matching_reduction(g::Graph, w::Matrix{Real}) -> Array{Edge}
93
+
94
+ Given a graph `g` and an edgemap `w` containing weights associated to edges,
95
+ returns a matching with the maximum total weight.
96
+ `w` is an adjacent matrix that maps edges i => j to weights.
97
+ If no weight parameter is given, all edges will be considered to have weight 1
98
+
99
+ This algorithm uses a reduction based on the minimum_weight_perfect_matching function
100
+ to find the maximum weight matching (see https://homepages.cwi.nl/~schaefer/ftp/pdf/masters-thesis.pdf section 1.5.1).
101
+
102
+ Return an array of edges contained in the matching.
103
+ """
104
+ function maximum_weight_matching_reduction (
105
+ g:: Graph ,
106
+ w:: AbstractMatrix{U} = default_weights (g),
107
+ ) where {U<: Real }
108
+
109
+ h = deepcopy (g)
110
+ # collect needed since we modify the edges later
111
+ edge_iter = collect (edges (h))
112
+ l = nv (h)
113
+ add_vertices! (h, l)
114
+ weights = Dict {edgetype(g), eltype(w)} ()
115
+ for edge in edge_iter
116
+ add_edge! (h, src (edge) + l, dst (edge) + l)
117
+ weights[edge] = - w[src (edge), dst (edge)]
118
+ weights[Edge (dst (edge), src (edge))] = - w[src (edge), dst (edge)]
119
+ weights[Edge (src (edge) + l, dst (edge) + l)] = - w[src (edge), dst (edge)]
120
+ weights[Edge (dst (edge) + l, src (edge) + l)] = - w[src (edge), dst (edge)]
121
+ end
122
+ for i = 1 : l
123
+ add_edge! (g, i, i + l)
124
+ weights[Edge (i, i + l)] = 0
125
+ end
126
+
127
+ match = minimum_weight_perfect_matching (h, weights)
128
+
129
+ result = [Edge (i, match. mate[i]) for i in 1 : l if match. mate[i] <= l && match. mate[i] > 0 ]
130
+
131
+ return result
79
132
end
0 commit comments