Version & licenses
Creative Commons License

naive_edmonds.ml

Guyslain Naves
  1. module type S =
  2. sig
  3. module M:Matching.S

  4. type kind =
  5. | Outer of M.P.G.edge option
  6. | Inner of M.P.G.edge
  7. | Away

  8. type forest

  9. val initial_forest : M.t -> forest
  10. val link : forest -> M.P.G.vertex -> kind
  11. val g : forest -> M.P.G.graph

  12. type augment_result =
  13. | Augmenting_Path of M.P.t
  14. | Blossom of M.P.t
  15. | Forest of forest

  16. val augment_forest : forest -> M.P.G.edge -> augment_result

  17. type edmonds_result =
  18. | Augment of M.P.t
  19. | Tutte of M.P.G.V.t

  20. val edmonds : M.t -> edmonds_result

  21. val maximum_matching: M.t -> (M.t * M.P.G.V.t)

  22. end

  23. module Make = functor (M : Matching.S) ->
  24. struct

  25. module M = M
  26. module G = M.P.G
  27. module P = M.P


  28. let equal u v = G.compare_vertex u v = 0


  29. type kind = (* for a matching M: *)
  30. | Outer of G.edge option (* Vertex at even distance from a root, covered by M or root *)
  31. | Inner of G.edge (* at odd distance, with father *)
  32. | Away (* vertex covered by M but not in the forest *)



  33. type forest =
  34. { matching : M.t;
  35. links : kind G.VMap.t;
  36. unscanned : G.E.t
  37. }

  38. let link forest v = try G.VMap.find v forest.links with Not_found -> Away


  39. let g forest = M.graph forest.matching (* helper to get the subjacent graph *)

  40. let rec get_path forest v path =
  41. match link forest v with
  42. | Outer None -> path
  43. | Outer (Some e)
  44. | Inner e -> get_path forest (G.opposite (g forest) e v) (P.add_last path e)
  45. | Away -> assert false

  46. let rec remove_common_suffix path1 path2 =
  47. if (P.is_empty path1) || (P.is_empty path2) ||
  48. (G.compare_edge (P.last_edge path1) (P.last_edge path2) <> 0)
  49. then (path1,path2)
  50. else remove_common_suffix (P.remove_last path1) (P.remove_last path2)

  51. (** computes the set of vertices covered by a path *)
  52. let vset_of_path p =
  53. P.fold (fun vset u e v -> G.V.add u vset) (G.V.singleton (P.last_vertex p)) p


  54. type augment_result = (* Possible result for adding an edge to an alternating forest *)
  55. | Augmenting_Path of P.t (* an augmenting path defined by its list of edges *)
  56. | Blossom of P.t (* a blossom, path starting at top-most vertex *)
  57. | Forest of forest (* a (possibly bigger) forest *)



  58. (* grows an alternating forest where [u] is outer, [v] is away, [u] and [v] are adjacent. *)
  59. let grow forest edge u v =
  60. match M.matched_to forest.matching v with
  61. | Some (e_in_m,w) ->
  62. Forest (
  63. { forest with
  64. links = G.VMap.add v (Inner edge) (G.VMap.add w (Outer (Some e_in_m)) forest.links);
  65. unscanned = G.E.remove edge (G.E.fold G.E.add (G.delta (g forest) w) forest.unscanned)
  66. })
  67. | None -> assert false (* if [v] is away, [v] must be matched *)


  68. (* function adding an [edge] to an alternating [forest] on matching [m]*)
  69. let augment_forest forest edge =
  70. let (u,v) = G.extremities (g forest) edge in
  71. let chain path1 edge path2 = P.rev_concat path1 (P.add_first edge path2) in
  72. if equal u v then Forest forest (* do not consider loops *)
  73. else match link forest u, link forest v with

  74. (* blossom if same tree, augmenting paths if distinct trees *)
  75. | Outer _, Outer _ ->
  76. let path1 = get_path forest u (P.empty (g forest) u) in
  77. let path2 = get_path forest v (P.empty (g forest) v) in
  78. if equal (P.last_vertex path1) (P.last_vertex path2)
  79. then (* Trees are the same: blossom *)
  80. let (subpath1, subpath2) = remove_common_suffix path1 path2 in
  81. Blossom (chain subpath1 edge subpath2)
  82. else (* distinct trees, we have an augmenting path *)
  83. Augmenting_Path (chain path1 edge path2)

  84. (* growing forest (two symmetric cases) *)
  85. | Outer _, Away -> grow forest edge u v
  86. | Away, Outer _ -> grow forest edge v u

  87. (* ignore: if one vertex is inner, or both are away (that last case cannot happen) *)
  88. | _ , Inner _
  89. | Inner _, _
  90. | Away, Away -> Forest { forest with unscanned = G.E.remove edge forest.unscanned }



  91. type edmonds_result =
  92. | Augment of P.t
  93. | Tutte of G.V.t



  94. (* initialize a forest given a matching. *)
  95. let initial_forest matching =
  96. let (links,to_scan) =
  97. G.V.fold (* uncovered vertices are root, others are away. Initialize the search. *)
  98. (fun v (l,r) -> (G.VMap.add v (Outer None) l,
  99. G.E.fold G.E.add (G.delta (M.graph matching) v) r)
  100. )
  101. (M.uncovered matching)
  102. (G.VMap.empty, G.E.empty)
  103. in
  104. { matching = matching;
  105. links = links;
  106. unscanned = to_scan
  107. }


  108. let cut_when f path =
  109. let rec iter path1 path2 =
  110. if (P.is_empty path2) || (f (P.first_vertex path2))
  111. then (path1, path2)
  112. else iter (P.add_last path1 (P.first_edge path2)) (P.remove_first path2)
  113. in iter (P.empty (P.graph path) (P.first_vertex path)) path


  114. (* functions to translate a path in G into a path in G' where this path also exists
  115. (as a sequence of incident edges) *)
  116. let translate_f graph path =
  117. P.fold (fun p _ e _ -> P.add_last p e) (P.empty graph (P.first_vertex path)) path
  118. let translate_b graph path =
  119. P.fold_backward (fun _ e _ p -> P.add_first e p) path (P.empty graph (P.last_vertex path))

  120. let uncontract_blossom forest m_c blossom path =
  121. (* path exists in the contracted graph, we want the result to be a path in the original graph *)
  122. let w = P.first_vertex blossom in
  123. let (p1,p2) = cut_when (fun u -> equal u w) path in
  124. if P.is_empty p2 then (* p does not contain w, as w cannot be an endpoint *)
  125. translate_f (g forest) path
  126. else
  127. let (path1,path2) = (translate_f (g forest) p1, translate_b (g forest) p2) in
  128. let (v1,v2) = (P.last_vertex path1, P.first_vertex path2) in
  129. let w' = if equal w v1 then v2 else v1 in
  130. let (c1,c2) = cut_when (fun u -> equal u w') blossom in
  131. match G.E.cardinal (P.edge_set c1) mod 2 = 0, equal w v1 with
  132. | false, true -> P.concat path1 (P.concat (P.reverse c2) path2)
  133. | false, false -> P.concat path1 (P.concat c2 path2)
  134. | true, true -> P.concat path1 (P.concat c1 path2)
  135. | true, false -> P.concat path1 (P.concat (P.reverse c1) path2)


  136. let rec edmonds_forest forest =
  137. if G.E.is_empty forest.unscanned

  138. then (* no more edges incident to outer to consider: matching is maximum *)
  139. Tutte (G.VMap.fold
  140. (fun v k vset -> match k with Inner _ -> G.V.add v vset | _ -> vset)
  141. forest.links G.V.empty
  142. )

  143. else (* there is an outer-something edge to check *)
  144. let edge = G.E.choose forest.unscanned in
  145. match augment_forest forest edge with
  146. | Augmenting_Path p -> Augment p
  147. | Blossom c ->
  148. begin
  149. let m_c = M.contract_blossom forest.matching c in
  150. match edmonds m_c with
  151. | Augment p -> Augment (uncontract_blossom forest m_c c p)
  152. | Tutte vset -> Tutte vset (* we take the same Tutte set *)

  153. end
  154. | Forest f -> edmonds_forest f
  155. and edmonds matching = edmonds_forest (initial_forest matching)


  156. let rec maximum_matching matching =
  157. match edmonds matching with
  158. | Tutte set -> (matching,set)
  159. | Augment p -> let matching = M.augment matching p in maximum_matching matching


  160. end