module User:sig..end
If such hidden parameters are modified, the cache should be
cleared with Memo.clear, if it is local, otherwise the global
cache should be cleared with Cudd.Man.flush.
Types and values
Type of registered operations
typepid =Cudd.Custom.pid
typecommon =Cudd.Custom.common= {
|
pid : |
(* | Identifiers for shared memoization tables | *) |
|
arity : |
(* | Arity of the operations | *) |
|
memo : |
(* | Memoization table | *) |
val newpid : unit -> Cudd.Custom.pidval make_common : ?memo:Cudd.Memo.t -> int -> commontype('a, 'b)op1 = private('a, 'b) Cudd.Custom.op1= private {
|
common1 : |
|||
|
closure1 : |
(* | Operation on leaves | *) |
val make_op1 : ?memo:Cudd.Memo.t -> ('a -> 'b) -> ('a, 'b) op1val apply_op1 : ('a, 'b) op1 -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.t
Assuming type t = bool Vdd.t, and corresponding diagrams
bdd:t and bdd2:t (with type bool, it is safe to use
directly VDDs, and it simplifies the examples).
We want to negate every leaf of bdd1 and bdd2.
let op = make_op1 ~memo:(Cache (Cache.create 1)) (fun b -> not b);;
bdd1 and bdd2 with
let res1 = apply_op1 op bdd1 and res2 = apply_op1 op bdd2;;
apply_op1, which is nice if bdd1 and bdd2 share common
nodes. The cache is automatically garbage collected when
needed. But even if diagrams in the caches entries may be
garbage collected, the cache itself takes memory. You can
clear it with Cache.clear or Memo.clear. ~memo::(Cache (Cache.create 1)) is replaced by
~memo::(Hash (Hash.create 1)), then diagrams in the table
are referenced and cannot be gabage collected. You should
clear them explicitly with Hash.clear or Memo.clear. let res1 = map_op1 (fun b -> not b) bdd1;;
type('a, 'b, 'c)op2 = private('a, 'b, 'c) Cudd.Custom.op2= private {
|
common2 : |
|||
|
closure2 : |
(* | Operation on leaves | *) |
|
ospecial2 : |
(* | Special case operation | *) |
|
commutative : |
(* | Is the operation commutative ? | *) |
|
idempotent : |
(* | Is the operation idempotent (op x x = x) ? | *) |
val make_op2 : ?memo:Cudd.Memo.t ->
?commutative:bool ->
?idempotent:bool ->
?special:('a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> 'c Cudd.Vdd.t option) ->
('a -> 'b -> 'c) -> ('a, 'b, 'c) op2
commutative (default: false), when true, allows to
optimize the cache usage (hence the speed) when the operation
is commutative.
idempotent (default: false) allows similarly some
optimization when op is idempotent: op x x = x.
This makes sense only if 'a='b='c (the case will never
happens otherwise).
?special (default: None), if equal to Some
specialcase, modifies op as follows: it is applied to
every pair of node during the recursive descend, and if
specialcase vdda vddb = (Some vddc), then vddc is
assumed to be the result of map_op2 op vdda vddb. This
allows not to perform a full recursive descend when a
special case is encountered. See the example below.
val apply_op2 : ('a, 'b, 'c) op2 -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> 'c Cudd.Vdd.t
Assuming as for unary operation example type t = bool Vdd.t
and corresponding diagrams bdd1:t and bdd2:t.
We can compute their conjunction with
let res = map_op2
~commutative:true ~idempotent:true
~special:(fun bdd1 bdd2 ->
if Vdd.is_cst bdd1 && Vdd.dval bdd1 = false then Some(bdd1)
else if Vdd.is_cst bdd2 && Vdd.dval bdd2 = false then Some(bdd2)
else None
(fun b1 b2 -> b1 && b2) bdd1 bdd2;;
Ternary operations
type('a, 'b, 'c, 'd)op3 = private('a, 'b, 'c, 'd) Cudd.Custom.op3= private {
|
common3 : |
|||
|
closure3 : |
(* | Operation on leaves | *) |
|
ospecial3 : |
(* | Special cases | *) |
val make_op3 : ?memo:Cudd.Memo.t ->
?special:('a Cudd.Vdd.t ->
'b Cudd.Vdd.t -> 'c Cudd.Vdd.t -> 'd Cudd.Vdd.t option) ->
('a -> 'b -> 'c -> 'd) -> ('a, 'b, 'c, 'd) op3val apply_op3 : ('a, 'b, 'c, 'd) op3 ->
'a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> 'c Cudd.Vdd.t -> 'd Cudd.Vdd.t?memo=None, then a hash table is used, and cleared at the end.
Still assuming type t = bool Vdd.t
and corresponding diagrams bdd1:t, bdd2:t, bdd3:t.
We can define if-then-else with
let res = map_op3
~special:(fun bdd1 bdd2 bdd3 ->
if Vdd.is_cst bdd1
then Some(if Vdd.dval bdd1 (* = true *) then bdd2 else bdd3)
else None
)
(fun b1 b2 b3 -> if b1 then b2 else b3) bdd1 bdd2 bdd3
Nary operations
type('a, 'b)opN = private('a, 'b) Cudd.Custom.opN= private {
|
commonN : |
|||
|
arityNbdd : |
|||
|
closureN : |
(* | Operation on leaves | *) |
val make_opN : ?memo:Cudd.Memo.t ->
int ->
int ->
(Cudd.Bdd.vt array -> 'a Cudd.Vdd.t array -> 'b Cudd.Vdd.t option) ->
('a, 'b) opNval apply_opN : ('a, 'b) opN ->
Cudd.Bdd.vt array -> 'a Cudd.Vdd.t array -> 'b Cudd.Vdd.ttype('a, 'b)opG = private('a, 'b) Cudd.Custom.opG= private {
|
commonG : |
|
arityGbdd : |
|
closureG : |
|
oclosureBeforeRec : |
|
oclosureIte : |
val make_opG : ?memo:Cudd.Memo.t ->
?beforeRec:(int * bool ->
Cudd.Bdd.vt array ->
'a Cudd.Vdd.t array -> Cudd.Bdd.vt array * 'a Cudd.Vdd.t array) ->
?ite:(int -> 'b Cudd.Vdd.t -> 'b Cudd.Vdd.t -> 'b Cudd.Vdd.t) ->
int ->
int ->
(Cudd.Bdd.vt array -> 'a Cudd.Vdd.t array -> 'b Cudd.Vdd.t option) ->
('a, 'b) opGval apply_opG : ('a, 'b) opG ->
Cudd.Bdd.vt array -> 'a Cudd.Vdd.t array -> 'b Cudd.Vdd.ttype('a, 'b)test2 = private('a, 'b) Cudd.Custom.test2= private {
|
common2t : |
|||
|
closure2t : |
(* | Test on leaves | *) |
|
ospecial2t : |
(* | Special cases | *) |
|
symetric : |
(* | Is the relation symetric ? | *) |
|
reflexive : |
(* | Is the relation reflexive ? (test x x = true) ? | *) |
val make_test2 : ?memo:Cudd.Memo.t ->
?symetric:bool ->
?reflexive:bool ->
?special:('a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> bool option) ->
('a -> 'b -> bool) -> ('a, 'b) test2
symetric (default: false), when true, allows to
optimize the cache usage (hence the speed) when the relation is symetric.
reflexive (default: false) allows similarly some
optimization when the relation is reflexive: test x x = true.
This makes sense only if 'a='b (the case will never
happen otherwise).
?special (default: None) has the same semantics as for
binary operation above.
val apply_test2 : ('a, 'b) test2 -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> booltype'aexist = private'a Cudd.Custom.exist= private {
|
commonexist : |
|||
|
combineexist : |
(* | Combining operation when a decision is eliminated | *) |
val make_exist : ?memo:Cudd.Memo.t -> ('a, 'a, 'a) op2 -> 'a existval apply_exist : 'a exist -> supp:Cudd.Bdd.vt -> 'a Cudd.Vdd.t -> 'a Cudd.Vdd.t
Still assuming type t = bool Vdd.t
and corresponding diagrams bdd:t
We define ordinary existential quantification with
let dor = make_op2 ~commutative:true ~idempotent:true ( || );;
let exist = make_exist dor;;
let res = apply_exist exist ~supp bdd;;
We can define ordinary universal quantification by replacing
|| with &&.
Quantification combined with intersection
type'aexistand = private'a Cudd.Custom.existand= private {
|
commonexistand : |
|||
|
combineexistand : |
(* | Combining operation when a decision is eliminated | *) |
|
bottomexistand : |
(* | Value returned when intersecting with Bdd.dfalse | *) |
val make_existand : ?memo:Cudd.Memo.t ->
bottom:'a -> ('a, 'a, 'a) op2 -> 'a existandval apply_existand : 'a existand ->
supp:Cudd.Bdd.vt -> Cudd.Bdd.vt -> 'a Cudd.Vdd.t -> 'a Cudd.Vdd.texistand ~bottom op2 supp bdd f is equivalent to
exist op2 supp (ite bdd f bottom).
The leaf operation op2:'a -> 'a -> 'a is assumed to be
commutative, idempotent, and also op2 f bottom = f.
Quantification combined with unary operation
type('a, 'b)existop1 = private('a, 'b) Cudd.Custom.existop1= private {
|
commonexistop1 : |
|||
|
combineexistop1 : |
(* | Combining operation when a decision is eliminated | *) |
|
existop1 : |
(* | Unary operations applied before elimination | *) |
val make_existop1 : ?memo:Cudd.Memo.t ->
op1:('a, 'b) op1 ->
('b, 'b, 'b) op2 -> ('a, 'b) existop1val apply_existop1 : ('a, 'b) existop1 ->
supp:Cudd.Bdd.vt -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.t
existop1 op1 op2 supp f is equivalent to
exist op2 supp (op1 f).
The leaf operation op2:'b -> 'b -> 'b is assumed to be
commutative and idempotent.
Quantification combined with intersection and unary operation
type('a, 'b)existandop1 = private('a, 'b) Cudd.Custom.existandop1= private {
|
commonexistandop1 : |
|||
|
combineexistandop1 : |
(* | Combining operation when a decision is eliminated | *) |
|
existandop1 : |
(* | Unary operations applied before elimination | *) |
|
bottomexistandop1 : |
(* | Value returned when intersecting with Bdd.dfalse | *) |
val make_existandop1 : ?memo:Cudd.Memo.t ->
op1:('a, 'b) op1 ->
bottom:'b -> ('b, 'b, 'b) op2 -> ('a, 'b) existandop1val apply_existandop1 : ('a, 'b) existandop1 ->
supp:Cudd.Bdd.vt -> Cudd.Bdd.vt -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.texistandop1 ~bottom op op1 supp bdd f is equivalent to
exist op2 supp (ite bdd (op1 f) bottom)).
The leaf operation op2:'b -> 'b -> 'b is assumed to be
commutative, idempotent, and also op2 f bottom = f.
Clearing memoization tables
val clear_common : common -> unitval clear_op1 : ('a, 'b) op1 -> unitval clear_op2 : ('a, 'b, 'c) op2 -> unitval clear_op3 : ('a, 'b, 'c, 'd) op3 -> unitval clear_opN : ('a, 'b) opN -> unitval clear_opG : ('a, 'b) opG -> unitval clear_test2 : ('a, 'b) test2 -> unitval clear_exist : 'a exist -> unitval clear_existand : 'a existand -> unitval clear_existop1 : ('a, 'b) existop1 -> unitval clear_existandop1 : ('a, 'b) existandop1 -> unitmake_opXXX and apply_opXXX
operations.
if ?memo=None, then a hash table is used, and cleared at the
end.
val map_op1 : ?memo:Cudd.Memo.t -> ('a -> 'b) -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.tval map_op2 : ?memo:Cudd.Memo.t ->
?commutative:bool ->
?idempotent:bool ->
?special:('a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> 'c Cudd.Vdd.t option) ->
('a -> 'b -> 'c) -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> 'c Cudd.Vdd.tval map_op3 : ?memo:Cudd.Memo.t ->
?special:('a Cudd.Vdd.t ->
'b Cudd.Vdd.t -> 'c Cudd.Vdd.t -> 'd Cudd.Vdd.t option) ->
('a -> 'b -> 'c -> 'd) ->
'a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> 'c Cudd.Vdd.t -> 'd Cudd.Vdd.tval map_opN : ?memo:Cudd.Memo.t ->
(Cudd.Bdd.vt array -> 'a Cudd.Vdd.t array -> 'b Cudd.Vdd.t option) ->
Cudd.Bdd.vt array -> 'a Cudd.Vdd.t array -> 'b Cudd.Vdd.tval map_test2 : ?memo:Cudd.Memo.t ->
?symetric:bool ->
?reflexive:bool ->
?special:('a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> bool option) ->
('a -> 'b -> bool) -> 'a Cudd.Vdd.t -> 'b Cudd.Vdd.t -> bool