The bdd package

advertisement
/*
* Revision Control Information
*
* /projects/hsis/CVS/utilities/bdd_ucb/bdd.doc,v
* rajeev
* 1.3
* 1995/08/08 22:38:11
*
*/
Binary Decision Diagram (BDD) Package, Version 2.4
Tom Shiple (original contributors: Herve' Touati, Wendell Baker)
University of California, Berkeley, 1991, 1992
Introduction ----------------------------------------------------------------The interface for this package is modeled on that described in Brace,
Rudell,
Bryant, "Efficient Implementation of a BDD Package", DAC 1990. The user
manipulates only BDD formulas, corresponding to the data type bdd_t. A
bdd_t is
a pointer to a bdd_node, and more than one bdd_t can point to the same
bdd_node.
A BDD manager is a collection of bdd_nodes, which together constitute a
number
of BDDs. A variable ordering is associated with each manager. Variables
are
numbered starting from zero. This ordering is not stored explicitly in
the
manager, but is implicit in the variable ID stored in each node created.
It is
the user's responsibility to define an ordering, and to maintain the
correspondence between application variables and BDD variable IDs. The
BDD
manager maintains the invariant that variables occur in ascending order
in every
root-to-leaf path.
A "single variable BDD formula" is a BDD for a single variable: it has
one node,
with the THEN branch pointing to 1, and the ELSE branch pointing to 0.
All of the functions which operate on BDD formulas return new BDD
formulas. The
BDDs associated with BDD formulas which are arguments to a function are
not
disturbed. For example, bdd_substitute creates a new BDD and leaves the
old one
intact. To purge the old BDD, free the bdd_t pointing to it, and the
next
garbage collection will free those bdd_nodes which can no longer be
reached from
a bdd_t.
A user should never allocate bdd_t data structures; this should only be
done by
the BDD manager. A BDD which is only pointed to by a bdd_t allocated by
a user
will be treated as garbage during garbage collection since the bdd_t is
not
registered with the manager.
For any operation involving multiple BDDs, all the BDDs must belong to
the same
manager. For example, in bdd_and(f, g, 0, 1), the BDDs for 'f' and 'g'
must
exist in the same manager. Also, it is bad form to nest BDD operations
because
the returned bdd_t's will be "lost", potentially causing a buildup of
garbage
which cannot be collected. For example, bdd_and(bdd_or(f,g,1,1),
bdd_xor(h,e),
1, 0) should be avoided.
IMPORTANT: To reiterate the above points: 1) you should only be using
bdd_t's
which are returned by BDD package functions, and 2) you should free
bdd_t's
(using bdd_free) as soon as you no longer need them, so that their
corresponding
BDDs may be garbaged collected at the next opportunity.
Summary ---------------------------------------------------------------------BDD Manager Allocation And Destruction:
bdd_end()
bdd_set_mgr_init_dflts()
bdd_start()
bdd_start_with_params()
BDD Variable Allocation:
bdd_create_variable()
bdd_get_variable()
BDD Formula Management:
bdd_dup()
bdd_free()
Operations on BDD Formulas:
bdd_and()
bdd_and_smooth()
bdd_between()
bdd_cofactor()
bdd_compose()
bdd_consensus()
bdd_cproject()
bdd_else()
bdd_ite()
bdd_minimize()
bdd_minimize_with_params()
bdd_not()
bdd_one()
bdd_or()
bdd_smooth()
bdd_substitute()
bdd_then()
bdd_top_var()
bdd_xnor()
bdd_xor()
bdd_zero()
Queries about BDD Formulas:
bdd_equal()
bdd_is_cube()
bdd_is_tautology()
bdd_leq()
Statistics and Other Queries:
bdd_count_onset()
bdd_get_manager()
bdd_get_free()
bdd_get_node()
bdd_get_stats()
bdd_get_support()
bdd_get_varids()
bdd_num_vars()
bdd_print()
bdd_print_stats()
bdd_size()
bdd_size_multiple()
bdd_top_var_id()
Traversal of BDD Formulas:
bdd_gen_free()
foreach_bdd_cube()
foreach_bdd_node()
Miscellaneous:
bdd_get_external_hooks()
bdd_register_daemon()
bdd_set_gc_mode()
bdd_dynamic_reordering()
BDD Manager Allocation And Destruction --------------------------------------void
bdd_end(manager)
bdd_manager *manager;
Frees all the resources associated with a BDD manager.
void
bdd_set_mgr_init_dflts(mgr_init)
bdd_mgr_init *mgr_init;
Initializes `mgr_init' with the default values. (See BDD_DFLT_* in
bdd.h
for the default values.) See bdd_start_with_params and
bdd_register_daemon
for sample usages.
bdd_manager *
bdd_start(nvariables)
int nvariables;
Initializes a new BDD manager that can contain at most nvariables.
The BDD
manager ensures that no variable in the manager is greater than or
equal to
nvariables over the lifetime of the manager. The range of variable
IDs is
zero to nvariables-1. The manager's nvariables value can be
incremented by
calling bdd_create_variable. This function also creates the constant
1 BDD,
and registers it with the manager.
bdd_manager *
bdd_start_with_params(nvariables, mgr_init)
int nvariables;
bdd_mgr_init *mgr_init;
Same as bdd_start, but allows user to set the parameters specified in
the
bdd_mgr_init data structure.
The ITE cache, ITE_const cache, and the adhoc cache store the result
of
previous computations, in an attempt to improve performance. See
bdd_print_stats for more information on the caches. For each of the
three
caches, the following fields can be set ("*" is one of ITE,
ITE_const,
adhoc):
1) *_cache_on: turn it on or off. In general, the performance of
the BDD
package is horrible if the caches are turned off.
2) *_cache_resize_at: set the percentage at which you want resizing
to
occur. For example, a value of 85 means to resize when greater than
85% of
the buckets are being used. This setting does not apply to the
adhoc
cache.
3) *_cache_max_size: for the ITE and ITE_const caches, set the
maximum
number of buckets allowed in the cache. When this value is reached,
the
caches will not be grown. For the adhoc cache, set the maximum
number of
entries allowed in the cache. When this value is reached, new
entries
will not be inserted into the cache.
garbage_collector.on determines whether or not the garbage collector
is
used. memory.limit, specified as a positive integer denoting
megabytes (1MB
= 1048576 bytes), places a limit on how much memory the BDD package
is
allowed to use. memory.deamon specifies the callback routine to be
used
when the memory limit is exceeded. If a non-default memory limit is
specified, then a daemon must be given (see bdd_register_daemon for
sample
usage).
nodes.ratio determines how many new bdd_node blocks should be
allocated
after garbage collection to achieve the desired ratio between "used"
and
"unused" bdd_nodes. A larger value of nodes.ratio gives the BDDs
more room
to grow before the next garbage collection, but uses more space.
nodes.init_blocks determines the number of bdd_node blocks allocated
at
initialization. A larger value of nodes.init_blocks delays the
occurrence of
the first garbage collection.
bdd_start(n) is equivalent to bdd_start_with_parameters(n, mgr_init),
where
`mgr_init' has been initialized by bdd_set_mgr_init_dflts.
Below is a sample usage of bdd_set_mgr_init_dflts and
bdd_start_with_params:
bdd_mgr_init mgr_init;
bdd_manager *manager;
bdd_set_mgr_init_dflts(&mgr_init); /* get the defaults */
mgr_init.adhoc_cache.max_size = 50000;
/* change those you want
to */
mgr_init.node.init_blocks = 20;
manager = bdd_start_with_params(SOME_NUMBER, &mgr_init); /* create
mgr */
BDD Variable Allocation -----------------------------------------------------bdd_t *
bdd_create_variable(manager)
bdd_manager *manager;
Returns a single variable BDD formula for a new variable after
registering
the variable with the manager. This function calls bdd_get_variable
with
the arguments `manager' and the manager's nvariables value, and then
increments the manager's nvariables value. Thus, the variable
created by
bdd_create_variable is effectively placed at the end of the current
variable
ordering.
bdd_t *
bdd_get_variable(manager, variable_ID)
bdd_manager *manager;
bdd_variableId variable_ID;
/* unsigned int */
If the single variable BDD formula corresponding to variable_ID
already
exists, then return it. Otherwise, create a new single variable BDD
formula
with ID `variable_ID', register it with the manager, and return it.
This
function checks that variable_ID is less than the manager's current
value of
nvariables.
Sample usages of these functions:
bdd_manager_t *manager;
bdd_t *bdd;
manager = bdd_start(SOME_NUMBER);
for (i = 0; i < SOME_NUMBER; i++) {
bdd = bdd_get_variable(manager, i);
/* do something with bdd */
}
.
.
bdd = bdd_create_variable(manager); /* create another var, add to end
*/
.
.
bdd_end(manager);
The following is equivalent:
manager = bdd_start(0);
for (i = 0; i < SOME_NUMBER; i++) {
bdd = bdd_create_variable(manager);
/* do something with bdd */
}
.
.
bdd = bdd_create_variable(manager); /* create another var, add to end
*/
.
.
bdd_end(manager);
BDD Formula Management ------------------------------------------------------bdd_t *
bdd_dup(bdd)
bdd_t *bdd;
Returns a copy of the BDD formula.
since the
existing bdd_nodes will be shared.
No new bdd_nodes are created,
void
bdd_free(bdd)
bdd_t *bdd;
Frees the bdd_t `bdd'. During garbage collection, a bdd_node is
freed if it
cannot be reached starting from a bdd_t. Intermediate results and
unused
bdd_t's should be freed as soon as you no longer need them. For
example:
/* to compute f + gh */
temp = bdd_and(g, h, 1, 1);
result = bdd_or(f, temp, 1, 1);
bdd_free(temp);
Operations on BDD Formulas --------------------------------------------------IMPORTANT: All of the operations in this section return a pointer to a
bdd_t.
Such a bdd_t is registered with the BDD manager. The garbage collector
locates
all bdd_nodes which can be reached by recursive descent from the bdd_t's
registered with the manager, and these nodes are saved. All nodes not
reachable
are considered garbage and are destroyed. The function bdd_free
"unregisters" a
bdd_t. Thus, it is crucial that you use bdd_free to unregister bdd_t's
after
you no longer need them so that the garbage collector can reclaim as much
space
as possible.
bdd_t *
bdd_and(f, g, f_phase, g_phase)
bdd_t *f;
bdd_t *g;
boolean f_phase;
boolean g_phase;
Returns the BDD formula for the and of `f' and `g' in the phases
specified
by `f_phase' and `g_phase'. For example, bdd_and(f,g,0,1) will return
the
BDD formula for (f' and g). Don't forget the phase arguments.
bdd_t *
bdd_and_smooth(f, g, smoothing_vars)
bdd_t *f;
bdd_t *g;
array_t *smoothing_vars;
/* of bdd_t *'s */
Ands and smooths `f' and 'g' with respect to the `smoothing_vars'.
`smoothing_vars' is an array of bdd_t *'s, which are the single
variable BDD
formulas to be smoothed out.
bdd_t *
bdd_between(f_min, f_max)
bdd_t *f_min;
bdd_t *f_max;
Returns a heuristically minimized BDD containing `f_min', and
contained in
`f_max'. Calls bdd_minimize(f_min, f_min+!f_max);
bdd_t *
bdd_cofactor(f, g)
bdd_t *f;
bdd_t *g;
Returns the BDD formula of cofactor of `f' with respect to `g'. `g'
must be
different from 0. If `g' is a cube, this computes the usual
cofactor. If
`g' is not a cube, then this computes the generalized cofactor.
bdd_t *
bdd_compose(f, v, g)
bdd_t *f;
bdd_t *v;
bdd_t *g;
Returns the BDD formula with `v' replaced by `g' in `f'. `v' must be
a
single variable BDD formula.
bdd_t *
bdd_consensus(f, quantifying_vars)
bdd_t *f;
array_t *quantifying_vars;
/* of bdd_t *'s */
Returns the BDD formula of `f' universally quantified with respect to
the
variables in the array `quantifying_vars'. `quantifying_vars' is an
array
of bdd_t *'s, which are the single variable BDD formulas to be
quantified.
bdd_t *
bdd_cproject(f, var_array)
bdd_t *f;
array_t *var_array;
/* of bdd_t *'s */
Returns the BDD corresponding to the compatible projection of `f'
onto the
variables in `var_array'. `f' is interpreted as the characteristic
function of a one-to-many relation. `var_array' is an array of bdd_t
*'s,
which are single variable BDD formulas. The reference vertex is
hardcoded
to be all 1's.
The compatible projection operator is used to symbolically manipulate
equivalence classes. See the following for more details: Bill Lin,
"Implicit Manipulation of Equivalence Classes using Binary Decision
Diagrams", International Conference on Computer Design, Cambridge,
Massachusetts, November, 1991.
bdd_t *
bdd_else(bdd)
bdd_t *bdd;
Returns the BDD formula of the function when the top_var of `bdd'
evaluates
to 0. The return value takes into account that `bdd' may be a
"complemented
pointer".
bdd_t *
bdd_ite(i, t, e, i_phase, t_phase, e_phase)
bdd_t *i;
bdd_t *t;
bdd_t *e;
boolean i_phase;
boolean t_phase;
boolean e_phase;
Returns the BDD formula resulting in the ITE of `i', `t' and `e'.
For
example, bdd_ite(i, t, e, 1, 0, 1) will return the BDD formula for
((i and
t') or (i' and e)).
bdd_t *
bdd_not(f)
bdd_t *f;
Returns the BDD formula for the complement of `f'.
bdd_t *
bdd_minimize(f, c)
bdd_t *f;
bdd_t *c;
Returns bdd_minimize_with_params(f, c, BDD_MIN_OSM, TRUE, TRUE,
TRUE).
bdd_t *
bdd_minimize_with_params(f, c, match_type, compl, no_new_vars,
return_min)
bdd_t *f;
bdd_t *c;
bdd_min_match_type_t match_type;
boolean compl;
boolean no_new_vars;
boolean return_min;
Minimizes `f' with respect to the care function `c'; that is, `f' is
a don't
care wherever `c' is 0. The result contains f*c, and is contained in
f+!c.
`match_type' is one of BDD_MIN_OSM, BDD_MIN_OSDM, or BDD_MIN_TSM.
When
`compl' is TRUE, matches between sub-functions and the complements of
others
are attempted. When `no_new_vars' is TRUE, the variables in the
result will
be a subset of those in `f'. When `return_min' is TRUE, if the result
is
larger than `f', then a copy of `f' is returned. The restrict
operator of
Coudert and Madre, ICCAD '90, p. 126, is (BDD_MIN_OSDM, FALSE, TRUE,
FALSE),
and the constrain operator (which is the same as bdd_cofactor) is
(BDD_MIN_OSDM, FALSE, FALSE, FALSE). See Shiple et al., "Heuristic
Minimization of BDDs Using Don't Cares," UCB ERL Memo, UCB/ERL
M93/58, for
more details.
bdd_t *
bdd_one(manager)
bdd_manager *manager;
Returns a BDD formula pointing to the constant function 1 of the
manager.
bdd_t *
bdd_or(f, g, f_phase, g_phase)
bdd_t *f;
bdd_t *g;
boolean f_phase;
boolean g_phase;
Returns the BDD formula for the or of `f' and `g' in the phases
specified by
`f_phase' and `g_phase'. For example, bdd_or(f, g, 0, 1) will return
the BDD
formula for (f' or g).
bdd_t *
bdd_smooth(f, smoothing_vars)
bdd_t *f;
array_t *smoothing_vars;
/* of bdd_t *'s */
Returns the BDD formula of `f' existentially quantified with respect
to
the variables in the array `smoothing_vars'. `smoothing_vars' is an
array of
bdd_t *'s, which are the single variable BDD formulas to be
quantified.
bdd_t *
bdd_substitute(f, old_array, new_array)
bdd_t *f;
array_t *old_array;
/* of bdd_t *'s */
array_t *new_array;
/* of bdd_t *'s */
Substitute all old_array vars with new_array vars. `old_array' and
`new_array' are arrays of bdd_t *'s. Given two arrays of variables a
and b
consisting of member values (a1 .. an) and (b1 .. bn), replace all
occurrences of ai by bi. This could be done iteratively with
bdd_compose but
would require n passes instead of one. Thus, this algorithm is only
a
performance optimization.
bdd_t *
bdd_then(bdd)
bdd_t *bdd;
Returns the BDD formula of the function when the top_var of `bdd'
evaluates
to 1. The return value takes into account that `bdd' may be a
"complemented
pointer".
bdd_t *
bdd_top_var(bdd)
bdd_t *bdd;
Returns the BDD formula corresponding to the top variable of `bdd'.
For
example, if the top variable of 'bdd' is x, then returns the single
variable
BDD formula for x.
bdd_t *
bdd_xnor(f, g)
bdd_t *f;
bdd_t *g;
Returns the BDD formula for the xnor of `f' and `g'.
bdd_t *
bdd_xor(f, g)
bdd_t *f;
bdd_t *g;
Returns the BDD formula for the xor of `f' and `g'.
bdd_t *
bdd_zero(manager)
bdd_manager *manager;
Returns the BDD formula pointing to the constant function 0 of the
manager
(a complemented pointer to the constant function 1).
Queries about BDD Formulas --------------------------------------------------boolean
bdd_equal(f, g)
bdd_t *f;
bdd_t *g;
Checks if the two BDD are identical; returns `1' if they are, `0'
otherwise.
boolean
bdd_is_cube(f)
bdd_t *f;
Returns TRUE if `f' is a cube, else returns FALSE.
it has
a single path to the constant ONE node.
`f' is a cube if
boolean
bdd_is_tautology(f, phase)
bdd_t *f;
boolean phase;
Checks if the given function is tautologously true. `phase' indicates
the
phase to be used for `f', i.e. phase==1 checks if f==1 and phase==0
checks
if f'==1.
boolean
bdd_leq(f, g, f_phase, g_phase)
bdd_t *f;
bdd_t *g;
boolean f_phase;
boolean g_phase;
Checks for implications. `f_phase' and `g_phase' indicate the phases
to be
used for `f' and `g'. For example, bdd_leq(f, g, 1, 0) returns
returns the
value of (f => g'). (While this can be done using bdd_or and then
checking
if this result is a constant value, using bdd_leq is generally faster
and
uses less memory. Note that bdd_leq uses the ITE_const cache.)
Statistics and Other Queries ------------------------------------------------double
bdd_count_onset(fn, var_array)
bdd_t *fn;
array_t *var_array;
/* of bdd_t *'s */
Counts the number of minterms in the onset of the function `fn', over
the
variables in `var_array' (single variable BDD formulas).
`var_array'
must
contain the variables in the support of `fn'. For example, if
fn=b*d, and
var_array=[a,b,c,d], then this function returns 4.0.
bdd_manager *
bdd_get_manager(f)
bdd_t *f;
Returns the bdd_manager to which `f' is associated.
boolean
bdd_get_free(f)
bdd_t *f;
Returns 1 if bdd is free else returns 0.
bdd_node *
bdd_get_node(f, is_complemented)
bdd_t *f;
boolean *is_complemented;
/* return */
Return the regularized (i.e. uncomplemented) pointer to the bdd_node
to which `f' refers. Also, returns whether or not the bdd_node
pointer
is complemented. Note well: the address of a bdd_node changes upon
garbage collection. If your code is sensitive to the addresses of
bdd_nodes, and you invoke BDD routines which cause new bdd_nodes to
be
created, then a garbage collection may automatically be triggered.
To
prevent a garbage collection from automatically occurring, use
bdd_set_gc_mode to turn off garbage collection before a sensitive
piece
of code is executed.
void
bdd_get_stats(manager, stats)
bdd_manager *manager;
bdd_stats *stats; /* return */
Statistics for the BDD manager are returned in stats; these stats can
then
be used directly in a call to bdd_print_stats. Statistics include
information about usage of caches, number of ITE operations, number
of
bdd_node's and bdd_t's, and operation of the garbage collector.
Following is a sample usage:
bdd_stats stats;
/* allocate a struct, not a pointer to a struct
*/
bdd_get_stats(manager, &stats);
bdd_print_stats(stats, stdout);
var_set_t
*bdd_get_support(fn)
bdd_t *fn;
Returns the support of `fn' in terms of the variables in the BDD
manager.
The result is returned as a bit array (see the var_set package).
array_t *
bdd_get_varids(var_array)
array_t *var_array; /* of bdd_t * */
Given an array of single variable BDD formulas, returns an array of
the
same length whose entries are the corresponding bdd_variableIds of
the BDDs.
unsigned int
bdd_num_vars(manager)
bdd_manager *manager;
Returns the number of variables in the BDD manager.
void
bdd_print(f)
bdd_t *f;
A representation of the BDD is printed out on the standard output.
For
example, the following is printed out for the function z = !a*b +
a*!b where
the indices (variable IDs) for a and b are 0 and 1 respectively. A
"!"
before an address indicates a complemented pointer. Note that the
variables
from the root to the leaves of the BDD are in ascending order.
node: a
index 0 is v#0
index 1 is v#1
0x1007651
index = 0
T =
1
E =
0
node: b
index 0 is v#0
index 1 is v#1
ID = 0x1007652
index = 1
T =
1
E =
0
node: {z}
index 0 is v#0
index 1 is v#1
ID = !0x1007655
index = 0
ID = 0x1007652
index = 1
T =
T =
1
ID =
0x1007652
E = !0x1007652
E = 0
void
bdd_print_stats(stats, file)
bdd_stats stats;
FILE *file;
Print the statistics as returned by bdd_get_stats.
a
sample output.
BDD Package Statistics
Blocks (bdd_nodeBlock): 10
Nodes (bdd_node):
used
unused
7672
2558
total
10230
Extptr (bdd_t):
used
unused
2
350
total
352
Hashtable:
hits:
misses:
total:
peak
10230
168530 (75.0%)
56325 (25.0%)
224855 (find_or_add calls)
Caches:
Total calls:
trivial:
cached:
full:
Total lookups:
misses:
Total inserts:
collisions:
ITE
240415
64.8%
6.3%
28.9%
86590
82.6%
69487
82.7%
ITE_const
3790
41.2%
4.7%
54.1%
2229
92.0%
2051
68.6%
Garbage Collections:
collections: 6
total nodes collected: 48653
adhoc
318680
22.7%
9.0%
68.3%
246289
88.4%
---
The following is
total time:
0.48 sec
Memory Usage (bytes):
manager:
492
bdd_nodes:
327520
hashtable:
16364
extptrs (bdd_t):
4312
ITE cache:
35852
ITE_const cache:
2068
adhoc cache:
0
total:
386608
"Blocks" gives the number of pairs of bdd_nodeBlocks allocated. A
bdd_nodeBlock is an array of bdd_nodes. Blocks are allocated in pairs
since
an equal space must be maintained for the stop-and-copy garbage
collector.
Thus, at any time, only one block of the pair is being used. For a
given
manager, the number of blocks is a non-decreasing number over time.
The
number of blocks initially allocated can be controlled by using
bdd_start_with_params.
"Nodes" gives the number of bdd_nodes currently in use ("used"), and
the
number currently available for use ("unused"). Their sum is "total".
"total" divided by the number of blocks gives the number of nodes per
block.
"peak" gives the maximum value of "used" seen so far. Nodes which
are not
reachable from bdd_t's, but which have not been garbage collected
yet, are
counted under "used". If garbage nodes are found during a garbage
collection, then "used" will decrease and "unused" will increase.
Immediately following a garbage collection, more bdd_nodeBlocks are
allocated (if sufficient memory exists) so that the ratio of "used"
to
"unused" is 2 to 1. This ratio can be controlled by using
bdd_start_with_params.
"Extptr" gives the number of bdd_t's currently in use ("used") and
the
number currently available for use ("unused").
Their sum is "total".
"Hashtable" gives statistics on the table which maintains the
uniqueness of
bdd_nodes. Before a new node is inserted into the table, a
find_or_add call
is made. If the node already exists in the table, a "hit" is
recorded, and
if not, a "miss" is recorded and the node is added to the table.
"Caches" gives statistics on the caches used to improve the
performance of
the BDD package. A cache is used to remember the result of certain
computations. The ITE cache remembers the results of ITE(f,g,h)
operations;
the ITE_const cache remembers the result of ITE_const operations
(used only
for bdd_leq; see the Brace paper); the adhoc cache remembers the
results of
"adhoc operations" (bdd_and_smooth, bdd_cofactor, bdd_compose,
bdd_cproject,
bdd_smooth, and bdd_substitute). Only one ITE cache and one
ITE_const cache
are used throughout the lifetime of a manager. On the other hand, a
new
adhoc cache is created each time an application calls an adhoc
operation; the
cache is destroyed at the end of the operation.
These caches are appropriately modified so that they are consistent
after a
garbage collection. An entry ((f,g,h), result) in the ITE or
ITE_const
cache is saved only if f, g, h and `result' survived the garbage
collection.
All the entries in the adhoc cache are saved.
"Total calls" gives the number of calls to the "core" function
involved.
For example, for the ITE cache, this gives the number of calls to
bdd__ITE_.
Note that these core functions are recursive. Of the total number of
calls,
"trivial" gives the percentage for which the result is determined
trivially
(e.g. bdd__ITE_(1,g,h) = g), "cached" gives the percentage for which
the
result was found in the cache, and "full" gives the percentage for
which a
full computation had do be performed. The sum of these three
percentages is
100%.
"Total lookups" gives the number of non-trivial calls. "misses"
gives the
percentage of lookups which resulted in a cache miss. "Total
inserts" gives
the number of cache misses, which is the number of insertions into
the
cache.
The ITE and ITE_const caches do not maintain collision chains: they
are
"closed" caches. Consequently, if a key hashes to a bucket which is
already
occupied, causing a collision, then the old key is destroyed and the
new key
takes its place. "collisions" gives the percentage of insertions
which
result in a collision. The adhoc cache is an open cache, and thus
there are
no collisions.
"Garbage Collections" gives the total number of garbage collections,
the
cumulative number of nodes collected, and the total time spent
garbage
collecting.
"Memory Usage" gives a breakdown of the memory used by various
components of
the BDD manager. "manager" refers to the bdd_manager data structure.
Remember that the adhoc cache is destroyed after each call to an
operation
that uses the adhoc cache, and thus will probably read 0. The memory
usage
statistics are not updated incrementally; they are only updated by
calling
bdd_get_stats.
Upon certain memory allocation failures in the BDD package,
statistics are
printed under the title "BDD Manager Death Statistics" before the
program
exits.
int
bdd_size(bdd)
bdd_t *bdd;
Returns the size of the BDD: 1 for the constant functions 0 and 1, 2
for a
single variable BDD formula, and so on.
long
bdd_size_multiple(bddArray)
array_t *bddArray;
Returns the shared bdd size of the array of BDDs.
bdd_variableId
bdd_top_var_id(f)
bdd_t *f;
Returns the variable ID of the bdd_node to which `f' refers.
variable
The
ID of a node never changes.
one
of the constant functions.
The result is undefined if `f' refers to
Traversal of BDD Formulas ---------------------------------------------------int
bdd_gen_free(gen)
bdd_gen *gen;
Frees the bdd_gen `gen'. This should be called after breaking out of
foreach_bdd_cube or foreach_bdd_node. Always returns 0.
foreach_bdd_cube(fn, gen, cube)
bdd_t *fn;
bdd_gen *gen;
array_t *cube;
/* return of bdd_literal */
The BDD corresponding to `fn' is traversed via `gen', with `cube'
being
filled with successive members of the BDD formula onset. The cube is
an
array of bdd_literals. A bdd_literal can have values 0 (variable
appears
complemented), 1 (variable appears not complemented), or 2 (variable
does
not appear). The cover generated is disjoint. The BDD is traversed
using
depth-first search, with the ELSE branch searched before the THEN
branch.
The same array `cube' is used for each iteration; do not attempt to
free
it, and use array_dup if you need to remember it. A single array is
allocated for `cube' by the generator, and it is freed when the
generator
completes, or when bdd_gen_free is called.
foreach_bdd_node(fn, gen, node)
bdd_t *fn;
bdd_gen *gen;
bdd_node *node;
/* return */
The BDD corresponding to `fn' is traversed via `gen', with `node'
being
filled with each node in the BDD. The BDD is traversed using depthfirst
search, with the ELSE branch searched before the THEN branch, and a
node
returned only after its children have been returned. Note that the
returned
bdd_node pointer has the complement bit zeroed out.
These generators are macros.
Typical usage is:
foreach_bdd_node(fn, gen, node) {
/* do something with node */
}
Caution: If you are creating new BDDs while iterating through the
nodes or
cubes, and a garbage collection happens to be performed during this
process,
the program will abort. To avoid this, use bdd_set_gc_mode to toggle
the
garbage collector off before you start the iteration, and back on
after you
have completed the iteration.
Miscellaneous ---------------------------------------------------------------bdd_external_hooks *
bdd_get_external_hooks(manager)
bdd_manager *manager;
Returns a pointer to the external_hooks data structure of `manager'.
The
external_hooks structure is used to store application-specific data
with
the manager.
void
bdd_register_daemon(manager, daemon)
bdd_manager *manager;
void (*daemon)();
Register `daemon' as the function to be called from the BDD manager
when the
application-specified memory limit is exceeded. If no memory limit
is set,
then no daemon needs to be registered. The `daemon' must take a
pointer to
bdd_manager as its only argument and must return void:
void daemon(manager)
bdd_manager *manager;
Memory allocations in the BDD package fall into three categories:
1) bdd_nodeBlock allocations - the memory limit is checked.
If the
limit
will be exceeded, then the current, partial computation is cleared,
the
garbage collector is called, and then the daemon is called.
2) cache allocations - the memory limit is checked. If the limit
will be
exceeded, then the allocation is not made, and the BDD package
continues.
Such allocations only serve to cache previous results, and thus not
making
such allocations trades off time in favor of space.
3) small, one-time allocations or temporary allocations - the
memory limit
is not checked. (Also included in this category are allocations of
bdd_t's.)
Following is a sample usage:
static void
my_callback(manager)
bdd_manager *manager;
{
bdd_stats stats;
bdd_get_stats(manager, &stats);
bdd_print_stats(stats, stdout);
(void) fprintf(stdout, "Exceeded memory limit\n");
exit(1);
}
int check_networks_equal(ntwk1, ntwk2, num_vars)
network_t *ntwk1;
network_t *ntwk2;
int num_vars;
{
bdd_manager *manager;
bdd_mgr_init mgr_init;
bdd_set_mgr_init_dflts(&mgr_init); /* get defaults */
mgr_init.memory.limit = 10;
/* set at 10 megabytes */
mgr_init.memory.deamon = my_callback;
/* set daemon */
manager = bdd_start_with_params(num_vars, &mgr_init);
return(networks_equal(manager, ntwk1, ntwk2));
}
Another sample usage (see "man setjmp" for details on setjmp and
longjmp):
#include <setjmp.h>
/* defines jmp_buf, setjmp, longjmp */
static jmp_buf my_jmpbuf
/* global variable */
static void mem_limit_callback() /* callback routine */
{
longjmp(my_jmpbuf, 1);
}
void simplify_network(network, manager)
network_t *network;
bdd_manager *manager; /* already initialized and in use */
{
node_t *node;
lsGen gen;
bdd_register_daemon(manager, mem_limit_callback);
foreach_node(network, gen, node){
if (setjmp(my_jmpbuf) > 0) {
/* memory limit exceeded; go to next node */
continue;
}
simplify_node_using_bdds(node);
}
}
void
bdd_set_gc_mode(manager, no_gc)
bdd_manager *manager;
boolean no_gc;
If no_gc==0, turn on garbage collection, else if no_gc==1, turn off
garbage
collection.
void
bdd_dynamic_reordering(manager, algorithm_type)
bdd_manager *manager;
bdd_reorder_type_t algorithm_type;
Prints the following message: "WARNING: Dynamic variable reordering
not
implemented in the Berkeley BDD package."
Download