module type S =
sig
module M:Matching.S
type kind =
| Outer of M.P.G.edge option
| Inner of M.P.G.edge
| Away
type forest
val initial_forest : M.t -> forest
val link : forest -> M.P.G.vertex -> kind
val g : forest -> M.P.G.graph
type augment_result =
| Augmenting_Path of M.P.t
| Blossom of M.P.t
| Forest of forest
val augment_forest : forest -> M.P.G.edge -> augment_result
type edmonds_result =
| Augment of M.P.t
| Tutte of M.P.G.V.t
val edmonds : M.t -> edmonds_result
val maximum_matching: M.t -> (M.t * M.P.G.V.t)
end
module Make = functor (M : Matching.S) ->
struct
module M = M
module G = M.P.G
module P = M.P
let equal u v = G.compare_vertex u v = 0
type kind =
| Outer of G.edge option
| Inner of G.edge
| Away
type forest =
{ matching : M.t;
links : kind G.VMap.t;
unscanned : G.E.t
}
let link forest v = try G.VMap.find v forest.links with Not_found -> Away
let g forest = M.graph forest.matching
let rec get_path forest v path =
match link forest v with
| Outer None -> path
| Outer (Some e)
| Inner e -> get_path forest (G.opposite (g forest) e v) (P.add_last path e)
| Away -> assert false
let rec remove_common_suffix path1 path2 =
if (P.is_empty path1) || (P.is_empty path2) ||
(G.compare_edge (P.last_edge path1) (P.last_edge path2) <> 0)
then (path1,path2)
else remove_common_suffix (P.remove_last path1) (P.remove_last path2)
let vset_of_path p =
P.fold (fun vset u e v -> G.V.add u vset) (G.V.singleton (P.last_vertex p)) p
type augment_result =
| Augmenting_Path of P.t
| Blossom of P.t
| Forest of forest
let grow forest edge u v =
match M.matched_to forest.matching v with
| Some (e_in_m,w) ->
Forest (
{ forest with
links = G.VMap.add v (Inner edge) (G.VMap.add w (Outer (Some e_in_m)) forest.links);
unscanned = G.E.remove edge (G.E.fold G.E.add (G.delta (g forest) w) forest.unscanned)
})
| None -> assert false
let augment_forest forest edge =
let (u,v) = G.extremities (g forest) edge in
let chain path1 edge path2 = P.rev_concat path1 (P.add_first edge path2) in
if equal u v then Forest forest
else match link forest u, link forest v with
| Outer _, Outer _ ->
let path1 = get_path forest u (P.empty (g forest) u) in
let path2 = get_path forest v (P.empty (g forest) v) in
if equal (P.last_vertex path1) (P.last_vertex path2)
then
let (subpath1, subpath2) = remove_common_suffix path1 path2 in
Blossom (chain subpath1 edge subpath2)
else
Augmenting_Path (chain path1 edge path2)
| Outer _, Away -> grow forest edge u v
| Away, Outer _ -> grow forest edge v u
| _ , Inner _
| Inner _, _
| Away, Away -> Forest { forest with unscanned = G.E.remove edge forest.unscanned }
type edmonds_result =
| Augment of P.t
| Tutte of G.V.t
let initial_forest matching =
let (links,to_scan) =
G.V.fold
(fun v (l,r) -> (G.VMap.add v (Outer None) l,
G.E.fold G.E.add (G.delta (M.graph matching) v) r)
)
(M.uncovered matching)
(G.VMap.empty, G.E.empty)
in
{ matching = matching;
links = links;
unscanned = to_scan
}
let cut_when f path =
let rec iter path1 path2 =
if (P.is_empty path2) || (f (P.first_vertex path2))
then (path1, path2)
else iter (P.add_last path1 (P.first_edge path2)) (P.remove_first path2)
in iter (P.empty (P.graph path) (P.first_vertex path)) path
let translate_f graph path =
P.fold (fun p _ e _ -> P.add_last p e) (P.empty graph (P.first_vertex path)) path
let translate_b graph path =
P.fold_backward (fun _ e _ p -> P.add_first e p) path (P.empty graph (P.last_vertex path))
let uncontract_blossom forest m_c blossom path =
let w = P.first_vertex blossom in
let (p1,p2) = cut_when (fun u -> equal u w) path in
if P.is_empty p2 then
translate_f (g forest) path
else
let (path1,path2) = (translate_f (g forest) p1, translate_b (g forest) p2) in
let (v1,v2) = (P.last_vertex path1, P.first_vertex path2) in
let w' = if equal w v1 then v2 else v1 in
let (c1,c2) = cut_when (fun u -> equal u w') blossom in
match G.E.cardinal (P.edge_set c1) mod 2 = 0, equal w v1 with
| false, true -> P.concat path1 (P.concat (P.reverse c2) path2)
| false, false -> P.concat path1 (P.concat c2 path2)
| true, true -> P.concat path1 (P.concat c1 path2)
| true, false -> P.concat path1 (P.concat (P.reverse c1) path2)
let rec edmonds_forest forest =
if G.E.is_empty forest.unscanned
then
Tutte (G.VMap.fold
(fun v k vset -> match k with Inner _ -> G.V.add v vset | _ -> vset)
forest.links G.V.empty
)
else
let edge = G.E.choose forest.unscanned in
match augment_forest forest edge with
| Augmenting_Path p -> Augment p
| Blossom c ->
begin
let m_c = M.contract_blossom forest.matching c in
match edmonds m_c with
| Augment p -> Augment (uncontract_blossom forest m_c c p)
| Tutte vset -> Tutte vset
end
| Forest f -> edmonds_forest f
and edmonds matching = edmonds_forest (initial_forest matching)
let rec maximum_matching matching =
match edmonds matching with
| Tutte set -> (matching,set)
| Augment p -> let matching = M.augment matching p in maximum_matching matching
end