ocaml/utils/strongly_connected_componen...

201 lines
6.2 KiB
OCaml

(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Pierre Chambart, OCamlPro *)
(* Mark Shinwell and Leo White, Jane Street Europe *)
(* *)
(* Copyright 2013--2016 OCamlPro SAS *)
(* Copyright 2014--2016 Jane Street Group LLC *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
module Int = Numbers.Int
module Kosaraju : sig
type component_graph =
{ sorted_connected_components : int list array;
component_edges : int list array;
}
val component_graph : int list array -> component_graph
end = struct
let transpose graph =
let size = Array.length graph in
let transposed = Array.make size [] in
let add src dst = transposed.(src) <- dst :: transposed.(src) in
Array.iteri (fun src dsts -> List.iter (fun dst -> add dst src) dsts)
graph;
transposed
let depth_first_order (graph : int list array) : int array =
let size = Array.length graph in
let marked = Array.make size false in
let stack = Array.make size ~-1 in
let pos = ref 0 in
let push i =
stack.(!pos) <- i;
incr pos
in
let rec aux node =
if not marked.(node)
then begin
marked.(node) <- true;
List.iter aux graph.(node);
push node
end
in
for i = 0 to size - 1 do
aux i
done;
stack
let mark order graph =
let size = Array.length graph in
let graph = transpose graph in
let marked = Array.make size false in
let id = Array.make size ~-1 in
let count = ref 0 in
let rec aux node =
if not marked.(node)
then begin
marked.(node) <- true;
id.(node) <- !count;
List.iter aux graph.(node)
end
in
for i = size - 1 downto 0 do
let node = order.(i) in
if not marked.(node)
then begin
aux order.(i);
incr count
end
done;
id, !count
let kosaraju graph =
let dfo = depth_first_order graph in
let components, ncomponents = mark dfo graph in
ncomponents, components
type component_graph =
{ sorted_connected_components : int list array;
component_edges : int list array;
}
let component_graph graph =
let ncomponents, components = kosaraju graph in
let id_scc = Array.make ncomponents [] in
let component_graph = Array.make ncomponents Int.Set.empty in
let add_component_dep node set =
let node_deps = graph.(node) in
List.fold_left (fun set dep -> Int.Set.add components.(dep) set)
set node_deps
in
Array.iteri (fun node component ->
id_scc.(component) <- node :: id_scc.(component);
component_graph.(component) <-
add_component_dep node (component_graph.(component)))
components;
{ sorted_connected_components = id_scc;
component_edges = Array.map Int.Set.elements component_graph;
}
end
module type S = sig
module Id : Identifiable.S
type directed_graph = Id.Set.t Id.Map.t
type component =
| Has_loop of Id.t list
| No_loop of Id.t
val connected_components_sorted_from_roots_to_leaf
: directed_graph
-> component array
val component_graph : directed_graph -> (component * int list) array
end
module Make (Id : Identifiable.S) = struct
type directed_graph = Id.Set.t Id.Map.t
type component =
| Has_loop of Id.t list
| No_loop of Id.t
(* Ensure that the dependency graph does not have external dependencies. *)
(* Note: this function is currently not used. *)
let _check dependencies =
Id.Map.iter (fun id set ->
Id.Set.iter (fun v ->
if not (Id.Map.mem v dependencies)
then
Misc.fatal_errorf "Strongly_connected_components.check: the \
graph has external dependencies (%a -> %a)"
Id.print id Id.print v)
set)
dependencies
type numbering = {
back : int Id.Map.t;
forth : Id.t array;
}
let number graph =
let size = Id.Map.cardinal graph in
let bindings = Id.Map.bindings graph in
let a = Array.of_list bindings in
let forth = Array.map fst a in
let back =
let back = ref Id.Map.empty in
for i = 0 to size - 1 do
back := Id.Map.add forth.(i) i !back;
done;
!back
in
let integer_graph =
Array.init size (fun i ->
let _, dests = a.(i) in
Id.Set.fold (fun dest acc ->
let v =
try Id.Map.find dest back
with Not_found ->
Misc.fatal_errorf
"Strongly_connected_components: missing dependency %a"
Id.print dest
in
v :: acc)
dests [])
in
{ back; forth }, integer_graph
let component_graph graph =
let numbering, integer_graph = number graph in
let { Kosaraju. sorted_connected_components;
component_edges } =
Kosaraju.component_graph integer_graph
in
Array.mapi (fun component nodes ->
match nodes with
| [] -> assert false
| [node] ->
(if List.mem node integer_graph.(node)
then Has_loop [numbering.forth.(node)]
else No_loop numbering.forth.(node)),
component_edges.(component)
| _::_ ->
(Has_loop (List.map (fun node -> numbering.forth.(node)) nodes)),
component_edges.(component))
sorted_connected_components
let connected_components_sorted_from_roots_to_leaf graph =
Array.map fst (component_graph graph)
end