123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824(* This file is free software, part of containers. See file "license" for more details. *)(** {1 Simple Graph Interface} *)(** {2 Iter Helpers} *)type'aiter=('a->unit)->unit(** A sequence of items of type ['a], possibly infinite
@since 2.8 *)type'aiter_once='aiter(** Iter that should be used only once
@since 2.8 *)type'asequence=('a->unit)->unit(** A sequence of items of type ['a], possibly infinite
@deprecate see {!iter} instead *)[@@ocaml.deprecated"see iter"]type'asequence_once='aiter(** Iter that should be used only once
@deprecate see {!iter_once} instead *)[@@ocaml.deprecated"see iter_once"]exceptionIter_oncelet(|>)xf=fxmoduleIter=structtype'at='aiterletreturnxk=kxlet(>>=)afk=a(funx->fxk)letmapfak=a(funx->k(fx))letfilter_mapfak=a(funx->matchfxwithNone->()|Somey->ky)letiterfa=afletfoldfacca=letacc=refaccina(funx->acc:=f!accx);!accletto_listseq=fold(funaccx->x::acc)[]seq|>List.revexceptionExit_letexists_fseq=tryseq(funx->iffxthenraiseExit_);falsewithExit_->trueendmoduleSeq=Iter(** {2 Interfaces for graphs} *)(** Directed graph with vertices of type ['v] and edges labeled with [e'] *)type('v,'e)t=('v->('e*'v)iter)type('v,'e)graph=('v,'e)tletmake(f:'v->('e*'v)iter):('v,'e)t=f(** Mutable bitset for values of type ['v] *)type'vtag_set={get_tag:'v->bool;set_tag:'v->unit;(** Set tag for the given element *)}(** Mutable table with keys ['k] and values ['a] *)type('k,'a)table={mem:'k->bool;find:'k->'a;(** @raise Not_found *)add:'k->'a->unit;(** Erases previous binding *)}(** Mutable set *)type'aset=('a,unit)tableletmk_table(typek)~eq?(hash=Hashtbl.hash)size=letmoduleH=Hashtbl.Make(structtypet=kletequal=eqlethash=hashend)inlettbl=H.createsizein{mem=(funk->H.memtblk);find=(funk->H.findtblk);add=(funkv->H.replacetblkv)}letmk_map(typek)~cmp()=letmoduleM=Map.Make(structtypet=kletcompare=cmpend)inlettbl=refM.emptyin{mem=(funk->M.memk!tbl);find=(funk->M.findk!tbl);add=(funkv->tbl:=M.addkv!tbl)}(** {2 Bags} *)type'abag={push:'a->unit;is_empty:unit->bool;pop:unit->'a;(** raises some exception is empty *)}letmk_queue()=letq=Queue.create()in{push=(funx->Queue.pushxq);is_empty=(fun()->Queue.is_emptyq);pop=(fun()->Queue.popq);}letmk_stack()=lets=Stack.create()in{push=(funx->Stack.pushxs);is_empty=(fun()->Stack.is_emptys);pop=(fun()->Stack.pops);}(** Implementation from http://en.wikipedia.org/wiki/Skew_heap *)moduleHeap=structtype'at=|E|Nof'a*'at*'atletis_empty=function|E->true|N_->falseletrecunion~leqt1t2=matcht1,t2with|E,_->t2|_,E->t1|N(x1,l1,r1),N(x2,l2,r2)->ifleqx1x2thenN(x1,union~leqt2r1,l1)elseN(x2,union~leqt1r2,l2)letinsert~leqhx=union~leq(N(x,E,E))hletpop~leqh=matchhwith|E->raiseNot_found|N(x,l,r)->x,union~leqlrendletmk_heap~leq=lett=refHeap.Ein{push=(funx->t:=Heap.insert~leq!tx);is_empty=(fun()->Heap.is_empty!t);pop=(fun()->letx,h=Heap.pop~leq!tint:=h;x)}(** {2 Traversals} *)moduleTraverse=structtype('v,'e)path=('v*'e*'v)listletgeneric_tag~tags~bag~graphiter=letfirst=reftrueinfunk->(* ensure linearity *)if!firstthenfirst:=falseelseraiseIter_once;Iter.iterbag.pushiter;whilenot(bag.is_empty())doletx=bag.pop()inifnot(tags.get_tagx)then(kx;tags.set_tagx;Iter.iter(fun(_,dest)->bag.pushdest)(graphx))doneletgeneric~tbl~bag~graphiter=lettags={get_tag=tbl.mem;set_tag=(funv->tbl.addv());}ingeneric_tag~tags~bag~graphiterletbfs~tbl~graphiter=generic~tbl~bag:(mk_queue())~graphiterletbfs_tag~tags~graphiter=generic_tag~tags~bag:(mk_queue())~graphiterletdijkstra_tag?(dist=fun_->1)~tags~graphiter=lettags'={get_tag=(fun(v,_,_)->tags.get_tagv);set_tag=(fun(v,_,_)->tags.set_tagv);}anditer'=Iter.map(funv->v,0,[])iterandgraph'(v,d,p)=graphv|>Iter.map(fun(e,v')->e,(v',d+diste,(v,e,v')::p))inletbag=mk_heap~leq:(fun(_,d1,_)(_,d2,_)->d1<=d2)ingeneric_tag~tags:tags'~bag~graph:graph'iter'letdijkstra~tbl?dist~graphiter=lettags={get_tag=tbl.mem;set_tag=(funv->tbl.addv());}indijkstra_tag~tags?dist~graphiterletdfs~tbl~graphiter=generic~tbl~bag:(mk_stack())~graphiterletdfs_tag~tags~graphiter=generic_tag~tags~bag:(mk_stack())~graphitermoduleEvent=structtypeedge_kind=[`Forward|`Back|`Cross](** A traversal is a iteruence of such events *)type('v,'e)t=[`Enterof'v*int*('v,'e)path(* unique index in traversal, path from start *)|`Exitof'v|`Edgeof'v*'e*'v*edge_kind]letget_vertex=function|`Enter(v,_,_)->Some(v,`Enter)|`Exitv->Some(v,`Exit)|`Edge_->Noneletget_enter=function|`Enter(v,_,_)->Somev|`Exit_|`Edge_->Noneletget_exit=function|`Exitv->Somev|`Enter_|`Edge_->Noneletget_edge=function|`Edge(v1,e,v2,_)->Some(v1,e,v2)|`Enter_|`Exit_->Noneletget_edge_kind=function|`Edge(v,e,v',k)->Some(v,e,v',k)|`Enter_|`Exit_->None(* is [v] the origin of some edge in [path]? *)letreclist_mem_~eq~graphvpath=matchpathwith|[]->false|(v1,_,_)::path'->eqvv1||list_mem_~eq~graphvpath'letdfs_tag~eq~tags~graphiter=letfirst=reftrueinfunk->if!firstthenfirst:=falseelseraiseIter_once;letbag=mk_stack()inletn=ref0inIter.iter(funv->(* start DFS from this vertex *)bag.push(`Enter(v,[]));whilenot(bag.is_empty())domatchbag.pop()with|`Enter(v,path)->ifnot(tags.get_tagv)then(letnum=!ninincrn;tags.set_tagv;k(`Enter(v,num,path));bag.push(`Exitv);Iter.iter(fun(e,v')->bag.push(`Edge(v,e,v',(v,e,v')::path)))(graphv);)|`Exitx->k(`Exitx)|`Edge(v,e,v',path)->letedge_kind=iftags.get_tagv'theniflist_mem_~eq~graphv'paththen`Backelse`Crosselse(bag.push(`Enter(v',path));`Forward)ink(`Edge(v,e,v',edge_kind))done)iterletdfs~tbl~eq~graphiter=lettags={set_tag=(funv->tbl.addv());get_tag=tbl.mem;}indfs_tag~eq~tags~graphiterend(*$R
let l =
let tbl = mk_table ~eq:CCInt.equal 128 in
Traverse.Event.dfs ~tbl ~eq:CCInt.equal ~graph:divisors_graph (Iter.return 345614)
|> Iter.to_list in
let expected =
[`Enter (345614, 0, []); `Edge (345614, (), 172807, `Forward);
`Enter (172807, 1, [(345614, (), 172807)]); `Edge (172807, (), 1, `Forward);
`Enter (1, 2, [(172807, (), 1); (345614, (), 172807)]); `Exit 1; `Exit 172807;
`Edge (345614, (), 2, `Forward); `Enter (2, 3, [(345614, (), 2)]);
`Edge (2, (), 1, `Cross); `Exit 2; `Edge (345614, (), 1, `Cross);
`Exit 345614]
in
assert_equal expected l
*)end(** {2 Cycles} *)letis_dag~tbl~eq~graphvs=Traverse.Event.dfs~tbl~eq~graphvs|>Iter.exists_(function|`Edge(_,_,_,`Back)->true|_->false)(** {2 Topological Sort} *)exceptionHas_cyclelettopo_sort_tag~eq?(rev=false)~tags~graphiter=(* use DFS *)letl=Traverse.Event.dfs_tag~eq~tags~graphiter|>Iter.filter_map(function|`Exitv->Somev|`Edge(_,_,_,`Back)->raiseHas_cycle|`Enter_|`Edge_->None)|>Iter.fold(funaccx->x::acc)[]inifrevthenList.revlelsellettopo_sort~eq?rev~tbl~graphiter=lettags={get_tag=tbl.mem;set_tag=(funv->tbl.addv());}intopo_sort_tag~eq?rev~tags~graphiter(*$T
let tbl = mk_table ~eq:CCInt.equal 128 in \
let l = topo_sort ~eq:CCInt.equal ~tbl ~graph:divisors_graph (Iter.return 42) in \
List.for_all (fun (i,j) -> \
let idx_i = CCList.find_idx ((=)i) l |> CCOpt.get_exn |> fst in \
let idx_j = CCList.find_idx ((=)j) l |> CCOpt.get_exn |> fst in \
idx_i < idx_j) \
[ 42, 21; 14, 2; 3, 1; 21, 7; 42, 3]
let tbl = mk_table ~eq:CCInt.equal 128 in \
let l = topo_sort ~eq:CCInt.equal ~rev:true ~tbl ~graph:divisors_graph (Iter.return 42) in \
List.for_all (fun (i,j) -> \
let idx_i = CCList.find_idx ((=)i) l |> CCOpt.get_exn |> fst in \
let idx_j = CCList.find_idx ((=)j) l |> CCOpt.get_exn |> fst in \
idx_i > idx_j) \
[ 42, 21; 14, 2; 3, 1; 21, 7; 42, 3]
*)(** {2 Lazy Spanning Tree} *)moduleLazy_tree=structtype('v,'e)t={vertex:'v;children:('e*('v,'e)t)listLazy.t;}letmake_vertexchildren={vertex;children}letrecmap_vf{vertex=v;children=l}=letl'=lazy(List.map(fun(e,child)->e,map_vfchild)(Lazy.forcel))inmake_(fv)l'letrecfold_vfacc{vertex=v;children=l}=letacc=faccvinList.fold_left(funacc(_,t')->fold_vfacct')acc(Lazy.forcel)endletspanning_tree_tag~tags~graphv=letrecmk_nodev=letchildren=lazy(Iter.fold(funacc(e,v')->iftags.get_tagv'thenaccelse(tags.set_tagv';(e,mk_nodev')::acc))[](graphv))inLazy_tree.make_vchildreninmk_nodevletspanning_tree~tbl~graphv=lettags={get_tag=tbl.mem;set_tag=(funv->tbl.addv());}inspanning_tree_tag~tags~graphv(** {2 Strongly Connected Components} *)moduleSCC=structtype'vstate={mutablemin_id:int;(* min ID of the vertex' scc *)id:int;(* ID of the vertex *)mutableon_stack:bool;mutablevertex:'v;}letmk_cellvn={min_id=n;id=n;on_stack=false;vertex=v;}(* pop elements of [stack] until we reach node with given [id] *)letrecpop_down_to~idaccstack=assert(not(Stack.is_emptystack));letcell=Stack.popstackincell.on_stack<-false;ifcell.id=idthen(assert(cell.id=cell.min_id);cell.vertex::acc(* return SCC *))elsepop_down_to~id(cell.vertex::acc)stackletexplore~tbl~graphiter=letfirst=reftrueinfunk->if!firstthenfirst:=falseelseraiseIter_once;(* stack of nodes being explored, for the DFS *)letto_explore=Stack.create()in(* stack for Tarjan's algorithm itself *)letstack=Stack.create()in(* unique ID *)letn=ref0in(* exploration *)Iter.iter(funv->Stack.push(`Enterv)to_explore;whilenot(Stack.is_emptyto_explore)domatchStack.popto_explorewith|`Enterv->ifnot(tbl.memv)then((* remember unique ID for [v] *)letid=!ninincrn;letcell=mk_cellvidincell.on_stack<-true;tbl.addvcell;Stack.pushcellstack;Stack.push(`Exit(v,cell))to_explore;(* explore children *)Iter.iter(fun(_,v')->Stack.push(`Enterv')to_explore)(graphv))|`Exit(v,cell)->(* update [min_id] *)assertcell.on_stack;Iter.iter(fun(_,dest)->(* must not fail, [dest] already explored *)letdest_cell=tbl.finddestin(* same SCC? yes if [dest] points to [cell.v] *)ifdest_cell.on_stackthencell.min_id<-mincell.min_iddest_cell.min_id)(graphv);(* pop from stack if SCC found *)ifcell.id=cell.min_idthen(letscc=pop_down_to~id:cell.id[]stackinkscc)done)iter;assert(Stack.is_emptystack);()endtype'vscc_state='vSCC.stateletscc~tbl~graphiter=SCC.explore~tbl~graphiter(* example from https://en.wikipedia.org/wiki/Strongly_connected_component *)(*$R
let set_eq ?(eq=(=)) l1 l2 = CCList.subset ~eq l1 l2 && CCList.subset ~eq l2 l1 in
let graph = of_list ~eq:CCString.equal
[ "a", "b"
; "b", "e"
; "e", "a"
; "b", "f"
; "e", "f"
; "f", "g"
; "g", "f"
; "b", "c"
; "c", "g"
; "c", "d"
; "d", "c"
; "d", "h"
; "h", "d"
; "h", "g"
] in
let tbl = mk_table ~eq:CCString.equal 128 in
let res = scc ~tbl ~graph (Iter.return "a") |> Iter.to_list in
assert_bool "scc"
(set_eq ~eq:(set_eq ?eq:None) res
[ [ "a"; "b"; "e" ]
; [ "f"; "g" ]
; [ "c"; "d"; "h" ]
]
)
*)(** {2 Pretty printing in the DOT (graphviz) format} *)moduleDot=structtypeattribute=[|`Colorofstring|`Shapeofstring|`Weightofint|`Styleofstring|`Labelofstring|`Otherofstring*string](** Dot attribute *)letpp_listpp_xoutl=Format.pp_print_stringout"[";List.iteri(funix->ifi>0thenFormat.fprintfout",@;";pp_xoutx)l;Format.pp_print_stringout"]"typevertex_state={mutableexplored:bool;id:int;}(** Print an enum of Full.traverse_event *)letpp_all~tbl~eq?(attrs_v=fun_->[])?(attrs_e=fun_->[])?(name="graph")~graphoutiter=(* print an attribute *)letpp_attroutattr=matchattrwith|`Colorc->Format.fprintfout"color=%s"c|`Shapes->Format.fprintfout"shape=%s"s|`Weightw->Format.fprintfout"weight=%d"w|`Styles->Format.fprintfout"style=%s"s|`Labell->Format.fprintfout"label=\"%s\""l|`Other(name,value)->Format.fprintfout"%s=\"%s\""namevalue(* map from vertices to integers *)andget_node=letcount=ref0infunv->trytbl.findvwithNot_found->letnode={id=!count;explored=false}inincrcount;tbl.addvnode;nodeandvertex_exploredv=try(tbl.findv).exploredwithNot_found->falseinletset_exploredv=(get_nodev).explored<-trueandget_idv=(get_nodev).idin(* the unique name of a vertex *)letpp_vertexoutv=Format.fprintfout"vertex_%d"(get_idv)in(* print preamble *)Format.fprintfout"@[<v2>digraph \"%s\" {@;"name;(* traverse *)lettags={get_tag=vertex_explored;set_tag=set_explored;(* allocate new ID *)}inletevents=Traverse.Event.dfs_tag~eq~tags~graphiterinIter.iter(function|`Enter(v,_n,_path)->letattrs=attrs_vvinFormat.fprintfout"@[<h>%a %a;@]@,"pp_vertexv(pp_listpp_attr)attrs|`Exit_->()|`Edge(v1,e,v2,_)->letattrs=attrs_eeinFormat.fprintfout"@[<h>%a -> %a %a;@]@,"pp_vertexv1pp_vertexv2(pp_listpp_attr)attrs)events;(* close *)Format.fprintfout"}@]@;@?";()letpp_seq=pp_allletpp~tbl~eq?attrs_v?attrs_e?name~graphfmtv=pp_all~tbl~eq?attrs_v?attrs_e?name~graphfmt(Iter.returnv)letwith_outfilenamef=letoc=open_outfilenameintryletfmt=Format.formatter_of_out_channelocinletx=ffmtinFormat.pp_print_flushfmt();close_outoc;xwithe->close_outoc;raiseeend(** {2 Mutable Graph} *)type('v,'e)mut_graph={graph:('v,'e)t;add_edge:'v->'e->'v->unit;remove:'v->unit;}letmk_mut_tbl(typek)~eq?(hash=Hashtbl.hash)size=letmoduleTbl=Hashtbl.Make(structtypet=klethash=hashletequal=eqend)inlettbl=Tbl.createsizein{graph=(funvyield->tryList.iteryield(Tbl.findtblv)withNot_found->());add_edge=(funv1ev2->letl=tryTbl.findtblv1withNot_found->[]inTbl.replacetblv1((e,v2)::l));remove=(funv->Tbl.removetblv);}(** {2 Immutable Graph} *)moduletypeMAP=sigtypevertextype'atvalas_graph:'at->(vertex,'a)graph(** Graph view of the map. *)valempty:'atvaladd_edge:vertex->'a->vertex->'at->'atvalremove_edge:vertex->vertex->'at->'atvaladd:vertex->'at->'at(** Add a vertex, possibly with no outgoing edge. *)valremove:vertex->'at->'at(** Remove the vertex and all its outgoing edges.
Edges that point to the vertex are {b NOT} removed, they must be
manually removed with {!remove_edge}. *)valunion:'at->'at->'atvalvertices:_t->vertexitervalvertices_l:_t->vertexlistvalof_list:(vertex*'a*vertex)list->'atvaladd_list:(vertex*'a*vertex)list->'at->'atvalto_list:'at->(vertex*'a*vertex)listvalof_iter:(vertex*'a*vertex)iter->'at(** @since 2.8 *)valadd_iter:(vertex*'a*vertex)iter->'at->'at(** @since 2.8 *)valto_iter:'at->(vertex*'a*vertex)iter(** @since 2.8 *)valof_seq:(vertex*'a*vertex)iter->'at(** @deprecated use {!of_iter} instead *)valadd_seq:(vertex*'a*vertex)iter->'at->'at(** @deprecated use {!add_iter} instead *)valto_seq:'at->(vertex*'a*vertex)iter(** @deprecated use {!to_iter} instead *)endmoduleMap(O:Map.OrderedType):MAPwithtypevertex=O.t=structmoduleM=Map.Make(O)typevertex=O.ttype'at='aM.tM.t(* vertex -> set of (vertex * label) *)letas_graphm=(funvyield->tryletsub=M.findvminM.iter(funv'e->yield(e,v'))subwithNot_found->())letempty=M.emptyletadd_edgev1ev2m=letsub=tryM.findv1mwithNot_found->M.emptyinM.addv1(M.addv2esub)mletremove_edgev1v2m=tryletmap=M.removev2(M.findv1m)inifM.is_emptymapthenM.removev1melseM.addv1mapmwithNot_found->mletaddvm=ifM.memvmthenmelseM.addvM.emptymletremovevm=M.removevmletunionm1m2=M.merge(fun_s1s2->matchs1,s2with|Somes,None|None,Somes->Somes|None,None->assertfalse|Somes1,Somes2->lets=M.merge(fun_e1e2->matche1,e2with|Some_,_->e1|None,_->e2)s1s2inSomes)m1m2letverticesmyield=M.iter(funv_->yieldv)mletvertices_lm=M.fold(funv_acc->v::acc)m[]letadd_listlm=List.fold_left(funm(v1,e,v2)->add_edgev1ev2m)mlletof_listl=add_listlemptyletto_listm=M.fold(funvmapacc->M.fold(funv'eacc->(v,e,v')::acc)mapacc)m[]letadd_iteriterm=Iter.fold(funm(v1,e,v2)->add_edgev1ev2m)miterletof_iteriter=add_iteriteremptyletto_itermk=M.iter(funvmap->M.iter(funv'e->k(v,e,v'))map)mletadd_seq=add_iterletof_seq=of_iterletto_seq=to_iterend(** {2 Misc} *)letof_list~eql=(funvyield->List.iter(fun(a,b)->ifeqavthenyield((),b))l)letof_funf=(funvyield->letl=fvinList.iter(funv'->yield((),v'))l)letof_hashtbltbl=(funvyield->tryList.iter(funb->yield((),b))(Hashtbl.findtblv)withNot_found->())letdivisors_graph=(funi->(* divisors of [i] that are [>= j] *)letrecdivisorsjiyield=ifj<ithen(if(imodj=0)thenyield((),j);divisors(j+1)iyield)indivisors1i)