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.
1660 lines
55 KiB
1660 lines
55 KiB
/**CFile***********************************************************************
|
|
|
|
FileName [cuddSubsetSP.c]
|
|
|
|
PackageName [cudd]
|
|
|
|
Synopsis [Procedure to subset the given BDD choosing the shortest paths
|
|
(largest cubes) in the BDD.]
|
|
|
|
|
|
Description [External procedures included in this module:
|
|
<ul>
|
|
<li> Cudd_SubsetShortPaths()
|
|
<li> Cudd_SupersetShortPaths()
|
|
</ul>
|
|
Internal procedures included in this module:
|
|
<ul>
|
|
<li> cuddSubsetShortPaths()
|
|
</ul>
|
|
Static procedures included in this module:
|
|
<ul>
|
|
<li> BuildSubsetBdd()
|
|
<li> CreatePathTable()
|
|
<li> AssessPathLength()
|
|
<li> CreateTopDist()
|
|
<li> CreateBotDist()
|
|
<li> ResizeNodeDistPages()
|
|
<li> ResizeQueuePages()
|
|
<li> stPathTableDdFree()
|
|
</ul>
|
|
]
|
|
|
|
SeeAlso [cuddSubsetHB.c]
|
|
|
|
Author [Kavita Ravi]
|
|
|
|
Copyright [Copyright (c) 1995-2012, 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.]
|
|
|
|
******************************************************************************/
|
|
|
|
#include "util.h"
|
|
#include "cuddInt.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Constant declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#define DEFAULT_PAGE_SIZE 2048 /* page size to store the BFS queue element type */
|
|
#define DEFAULT_NODE_DIST_PAGE_SIZE 2048 /* page size to store NodeDist_t type */
|
|
#define MAXSHORTINT ((DdHalfWord) ~0) /* constant defined to store
|
|
* maximum distance of a node
|
|
* from the root or the constant
|
|
*/
|
|
#define INITIAL_PAGES 128 /* number of initial pages for the
|
|
* queue/NodeDist_t type */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* structure created to store subset results for each node and distances with
|
|
* odd and even parity of the node from the root and sink. Main data structure
|
|
* in this procedure.
|
|
*/
|
|
struct NodeDist {
|
|
DdHalfWord oddTopDist;
|
|
DdHalfWord evenTopDist;
|
|
DdHalfWord oddBotDist;
|
|
DdHalfWord evenBotDist;
|
|
DdNode *regResult;
|
|
DdNode *compResult;
|
|
};
|
|
|
|
/* assorted information needed by the BuildSubsetBdd procedure. */
|
|
struct AssortedInfo {
|
|
unsigned int maxpath;
|
|
int findShortestPath;
|
|
int thresholdReached;
|
|
st_table *maxpathTable;
|
|
int threshold;
|
|
};
|
|
|
|
struct GlobalInfo {
|
|
struct NodeDist **nodeDistPages; /* pointers to the pages */
|
|
int nodeDistPageIndex; /* index to next element */
|
|
int nodeDistPage; /* index to current page */
|
|
int nodeDistPageSize; /* page size */
|
|
int maxNodeDistPages; /* number of page pointers */
|
|
struct NodeDist *currentNodeDistPage; /* current page */
|
|
DdNode ***queuePages; /* pointers to the pages */
|
|
int queuePageIndex; /* index to next element */
|
|
int queuePage; /* index to current page */
|
|
int queuePageSize; /* page size */
|
|
int maxQueuePages; /* number of page pointers */
|
|
DdNode **currentQueuePage; /* current page */
|
|
#ifdef DD_DEBUG
|
|
int numCalls;
|
|
int hits;
|
|
int thishit;
|
|
#endif
|
|
};
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
typedef struct NodeDist NodeDist_t;
|
|
typedef struct GlobalInfo GlobalInfo_t;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] DD_UNUSED = "$Id: cuddSubsetSP.c,v 1.36 2012/02/05 01:07:19 fabio Exp $";
|
|
#endif
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**AutomaticStart*************************************************************/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void ResizeNodeDistPages (DdManager *dd, GlobalInfo_t *gInfo);
|
|
static void ResizeQueuePages (DdManager *dd, GlobalInfo_t *gInfo);
|
|
static void CreateTopDist (DdManager *dd, GlobalInfo_t *gInfo, st_table *pathTable, int parentPage, int parentQueueIndex, int topLen, DdNode **childPage, int childQueueIndex, int numParents, FILE *fp);
|
|
static int CreateBotDist (DdNode *node, st_table *pathTable, unsigned int *pathLengthArray, FILE *fp);
|
|
static st_table * CreatePathTable (DdManager *dd, GlobalInfo_t *gInfo, DdNode *node, unsigned int *pathLengthArray, FILE *fp);
|
|
static unsigned int AssessPathLength (unsigned int *pathLengthArray, int threshold, int numVars, unsigned int *excess, FILE *fp);
|
|
static DdNode * BuildSubsetBdd (DdManager *dd, GlobalInfo_t *gInfo, st_table *pathTable, DdNode *node, struct AssortedInfo *info, st_table *subsetNodeTable);
|
|
static enum st_retval stPathTableDdFree (char *key, char *value, char *arg);
|
|
|
|
/**AutomaticEnd***************************************************************/
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of Exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Extracts a dense subset from a BDD with the shortest paths
|
|
heuristic.]
|
|
|
|
Description [Extracts a dense subset from a BDD. This procedure
|
|
tries to preserve the shortest paths of the input BDD, because they
|
|
give many minterms and contribute few nodes. This procedure may
|
|
increase the number of nodes in trying to create the subset or
|
|
reduce the number of nodes due to recombination as compared to the
|
|
original BDD. Hence the threshold may not be strictly adhered to. In
|
|
practice, recombination overshadows the increase in the number of
|
|
nodes and results in small BDDs as compared to the threshold. The
|
|
hardlimit specifies whether threshold needs to be strictly adhered
|
|
to. If it is set to 1, the procedure ensures that result is never
|
|
larger than the specified limit but may be considerably less than
|
|
the threshold. Returns a pointer to the BDD for the subset if
|
|
successful; NULL otherwise. The value for numVars should be as
|
|
close as possible to the size of the support of f for better
|
|
efficiency. However, it is safe to pass the value returned by
|
|
Cudd_ReadSize for numVars. If 0 is passed, then the value returned
|
|
by Cudd_ReadSize is used.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso [Cudd_SupersetShortPaths Cudd_SubsetHeavyBranch Cudd_ReadSize]
|
|
|
|
******************************************************************************/
|
|
DdNode *
|
|
Cudd_SubsetShortPaths(
|
|
DdManager * dd /* manager */,
|
|
DdNode * f /* function to be subset */,
|
|
int numVars /* number of variables in the support of f */,
|
|
int threshold /* maximum number of nodes in the subset */,
|
|
int hardlimit /* flag: 1 if threshold is a hard limit */)
|
|
{
|
|
DdNode *subset;
|
|
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddSubsetShortPaths(dd, f, numVars, threshold, hardlimit);
|
|
} while(dd->reordered == 1);
|
|
|
|
return(subset);
|
|
|
|
} /* end of Cudd_SubsetShortPaths */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Extracts a dense superset from a BDD with the shortest paths
|
|
heuristic.]
|
|
|
|
Description [Extracts a dense superset from a BDD. The procedure is
|
|
identical to the subset procedure except for the fact that it
|
|
receives the complement of the given function. Extracting the subset
|
|
of the complement function is equivalent to extracting the superset
|
|
of the function. This procedure tries to preserve the shortest
|
|
paths of the complement BDD, because they give many minterms and
|
|
contribute few nodes. This procedure may increase the number of
|
|
nodes in trying to create the superset or reduce the number of nodes
|
|
due to recombination as compared to the original BDD. Hence the
|
|
threshold may not be strictly adhered to. In practice, recombination
|
|
overshadows the increase in the number of nodes and results in small
|
|
BDDs as compared to the threshold. The hardlimit specifies whether
|
|
threshold needs to be strictly adhered to. If it is set to 1, the
|
|
procedure ensures that result is never larger than the specified
|
|
limit but may be considerably less than the threshold. Returns a
|
|
pointer to the BDD for the superset if successful; NULL
|
|
otherwise. The value for numVars should be as close as possible to
|
|
the size of the support of f for better efficiency. However, it is
|
|
safe to pass the value returned by Cudd_ReadSize for numVar. If 0
|
|
is passed, then the value returned by Cudd_ReadSize is used.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso [Cudd_SubsetShortPaths Cudd_SupersetHeavyBranch Cudd_ReadSize]
|
|
|
|
******************************************************************************/
|
|
DdNode *
|
|
Cudd_SupersetShortPaths(
|
|
DdManager * dd /* manager */,
|
|
DdNode * f /* function to be superset */,
|
|
int numVars /* number of variables in the support of f */,
|
|
int threshold /* maximum number of nodes in the subset */,
|
|
int hardlimit /* flag: 1 if threshold is a hard limit */)
|
|
{
|
|
DdNode *subset, *g;
|
|
|
|
g = Cudd_Not(f);
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddSubsetShortPaths(dd, g, numVars, threshold, hardlimit);
|
|
} while(dd->reordered == 1);
|
|
|
|
return(Cudd_NotCond(subset, (subset != NULL)));
|
|
|
|
} /* end of Cudd_SupersetShortPaths */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [The outermost procedure to return a subset of the given BDD
|
|
with the shortest path lengths.]
|
|
|
|
Description [The outermost procedure to return a subset of the given
|
|
BDD with the largest cubes. The path lengths are calculated, the maximum
|
|
allowable path length is determined and the number of nodes of this
|
|
path length that can be used to build a subset. If the threshold is
|
|
larger than the size of the original BDD, the original BDD is
|
|
returned. ]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso [Cudd_SubsetShortPaths]
|
|
|
|
******************************************************************************/
|
|
DdNode *
|
|
cuddSubsetShortPaths(
|
|
DdManager * dd /* DD manager */,
|
|
DdNode * f /* function to be subset */,
|
|
int numVars /* total number of variables in consideration */,
|
|
int threshold /* maximum number of nodes allowed in the subset */,
|
|
int hardlimit /* flag determining whether threshold should be respected strictly */)
|
|
{
|
|
GlobalInfo_t gInfo;
|
|
st_table *pathTable;
|
|
DdNode *N, *subset;
|
|
|
|
unsigned int *pathLengthArray;
|
|
unsigned int maxpath, oddLen, evenLen, pathLength, *excess;
|
|
int i;
|
|
NodeDist_t *nodeStat;
|
|
struct AssortedInfo *info;
|
|
st_table *subsetNodeTable;
|
|
|
|
gInfo.nodeDistPageSize = DEFAULT_NODE_DIST_PAGE_SIZE;
|
|
gInfo.queuePageSize = DEFAULT_PAGE_SIZE;
|
|
|
|
if (numVars == 0) {
|
|
/* set default value */
|
|
numVars = Cudd_ReadSize(dd);
|
|
}
|
|
|
|
if (threshold > numVars) {
|
|
threshold = threshold - numVars;
|
|
}
|
|
if (f == NULL) {
|
|
fprintf(dd->err, "Cannot partition, nil object\n");
|
|
dd->errorCode = CUDD_INVALID_ARG;
|
|
return(NULL);
|
|
}
|
|
if (Cudd_IsConstant(f))
|
|
return (f);
|
|
|
|
pathLengthArray = ALLOC(unsigned int, numVars+1);
|
|
for (i = 0; i < numVars+1; i++) pathLengthArray[i] = 0;
|
|
|
|
|
|
#ifdef DD_DEBUG
|
|
gInfo.numCalls = 0;
|
|
#endif
|
|
|
|
pathTable = CreatePathTable(dd, &gInfo, f, pathLengthArray, dd->err);
|
|
|
|
if ((pathTable == NULL) || (dd->errorCode == CUDD_MEMORY_OUT)) {
|
|
if (pathTable != NULL)
|
|
st_free_table(pathTable);
|
|
FREE(pathLengthArray);
|
|
return (NIL(DdNode));
|
|
}
|
|
|
|
excess = ALLOC(unsigned int, 1);
|
|
*excess = 0;
|
|
maxpath = AssessPathLength(pathLengthArray, threshold, numVars, excess,
|
|
dd->err);
|
|
|
|
if (maxpath != (unsigned) (numVars + 1)) {
|
|
|
|
info = ALLOC(struct AssortedInfo, 1);
|
|
info->maxpath = maxpath;
|
|
info->findShortestPath = 0;
|
|
info->thresholdReached = *excess;
|
|
info->maxpathTable = st_init_table(st_ptrcmp, st_ptrhash);
|
|
info->threshold = threshold;
|
|
|
|
#ifdef DD_DEBUG
|
|
(void) fprintf(dd->out, "Path length array\n");
|
|
for (i = 0; i < (numVars+1); i++) {
|
|
if (pathLengthArray[i])
|
|
(void) fprintf(dd->out, "%d ",i);
|
|
}
|
|
(void) fprintf(dd->out, "\n");
|
|
for (i = 0; i < (numVars+1); i++) {
|
|
if (pathLengthArray[i])
|
|
(void) fprintf(dd->out, "%d ",pathLengthArray[i]);
|
|
}
|
|
(void) fprintf(dd->out, "\n");
|
|
(void) fprintf(dd->out, "Maxpath = %d, Thresholdreached = %d\n",
|
|
maxpath, info->thresholdReached);
|
|
#endif
|
|
|
|
N = Cudd_Regular(f);
|
|
if (!st_lookup(pathTable, N, &nodeStat)) {
|
|
fprintf(dd->err, "Something wrong, root node must be in table\n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
FREE(excess);
|
|
FREE(info);
|
|
return(NULL);
|
|
} else {
|
|
if ((nodeStat->oddTopDist != MAXSHORTINT) &&
|
|
(nodeStat->oddBotDist != MAXSHORTINT))
|
|
oddLen = (nodeStat->oddTopDist + nodeStat->oddBotDist);
|
|
else
|
|
oddLen = MAXSHORTINT;
|
|
|
|
if ((nodeStat->evenTopDist != MAXSHORTINT) &&
|
|
(nodeStat->evenBotDist != MAXSHORTINT))
|
|
evenLen = (nodeStat->evenTopDist +nodeStat->evenBotDist);
|
|
else
|
|
evenLen = MAXSHORTINT;
|
|
|
|
pathLength = (oddLen <= evenLen) ? oddLen : evenLen;
|
|
if (pathLength > maxpath) {
|
|
(void) fprintf(dd->err, "All computations are bogus, since root has path length greater than max path length within threshold %u, %u\n", maxpath, pathLength);
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
#ifdef DD_DEBUG
|
|
gInfo.numCalls = 0;
|
|
gInfo.hits = 0;
|
|
gInfo.thishit = 0;
|
|
#endif
|
|
/* initialize a table to store computed nodes */
|
|
if (hardlimit) {
|
|
subsetNodeTable = st_init_table(st_ptrcmp, st_ptrhash);
|
|
} else {
|
|
subsetNodeTable = NIL(st_table);
|
|
}
|
|
subset = BuildSubsetBdd(dd, &gInfo, pathTable, f, info, subsetNodeTable);
|
|
if (subset != NULL) {
|
|
cuddRef(subset);
|
|
}
|
|
/* record the number of times a computed result for a node is hit */
|
|
|
|
#ifdef DD_DEBUG
|
|
(void) fprintf(dd->out, "Hits = %d, New==Node = %d, NumCalls = %d\n",
|
|
gInfo.hits, gInfo.thishit, gInfo.numCalls);
|
|
#endif
|
|
|
|
if (subsetNodeTable != NIL(st_table)) {
|
|
st_free_table(subsetNodeTable);
|
|
}
|
|
st_free_table(info->maxpathTable);
|
|
st_foreach(pathTable, stPathTableDdFree, (char *)dd);
|
|
|
|
FREE(info);
|
|
|
|
} else {/* if threshold larger than size of dd */
|
|
subset = f;
|
|
cuddRef(subset);
|
|
}
|
|
FREE(excess);
|
|
st_free_table(pathTable);
|
|
FREE(pathLengthArray);
|
|
for (i = 0; i <= gInfo.nodeDistPage; i++) FREE(gInfo.nodeDistPages[i]);
|
|
FREE(gInfo.nodeDistPages);
|
|
|
|
#ifdef DD_DEBUG
|
|
/* check containment of subset in f */
|
|
if (subset != NULL) {
|
|
if (!Cudd_bddLeq(dd, subset, f)) {
|
|
(void) fprintf(dd->err, "Wrong partition\n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (subset != NULL) {
|
|
cuddDeref(subset);
|
|
return(subset);
|
|
} else {
|
|
return(NULL);
|
|
}
|
|
|
|
} /* end of cuddSubsetShortPaths */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Resize the number of pages allocated to store the distances
|
|
related to each node.]
|
|
|
|
Description [Resize the number of pages allocated to store the distances
|
|
related to each node. The procedure moves the counter to the
|
|
next page when the end of the page is reached and allocates new
|
|
pages when necessary. ]
|
|
|
|
SideEffects [Changes the size of pages, page, page index, maximum
|
|
number of pages freeing stuff in case of memory out. ]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static void
|
|
ResizeNodeDistPages(
|
|
DdManager *dd /* DD manager */,
|
|
GlobalInfo_t *gInfo /* global information */)
|
|
{
|
|
int i;
|
|
NodeDist_t **newNodeDistPages;
|
|
|
|
/* move to next page */
|
|
gInfo->nodeDistPage++;
|
|
|
|
/* If the current page index is larger than the number of pages
|
|
* allocated, allocate a new page array. Page numbers are incremented by
|
|
* INITIAL_PAGES
|
|
*/
|
|
if (gInfo->nodeDistPage == gInfo->maxNodeDistPages) {
|
|
newNodeDistPages = ALLOC(NodeDist_t *,gInfo->maxNodeDistPages + INITIAL_PAGES);
|
|
if (newNodeDistPages == NULL) {
|
|
for (i = 0; i < gInfo->nodeDistPage; i++) FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return;
|
|
} else {
|
|
for (i = 0; i < gInfo->maxNodeDistPages; i++) {
|
|
newNodeDistPages[i] = gInfo->nodeDistPages[i];
|
|
}
|
|
/* Increase total page count */
|
|
gInfo->maxNodeDistPages += INITIAL_PAGES;
|
|
FREE(gInfo->nodeDistPages);
|
|
gInfo->nodeDistPages = newNodeDistPages;
|
|
}
|
|
}
|
|
/* Allocate a new page */
|
|
gInfo->currentNodeDistPage = gInfo->nodeDistPages[gInfo->nodeDistPage] =
|
|
ALLOC(NodeDist_t, gInfo->nodeDistPageSize);
|
|
if (gInfo->currentNodeDistPage == NULL) {
|
|
for (i = 0; i < gInfo->nodeDistPage; i++) FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return;
|
|
}
|
|
/* reset page index */
|
|
gInfo->nodeDistPageIndex = 0;
|
|
return;
|
|
|
|
} /* end of ResizeNodeDistPages */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Resize the number of pages allocated to store nodes in the BFS
|
|
traversal of the Bdd .]
|
|
|
|
Description [Resize the number of pages allocated to store nodes in the BFS
|
|
traversal of the Bdd. The procedure moves the counter to the
|
|
next page when the end of the page is reached and allocates new
|
|
pages when necessary.]
|
|
|
|
SideEffects [Changes the size of pages, page, page index, maximum
|
|
number of pages freeing stuff in case of memory out. ]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static void
|
|
ResizeQueuePages(
|
|
DdManager *dd /* DD manager */,
|
|
GlobalInfo_t *gInfo /* global information */)
|
|
{
|
|
int i;
|
|
DdNode ***newQueuePages;
|
|
|
|
gInfo->queuePage++;
|
|
/* If the current page index is larger than the number of pages
|
|
* allocated, allocate a new page array. Page numbers are incremented by
|
|
* INITIAL_PAGES
|
|
*/
|
|
if (gInfo->queuePage == gInfo->maxQueuePages) {
|
|
newQueuePages = ALLOC(DdNode **,gInfo->maxQueuePages + INITIAL_PAGES);
|
|
if (newQueuePages == NULL) {
|
|
for (i = 0; i < gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return;
|
|
} else {
|
|
for (i = 0; i < gInfo->maxQueuePages; i++) {
|
|
newQueuePages[i] = gInfo->queuePages[i];
|
|
}
|
|
/* Increase total page count */
|
|
gInfo->maxQueuePages += INITIAL_PAGES;
|
|
FREE(gInfo->queuePages);
|
|
gInfo->queuePages = newQueuePages;
|
|
}
|
|
}
|
|
/* Allocate a new page */
|
|
gInfo->currentQueuePage = gInfo->queuePages[gInfo->queuePage] =
|
|
ALLOC(DdNode *,gInfo->queuePageSize);
|
|
if (gInfo->currentQueuePage == NULL) {
|
|
for (i = 0; i < gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return;
|
|
}
|
|
/* reset page index */
|
|
gInfo->queuePageIndex = 0;
|
|
return;
|
|
|
|
} /* end of ResizeQueuePages */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [ Labels each node with its shortest distance from the root]
|
|
|
|
Description [ Labels each node with its shortest distance from the root.
|
|
This is done in a BFS search of the BDD. The nodes are processed
|
|
in a queue implemented as pages(array) to reduce memory fragmentation.
|
|
An entry is created for each node visited. The distance from the root
|
|
to the node with the corresponding parity is updated. The procedure
|
|
is called recursively each recusion level handling nodes at a given
|
|
level from the root.]
|
|
|
|
|
|
SideEffects [Creates entries in the pathTable]
|
|
|
|
SeeAlso [CreatePathTable CreateBotDist]
|
|
|
|
******************************************************************************/
|
|
static void
|
|
CreateTopDist(
|
|
DdManager *dd /* DD manager */,
|
|
GlobalInfo_t *gInfo /* global information */,
|
|
st_table * pathTable /* hast table to store path lengths */,
|
|
int parentPage /* the pointer to the page on which the first parent in the queue is to be found. */,
|
|
int parentQueueIndex /* pointer to the first parent on the page */,
|
|
int topLen /* current distance from the root */,
|
|
DdNode ** childPage /* pointer to the page on which the first child is to be added. */,
|
|
int childQueueIndex /* pointer to the first child */,
|
|
int numParents /* number of parents to process in this recursive call */,
|
|
FILE *fp /* where to write messages */)
|
|
{
|
|
NodeDist_t *nodeStat;
|
|
DdNode *N, *Nv, *Nnv, *node, *child, *regChild;
|
|
int i;
|
|
int processingDone, childrenCount;
|
|
|
|
#ifdef DD_DEBUG
|
|
gInfo->numCalls++;
|
|
|
|
/* assume this procedure comes in with only the root node*/
|
|
/* set queue index to the next available entry for addition */
|
|
/* set queue page to page of addition */
|
|
if ((gInfo->queuePages[parentPage] == childPage) && (parentQueueIndex ==
|
|
childQueueIndex)) {
|
|
fprintf(fp, "Should not happen that they are equal\n");
|
|
}
|
|
assert(gInfo->queuePageIndex == childQueueIndex);
|
|
assert(gInfo->currentQueuePage == childPage);
|
|
#endif
|
|
/* number children added to queue is initialized , needed for
|
|
* numParents in the next call
|
|
*/
|
|
childrenCount = 0;
|
|
/* process all the nodes in this level */
|
|
while (numParents) {
|
|
numParents--;
|
|
if (parentQueueIndex == gInfo->queuePageSize) {
|
|
parentPage++;
|
|
parentQueueIndex = 0;
|
|
}
|
|
/* a parent to process */
|
|
node = *(gInfo->queuePages[parentPage] + parentQueueIndex);
|
|
parentQueueIndex++;
|
|
/* get its children */
|
|
N = Cudd_Regular(node);
|
|
Nv = Cudd_T(N);
|
|
Nnv = Cudd_E(N);
|
|
|
|
Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node));
|
|
Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node));
|
|
|
|
processingDone = 2;
|
|
while (processingDone) {
|
|
/* processing the THEN and the ELSE children, the THEN
|
|
* child first
|
|
*/
|
|
if (processingDone == 2) {
|
|
child = Nv;
|
|
} else {
|
|
child = Nnv;
|
|
}
|
|
|
|
regChild = Cudd_Regular(child);
|
|
/* dont process if the child is a constant */
|
|
if (!Cudd_IsConstant(child)) {
|
|
/* check is already visited, if not add a new entry in
|
|
* the path Table
|
|
*/
|
|
if (!st_lookup(pathTable, regChild, &nodeStat)) {
|
|
/* if not in table, has never been visited */
|
|
/* create entry for table */
|
|
if (gInfo->nodeDistPageIndex == gInfo->nodeDistPageSize)
|
|
ResizeNodeDistPages(dd, gInfo);
|
|
if (dd->errorCode == CUDD_MEMORY_OUT) {
|
|
for (i = 0; i <= gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
st_free_table(pathTable);
|
|
return;
|
|
}
|
|
/* New entry for child in path Table is created here */
|
|
nodeStat = gInfo->currentNodeDistPage + gInfo->nodeDistPageIndex;
|
|
gInfo->nodeDistPageIndex++;
|
|
|
|
/* Initialize fields of the node data */
|
|
nodeStat->oddTopDist = MAXSHORTINT;
|
|
nodeStat->evenTopDist = MAXSHORTINT;
|
|
nodeStat->evenBotDist = MAXSHORTINT;
|
|
nodeStat->oddBotDist = MAXSHORTINT;
|
|
nodeStat->regResult = NULL;
|
|
nodeStat->compResult = NULL;
|
|
/* update the table entry element, the distance keeps
|
|
* track of the parity of the path from the root
|
|
*/
|
|
if (Cudd_IsComplement(child)) {
|
|
nodeStat->oddTopDist = (DdHalfWord) topLen + 1;
|
|
} else {
|
|
nodeStat->evenTopDist = (DdHalfWord) topLen + 1;
|
|
}
|
|
|
|
/* insert entry element for child in the table */
|
|
if (st_insert(pathTable, regChild,
|
|
nodeStat) == ST_OUT_OF_MEM) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
for (i = 0; i <= gInfo->nodeDistPage; i++)
|
|
FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
for (i = 0; i <= gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
st_free_table(pathTable);
|
|
return;
|
|
}
|
|
|
|
/* Create list element for this child to process its children.
|
|
* If this node has been processed already, then it appears
|
|
* in the path table and hence is never added to the list
|
|
* again.
|
|
*/
|
|
|
|
if (gInfo->queuePageIndex == gInfo->queuePageSize) ResizeQueuePages(dd, gInfo);
|
|
if (dd->errorCode == CUDD_MEMORY_OUT) {
|
|
for (i = 0; i <= gInfo->nodeDistPage; i++)
|
|
FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
st_free_table(pathTable);
|
|
return;
|
|
}
|
|
*(gInfo->currentQueuePage + gInfo->queuePageIndex) = child;
|
|
gInfo->queuePageIndex++;
|
|
|
|
childrenCount++;
|
|
} else {
|
|
/* if not been met in a path with this parity before */
|
|
/* put in list */
|
|
if (((Cudd_IsComplement(child)) && (nodeStat->oddTopDist ==
|
|
MAXSHORTINT)) || ((!Cudd_IsComplement(child)) &&
|
|
(nodeStat->evenTopDist == MAXSHORTINT))) {
|
|
|
|
if (gInfo->queuePageIndex == gInfo->queuePageSize) ResizeQueuePages(dd, gInfo);
|
|
if (dd->errorCode == CUDD_MEMORY_OUT) {
|
|
for (i = 0; i <= gInfo->nodeDistPage; i++)
|
|
FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
st_free_table(pathTable);
|
|
return;
|
|
|
|
}
|
|
*(gInfo->currentQueuePage + gInfo->queuePageIndex) = child;
|
|
gInfo->queuePageIndex++;
|
|
|
|
/* update the distance with the appropriate parity */
|
|
if (Cudd_IsComplement(child)) {
|
|
nodeStat->oddTopDist = (DdHalfWord) topLen + 1;
|
|
} else {
|
|
nodeStat->evenTopDist = (DdHalfWord) topLen + 1;
|
|
}
|
|
childrenCount++;
|
|
}
|
|
|
|
} /* end of else (not found in st_table) */
|
|
} /*end of if Not constant child */
|
|
processingDone--;
|
|
} /*end of while processing Nv, Nnv */
|
|
} /*end of while numParents */
|
|
|
|
#ifdef DD_DEBUG
|
|
assert(gInfo->queuePages[parentPage] == childPage);
|
|
assert(parentQueueIndex == childQueueIndex);
|
|
#endif
|
|
|
|
if (childrenCount != 0) {
|
|
topLen++;
|
|
childPage = gInfo->currentQueuePage;
|
|
childQueueIndex = gInfo->queuePageIndex;
|
|
CreateTopDist(dd, gInfo, pathTable, parentPage, parentQueueIndex, topLen,
|
|
childPage, childQueueIndex, childrenCount, fp);
|
|
}
|
|
|
|
return;
|
|
|
|
} /* end of CreateTopDist */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [ Labels each node with the shortest distance from the constant.]
|
|
|
|
Description [Labels each node with the shortest distance from the constant.
|
|
This is done in a DFS search of the BDD. Each node has an odd
|
|
and even parity distance from the sink (since there exists paths to both
|
|
zero and one) which is less than MAXSHORTINT. At each node these distances
|
|
are updated using the minimum distance of its children from the constant.
|
|
SInce now both the length from the root and child is known, the minimum path
|
|
length(length of the shortest path between the root and the constant that
|
|
this node lies on) of this node can be calculated and used to update the
|
|
pathLengthArray]
|
|
|
|
SideEffects [Updates Path Table and path length array]
|
|
|
|
SeeAlso [CreatePathTable CreateTopDist AssessPathLength]
|
|
|
|
******************************************************************************/
|
|
static int
|
|
CreateBotDist(
|
|
DdNode * node /* current node */,
|
|
st_table * pathTable /* path table with path lengths */,
|
|
unsigned int * pathLengthArray /* array that stores number of nodes belonging to a particular path length. */,
|
|
FILE *fp /* where to write messages */)
|
|
{
|
|
DdNode *N, *Nv, *Nnv;
|
|
DdNode *realChild;
|
|
DdNode *child, *regChild;
|
|
NodeDist_t *nodeStat, *nodeStatChild;
|
|
unsigned int oddLen, evenLen, pathLength;
|
|
DdHalfWord botDist;
|
|
int processingDone;
|
|
|
|
if (Cudd_IsConstant(node))
|
|
return(1);
|
|
N = Cudd_Regular(node);
|
|
/* each node has one table entry */
|
|
/* update as you go down the min dist of each node from
|
|
the root in each (odd and even) parity */
|
|
if (!st_lookup(pathTable, N, &nodeStat)) {
|
|
fprintf(fp, "Something wrong, the entry doesn't exist\n");
|
|
return(0);
|
|
}
|
|
|
|
/* compute length of odd parity distances */
|
|
if ((nodeStat->oddTopDist != MAXSHORTINT) &&
|
|
(nodeStat->oddBotDist != MAXSHORTINT))
|
|
oddLen = (nodeStat->oddTopDist + nodeStat->oddBotDist);
|
|
else
|
|
oddLen = MAXSHORTINT;
|
|
|
|
/* compute length of even parity distances */
|
|
if (!((nodeStat->evenTopDist == MAXSHORTINT) ||
|
|
(nodeStat->evenBotDist == MAXSHORTINT)))
|
|
evenLen = (nodeStat->evenTopDist +nodeStat->evenBotDist);
|
|
else
|
|
evenLen = MAXSHORTINT;
|
|
|
|
/* assign pathlength to minimum of the two */
|
|
pathLength = (oddLen <= evenLen) ? oddLen : evenLen;
|
|
|
|
Nv = Cudd_T(N);
|
|
Nnv = Cudd_E(N);
|
|
|
|
/* process each child */
|
|
processingDone = 0;
|
|
while (processingDone != 2) {
|
|
if (!processingDone) {
|
|
child = Nv;
|
|
} else {
|
|
child = Nnv;
|
|
}
|
|
|
|
realChild = Cudd_NotCond(child, Cudd_IsComplement(node));
|
|
regChild = Cudd_Regular(child);
|
|
if (Cudd_IsConstant(realChild)) {
|
|
/* Found a minterm; count parity and shortest distance
|
|
** from the constant.
|
|
*/
|
|
if (Cudd_IsComplement(child))
|
|
nodeStat->oddBotDist = 1;
|
|
else
|
|
nodeStat->evenBotDist = 1;
|
|
} else {
|
|
/* If node not in table, recur. */
|
|
if (!st_lookup(pathTable, regChild, &nodeStatChild)) {
|
|
fprintf(fp, "Something wrong, node in table should have been created in top dist proc.\n");
|
|
return(0);
|
|
}
|
|
|
|
if (nodeStatChild->oddBotDist == MAXSHORTINT) {
|
|
if (nodeStatChild->evenBotDist == MAXSHORTINT) {
|
|
if (!CreateBotDist(realChild, pathTable, pathLengthArray, fp))
|
|
return(0);
|
|
} else {
|
|
fprintf(fp, "Something wrong, both bot nodeStats should be there\n");
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* Update shortest distance from the constant depending on
|
|
** parity. */
|
|
|
|
if (Cudd_IsComplement(child)) {
|
|
/* If parity on the edge then add 1 to even distance
|
|
** of child to get odd parity distance and add 1 to
|
|
** odd distance of child to get even parity
|
|
** distance. Change distance of current node only if
|
|
** the calculated distance is less than existing
|
|
** distance. */
|
|
if (nodeStatChild->oddBotDist != MAXSHORTINT)
|
|
botDist = nodeStatChild->oddBotDist + 1;
|
|
else
|
|
botDist = MAXSHORTINT;
|
|
if (nodeStat->evenBotDist > botDist )
|
|
nodeStat->evenBotDist = botDist;
|
|
|
|
if (nodeStatChild->evenBotDist != MAXSHORTINT)
|
|
botDist = nodeStatChild->evenBotDist + 1;
|
|
else
|
|
botDist = MAXSHORTINT;
|
|
if (nodeStat->oddBotDist > botDist)
|
|
nodeStat->oddBotDist = botDist;
|
|
|
|
} else {
|
|
/* If parity on the edge then add 1 to even distance
|
|
** of child to get even parity distance and add 1 to
|
|
** odd distance of child to get odd parity distance.
|
|
** Change distance of current node only if the
|
|
** calculated distance is lesser than existing
|
|
** distance. */
|
|
if (nodeStatChild->evenBotDist != MAXSHORTINT)
|
|
botDist = nodeStatChild->evenBotDist + 1;
|
|
else
|
|
botDist = MAXSHORTINT;
|
|
if (nodeStat->evenBotDist > botDist)
|
|
nodeStat->evenBotDist = botDist;
|
|
|
|
if (nodeStatChild->oddBotDist != MAXSHORTINT)
|
|
botDist = nodeStatChild->oddBotDist + 1;
|
|
else
|
|
botDist = MAXSHORTINT;
|
|
if (nodeStat->oddBotDist > botDist)
|
|
nodeStat->oddBotDist = botDist;
|
|
}
|
|
} /* end of else (if not constant child ) */
|
|
processingDone++;
|
|
} /* end of while processing Nv, Nnv */
|
|
|
|
/* Compute shortest path length on the fly. */
|
|
if ((nodeStat->oddTopDist != MAXSHORTINT) &&
|
|
(nodeStat->oddBotDist != MAXSHORTINT))
|
|
oddLen = (nodeStat->oddTopDist + nodeStat->oddBotDist);
|
|
else
|
|
oddLen = MAXSHORTINT;
|
|
|
|
if ((nodeStat->evenTopDist != MAXSHORTINT) &&
|
|
(nodeStat->evenBotDist != MAXSHORTINT))
|
|
evenLen = (nodeStat->evenTopDist +nodeStat->evenBotDist);
|
|
else
|
|
evenLen = MAXSHORTINT;
|
|
|
|
/* Update path length array that has number of nodes of a particular
|
|
** path length. */
|
|
if (oddLen < pathLength ) {
|
|
if (pathLength != MAXSHORTINT)
|
|
pathLengthArray[pathLength]--;
|
|
if (oddLen != MAXSHORTINT)
|
|
pathLengthArray[oddLen]++;
|
|
pathLength = oddLen;
|
|
}
|
|
if (evenLen < pathLength ) {
|
|
if (pathLength != MAXSHORTINT)
|
|
pathLengthArray[pathLength]--;
|
|
if (evenLen != MAXSHORTINT)
|
|
pathLengthArray[evenLen]++;
|
|
}
|
|
|
|
return(1);
|
|
|
|
} /*end of CreateBotDist */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [ The outer procedure to label each node with its shortest
|
|
distance from the root and constant]
|
|
|
|
Description [ The outer procedure to label each node with its shortest
|
|
distance from the root and constant. Calls CreateTopDist and CreateBotDist.
|
|
The basis for computing the distance between root and constant is that
|
|
the distance may be the sum of even distances from the node to the root
|
|
and constant or the sum of odd distances from the node to the root and
|
|
constant. Both CreateTopDist and CreateBotDist create the odd and
|
|
even parity distances from the root and constant respectively.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso [CreateTopDist CreateBotDist]
|
|
|
|
******************************************************************************/
|
|
static st_table *
|
|
CreatePathTable(
|
|
DdManager *dd /* DD manager */,
|
|
GlobalInfo_t *gInfo /* global information */,
|
|
DdNode * node /* root of function */,
|
|
unsigned int * pathLengthArray /* array of path lengths to store nodes labeled with the various path lengths */,
|
|
FILE *fp /* where to write messages */)
|
|
{
|
|
|
|
st_table *pathTable;
|
|
NodeDist_t *nodeStat;
|
|
DdHalfWord topLen;
|
|
DdNode *N;
|
|
int i, numParents;
|
|
int insertValue;
|
|
DdNode **childPage;
|
|
int parentPage;
|
|
int childQueueIndex, parentQueueIndex;
|
|
|
|
/* Creating path Table for storing data about nodes */
|
|
pathTable = st_init_table(st_ptrcmp,st_ptrhash);
|
|
|
|
/* initializing pages for info about each node */
|
|
gInfo->maxNodeDistPages = INITIAL_PAGES;
|
|
gInfo->nodeDistPages = ALLOC(NodeDist_t *, gInfo->maxNodeDistPages);
|
|
if (gInfo->nodeDistPages == NULL) {
|
|
goto OUT_OF_MEM;
|
|
}
|
|
gInfo->nodeDistPage = 0;
|
|
gInfo->currentNodeDistPage = gInfo->nodeDistPages[gInfo->nodeDistPage] =
|
|
ALLOC(NodeDist_t, gInfo->nodeDistPageSize);
|
|
if (gInfo->currentNodeDistPage == NULL) {
|
|
for (i = 0; i <= gInfo->nodeDistPage; i++) FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
goto OUT_OF_MEM;
|
|
}
|
|
gInfo->nodeDistPageIndex = 0;
|
|
|
|
/* Initializing pages for the BFS search queue, implemented as an array. */
|
|
gInfo->maxQueuePages = INITIAL_PAGES;
|
|
gInfo->queuePages = ALLOC(DdNode **, gInfo->maxQueuePages);
|
|
if (gInfo->queuePages == NULL) {
|
|
goto OUT_OF_MEM;
|
|
}
|
|
gInfo->queuePage = 0;
|
|
gInfo->currentQueuePage = gInfo->queuePages[gInfo->queuePage] =
|
|
ALLOC(DdNode *, gInfo->queuePageSize);
|
|
if (gInfo->currentQueuePage == NULL) {
|
|
for (i = 0; i <= gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
goto OUT_OF_MEM;
|
|
}
|
|
gInfo->queuePageIndex = 0;
|
|
|
|
/* Enter the root node into the queue to start with. */
|
|
parentPage = gInfo->queuePage;
|
|
parentQueueIndex = gInfo->queuePageIndex;
|
|
topLen = 0;
|
|
*(gInfo->currentQueuePage + gInfo->queuePageIndex) = node;
|
|
gInfo->queuePageIndex++;
|
|
childPage = gInfo->currentQueuePage;
|
|
childQueueIndex = gInfo->queuePageIndex;
|
|
|
|
N = Cudd_Regular(node);
|
|
|
|
if (gInfo->nodeDistPageIndex == gInfo->nodeDistPageSize) ResizeNodeDistPages(dd, gInfo);
|
|
if (dd->errorCode == CUDD_MEMORY_OUT) {
|
|
for (i = 0; i <= gInfo->nodeDistPage; i++) FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
for (i = 0; i <= gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
st_free_table(pathTable);
|
|
goto OUT_OF_MEM;
|
|
}
|
|
|
|
nodeStat = gInfo->currentNodeDistPage + gInfo->nodeDistPageIndex;
|
|
gInfo->nodeDistPageIndex++;
|
|
|
|
nodeStat->oddTopDist = MAXSHORTINT;
|
|
nodeStat->evenTopDist = MAXSHORTINT;
|
|
nodeStat->evenBotDist = MAXSHORTINT;
|
|
nodeStat->oddBotDist = MAXSHORTINT;
|
|
nodeStat->regResult = NULL;
|
|
nodeStat->compResult = NULL;
|
|
|
|
insertValue = st_insert(pathTable, N, nodeStat);
|
|
if (insertValue == ST_OUT_OF_MEM) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
for (i = 0; i <= gInfo->nodeDistPage; i++) FREE(gInfo->nodeDistPages[i]);
|
|
FREE(gInfo->nodeDistPages);
|
|
for (i = 0; i <= gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
st_free_table(pathTable);
|
|
goto OUT_OF_MEM;
|
|
} else if (insertValue == 1) {
|
|
fprintf(fp, "Something wrong, the entry exists but didnt show up in st_lookup\n");
|
|
return(NULL);
|
|
}
|
|
|
|
if (Cudd_IsComplement(node)) {
|
|
nodeStat->oddTopDist = 0;
|
|
} else {
|
|
nodeStat->evenTopDist = 0;
|
|
}
|
|
numParents = 1;
|
|
/* call the function that counts the distance of each node from the
|
|
* root
|
|
*/
|
|
#ifdef DD_DEBUG
|
|
gInfo->numCalls = 0;
|
|
#endif
|
|
CreateTopDist(dd, gInfo, pathTable, parentPage, parentQueueIndex, (int) topLen,
|
|
childPage, childQueueIndex, numParents, fp);
|
|
if (dd->errorCode == CUDD_MEMORY_OUT) {
|
|
fprintf(fp, "Out of Memory and cant count path lengths\n");
|
|
goto OUT_OF_MEM;
|
|
}
|
|
|
|
#ifdef DD_DEBUG
|
|
gInfo->numCalls = 0;
|
|
#endif
|
|
/* call the function that counts the distance of each node from the
|
|
* constant
|
|
*/
|
|
if (!CreateBotDist(node, pathTable, pathLengthArray, fp)) return(NULL);
|
|
|
|
/* free BFS queue pages as no longer required */
|
|
for (i = 0; i <= gInfo->queuePage; i++) FREE(gInfo->queuePages[i]);
|
|
FREE(gInfo->queuePages);
|
|
return(pathTable);
|
|
|
|
OUT_OF_MEM:
|
|
(void) fprintf(fp, "Out of Memory, cannot allocate pages\n");
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
|
|
} /*end of CreatePathTable */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Chooses the maximum allowable path length of nodes under the
|
|
threshold.]
|
|
|
|
Description [Chooses the maximum allowable path length under each node.
|
|
The corner cases are when the threshold is larger than the number
|
|
of nodes in the BDD iself, in which case 'numVars + 1' is returned.
|
|
If all nodes of a particular path length are needed, then the
|
|
maxpath returned is the next one with excess nodes = 0;]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static unsigned int
|
|
AssessPathLength(
|
|
unsigned int * pathLengthArray /* array determining number of nodes belonging to the different path lengths */,
|
|
int threshold /* threshold to determine maximum allowable nodes in the subset */,
|
|
int numVars /* maximum number of variables */,
|
|
unsigned int * excess /* number of nodes labeled maxpath required in the subset */,
|
|
FILE *fp /* where to write messages */)
|
|
{
|
|
unsigned int i, maxpath;
|
|
int temp;
|
|
|
|
temp = threshold;
|
|
i = 0;
|
|
maxpath = 0;
|
|
/* quit loop if i reaches max number of variables or if temp reaches
|
|
* below zero
|
|
*/
|
|
while ((i < (unsigned) numVars+1) && (temp > 0)) {
|
|
if (pathLengthArray[i] > 0) {
|
|
maxpath = i;
|
|
temp = temp - pathLengthArray[i];
|
|
}
|
|
i++;
|
|
}
|
|
/* if all nodes of max path are needed */
|
|
if (temp >= 0) {
|
|
maxpath++; /* now maxpath becomes the next maxppath or max number
|
|
of variables */
|
|
*excess = 0;
|
|
} else { /* normal case when subset required is less than size of
|
|
original BDD */
|
|
*excess = temp + pathLengthArray[maxpath];
|
|
}
|
|
|
|
if (maxpath == 0) {
|
|
fprintf(fp, "Path Length array seems to be all zeroes, check\n");
|
|
}
|
|
return(maxpath);
|
|
|
|
} /* end of AssessPathLength */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Builds the BDD with nodes labeled with path length less than or equal to maxpath]
|
|
|
|
Description [Builds the BDD with nodes labeled with path length
|
|
under maxpath and as many nodes labeled maxpath as determined by the
|
|
threshold. The procedure uses the path table to determine which nodes
|
|
in the original bdd need to be retained. This procedure picks a
|
|
shortest path (tie break decided by taking the child with the shortest
|
|
distance to the constant) and recurs down the path till it reaches the
|
|
constant. the procedure then starts building the subset upward from
|
|
the constant. All nodes labeled by path lengths less than the given
|
|
maxpath are used to build the subset. However, in the case of nodes
|
|
that have label equal to maxpath, as many are chosen as required by
|
|
the threshold. This number is stored in the info structure in the
|
|
field thresholdReached. This field is decremented whenever a node
|
|
labeled maxpath is encountered and the nodes labeled maxpath are
|
|
aggregated in a maxpath table. As soon as the thresholdReached count
|
|
goes to 0, the shortest path from this node to the constant is found.
|
|
The extraction of nodes with the above labeling is based on the fact
|
|
that each node, labeled with a path length, P, has at least one child
|
|
labeled P or less. So extracting all nodes labeled a given path length
|
|
P ensures complete paths between the root and the constant. Extraction
|
|
of a partial number of nodes with a given path length may result in
|
|
incomplete paths and hence the additional number of nodes are grabbed
|
|
to complete the path. Since the Bdd is built bottom-up, other nodes
|
|
labeled maxpath do lie on complete paths. The procedure may cause the
|
|
subset to have a larger or smaller number of nodes than the specified
|
|
threshold. The increase in the number of nodes is caused by the
|
|
building of a subset and the reduction by recombination. However in
|
|
most cases, the recombination overshadows the increase and the
|
|
procedure returns a result with lower number of nodes than specified.
|
|
The subsetNodeTable is NIL when there is no hard limit on the number
|
|
of nodes. Further efforts towards keeping the subset closer to the
|
|
threshold number were abandoned in favour of keeping the procedure
|
|
simple and fast.]
|
|
|
|
SideEffects [SubsetNodeTable is changed if it is not NIL.]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static DdNode *
|
|
BuildSubsetBdd(
|
|
DdManager * dd /* DD manager */,
|
|
GlobalInfo_t *gInfo /* global information */,
|
|
st_table * pathTable /* path table with path lengths and computed results */,
|
|
DdNode * node /* current node */,
|
|
struct AssortedInfo * info /* assorted information structure */,
|
|
st_table * subsetNodeTable /* table storing computed results */)
|
|
{
|
|
DdNode *N, *Nv, *Nnv;
|
|
DdNode *ThenBranch, *ElseBranch, *childBranch;
|
|
DdNode *child, *regChild, *regNnv, *regNv;
|
|
NodeDist_t *nodeStatNv, *nodeStat, *nodeStatNnv;
|
|
DdNode *neW, *topv, *regNew;
|
|
char *entry;
|
|
unsigned int topid;
|
|
unsigned int childPathLength, oddLen, evenLen, NnvPathLength, NvPathLength;
|
|
unsigned int NvBotDist, NnvBotDist;
|
|
int tiebreakChild;
|
|
int processingDone, thenDone, elseDone;
|
|
|
|
DdNode *zero = Cudd_Not(DD_ONE(dd));
|
|
#ifdef DD_DEBUG
|
|
gInfo->numCalls++;
|
|
#endif
|
|
if (Cudd_IsConstant(node))
|
|
return(node);
|
|
|
|
N = Cudd_Regular(node);
|
|
/* Find node in table. */
|
|
if (!st_lookup(pathTable, N, &nodeStat)) {
|
|
(void) fprintf(dd->err, "Something wrong, node must be in table \n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
/* If the node in the table has been visited, then return the corresponding
|
|
** Dd. Since a node can become a subset of itself, its
|
|
** complement (that is te same node reached by a different parity) will
|
|
** become a superset of the original node and result in some minterms
|
|
** that were not in the original set. Hence two different results are
|
|
** maintained, corresponding to the odd and even parities.
|
|
*/
|
|
|
|
/* If this node is reached with an odd parity, get odd parity results. */
|
|
if (Cudd_IsComplement(node)) {
|
|
if (nodeStat->compResult != NULL) {
|
|
#ifdef DD_DEBUG
|
|
gInfo->hits++;
|
|
#endif
|
|
return(nodeStat->compResult);
|
|
}
|
|
} else {
|
|
/* if this node is reached with an even parity, get even parity
|
|
* results
|
|
*/
|
|
if (nodeStat->regResult != NULL) {
|
|
#ifdef DD_DEBUG
|
|
gInfo->hits++;
|
|
#endif
|
|
return(nodeStat->regResult);
|
|
}
|
|
}
|
|
|
|
|
|
/* get children */
|
|
Nv = Cudd_T(N);
|
|
Nnv = Cudd_E(N);
|
|
|
|
Nv = Cudd_NotCond(Nv, Cudd_IsComplement(node));
|
|
Nnv = Cudd_NotCond(Nnv, Cudd_IsComplement(node));
|
|
|
|
/* no child processed */
|
|
processingDone = 0;
|
|
/* then child not processed */
|
|
thenDone = 0;
|
|
ThenBranch = NULL;
|
|
/* else child not processed */
|
|
elseDone = 0;
|
|
ElseBranch = NULL;
|
|
/* if then child constant, branch is the child */
|
|
if (Cudd_IsConstant(Nv)) {
|
|
/*shortest path found */
|
|
if ((Nv == DD_ONE(dd)) && (info->findShortestPath)) {
|
|
info->findShortestPath = 0;
|
|
}
|
|
|
|
ThenBranch = Nv;
|
|
cuddRef(ThenBranch);
|
|
if (ThenBranch == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
thenDone++;
|
|
processingDone++;
|
|
NvBotDist = MAXSHORTINT;
|
|
} else {
|
|
/* Derive regular child for table lookup. */
|
|
regNv = Cudd_Regular(Nv);
|
|
/* Get node data for shortest path length. */
|
|
if (!st_lookup(pathTable, regNv, &nodeStatNv) ) {
|
|
(void) fprintf(dd->err, "Something wrong, node must be in table\n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
/* Derive shortest path length for child. */
|
|
if ((nodeStatNv->oddTopDist != MAXSHORTINT) &&
|
|
(nodeStatNv->oddBotDist != MAXSHORTINT)) {
|
|
oddLen = (nodeStatNv->oddTopDist + nodeStatNv->oddBotDist);
|
|
} else {
|
|
oddLen = MAXSHORTINT;
|
|
}
|
|
|
|
if ((nodeStatNv->evenTopDist != MAXSHORTINT) &&
|
|
(nodeStatNv->evenBotDist != MAXSHORTINT)) {
|
|
evenLen = (nodeStatNv->evenTopDist +nodeStatNv->evenBotDist);
|
|
} else {
|
|
evenLen = MAXSHORTINT;
|
|
}
|
|
|
|
NvPathLength = (oddLen <= evenLen) ? oddLen : evenLen;
|
|
NvBotDist = (oddLen <= evenLen) ? nodeStatNv->oddBotDist:
|
|
nodeStatNv->evenBotDist;
|
|
}
|
|
/* if else child constant, branch is the child */
|
|
if (Cudd_IsConstant(Nnv)) {
|
|
/*shortest path found */
|
|
if ((Nnv == DD_ONE(dd)) && (info->findShortestPath)) {
|
|
info->findShortestPath = 0;
|
|
}
|
|
|
|
ElseBranch = Nnv;
|
|
cuddRef(ElseBranch);
|
|
if (ElseBranch == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
elseDone++;
|
|
processingDone++;
|
|
NnvBotDist = MAXSHORTINT;
|
|
} else {
|
|
/* Derive regular child for table lookup. */
|
|
regNnv = Cudd_Regular(Nnv);
|
|
/* Get node data for shortest path length. */
|
|
if (!st_lookup(pathTable, regNnv, &nodeStatNnv) ) {
|
|
(void) fprintf(dd->err, "Something wrong, node must be in table\n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
/* Derive shortest path length for child. */
|
|
if ((nodeStatNnv->oddTopDist != MAXSHORTINT) &&
|
|
(nodeStatNnv->oddBotDist != MAXSHORTINT)) {
|
|
oddLen = (nodeStatNnv->oddTopDist + nodeStatNnv->oddBotDist);
|
|
} else {
|
|
oddLen = MAXSHORTINT;
|
|
}
|
|
|
|
if ((nodeStatNnv->evenTopDist != MAXSHORTINT) &&
|
|
(nodeStatNnv->evenBotDist != MAXSHORTINT)) {
|
|
evenLen = (nodeStatNnv->evenTopDist +nodeStatNnv->evenBotDist);
|
|
} else {
|
|
evenLen = MAXSHORTINT;
|
|
}
|
|
|
|
NnvPathLength = (oddLen <= evenLen) ? oddLen : evenLen;
|
|
NnvBotDist = (oddLen <= evenLen) ? nodeStatNnv->oddBotDist :
|
|
nodeStatNnv->evenBotDist;
|
|
}
|
|
|
|
tiebreakChild = (NvBotDist <= NnvBotDist) ? 1 : 0;
|
|
/* while both children not processed */
|
|
while (processingDone != 2) {
|
|
if (!processingDone) {
|
|
/* if no child processed */
|
|
/* pick the child with shortest path length and record which one
|
|
* picked
|
|
*/
|
|
if ((NvPathLength < NnvPathLength) ||
|
|
((NvPathLength == NnvPathLength) && (tiebreakChild == 1))) {
|
|
child = Nv;
|
|
regChild = regNv;
|
|
thenDone = 1;
|
|
childPathLength = NvPathLength;
|
|
} else {
|
|
child = Nnv;
|
|
regChild = regNnv;
|
|
elseDone = 1;
|
|
childPathLength = NnvPathLength;
|
|
} /* then path length less than else path length */
|
|
} else {
|
|
/* if one child processed, process the other */
|
|
if (thenDone) {
|
|
child = Nnv;
|
|
regChild = regNnv;
|
|
elseDone = 1;
|
|
childPathLength = NnvPathLength;
|
|
} else {
|
|
child = Nv;
|
|
regChild = regNv;
|
|
thenDone = 1;
|
|
childPathLength = NvPathLength;
|
|
} /* end of else pick the Then child if ELSE child processed */
|
|
} /* end of else one child has been processed */
|
|
|
|
/* ignore (replace with constant 0) all nodes which lie on paths larger
|
|
* than the maximum length of the path required
|
|
*/
|
|
if (childPathLength > info->maxpath) {
|
|
/* record nodes visited */
|
|
childBranch = zero;
|
|
} else {
|
|
if (childPathLength < info->maxpath) {
|
|
if (info->findShortestPath) {
|
|
info->findShortestPath = 0;
|
|
}
|
|
childBranch = BuildSubsetBdd(dd, gInfo, pathTable, child, info,
|
|
subsetNodeTable);
|
|
|
|
} else { /* Case: path length of node = maxpath */
|
|
/* If the node labeled with maxpath is found in the
|
|
** maxpathTable, use it to build the subset BDD. */
|
|
if (st_lookup(info->maxpathTable, regChild, &entry)) {
|
|
/* When a node that is already been chosen is hit,
|
|
** the quest for a complete path is over. */
|
|
if (info->findShortestPath) {
|
|
info->findShortestPath = 0;
|
|
}
|
|
childBranch = BuildSubsetBdd(dd, gInfo, pathTable, child, info,
|
|
subsetNodeTable);
|
|
} else {
|
|
/* If node is not found in the maxpathTable and
|
|
** the threshold has been reached, then if the
|
|
** path needs to be completed, continue. Else
|
|
** replace the node with a zero. */
|
|
if (info->thresholdReached <= 0) {
|
|
if (info->findShortestPath) {
|
|
if (st_insert(info->maxpathTable, regChild,
|
|
NULL) == ST_OUT_OF_MEM) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
(void) fprintf(dd->err, "OUT of memory\n");
|
|
info->thresholdReached = 0;
|
|
childBranch = zero;
|
|
} else {
|
|
info->thresholdReached--;
|
|
childBranch = BuildSubsetBdd(dd, gInfo, pathTable,
|
|
child, info,subsetNodeTable);
|
|
}
|
|
} else { /* not find shortest path, we dont need this
|
|
node */
|
|
childBranch = zero;
|
|
}
|
|
} else { /* Threshold hasn't been reached,
|
|
** need the node. */
|
|
if (st_insert(info->maxpathTable, regChild,
|
|
NULL) == ST_OUT_OF_MEM) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
(void) fprintf(dd->err, "OUT of memory\n");
|
|
info->thresholdReached = 0;
|
|
childBranch = zero;
|
|
} else {
|
|
info->thresholdReached--;
|
|
if (info->thresholdReached <= 0) {
|
|
info->findShortestPath = 1;
|
|
}
|
|
childBranch = BuildSubsetBdd(dd, gInfo, pathTable,
|
|
child, info, subsetNodeTable);
|
|
|
|
} /* end of st_insert successful */
|
|
} /* end of threshold hasnt been reached yet */
|
|
} /* end of else node not found in maxpath table */
|
|
} /* end of if (path length of node = maxpath) */
|
|
} /* end if !(childPathLength > maxpath) */
|
|
if (childBranch == NULL) {
|
|
/* deref other stuff incase reordering has taken place */
|
|
if (ThenBranch != NULL) {
|
|
Cudd_RecursiveDeref(dd, ThenBranch);
|
|
ThenBranch = NULL;
|
|
}
|
|
if (ElseBranch != NULL) {
|
|
Cudd_RecursiveDeref(dd, ElseBranch);
|
|
ElseBranch = NULL;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
cuddRef(childBranch);
|
|
|
|
if (child == Nv) {
|
|
ThenBranch = childBranch;
|
|
} else {
|
|
ElseBranch = childBranch;
|
|
}
|
|
processingDone++;
|
|
|
|
} /*end of while processing Nv, Nnv */
|
|
|
|
info->findShortestPath = 0;
|
|
topid = Cudd_NodeReadIndex(N);
|
|
topv = Cudd_ReadVars(dd, topid);
|
|
cuddRef(topv);
|
|
neW = cuddBddIteRecur(dd, topv, ThenBranch, ElseBranch);
|
|
if (neW != NULL) {
|
|
cuddRef(neW);
|
|
}
|
|
Cudd_RecursiveDeref(dd, topv);
|
|
Cudd_RecursiveDeref(dd, ThenBranch);
|
|
Cudd_RecursiveDeref(dd, ElseBranch);
|
|
|
|
|
|
/* Hard Limit of threshold has been imposed */
|
|
if (subsetNodeTable != NIL(st_table)) {
|
|
/* check if a new node is created */
|
|
regNew = Cudd_Regular(neW);
|
|
/* subset node table keeps all new nodes that have been created to keep
|
|
* a running count of how many nodes have been built in the subset.
|
|
*/
|
|
if (!st_lookup(subsetNodeTable, regNew, &entry)) {
|
|
if (!Cudd_IsConstant(regNew)) {
|
|
if (st_insert(subsetNodeTable, regNew,
|
|
NULL) == ST_OUT_OF_MEM) {
|
|
(void) fprintf(dd->err, "Out of memory\n");
|
|
return (NULL);
|
|
}
|
|
if (st_count(subsetNodeTable) > info->threshold) {
|
|
info->thresholdReached = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (neW == NULL) {
|
|
return(NULL);
|
|
} else {
|
|
/*store computed result in regular form*/
|
|
if (Cudd_IsComplement(node)) {
|
|
nodeStat->compResult = neW;
|
|
cuddRef(nodeStat->compResult);
|
|
/* if the new node is the same as the corresponding node in the
|
|
* original bdd then its complement need not be computed as it
|
|
* cannot be larger than the node itself
|
|
*/
|
|
if (neW == node) {
|
|
#ifdef DD_DEBUG
|
|
gInfo->thishit++;
|
|
#endif
|
|
/* if a result for the node has already been computed, then
|
|
* it can only be smaller than teh node itself. hence store
|
|
* the node result in order not to break recombination
|
|
*/
|
|
if (nodeStat->regResult != NULL) {
|
|
Cudd_RecursiveDeref(dd, nodeStat->regResult);
|
|
}
|
|
nodeStat->regResult = Cudd_Not(neW);
|
|
cuddRef(nodeStat->regResult);
|
|
}
|
|
|
|
} else {
|
|
nodeStat->regResult = neW;
|
|
cuddRef(nodeStat->regResult);
|
|
if (neW == node) {
|
|
#ifdef DD_DEBUG
|
|
gInfo->thishit++;
|
|
#endif
|
|
if (nodeStat->compResult != NULL) {
|
|
Cudd_RecursiveDeref(dd, nodeStat->compResult);
|
|
}
|
|
nodeStat->compResult = Cudd_Not(neW);
|
|
cuddRef(nodeStat->compResult);
|
|
}
|
|
}
|
|
|
|
cuddDeref(neW);
|
|
return(neW);
|
|
} /* end of else i.e. Subset != NULL */
|
|
} /* end of BuildSubsetBdd */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Procedure to free te result dds stored in the NodeDist pages.]
|
|
|
|
Description [None]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static enum st_retval
|
|
stPathTableDdFree(
|
|
char * key,
|
|
char * value,
|
|
char * arg)
|
|
{
|
|
NodeDist_t *nodeStat;
|
|
DdManager *dd;
|
|
|
|
nodeStat = (NodeDist_t *)value;
|
|
dd = (DdManager *)arg;
|
|
if (nodeStat->regResult != NULL) {
|
|
Cudd_RecursiveDeref(dd, nodeStat->regResult);
|
|
}
|
|
if (nodeStat->compResult != NULL) {
|
|
Cudd_RecursiveDeref(dd, nodeStat->compResult);
|
|
}
|
|
return(ST_CONTINUE);
|
|
|
|
} /* end of stPathTableFree */
|