Source file topological.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
(**************************************************************************)
(*                                                                        *)
(*  Ocamlgraph: a generic graph library for OCaml                         *)
(*  Copyright (C) 2004-2010                                               *)
(*  Sylvain Conchon, Jean-Christophe Filliatre and Julien Signoles        *)
(*                                                                        *)
(*  This software is free software; you can redistribute it and/or        *)
(*  modify it under the terms of the GNU Library General Public           *)
(*  License version 2.1, with the special exception on linking            *)
(*  described in file LICENSE.                                            *)
(*                                                                        *)
(*  This software is distributed in the hope that it will be useful,      *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *)
(*                                                                        *)
(**************************************************************************)

module type G = sig
  type t
  module V : Sig.COMPARABLE
  val iter_vertex : (V.t -> unit) -> t -> unit
  val iter_succ : (V.t -> unit) -> t -> V.t -> unit
end

module Make(G: G) = struct

  module Scc = Components.Make(G)

  let fold f g acc =
    (* build the graph of strongly-connected components *)
    let n, scc = Scc.scc g in
    let vertices = Array.make n [] in
    let edges = Array.make n [] in
    let degree = Array.make n 0 in (* in-degree *)
    let add_vertex x =
      let ix = scc x in
      vertices.(ix) <- x :: vertices.(ix);
      let add_edge y =
        let iy = scc y in
        if ix <> iy then begin
          edges.(ix) <- iy :: edges.(ix);
          degree.(iy) <- degree.(iy) + 1
        end
      in
      G.iter_succ add_edge g x
    in
    G.iter_vertex add_vertex g;
    (* standard topological sort on a DAG *)
    let todo = Queue.create () in
    let rec walk acc =
      if Queue.is_empty todo then
        acc
      else
        let i = Queue.pop todo in
        let acc = List.fold_right f vertices.(i) acc in
        List.iter
          (fun j ->
             let d = degree.(j) in
             assert (d > 0); (* no back edge *)
             if d = 1 then Queue.push j todo else degree.(j) <- d-1)
          edges.(i);
        walk acc
    in
    for i = 0 to n-1 do if degree.(i) = 0 then Queue.push i todo done;
    walk acc

  let iter f g = fold (fun v () -> f v) g ()

end

module Make_stable(G: sig include G val in_degree : t -> V.t -> int end) =
struct

  module H = Hashtbl.Make(G.V)
  module C = Path.Check(G)

  let choose ~old (v, n: G.V.t * int) =
    let l, min = old in
    if n = min then v :: l, n
    else if n < min then [ v ], n
    else old

  module Q = struct
    module S = Set.Make(G.V)
    let create () = ref S.empty
    let push v s = s := S.add v !s
    let pop s =
      let r = S.min_elt !s in
      s := S.remove r !s;
      r
    let is_empty s = S.is_empty !s
    let choose ~old new_ =
      let l, n = choose ~old new_ in
      List.sort G.V.compare l, n
  end

  (* in case of multiple cycles, choose one vertex in a cycle which
     does not depend of any other. *)
  let find_top_cycle checker vl =
    (* choose [v] if each other vertex [v'] is in the same cycle
       (a path from v to v') or is in a separate component
       (no path from v' to v).
       So, if there is a path from v' to without any path from v to v',
       discard v. *)
    let on_top_cycle v =
      List.for_all
        (fun v' ->
           G.V.equal v v' ||
           C.check_path checker v v' || not (C.check_path checker v' v))
        vl
    in
    List.filter on_top_cycle vl

  let fold f g acc =
    let checker = C.create g in
    let degree = H.create 97 in
    let todo = Q.create () in
    let push x =
      H.remove degree x;
      Q.push x todo
    in
    let add_vertex acc v = 
        G.iter_succ
          (fun x->
             try
               let d = H.find degree x in
               if d = 1 then push x else H.replace degree x (d-1)
             with Not_found ->
               (* [x] already visited *)
               ())
          g v;
          f v acc in
    let rec walk acc =
      if Q.is_empty todo then
        (* let's find any node of minimal degree *)
        let min, _ =
          H.fold (fun v d old -> Q.choose ~old (v, d)) degree ([], max_int)
        in
        match min with
        | [] -> acc
        | _ ->
          let vl = find_top_cycle checker min in
          List.iter (H.remove degree) vl;
          let acc = List.fold_left add_vertex acc vl in
          (* let v = choose_independent_vertex checker min in push v; *)
          walk acc
      else
        let v = Q.pop todo in
        let acc = add_vertex acc v in
        walk acc
    in
    G.iter_vertex
      (fun v ->
         let d = G.in_degree g v in
         if d = 0 then Q.push v todo
         else H.add degree v d)
      g;
    walk acc

  let iter f g = fold (fun v () -> f v) g ()

end


(*
Local Variables:
compile-command: "make -C .."
End:
*)