123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331openCoremoduletypeHashCmp=sigtypet[@@derivingsexp,compare,eq,hash](* val pp : Format.formatter -> t -> unit *)valto_string:t->stringendmoduletypeLattice=sigincludeHashCmpvalsubset_eq:t->t->boolendmoduletypeResult=sigincludeHashCmpvalsum:t->t->tvalprod:t->t->tvalone:tvalzero:tendmoduleIntPair=structtypet=(int*int)[@@derivingsexp,compare]lethash(t1,t2)=617*t1+619*t2endmoduleIntPairTbl=Hashtbl.Make(IntPair)moduleMake(V:HashCmp)(L:Lattice)(R:Result)=structtypev=V.t*L.t[@@derivingsexp,compare,hash]typer=R.t[@@derivingsexp,compare,hash]typed=Leafofr|Branchof{test:v;tru:int;fls:int;all_fls:int[@compare.ignore](* implies [@hash.ignore] *);}[@@derivingsexp,compare,hash](* A tree structure representing the decision diagram. The [Leaf] variant
* represents a constant function. The [Branch(v, l, t, f)] represents an
* if-then-else. When variable [v] takes on the value [l], then [t] should
* hold. Otherwise, [f] should hold.
*
* [Branch] nodes appear in an order determined first by the total order on
* the [V.t] value with with ties broken by the total order on [L.t]. The
* least such pair should appear at the root of the diagram, with each child
* nodes being strictly greater than their parent node. This invariant is
* important both for efficiency and correctness.
* *)moduleT=structtypet=int[@@derivingsexp,compare,eq]endincludeTincludeComparator.Make(T)moduleD=Frenetic_kernel.Hashcons.Make(structtypet=d[@@derivingsexp,compare,hash]end)letget=D.getletunget=D.ungetletget_uid(t:t):int=tmoduleTbl=Int.TablemoduleBinTbl=IntPairTblletmk_leafr=D.get(Leafr)letmk_branch((v,l)astest)trufls=(* When the ids of the diagrams are equal, then the diagram will take on the
same value regardless of variable assignment. The node that's being
constructed can therefore be eliminated and replaced with one of the
sub-diagrams, which are identical.
If the ids are distinct, then the node has to be constructed and assigned
a new id. *)ifequaltruflsthenflselsematchungetflswith|Branch{test=(v',_);all_fls;_}whenPoly.(v=v')->ifall_fls=truthenflselseD.get(Branch{test;tru;fls;all_fls})|_->D.get(Branch{test;tru;fls;all_fls=fls})letunchecked_cond=mk_branchletdrop=mk_leaf(R.zero)letid=mk_leaf(R.one)letrecto_stringt=ift=dropthen"0"elseift=idthen"1"elsematchD.ungettwith|Leafr->Printf.sprintf"%s"(R.to_stringr)|Branch{test=(v,l);tru=t;fls=f}->Printf.sprintf"(%s = %s ? %s : %s)"(V.to_stringv)(L.to_stringl)(to_stringt)(to_stringf)letrecfold~f~gt=matchD.ungettwith|Leafr->fr|Branch{test=(v,l);tru;fls}->g(v,l)(fold~f~gtru)(fold~f~gfls)letconstr=mk_leafrletatom(v,l)tf=mk_branch(v,l)(constt)(constf)letrecmap_r~ft=foldt~f:(funr->const(fr))~g:(fun(v,l)trufls->mk_branch(v,l)trufls)letrestrictlstu=letrecloopxsu=matchxs,D.ungetuwith|[],_|_,Leaf_->u|(v,l)::xs',Branch{test=(v',l');tru=t;fls=f}->matchV.comparevv'with|0->ifL.subset_eqll'thenloopxs'telseloopxsf|-1->loopxs'u|1->mk_branch(v',l')(loopxst)(loopxsf)|_->assertfalseinloop(List.sortlst~compare:(fun(u,_)(v,_)->V.compareuv))uletapplyfzero~(cache:(t*t,t)Hashtbl.t)=letrecsumxy=Hashtbl.find_or_addcache(x,y)~default:(fun()->sum'xy)andsum'xy=matchD.ungetx,D.ungetywith|Leafr,_->ifR.comparerzero=0thenyelsemap_r(funy->fry)y|_,Leafr->ifR.comparezeror=0thenxelsemap_r(funx->fxr)x|Branch{test=(vx,lx);tru=tx;fls=fx;all_fls=all_fls_x},Branch{test=(vy,ly);tru=ty;fls=fy;all_fls=all_fls_y}->beginmatchV.comparevxvywith|0->beginmatchL.comparelxlywith|0->mk_branch(vx,lx)(sumtxty)(sumfxfy)|-1->mk_branch(vx,lx)(sumtxall_fls_y)(sumfxy)|1->mk_branch(vy,ly)(sumall_fls_xty)(sumxfy)|_->assertfalseend|-1->mk_branch(vx,lx)(sumtxy)(sumfxy)|1->mk_branch(vy,ly)(sumxty)(sumxfy)|_->assertfalseendinsumletsum_tbl:(t*t,t)Hashtbl.t=BinTbl.create~size:1000()letsum=applyR.sumR.zero~cache:sum_tblletprod_tbl:(t*t,t)Hashtbl.t=BinTbl.create~size:1000()letprod=applyR.prodR.one~cache:prod_tblletchildreent=letreclooptacc=matchungettwith|Leaf_->acc|Branch{tru=l;fls=r}->l::r::acc|>loopl|>looprinloopt[]letclear_cache~(preserve:Int.Set.t)=(* SJS: the interface exposes `id` and `drop` as constants,
so they must NEVER be cleared from the cache *)letpreserve=Int.Set.(add(addpreservedrop)id)|>funinit->Int.Set.foldinit~init~f:(funinitroot->List.fold(childreenroot)~init~f:Int.Set.add)inbeginHashtbl.clearsum_tbl;Hashtbl.clearprod_tbl;D.clearpreserve;endletcondvtf=letokt=matchungettwith|Leaf_->true|Branch{test=(f',v')}->V.compare(fstv)f'=-1inifequaltfthentelseifokt&&okfthenmk_branchvtfelse(sum(prod(atomvR.oneR.zero)t)(prod(atomvR.zeroR.one)f))letmap~(f:R.t->t)~(g:V.t*L.t->t->t->t)(t:t):t=letrecmapt=matchungettwith|Leafr->fr|Branch{test=(v,l);tru;fls}->g(v,l)(maptru)(mapfls)inmaptletdp_map~(f:R.t->t)~(g:V.t*L.t->t->t->t)(t:t)~find_or_add:t=letrecmapt=find_or_addt~default:(fun()->map't)andmap't=matchungettwith|Leafr->fr|Branch{test=(v,l);tru;fls}->g(v,l)(maptru)(mapfls)inmaptletcompressed_size(node:t):int=letrecf(node:t)(seen:Int.Set.t)=ifInt.Set.memseennodethen(0,seen)elsematchD.ungetnodewith|Leaf_->(1,Int.Set.addseennode)|Branch{tru;fls}->(* Due to variable-ordering, there is no need to add node.id to seen
in the recursive calls *)let(tru_size,seen)=ftruseeninlet(fls_size,seen)=fflsseenin(1+tru_size+fls_size,Int.Set.addseennode)infnodeInt.Set.empty|>fstletrecuncompressed_size(node:t):int=matchD.ungetnodewith|Leaf_->1|Branch{tru;fls}->1+uncompressed_sizetru+uncompressed_sizeflsletto_dott=letopenFormatinletbuf=Buffer.create200inletfmt=formatter_of_bufferbufinletseen:Int.Hash_set.t=Int.Hash_set.create~size:10()inletrank:((V.t*L.t),Int.Hash_set.t)Hashtbl.t=Hashtbl.Poly.create~size:20()inpp_set_marginfmt(1lsl29);fprintffmt"digraph tdk {@\n";letrecloopt=ifnot(Hash_set.memseent)thenbeginHash_set.addseent;matchD.ungettwith|Leafr->fprintffmt"%d [shape=box label=\"%s\"];@\n"t(R.to_stringr)|Branch{test=(v,l);tru=a;fls=b}->(* FIXME: temporary hack to avoid Jane Street's annoying warnings. *)begin[@warning"-3"]tryHash_set.add(Hashtbl.find_exnrank(v,l))twithNot_found|Not_found_s_->lets=Int.Hash_set.create~size:10()inHash_set.addst;Hashtbl.setrank(v,l)send;fprintffmt"%d [label=\"%s = %s\"];@\n"t(V.to_stringv)(L.to_stringl);fprintffmt"%d -> %d;@\n"ta;fprintffmt"%d -> %d [style=\"dashed\"];@\n"tb;loopa;loopbendinloopt;Hashtbl.iterirank~f:(fun~key:_~data:s->fprintffmt"{rank=same; ";Hash_set.iters~f:(funx->fprintffmt"%d "x);fprintffmt";}@\n");fprintffmt"}@.";Buffer.contentsbufletrender?(format="pdf")?(title="FDD")t=Frenetic_kernel.Util.show_dot~format~title(to_dott)letrefs(t:t):Int.Set.t=letrecf(node:t)(seen:Int.Set.t)=ifInt.Set.memseennodethenseenelsematchD.ungetnodewith|Leaf_->Int.Set.addseennode|Branch{tru=hi;fls=lo}->Int.Set.add(flo(fhiseen))nodeinftInt.Set.emptyletrecnode_to_sexpnode=letopenSexplib.Sexpinmatchnodewith|Leafr->List[Atom"Leaf";R.sexp_of_tr]|Branch{test;tru;fls}->lettru=node_to_sexp@@ungettruinletfls=node_to_sexp@@ungetflsinList[Atom"Branch";sexp_of_vtest;tru;fls]letrecnode_of_sexpsexp=letopenSexplib.Sexpinmatchsexpwith|List[Atom"Leaf";sexp]->get(Leaf(R.t_of_sexpsexp))|List[Atom"Branch";test;tru;fls]->lettest=v_of_sexptestinlettru=node_of_sexptruinletfls=node_of_sexpflsinmk_branchtesttrufls|_->failwith"unsexpected s-expression!"letserialize(t:t):string=ungett|>node_to_sexp|>Sexp.to_stringletdeserialize(s:string):t=Sexp.of_strings|>node_of_sexpend