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.
2177 lines
64 KiB
2177 lines
64 KiB
/**
|
|
@file
|
|
|
|
@ingroup cudd
|
|
|
|
@brief Procedures to approximate a given %BDD.
|
|
|
|
@see cuddSubsetHB.c cuddSubsetSP.c cuddGenCof.c
|
|
|
|
@author 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
|
|
|
|
*/
|
|
|
|
#ifdef __STDC__
|
|
#include <float.h>
|
|
#else
|
|
#define DBL_MAX_EXP 1024
|
|
#endif
|
|
#include "util.h"
|
|
#include "cuddInt.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Constant declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#define NOTHING 0
|
|
#define REPLACE_T 1
|
|
#define REPLACE_E 2
|
|
#define REPLACE_N 3
|
|
#define REPLACE_TT 4
|
|
#define REPLACE_TE 5
|
|
|
|
#define DONT_CARE 0
|
|
#define CARE 1
|
|
#define TOTAL_CARE 2
|
|
#define CARE_ERROR 3
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
** @brief Data structure to store the information on each node.
|
|
**
|
|
** @details It keeps the number of minterms of the function rooted at
|
|
** this node in terms of the number of variables specified by the
|
|
** user; the number of minterms of the complement; the impact of the
|
|
** number of minterms of this function on the number of minterms of
|
|
** the root function; the reference count of the node from within the
|
|
** root function; the flag that says whether the node intersects the
|
|
** care set; the flag that says whether the node should be replaced
|
|
** and how; the results of subsetting in both phases.
|
|
*/
|
|
typedef struct NodeData {
|
|
double mintermsP; /**< minterms for the regular node */
|
|
double mintermsN; /**< minterms for the complemented node */
|
|
int functionRef; /**< references from within this function */
|
|
char care; /**< node intersects care set */
|
|
char replace; /**< replacement decision */
|
|
short int parity; /**< 1: even; 2: odd; 3: both */
|
|
DdNode *resultP; /**< result for even parity */
|
|
DdNode *resultN; /**< result for odd parity */
|
|
} NodeData;
|
|
|
|
/**
|
|
** @brief Main bookkeeping data structure for approximation algorithms.
|
|
*/
|
|
typedef struct ApproxInfo {
|
|
DdNode *one; /**< one constant */
|
|
DdNode *zero; /**< %BDD zero constant */
|
|
NodeData *page; /**< per-node information */
|
|
DdHashTable *table; /**< hash table to access the per-node info */
|
|
int index; /**< index of the current node */
|
|
double max; /**< max number of minterms */
|
|
int size; /**< how many nodes are left */
|
|
double minterms; /**< how many minterms are left */
|
|
} ApproxInfo;
|
|
|
|
/**
|
|
** @brief Item of the queue used in the levelized traversal of the %BDD.
|
|
*/
|
|
typedef struct GlobalQueueItem {
|
|
struct GlobalQueueItem *next;
|
|
struct GlobalQueueItem *cnext;
|
|
DdNode *node;
|
|
double impactP;
|
|
double impactN;
|
|
} GlobalQueueItem;
|
|
|
|
/**
|
|
** @brief Type of the item of the local queue.
|
|
*/
|
|
typedef struct LocalQueueItem {
|
|
struct LocalQueueItem *next;
|
|
struct LocalQueueItem *cnext;
|
|
DdNode *node;
|
|
int localRef;
|
|
} LocalQueueItem;
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/** \cond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void updateParity (DdNode *node, ApproxInfo *info, int newparity);
|
|
static NodeData * gatherInfoAux (DdNode *node, ApproxInfo *info, int parity);
|
|
static ApproxInfo * gatherInfo (DdManager *dd, DdNode *node, int numVars, int parity);
|
|
static int computeSavings (DdManager *dd, DdNode *f, DdNode *skip, ApproxInfo *info, DdLevelQueue *queue);
|
|
static int updateRefs (DdManager *dd, DdNode *f, DdNode *skip, ApproxInfo *info, DdLevelQueue *queue);
|
|
static int UAmarkNodes (DdManager *dd, DdNode *f, ApproxInfo *info, int threshold, int safe, double quality);
|
|
static DdNode * UAbuildSubset (DdManager *dd, DdNode *node, ApproxInfo *info);
|
|
static int RAmarkNodes (DdManager *dd, DdNode *f, ApproxInfo *info, int threshold, double quality);
|
|
static int BAmarkNodes (DdManager *dd, DdNode *f, ApproxInfo *info, int threshold, double quality1, double quality0);
|
|
static DdNode * RAbuildSubset (DdManager *dd, DdNode *node, ApproxInfo *info);
|
|
static int BAapplyBias (DdManager *dd, DdNode *f, DdNode *b, ApproxInfo *info, DdHashTable *cache);
|
|
|
|
/** \endcond */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
@brief Extracts a dense subset from a %BDD with Shiple's
|
|
underapproximation method.
|
|
|
|
@details This procedure uses a variant of Tom Shiple's
|
|
underapproximation method. The main difference from the original
|
|
method is that density is used as cost function. The parameter
|
|
numVars is the maximum number of variables to be used in minterm
|
|
calculation. The optimal number should be as close as possible to
|
|
the size of the support of f. However, it is safe to pass the value
|
|
returned by Cudd_ReadSize for numVars when the number of variables
|
|
is under 1023. If numVars is larger than 1023, it will cause
|
|
overflow. If a 0 parameter is passed then the procedure will compute
|
|
a value which will avoid overflow but will cause underflow with 2046
|
|
variables or more.
|
|
|
|
@return a pointer to the %BDD of the subset if successful; NULL if
|
|
the procedure runs out of memory.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SubsetShortPaths Cudd_SubsetHeavyBranch Cudd_ReadSize
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_UnderApprox(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function to be subset */,
|
|
int numVars /**< number of variables in the support of f */,
|
|
int threshold /**< when to stop approximation */,
|
|
int safe /**< enforce safe approximation */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
DdNode *subset;
|
|
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddUnderApprox(dd, f, numVars, threshold, safe, quality);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
|
|
return(subset);
|
|
|
|
} /* end of Cudd_UnderApprox */
|
|
|
|
|
|
/**
|
|
@brief Extracts a dense superset from a %BDD with Shiple's
|
|
underapproximation method.
|
|
|
|
@details The procedure is identical to the underapproximation
|
|
procedure except for the fact that it works on the complement of the
|
|
given function. Extracting the subset of the complement function is
|
|
equivalent to extracting the superset of the function. The
|
|
parameter numVars is the maximum number of variables to be used in
|
|
minterm calculation. The optimal number should be as close as
|
|
possible to the size of the support of f. However, it is safe to
|
|
pass the value returned by Cudd_ReadSize for numVars when the number
|
|
of variables is under 1023. If numVars is larger than 1023, it will
|
|
overflow. If a 0 parameter is passed then the procedure will compute
|
|
a value which will avoid overflow but will cause underflow with 2046
|
|
variables or more.
|
|
|
|
@return a pointer to the %BDD of the superset if successful. NULL if
|
|
intermediate result causes the procedure to run out of memory.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SupersetHeavyBranch Cudd_SupersetShortPaths Cudd_ReadSize
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_OverApprox(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function to be superset */,
|
|
int numVars /**< number of variables in the support of f */,
|
|
int threshold /**< when to stop approximation */,
|
|
int safe /**< enforce safe approximation */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
DdNode *subset, *g;
|
|
|
|
g = Cudd_Not(f);
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddUnderApprox(dd, g, numVars, threshold, safe, quality);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
|
|
return(Cudd_NotCond(subset, (subset != NULL)));
|
|
|
|
} /* end of Cudd_OverApprox */
|
|
|
|
|
|
/**
|
|
@brief Extracts a dense subset from a %BDD with the remapping
|
|
underapproximation method.
|
|
|
|
@details This procedure uses a remapping technique and density as
|
|
the cost function. The parameter numVars is the maximum number of
|
|
variables to be used in minterm calculation. The optimal number
|
|
should be as close as possible to the size of the support of f.
|
|
However, it is safe to pass the value returned by Cudd_ReadSize for
|
|
numVars when the number of variables is under 1023. If numVars is
|
|
larger than 1023, it will cause overflow. If a 0 parameter is passed
|
|
then the procedure will compute a value which will avoid overflow
|
|
but will cause underflow with 2046 variables or more.
|
|
|
|
@return a pointer to the %BDD of the subset if successful. NULL if
|
|
the procedure runs out of memory.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SubsetShortPaths Cudd_SubsetHeavyBranch Cudd_UnderApprox Cudd_ReadSize
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_RemapUnderApprox(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function to be subset */,
|
|
int numVars /**< number of variables in the support of f */,
|
|
int threshold /**< when to stop approximation */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
DdNode *subset;
|
|
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddRemapUnderApprox(dd, f, numVars, threshold, quality);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
|
|
return(subset);
|
|
|
|
} /* end of Cudd_RemapUnderApprox */
|
|
|
|
|
|
/**
|
|
@brief Extracts a dense superset from a %BDD with the remapping
|
|
underapproximation method.
|
|
|
|
@details The procedure is identical to the underapproximation
|
|
procedure except for the fact that it works on the complement of the
|
|
given function. Extracting the subset of the complement function is
|
|
equivalent to extracting the superset of the function. The parameter
|
|
numVars is the maximum number of variables to be used in minterm
|
|
calculation. The optimal number should be as close as possible to
|
|
the size of the support of f. However, it is safe to pass the value
|
|
returned by Cudd_ReadSize for numVars when the number of variables
|
|
is under 1023. If numVars is larger than 1023, it will overflow. If
|
|
a 0 parameter is passed then the procedure will compute a value
|
|
which will avoid overflow but will cause underflow with 2046
|
|
variables or more.
|
|
|
|
@return a pointer to the %BDD of the superset if successful. NULL if
|
|
intermediate result causes the procedure to run out of memory.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SupersetHeavyBranch Cudd_SupersetShortPaths Cudd_ReadSize
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_RemapOverApprox(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function to be superset */,
|
|
int numVars /**< number of variables in the support of f */,
|
|
int threshold /**< when to stop approximation */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
DdNode *subset, *g;
|
|
|
|
g = Cudd_Not(f);
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddRemapUnderApprox(dd, g, numVars, threshold, quality);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
|
|
return(Cudd_NotCond(subset, (subset != NULL)));
|
|
|
|
} /* end of Cudd_RemapOverApprox */
|
|
|
|
|
|
/**
|
|
@brief Extracts a dense subset from a %BDD with the biased
|
|
underapproximation method.
|
|
|
|
@details This procedure uses a biased remapping technique and
|
|
density as the cost function. The bias is a function. This procedure
|
|
tries to approximate where the bias is 0 and preserve the given
|
|
function where the bias is 1. The parameter numVars is the maximum
|
|
number of variables to be used in minterm calculation. The optimal
|
|
number should be as close as possible to the size of the support of
|
|
f. However, it is safe to pass the value returned by Cudd_ReadSize
|
|
for numVars when the number of variables is under 1023. If numVars
|
|
is larger than 1023, it will cause overflow. If a 0 parameter is
|
|
passed then the procedure will compute a value which will avoid
|
|
overflow but will cause underflow with 2046 variables or more.
|
|
|
|
@return a pointer to the %BDD of the subset if successful. NULL if
|
|
the procedure runs out of memory.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SubsetShortPaths Cudd_SubsetHeavyBranch Cudd_UnderApprox
|
|
Cudd_RemapUnderApprox Cudd_ReadSize
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_BiasedUnderApprox(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *f /**< function to be subset */,
|
|
DdNode *b /**< bias function */,
|
|
int numVars /**< number of variables in the support of f */,
|
|
int threshold /**< when to stop approximation */,
|
|
double quality1 /**< minimum improvement for accepted changes when b=1 */,
|
|
double quality0 /**< minimum improvement for accepted changes when b=0 */)
|
|
{
|
|
DdNode *subset;
|
|
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddBiasedUnderApprox(dd, f, b, numVars, threshold, quality1,
|
|
quality0);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
|
|
return(subset);
|
|
|
|
} /* end of Cudd_BiasedUnderApprox */
|
|
|
|
|
|
/**
|
|
@brief Extracts a dense superset from a %BDD with the biased
|
|
underapproximation method.
|
|
|
|
@details The procedure is identical to the underapproximation
|
|
procedure except for the fact that it works on the complement of the
|
|
given function. Extracting the subset of the complement function is
|
|
equivalent to extracting the superset of the function. The
|
|
parameter numVars is the maximum number of variables to be used in
|
|
minterm calculation. The optimal number should be as close as
|
|
possible to the size of the support of f. However, it is safe to
|
|
pass the value returned by Cudd_ReadSize for numVars when the number
|
|
of variables is under 1023. If numVars is larger than 1023, it will
|
|
overflow. If a 0 parameter is passed then the procedure will compute
|
|
a value which will avoid overflow but will cause underflow with 2046
|
|
variables or more.
|
|
|
|
@return a pointer to the %BDD of the superset if successful. NULL if
|
|
intermediate result causes the procedure to run out of memory.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SupersetHeavyBranch Cudd_SupersetShortPaths
|
|
Cudd_RemapOverApprox Cudd_BiasedUnderApprox Cudd_ReadSize
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_BiasedOverApprox(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *f /**< function to be superset */,
|
|
DdNode *b /**< bias function */,
|
|
int numVars /**< number of variables in the support of f */,
|
|
int threshold /**< when to stop approximation */,
|
|
double quality1 /**< minimum improvement for accepted changes when b=1*/,
|
|
double quality0 /**< minimum improvement for accepted changes when b=0 */)
|
|
{
|
|
DdNode *subset, *g;
|
|
|
|
g = Cudd_Not(f);
|
|
do {
|
|
dd->reordered = 0;
|
|
subset = cuddBiasedUnderApprox(dd, g, b, numVars, threshold, quality1,
|
|
quality0);
|
|
} while (dd->reordered == 1);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
|
|
return(Cudd_NotCond(subset, (subset != NULL)));
|
|
|
|
} /* end of Cudd_BiasedOverApprox */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Applies Tom Shiple's underappoximation algorithm.
|
|
|
|
@details Proceeds in three phases:
|
|
<ul>
|
|
<li> collect information on each node in the %BDD; this is done via DFS.
|
|
<li> traverse the %BDD in top-down fashion and compute for each node
|
|
whether its elimination increases density.
|
|
<li> traverse the %BDD via DFS and actually perform the elimination.
|
|
</ul>
|
|
|
|
@return the approximated %BDD if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_UnderApprox
|
|
|
|
*/
|
|
DdNode *
|
|
cuddUnderApprox(
|
|
DdManager * dd /**< %DD manager */,
|
|
DdNode * f /**< current %DD */,
|
|
int numVars /**< maximum number of variables */,
|
|
int threshold /**< threshold under which approximation stops */,
|
|
int safe /**< enforce safe approximation */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
ApproxInfo *info;
|
|
DdNode *subset;
|
|
int result;
|
|
|
|
if (f == NULL) {
|
|
fprintf(dd->err, "Cannot subset, nil object\n");
|
|
return(NULL);
|
|
}
|
|
|
|
if (Cudd_IsConstantInt(f)) {
|
|
return(f);
|
|
}
|
|
|
|
/* Create table where node data are accessible via a hash table. */
|
|
info = gatherInfo(dd, f, numVars, safe);
|
|
if (info == NULL) {
|
|
(void) fprintf(dd->err, "Out-of-memory; Cannot subset\n");
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
/* Mark nodes that should be replaced by zero. */
|
|
result = UAmarkNodes(dd, f, info, threshold, safe, quality);
|
|
if (result == 0) {
|
|
(void) fprintf(dd->err, "Out-of-memory; Cannot subset\n");
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
/* Build the result. */
|
|
subset = UAbuildSubset(dd, f, info);
|
|
#if 1
|
|
if (subset && info->size < Cudd_DagSize(subset))
|
|
(void) fprintf(dd->err, "Wrong prediction: %d versus actual %d\n",
|
|
info->size, Cudd_DagSize(subset));
|
|
#endif
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
|
|
#ifdef DD_DEBUG
|
|
if (subset != NULL) {
|
|
cuddRef(subset);
|
|
#if 0
|
|
(void) Cudd_DebugCheck(dd);
|
|
(void) Cudd_CheckKeys(dd);
|
|
#endif
|
|
if (!Cudd_bddLeq(dd, subset, f)) {
|
|
(void) fprintf(dd->err, "Wrong subset\n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
}
|
|
cuddDeref(subset);
|
|
}
|
|
#endif
|
|
return(subset);
|
|
|
|
} /* end of cuddUnderApprox */
|
|
|
|
|
|
/**
|
|
@brief Applies the remapping underappoximation algorithm.
|
|
|
|
@details Proceeds in three phases:
|
|
<ul>
|
|
<li> collect information on each node in the %BDD; this is done via DFS.
|
|
<li> traverse the %BDD in top-down fashion and compute for each node
|
|
whether remapping increases density.
|
|
<li> traverse the %BDD via DFS and actually perform the elimination.
|
|
</ul>
|
|
|
|
@return the approximated %BDD if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_RemapUnderApprox
|
|
|
|
*/
|
|
DdNode *
|
|
cuddRemapUnderApprox(
|
|
DdManager * dd /**< %DD manager */,
|
|
DdNode * f /**< current %DD */,
|
|
int numVars /**< maximum number of variables */,
|
|
int threshold /**< threshold under which approximation stops */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
ApproxInfo *info;
|
|
DdNode *subset;
|
|
int result;
|
|
|
|
if (f == NULL) {
|
|
fprintf(dd->err, "Cannot subset, nil object\n");
|
|
dd->errorCode = CUDD_INVALID_ARG;
|
|
return(NULL);
|
|
}
|
|
|
|
if (Cudd_IsConstantInt(f)) {
|
|
return(f);
|
|
}
|
|
|
|
/* Create table where node data are accessible via a hash table. */
|
|
info = gatherInfo(dd, f, numVars, CUDD_TRUE);
|
|
if (info == NULL) {
|
|
(void) fprintf(dd->err, "Out-of-memory; Cannot subset\n");
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
/* Mark nodes that should be replaced by zero. */
|
|
result = RAmarkNodes(dd, f, info, threshold, quality);
|
|
if (result == 0) {
|
|
(void) fprintf(dd->err, "Out-of-memory; Cannot subset\n");
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
/* Build the result. */
|
|
subset = RAbuildSubset(dd, f, info);
|
|
#if 1
|
|
if (subset && info->size < Cudd_DagSize(subset))
|
|
(void) fprintf(dd->err, "Wrong prediction: %d versus actual %d\n",
|
|
info->size, Cudd_DagSize(subset));
|
|
#endif
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
|
|
#ifdef DD_DEBUG
|
|
if (subset != NULL) {
|
|
cuddRef(subset);
|
|
#if 0
|
|
(void) Cudd_DebugCheck(dd);
|
|
(void) Cudd_CheckKeys(dd);
|
|
#endif
|
|
if (!Cudd_bddLeq(dd, subset, f)) {
|
|
(void) fprintf(dd->err, "Wrong subset\n");
|
|
}
|
|
cuddDeref(subset);
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
}
|
|
#endif
|
|
return(subset);
|
|
|
|
} /* end of cuddRemapUnderApprox */
|
|
|
|
|
|
/**
|
|
@brief Applies the biased remapping underappoximation algorithm.
|
|
|
|
@details Proceeds in three phases:
|
|
<ul>
|
|
<li> collect information on each node in the %BDD; this is done via DFS.
|
|
<li> traverse the %BDD in top-down fashion and compute for each node
|
|
whether remapping increases density.
|
|
<li> traverse the %BDD via DFS and actually perform the elimination.
|
|
</ul>
|
|
|
|
@return the approximated %BDD if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_BiasedUnderApprox
|
|
|
|
*/
|
|
DdNode *
|
|
cuddBiasedUnderApprox(
|
|
DdManager *dd /**< %DD manager */,
|
|
DdNode *f /**< current %DD */,
|
|
DdNode *b /**< bias function */,
|
|
int numVars /**< maximum number of variables */,
|
|
int threshold /**< threshold under which approximation stops */,
|
|
double quality1 /**< minimum improvement for accepted changes when b=1 */,
|
|
double quality0 /**< minimum improvement for accepted changes when b=0 */)
|
|
{
|
|
ApproxInfo *info;
|
|
DdNode *subset;
|
|
int result;
|
|
DdHashTable *cache;
|
|
|
|
if (f == NULL) {
|
|
fprintf(dd->err, "Cannot subset, nil object\n");
|
|
dd->errorCode = CUDD_INVALID_ARG;
|
|
return(NULL);
|
|
}
|
|
|
|
if (Cudd_IsConstantInt(f)) {
|
|
return(f);
|
|
}
|
|
|
|
/* Create table where node data are accessible via a hash table. */
|
|
info = gatherInfo(dd, f, numVars, CUDD_TRUE);
|
|
if (info == NULL) {
|
|
(void) fprintf(dd->err, "Out-of-memory; Cannot subset\n");
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
cache = cuddHashTableInit(dd,2,2);
|
|
result = BAapplyBias(dd, Cudd_Regular(f), b, info, cache);
|
|
if (result == CARE_ERROR) {
|
|
(void) fprintf(dd->err, "Out-of-memory; Cannot subset\n");
|
|
cuddHashTableQuit(cache);
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
cuddHashTableQuit(cache);
|
|
|
|
/* Mark nodes that should be replaced by zero. */
|
|
result = BAmarkNodes(dd, f, info, threshold, quality1, quality0);
|
|
if (result == 0) {
|
|
(void) fprintf(dd->err, "Out-of-memory; Cannot subset\n");
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
/* Build the result. */
|
|
subset = RAbuildSubset(dd, f, info);
|
|
#if 1
|
|
if (subset && info->size < Cudd_DagSize(subset))
|
|
(void) fprintf(dd->err, "Wrong prediction: %d versus actual %d\n",
|
|
info->size, Cudd_DagSize(subset));
|
|
#endif
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
|
|
#ifdef DD_DEBUG
|
|
if (subset != NULL) {
|
|
cuddRef(subset);
|
|
#if 0
|
|
(void) Cudd_DebugCheck(dd);
|
|
(void) Cudd_CheckKeys(dd);
|
|
#endif
|
|
if (!Cudd_bddLeq(dd, subset, f)) {
|
|
(void) fprintf(dd->err, "Wrong subset\n");
|
|
}
|
|
cuddDeref(subset);
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
}
|
|
#endif
|
|
return(subset);
|
|
|
|
} /* end of cuddBiasedUnderApprox */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Recursively update the parity of the paths reaching a node.
|
|
|
|
@details Assumes that node is regular and propagates the invariant.
|
|
|
|
@sideeffect None
|
|
|
|
@see gatherInfoAux
|
|
|
|
*/
|
|
static void
|
|
updateParity(
|
|
DdNode * node /**< function to analyze */,
|
|
ApproxInfo * info /**< info on %BDD */,
|
|
int newparity /**< new parity for node */)
|
|
{
|
|
NodeData *infoN;
|
|
DdNode *E;
|
|
|
|
if ((infoN = (NodeData *) cuddHashTableGenericLookup(info->table, node)) == NULL)
|
|
return;
|
|
if ((infoN->parity & newparity) != 0) return;
|
|
infoN->parity |= (short) newparity;
|
|
if (Cudd_IsConstantInt(node)) return;
|
|
updateParity(cuddT(node),info,newparity);
|
|
E = cuddE(node);
|
|
if (Cudd_IsComplement(E)) {
|
|
updateParity(Cudd_Not(E),info,3-newparity);
|
|
} else {
|
|
updateParity(E,info,newparity);
|
|
}
|
|
return;
|
|
|
|
} /* end of updateParity */
|
|
|
|
|
|
/**
|
|
@brief Recursively counts minterms and computes reference counts
|
|
of each node in the %BDD.
|
|
|
|
@details Similar to the cuddCountMintermAux which recursively counts
|
|
the number of minterms for the dag rooted at each node in terms of
|
|
the total number of variables (max). It assumes that the node
|
|
pointer passed to it is regular and it maintains the invariant.
|
|
|
|
@sideeffect None
|
|
|
|
@see gatherInfo
|
|
|
|
*/
|
|
static NodeData *
|
|
gatherInfoAux(
|
|
DdNode * node /**< function to analyze */,
|
|
ApproxInfo * info /**< info on %BDD */,
|
|
int parity /**< gather parity information */)
|
|
{
|
|
DdNode *N, *Nt, *Ne;
|
|
NodeData *infoN, *infoT, *infoE;
|
|
|
|
N = Cudd_Regular(node);
|
|
|
|
/* Check whether entry for this node exists. */
|
|
if ((infoN = (NodeData *) cuddHashTableGenericLookup(info->table, N)) != NULL) {
|
|
if (parity) {
|
|
/* Update parity and propagate. */
|
|
updateParity(N, info, 1 + (int) Cudd_IsComplement(node));
|
|
}
|
|
return(infoN);
|
|
}
|
|
|
|
/* Compute the cofactors. */
|
|
Nt = Cudd_NotCond(cuddT(N), N != node);
|
|
Ne = Cudd_NotCond(cuddE(N), N != node);
|
|
|
|
infoT = gatherInfoAux(Nt, info, parity);
|
|
if (infoT == NULL) return(NULL);
|
|
infoE = gatherInfoAux(Ne, info, parity);
|
|
if (infoE == NULL) return(NULL);
|
|
|
|
infoT->functionRef++;
|
|
infoE->functionRef++;
|
|
|
|
/* Point to the correct location in the page. */
|
|
infoN = &(info->page[info->index++]);
|
|
infoN->parity |= (short) (1 + Cudd_IsComplement(node));
|
|
|
|
infoN->mintermsP = infoT->mintermsP/2;
|
|
infoN->mintermsN = infoT->mintermsN/2;
|
|
if (Cudd_IsComplement(Ne) ^ Cudd_IsComplement(node)) {
|
|
infoN->mintermsP += infoE->mintermsN/2;
|
|
infoN->mintermsN += infoE->mintermsP/2;
|
|
} else {
|
|
infoN->mintermsP += infoE->mintermsP/2;
|
|
infoN->mintermsN += infoE->mintermsN/2;
|
|
}
|
|
|
|
/* Insert entry for the node in the table. */
|
|
if (cuddHashTableGenericInsert(info->table, N, infoN) == 0) {
|
|
return(NULL);
|
|
}
|
|
return(infoN);
|
|
|
|
} /* end of gatherInfoAux */
|
|
|
|
|
|
/**
|
|
@brief Gathers information about each node.
|
|
|
|
@details Counts minterms and computes reference counts of each
|
|
node in the %BDD. The minterm count is separately computed for the
|
|
node and its complement. This is to avoid cancellation
|
|
errors.
|
|
|
|
@return a pointer to the data structure holding the information
|
|
gathered if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddUnderApprox gatherInfoAux
|
|
|
|
*/
|
|
static ApproxInfo *
|
|
gatherInfo(
|
|
DdManager * dd /* manager */,
|
|
DdNode * node /* function to be analyzed */,
|
|
int numVars /* number of variables node depends on */,
|
|
int parity /* gather parity information */)
|
|
{
|
|
ApproxInfo * info;
|
|
NodeData * infoTop;
|
|
|
|
/* If user did not give numVars value, set it to the maximum
|
|
** exponent that the pow function can take. The -1 is due to the
|
|
** discrepancy in the value that pow takes and the value that
|
|
** log gives.
|
|
*/
|
|
if (numVars == 0) {
|
|
numVars = DBL_MAX_EXP - 1;
|
|
}
|
|
|
|
info = ALLOC(ApproxInfo,1);
|
|
if (info == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
info->max = pow(2.0,(double) numVars);
|
|
info->one = DD_ONE(dd);
|
|
info->zero = Cudd_Not(info->one);
|
|
info->size = Cudd_DagSize(node);
|
|
/* All the information gathered will be stored in a contiguous
|
|
** piece of memory, which is allocated here. This can be done
|
|
** efficiently because we have counted the number of nodes of the
|
|
** BDD. info->index points to the next available entry in the array
|
|
** that stores the per-node information. */
|
|
info->page = ALLOC(NodeData,info->size);
|
|
if (info->page == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(info);
|
|
return(NULL);
|
|
}
|
|
memset(info->page, 0, info->size * sizeof(NodeData)); /* clear all page */
|
|
info->table = cuddHashTableInit(dd,1,info->size);
|
|
if (info->table == NULL) {
|
|
FREE(info->page);
|
|
FREE(info);
|
|
return(NULL);
|
|
}
|
|
/* We visit the DAG in post-order DFS. Hence, the constant node is
|
|
** in first position, and the root of the DAG is in last position. */
|
|
|
|
/* Info for the constant node: Initialize only fields different from 0. */
|
|
if (cuddHashTableGenericInsert(info->table, info->one, info->page) == 0) {
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
return(NULL);
|
|
}
|
|
info->page[0].mintermsP = info->max;
|
|
info->index = 1;
|
|
|
|
infoTop = gatherInfoAux(node,info,parity);
|
|
if (infoTop == NULL) {
|
|
FREE(info->page);
|
|
cuddHashTableGenericQuit(info->table);
|
|
FREE(info);
|
|
return(NULL);
|
|
}
|
|
if (Cudd_IsComplement(node)) {
|
|
info->minterms = infoTop->mintermsN;
|
|
} else {
|
|
info->minterms = infoTop->mintermsP;
|
|
}
|
|
|
|
infoTop->functionRef = 1;
|
|
return(info);
|
|
|
|
} /* end of gatherInfo */
|
|
|
|
|
|
/**
|
|
@brief Counts the nodes that would be eliminated if a given node
|
|
were replaced by zero.
|
|
|
|
@details This procedure uses a queue passed by the caller for
|
|
efficiency: since the queue is left empty at the endof the search,
|
|
it can be reused as is by the next search.
|
|
|
|
@return the count (always striclty positive) if successful; 0
|
|
otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see UAmarkNodes RAmarkNodes BAmarkNodes
|
|
|
|
*/
|
|
static int
|
|
computeSavings(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
DdNode * skip,
|
|
ApproxInfo * info,
|
|
DdLevelQueue * queue)
|
|
{
|
|
NodeData *infoN;
|
|
LocalQueueItem *item;
|
|
DdNode *node;
|
|
int savings = 0;
|
|
|
|
node = Cudd_Regular(f);
|
|
if (node == NULL) return(0);
|
|
skip = Cudd_Regular(skip);
|
|
/* Insert the given node in the level queue. Its local reference
|
|
** count is set equal to the function reference count so that the
|
|
** search will continue from it when it is retrieved. */
|
|
item = (LocalQueueItem *)
|
|
cuddLevelQueueFirst(queue,node,cuddI(dd,node->index));
|
|
if (item == NULL)
|
|
return(0);
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table, node);
|
|
item->localRef = infoN->functionRef;
|
|
|
|
/* Process the queue. */
|
|
while ((item = (LocalQueueItem *) queue->first) != NULL) {
|
|
node = item->node;
|
|
if (node != skip) {
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table,node);
|
|
if (item->localRef == infoN->functionRef) {
|
|
/* This node is not shared. */
|
|
DdNode *nodeT, *nodeE;
|
|
savings++;
|
|
nodeT = cuddT(node);
|
|
if (!cuddIsConstant(nodeT)) {
|
|
item = (LocalQueueItem *)
|
|
cuddLevelQueueEnqueue(queue,nodeT,cuddI(dd,nodeT->index));
|
|
if (item == NULL) return(0);
|
|
item->localRef++;
|
|
}
|
|
nodeE = Cudd_Regular(cuddE(node));
|
|
if (!cuddIsConstant(nodeE)) {
|
|
item = (LocalQueueItem *)
|
|
cuddLevelQueueEnqueue(queue,nodeE,cuddI(dd,nodeE->index));
|
|
if (item == NULL) return(0);
|
|
item->localRef++;
|
|
}
|
|
}
|
|
}
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
}
|
|
|
|
#ifdef DD_DEBUG
|
|
/* At the end of a local search the queue should be empty. */
|
|
assert(queue->size == 0);
|
|
#endif
|
|
return(savings);
|
|
|
|
} /* end of computeSavings */
|
|
|
|
|
|
/**
|
|
@brief Update function reference counts to account for replacement.
|
|
|
|
@return the number of nodes saved if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see UAmarkNodes RAmarkNodes BAmarkNodes
|
|
|
|
*/
|
|
static int
|
|
updateRefs(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
DdNode * skip,
|
|
ApproxInfo * info,
|
|
DdLevelQueue * queue)
|
|
{
|
|
NodeData *infoN;
|
|
LocalQueueItem *item;
|
|
DdNode *node;
|
|
int savings = 0;
|
|
|
|
node = Cudd_Regular(f);
|
|
/* Insert the given node in the level queue. Its function reference
|
|
** count is set equal to 0 so that the search will continue from it
|
|
** when it is retrieved. */
|
|
item = (LocalQueueItem *) cuddLevelQueueFirst(queue,node,cuddI(dd,node->index));
|
|
if (item == NULL)
|
|
return(0);
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table, node);
|
|
infoN->functionRef = 0;
|
|
|
|
if (skip != NULL) {
|
|
/* Increase the function reference count of the node to be skipped
|
|
** by 1 to account for the node pointing to it that will be created. */
|
|
skip = Cudd_Regular(skip);
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table, skip);
|
|
infoN->functionRef++;
|
|
}
|
|
|
|
/* Process the queue. */
|
|
while ((item = (LocalQueueItem *) queue->first) != NULL) {
|
|
node = item->node;
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table,node);
|
|
if (infoN->functionRef == 0) {
|
|
/* This node is not shared or to be be skipped. */
|
|
DdNode *nodeT, *nodeE;
|
|
savings++;
|
|
nodeT = cuddT(node);
|
|
if (!cuddIsConstant(nodeT)) {
|
|
item = (LocalQueueItem *)
|
|
cuddLevelQueueEnqueue(queue,nodeT,cuddI(dd,nodeT->index));
|
|
if (item == NULL) return(0);
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table,nodeT);
|
|
infoN->functionRef--;
|
|
}
|
|
nodeE = Cudd_Regular(cuddE(node));
|
|
if (!cuddIsConstant(nodeE)) {
|
|
item = (LocalQueueItem *)
|
|
cuddLevelQueueEnqueue(queue,nodeE,cuddI(dd,nodeE->index));
|
|
if (item == NULL) return(0);
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table,nodeE);
|
|
infoN->functionRef--;
|
|
}
|
|
}
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
}
|
|
|
|
#ifdef DD_DEBUG
|
|
/* At the end of a local search the queue should be empty. */
|
|
assert(queue->size == 0);
|
|
#endif
|
|
return(savings);
|
|
|
|
} /* end of updateRefs */
|
|
|
|
|
|
/**
|
|
@brief Marks nodes for replacement by zero.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddUnderApprox
|
|
|
|
*/
|
|
static int
|
|
UAmarkNodes(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function to be analyzed */,
|
|
ApproxInfo * info /**< info on %BDD */,
|
|
int threshold /**< when to stop approximating */,
|
|
int safe /**< enforce safe approximation */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
DdLevelQueue *queue;
|
|
DdLevelQueue *localQueue;
|
|
NodeData *infoN;
|
|
GlobalQueueItem *item;
|
|
DdNode *node;
|
|
double numOnset;
|
|
double impactP, impactN;
|
|
int savings;
|
|
|
|
#if 0
|
|
(void) printf("initial size = %d initial minterms = %g\n",
|
|
info->size, info->minterms);
|
|
#endif
|
|
queue = cuddLevelQueueInit(dd->size,sizeof(GlobalQueueItem),info->size,dd);
|
|
if (queue == NULL) {
|
|
return(0);
|
|
}
|
|
localQueue = cuddLevelQueueInit(dd->size,sizeof(LocalQueueItem),
|
|
dd->initSlots,dd);
|
|
if (localQueue == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
return(0);
|
|
}
|
|
node = Cudd_Regular(f);
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index));
|
|
if (item == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
if (Cudd_IsComplement(f)) {
|
|
item->impactP = 0.0;
|
|
item->impactN = 1.0;
|
|
} else {
|
|
item->impactP = 1.0;
|
|
item->impactN = 0.0;
|
|
}
|
|
while (queue->first != NULL) {
|
|
/* If the size of the subset is below the threshold, quit. */
|
|
if (info->size <= threshold)
|
|
break;
|
|
item = (GlobalQueueItem *) queue->first;
|
|
node = item->node;
|
|
node = Cudd_Regular(node);
|
|
infoN = (NodeData *) cuddHashTableGenericLookup(info->table, node);
|
|
if (safe && infoN->parity == 3) {
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
continue;
|
|
}
|
|
impactP = item->impactP;
|
|
impactN = item->impactN;
|
|
numOnset = infoN->mintermsP * impactP + infoN->mintermsN * impactN;
|
|
savings = computeSavings(dd,node,NULL,info,localQueue);
|
|
if (savings == 0) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
#if 0
|
|
(void) printf("node %p: impact = %g/%g numOnset = %g savings %d\n",
|
|
node, impactP, impactN, numOnset, savings);
|
|
#endif
|
|
if ((1 - numOnset / info->minterms) >
|
|
quality * (1 - (double) savings / info->size)) {
|
|
infoN->replace = CUDD_TRUE;
|
|
info->size -= savings;
|
|
info->minterms -=numOnset;
|
|
#if 0
|
|
(void) printf("replace: new size = %d new minterms = %g\n",
|
|
info->size, info->minterms);
|
|
#endif
|
|
savings -= updateRefs(dd,node,NULL,info,localQueue);
|
|
assert(savings == 0);
|
|
continue;
|
|
}
|
|
if (!cuddIsConstant(cuddT(node))) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node),
|
|
cuddI(dd,cuddT(node)->index));
|
|
item->impactP += impactP/2.0;
|
|
item->impactN += impactN/2.0;
|
|
}
|
|
if (!Cudd_IsConstantInt(cuddE(node))) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)),
|
|
cuddI(dd,Cudd_Regular(cuddE(node))->index));
|
|
if (Cudd_IsComplement(cuddE(node))) {
|
|
item->impactP += impactN/2.0;
|
|
item->impactN += impactP/2.0;
|
|
} else {
|
|
item->impactP += impactP/2.0;
|
|
item->impactN += impactN/2.0;
|
|
}
|
|
}
|
|
}
|
|
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(1);
|
|
|
|
} /* end of UAmarkNodes */
|
|
|
|
|
|
/**
|
|
@brief Builds the subset %BDD.
|
|
|
|
@details Based on the info table, replaces selected nodes by zero.
|
|
|
|
@return a pointer to the result if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddUnderApprox
|
|
|
|
*/
|
|
static DdNode *
|
|
UAbuildSubset(
|
|
DdManager * dd /**< %DD manager */,
|
|
DdNode * node /**< current node */,
|
|
ApproxInfo * info /**< node info */)
|
|
{
|
|
|
|
DdNode *Nt, *Ne, *N, *t, *e, *r;
|
|
NodeData *infoN;
|
|
|
|
if (Cudd_IsConstantInt(node))
|
|
return(node);
|
|
|
|
N = Cudd_Regular(node);
|
|
|
|
if ((infoN = (NodeData *) cuddHashTableGenericLookup(info->table, N)) != NULL) {
|
|
if (infoN->replace == CUDD_TRUE) {
|
|
return(info->zero);
|
|
}
|
|
if (N == node ) {
|
|
if (infoN->resultP != NULL) {
|
|
return(infoN->resultP);
|
|
}
|
|
} else {
|
|
if (infoN->resultN != NULL) {
|
|
return(infoN->resultN);
|
|
}
|
|
}
|
|
} else {
|
|
(void) fprintf(dd->err,
|
|
"Something is wrong, ought to be in info table\n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
|
|
Nt = Cudd_NotCond(cuddT(N), Cudd_IsComplement(node));
|
|
Ne = Cudd_NotCond(cuddE(N), Cudd_IsComplement(node));
|
|
|
|
t = UAbuildSubset(dd, Nt, info);
|
|
if (t == NULL) {
|
|
return(NULL);
|
|
}
|
|
cuddRef(t);
|
|
|
|
e = UAbuildSubset(dd, Ne, info);
|
|
if (e == NULL) {
|
|
Cudd_RecursiveDeref(dd,t);
|
|
return(NULL);
|
|
}
|
|
cuddRef(e);
|
|
|
|
if (Cudd_IsComplement(t)) {
|
|
t = Cudd_Not(t);
|
|
e = Cudd_Not(e);
|
|
r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e);
|
|
if (r == NULL) {
|
|
Cudd_RecursiveDeref(dd, e);
|
|
Cudd_RecursiveDeref(dd, t);
|
|
return(NULL);
|
|
}
|
|
r = Cudd_Not(r);
|
|
} else {
|
|
r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e);
|
|
if (r == NULL) {
|
|
Cudd_RecursiveDeref(dd, e);
|
|
Cudd_RecursiveDeref(dd, t);
|
|
return(NULL);
|
|
}
|
|
}
|
|
cuddDeref(t);
|
|
cuddDeref(e);
|
|
|
|
if (N == node) {
|
|
infoN->resultP = r;
|
|
} else {
|
|
infoN->resultN = r;
|
|
}
|
|
|
|
return(r);
|
|
|
|
} /* end of UAbuildSubset */
|
|
|
|
|
|
/**
|
|
@brief Marks nodes for remapping.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddRemapUnderApprox
|
|
|
|
*/
|
|
static int
|
|
RAmarkNodes(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function to be analyzed */,
|
|
ApproxInfo * info /**< info on %BDD */,
|
|
int threshold /**< when to stop approximating */,
|
|
double quality /**< minimum improvement for accepted changes */)
|
|
{
|
|
DdLevelQueue *queue;
|
|
DdLevelQueue *localQueue;
|
|
NodeData *infoN, *infoT, *infoE;
|
|
GlobalQueueItem *item;
|
|
DdNode *node, *T, *E;
|
|
DdNode *shared; /* grandchild shared by the two children of node */
|
|
double numOnset;
|
|
double impact, impactP, impactN;
|
|
double minterms;
|
|
int savings;
|
|
int replace;
|
|
|
|
#if 0
|
|
(void) fprintf(dd->out,"initial size = %d initial minterms = %g\n",
|
|
info->size, info->minterms);
|
|
#endif
|
|
queue = cuddLevelQueueInit(dd->size,sizeof(GlobalQueueItem),info->size,dd);
|
|
if (queue == NULL) {
|
|
return(0);
|
|
}
|
|
localQueue = cuddLevelQueueInit(dd->size,sizeof(LocalQueueItem),
|
|
dd->initSlots,dd);
|
|
if (localQueue == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
return(0);
|
|
}
|
|
/* Enqueue regular pointer to root and initialize impact. */
|
|
node = Cudd_Regular(f);
|
|
item = (GlobalQueueItem *)
|
|
cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index));
|
|
if (item == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
if (Cudd_IsComplement(f)) {
|
|
item->impactP = 0.0;
|
|
item->impactN = 1.0;
|
|
} else {
|
|
item->impactP = 1.0;
|
|
item->impactN = 0.0;
|
|
}
|
|
/* The nodes retrieved here are guaranteed to be non-terminal.
|
|
** The initial node is not terminal because constant nodes are
|
|
** dealt with in the calling procedure. Subsequent nodes are inserted
|
|
** only if they are not terminal. */
|
|
while ((item = (GlobalQueueItem *) queue->first) != NULL) {
|
|
/* If the size of the subset is below the threshold, quit. */
|
|
if (info->size <= threshold)
|
|
break;
|
|
node = item->node;
|
|
#ifdef DD_DEBUG
|
|
assert(item->impactP >= 0 && item->impactP <= 1.0);
|
|
assert(item->impactN >= 0 && item->impactN <= 1.0);
|
|
assert(!Cudd_IsComplement(node));
|
|
assert(!Cudd_IsConstantInt(node));
|
|
#endif
|
|
if ((infoN = (NodeData *) cuddHashTableGenericLookup(info->table, node)) == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
#ifdef DD_DEBUG
|
|
assert(infoN->parity >= 1 && infoN->parity <= 3);
|
|
#endif
|
|
if (infoN->parity == 3) {
|
|
/* This node can be reached through paths of different parity.
|
|
** It is not safe to replace it, because remapping will give
|
|
** an incorrect result, while replacement by 0 may cause node
|
|
** splitting. */
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
continue;
|
|
}
|
|
T = cuddT(node);
|
|
E = cuddE(node);
|
|
shared = NULL;
|
|
impactP = item->impactP;
|
|
impactN = item->impactN;
|
|
if (Cudd_bddLeq(dd,T,E)) {
|
|
/* Here we know that E is regular. */
|
|
#ifdef DD_DEBUG
|
|
assert(!Cudd_IsComplement(E));
|
|
#endif
|
|
infoT = (NodeData *) cuddHashTableGenericLookup(info->table, T);
|
|
infoE = (NodeData *) cuddHashTableGenericLookup(info->table, E);
|
|
if (infoN->parity == 1) {
|
|
impact = impactP;
|
|
minterms = infoE->mintermsP/2.0 - infoT->mintermsP/2.0;
|
|
if (infoE->functionRef == 1 && !cuddIsConstant(E)) {
|
|
savings = 1 + computeSavings(dd,E,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_E;
|
|
} else {
|
|
#ifdef DD_DEBUG
|
|
assert(infoN->parity == 2);
|
|
#endif
|
|
impact = impactN;
|
|
minterms = infoT->mintermsN/2.0 - infoE->mintermsN/2.0;
|
|
if (infoT->functionRef == 1 && !cuddIsConstant(T)) {
|
|
savings = 1 + computeSavings(dd,T,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_T;
|
|
}
|
|
numOnset = impact * minterms;
|
|
} else if (Cudd_bddLeq(dd,E,T)) {
|
|
/* Here E may be complemented. */
|
|
DdNode *Ereg = Cudd_Regular(E);
|
|
infoT = (NodeData *) cuddHashTableGenericLookup(info->table, T);
|
|
infoE = (NodeData *) cuddHashTableGenericLookup(info->table, Ereg);
|
|
if (infoN->parity == 1) {
|
|
impact = impactP;
|
|
minterms = infoT->mintermsP/2.0 -
|
|
((E == Ereg) ? infoE->mintermsP : infoE->mintermsN)/2.0;
|
|
if (infoT->functionRef == 1 && !cuddIsConstant(T)) {
|
|
savings = 1 + computeSavings(dd,T,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_T;
|
|
} else {
|
|
#ifdef DD_DEBUG
|
|
assert(infoN->parity == 2);
|
|
#endif
|
|
impact = impactN;
|
|
minterms = ((E == Ereg) ? infoE->mintermsN :
|
|
infoE->mintermsP)/2.0 - infoT->mintermsN/2.0;
|
|
if (infoE->functionRef == 1 && !cuddIsConstant(Ereg)) {
|
|
savings = 1 + computeSavings(dd,E,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_E;
|
|
}
|
|
numOnset = impact * minterms;
|
|
} else {
|
|
DdNode *Ereg = Cudd_Regular(E);
|
|
DdNode *TT = cuddT(T);
|
|
DdNode *ET = Cudd_NotCond(cuddT(Ereg), Cudd_IsComplement(E));
|
|
if (T->index == Ereg->index && TT == ET) {
|
|
shared = TT;
|
|
replace = REPLACE_TT;
|
|
} else {
|
|
DdNode *TE = cuddE(T);
|
|
DdNode *EE = Cudd_NotCond(cuddE(Ereg), Cudd_IsComplement(E));
|
|
if (T->index == Ereg->index && TE == EE) {
|
|
shared = TE;
|
|
replace = REPLACE_TE;
|
|
} else {
|
|
replace = REPLACE_N;
|
|
}
|
|
}
|
|
numOnset = infoN->mintermsP * impactP + infoN->mintermsN * impactN;
|
|
savings = computeSavings(dd,node,shared,info,localQueue);
|
|
if (shared != NULL) {
|
|
NodeData *infoS;
|
|
infoS = (NodeData *) cuddHashTableGenericLookup(info->table, Cudd_Regular(shared));
|
|
if (Cudd_IsComplement(shared)) {
|
|
numOnset -= (infoS->mintermsN * impactP +
|
|
infoS->mintermsP * impactN)/2.0;
|
|
} else {
|
|
numOnset -= (infoS->mintermsP * impactP +
|
|
infoS->mintermsN * impactN)/2.0;
|
|
}
|
|
savings--;
|
|
}
|
|
}
|
|
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
#if 0
|
|
if (replace == REPLACE_T || replace == REPLACE_E)
|
|
(void) printf("node %p: impact = %g numOnset = %g savings %d\n",
|
|
node, impact, numOnset, savings);
|
|
else
|
|
(void) printf("node %p: impact = %g/%g numOnset = %g savings %d\n",
|
|
node, impactP, impactN, numOnset, savings);
|
|
#endif
|
|
if ((1 - numOnset / info->minterms) >
|
|
quality * (1 - (double) savings / info->size)) {
|
|
infoN->replace = (char) replace;
|
|
info->size -= savings;
|
|
info->minterms -=numOnset;
|
|
#if 0
|
|
(void) printf("remap(%d): new size = %d new minterms = %g\n",
|
|
replace, info->size, info->minterms);
|
|
#endif
|
|
if (replace == REPLACE_N) {
|
|
savings -= updateRefs(dd,node,NULL,info,localQueue);
|
|
} else if (replace == REPLACE_T) {
|
|
savings -= updateRefs(dd,node,E,info,localQueue);
|
|
} else if (replace == REPLACE_E) {
|
|
savings -= updateRefs(dd,node,T,info,localQueue);
|
|
} else {
|
|
#ifdef DD_DEBUG
|
|
assert(replace == REPLACE_TT || replace == REPLACE_TE);
|
|
#endif
|
|
savings -= updateRefs(dd,node,shared,info,localQueue) - 1;
|
|
}
|
|
assert(savings == 0);
|
|
} else {
|
|
replace = NOTHING;
|
|
}
|
|
if (replace == REPLACE_N) continue;
|
|
if ((replace == REPLACE_E || replace == NOTHING) &&
|
|
!cuddIsConstant(cuddT(node))) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node),
|
|
cuddI(dd,cuddT(node)->index));
|
|
if (replace == REPLACE_E) {
|
|
item->impactP += impactP;
|
|
item->impactN += impactN;
|
|
} else {
|
|
item->impactP += impactP/2.0;
|
|
item->impactN += impactN/2.0;
|
|
}
|
|
}
|
|
if ((replace == REPLACE_T || replace == NOTHING) &&
|
|
!Cudd_IsConstantInt(cuddE(node))) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)),
|
|
cuddI(dd,Cudd_Regular(cuddE(node))->index));
|
|
if (Cudd_IsComplement(cuddE(node))) {
|
|
if (replace == REPLACE_T) {
|
|
item->impactP += impactN;
|
|
item->impactN += impactP;
|
|
} else {
|
|
item->impactP += impactN/2.0;
|
|
item->impactN += impactP/2.0;
|
|
}
|
|
} else {
|
|
if (replace == REPLACE_T) {
|
|
item->impactP += impactP;
|
|
item->impactN += impactN;
|
|
} else {
|
|
item->impactP += impactP/2.0;
|
|
item->impactN += impactN/2.0;
|
|
}
|
|
}
|
|
}
|
|
if ((replace == REPLACE_TT || replace == REPLACE_TE) &&
|
|
!Cudd_IsConstantInt(shared)) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(shared),
|
|
cuddI(dd,Cudd_Regular(shared)->index));
|
|
if (Cudd_IsComplement(shared)) {
|
|
item->impactP += impactN;
|
|
item->impactN += impactP;
|
|
} else {
|
|
item->impactP += impactP;
|
|
item->impactN += impactN;
|
|
}
|
|
}
|
|
}
|
|
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(1);
|
|
|
|
} /* end of RAmarkNodes */
|
|
|
|
|
|
/**
|
|
@brief Marks nodes for remapping.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddBiasedUnderApprox
|
|
|
|
*/
|
|
static int
|
|
BAmarkNodes(
|
|
DdManager *dd /**< manager */,
|
|
DdNode *f /**< function to be analyzed */,
|
|
ApproxInfo *info /**< info on %BDD */,
|
|
int threshold /**< when to stop approximating */,
|
|
double quality1 /**< minimum improvement for accepted changes when b=1 */,
|
|
double quality0 /**< minimum improvement for accepted changes when b=0 */)
|
|
{
|
|
DdLevelQueue *queue;
|
|
DdLevelQueue *localQueue;
|
|
NodeData *infoN, *infoT, *infoE;
|
|
GlobalQueueItem *item;
|
|
DdNode *node, *T, *E;
|
|
DdNode *shared; /* grandchild shared by the two children of node */
|
|
double numOnset;
|
|
double impact, impactP, impactN;
|
|
double minterms;
|
|
double quality;
|
|
int savings;
|
|
int replace;
|
|
|
|
#if 0
|
|
(void) fprintf(dd->out,"initial size = %d initial minterms = %g\n",
|
|
info->size, info->minterms);
|
|
#endif
|
|
queue = cuddLevelQueueInit(dd->size,sizeof(GlobalQueueItem),info->size,dd);
|
|
if (queue == NULL) {
|
|
return(0);
|
|
}
|
|
localQueue = cuddLevelQueueInit(dd->size,sizeof(LocalQueueItem),
|
|
dd->initSlots,dd);
|
|
if (localQueue == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
return(0);
|
|
}
|
|
/* Enqueue regular pointer to root and initialize impact. */
|
|
node = Cudd_Regular(f);
|
|
item = (GlobalQueueItem *)
|
|
cuddLevelQueueEnqueue(queue,node,cuddI(dd,node->index));
|
|
if (item == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
if (Cudd_IsComplement(f)) {
|
|
item->impactP = 0.0;
|
|
item->impactN = 1.0;
|
|
} else {
|
|
item->impactP = 1.0;
|
|
item->impactN = 0.0;
|
|
}
|
|
/* The nodes retrieved here are guaranteed to be non-terminal.
|
|
** The initial node is not terminal because constant nodes are
|
|
** dealt with in the calling procedure. Subsequent nodes are inserted
|
|
** only if they are not terminal. */
|
|
while (queue->first != NULL) {
|
|
/* If the size of the subset is below the threshold, quit. */
|
|
if (info->size <= threshold)
|
|
break;
|
|
item = (GlobalQueueItem *) queue->first;
|
|
node = item->node;
|
|
#ifdef DD_DEBUG
|
|
assert(item->impactP >= 0 && item->impactP <= 1.0);
|
|
assert(item->impactN >= 0 && item->impactN <= 1.0);
|
|
assert(!Cudd_IsComplement(node));
|
|
assert(!Cudd_IsConstantInt(node));
|
|
#endif
|
|
if ((infoN = (NodeData *) cuddHashTableGenericLookup(info->table, node)) == NULL) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
quality = infoN->care ? quality1 : quality0;
|
|
#ifdef DD_DEBUG
|
|
assert(infoN->parity >= 1 && infoN->parity <= 3);
|
|
#endif
|
|
if (infoN->parity == 3) {
|
|
/* This node can be reached through paths of different parity.
|
|
** It is not safe to replace it, because remapping will give
|
|
** an incorrect result, while replacement by 0 may cause node
|
|
** splitting. */
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
continue;
|
|
}
|
|
T = cuddT(node);
|
|
E = cuddE(node);
|
|
shared = NULL;
|
|
impactP = item->impactP;
|
|
impactN = item->impactN;
|
|
if (Cudd_bddLeq(dd,T,E)) {
|
|
/* Here we know that E is regular. */
|
|
#ifdef DD_DEBUG
|
|
assert(!Cudd_IsComplement(E));
|
|
#endif
|
|
infoT = (NodeData *) cuddHashTableGenericLookup(info->table, T);
|
|
infoE = (NodeData *) cuddHashTableGenericLookup(info->table, E);
|
|
if (infoN->parity == 1) {
|
|
impact = impactP;
|
|
minterms = infoE->mintermsP/2.0 - infoT->mintermsP/2.0;
|
|
if (infoE->functionRef == 1 && !Cudd_IsConstantInt(E)) {
|
|
savings = 1 + computeSavings(dd,E,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_E;
|
|
} else {
|
|
#ifdef DD_DEBUG
|
|
assert(infoN->parity == 2);
|
|
#endif
|
|
impact = impactN;
|
|
minterms = infoT->mintermsN/2.0 - infoE->mintermsN/2.0;
|
|
if (infoT->functionRef == 1 && !Cudd_IsConstantInt(T)) {
|
|
savings = 1 + computeSavings(dd,T,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_T;
|
|
}
|
|
numOnset = impact * minterms;
|
|
} else if (Cudd_bddLeq(dd,E,T)) {
|
|
/* Here E may be complemented. */
|
|
DdNode *Ereg = Cudd_Regular(E);
|
|
infoT = (NodeData *) cuddHashTableGenericLookup(info->table, T);
|
|
infoE = (NodeData *) cuddHashTableGenericLookup(info->table, Ereg);
|
|
if (infoN->parity == 1) {
|
|
impact = impactP;
|
|
minterms = infoT->mintermsP/2.0 -
|
|
((E == Ereg) ? infoE->mintermsP : infoE->mintermsN)/2.0;
|
|
if (infoT->functionRef == 1 && !Cudd_IsConstantInt(T)) {
|
|
savings = 1 + computeSavings(dd,T,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_T;
|
|
} else {
|
|
#ifdef DD_DEBUG
|
|
assert(infoN->parity == 2);
|
|
#endif
|
|
impact = impactN;
|
|
minterms = ((E == Ereg) ? infoE->mintermsN :
|
|
infoE->mintermsP)/2.0 - infoT->mintermsN/2.0;
|
|
if (infoE->functionRef == 1 && !Cudd_IsConstantInt(E)) {
|
|
savings = 1 + computeSavings(dd,E,NULL,info,localQueue);
|
|
if (savings == 1) {
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(0);
|
|
}
|
|
} else {
|
|
savings = 1;
|
|
}
|
|
replace = REPLACE_E;
|
|
}
|
|
numOnset = impact * minterms;
|
|
} else {
|
|
DdNode *Ereg = Cudd_Regular(E);
|
|
DdNode *TT = cuddT(T);
|
|
DdNode *ET = Cudd_NotCond(cuddT(Ereg), Cudd_IsComplement(E));
|
|
if (T->index == Ereg->index && TT == ET) {
|
|
shared = TT;
|
|
replace = REPLACE_TT;
|
|
} else {
|
|
DdNode *TE = cuddE(T);
|
|
DdNode *EE = Cudd_NotCond(cuddE(Ereg), Cudd_IsComplement(E));
|
|
if (T->index == Ereg->index && TE == EE) {
|
|
shared = TE;
|
|
replace = REPLACE_TE;
|
|
} else {
|
|
replace = REPLACE_N;
|
|
}
|
|
}
|
|
numOnset = infoN->mintermsP * impactP + infoN->mintermsN * impactN;
|
|
savings = computeSavings(dd,node,shared,info,localQueue);
|
|
if (shared != NULL) {
|
|
NodeData *infoS;
|
|
infoS = (NodeData *) cuddHashTableGenericLookup(info->table, Cudd_Regular(shared));
|
|
if (Cudd_IsComplement(shared)) {
|
|
numOnset -= (infoS->mintermsN * impactP +
|
|
infoS->mintermsP * impactN)/2.0;
|
|
} else {
|
|
numOnset -= (infoS->mintermsP * impactP +
|
|
infoS->mintermsN * impactN)/2.0;
|
|
}
|
|
savings--;
|
|
}
|
|
}
|
|
|
|
cuddLevelQueueDequeue(queue,cuddI(dd,node->index));
|
|
#if 0
|
|
if (replace == REPLACE_T || replace == REPLACE_E)
|
|
(void) printf("node %p: impact = %g numOnset = %g savings %d\n",
|
|
node, impact, numOnset, savings);
|
|
else
|
|
(void) printf("node %p: impact = %g/%g numOnset = %g savings %d\n",
|
|
node, impactP, impactN, numOnset, savings);
|
|
#endif
|
|
if ((1 - numOnset / info->minterms) >
|
|
quality * (1 - (double) savings / info->size)) {
|
|
infoN->replace = (char) replace;
|
|
info->size -= savings;
|
|
info->minterms -=numOnset;
|
|
#if 0
|
|
(void) printf("remap(%d): new size = %d new minterms = %g\n",
|
|
replace, info->size, info->minterms);
|
|
#endif
|
|
if (replace == REPLACE_N) {
|
|
savings -= updateRefs(dd,node,NULL,info,localQueue);
|
|
} else if (replace == REPLACE_T) {
|
|
savings -= updateRefs(dd,node,E,info,localQueue);
|
|
} else if (replace == REPLACE_E) {
|
|
savings -= updateRefs(dd,node,T,info,localQueue);
|
|
} else {
|
|
#ifdef DD_DEBUG
|
|
assert(replace == REPLACE_TT || replace == REPLACE_TE);
|
|
#endif
|
|
savings -= updateRefs(dd,node,shared,info,localQueue) - 1;
|
|
}
|
|
assert(savings == 0);
|
|
} else {
|
|
replace = NOTHING;
|
|
}
|
|
if (replace == REPLACE_N) continue;
|
|
if ((replace == REPLACE_E || replace == NOTHING) &&
|
|
!cuddIsConstant(cuddT(node))) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,cuddT(node),
|
|
cuddI(dd,cuddT(node)->index));
|
|
if (replace == REPLACE_E) {
|
|
item->impactP += impactP;
|
|
item->impactN += impactN;
|
|
} else {
|
|
item->impactP += impactP/2.0;
|
|
item->impactN += impactN/2.0;
|
|
}
|
|
}
|
|
if ((replace == REPLACE_T || replace == NOTHING) &&
|
|
!Cudd_IsConstantInt(cuddE(node))) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(cuddE(node)),
|
|
cuddI(dd,Cudd_Regular(cuddE(node))->index));
|
|
if (Cudd_IsComplement(cuddE(node))) {
|
|
if (replace == REPLACE_T) {
|
|
item->impactP += impactN;
|
|
item->impactN += impactP;
|
|
} else {
|
|
item->impactP += impactN/2.0;
|
|
item->impactN += impactP/2.0;
|
|
}
|
|
} else {
|
|
if (replace == REPLACE_T) {
|
|
item->impactP += impactP;
|
|
item->impactN += impactN;
|
|
} else {
|
|
item->impactP += impactP/2.0;
|
|
item->impactN += impactN/2.0;
|
|
}
|
|
}
|
|
}
|
|
if ((replace == REPLACE_TT || replace == REPLACE_TE) &&
|
|
!Cudd_IsConstantInt(shared)) {
|
|
item = (GlobalQueueItem *) cuddLevelQueueEnqueue(queue,Cudd_Regular(shared),
|
|
cuddI(dd,Cudd_Regular(shared)->index));
|
|
if (Cudd_IsComplement(shared)) {
|
|
if (replace == REPLACE_T) {
|
|
item->impactP += impactN;
|
|
item->impactN += impactP;
|
|
} else {
|
|
item->impactP += impactN/2.0;
|
|
item->impactN += impactP/2.0;
|
|
}
|
|
} else {
|
|
if (replace == REPLACE_T) {
|
|
item->impactP += impactP;
|
|
item->impactN += impactN;
|
|
} else {
|
|
item->impactP += impactP/2.0;
|
|
item->impactN += impactN/2.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cuddLevelQueueQuit(queue);
|
|
cuddLevelQueueQuit(localQueue);
|
|
return(1);
|
|
|
|
} /* end of BAmarkNodes */
|
|
|
|
|
|
/**
|
|
@brief Builds the subset %BDD for cuddRemapUnderApprox.
|
|
|
|
@details Based on the info table, performs remapping or replacement
|
|
at selected nodes.
|
|
|
|
@return a pointer to the result if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddRemapUnderApprox
|
|
|
|
*/
|
|
static DdNode *
|
|
RAbuildSubset(
|
|
DdManager * dd /**< %DD manager */,
|
|
DdNode * node /**< current node */,
|
|
ApproxInfo * info /**< node info */)
|
|
{
|
|
DdNode *Nt, *Ne, *N, *t, *e, *r;
|
|
NodeData *infoN;
|
|
|
|
if (Cudd_IsConstantInt(node))
|
|
return(node);
|
|
|
|
N = Cudd_Regular(node);
|
|
|
|
Nt = Cudd_NotCond(cuddT(N), Cudd_IsComplement(node));
|
|
Ne = Cudd_NotCond(cuddE(N), Cudd_IsComplement(node));
|
|
|
|
if ((infoN = (NodeData *) cuddHashTableGenericLookup(info->table, N)) != NULL) {
|
|
if (N == node ) {
|
|
if (infoN->resultP != NULL) {
|
|
return(infoN->resultP);
|
|
}
|
|
} else {
|
|
if (infoN->resultN != NULL) {
|
|
return(infoN->resultN);
|
|
}
|
|
}
|
|
if (infoN->replace == REPLACE_T) {
|
|
r = RAbuildSubset(dd, Ne, info);
|
|
return(r);
|
|
} else if (infoN->replace == REPLACE_E) {
|
|
r = RAbuildSubset(dd, Nt, info);
|
|
return(r);
|
|
} else if (infoN->replace == REPLACE_N) {
|
|
return(info->zero);
|
|
} else if (infoN->replace == REPLACE_TT) {
|
|
DdNode *Ntt = Cudd_NotCond(cuddT(cuddT(N)),
|
|
Cudd_IsComplement(node));
|
|
int index = cuddT(N)->index;
|
|
e = info->zero;
|
|
t = RAbuildSubset(dd, Ntt, info);
|
|
if (t == NULL) {
|
|
return(NULL);
|
|
}
|
|
cuddRef(t);
|
|
if (Cudd_IsComplement(t)) {
|
|
t = Cudd_Not(t);
|
|
e = Cudd_Not(e);
|
|
r = (t == e) ? t : cuddUniqueInter(dd, index, t, e);
|
|
if (r == NULL) {
|
|
Cudd_RecursiveDeref(dd, t);
|
|
return(NULL);
|
|
}
|
|
r = Cudd_Not(r);
|
|
} else {
|
|
r = (t == e) ? t : cuddUniqueInter(dd, index, t, e);
|
|
if (r == NULL) {
|
|
Cudd_RecursiveDeref(dd, t);
|
|
return(NULL);
|
|
}
|
|
}
|
|
cuddDeref(t);
|
|
return(r);
|
|
} else if (infoN->replace == REPLACE_TE) {
|
|
DdNode *Nte = Cudd_NotCond(cuddE(cuddT(N)),
|
|
Cudd_IsComplement(node));
|
|
unsigned int index = cuddT(N)->index;
|
|
t = info->one;
|
|
e = RAbuildSubset(dd, Nte, info);
|
|
if (e == NULL) {
|
|
return(NULL);
|
|
}
|
|
cuddRef(e);
|
|
e = Cudd_Not(e);
|
|
r = (t == e) ? t : cuddUniqueInter(dd, index, t, e);
|
|
if (r == NULL) {
|
|
Cudd_RecursiveDeref(dd, e);
|
|
return(NULL);
|
|
}
|
|
r =Cudd_Not(r);
|
|
cuddDeref(e);
|
|
return(r);
|
|
}
|
|
} else {
|
|
(void) fprintf(dd->err,
|
|
"Something is wrong, ought to be in info table\n");
|
|
dd->errorCode = CUDD_INTERNAL_ERROR;
|
|
return(NULL);
|
|
}
|
|
|
|
t = RAbuildSubset(dd, Nt, info);
|
|
if (t == NULL) {
|
|
return(NULL);
|
|
}
|
|
cuddRef(t);
|
|
|
|
e = RAbuildSubset(dd, Ne, info);
|
|
if (e == NULL) {
|
|
Cudd_RecursiveDeref(dd,t);
|
|
return(NULL);
|
|
}
|
|
cuddRef(e);
|
|
|
|
if (Cudd_IsComplement(t)) {
|
|
t = Cudd_Not(t);
|
|
e = Cudd_Not(e);
|
|
r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e);
|
|
if (r == NULL) {
|
|
Cudd_RecursiveDeref(dd, e);
|
|
Cudd_RecursiveDeref(dd, t);
|
|
return(NULL);
|
|
}
|
|
r = Cudd_Not(r);
|
|
} else {
|
|
r = (t == e) ? t : cuddUniqueInter(dd, N->index, t, e);
|
|
if (r == NULL) {
|
|
Cudd_RecursiveDeref(dd, e);
|
|
Cudd_RecursiveDeref(dd, t);
|
|
return(NULL);
|
|
}
|
|
}
|
|
cuddDeref(t);
|
|
cuddDeref(e);
|
|
|
|
if (N == node) {
|
|
infoN->resultP = r;
|
|
} else {
|
|
infoN->resultN = r;
|
|
}
|
|
|
|
return(r);
|
|
|
|
} /* end of RAbuildSubset */
|
|
|
|
|
|
/**
|
|
@brief Finds don't care nodes by traversing f and b in parallel.
|
|
|
|
@return the care status of the visited f node if successful;
|
|
CARE_ERROR otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddBiasedUnderApprox
|
|
|
|
*/
|
|
static int
|
|
BAapplyBias(
|
|
DdManager *dd,
|
|
DdNode *f,
|
|
DdNode *b,
|
|
ApproxInfo *info,
|
|
DdHashTable *cache)
|
|
{
|
|
DdNode *one, *zero, *res;
|
|
DdNode *Ft, *Fe, *B, *Bt, *Be;
|
|
int topf, topb;
|
|
NodeData *infoF;
|
|
int careT, careE;
|
|
|
|
one = DD_ONE(dd);
|
|
zero = Cudd_Not(one);
|
|
|
|
if ((infoF = (NodeData *) cuddHashTableGenericLookup(info->table, f)) == NULL)
|
|
return(CARE_ERROR);
|
|
if (f == one) return(TOTAL_CARE);
|
|
if (b == zero) return(infoF->care);
|
|
if (infoF->care == TOTAL_CARE) return(TOTAL_CARE);
|
|
|
|
if ((f->ref != 1 || Cudd_Regular(b)->ref != 1) &&
|
|
(res = cuddHashTableLookup2(cache,f,b)) != NULL) {
|
|
if (res->ref == 0) {
|
|
cache->manager->dead++;
|
|
cache->manager->constants.dead++;
|
|
}
|
|
return(infoF->care);
|
|
}
|
|
|
|
topf = dd->perm[f->index];
|
|
B = Cudd_Regular(b);
|
|
topb = cuddI(dd,B->index);
|
|
if (topf <= topb) {
|
|
Ft = cuddT(f); Fe = cuddE(f);
|
|
} else {
|
|
Ft = Fe = f;
|
|
}
|
|
if (topb <= topf) {
|
|
/* We know that b is not constant because f is not. */
|
|
Bt = cuddT(B); Be = cuddE(B);
|
|
if (Cudd_IsComplement(b)) {
|
|
Bt = Cudd_Not(Bt);
|
|
Be = Cudd_Not(Be);
|
|
}
|
|
} else {
|
|
Bt = Be = b;
|
|
}
|
|
|
|
careT = BAapplyBias(dd, Ft, Bt, info, cache);
|
|
if (careT == CARE_ERROR)
|
|
return(CARE_ERROR);
|
|
careE = BAapplyBias(dd, Cudd_Regular(Fe), Be, info, cache);
|
|
if (careE == CARE_ERROR)
|
|
return(CARE_ERROR);
|
|
if (careT == TOTAL_CARE && careE == TOTAL_CARE) {
|
|
infoF->care = TOTAL_CARE;
|
|
} else {
|
|
infoF->care = CARE;
|
|
}
|
|
|
|
if (f->ref != 1 || Cudd_Regular(b)->ref != 1) {
|
|
ptrint fanout = (ptrint) f->ref * Cudd_Regular(b)->ref;
|
|
cuddSatDec(fanout);
|
|
if (!cuddHashTableInsert2(cache,f,b,one,fanout)) {
|
|
return(CARE_ERROR);
|
|
}
|
|
}
|
|
return(infoF->care);
|
|
|
|
} /* end of BAapplyBias */
|