123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215(**************************************************************************)(* *)(* 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. *)(* *)(**************************************************************************)(* Basic operations over graphs *)moduletypeS=sigtypegvaltransitive_closure:?reflexive:bool->g->gvaladd_transitive_closure:?reflexive:bool->g->gvaltransitive_reduction:?reflexive:bool->g->gvalreplace_by_transitive_reduction:?reflexive:bool->g->gvalmirror:g->gvalcomplement:g->gvalintersect:g->g->gvalunion:g->g->gendmoduleMake(B:Builder.S)=structopenB(* Roy-Warshall's algorithm *)typeg=G.tletadd_transitive_closure?(reflexive=false)g0=letphivg=letg=ifreflexivethenB.add_edgegvvelseginG.fold_succ(funsvg->G.fold_pred(funpvg->B.add_edgegpvsv)gvg)gvginG.fold_vertexphig0g0lettransitive_closure?(reflexive=false)g0=add_transitive_closure~reflexive(B.copyg0)letmirrorg=ifG.is_directedthenbeginletg'=G.fold_vertex(funvg'->B.add_vertexg'v)g(B.empty())inG.fold_edges_e(funeg'->letv1=G.E.srceinletv2=G.E.dsteinB.add_edge_eg'(G.E.createv2(G.E.labele)v1))gg'endelsegletcomplementg=G.fold_vertex(funvg'->G.fold_vertex(funwg'->ifG.mem_edgegvwtheng'elseB.add_edgeg'vw)gg')g(B.empty())letintersectg1g2=G.fold_vertex(funvg->ifG.mem_vertexg2vthenG.fold_succ_e(funeg->ifG.mem_edge_eg2ethenB.add_edge_egeelseg)g1v(B.add_vertexgv)else(* [v] not in [g2] *)g)g1(B.empty())letuniong1g2=letaddg1g2=(* add the graph [g1] in [g2] *)G.fold_vertex(funvg->G.fold_succ_e(funeg->B.add_edge_ege)g1v(B.add_vertexgv))g1g2inaddg1(B.copyg2)(* source: tred.c from Graphviz
time and space O(VE) *)letreplace_by_transitive_reduction?(reflexive=false)g=letmoduleH=Hashtbl.Make(G.V)inletreducegv0=(* runs a DFS from v0 and records the length (=1 or >1) of paths from
v0 for reachable vertices *)letnv=G.nb_vertexginletdist=H.createnvinG.iter_vertex(funw->H.adddistw0)g;letupdatevw=H.replacedistw(1+min1(H.finddistv))inletonstack=H.createnvinletpushvst=H.replaceonstackv();(v,G.succgv)::stinletrecdfs=function|[]->()|(v,[])::st->H.removeonstackv;dfsst|(v,w::sv)::stwhenG.V.equalwv||H.memonstackw->dfs((v,sv)::st)|(v,w::sv)::st->ifH.finddistw=0then(updatevw;dfs(pushw((v,sv)::st)))else(ifH.finddistw=1thenupdatevw;dfs((v,sv)::st))indfs(pushv0[]);(* then delete any edge v0->v when the distance for v is >1 *)letdeletegv=ifG.V.equalvv0&&reflexive||H.finddistv>1thenB.remove_edgegv0velseginletsv0=G.fold_succ(funvsv0->v::sv0)gv0[]in(* CAVEAT: iterate *then* modify *)List.fold_leftdeletegsv0in(* run the above from any vertex *)letvl=G.fold_vertex(funvvl->v::vl)g[]in(* CAVEAT: iterate *then* modify *)List.fold_leftreducegvllettransitive_reduction?(reflexive=false)g0=replace_by_transitive_reduction~reflexive(B.copyg0)endmoduleP(G:Sig.P)=Make(Builder.P(G))moduleI(G:Sig.I)=Make(Builder.I(G))moduleChoose(G:sigtypettypevertextypeedgevaliter_vertex:(vertex->unit)->t->unitvaliter_edges_e:(edge->unit)->t->unitend)=structexceptionFound_VertexofG.vertexletchoose_vertexg=tryG.iter_vertex(funv->raise(Found_Vertexv))g;invalid_arg"choose_vertex"withFound_Vertexv->vexceptionFound_EdgeofG.edgeletchoose_edgeg=tryG.iter_edges_e(funv->raise(Found_Edgev))g;invalid_arg"choose_vertex"withFound_Edgev->vendmoduleNeighbourhood(G:sigtypetmoduleV:Sig.COMPARABLEvalfold_succ:(V.t->'a->'a)->t->V.t->'a->'avalsucc:t->V.t->V.tlistend)=structmoduleVertex_Set=Set.Make(G.V)letset_from_vertexgv=G.fold_succ(funv's->ifG.V.equalvv'thenselseVertex_Set.addv's)gvVertex_Set.emptyletlist_from_vertexgv=letrecaux=function|[]->[]|v'::l->ifG.V.equalvv'thenbeginassert(not(List.exists(G.V.equalv)l));lendelsev'::auxlinaux(G.succgv)letset_from_verticesgl=letfold_leftf=List.fold_leftfVertex_Set.emptylinletenv_init=fold_left(funsv->Vertex_Set.addvs)inletaddxs=ifVertex_Set.memxenv_initthenselseVertex_Set.addxsinfold_left(funsv->G.fold_succaddgvs)letlist_from_verticesgl=Vertex_Set.elements(set_from_verticesgl)end