You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1795 lines
46 KiB
1795 lines
46 KiB
/**
|
|
@file
|
|
|
|
@ingroup cudd
|
|
|
|
@brief Functions for the solution of satisfiability related problems.
|
|
|
|
@author Seh-Woong Jeong, Fabio Somenzi
|
|
|
|
@copyright@parblock
|
|
Copyright (c) 1995-2015, Regents of the University of Colorado
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
Neither the name of the University of Colorado nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
@endparblock
|
|
|
|
*/
|
|
|
|
#include "util.h"
|
|
#include "cuddInt.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Constant declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#define DD_BIGGY 100000000
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
@brief Type of item stored in memoization table.
|
|
*/
|
|
typedef struct cuddPathPair {
|
|
int pos;
|
|
int neg;
|
|
} cuddPathPair;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#define WEIGHT(weight, col) ((weight) == NULL ? 1 : weight[col])
|
|
|
|
/** \cond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static enum st_retval freePathPair (void *key, void *value, void *arg);
|
|
static cuddPathPair getShortest (DdManager *dd, DdNode *root, int *cost, int *support, st_table *visited);
|
|
static DdNode * getPath (DdManager *manager, st_table *visited, DdNode *f, int *weight, int cost);
|
|
static cuddPathPair getLargest (DdManager *dd, DdNode *root, st_table *visited);
|
|
static DdNode * getCube (DdManager *manager, st_table *visited, DdNode *f, int cost);
|
|
static DdNode * ddBddMaximallyExpand(DdManager *dd, DdNode *lb, DdNode *ub, DdNode *f);
|
|
static int ddBddShortestPathUnate(DdManager *dd, DdNode *f, int *phases, st_table *table);
|
|
static DdNode * ddGetLargestCubeUnate(DdManager *dd, DdNode *f, int *phases, st_table *table);
|
|
|
|
/** \endcond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Returns the value of a %DD for a given variable assignment.
|
|
|
|
@details The variable assignment is passed in an array of int's,
|
|
that should specify a zero or a one for each variable in the support
|
|
of the function.
|
|
|
|
@return a pointer to a constant node. No new nodes are produced.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddLeq Cudd_addEvalConst
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_Eval(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
int * inputs)
|
|
{
|
|
int comple;
|
|
DdNode *ptr;
|
|
|
|
(void) dd; /* avoid warning */
|
|
comple = Cudd_IsComplement(f);
|
|
ptr = Cudd_Regular(f);
|
|
|
|
while (!cuddIsConstant(ptr)) {
|
|
if (inputs[ptr->index] == 1) {
|
|
ptr = cuddT(ptr);
|
|
} else {
|
|
comple ^= Cudd_IsComplement(cuddE(ptr));
|
|
ptr = Cudd_Regular(cuddE(ptr));
|
|
}
|
|
}
|
|
return(Cudd_NotCond(ptr,comple));
|
|
|
|
} /* end of Cudd_Eval */
|
|
|
|
|
|
/**
|
|
@brief Finds a shortest path in a %DD.
|
|
|
|
@details f is the %DD we want to get the shortest path for;
|
|
weight\[i\] is the weight of the THEN arc coming from the node whose
|
|
index is i. If weight is NULL, then unit weights are assumed for all
|
|
THEN arcs. All ELSE arcs have 0 weight. If non-NULL, both weight
|
|
and support should point to arrays with at least as many entries as
|
|
there are variables in the manager.
|
|
|
|
@return the shortest path as the %BDD of a cube.
|
|
|
|
@sideeffect support contains on return the true support of f.
|
|
If support is NULL on entry, then Cudd_ShortestPath does not compute
|
|
the true support info. length contains the length of the path.
|
|
|
|
@see Cudd_ShortestLength Cudd_LargestCube
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_ShortestPath(
|
|
DdManager * manager,
|
|
DdNode * f,
|
|
int * weight,
|
|
int * support,
|
|
int * length)
|
|
{
|
|
DdNode *F;
|
|
st_table *visited;
|
|
DdNode *sol;
|
|
cuddPathPair *rootPair;
|
|
int complement, cost;
|
|
int i;
|
|
|
|
DdNode *one = DD_ONE(manager);
|
|
DdNode *zero = DD_ZERO(manager);
|
|
|
|
/* Initialize support. Support does not depend on variable order.
|
|
** Hence, it does not need to be reinitialized if reordering occurs.
|
|
*/
|
|
if (support) {
|
|
for (i = 0; i < manager->size; i++) {
|
|
support[i] = 0;
|
|
}
|
|
}
|
|
|
|
if (f == Cudd_Not(one) || f == zero) {
|
|
*length = DD_BIGGY;
|
|
return(Cudd_Not(one));
|
|
}
|
|
/* From this point on, a path exists. */
|
|
|
|
do {
|
|
manager->reordered = 0;
|
|
|
|
/* Initialize visited table. */
|
|
visited = st_init_table(st_ptrcmp, st_ptrhash);
|
|
|
|
/* Now get the length of the shortest path(s) from f to 1. */
|
|
(void) getShortest(manager, f, weight, support, visited);
|
|
|
|
complement = Cudd_IsComplement(f);
|
|
|
|
F = Cudd_Regular(f);
|
|
|
|
if (!st_lookup(visited, F, (void **) &rootPair)) return(NULL);
|
|
|
|
if (complement) {
|
|
cost = rootPair->neg;
|
|
} else {
|
|
cost = rootPair->pos;
|
|
}
|
|
|
|
/* Recover an actual shortest path. */
|
|
sol = getPath(manager,visited,f,weight,cost);
|
|
|
|
st_foreach(visited, freePathPair, NULL);
|
|
st_free_table(visited);
|
|
|
|
} while (manager->reordered == 1);
|
|
if (manager->errorCode == CUDD_TIMEOUT_EXPIRED && manager->timeoutHandler) {
|
|
manager->timeoutHandler(manager, manager->tohArg);
|
|
}
|
|
|
|
*length = cost;
|
|
return(sol);
|
|
|
|
} /* end of Cudd_ShortestPath */
|
|
|
|
|
|
/**
|
|
@brief Finds a largest cube in a %DD.
|
|
|
|
@details f is the %DD we want to get the largest cube for. The
|
|
problem is translated into the one of finding a shortest path in f,
|
|
when both THEN and ELSE arcs are assumed to have unit length. This
|
|
yields a largest cube in the disjoint cover corresponding to the
|
|
%DD. Therefore, it is not necessarily the largest implicant of f.
|
|
|
|
@return the largest cube as a %BDD.
|
|
|
|
@sideeffect The number of literals of the cube is returned in the location
|
|
pointed by length if it is non-null.
|
|
|
|
@see Cudd_ShortestPath
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_LargestCube(
|
|
DdManager * manager,
|
|
DdNode * f,
|
|
int * length)
|
|
{
|
|
DdNode *F;
|
|
st_table *visited;
|
|
DdNode *sol;
|
|
cuddPathPair *rootPair;
|
|
int complement, cost;
|
|
|
|
DdNode *one = DD_ONE(manager);
|
|
DdNode *zero = DD_ZERO(manager);
|
|
|
|
if (f == Cudd_Not(one) || f == zero) {
|
|
if (length != NULL) {
|
|
*length = DD_BIGGY;
|
|
}
|
|
return(Cudd_Not(one));
|
|
}
|
|
/* From this point on, a path exists. */
|
|
|
|
do {
|
|
manager->reordered = 0;
|
|
|
|
/* Initialize visited table. */
|
|
visited = st_init_table(st_ptrcmp, st_ptrhash);
|
|
|
|
/* Now get the length of the shortest path(s) from f to 1. */
|
|
(void) getLargest(manager, f, visited);
|
|
|
|
complement = Cudd_IsComplement(f);
|
|
|
|
F = Cudd_Regular(f);
|
|
|
|
if (!st_lookup(visited, F, (void **) &rootPair)) return(NULL);
|
|
|
|
if (complement) {
|
|
cost = rootPair->neg;
|
|
} else {
|
|
cost = rootPair->pos;
|
|
}
|
|
|
|
/* Recover an actual shortest path. */
|
|
sol = getCube(manager,visited,f,cost);
|
|
|
|
st_foreach(visited, freePathPair, NULL);
|
|
st_free_table(visited);
|
|
|
|
} while (manager->reordered == 1);
|
|
|
|
if (length != NULL) {
|
|
*length = cost;
|
|
}
|
|
if (manager->errorCode == CUDD_TIMEOUT_EXPIRED && manager->timeoutHandler) {
|
|
manager->timeoutHandler(manager, manager->tohArg);
|
|
}
|
|
return(sol);
|
|
|
|
} /* end of Cudd_LargestCube */
|
|
|
|
|
|
/**
|
|
@brief Find the length of the shortest path(s) in a %DD.
|
|
|
|
@details f is the %DD we want to get the shortest path for;
|
|
weight\[i\] is the weight of the THEN edge coming from the node
|
|
whose index is i. All ELSE edges have 0 weight.
|
|
|
|
@return the length of the shortest path(s) if such a path is found;
|
|
a large number if the function is identically 0, and CUDD_OUT_OF_MEM
|
|
in case of failure.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_ShortestPath
|
|
|
|
*/
|
|
int
|
|
Cudd_ShortestLength(
|
|
DdManager * manager,
|
|
DdNode * f,
|
|
int * weight)
|
|
{
|
|
DdNode *F;
|
|
st_table *visited;
|
|
cuddPathPair *my_pair;
|
|
int complement, cost;
|
|
|
|
DdNode *one = DD_ONE(manager);
|
|
DdNode *zero = DD_ZERO(manager);
|
|
|
|
if (f == Cudd_Not(one) || f == zero) {
|
|
return(DD_BIGGY);
|
|
}
|
|
|
|
/* From this point on, a path exists. */
|
|
/* Initialize visited table and support. */
|
|
visited = st_init_table(st_ptrcmp, st_ptrhash);
|
|
|
|
/* Now get the length of the shortest path(s) from f to 1. */
|
|
(void) getShortest(manager, f, weight, NULL, visited);
|
|
|
|
complement = Cudd_IsComplement(f);
|
|
|
|
F = Cudd_Regular(f);
|
|
|
|
if (!st_lookup(visited, F, (void **) &my_pair)) return(CUDD_OUT_OF_MEM);
|
|
|
|
if (complement) {
|
|
cost = my_pair->neg;
|
|
} else {
|
|
cost = my_pair->pos;
|
|
}
|
|
|
|
st_foreach(visited, freePathPair, NULL);
|
|
st_free_table(visited);
|
|
|
|
return(cost);
|
|
|
|
} /* end of Cudd_ShortestLength */
|
|
|
|
|
|
/**
|
|
@brief Checks whether a %BDD is negative unate in a
|
|
variable.
|
|
|
|
@details Determines whether the function represented by %BDD f is
|
|
negative unate (monotonic decreasing) in variable i. This function
|
|
does not generate any new nodes.
|
|
|
|
@return the constant one is f is unate and the (logical) constant
|
|
zero if it is not.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Increasing
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_Decreasing(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
int i)
|
|
{
|
|
int topf, level;
|
|
DdNode *F, *fv, *fvn, *res;
|
|
DD_CTFP cacheOp;
|
|
|
|
statLine(dd);
|
|
#ifdef DD_DEBUG
|
|
assert(0 <= i && i < dd->size);
|
|
#endif
|
|
|
|
F = Cudd_Regular(f);
|
|
topf = cuddI(dd,F->index);
|
|
|
|
/* Check terminal case. If topf > i, f does not depend on var.
|
|
** Therefore, f is unate in i.
|
|
*/
|
|
level = (unsigned) dd->perm[i];
|
|
if (topf > level) {
|
|
return(DD_ONE(dd));
|
|
}
|
|
|
|
/* From now on, f is not constant. */
|
|
|
|
/* Check cache. */
|
|
cacheOp = (DD_CTFP) Cudd_Decreasing;
|
|
res = cuddCacheLookup2(dd,cacheOp,f,dd->vars[i]);
|
|
if (res != NULL) {
|
|
return(res);
|
|
}
|
|
|
|
checkWhetherToGiveUp(dd);
|
|
|
|
/* Compute cofactors. */
|
|
fv = cuddT(F); fvn = cuddE(F);
|
|
if (F != f) {
|
|
fv = Cudd_Not(fv);
|
|
fvn = Cudd_Not(fvn);
|
|
}
|
|
|
|
if (topf == level) {
|
|
/* Special case: if fv is regular, fv(1,...,1) = 1;
|
|
** If in addition fvn is complemented, fvn(1,...,1) = 0.
|
|
** But then f(1,1,...,1) > f(0,1,...,1). Hence f is not
|
|
** monotonic decreasing in i.
|
|
*/
|
|
if (!Cudd_IsComplement(fv) && Cudd_IsComplement(fvn)) {
|
|
return(Cudd_Not(DD_ONE(dd)));
|
|
}
|
|
res = Cudd_bddLeq(dd,fv,fvn) ? DD_ONE(dd) : Cudd_Not(DD_ONE(dd));
|
|
} else {
|
|
res = Cudd_Decreasing(dd,fv,i);
|
|
if (res == DD_ONE(dd)) {
|
|
res = Cudd_Decreasing(dd,fvn,i);
|
|
}
|
|
}
|
|
|
|
cuddCacheInsert2(dd,cacheOp,f,dd->vars[i],res);
|
|
return(res);
|
|
|
|
} /* end of Cudd_Decreasing */
|
|
|
|
|
|
/**
|
|
@brief Checks whether a %BDD is positive unate in a variable.
|
|
|
|
@details Determines whether the function represented by %BDD f is
|
|
positive unate (monotonic increasing) in variable i. It is based on
|
|
Cudd_Decreasing and the fact that f is monotonic increasing in i if
|
|
and only if its complement is monotonic decreasing in i.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Decreasing
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_Increasing(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
int i)
|
|
{
|
|
return(Cudd_Decreasing(dd,Cudd_Not(f),i));
|
|
|
|
} /* end of Cudd_Increasing */
|
|
|
|
|
|
/**
|
|
@brief Tells whether F and G are identical wherever D is 0.
|
|
|
|
@details F and G are either two ADDs or two BDDs. D is either a 0-1
|
|
%ADD or a %BDD. No new nodes are created.
|
|
|
|
@return 1 if F and G are equivalent, and 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddLeqUnless
|
|
|
|
*/
|
|
int
|
|
Cudd_EquivDC(
|
|
DdManager * dd,
|
|
DdNode * F,
|
|
DdNode * G,
|
|
DdNode * D)
|
|
{
|
|
DdNode *tmp, *One, *Gr, *Dr;
|
|
DdNode *Fv, *Fvn, *Gv, *Gvn, *Dv, *Dvn;
|
|
int res;
|
|
int flevel, glevel, dlevel, top;
|
|
|
|
One = DD_ONE(dd);
|
|
|
|
statLine(dd);
|
|
/* Check terminal cases. */
|
|
if (D == One || F == G) return(1);
|
|
if (D == Cudd_Not(One) || D == DD_ZERO(dd) || F == Cudd_Not(G)) return(0);
|
|
|
|
/* From now on, D is non-constant. */
|
|
|
|
/* Normalize call to increase cache efficiency. */
|
|
if (F > G) {
|
|
tmp = F;
|
|
F = G;
|
|
G = tmp;
|
|
}
|
|
if (Cudd_IsComplement(F)) {
|
|
F = Cudd_Not(F);
|
|
G = Cudd_Not(G);
|
|
}
|
|
|
|
/* From now on, F is regular. */
|
|
|
|
/* Check cache. */
|
|
tmp = cuddCacheLookup(dd,DD_EQUIV_DC_TAG,F,G,D);
|
|
if (tmp != NULL) return(tmp == One);
|
|
|
|
/* Find splitting variable. */
|
|
flevel = cuddI(dd,F->index);
|
|
Gr = Cudd_Regular(G);
|
|
glevel = cuddI(dd,Gr->index);
|
|
top = ddMin(flevel,glevel);
|
|
Dr = Cudd_Regular(D);
|
|
dlevel = dd->perm[Dr->index];
|
|
top = ddMin(top,dlevel);
|
|
|
|
/* Compute cofactors. */
|
|
if (top == flevel) {
|
|
Fv = cuddT(F);
|
|
Fvn = cuddE(F);
|
|
} else {
|
|
Fv = Fvn = F;
|
|
}
|
|
if (top == glevel) {
|
|
Gv = cuddT(Gr);
|
|
Gvn = cuddE(Gr);
|
|
if (G != Gr) {
|
|
Gv = Cudd_Not(Gv);
|
|
Gvn = Cudd_Not(Gvn);
|
|
}
|
|
} else {
|
|
Gv = Gvn = G;
|
|
}
|
|
if (top == dlevel) {
|
|
Dv = cuddT(Dr);
|
|
Dvn = cuddE(Dr);
|
|
if (D != Dr) {
|
|
Dv = Cudd_Not(Dv);
|
|
Dvn = Cudd_Not(Dvn);
|
|
}
|
|
} else {
|
|
Dv = Dvn = D;
|
|
}
|
|
|
|
/* Solve recursively. */
|
|
res = Cudd_EquivDC(dd,Fv,Gv,Dv);
|
|
if (res != 0) {
|
|
res = Cudd_EquivDC(dd,Fvn,Gvn,Dvn);
|
|
}
|
|
cuddCacheInsert(dd,DD_EQUIV_DC_TAG,F,G,D,(res) ? One : Cudd_Not(One));
|
|
|
|
return(res);
|
|
|
|
} /* end of Cudd_EquivDC */
|
|
|
|
|
|
/**
|
|
@brief Tells whether f is less than of equal to G unless D is 1.
|
|
|
|
@details f, g, and D are BDDs. No new nodes are created.
|
|
|
|
@return 1 if f is less than of equal to G, and 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_EquivDC Cudd_bddLeq Cudd_bddIteConstant
|
|
|
|
*/
|
|
int
|
|
Cudd_bddLeqUnless(
|
|
DdManager *dd,
|
|
DdNode *f,
|
|
DdNode *g,
|
|
DdNode *D)
|
|
{
|
|
DdNode *tmp, *One, *F, *G;
|
|
DdNode *Ft, *Fe, *Gt, *Ge, *Dt, *De;
|
|
int res;
|
|
int flevel, glevel, dlevel, top;
|
|
|
|
statLine(dd);
|
|
|
|
One = DD_ONE(dd);
|
|
|
|
/* Check terminal cases. */
|
|
if (f == g || g == One || f == Cudd_Not(One) || D == One ||
|
|
D == f || D == Cudd_Not(g)) return(1);
|
|
/* Check for two-operand cases. */
|
|
if (D == Cudd_Not(One) || D == g || D == Cudd_Not(f))
|
|
return(Cudd_bddLeq(dd,f,g));
|
|
if (g == Cudd_Not(One) || g == Cudd_Not(f)) return(Cudd_bddLeq(dd,f,D));
|
|
if (f == One) return(Cudd_bddLeq(dd,Cudd_Not(g),D));
|
|
|
|
/* From now on, f, g, and D are non-constant, distinct, and
|
|
** non-complementary. */
|
|
|
|
/* Normalize call to increase cache efficiency. We rely on the
|
|
** fact that f <= g unless D is equivalent to not(g) <= not(f)
|
|
** unless D and to f <= D unless g. We make sure that D is
|
|
** regular, and that at most one of f and g is complemented. We also
|
|
** ensure that when two operands can be swapped, the one with the
|
|
** lowest address comes first. */
|
|
|
|
if (Cudd_IsComplement(D)) {
|
|
if (Cudd_IsComplement(g)) {
|
|
/* Special case: if f is regular and g is complemented,
|
|
** f(1,...,1) = 1 > 0 = g(1,...,1). If D(1,...,1) = 0, return 0.
|
|
*/
|
|
if (!Cudd_IsComplement(f)) return(0);
|
|
/* !g <= D unless !f or !D <= g unless !f */
|
|
tmp = D;
|
|
D = Cudd_Not(f);
|
|
if (g < tmp) {
|
|
f = Cudd_Not(g);
|
|
g = tmp;
|
|
} else {
|
|
f = Cudd_Not(tmp);
|
|
}
|
|
} else {
|
|
if (Cudd_IsComplement(f)) {
|
|
/* !D <= !f unless g or !D <= g unless !f */
|
|
tmp = f;
|
|
f = Cudd_Not(D);
|
|
if (tmp < g) {
|
|
D = g;
|
|
g = Cudd_Not(tmp);
|
|
} else {
|
|
D = Cudd_Not(tmp);
|
|
}
|
|
} else {
|
|
/* f <= D unless g or !D <= !f unless g */
|
|
tmp = D;
|
|
D = g;
|
|
if (tmp < f) {
|
|
g = Cudd_Not(f);
|
|
f = Cudd_Not(tmp);
|
|
} else {
|
|
g = tmp;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (Cudd_IsComplement(g)) {
|
|
if (Cudd_IsComplement(f)) {
|
|
/* !g <= !f unless D or !g <= D unless !f */
|
|
tmp = f;
|
|
f = Cudd_Not(g);
|
|
if (D < tmp) {
|
|
g = D;
|
|
D = Cudd_Not(tmp);
|
|
} else {
|
|
g = Cudd_Not(tmp);
|
|
}
|
|
} else {
|
|
/* f <= g unless D or !g <= !f unless D */
|
|
if (g < f) {
|
|
tmp = g;
|
|
g = Cudd_Not(f);
|
|
f = Cudd_Not(tmp);
|
|
}
|
|
}
|
|
} else {
|
|
/* f <= g unless D or f <= D unless g */
|
|
if (D < g) {
|
|
tmp = D;
|
|
D = g;
|
|
g = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* From now on, D is regular. */
|
|
|
|
/* Check cache. */
|
|
tmp = cuddCacheLookup(dd,DD_BDD_LEQ_UNLESS_TAG,f,g,D);
|
|
if (tmp != NULL) return(tmp == One);
|
|
|
|
/* Find splitting variable. */
|
|
F = Cudd_Regular(f);
|
|
flevel = dd->perm[F->index];
|
|
G = Cudd_Regular(g);
|
|
glevel = dd->perm[G->index];
|
|
top = ddMin(flevel,glevel);
|
|
dlevel = dd->perm[D->index];
|
|
top = ddMin(top,dlevel);
|
|
|
|
/* Compute cofactors. */
|
|
if (top == flevel) {
|
|
Ft = cuddT(F);
|
|
Fe = cuddE(F);
|
|
if (F != f) {
|
|
Ft = Cudd_Not(Ft);
|
|
Fe = Cudd_Not(Fe);
|
|
}
|
|
} else {
|
|
Ft = Fe = f;
|
|
}
|
|
if (top == glevel) {
|
|
Gt = cuddT(G);
|
|
Ge = cuddE(G);
|
|
if (G != g) {
|
|
Gt = Cudd_Not(Gt);
|
|
Ge = Cudd_Not(Ge);
|
|
}
|
|
} else {
|
|
Gt = Ge = g;
|
|
}
|
|
if (top == dlevel) {
|
|
Dt = cuddT(D);
|
|
De = cuddE(D);
|
|
} else {
|
|
Dt = De = D;
|
|
}
|
|
|
|
/* Solve recursively. */
|
|
res = Cudd_bddLeqUnless(dd,Ft,Gt,Dt);
|
|
if (res != 0) {
|
|
res = Cudd_bddLeqUnless(dd,Fe,Ge,De);
|
|
}
|
|
cuddCacheInsert(dd,DD_BDD_LEQ_UNLESS_TAG,f,g,D,Cudd_NotCond(One,!res));
|
|
|
|
return(res);
|
|
|
|
} /* end of Cudd_bddLeqUnless */
|
|
|
|
|
|
/**
|
|
@brief Compares two ADDs for equality within tolerance.
|
|
|
|
@details Two ADDs are reported to be equal if the maximum difference
|
|
between them (the sup norm of their difference) is less than or
|
|
equal to the tolerance parameter. If parameter <code>pr</code> is
|
|
positive the first failure is reported to the standard output.
|
|
|
|
@return 1 if the two ADDs are equal (within tolerance); 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
int
|
|
Cudd_EqualSupNorm(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< first %ADD */,
|
|
DdNode * g /**< second %ADD */,
|
|
CUDD_VALUE_TYPE tolerance /**< maximum allowed difference */,
|
|
int pr /**< verbosity level */)
|
|
{
|
|
DdNode *fv, *fvn, *gv, *gvn, *r;
|
|
int topf, topg;
|
|
|
|
statLine(dd);
|
|
/* Check terminal cases. */
|
|
if (f == g) return(1);
|
|
if (cuddIsConstant(f) && cuddIsConstant(g)) {
|
|
if (ddEqualVal(cuddV(f),cuddV(g),tolerance)) {
|
|
return(1);
|
|
} else {
|
|
if (pr>0) {
|
|
(void) fprintf(dd->out,"Offending nodes:\n");
|
|
(void) fprintf(dd->out,
|
|
"f: address = %p\t value = %40.30f\n",
|
|
(void *) f, cuddV(f));
|
|
(void) fprintf(dd->out,
|
|
"g: address = %p\t value = %40.30f\n",
|
|
(void *) g, cuddV(g));
|
|
}
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* We only insert the result in the cache if the comparison is
|
|
** successful. Therefore, if we hit we return 1. */
|
|
r = cuddCacheLookup2(dd,(DD_CTFP)Cudd_EqualSupNorm,f,g);
|
|
if (r != NULL) {
|
|
return(1);
|
|
}
|
|
|
|
/* Compute the cofactors and solve the recursive subproblems. */
|
|
topf = cuddI(dd,f->index);
|
|
topg = cuddI(dd,g->index);
|
|
|
|
if (topf <= topg) {fv = cuddT(f); fvn = cuddE(f);} else {fv = fvn = f;}
|
|
if (topg <= topf) {gv = cuddT(g); gvn = cuddE(g);} else {gv = gvn = g;}
|
|
|
|
if (!Cudd_EqualSupNorm(dd,fv,gv,tolerance,pr)) return(0);
|
|
if (!Cudd_EqualSupNorm(dd,fvn,gvn,tolerance,pr)) return(0);
|
|
|
|
cuddCacheInsert2(dd,(DD_CTFP)Cudd_EqualSupNorm,f,g,DD_ONE(dd));
|
|
|
|
return(1);
|
|
|
|
} /* end of Cudd_EqualSupNorm */
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Compares two ADDs for equality within tolerance.]
|
|
|
|
Description [Same as Cudd_EqualSupNorm but tests for max _relative_ difference
|
|
i.e. (f-g/f)<e instead of (f-g)<e ]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
int
|
|
Cudd_EqualSupNormRel(
|
|
DdManager * dd /* manager */,
|
|
DdNode * f /* first ADD */,
|
|
DdNode * g /* second ADD */,
|
|
CUDD_VALUE_TYPE tolerance /* maximum allowed difference */,
|
|
int pr /* verbosity level */)
|
|
{
|
|
DdNode *fv, *fvn, *gv, *gvn, *r;
|
|
unsigned int topf, topg;
|
|
|
|
statLine(dd);
|
|
/* Check terminal cases. */
|
|
if (f == g) return(1);
|
|
if (Cudd_IsConstant(f) && Cudd_IsConstant(g)) {
|
|
if (ddAbs((cuddV(f) - cuddV(g))/cuddV(f)) < tolerance) {
|
|
return(1);
|
|
} else {
|
|
if (pr>0) {
|
|
(void) fprintf(dd->out,"Offending nodes:\n");
|
|
(void) fprintf(dd->out,
|
|
"f: address = %p\t value = %40.30f\n",
|
|
(void *) f, cuddV(f));
|
|
(void) fprintf(dd->out,
|
|
"g: address = %p\t value = %40.30f\n",
|
|
(void *) g, cuddV(g));
|
|
}
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* We only insert the result in the cache if the comparison is
|
|
** successful. Therefore, if we hit we return 1. */
|
|
r = cuddCacheLookup2(dd,(DD_CTFP)Cudd_EqualSupNormRel,f,g);
|
|
if (r != NULL) {
|
|
return(1);
|
|
}
|
|
|
|
/* Compute the cofactors and solve the recursive subproblems. */
|
|
topf = cuddI(dd,f->index);
|
|
topg = cuddI(dd,g->index);
|
|
|
|
if (topf <= topg) {fv = cuddT(f); fvn = cuddE(f);} else {fv = fvn = f;}
|
|
if (topg <= topf) {gv = cuddT(g); gvn = cuddE(g);} else {gv = gvn = g;}
|
|
|
|
if (!Cudd_EqualSupNormRel(dd,fv,gv,tolerance,pr)) return(0);
|
|
if (!Cudd_EqualSupNormRel(dd,fvn,gvn,tolerance,pr)) return(0);
|
|
|
|
cuddCacheInsert2(dd,(DD_CTFP)Cudd_EqualSupNormRel,f,g,DD_ONE(dd));
|
|
|
|
return(1);
|
|
|
|
} /* end of Cudd_EqualSupNormRel */
|
|
|
|
/**
|
|
@brief Expands cube to a prime implicant of f.
|
|
|
|
@return the prime if successful; NULL otherwise. In particular,
|
|
NULL is returned if cube is not a real cube or is not an implicant
|
|
of f.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddMaximallyExpand
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_bddMakePrime(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *cube /**< cube to be expanded */,
|
|
DdNode *f /**< function of which the cube is to be made a prime */)
|
|
{
|
|
DdNode *res;
|
|
|
|
if (!Cudd_bddLeq(dd,cube,f)) return(NULL);
|
|
|
|
do {
|
|
dd->reordered = 0;
|
|
res = cuddBddMakePrime(dd,cube,f);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
return(res);
|
|
|
|
} /* end of Cudd_bddMakePrime */
|
|
|
|
|
|
/**
|
|
@brief Expands lb to prime implicants of (f and ub).
|
|
|
|
@details Expands lb to all prime implicants of (f and ub) that
|
|
contain lb. Assumes that lb is contained in ub.
|
|
|
|
@return the disjunction of the primes if lb is contained in f;
|
|
returns the zero %BDD if lb is not contained in f; returns NULL in
|
|
case of failure. In particular, NULL is returned if cube is not a
|
|
real cube or is not an implicant of f. Returning the disjunction of
|
|
all prime implicants works because the resulting function is unate.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddMakePrime
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_bddMaximallyExpand(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *lb /**< cube to be expanded */,
|
|
DdNode *ub /**< upper bound cube */,
|
|
DdNode *f /**< function against which to expand */)
|
|
{
|
|
DdNode *res;
|
|
|
|
if (!Cudd_bddLeq(dd,lb,ub)) return(NULL);
|
|
|
|
do {
|
|
dd->reordered = 0;
|
|
res = ddBddMaximallyExpand(dd,lb,ub,f);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
return(res);
|
|
|
|
} /* end of Cudd_bddMaximallyExpand */
|
|
|
|
|
|
/**
|
|
@brief Find a largest prime implicant of a unate function.
|
|
|
|
@details The behavior is undefined if f is not unate. The third
|
|
argument is used to determine whether f is unate positive
|
|
(increasing) or negative (decreasing) in each of the variables in
|
|
its support.
|
|
|
|
@return the %BDD for the prime if succesful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddMaximallyExpand
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_bddLargestPrimeUnate(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *f /**< unate function */,
|
|
DdNode *phaseBdd /**< cube of the phases */)
|
|
{
|
|
DdNode *res;
|
|
int *phases;
|
|
int retval;
|
|
st_table *table;
|
|
|
|
/* Extract phase vector for quick access. */
|
|
phases = ALLOC(int, dd->size);
|
|
if (phases == NULL) return(NULL);
|
|
retval = Cudd_BddToCubeArray(dd, phaseBdd, phases);
|
|
if (retval == 0) {
|
|
FREE(phases);
|
|
return(NULL);
|
|
}
|
|
do {
|
|
dd->reordered = 0;
|
|
table = st_init_table(st_ptrcmp,st_ptrhash);
|
|
if (table == NULL) {
|
|
FREE(phases);
|
|
return(NULL);
|
|
}
|
|
(void) ddBddShortestPathUnate(dd, f, phases, table);
|
|
res = ddGetLargestCubeUnate(dd, f, phases, table);
|
|
st_free_table(table);
|
|
} while (dd->reordered == 1);
|
|
|
|
FREE(phases);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
return(res);
|
|
|
|
} /* end of Cudd_bddLargestPrimeUnate */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_bddMakePrime.
|
|
|
|
@return the prime if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
DdNode *
|
|
cuddBddMakePrime(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *cube /**< cube to be expanded */,
|
|
DdNode *f /**< function of which the cube is to be made a prime */)
|
|
{
|
|
DdNode *scan;
|
|
DdNode *t, *e;
|
|
DdNode *res = cube;
|
|
DdNode *lzero = Cudd_Not(DD_ONE(dd));
|
|
|
|
Cudd_Ref(res);
|
|
scan = cube;
|
|
while (!Cudd_IsConstantInt(scan)) {
|
|
DdNode *reg = Cudd_Regular(scan);
|
|
DdNode *var = dd->vars[reg->index];
|
|
DdNode *expanded = Cudd_bddExistAbstract(dd,res,var);
|
|
if (expanded == NULL) {
|
|
Cudd_RecursiveDeref(dd,res);
|
|
return(NULL);
|
|
}
|
|
Cudd_Ref(expanded);
|
|
if (Cudd_bddLeq(dd,expanded,f)) {
|
|
Cudd_RecursiveDeref(dd,res);
|
|
res = expanded;
|
|
} else {
|
|
Cudd_RecursiveDeref(dd,expanded);
|
|
}
|
|
cuddGetBranches(scan,&t,&e);
|
|
if (t == lzero) {
|
|
scan = e;
|
|
} else if (e == lzero) {
|
|
scan = t;
|
|
} else {
|
|
Cudd_RecursiveDeref(dd,res);
|
|
return(NULL); /* cube is not a cube */
|
|
}
|
|
}
|
|
|
|
if (scan == DD_ONE(dd)) {
|
|
Cudd_Deref(res);
|
|
return(res);
|
|
} else {
|
|
Cudd_RecursiveDeref(dd,res);
|
|
return(NULL);
|
|
}
|
|
|
|
} /* end of cuddBddMakePrime */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Frees the entries of the visited symbol table.
|
|
|
|
@return ST_CONTINUE.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static enum st_retval
|
|
freePathPair(
|
|
void * key,
|
|
void * value,
|
|
void * arg)
|
|
{
|
|
cuddPathPair *pair = (cuddPathPair *) value;
|
|
|
|
(void) key; /* avoid warning */
|
|
(void) arg; /* avoid warning */
|
|
FREE(pair);
|
|
return(ST_CONTINUE);
|
|
|
|
} /* end of freePathPair */
|
|
|
|
|
|
/**
|
|
@brief Finds the length of the shortest path(s) in a %DD.
|
|
|
|
@details Uses a local symbol table to store the lengths for each
|
|
node. Only the lengths for the regular nodes are entered in the
|
|
table, because those for the complement nodes are simply obtained by
|
|
swapping the two lenghts.
|
|
|
|
@return a pair of lengths: the length of the shortest path to 1; and
|
|
the length of the shortest path to 0. This is done so as to take
|
|
complement arcs into account.
|
|
|
|
@sideeffect Accumulates the support of the %DD in support.
|
|
|
|
*/
|
|
static cuddPathPair
|
|
getShortest(
|
|
DdManager * dd,
|
|
DdNode * root,
|
|
int * cost,
|
|
int * support,
|
|
st_table * visited)
|
|
{
|
|
cuddPathPair *my_pair, res_pair, pair_T, pair_E;
|
|
DdNode *my_root, *T, *E;
|
|
int weight;
|
|
DdNode *zero = DD_ZERO(dd);
|
|
|
|
my_root = Cudd_Regular(root);
|
|
|
|
if (st_lookup(visited, my_root, (void **) &my_pair)) {
|
|
if (Cudd_IsComplement(root)) {
|
|
res_pair.pos = my_pair->neg;
|
|
res_pair.neg = my_pair->pos;
|
|
} else {
|
|
res_pair.pos = my_pair->pos;
|
|
res_pair.neg = my_pair->neg;
|
|
}
|
|
return(res_pair);
|
|
}
|
|
|
|
/* In the case of a BDD the following test is equivalent to
|
|
** testing whether the BDD is the constant 1. This formulation,
|
|
** however, works for ADDs as well, by assuming the usual
|
|
** dichotomy of 0 and != 0.
|
|
*/
|
|
if (cuddIsConstant(my_root)) {
|
|
if (my_root != zero) {
|
|
res_pair.pos = 0;
|
|
res_pair.neg = DD_BIGGY;
|
|
} else {
|
|
res_pair.pos = DD_BIGGY;
|
|
res_pair.neg = 0;
|
|
}
|
|
} else {
|
|
T = cuddT(my_root);
|
|
E = cuddE(my_root);
|
|
|
|
pair_T = getShortest(dd, T, cost, support, visited);
|
|
pair_E = getShortest(dd, E, cost, support, visited);
|
|
weight = WEIGHT(cost, my_root->index);
|
|
res_pair.pos = ddMin(pair_T.pos+weight, pair_E.pos);
|
|
res_pair.neg = ddMin(pair_T.neg+weight, pair_E.neg);
|
|
|
|
/* Update support. */
|
|
if (support != NULL) {
|
|
support[my_root->index] = 1;
|
|
}
|
|
}
|
|
|
|
my_pair = ALLOC(cuddPathPair, 1);
|
|
if (my_pair == NULL) {
|
|
if (Cudd_IsComplement(root)) {
|
|
int tmp = res_pair.pos;
|
|
res_pair.pos = res_pair.neg;
|
|
res_pair.neg = tmp;
|
|
}
|
|
return(res_pair);
|
|
}
|
|
my_pair->pos = res_pair.pos;
|
|
my_pair->neg = res_pair.neg;
|
|
|
|
st_insert(visited, my_root, my_pair);
|
|
if (Cudd_IsComplement(root)) {
|
|
res_pair.pos = my_pair->neg;
|
|
res_pair.neg = my_pair->pos;
|
|
} else {
|
|
res_pair.pos = my_pair->pos;
|
|
res_pair.neg = my_pair->neg;
|
|
}
|
|
return(res_pair);
|
|
|
|
} /* end of getShortest */
|
|
|
|
|
|
/**
|
|
@brief Build a %BDD for a shortest path of f.
|
|
|
|
@details Given the minimum length from the root, and the minimum
|
|
lengths for each node (in visited), apply triangulation at each
|
|
node. Of the two children of each node on a shortest path, at least
|
|
one is on a shortest path. In case of ties the procedure chooses the
|
|
THEN children.
|
|
|
|
@return a pointer to the cube %BDD representing the path if
|
|
successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static DdNode *
|
|
getPath(
|
|
DdManager * manager,
|
|
st_table * visited,
|
|
DdNode * f,
|
|
int * weight,
|
|
int cost)
|
|
{
|
|
DdNode *sol, *tmp;
|
|
DdNode *my_dd, *T, *E;
|
|
cuddPathPair *T_pair, *E_pair;
|
|
int Tcost, Ecost;
|
|
int complement;
|
|
|
|
my_dd = Cudd_Regular(f);
|
|
complement = Cudd_IsComplement(f);
|
|
|
|
sol = DD_ONE(manager);
|
|
cuddRef(sol);
|
|
|
|
while (!cuddIsConstant(my_dd)) {
|
|
Tcost = cost - WEIGHT(weight, my_dd->index);
|
|
Ecost = cost;
|
|
|
|
T = cuddT(my_dd);
|
|
E = cuddE(my_dd);
|
|
|
|
if (complement) {T = Cudd_Not(T); E = Cudd_Not(E);}
|
|
|
|
st_lookup(visited, Cudd_Regular(T), (void **) &T_pair);
|
|
if ((Cudd_IsComplement(T) && T_pair->neg == Tcost) ||
|
|
(!Cudd_IsComplement(T) && T_pair->pos == Tcost)) {
|
|
tmp = cuddBddAndRecur(manager,manager->vars[my_dd->index],sol);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
sol = tmp;
|
|
|
|
complement = Cudd_IsComplement(T);
|
|
my_dd = Cudd_Regular(T);
|
|
cost = Tcost;
|
|
continue;
|
|
}
|
|
st_lookup(visited, Cudd_Regular(E), (void **) &E_pair);
|
|
if ((Cudd_IsComplement(E) && E_pair->neg == Ecost) ||
|
|
(!Cudd_IsComplement(E) && E_pair->pos == Ecost)) {
|
|
tmp = cuddBddAndRecur(manager,Cudd_Not(manager->vars[my_dd->index]),sol);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
sol = tmp;
|
|
complement = Cudd_IsComplement(E);
|
|
my_dd = Cudd_Regular(E);
|
|
cost = Ecost;
|
|
continue;
|
|
}
|
|
(void) fprintf(manager->err,"We shouldn't be here!!\n");
|
|
manager->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
|
|
cuddDeref(sol);
|
|
return(sol);
|
|
|
|
} /* end of getPath */
|
|
|
|
|
|
/**
|
|
@brief Finds the size of the largest cube(s) in a %DD.
|
|
|
|
@details This problem is translated into finding the shortest paths
|
|
from a node when both THEN and ELSE arcs have unit lengths. Uses a
|
|
local symbol table to store the lengths for each node. Only the
|
|
lengths for the regular nodes are entered in the table, because
|
|
those for the complement nodes are simply obtained by swapping the
|
|
two lenghts.
|
|
|
|
@return a pair of lengths: the length of the shortest path to 1; and
|
|
the length of the shortest path to 0. This is done so as to take
|
|
complement arcs into account.
|
|
|
|
@sideeffect none
|
|
|
|
*/
|
|
static cuddPathPair
|
|
getLargest(
|
|
DdManager * dd,
|
|
DdNode * root,
|
|
st_table * visited)
|
|
{
|
|
cuddPathPair *my_pair, res_pair, pair_T, pair_E;
|
|
DdNode *my_root, *T, *E;
|
|
DdNode *zero = DD_ZERO(dd);
|
|
|
|
my_root = Cudd_Regular(root);
|
|
|
|
if (st_lookup(visited, my_root, (void **) &my_pair)) {
|
|
if (Cudd_IsComplement(root)) {
|
|
res_pair.pos = my_pair->neg;
|
|
res_pair.neg = my_pair->pos;
|
|
} else {
|
|
res_pair.pos = my_pair->pos;
|
|
res_pair.neg = my_pair->neg;
|
|
}
|
|
return(res_pair);
|
|
}
|
|
|
|
/* In the case of a BDD the following test is equivalent to
|
|
** testing whether the BDD is the constant 1. This formulation,
|
|
** however, works for ADDs as well, by assuming the usual
|
|
** dichotomy of 0 and != 0.
|
|
*/
|
|
if (cuddIsConstant(my_root)) {
|
|
if (my_root != zero) {
|
|
res_pair.pos = 0;
|
|
res_pair.neg = DD_BIGGY;
|
|
} else {
|
|
res_pair.pos = DD_BIGGY;
|
|
res_pair.neg = 0;
|
|
}
|
|
} else {
|
|
T = cuddT(my_root);
|
|
E = cuddE(my_root);
|
|
|
|
pair_T = getLargest(dd, T, visited);
|
|
pair_E = getLargest(dd, E, visited);
|
|
res_pair.pos = ddMin(pair_T.pos, pair_E.pos) + 1;
|
|
res_pair.neg = ddMin(pair_T.neg, pair_E.neg) + 1;
|
|
}
|
|
|
|
my_pair = ALLOC(cuddPathPair, 1);
|
|
if (my_pair == NULL) { /* simply do not cache this result */
|
|
if (Cudd_IsComplement(root)) {
|
|
int tmp = res_pair.pos;
|
|
res_pair.pos = res_pair.neg;
|
|
res_pair.neg = tmp;
|
|
}
|
|
return(res_pair);
|
|
}
|
|
my_pair->pos = res_pair.pos;
|
|
my_pair->neg = res_pair.neg;
|
|
|
|
/* Caching may fail without affecting correctness. */
|
|
st_insert(visited, my_root, my_pair);
|
|
if (Cudd_IsComplement(root)) {
|
|
res_pair.pos = my_pair->neg;
|
|
res_pair.neg = my_pair->pos;
|
|
} else {
|
|
res_pair.pos = my_pair->pos;
|
|
res_pair.neg = my_pair->neg;
|
|
}
|
|
return(res_pair);
|
|
|
|
} /* end of getLargest */
|
|
|
|
|
|
/**
|
|
@brief Build a %BDD for a largest cube of f.
|
|
|
|
@details Given the minimum length from the root, and the minimum
|
|
lengths for each node (in visited), apply triangulation at each
|
|
node. Of the two children of each node on a shortest path, at least
|
|
one is on a shortest path. In case of ties the procedure chooses the
|
|
THEN children.
|
|
|
|
@return a pointer to the cube %BDD representing the path if
|
|
successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static DdNode *
|
|
getCube(
|
|
DdManager * manager,
|
|
st_table * visited,
|
|
DdNode * f,
|
|
int cost)
|
|
{
|
|
DdNode *sol, *tmp;
|
|
DdNode *my_dd, *T, *E;
|
|
cuddPathPair *T_pair, *E_pair;
|
|
int Tcost, Ecost;
|
|
int complement;
|
|
|
|
my_dd = Cudd_Regular(f);
|
|
complement = Cudd_IsComplement(f);
|
|
|
|
sol = DD_ONE(manager);
|
|
cuddRef(sol);
|
|
|
|
while (!cuddIsConstant(my_dd)) {
|
|
Tcost = cost - 1;
|
|
Ecost = cost - 1;
|
|
|
|
T = cuddT(my_dd);
|
|
E = cuddE(my_dd);
|
|
|
|
if (complement) {T = Cudd_Not(T); E = Cudd_Not(E);}
|
|
|
|
if (!st_lookup(visited, Cudd_Regular(T), (void **)&T_pair)) return(NULL);
|
|
if ((Cudd_IsComplement(T) && T_pair->neg == Tcost) ||
|
|
(!Cudd_IsComplement(T) && T_pair->pos == Tcost)) {
|
|
tmp = cuddBddAndRecur(manager,manager->vars[my_dd->index],sol);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
sol = tmp;
|
|
|
|
complement = Cudd_IsComplement(T);
|
|
my_dd = Cudd_Regular(T);
|
|
cost = Tcost;
|
|
continue;
|
|
}
|
|
if (!st_lookup(visited, Cudd_Regular(E), (void **)&E_pair)) return(NULL);
|
|
if ((Cudd_IsComplement(E) && E_pair->neg == Ecost) ||
|
|
(!Cudd_IsComplement(E) && E_pair->pos == Ecost)) {
|
|
tmp = cuddBddAndRecur(manager,Cudd_Not(manager->vars[my_dd->index]),sol);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(manager,sol);
|
|
sol = tmp;
|
|
complement = Cudd_IsComplement(E);
|
|
my_dd = Cudd_Regular(E);
|
|
cost = Ecost;
|
|
continue;
|
|
}
|
|
(void) fprintf(manager->err,"We shouldn't be here!\n");
|
|
manager->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
|
|
cuddDeref(sol);
|
|
return(sol);
|
|
|
|
} /* end of getCube */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_bddMaximallyExpand.
|
|
|
|
@details On entry to this function, ub and lb should be different
|
|
from the zero %BDD. The function then maintains this invariant.
|
|
|
|
@return set of primes or zero %BDD if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static DdNode *
|
|
ddBddMaximallyExpand(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *lb /**< cube to be expanded */,
|
|
DdNode *ub /**< upper bound cube */,
|
|
DdNode *f /**< function against which to expand */)
|
|
{
|
|
DdNode *lone, *lzero, *lbv, *lbvn, *lbnx, *ubv, *ubvn, *fv, *fvn, *res;
|
|
DdNode *F, *UB, *LB, *t, *e;
|
|
int top, toplb, topub, topf;
|
|
unsigned int index;
|
|
|
|
statLine(dd);
|
|
/* Terminal cases. */
|
|
lone = DD_ONE(dd);
|
|
lzero = Cudd_Not(lone);
|
|
assert(ub != lzero && lb != lzero);
|
|
/** There are three major terminal cases in theory:
|
|
** ub -> f : return ub
|
|
** lb == f : return lb
|
|
** not(lb -> f): return zero
|
|
** Only the second case can be checked exactly in constant time.
|
|
** For the others, we check for sufficient conditions.
|
|
*/
|
|
if (ub == f || f == lone) return(ub);
|
|
if (lb == f) return(lb);
|
|
if (f == lzero || ub == Cudd_Not(f) || lb == lone || lb == Cudd_Not(f))
|
|
return(lzero);
|
|
if (!Cudd_IsComplement(lb) && Cudd_IsComplement(f)) return(lzero);
|
|
|
|
/* Here lb and f are not constant. */
|
|
|
|
/* Check cache. Since lb and ub are cubes, their local reference counts
|
|
** are always 1. Hence, we only check the reference count of f.
|
|
*/
|
|
F = Cudd_Regular(f);
|
|
if (F->ref != 1) {
|
|
DdNode *tmp = cuddCacheLookup(dd, DD_BDD_MAX_EXP_TAG, lb, ub, f);
|
|
if (tmp != NULL) {
|
|
return(tmp);
|
|
}
|
|
}
|
|
|
|
checkWhetherToGiveUp(dd);
|
|
|
|
/* Compute cofactors. For lb we use the non-zero one in
|
|
** both branches of the recursion.
|
|
*/
|
|
LB = Cudd_Regular(lb);
|
|
UB = Cudd_Regular(ub);
|
|
topf = dd->perm[F->index];
|
|
toplb = dd->perm[LB->index];
|
|
topub = (ub == lone) ? CUDD_CONST_INDEX : (unsigned int) dd->perm[UB->index];
|
|
assert(toplb <= topub);
|
|
top = ddMin(topf,toplb);
|
|
if (toplb == top) {
|
|
index = LB->index;
|
|
lbv = cuddT(LB);
|
|
lbvn = cuddE(LB);
|
|
if (lb != LB) {
|
|
lbv = Cudd_Not(lbv);
|
|
lbvn = Cudd_Not(lbvn);
|
|
}
|
|
if (lbv == lzero) {
|
|
lbnx = lbvn;
|
|
} else {
|
|
lbnx = lbv;
|
|
}
|
|
} else {
|
|
index = F->index;
|
|
lbnx = lbv = lbvn = lb;
|
|
}
|
|
if (topub == top) {
|
|
ubv = cuddT(UB);
|
|
ubvn = cuddE(UB);
|
|
if (ub != UB) {
|
|
ubv = Cudd_Not(ubv);
|
|
ubvn = Cudd_Not(ubvn);
|
|
}
|
|
} else {
|
|
ubv = ubvn = ub;
|
|
}
|
|
if (topf == top) {
|
|
fv = cuddT(F);
|
|
fvn = cuddE(F);
|
|
if (f != F) {
|
|
fv = Cudd_Not(fv);
|
|
fvn = Cudd_Not(fvn);
|
|
}
|
|
} else {
|
|
fv = fvn = f;
|
|
}
|
|
|
|
/* Recursive calls. */
|
|
if (ubv != lzero) {
|
|
t = ddBddMaximallyExpand(dd, lbnx, ubv, fv);
|
|
if (t == NULL) return(NULL);
|
|
} else {
|
|
assert(topub == toplb && topub == top && lbv == lzero);
|
|
t = lzero;
|
|
}
|
|
cuddRef(t);
|
|
|
|
/* If the top variable appears only in lb, the positive and negative
|
|
** cofactors of each operand are the same. We want to avoid a
|
|
** needless recursive call, which would force us to give up the
|
|
** cache optimization trick based on reference counts.
|
|
*/
|
|
if (ubv == ubvn && fv == fvn) {
|
|
res = t;
|
|
} else {
|
|
if (ubvn != lzero) {
|
|
e = ddBddMaximallyExpand(dd, lbnx, ubvn, fvn);
|
|
if (e == NULL) {
|
|
Cudd_IterDerefBdd(dd,t);
|
|
return(NULL);
|
|
}
|
|
} else {
|
|
assert(topub == toplb && topub == top && lbvn == lzero);
|
|
e = lzero;
|
|
}
|
|
|
|
if (t == e) {
|
|
res = t;
|
|
} else {
|
|
cuddRef(e);
|
|
|
|
if (toplb == top) {
|
|
if (lbv == lzero) {
|
|
/* Top variable appears in negative phase. */
|
|
if (t != lone) {
|
|
DdNode *newT;
|
|
if (Cudd_IsComplement(t)) {
|
|
newT = cuddUniqueInter(dd, index, Cudd_Not(t), lzero);
|
|
if (newT == NULL) {
|
|
Cudd_IterDerefBdd(dd,t);
|
|
Cudd_IterDerefBdd(dd,e);
|
|
return(NULL);
|
|
}
|
|
newT = Cudd_Not(newT);
|
|
} else {
|
|
newT = cuddUniqueInter(dd, index, t, lone);
|
|
if (newT == NULL) {
|
|
Cudd_IterDerefBdd(dd,t);
|
|
Cudd_IterDerefBdd(dd,e);
|
|
return(NULL);
|
|
}
|
|
}
|
|
cuddRef(newT);
|
|
cuddDeref(t);
|
|
t = newT;
|
|
}
|
|
} else if (lbvn == lzero) {
|
|
/* Top variable appears in positive phase. */
|
|
if (e != lone) {
|
|
DdNode *newE;
|
|
newE = cuddUniqueInter(dd, index, lone, e);
|
|
if (newE == NULL) {
|
|
Cudd_IterDerefBdd(dd,t);
|
|
Cudd_IterDerefBdd(dd,e);
|
|
return(NULL);
|
|
}
|
|
cuddRef(newE);
|
|
cuddDeref(e);
|
|
e = newE;
|
|
}
|
|
} else {
|
|
/* Not a cube. */
|
|
Cudd_IterDerefBdd(dd,t);
|
|
Cudd_IterDerefBdd(dd,e);
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
/* Combine results. */
|
|
res = cuddBddAndRecur(dd, t, e);
|
|
if (res == NULL) {
|
|
Cudd_IterDerefBdd(dd,t);
|
|
Cudd_IterDerefBdd(dd,e);
|
|
return(NULL);
|
|
}
|
|
cuddRef(res);
|
|
Cudd_IterDerefBdd(dd,t);
|
|
Cudd_IterDerefBdd(dd,e);
|
|
}
|
|
}
|
|
|
|
/* Cache result and return. */
|
|
if (F->ref != 1) {
|
|
cuddCacheInsert(dd, DD_BDD_MAX_EXP_TAG, lb, ub, f, res);
|
|
}
|
|
cuddDeref(res);
|
|
return(res);
|
|
|
|
} /* end of ddBddMaximallyExpand */
|
|
|
|
|
|
/**
|
|
@brief Performs shortest path computation on a unate function.
|
|
|
|
@details This function is based on the observation that in the %BDD
|
|
of a unate function no node except the constant is reachable from
|
|
the root via paths of different parity.
|
|
|
|
@return the length of the shortest path to one if successful;
|
|
CUDD_OUT_OF_MEM otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see getShortest
|
|
|
|
*/
|
|
static int
|
|
ddBddShortestPathUnate(
|
|
DdManager *dd,
|
|
DdNode *f,
|
|
int *phases,
|
|
st_table *table)
|
|
{
|
|
int positive, l, lT, lE;
|
|
DdNode *lone = DD_ONE(dd);
|
|
DdNode *lzero = Cudd_Not(lone);
|
|
DdNode *F, *fv, *fvn;
|
|
|
|
if (st_lookup_int(table, f, &l)) {
|
|
return(l);
|
|
}
|
|
if (f == lone) {
|
|
l = 0;
|
|
} else if (f == lzero) {
|
|
l = DD_BIGGY;
|
|
} else {
|
|
F = Cudd_Regular(f);
|
|
fv = cuddT(F);
|
|
fvn = cuddE(F);
|
|
if (f != F) {
|
|
fv = Cudd_Not(fv);
|
|
fvn = Cudd_Not(fvn);
|
|
}
|
|
lT = ddBddShortestPathUnate(dd, fv, phases, table);
|
|
lE = ddBddShortestPathUnate(dd, fvn, phases, table);
|
|
positive = phases[F->index];
|
|
l = positive ? ddMin(lT+1, lE) : ddMin(lT, lE+1);
|
|
}
|
|
if (st_insert(table, f, (void *)(ptrint) l) == ST_OUT_OF_MEM) {
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
return(l);
|
|
|
|
} /* end of ddShortestPathUnate */
|
|
|
|
|
|
/**
|
|
@brief Extracts largest prime of a unate function.
|
|
|
|
@return the %BDD of the prime if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see getPath
|
|
|
|
*/
|
|
static DdNode *
|
|
ddGetLargestCubeUnate(
|
|
DdManager *dd,
|
|
DdNode *f,
|
|
int *phases,
|
|
st_table *table)
|
|
{
|
|
DdNode *res, *scan;
|
|
int cost;
|
|
|
|
res = DD_ONE(dd);
|
|
cuddRef(res);
|
|
scan = f;
|
|
st_lookup_int(table, scan, &cost);
|
|
|
|
while (!Cudd_IsConstantInt(scan)) {
|
|
int Pcost, Ncost, Tcost;
|
|
DdNode *tmp, *T, *E;
|
|
DdNode *rscan = Cudd_Regular(scan);
|
|
unsigned int index = rscan->index;
|
|
assert(phases[index] == 0 || phases[index] == 1);
|
|
int positive = phases[index] == 1;
|
|
Pcost = positive ? cost - 1 : cost;
|
|
Ncost = positive ? cost : cost - 1;
|
|
T = cuddT(rscan);
|
|
E = cuddE(rscan);
|
|
if (rscan != scan) {
|
|
T = Cudd_Not(T);
|
|
E = Cudd_Not(E);
|
|
}
|
|
tmp = res;
|
|
st_lookup_int(table, T, &Tcost);
|
|
if (Tcost == Pcost) {
|
|
cost = Pcost;
|
|
scan = T;
|
|
if (positive) {
|
|
tmp = cuddBddAndRecur(dd, dd->vars[index], res);
|
|
}
|
|
} else {
|
|
cost = Ncost;
|
|
scan = E;
|
|
if (!positive) {
|
|
tmp = cuddBddAndRecur(dd, Cudd_Not(dd->vars[index]), res);
|
|
}
|
|
}
|
|
if (tmp == NULL) {
|
|
Cudd_IterDerefBdd(dd, res);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_IterDerefBdd(dd, res);
|
|
res = tmp;
|
|
}
|
|
|
|
cuddDeref(res);
|
|
return(res);
|
|
|
|
} /* end of ddGetLargestCubeUnate */
|