/**
@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 pr
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)0) {
(void) fprintf(dd->out,"Offending nodes (wrt. precision %0.30f) with diff %0.30f:\n", Cudd_ReadEpsilon(dd), absDiff);
(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 */