/** @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 */ /** @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 */