1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753(**************************************************************************)(* This file is part of the Codex semantics library. *)(* *)(* Copyright (C) 2013-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It 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. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE). *)(* *)(**************************************************************************)(** Possible implementations of conditions for the terms. *)moduleBed=Bdd.Make(structtypet=intletcompareab=(a-b)letpretty=Format.pp_print_intlethashx=xletequalab=a==bend)moduleConditionMy=structincludeBed.BDD;;letvar_count=ref0;;letfresh()=incrvar_count;!var_count;;letall=oneletdisjointab=((&&~)ab)==zeroletempty=zeroletis_includedab=(==>~)ab==oneletinter=(&&~)letunion=(||~)letis_emptyx=x==zeroletis_zerox=x==zeroletis_onex=x==oneletcomplementx=(!~)xletvar()=Bed.BDD.var@@fresh()end(* https://c-cube.github.io/ocaml-minisat/0.1/Minisat.html *)(* https://en.wikipedia.org/wiki/Tseytin_transformation *)(* https://github.com/c-cube/ocaml-minisat *)(* module ConditionMinisat = struct *)(* (\* Not very efficient. *)(* I should maybe try with SateLite or Minisat2, or techniques to minimize *)(* clauses in http://minisat.se/downloads/synth_in_sat.pdf, *)(* or do some BDD sweeping... *)(* + The minisat bindings I use are not very good; for instance they *)(* did not know that some literals were used that had the value true *)(* and false. *)(* *\) *)(* module Base = struct *)(* let state = Minisat.create();; *)(* (\* Minisat.set_verbose state 33;; *\) *)(* let add_clause state c = *)(* (\* Kernel.feedback "adding clause %a" Minisat.pp_clause (Array.to_list c); *\) *)(* Minisat.add_clause_a state c *)(* ;; *)(* let solve from state ~assumptions = *)(* (\* (match assumptions with *\) *)(* (\* | [| a;b|] -> Kernel.feedback "assumptions %s %a %a" from Minisat.Lit.pp a Minisat.Lit.pp b *\) *)(* (\* | [| a|] -> Kernel.feedback "assumptions %s %a" from Minisat.Lit.pp a *\) *)(* (\* | _ -> assert false); *\) *)(* Minisat.solve state ~assumptions *)(* ;; *)(* type t = Minisat.Lit.t *)(* (\* The first are actually reserved... *\) *)(* let count = ref 2;; *)(* let fresh() = incr count; Minisat.Raw.set_nvars state (10 + !count); Minisat.Lit.make @@ !count;; *)(* let true_ = *)(* let v = fresh() in *)(* add_clause state [| v |]; *)(* Minisat.simplify state; *)(* (\* Kernel.feedback "After true: value = %s" @@ (match Minisat.value state v with *\) *)(* (\* | Minisat.V_true -> "true" *\) *)(* (\* | Minisat.V_false -> "false" *\) *)(* (\* | Minisat.V_undef -> "undef"); *\) *)(* (\* assert (is_true v); *\) *)(* v *)(* ;; *)(* let false_ = *)(* let v = fresh() in *)(* add_clause state [| Minisat.Lit.neg v |]; *)(* (\* assert (is_false v); *\) *)(* v *)(* ;; *)(* let not_ x = Minisat.Lit.neg x;; *)(* let (&&~) a b = *)(* let c = fresh() in *)(* (\* Kernel.feedback "%a = %a || %a" Minisat.Lit.pp c Minisat.Lit.pp a Minisat.Lit.pp b; *\) *)(* let nc = Minisat.Lit.neg c in *)(* add_clause state [| Minisat.Lit.neg a; Minisat.Lit.neg b; c |]; *)(* add_clause state [| a; nc |]; *)(* add_clause state [| b; nc |]; *)(* c *)(* ;; *)(* let (||~) a b = *)(* let c = fresh() in *)(* (\* Kernel.feedback "%a = %a || %a" Minisat.Lit.pp c Minisat.Lit.pp a Minisat.Lit.pp b; *\) *)(* add_clause state [| a; b; Minisat.Lit.neg c |]; *)(* add_clause state [| Minisat.Lit.neg a; c |]; *)(* add_clause state [| Minisat.Lit.neg b; c |]; *)(* c *)(* ;; *)(* (\* TODO: We can simplify true/false directly here. *\) *)(* module Hashcons = Hashtbl.Make(struct *)(* type t = Minisat.Lit.t * Minisat.Lit.t *)(* let hash (a,b) = (lxor) (Obj.magic a) (Obj.magic b) ;; *)(* let equal (a,b) (c,d) = (a == c && b == d) || (a == d) && (b == c);; *)(* end) *)(* let and_h = Hashtbl.create 170;; *)(* let or_h = Hashtbl.create 170;; *)(* let (&&~) a b = *)(* try let c = Hashtbl.find and_h (a,b) in (\* Kernel.feedback "hit for %a" Minisat.Lit.pp c; *\) c *)(* with Not_found -> *)(* let r = (&&~) a b in *)(* Hashtbl.replace and_h (a,b) r; *)(* r *)(* ;; *)(* let (||~) a b = *)(* try let c = Hashtbl.find or_h (a,b) in (\* Kernel.feedback "hit for %a" Minisat.Lit.pp c; *\) c *)(* with Not_found -> *)(* let r = (||~) a b in *)(* Hashtbl.replace or_h (a,b) r; *)(* r *)(* ;; *)(* (\* Some simplifications. *\) *)(* let (&&~) a b = *)(* if a == true_ then b *)(* else if a == false_ then false_ *)(* else if b == true_ then a *)(* else if b == false_ then false_ *)(* else (&&~) a b *)(* ;; *)(* let (||~) a b = *)(* if a == true_ then true_ *)(* else if a == false_ then b *)(* else if b == true_ then true_ *)(* else if b == false_ then a *)(* else (||~) a b *)(* ;; *)(* let (&&~) a b = *)(* if a == b then a else (&&~) a b *)(* let (||~) a b = *)(* if a == b then a else (||~) a b *)(* let is_false x = (Minisat.value state x == Minisat.V_false);; *)(* let is_true x = (Minisat.value state x == Minisat.V_true);; *)(* let is_true x = *)(* let assumptions = [| Minisat.Lit.neg x |] in *)(* try solve "is_true" state ~assumptions; false *)(* with Minisat.Unsat -> true *)(* ;; *)(* let is_false x = *)(* let assumptions = [| x |] in *)(* try solve "is_false" state ~assumptions; false *)(* with Minisat.Unsat -> true *)(* ;; *)(* let is_false_h = Hashtbl.create 170;; *)(* let is_false x = *)(* try Hashtbl.find is_false_h x *)(* with Not_found -> *)(* let r = is_false x in *)(* Hashtbl.replace is_false_h x r; *)(* r *)(* ;; *)(* let is_true_h = Hashtbl.create 170;; *)(* let is_true x = *)(* try Hashtbl.find is_true_h x *)(* with Not_found -> *)(* let r = is_true x in *)(* Hashtbl.replace is_true_h x r; *)(* r *)(* ;; *)(* (\* TODO: cache the results of the && in disjoint? *\) *)(* (\* Or maybe, just try to see if solving a and b is exact. *\) *)(* let disjoint a b = is_false (a &&~ b) *)(* (\* let disjoint a b = *\) *)(* (\* let assumptions = [|a;b|] in *\) *)(* (\* try solve state ~assumptions; false *\) *)(* (\* with Minisat.Unsat -> true *\) *)(* (\* ;; *\) *)(* let implies a b = *)(* let assumptions = [|a;Minisat.Lit.neg b |] in *)(* try solve "implies" state ~assumptions; false *)(* with Minisat.Unsat -> true *)(* ;; *)(* let pretty = Minisat.Lit.pp *)(* let equal a b = a == b *)(* end *)(* (\* Note: no need to have t = ...: I could compare to the values of true_ and false_. *\) *)(* module Expr = struct *)(* type t = True | False | Var of Base.t *)(* let pretty fmt x = match x with *)(* | True -> Format.fprintf fmt "true" *)(* | False -> Format.fprintf fmt "false" *)(* | Var x -> Base.pretty fmt x *)(* let false_ = False *)(* let true_ = True *)(* let is_true = function *)(* | True -> true *)(* | False -> false *)(* | Var x -> Base.is_true x *)(* let is_false = function *)(* | True -> false *)(* | False -> true *)(* | Var x -> Base.is_false x *)(* let not_ = function *)(* | True -> False *)(* | False -> True *)(* | Var x -> Var (Base.not_ x) *)(* let (||~) a b = match (a,b) with *)(* | True, _ | _, True -> True *)(* | False, x | x, False -> x *)(* | Var a, Var b -> Var (Base.(||~) a b) *)(* let (&&~) a b = match (a,b) with *)(* | False, _ | _, False -> False *)(* | True, x | x, True -> x *)(* | Var a, Var b -> Var (Base.(&&~) a b) *)(* let implies a b = match (a,b) with *)(* | False, _ -> true *)(* | _, True -> true *)(* | True, False -> false *)(* | Var a, False -> Base.is_false a *)(* | True, Var b -> Base.is_true b *)(* | Var a, Var b -> Base.implies a b *)(* ;; *)(* let disjoint a b = match (a,b) with *)(* | False, _ -> true *)(* | _, False -> true *)(* | True, True -> false *)(* | True, Var a | Var a, True -> Base.is_false a *)(* | Var a, Var b -> Base.disjoint a b *)(* let fresh() = Var(Base.fresh()) *)(* let equal = (=) *)(* end *)(* (\* include Base *\) *)(* include Expr *)(* let is_included = implies;; *)(* let complement = not_ *)(* let union = (||~) *)(* let inter = (&&~) *)(* let empty = false_ *)(* let zero = false_ *)(* let all = true_ *)(* let one = true_ *)(* let is_empty = is_false *)(* let is_zero = is_false *)(* let is_one = is_true *)(* let (!~) = not_ *)(* let equal a b = *)(* implies a b && implies b a *)(* ;; *)(* let var = fresh;; *)(* let hash = Hashtbl.hash *)(* end *)(** This uses the structure of the dominator tree as conditions. It
observes the fact that we never perform intersection on arbitrary
conditions, as conditions represent set of paths, we either assume
a new condition or join existing ones.
This works together with skiplist.ml (fast implementation of useful
algorithms like online nearest_common_ancestor) and treemap.ml
(mapping from tree relations to lattices).
It is a good candidate for introducing scopes and getting rid of
Cudd. Still, when I tested these conditions with
Domain_non_relational, it worked for most benchmarks, but the
performance gains were not fantastic compared to cudd (sometimes
less time, some times more; sometimes a lot less memory, but
sometimes more). *)moduleConditionDom=struct(* least significant bit is 0 if positive, 1 otherwise. *)moduleLiteral=structtypet=intletvar_count=ref0;;letfresh()=var_count:=!var_count+2;!var_count;;letprettyfmtx=ifxland1==1thenFormat.fprintffmt"%d"(x/2)elseFormat.fprintffmt"!%d"(x/2)letequal=(==)letcomplementx=ifxland1==1thenxland(lnot1)elsexlor1lethashx=xend;;(* A path is a path in a domination tree. *)modulePath=structtypet'=|Root|Appendoft*Literal.t(* Append a path with a literal. *)(* Join node of a and b; the immediate dominator of the join
node is the common ancestor of a and b. *)|Joinof{a:t;b:t;ancestor:t}andt={id:int;content:t';depth:int}(* Hash-consed version. *);;letroot={id=0;content=Root;depth=0};;letrecprettyfmt=function|xwhenx==root->Format.fprintffmt"root"|{content=Append(p,l)}->Format.fprintffmt"%a::%a"prettypLiteral.prettyl|{content=Join{a;b;ancestor}}->Format.fprintffmt"join(%a,%a,%a)"prettyaprettybprettyancestor|_->assertfalse;;letequalab=a.id==b.id;;letcount=ref0;;lettailx=matchx.contentwith|Root->assertfalse|Append(x,_)->x|Join{ancestor}->ancestorletrecnearest_common_ancestorab=ifa.depth==b.depththenifa==bthenaelsenearest_common_ancestor(taila)(tailb)elseifa.depth<b.depththennearest_common_ancestora(tailb)elsenearest_common_ancestor(taila)b;;letis_prefixab=equal(nearest_common_ancestorab)a(* Not the most efficient. *)(* We should just check that one is the ancestor of the other, and
take the longuest one. *)letinterab=letab=nearest_common_ancestorabinifab==athenbelseifab==bthenaelseCodex_log.fatal"inter %a %a"prettyaprettyb(* match a.content,b.content with
* | Root, x -> b
* | x, Root -> a
* | _ -> assert false *);;(* XXX: Not completely true for join nodes, but should work
anyway, because we always consider then differently.
*)letdisjointab=letab=nearest_common_ancestorabinifab==a||ab==bthenfalseelsetrue(* On different branches. *)[@@@warning"-8"]moduleAppendHash=Weak.Make(structtypenonrect=tletequal({content=Append(pa,la)})({content=Append(pb,lb)})=pa==pb&&Literal.equallalb;;lethash({content=Append(pa,la)})=Hashing.hash2pa.id@@Literal.hashlaend);;moduleJoinHash=Weak.Make(structtypenonrect=tletequal({content=Join{a=a1;b=b1}})({content=Join{a=a2;b=b2}})=a1==a2&&b1==b2lethash({content=Join{a;b}})=Hashing.hash2a.idb.idend);;[@@@warning"+8"]letweakhash_default_size=2000;;lettag_ref=ref1;;letappend_table=AppendHash.createweakhash_default_size;;letjoin_table=JoinHash.createweakhash_default_size;;letappendpx=lettentative={id=!tag_ref;content=Append(p,x);depth=p.depth+1}inletret=AppendHash.mergeappend_tabletentativein(ifret==tentativethenincrtag_ref);ret;;letjoinab=letancestor=nearest_common_ancestorabinlettentative={id=!tag_ref;content=Join{a;b;ancestor};depth=ancestor.depth+1}inletret=JoinHash.mergejoin_tabletentativein(ifret==tentativethenincrtag_ref);ret;;letunion=joinletof_literalx=appendrootx;;endtypet=|False|LiteralofLiteral.t(* positive (if x&0) of negative (if x&1) literal *)|PathofPath.t|Complementoft(* lazy computation *)(* Ensures absence of clashes. *)lethash=function|False->0|Literalx->(Path.of_literalx).id|Path{id}->id|Complement_->assertfalse(* | Unused -> -1 *);;letprettyfmtx=matchxwith|False->Format.fprintffmt"False"(* | Unused -> Format.fprintf fmt "Unused" *)|Literalx->Format.fprintffmt"Literal(%a)"Literal.prettyx|Pathx->Format.fprintffmt"Path(%a)"Path.prettyx|Complement_->assertfalseletequalab=(* Codex_log.feedback "equal %a %a" pretty a pretty b; *)matcha,bwith|False,False->true|Literalx,Literaly->Literal.equalxy|Pathx,Pathy->Path.equalxy|Literalx,Pathy->Path.equaly(Path.of_literalx)|Pathx,Literaly->Path.equalx(Path.of_literaly)|_->false;;let_equalab=letres=equalabinCodex_log.feedback"equal %a %a res %b"prettyaprettybres;res;;letvar()=Literal(Literal.fresh());;letempty=False;;letall=PathPath.root;;letone=all;;letcomplementx=(* Codex_log.feedback "Complement %a" pretty x; *)matchxwith|Pathxwhenx==Path.root->False|Literalx->Literal(Literal.complementx)|False->Path(Path.root)|Complementx->x|x->Complementx;;let(!~)=complement;;(* Check that we are in the same path of the tree *)letis_includedab=matcha,bwith|_whenequalab->true|False,_->true|_,False->false|_,Path{content=Root}->true|Path{content=Root},_->false|_->Codex_log.feedback"is_included %a %a"prettyaprettyb;assertfalse;;(* let leq a b = is_included b a *)(* Find the common ancestore, and create a new one (except maybe for simple cases). *)letunionab=matcha,bwith|False,x|x,False->x|Literala,LiteralbwhenLiteral.equala(Literal.complementb)->one|Literala,Literalb->Path(Path.union(Path.of_literala)(Path.of_literalb))|Patha,Pathb->Path(Path.unionab)|Patha,Literalb->Path(Path.uniona(Path.of_literalb))|Literala,Pathb->Path(Path.union(Path.of_literala)b)|_->assertfalselet(||~)=unionletnearest_common_ancestorab=matcha,bwith|Path{content=Root},_->a|_,Path{content=Root}->b|(Literalaasres),LiteralbwhenLiteral.equalab->res|(Literal_a),Literal_b->all|Patha,Literalb->Path(Path.nearest_common_ancestora(Path.of_literalb))|Literala,Pathb->Path(Path.nearest_common_ancestor(Path.of_literala)b)|Patha,Pathb->Path(Path.nearest_common_ancestorab)|_->Codex_log.fatal"nearest_common_ancestor %a %a"prettyaprettybletis_prefixab=matcha,bwith|Path{content=Root},_->true|_,Path{content=Root}->false|(Literala),LiteralbwhenLiteral.equalab->true|(Literala),Literalb->false|Patha,Literalb->Path.is_prefixa(Path.of_literalb)|Literala,Pathb->Path.is_prefix(Path.of_literala)b|Patha,Pathb->Path.is_prefixab|_->Codex_log.fatal"is_prefix %a %a"prettyaprettyb(* Check that we are on the same branch, and take the lower one. *)(* MAYBE: when on disjoint branches, we know that inter is empty?
Not always, but sometime we do (when diverges from the same path)
*)letinterab=(* Codex_log.feedback "inter %a %a" pretty a pretty b; *)matcha,bwith|False,_|_,False->False|x,ywhenx==y->x|Literalx,Path{content=Append(p,l)}whenLiteral.equalxl->b|Literalx,Path{content=Append(p,l)}whenLiteral.equalx(Literal.complementl)->False|Path{content=Append(p,l)},LiteralxwhenLiteral.equalxl->a|Path{content=Append(p,l)},LiteralxwhenLiteral.equalx(Literal.complementl)->False(* Why does this happens? *)|Literala,LiteralbwhenLiteral.equala(Literal.complementb)->False|Literala,Literalb->(* In this case, the path is on the right. *)letb=Path.appendPath.rootbinPath(Path.appendba)|Literalx,Pathp->Path(Path.appendpx)|Pathp,Literalx->Path(Path.appendpx)|Patha,Pathb->Path(Path.interab)|_->Codex_log.fatal"inter %a %a"prettyaprettyb;;let(&&~)=interletis_emptyx=x=Falseletdisjointab=matcha,bwith|False,_|_,False->true|(Literala),LiteralbwhenLiteral.equalab->false|(Literala),LiteralbwhenLiteral.equala(Literal.complementb)->true|Patha,Literalb->Path.disjointa(Path.of_literalb)|Literala,Pathb->Path.disjoint(Path.of_literala)b|Patha,Pathb->Path.disjointab|_->Codex_log.fatal"disjoint %a %a"prettyaprettyb;;moduleLog=structletdisjointab=letres=disjointabinCodex_log.feedback"disjoint %a %a res %b"prettyaprettybres;res;;letinterab=letres=interabinCodex_log.feedback"inter %a %a res %a"prettyaprettybprettyres;res;;letunionab=letres=unionabinCodex_log.feedback"union %a %a res %a"prettyaprettybprettyres;res;;letis_prefixab=letres=is_prefixabinCodex_log.feedback"is_prefix %a %a res %b"prettyaprettybres;res;;letnearest_common_ancestorab=letres=nearest_common_ancestorabinCodex_log.feedback"nearest_common_ancestor %a %a res %a"prettyaprettybprettyres;res;;end(* include Log *)end(** A dummy Condition, which creates a new int each time. *)moduleConditionInt=structtypet=intletcount=ref0;;letunique()=incrcount;!count;;letpretty__=()letall=unique()letequalab=a==bletempty=unique()letis_emptyx=x==emptyletinter__=unique()let(&&~)__=unique()letunion__=unique()let(||~)__=unique()letdisjoint__=assertfalseletis_included__=assertfalseletcomplement_=unique()letvar()=unique()lethashx=xend(** Condition using Cudd binary-decision diagrams. *)moduleConditionCudd=structmoduleC_bdd=Cudd.Bdd()typet=Cudd.bdd(* Reordering makes the analysis longer. *)(* Cudd.Man.(enable_autodyn man REORDER_LAZY_SIFT);; *)letall=C_bdd.true_()letempty=C_bdd.false_()letinter=C_bdd.and_letunion=C_bdd.or_letdisjoint=C_bdd.is_inter_emptyletis_empty=C_bdd.is_falseletcomplement=C_bdd.not_letequal=C_bdd.is_equalletis_included=C_bdd.is_included_inlet(&&~)=interlet(||~)=unionlet(!~)=complementletone=allletzero=emptyletis_zero=C_bdd.is_falseletis_one=C_bdd.is_true(* let var_to_constraint = Hashtbl.create 17;; *)(* MAYBE: link newvar id to the id in b%d, for printing.
But only when debugging. *)(* let term_to_var = Term.Hashtbl.create 17;;
* let var_to_term = Hashtbl.create 17;;
* let var t =
* try Term.Hashtbl.find term_to_var t
* with Not_found ->
* let x = Cudd.Bdd.newvar man in
* Term.Hashtbl.replace term_to_var t x;
* Hashtbl.replace var_to_term (Cudd.Bdd.topvar x) t;
* x
* ;; *)(* Note: new vars are added at the bottom. *)letvar()=C_bdd.newvar()letpp_print_varfmtv=Format.fprintffmt"var%d"v;;letprettyfmtx=C_bdd.printpp_print_var(* Format.pp_print_int *)fmtxlethash(x:t)=Hashtbl.hashxendmoduleMakeConditionCudd(T:sigend):Condition_map.CONDITION=structincludeConditionCuddend(**************** MakeConditionMap implementations. ****************)moduleMakeConditionMapMTBDD(Lattice:sigincludeCondition_map.Lvalequal:t->t->boolvalhash:t->intvalcompare:t->t->intend)(* :Condition_map.LConditionMap with module L = Lattice = *)=structmoduleMaybe_Lattice=structincludeDatatype_sig.Option(structincludeLatticeletpretty_=assertfalseend)(* let pretty fmt = function
* | Some x -> Lattice.pretty fmt x
* | None -> Format.fprintf fmt "<none>"
* ;; *)letsomex=Somexletnone=None(* let bottom = Some (Lattice.bottom) *)endmoduleMTBDD=structmoduleTerminal=Maybe_LatticeincludeBed.MTBDD_Make(Maybe_Lattice);;includeWith_Set(structtypet=Lattice.t(* let empty = Lattice.bottom *)letempty=assertfalseletsingletonx=Maybe_Lattice.thexletunion=(* Lattice.join *)assertfalseletaddab=assertfalseletpretty_=assertfalseend)endtypevalue=Lattice.tmoduleL=LatticemoduleCond=ConditionMytypet=MTBDD.t(* value Bmtbdd *)letfind=MTBDD.findletpretty=MTBDD.prettyletrefinemtbdd~cond?(notcond=(Cond.complementcond))value=letvalue=Maybe_Lattice.somevalueinMTBDD.updatemtbddcond(funold_lattice->matchold_lattice,valuewith|None,value->value(* XXX: Ca c'est une difference aussi avec ce que je faisait avant: ici c'etait un assert false. *)|_,None->assertfalse(* Cannot put <notevaluated> in the lattice. *)|Somea,Someb->assertfalse);;letcreatex=assertfalseletcreate_partial=MTBDD.terminalNoneendmoduletypeSCONDITIONMAP_MTBDD=sigtypevalueincludeCondition_map.LConditionMapwithtypeCond.t=ConditionMy.tandtypeL.t=valueandtypet=valueoptionBed.mtbddmoduleMTBDD:Bed.MTBDDwithtypeTerminal.t=L.toptionendmoduleConditionMapMTBDD=structincludeCondition_map.MakePathInsensitive(ConditionMy)(structtype'at='aoptionBed.mtbddend)(* Replace previous value. *)(* let ar1 = Bed.ar1 *)(* let ar2 = Bed.ar2 *)(* MAYBE: Faire une implementation directe de ar1 ar2? map3 n'est
pas optimal, parce que dans certain cas on pourrait garder le
sous-arbre existant tel quel. Ou alors on devrait avoir une
fonction "special", comme dans Cudd. *)(* MAYBE: We also should be using cond. Or maybe not; it suffice to
propagate None as being "not defined"
*)letar1(typea)(moduleMa:SCONDITIONMAP_MTBDDwithtypevalue=a)(typeres)(moduleMres:SCONDITIONMAP_MTBDDwithtypevalue=res)condfmamold=letfaold=matcha,oldwith|None,x->x|Somea,None->Some(fa)|Somea,Someold->assertfalseinBed.map2(moduleMa.MTBDD)(moduleMres.MTBDD)(moduleMres.MTBDD)fmamold;;letar2(typea)(moduleMa:SCONDITIONMAP_MTBDDwithtypevalue=a)(typeb)(moduleMb:SCONDITIONMAP_MTBDDwithtypevalue=b)(typeres)(moduleMres:SCONDITIONMAP_MTBDDwithtypevalue=res)condfmambmold=letfabold=matcha,b,oldwith|None,_,x|_,None,x->x|Somea,Someb,None->Some(fab)|Somea,Someb,Someold->assertfalseinBed.map3(moduleMa.MTBDD)(moduleMb.MTBDD)(moduleMres.MTBDD)(moduleMres.MTBDD)fmambmold;;endmoduleMakeConditionMapPartitionPI(Condition:Condition_map.CONDITION)=structmoduleConditionMapPartition=Condition_map.ConditionMapPartition(Condition)moduletypeSCONDITIONMAP=sigtypevalueincludeCondition_map.LConditionMapFoldwithtypeCond.t=Condition.tandtypeL.t=valueandtypet=valueConditionMapPartition.tendmoduleMakeConditionMap(L:Condition_map.L)=structincludeConditionMapPartition.Make(L)typevalue=L.tendmoduleConditionMap=structmodulePI=Condition_map.MakePathInsensitive(Condition)(structtype'at='aConditionMapPartition.tend)includePIendend(* module MakeConditionMapPartitionPS(Condition:Condition_map.CONDITION) = struct
*
* module ConditionMapPartition = Condition_map.CONDITIONMapPartition(Condition)
* module type SCONDITIONMAP = sig
* type value
* include Condition_map.LConditionMapFold with type Cond.t = Condition.t
* and type L.t = value
* and type t = value ConditionMapPartition.t
* end
* module MakeConditionMap(L:Condition_map.L) =
* struct
* include ConditionMapPartition.Make(L)
* type value = L.t
* end
*
* module ConditionMap = struct
* module PS = Condition_map.MakePathSensitive(Condition)(struct type 'a t = 'a ConditionMapPartition.t end)
* include PS
* end
*
* end *)(* module MakeConditionMapTreeMapPI(Condition:Treemap.Key) = struct
*
* module M = Treemap.Make(Condition)
* (\* module ConditionMapTree = Condition_map.CONDITIONMapTree(Condition) *\)
* module type SCONDITIONMAP = sig
* type value
* include Condition_map.LConditionMap with type Cond.t = Condition.t
* and type L.t = value
* and type t = value M.t
* end
* module MakeConditionMap(L:Condition_map.L) =
* struct
* (\* include ConditionMapTree.Make(L) *\)
* type value = L.t
* end
*
* module ConditionMap = struct
* module PI = Condition_map.MakePathInsensitive(Condition)(struct type 'a t = 'a M.t end)
* include PI
* end
*
*
* end *)moduleMakeConditionMapTreePI(Condition:Condition_map.CONDITION)=structmoduleConditionMapTree=Condition_map.ConditionMapTree(Condition)(* This is unused? *)(* module type SCONDITIONMAP = sig *)(* type value *)(* include Condition_map.LConditionMap with type Cond.t = Condition.t *)(* and type L.t = value *)(* and type t = value ConditionMapTree.t *)(* end *)moduleMakeConditionMap(L:Condition_map.L)=structincludeConditionMapTree.Make(L)typevalue=L.tendmoduleConditionMap=structmodulePI=Condition_map.MakePathInsensitive(Condition)(structtype'at='aConditionMapTree.tend)includePIendend(* module MakeConditionMapTreePS(Condition:Condition_map.CONDITION) = struct
*
* module ConditionMapTree = Condition_map.CONDITIONMapTree(Condition)
* module type SCONDITIONMAP = sig
* type value
* include Condition_map.LConditionMap with type Cond.t = Condition.t
* and type L.t = value
* and type t = value ConditionMapTree.t
* end
* module MakeConditionMap(L:Condition_map.L) =
* struct
* include ConditionMapTree.Make(L)
* type value = L.t
* end
*
* module ConditionMap = struct
* module PS = Condition_map.MakePathSensitive(Condition)(struct type 'a t = 'a ConditionMapTree.t end)
* include PS
* end
*
* end *)(* Special MTBDD for Cudd. *)(* Actually, it could work on non-cudd conditions too. But this does
not work well on KCG.*)(* XXX: je pense qu'on peut faire mieux: toujours avoir Ite qui soit un ConditionCudd, *)(* pas seulement dans le cas le plus bas. Par exemple, on peut avoir deux mtbdd qui conduisent *)(* a des trucs differents; mais tous les chemins menent vers ces deux la. *)moduleCuddMTBDD=struct(* Like a MTBDD, except that when we have to choose between two
options, we encode this using a Cudd BDD. This avoids the need to
traverse a long tail of BDDs. *)(* Note: this is buggy and slow. *)typeid=inttypevar=inttype'at=|TerminalNone|Terminalofid*'a|Iteofid*ConditionCudd.t*'at*'atendmoduletypeSCONDITIONMAP_CUDD_MTBDD=sigtypevalueincludeCondition_map.LConditionMapFoldwithtypeCond.t=ConditionCudd.tandtypeL.t=valueandtypet=valueCuddMTBDD.tendmoduleMakeConditionMapCuddMTBDD(L:Condition_map.L)=structtypet=L.tCuddMTBDD.tletfresh_id=letcount=ref0infun()->incrcount;!count;;openCuddMTBDDlethash=function|TerminalNone->0|Terminal(id,_)->id|Ite(id,_,_,_)->id;;letequal=(==)letrecprettyppfmt=function|TerminalNone->Format.fprintffmt"<none>"|Terminal(id,a)->Format.fprintffmt"%d:%a"idppa|Ite(id,c,a,b)->Format.fprintffmt"%d:ite(@[<hv>%a@,,%a@,,%a@])"idConditionCudd.prettyc(prettypp)a(prettypp)b[@@@warning"-8"](* We use 2 different hashes for terminal and non-terminal
terms; this avoids spurious collisions in the table. MAYBE:
Use GADT to avoid testing the cases of the Sum. *)moduleTerminalHash=Weak.Make(structtypet=L.tCuddMTBDD.tletequal(Terminal(_,a))(Terminal(_,b))=L.equalab;;lethash(Terminal(_,a))=L.hashaend)moduleIfHash=Weak.Make(structtypet=L.tCuddMTBDD.tletequal(Ite(_,c1,then1,else1))(Ite(_,c2,then2,else2))=ConditionCudd.equalc1c2&&then1==then2&&else1==else2lethash(Ite(_,c,then_,else_))=Hashing.hash3(ConditionCudd.hashc)(hashthen_)(hashelse_)end)[@@@warning"+8"]letweakhash_default_size=2000;;lettag_ref=ref1;;letterminal_table=TerminalHash.createweakhash_default_size;;letif_table=IfHash.createweakhash_default_size;;letterminalx=lettentative=Terminal(!tag_ref,x)inletret=TerminalHash.mergeterminal_tabletentativein(ifret==tentativethen(tag_ref:=!tag_ref+1));ret;;(* let base_variable = function *)(* | TerminalNone | Terminal _ -> 1000000000 *)(* | Ite(_,c,_,_) -> Cudd.Bdd.topvar c *)(* ;; *)letbase_mkcondthen_else_=(* assert (Cudd.Bdd.topvar cond < base_variable then_); *)(* assert (Cudd.Bdd.topvar cond < base_variable else_); *)lettentative=(* Normalize that a < b; else take the complement. *)ifhashthen_<hashelse_thenIte(!tag_ref,cond,then_,else_)elseIte(!tag_ref,ConditionCudd.complementcond,else_,then_)inletret=IfHash.mergeif_tabletentativein(ifret==tentativethen(tag_ref:=!tag_ref+1));ret;;letmkcondthen_else_=(* Kernel.feedback "mk %a %a %a" ConditionCudd.pretty cond pretty then_ pretty else_; *)ifConditionCudd.is_onecondthenthen_elseifConditionCudd.is_zerocondthenelse_elseifequalthen_else_thenthen_elsematchthen_,else_with(* Cases where we have only two possible choices at the end -> extend a bdd. *)|_,Ite(_,c,a,b)whenthen_==a->base_mk(ConditionCudd.unioncondc)ab|_,Ite(_,c,a,b)whenthen_==b->base_mk(ConditionCudd.interc(ConditionCudd.complementcond))ab|Ite(_,c,a,b),_whenelse_==a->base_mk(ConditionCudd.intercond(ConditionCudd.complementc))ba|Ite(_,c,a,b),_whenelse_==b->base_mk(ConditionCudd.intercondc)aelse_|Ite(_,c1,a1,b1),Ite(_,c2,a2,b2)whena1==a2&&b1==b2->base_mk(ConditionCudd.union(ConditionCudd.intercondc1)(ConditionCudd.inter(ConditionCudd.complementcond)c2))a1b1|_->base_mkcondthen_else_;;letcreate_partial=TerminalNonemoduleCond=ConditionCuddmoduleL=Ltypevalue=L.tmoduleT=structtypet=L.tCuddMTBDD.tlethash=hashletequal=(==)endmoduleFind_cache=Ephemeron.K2.Make(T)(ConditionCudd);;letfind_cache=Find_cache.create117;;moduleFind_all_cache=Ephemeron.K1.Make(T);;letfind_all_cache=Find_all_cache.create117;;letrecfindtcondcur=(* Kernel.feedback "find %a %a " pretty t ConditionCudd.pretty cond; *)tryFind_cache.findfind_cache(t,cond)withNot_found->letres=matchCond.C_bdd.inspectcondwith|Cond.C_bdd.False->(* L.bottom *)assertfalse|Cond.C_bdd.True->letfind_allx=tryFind_all_cache.findfind_all_cachexwithNot_found->letres=matchxwith|TerminalNone->assertfalse|Terminal(_,v)->v|Ite(_,_,t1,t2)->assertfalseinFind_all_cache.replacefind_all_cachexres;resinfind_allt|Cond.C_bdd.Ifte(var1,then1,else1)->beginmatchtwith|TerminalNone->assertfalse|Terminal(_,a)->a|Ite(_,c,then_,else_)->beginletcurthen=ConditionCudd.intercurcinletcurelse=ConditionCudd.intercur(ConditionCudd.complementc)inmatch(Cond.C_bdd.is_inter_emptycondcurthen,Cond.C_bdd.is_inter_emptycondcurelse)with|false,false->assertfalse|true,false->findelse_condcurelse|false,true->findthen_condcurthen|true,true->assertfalseendendinFind_cache.replacefind_cache(t,cond)res;res;;letfindtcond=findtcondConditionCudd.one;;moduleRefine_cache=Find_cache;;moduleLoop_cache=Ephemeron.K2.Make(ConditionCudd)(ConditionCudd);;moduleT2_cache=Ephemeron.K2.Make(T)(T);;(* MAYBE: Memoize this. In the same cache than refine? *)letrecreplace_allvalue=function|TerminalNone->terminalvalue|Terminal(_,a)->assertfalse|Ite(_,c,then_,else_)->mkc(replace_allvaluethen_)(replace_allvalueelse_);;moduleValue_cache=Ephemeron.K1.Make(L);;letrefine_cache=Value_cache.create30;;(* Note: a simple choice is to make mk do all the re-arrangement
work. But of course it is better to optimize this, i.e. stop early when feasible. *)letrefine~inter(t:L.tCuddMTBDD.t)~(cond:ConditionCudd.t)?notcond(value:L.t)=(* Attempt to share the cache acrosse invokations. *)letrefine_cache,loop_cache=tryValue_cache.findrefine_cachevaluewithNot_found->letrefine_cache=Refine_cache.create3inletloop_cache=T2_cache.create3in(refine_cache,loop_cache)inletrecrefinet~cond=(* Kernel.feedback "refine %a cond %a v %a" pretty t ConditionCudd.pretty cond L.pretty value; *)tryRefine_cache.findrefine_cache(t,cond)withNot_found->letres=matchCond.C_bdd.inspectcondwith|Cond.C_bdd.True->(* Replace all. *)replace_allvaluet|Cond.C_bdd.False->t(* replace none *)|Cond.C_bdd.Ifte(var1,then1,else1)->begin(* Replace some *)matchtwith|TerminalNone->mkcond(terminalvalue)t|Terminal(_,a)->mkcond(terminal@@interavalue)t|Ite(_,c2,then2,else2)->letc1=condin(* Importang shortcuts. TODO: Also check leq. *)ifConditionCudd.equalc1c2thenmkc2(replace_allvaluethen2)else2elseifConditionCudd.equal(ConditionCudd.complementc1)c2thenmkc2then2(replace_allvalueelse2)elsebegin(* Note that loop_cache is shared between all instances of refine. *)letloop_cache=tryT2_cache.findloop_cache(then2,else2)withNot_found->letres=Loop_cache.create3inT2_cache.replaceloop_cache(then2,else2)res;resin(* XXX: Is it possible to do it better than manual iteration? *)(* let loop_cache = Loop_cache.create 12 in *)letrecloopc1c2=(* Kernel.feedback "loop c1 %a c2 %a" ConditionCudd.pretty c1 ConditionCudd.pretty c2; *)tryLoop_cache.findloop_cache(c1,c2)withNot_found->(* Kernel.feedback "not found"; *)letres=(* Important? shortcuts: catch the tail. *)ifConditionCudd.equalc1c2thenmkc2(replace_allvaluethen2)else2elseifConditionCudd.equalc1(ConditionCudd.complementc2)thenmkc2then2(replace_allvalueelse2)elsematchCond.C_bdd.inspectc1with|Cond.C_bdd.False->mkc2then2else2|Cond.C_bdd.True->mkc2(replace_allvaluethen2)(replace_allvalueelse2)|Cond.C_bdd.Ifte(var1,then1,else1)->beginmatchCond.C_bdd.inspectc2with|Cond.C_bdd.True->refinethen2~cond:c1(* mk c1 (replace_all value then2) then2 *)|Cond.C_bdd.False->refineelse2~cond:c1(* mk c1 (replace_all value else2) else2 *)|Cond.C_bdd.Ifte(var2,condthen2,condelse2)->ifvar1==var2thenmk(Cond.C_bdd.varvar1)(loopthen1condthen2)(loopelse1condelse2)elseifvar1<var2thenmk(Cond.C_bdd.varvar1)(loopthen1c2)(loopelse1c2)elsemk(Cond.C_bdd.varvar2)(loopc1condthen2)(loopc1condelse2)endinLoop_cache.replaceloop_cache(c1,c2)res;(* Kernel.feedback "loop c1 %a c2 %a res %a" ConditionCudd.pretty c1 ConditionCudd.pretty c2 pretty res; *)resinloopc1c2(* Kernel.feedback "c1 %a c2 %a" ConditionCudd.pretty c1 ConditionCudd.pretty c2; *)endendinRefine_cache.replacerefine_cache(t,cond)res;resinrefinet~cond;;letrefinet~cond?notcondv=letres=refinet~cond?notcondvin(* Kernel.feedback "refine %a cond %a v %a res %a" pretty t ConditionCudd.pretty cond L.pretty v pretty res; *)res;;letfold_with_cond_=assertfalseend(**************** Special Tree for Cudd. ****************)(* Actually, it could work on non-cudd conditions too. *)moduleCuddTree=structtypeid=inttype'at=|TerminalNone|Terminalofid*'a|Iteofid*ConditionCudd.t*'at*'atendmoduletypeSCONDITIONMAP_CUDD_TREE=sigtypevalueincludeCondition_map.LConditionMapFoldwithtypeCond.t=ConditionCudd.tandtypeL.t=valueandtypet=valueCuddTree.tendmoduleMakeConditionMapCuddTree(L:Condition_map.L)=structtypet=L.tCuddTree.tletfresh_id=letcount=ref0infun()->incrcount;!count;;openCuddTreelethash=function|TerminalNone->0|Terminal(id,_)->id|Ite(id,_,_,_)->id;;letequal=(==)[@@@warning"-8"](* We use 2 different hashes for terminal and non-terminal
terms; this avoids spurious collisions in the table. MAYBE:
Use GADT to avoid testing the cases of the Sum. *)moduleTerminalHash=Weak.Make(structtypet=L.tCuddTree.tletequal(Terminal(_,a))(Terminal(_,b))=L.equalab;;lethash(Terminal(_,a))=L.hashaend)moduleIfHash=Weak.Make(structtypet=L.tCuddTree.tletequal(Ite(_,c1,then1,else1))(Ite(_,c2,then2,else2))=ConditionCudd.equalc1c2&&then1==then2&&else1==else2lethash(Ite(_,c,then_,else_))=Hashing.hash3(ConditionCudd.hashc)(hashthen_)(hashelse_)end)[@@@warning"+8"]letweakhash_default_size=2000;;lettag_ref=ref1;;letterminal_table=TerminalHash.createweakhash_default_size;;letif_table=IfHash.createweakhash_default_size;;letterminalx=lettentative=Terminal(!tag_ref,x)inletret=TerminalHash.mergeterminal_tabletentativein(ifret==tentativethen(tag_ref:=!tag_ref+1));ret;;letrecmkvarthen_else_=assert(var!=ConditionCudd.one);(* MAYBE: Handle these cases? *)assert(var!=ConditionCudd.zero);(* TODO: Normalize that a < b; else take the complement. *)ifequalthen_else_thenthen_elsematchthen_,else_with(* Simplify the tree. *)|_,Ite(_,c,a,b)whenthen_==a->mk(ConditionCudd.unionvarc)ab|_,Ite(_,c,a,b)whenthen_==b->mk(ConditionCudd.interc(ConditionCudd.complementvar))ab|Ite(_,c,a,b),_whenelse_==a->mk(ConditionCudd.intervar(ConditionCudd.complementc))ba|Ite(_,c,a,b),_whenelse_==b->mk(ConditionCudd.intervarc)aelse_|Ite(_,c1,a1,b1),Ite(_,c2,a2,b2)whena1==a2&&b1==b2->mk(ConditionCudd.union(ConditionCudd.intervarc1)(ConditionCudd.inter(ConditionCudd.complementvar)c2))a1b1|_->lettentative=ifhashthen_<hashelse_thenIte(!tag_ref,var,then_,else_)elseIte(!tag_ref,ConditionCudd.complementvar,else_,then_)inletret=IfHash.mergeif_tabletentativein(ifret==tentativethen(tag_ref:=!tag_ref+1));ret;;letrec_prettyppfmt=function|TerminalNone->Format.fprintffmt"<none>"|Terminal(id,a)->Format.fprintffmt"%d:%a"idppa|Ite(id,c,a,b)->Format.fprintffmt"%d:ite(@[<v>%a@ ,%a@ ,%a@])"idConditionCudd.prettyc(_prettypp)a(_prettypp)b;;letprettyfmt_=();;letcreate_partial=TerminalNone(* Cur is the condition for the partition t. *)(* MAYBE: memoize. *)letrecrefine~inter(t:L.tCuddTree.t)~condcur(value:L.t)=ifConditionCudd.C_bdd.is_inter_emptycondcurthentelseifConditionCudd.C_bdd.is_included_incurcondthenletrecreplace_all=function|TerminalNone->terminalvalue|Terminal(_,a)->terminal(interavalue)|Ite(_,c,then_,else_)->mkc(replace_allthen_)(replace_allelse_)inreplace_alltelsematchtwith|TerminalNone->mkcond(terminalvalue)TerminalNone|Terminal(_,a)->mkcond(terminal(interavalue))t|Ite(_,c,then_,else_)->letcurthen=ConditionCudd.intercurcinletcurelse=ConditionCudd.intercur(ConditionCudd.complementc)inmkc(refine~interthen_~condcurthenvalue)(refine~interelse_~condcurelsevalue);;letrefinet~cond?notcondv=refinet~condConditionCudd.onev;;letrefinet~cond?notcondv=letres=refinet~cond?notcondvin(* Kernel.feedback "refine %a cond %a v %a res %a" pretty t ConditionCudd.pretty cond L.pretty v pretty res; *)res;;letrecfindtcurcond=matchtwith|TerminalNone->assertfalse|Terminal(_,v)->v|Ite(_,c,then_,else_)->(* MAYBE: minimize cond here too. *)(* Kernel.feedback "inter result: %a %a %a %b %b" ConditionCudd.pretty c ConditionCudd.pretty (ConditionCudd.complement c) ConditionCudd.pretty cond (Cudd.Bdd.is_inter_empty cond c) (Cudd.Bdd.is_inter_empty (ConditionCudd.complement c) cond); *)letcurthen=ConditionCudd.intercurcinletcurelse=ConditionCudd.intercur(ConditionCudd.complementc)inmatch(ConditionCudd.C_bdd.is_inter_emptycondcurthen,ConditionCudd.C_bdd.is_inter_emptycondcurelse)with|false,false->assertfalse|true,false->findelse_curelsecond|false,true->findthen_curthencond|true,true->assertfalse;;letfindtcond=(* Kernel.feedback "find %a %a" pretty t ConditionCudd.pretty cond; *)ifConditionCudd.is_emptycondthen(* L.bottom *)assertfalseelsefindtConditionCudd.onecond;;moduleCond=ConditionCuddmoduleL=Ltypevalue=L.tletrecfold_with_condtcondaccfcur=ifCond.C_bdd.is_inter_emptycurcondthenaccelsematchtwith|TerminalNone->assertfalse|Terminal(_,v)->fv(ConditionCudd.intercurcond)acc|Ite(_,c,then_,else_)->letcurthen=ConditionCudd.intercurcinletcurelse=ConditionCudd.intercur(ConditionCudd.complementc)inletacc=fold_with_condthen_condaccfcurtheninletacc=fold_with_condelse_condaccfcurelseinacc;;letfold_with_condtcondaccf=fold_with_condtcondaccfConditionCudd.one;;end(**************** Final configurations. ****************)(* Things to export: Condition; ConditionMap; SCONDITIONMAP; and MakeConditionMap.
Probably they are not all necessary. *)(* This one is relatively fast. *)moduleCuddPIPartition=structmoduleCondition=ConditionCuddincludeMakeConditionMapPartitionPI(Condition)end(* Our implementation of ConditionDom, based on treemaps. Works for
most tests, but there is still implementation work to do to improve
this. *)moduleDomPIPartition=structmoduleCondition=ConditionDommoduleM=Treemap.Make(Condition)moduletypeSCONDITIONMAP=sigtypevalueendmoduleConditionMap=structtype'at='aM.texceptionNever_refinedletfindconda=tryM.findcondawithNot_found->raiseNever_refinedletjoin__=assertfalseletar0(moduleLres:SCONDITIONMAP)~interrescondfold=M.refinecond~inter:interres~joinfold;;letar1(moduleLa:SCONDITIONMAP)~joina~bottoma(moduleLres:SCONDITIONMAP)~interrescondfaold=letav=findcondainM.refinecond~inter:interres~join(fav)old;;letar2(moduleLa:SCONDITIONMAP)~joina~bottoma(moduleLb:SCONDITIONMAP)~joinb~bottomb(moduleLres:SCONDITIONMAP)~interrescondfabold=letav=findcondainletbv=findcondbinM.refinecond~inter:interres~join(favbv)old;;letnondet_disjoint(moduleL:SCONDITIONMAP)~conda~notconda~cma~condb~notcondb~cmb~join~bottom~inter~old=letav=findcondacmainletbv=findcondbcmbinletres=M.refineconda~inter~joinavoldinletres=M.refinecondb~inter~joinbvresinres;;letnondet_non_disjoint(moduleL:SCONDITIONMAP)~conda~cma~condb~cmb~condaorb~notcondaorb~join~bottom~inter~old=letav=findcondacmainletbv=findcondbcmbinletabv=joinavbvinletres=M.refinecondaorb~inter~joinabvoldinres;;letchangedintercondr=function|None->Condition.empty,r|Somex->cond,M.refinecond~inter~joinxr;;letar1_bwd(moduleLa:SCONDITIONMAP)~joina~bottoma~intera(moduleLres:SCONDITIONMAP)~joinres~bottomrescondfares=letva=findcondainletv=findcondresinletnewva=fvavinchangedinteracondanewva;;letar2_bwd(moduleLa:SCONDITIONMAP)~joina~bottoma~intera(moduleLb:SCONDITIONMAP)~joinb~bottomb~interb(moduleLres:SCONDITIONMAP)~joinres~bottomrescondfabres=letva=findcondainletvb=findcondbinletv=findcondresinletnewva,newvb=fvavbvin(changedinteracondanewva,changedinterbcondbnewvb);;end(* module type of Treemap.Make(Condition) *)moduleMakeConditionMap(L:sigincludeCondition_map.Lvalpretty:Format.formatter->t->unitend)=structtypevalue=L.ttypet=L.tConditionMap.tletcreate_partial=M.emptyletfind~join~bottomxkey=ConditionMap.findkeyx;;letrefine~intermap~condvalue=M.refinecond~inter~join:(fun__->assertfalse)valuemapletpretty=M.make_prettyL.prettyletfind~join~bottomxkey=letres=find~join~bottomxkeyinCodex_log.feedback"Finding %a %a res %a"Condition.prettykeyprettyxL.prettyres;res;;letrefine~intermap~condvalue=letres=refine~intermap~condvalueinCodex_log.feedback"Refining %a %a %a res %a"Condition.prettycondL.prettyvalueprettymapprettyres;res;;endend(* module CuddPSPartition = struct
* module Condition = ConditionCudd
* include MakeConditionMapPartitionPS(Condition)
* end *)(* These are awfully slow *)(* module CuddPITree = struct *)(* module Condition = ConditionCudd *)(* include MakeConditionMapTreePI(Condition) *)(* end *)(* module CuddPSTree = struct *)(* module Condition = ConditionCudd *)(* include MakeConditionMapTreePS(Condition) *)(* end *)(* This is the fastest on kcg. *)moduleCuddPITree=structmoduleCondition=ConditionCuddmoduleConditionMap=structincludeCondition_map.MakePathInsensitive(ConditionCudd)(structtype'at='aCuddTree.tend)endmoduleMakeConditionMap=MakeConditionMapCuddTreemoduletypeSCONDITIONMAP=SCONDITIONMAP_CUDD_TREEend(* module CuddPSTree = struct
* module Condition = ConditionCudd
* module ConditionMap = struct
* include Condition_map.MakePathSensitive(ConditionCudd)(struct
* type 'a t = 'a CuddTree.t
* end)
* end
* module MakeConditionMap = MakeConditionMapCuddTree
* module type SCONDITIONMAP = SCONDITIONMAP_CUDD_TREE
* end *)moduleCuddPIMTBDD=structmoduleCondition=ConditionCuddmoduleConditionMap=structincludeCondition_map.MakePathInsensitive(ConditionCudd)(structtype'at='aCuddMTBDD.tend)endmoduleMakeConditionMap=MakeConditionMapCuddMTBDDmoduletypeSCONDITIONMAP=SCONDITIONMAP_CUDD_MTBDDend(* module CuddPSMTBDD = struct
* module Condition = ConditionCudd
* module ConditionMap = struct
* include Condition_map.MakePathSensitive(ConditionCudd)(struct
* type 'a t = 'a CuddMTBDD.t
* end)
* end
* module MakeConditionMap = MakeConditionMapCuddMTBDD
* module type SCONDITIONMAP = SCONDITIONMAP_CUDD_MTBDD
* end *)moduleHomeMadeBDDPartitionPI=structmoduleCondition=ConditionMyincludeMakeConditionMapPartitionPI(Condition)endmoduleHomeMadeMTBDD=structmoduleCondition=ConditionMymoduleMakeConditionMap=MakeConditionMapMTBDDmoduletypeSCONDITIONMAP=SCONDITIONMAP_MTBDDmoduleConditionMap=ConditionMapMTBDDend(* module MinisatPI = struct *)(* module Condition = ConditionMinisat *)(* include MakeConditionMapPartitionPI(Condition) *)(* end *)