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.
1711 lines
46 KiB
1711 lines
46 KiB
/**CFile***********************************************************************
|
|
|
|
FileName [cuddZddSymm.c]
|
|
|
|
PackageName [cudd]
|
|
|
|
Synopsis [Functions for symmetry-based ZDD variable reordering.]
|
|
|
|
Description [External procedures included in this module:
|
|
<ul>
|
|
<li> Cudd_zddSymmProfile()
|
|
</ul>
|
|
Internal procedures included in this module:
|
|
<ul>
|
|
<li> cuddZddSymmCheck()
|
|
<li> cuddZddSymmSifting()
|
|
<li> cuddZddSymmSiftingConv()
|
|
</ul>
|
|
Static procedures included in this module:
|
|
<ul>
|
|
<li> cuddZddUniqueCompare()
|
|
<li> cuddZddSymmSiftingAux()
|
|
<li> cuddZddSymmSiftingConvAux()
|
|
<li> cuddZddSymmSifting_up()
|
|
<li> cuddZddSymmSifting_down()
|
|
<li> zdd_group_move()
|
|
<li> cuddZddSymmSiftingBackward()
|
|
<li> zdd_group_move_backward()
|
|
</ul>
|
|
]
|
|
|
|
SeeAlso [cuddSymmetry.c]
|
|
|
|
Author [Hyong-Kyoon Shin, In-Ho Moon]
|
|
|
|
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 ZDD_MV_OOM (Move *)1
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] DD_UNUSED = "$Id: cuddZddSymm.c,v 1.31 2012/02/05 01:07:19 fabio Exp $";
|
|
#endif
|
|
|
|
extern int *zdd_entry;
|
|
|
|
extern int zddTotalNumberSwapping;
|
|
|
|
static DdNode *empty;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**AutomaticStart*************************************************************/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int cuddZddSymmSiftingAux (DdManager *table, int x, int x_low, int x_high);
|
|
static int cuddZddSymmSiftingConvAux (DdManager *table, int x, int x_low, int x_high);
|
|
static Move * cuddZddSymmSifting_up (DdManager *table, int x, int x_low, int initial_size);
|
|
static Move * cuddZddSymmSifting_down (DdManager *table, int x, int x_high, int initial_size);
|
|
static int cuddZddSymmSiftingBackward (DdManager *table, Move *moves, int size);
|
|
static int zdd_group_move (DdManager *table, int x, int y, Move **moves);
|
|
static int zdd_group_move_backward (DdManager *table, int x, int y);
|
|
static void cuddZddSymmSummary (DdManager *table, int lower, int upper, int *symvars, int *symgroups);
|
|
|
|
/**AutomaticEnd***************************************************************/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Prints statistics on symmetric ZDD variables.]
|
|
|
|
Description []
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
void
|
|
Cudd_zddSymmProfile(
|
|
DdManager * table,
|
|
int lower,
|
|
int upper)
|
|
{
|
|
int i, x, gbot;
|
|
int TotalSymm = 0;
|
|
int TotalSymmGroups = 0;
|
|
|
|
for (i = lower; i < upper; i++) {
|
|
if (table->subtableZ[i].next != (unsigned) i) {
|
|
x = i;
|
|
(void) fprintf(table->out,"Group:");
|
|
do {
|
|
(void) fprintf(table->out," %d", table->invpermZ[x]);
|
|
TotalSymm++;
|
|
gbot = x;
|
|
x = table->subtableZ[x].next;
|
|
} while (x != i);
|
|
TotalSymmGroups++;
|
|
#ifdef DD_DEBUG
|
|
assert(table->subtableZ[gbot].next == (unsigned) i);
|
|
#endif
|
|
i = gbot;
|
|
(void) fprintf(table->out,"\n");
|
|
}
|
|
}
|
|
(void) fprintf(table->out,"Total Symmetric = %d\n", TotalSymm);
|
|
(void) fprintf(table->out,"Total Groups = %d\n", TotalSymmGroups);
|
|
|
|
} /* end of Cudd_zddSymmProfile */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Checks for symmetry of x and y.]
|
|
|
|
Description [Checks for symmetry of x and y. Ignores projection
|
|
functions, unless they are isolated. Returns 1 in case of
|
|
symmetry; 0 otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
int
|
|
cuddZddSymmCheck(
|
|
DdManager * table,
|
|
int x,
|
|
int y)
|
|
{
|
|
int i;
|
|
DdNode *f, *f0, *f1, *f01, *f00, *f11, *f10;
|
|
int yindex;
|
|
int xsymmy = 1;
|
|
int xsymmyp = 1;
|
|
int arccount = 0;
|
|
int TotalRefCount = 0;
|
|
int symm_found;
|
|
|
|
empty = table->zero;
|
|
|
|
yindex = table->invpermZ[y];
|
|
for (i = table->subtableZ[x].slots - 1; i >= 0; i--) {
|
|
f = table->subtableZ[x].nodelist[i];
|
|
while (f != NULL) {
|
|
/* Find f1, f0, f11, f10, f01, f00 */
|
|
f1 = cuddT(f);
|
|
f0 = cuddE(f);
|
|
if ((int) f1->index == yindex) {
|
|
f11 = cuddT(f1);
|
|
f10 = cuddE(f1);
|
|
if (f10 != empty)
|
|
arccount++;
|
|
} else {
|
|
if ((int) f0->index != yindex) {
|
|
return(0); /* f bypasses layer y */
|
|
}
|
|
f11 = empty;
|
|
f10 = f1;
|
|
}
|
|
if ((int) f0->index == yindex) {
|
|
f01 = cuddT(f0);
|
|
f00 = cuddE(f0);
|
|
if (f00 != empty)
|
|
arccount++;
|
|
} else {
|
|
f01 = empty;
|
|
f00 = f0;
|
|
}
|
|
if (f01 != f10)
|
|
xsymmy = 0;
|
|
if (f11 != f00)
|
|
xsymmyp = 0;
|
|
if ((xsymmy == 0) && (xsymmyp == 0))
|
|
return(0);
|
|
|
|
f = f->next;
|
|
} /* for each element of the collision list */
|
|
} /* for each slot of the subtable */
|
|
|
|
/* Calculate the total reference counts of y
|
|
** whose else arc is not empty.
|
|
*/
|
|
for (i = table->subtableZ[y].slots - 1; i >= 0; i--) {
|
|
f = table->subtableZ[y].nodelist[i];
|
|
while (f != NIL(DdNode)) {
|
|
if (cuddE(f) != empty)
|
|
TotalRefCount += f->ref;
|
|
f = f->next;
|
|
}
|
|
}
|
|
|
|
symm_found = (arccount == TotalRefCount);
|
|
#if defined(DD_DEBUG) && defined(DD_VERBOSE)
|
|
if (symm_found) {
|
|
int xindex = table->invpermZ[x];
|
|
(void) fprintf(table->out,
|
|
"Found symmetry! x =%d\ty = %d\tPos(%d,%d)\n",
|
|
xindex,yindex,x,y);
|
|
}
|
|
#endif
|
|
|
|
return(symm_found);
|
|
|
|
} /* end cuddZddSymmCheck */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Symmetric sifting algorithm for ZDDs.]
|
|
|
|
Description [Symmetric sifting algorithm.
|
|
Assumes that no dead nodes are present.
|
|
<ol>
|
|
<li> Order all the variables according to the number of entries in
|
|
each unique subtable.
|
|
<li> Sift the variable up and down, remembering each time the total
|
|
size of the ZDD heap and grouping variables that are symmetric.
|
|
<li> Select the best permutation.
|
|
<li> Repeat 3 and 4 for all variables.
|
|
</ol>
|
|
Returns 1 plus the number of symmetric variables if successful; 0
|
|
otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso [cuddZddSymmSiftingConv]
|
|
|
|
******************************************************************************/
|
|
int
|
|
cuddZddSymmSifting(
|
|
DdManager * table,
|
|
int lower,
|
|
int upper)
|
|
{
|
|
int i;
|
|
int *var;
|
|
int nvars;
|
|
int x;
|
|
int result;
|
|
int symvars;
|
|
int symgroups;
|
|
int iteration;
|
|
#ifdef DD_STATS
|
|
int previousSize;
|
|
#endif
|
|
|
|
nvars = table->sizeZ;
|
|
|
|
/* Find order in which to sift variables. */
|
|
var = NULL;
|
|
zdd_entry = ALLOC(int, nvars);
|
|
if (zdd_entry == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
goto cuddZddSymmSiftingOutOfMem;
|
|
}
|
|
var = ALLOC(int, nvars);
|
|
if (var == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
goto cuddZddSymmSiftingOutOfMem;
|
|
}
|
|
|
|
for (i = 0; i < nvars; i++) {
|
|
x = table->permZ[i];
|
|
zdd_entry[i] = table->subtableZ[x].keys;
|
|
var[i] = i;
|
|
}
|
|
|
|
qsort((void *)var, nvars, sizeof(int), (DD_QSFP)cuddZddUniqueCompare);
|
|
|
|
/* Initialize the symmetry of each subtable to itself. */
|
|
for (i = lower; i <= upper; i++)
|
|
table->subtableZ[i].next = i;
|
|
|
|
iteration = ddMin(table->siftMaxVar, nvars);
|
|
for (i = 0; i < iteration; i++) {
|
|
if (zddTotalNumberSwapping >= table->siftMaxSwap)
|
|
break;
|
|
if (util_cpu_time() - table->startTime > table->timeLimit) {
|
|
table->autoDynZ = 0; /* prevent further reordering */
|
|
break;
|
|
}
|
|
x = table->permZ[var[i]];
|
|
#ifdef DD_STATS
|
|
previousSize = table->keysZ;
|
|
#endif
|
|
if (x < lower || x > upper) continue;
|
|
if (table->subtableZ[x].next == (unsigned) x) {
|
|
result = cuddZddSymmSiftingAux(table, x, lower, upper);
|
|
if (!result)
|
|
goto cuddZddSymmSiftingOutOfMem;
|
|
#ifdef DD_STATS
|
|
if (table->keysZ < (unsigned) previousSize) {
|
|
(void) fprintf(table->out,"-");
|
|
} else if (table->keysZ > (unsigned) previousSize) {
|
|
(void) fprintf(table->out,"+");
|
|
#ifdef DD_VERBOSE
|
|
(void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]);
|
|
#endif
|
|
} else {
|
|
(void) fprintf(table->out,"=");
|
|
}
|
|
fflush(table->out);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
FREE(var);
|
|
FREE(zdd_entry);
|
|
|
|
cuddZddSymmSummary(table, lower, upper, &symvars, &symgroups);
|
|
|
|
#ifdef DD_STATS
|
|
(void) fprintf(table->out,"\n#:S_SIFTING %8d: symmetric variables\n",symvars);
|
|
(void) fprintf(table->out,"#:G_SIFTING %8d: symmetric groups\n",symgroups);
|
|
#endif
|
|
|
|
return(1+symvars);
|
|
|
|
cuddZddSymmSiftingOutOfMem:
|
|
|
|
if (zdd_entry != NULL)
|
|
FREE(zdd_entry);
|
|
if (var != NULL)
|
|
FREE(var);
|
|
|
|
return(0);
|
|
|
|
} /* end of cuddZddSymmSifting */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Symmetric sifting to convergence algorithm for ZDDs.]
|
|
|
|
Description [Symmetric sifting to convergence algorithm for ZDDs.
|
|
Assumes that no dead nodes are present.
|
|
<ol>
|
|
<li> Order all the variables according to the number of entries in
|
|
each unique subtable.
|
|
<li> Sift the variable up and down, remembering each time the total
|
|
size of the ZDD heap and grouping variables that are symmetric.
|
|
<li> Select the best permutation.
|
|
<li> Repeat 3 and 4 for all variables.
|
|
<li> Repeat 1-4 until no further improvement.
|
|
</ol>
|
|
Returns 1 plus the number of symmetric variables if successful; 0
|
|
otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso [cuddZddSymmSifting]
|
|
|
|
******************************************************************************/
|
|
int
|
|
cuddZddSymmSiftingConv(
|
|
DdManager * table,
|
|
int lower,
|
|
int upper)
|
|
{
|
|
int i;
|
|
int *var;
|
|
int nvars;
|
|
int initialSize;
|
|
int x;
|
|
int result;
|
|
int symvars;
|
|
int symgroups;
|
|
int classes;
|
|
int iteration;
|
|
#ifdef DD_STATS
|
|
int previousSize;
|
|
#endif
|
|
|
|
initialSize = table->keysZ;
|
|
|
|
nvars = table->sizeZ;
|
|
|
|
/* Find order in which to sift variables. */
|
|
var = NULL;
|
|
zdd_entry = ALLOC(int, nvars);
|
|
if (zdd_entry == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
goto cuddZddSymmSiftingConvOutOfMem;
|
|
}
|
|
var = ALLOC(int, nvars);
|
|
if (var == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
goto cuddZddSymmSiftingConvOutOfMem;
|
|
}
|
|
|
|
for (i = 0; i < nvars; i++) {
|
|
x = table->permZ[i];
|
|
zdd_entry[i] = table->subtableZ[x].keys;
|
|
var[i] = i;
|
|
}
|
|
|
|
qsort((void *)var, nvars, sizeof(int), (DD_QSFP)cuddZddUniqueCompare);
|
|
|
|
/* Initialize the symmetry of each subtable to itself
|
|
** for first pass of converging symmetric sifting.
|
|
*/
|
|
for (i = lower; i <= upper; i++)
|
|
table->subtableZ[i].next = i;
|
|
|
|
iteration = ddMin(table->siftMaxVar, table->sizeZ);
|
|
for (i = 0; i < iteration; i++) {
|
|
if (zddTotalNumberSwapping >= table->siftMaxSwap)
|
|
break;
|
|
if (util_cpu_time() - table->startTime > table->timeLimit) {
|
|
table->autoDynZ = 0; /* prevent further reordering */
|
|
break;
|
|
}
|
|
x = table->permZ[var[i]];
|
|
if (x < lower || x > upper) continue;
|
|
/* Only sift if not in symmetry group already. */
|
|
if (table->subtableZ[x].next == (unsigned) x) {
|
|
#ifdef DD_STATS
|
|
previousSize = table->keysZ;
|
|
#endif
|
|
result = cuddZddSymmSiftingAux(table, x, lower, upper);
|
|
if (!result)
|
|
goto cuddZddSymmSiftingConvOutOfMem;
|
|
#ifdef DD_STATS
|
|
if (table->keysZ < (unsigned) previousSize) {
|
|
(void) fprintf(table->out,"-");
|
|
} else if (table->keysZ > (unsigned) previousSize) {
|
|
(void) fprintf(table->out,"+");
|
|
#ifdef DD_VERBOSE
|
|
(void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]);
|
|
#endif
|
|
} else {
|
|
(void) fprintf(table->out,"=");
|
|
}
|
|
fflush(table->out);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* Sifting now until convergence. */
|
|
while ((unsigned) initialSize > table->keysZ) {
|
|
initialSize = table->keysZ;
|
|
#ifdef DD_STATS
|
|
(void) fprintf(table->out,"\n");
|
|
#endif
|
|
/* Here we consider only one representative for each symmetry class. */
|
|
for (x = lower, classes = 0; x <= upper; x++, classes++) {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
/* Here x is the largest index in a group.
|
|
** Groups consists of adjacent variables.
|
|
** Hence, the next increment of x will move it to a new group.
|
|
*/
|
|
i = table->invpermZ[x];
|
|
zdd_entry[i] = table->subtableZ[x].keys;
|
|
var[classes] = i;
|
|
}
|
|
|
|
qsort((void *)var,classes,sizeof(int),(DD_QSFP)cuddZddUniqueCompare);
|
|
|
|
/* Now sift. */
|
|
iteration = ddMin(table->siftMaxVar, nvars);
|
|
for (i = 0; i < iteration; i++) {
|
|
if (zddTotalNumberSwapping >= table->siftMaxSwap)
|
|
break;
|
|
if (util_cpu_time() - table->startTime > table->timeLimit) {
|
|
table->autoDynZ = 0; /* prevent further reordering */
|
|
break;
|
|
}
|
|
x = table->permZ[var[i]];
|
|
if ((unsigned) x >= table->subtableZ[x].next) {
|
|
#ifdef DD_STATS
|
|
previousSize = table->keysZ;
|
|
#endif
|
|
result = cuddZddSymmSiftingConvAux(table, x, lower, upper);
|
|
if (!result)
|
|
goto cuddZddSymmSiftingConvOutOfMem;
|
|
#ifdef DD_STATS
|
|
if (table->keysZ < (unsigned) previousSize) {
|
|
(void) fprintf(table->out,"-");
|
|
} else if (table->keysZ > (unsigned) previousSize) {
|
|
(void) fprintf(table->out,"+");
|
|
#ifdef DD_VERBOSE
|
|
(void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]);
|
|
#endif
|
|
} else {
|
|
(void) fprintf(table->out,"=");
|
|
}
|
|
fflush(table->out);
|
|
#endif
|
|
}
|
|
} /* for */
|
|
}
|
|
|
|
cuddZddSymmSummary(table, lower, upper, &symvars, &symgroups);
|
|
|
|
#ifdef DD_STATS
|
|
(void) fprintf(table->out,"\n#:S_SIFTING %8d: symmetric variables\n",
|
|
symvars);
|
|
(void) fprintf(table->out,"#:G_SIFTING %8d: symmetric groups\n",
|
|
symgroups);
|
|
#endif
|
|
|
|
FREE(var);
|
|
FREE(zdd_entry);
|
|
|
|
return(1+symvars);
|
|
|
|
cuddZddSymmSiftingConvOutOfMem:
|
|
|
|
if (zdd_entry != NULL)
|
|
FREE(zdd_entry);
|
|
if (var != NULL)
|
|
FREE(var);
|
|
|
|
return(0);
|
|
|
|
} /* end of cuddZddSymmSiftingConv */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Given x_low <= x <= x_high moves x up and down between the
|
|
boundaries.]
|
|
|
|
Description [Given x_low <= x <= x_high moves x up and down between the
|
|
boundaries. Finds the best position and does the required changes.
|
|
Assumes that x is not part of a symmetry group. Returns 1 if
|
|
successful; 0 otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static int
|
|
cuddZddSymmSiftingAux(
|
|
DdManager * table,
|
|
int x,
|
|
int x_low,
|
|
int x_high)
|
|
{
|
|
Move *move;
|
|
Move *move_up; /* list of up move */
|
|
Move *move_down; /* list of down move */
|
|
int initial_size;
|
|
int result;
|
|
int i;
|
|
int topbot; /* index to either top or bottom of symmetry group */
|
|
int init_group_size, final_group_size;
|
|
|
|
initial_size = table->keysZ;
|
|
|
|
move_down = NULL;
|
|
move_up = NULL;
|
|
|
|
/* Look for consecutive symmetries above x. */
|
|
for (i = x; i > x_low; i--) {
|
|
if (!cuddZddSymmCheck(table, i - 1, i))
|
|
break;
|
|
/* find top of i-1's symmetry */
|
|
topbot = table->subtableZ[i - 1].next;
|
|
table->subtableZ[i - 1].next = i;
|
|
table->subtableZ[x].next = topbot;
|
|
/* x is bottom of group so its symmetry is top of i-1's
|
|
group */
|
|
i = topbot + 1; /* add 1 for i--, new i is top of symm group */
|
|
}
|
|
/* Look for consecutive symmetries below x. */
|
|
for (i = x; i < x_high; i++) {
|
|
if (!cuddZddSymmCheck(table, i, i + 1))
|
|
break;
|
|
/* find bottom of i+1's symm group */
|
|
topbot = i + 1;
|
|
while ((unsigned) topbot < table->subtableZ[topbot].next)
|
|
topbot = table->subtableZ[topbot].next;
|
|
|
|
table->subtableZ[topbot].next = table->subtableZ[i].next;
|
|
table->subtableZ[i].next = i + 1;
|
|
i = topbot - 1; /* add 1 for i++,
|
|
new i is bottom of symm group */
|
|
}
|
|
|
|
/* Now x maybe in the middle of a symmetry group. */
|
|
if (x == x_low) { /* Sift down */
|
|
/* Find bottom of x's symm group */
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
|
|
i = table->subtableZ[x].next;
|
|
init_group_size = x - i + 1;
|
|
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
/* after that point x --> x_high, unless early term */
|
|
if (move_down == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
|
|
if (move_down == NULL ||
|
|
table->subtableZ[move_down->y].next != move_down->y) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_down != NULL)
|
|
x = move_down->y;
|
|
else
|
|
x = table->subtableZ[x].next;
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
final_group_size = i - x + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetry groups detected,
|
|
return to best position */
|
|
result = cuddZddSymmSiftingBackward(table,
|
|
move_down, initial_size);
|
|
}
|
|
else {
|
|
initial_size = table->keysZ;
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
}
|
|
else if (x == x_high) { /* Sift up */
|
|
/* Find top of x's symm group */
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
x = table->subtableZ[x].next;
|
|
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
init_group_size = i - x + 1;
|
|
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size);
|
|
/* after that point x --> x_low, unless early term */
|
|
if (move_up == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
|
|
if (move_up == NULL ||
|
|
table->subtableZ[move_up->x].next != move_up->x) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_up != NULL)
|
|
x = move_up->x;
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = table->subtableZ[x].next;
|
|
final_group_size = x - i + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetry groups detected,
|
|
return to best position */
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
else {
|
|
initial_size = table->keysZ;
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
}
|
|
else if ((x - x_low) > (x_high - x)) { /* must go down first:
|
|
shorter */
|
|
/* Find bottom of x's symm group */
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
/* after that point x --> x_high, unless early term */
|
|
if (move_down == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
|
|
if (move_down != NULL) {
|
|
x = move_down->y;
|
|
}
|
|
else {
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
init_group_size = i - x + 1;
|
|
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size);
|
|
if (move_up == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
|
|
if (move_up == NULL ||
|
|
table->subtableZ[move_up->x].next != move_up->x) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_up != NULL) {
|
|
x = move_up->x;
|
|
}
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = table->subtableZ[x].next;
|
|
final_group_size = x - i + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetry groups detected,
|
|
return to best position */
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
else {
|
|
while (move_down != NULL) {
|
|
move = move_down->next;
|
|
cuddDeallocMove(table, move_down);
|
|
move_down = move;
|
|
}
|
|
initial_size = table->keysZ;
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
}
|
|
else { /* moving up first:shorter */
|
|
/* Find top of x's symmetry group */
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
x = table->subtableZ[x].next;
|
|
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size);
|
|
/* after that point x --> x_high, unless early term */
|
|
if (move_up == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
|
|
if (move_up != NULL) {
|
|
x = move_up->x;
|
|
}
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = table->subtableZ[x].next;
|
|
init_group_size = x - i + 1;
|
|
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
if (move_down == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
|
|
if (move_down == NULL ||
|
|
table->subtableZ[move_down->y].next != move_down->y) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_down != NULL) {
|
|
x = move_down->y;
|
|
}
|
|
else {
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
final_group_size = i - x + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetries detected,
|
|
go back to best position */
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
}
|
|
else {
|
|
while (move_up != NULL) {
|
|
move = move_up->next;
|
|
cuddDeallocMove(table, move_up);
|
|
move_up = move;
|
|
}
|
|
initial_size = table->keysZ;
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingAuxOutOfMem;
|
|
}
|
|
|
|
while (move_down != NULL) {
|
|
move = move_down->next;
|
|
cuddDeallocMove(table, move_down);
|
|
move_down = move;
|
|
}
|
|
while (move_up != NULL) {
|
|
move = move_up->next;
|
|
cuddDeallocMove(table, move_up);
|
|
move_up = move;
|
|
}
|
|
|
|
return(1);
|
|
|
|
cuddZddSymmSiftingAuxOutOfMem:
|
|
if (move_down != ZDD_MV_OOM) {
|
|
while (move_down != NULL) {
|
|
move = move_down->next;
|
|
cuddDeallocMove(table, move_down);
|
|
move_down = move;
|
|
}
|
|
}
|
|
if (move_up != ZDD_MV_OOM) {
|
|
while (move_up != NULL) {
|
|
move = move_up->next;
|
|
cuddDeallocMove(table, move_up);
|
|
move_up = move;
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
|
|
} /* end of cuddZddSymmSiftingAux */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Given x_low <= x <= x_high moves x up and down between the
|
|
boundaries.]
|
|
|
|
Description [Given x_low <= x <= x_high moves x up and down between the
|
|
boundaries. Finds the best position and does the required changes.
|
|
Assumes that x is either an isolated variable, or it is the bottom of
|
|
a symmetry group. All symmetries may not have been found, because of
|
|
exceeded growth limit. Returns 1 if successful; 0 otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static int
|
|
cuddZddSymmSiftingConvAux(
|
|
DdManager * table,
|
|
int x,
|
|
int x_low,
|
|
int x_high)
|
|
{
|
|
Move *move;
|
|
Move *move_up; /* list of up move */
|
|
Move *move_down; /* list of down move */
|
|
int initial_size;
|
|
int result;
|
|
int i;
|
|
int init_group_size, final_group_size;
|
|
|
|
initial_size = table->keysZ;
|
|
|
|
move_down = NULL;
|
|
move_up = NULL;
|
|
|
|
if (x == x_low) { /* Sift down */
|
|
i = table->subtableZ[x].next;
|
|
init_group_size = x - i + 1;
|
|
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
/* after that point x --> x_high, unless early term */
|
|
if (move_down == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
|
|
if (move_down == NULL ||
|
|
table->subtableZ[move_down->y].next != move_down->y) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_down != NULL)
|
|
x = move_down->y;
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
final_group_size = i - x + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetries detected,
|
|
go back to best position */
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
}
|
|
else {
|
|
initial_size = table->keysZ;
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
}
|
|
else if (x == x_high) { /* Sift up */
|
|
/* Find top of x's symm group */
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
x = table->subtableZ[x].next;
|
|
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
init_group_size = i - x + 1;
|
|
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size);
|
|
/* after that point x --> x_low, unless early term */
|
|
if (move_up == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
|
|
if (move_up == NULL ||
|
|
table->subtableZ[move_up->x].next != move_up->x) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_up != NULL)
|
|
x = move_up->x;
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = table->subtableZ[x].next;
|
|
final_group_size = x - i + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetry groups detected,
|
|
return to best position */
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
else {
|
|
initial_size = table->keysZ;
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
}
|
|
else if ((x - x_low) > (x_high - x)) { /* must go down first:
|
|
shorter */
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
/* after that point x --> x_high */
|
|
if (move_down == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
|
|
if (move_down != NULL) {
|
|
x = move_down->y;
|
|
}
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
init_group_size = i - x + 1;
|
|
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size);
|
|
if (move_up == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
|
|
if (move_up == NULL ||
|
|
table->subtableZ[move_up->x].next != move_up->x) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_up != NULL) {
|
|
x = move_up->x;
|
|
}
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = table->subtableZ[x].next;
|
|
final_group_size = x - i + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetry groups detected,
|
|
return to best position */
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
else {
|
|
while (move_down != NULL) {
|
|
move = move_down->next;
|
|
cuddDeallocMove(table, move_down);
|
|
move_down = move;
|
|
}
|
|
initial_size = table->keysZ;
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
}
|
|
else { /* moving up first:shorter */
|
|
/* Find top of x's symmetry group */
|
|
x = table->subtableZ[x].next;
|
|
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size);
|
|
/* after that point x --> x_high, unless early term */
|
|
if (move_up == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
|
|
if (move_up != NULL) {
|
|
x = move_up->x;
|
|
}
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = table->subtableZ[x].next;
|
|
init_group_size = x - i + 1;
|
|
|
|
move_down = cuddZddSymmSifting_down(table, x, x_high,
|
|
initial_size);
|
|
if (move_down == ZDD_MV_OOM)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
|
|
if (move_down == NULL ||
|
|
table->subtableZ[move_down->y].next != move_down->y) {
|
|
/* symmetry detected may have to make another complete
|
|
pass */
|
|
if (move_down != NULL) {
|
|
x = move_down->y;
|
|
}
|
|
else {
|
|
while ((unsigned) x < table->subtableZ[x].next)
|
|
x = table->subtableZ[x].next;
|
|
x = table->subtableZ[x].next;
|
|
}
|
|
i = x;
|
|
while ((unsigned) i < table->subtableZ[i].next) {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
final_group_size = i - x + 1;
|
|
|
|
if (init_group_size == final_group_size) {
|
|
/* No new symmetries detected,
|
|
go back to best position */
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
}
|
|
else {
|
|
while (move_up != NULL) {
|
|
move = move_up->next;
|
|
cuddDeallocMove(table, move_up);
|
|
move_up = move;
|
|
}
|
|
initial_size = table->keysZ;
|
|
move_up = cuddZddSymmSifting_up(table, x, x_low,
|
|
initial_size);
|
|
result = cuddZddSymmSiftingBackward(table, move_up,
|
|
initial_size);
|
|
}
|
|
}
|
|
else {
|
|
result = cuddZddSymmSiftingBackward(table, move_down,
|
|
initial_size);
|
|
/* move backward and stop at best position */
|
|
}
|
|
if (!result)
|
|
goto cuddZddSymmSiftingConvAuxOutOfMem;
|
|
}
|
|
|
|
while (move_down != NULL) {
|
|
move = move_down->next;
|
|
cuddDeallocMove(table, move_down);
|
|
move_down = move;
|
|
}
|
|
while (move_up != NULL) {
|
|
move = move_up->next;
|
|
cuddDeallocMove(table, move_up);
|
|
move_up = move;
|
|
}
|
|
|
|
return(1);
|
|
|
|
cuddZddSymmSiftingConvAuxOutOfMem:
|
|
if (move_down != ZDD_MV_OOM) {
|
|
while (move_down != NULL) {
|
|
move = move_down->next;
|
|
cuddDeallocMove(table, move_down);
|
|
move_down = move;
|
|
}
|
|
}
|
|
if (move_up != ZDD_MV_OOM) {
|
|
while (move_up != NULL) {
|
|
move = move_up->next;
|
|
cuddDeallocMove(table, move_up);
|
|
move_up = move;
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
|
|
} /* end of cuddZddSymmSiftingConvAux */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Moves x up until either it reaches the bound (x_low) or
|
|
the size of the ZDD heap increases too much.]
|
|
|
|
Description [Moves x up until either it reaches the bound (x_low) or
|
|
the size of the ZDD heap increases too much. Assumes that x is the top
|
|
of a symmetry group. Checks x for symmetry to the adjacent
|
|
variables. If symmetry is found, the symmetry group of x is merged
|
|
with the symmetry group of the other variable. Returns the set of
|
|
moves in case of success; ZDD_MV_OOM if memory is full.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static Move *
|
|
cuddZddSymmSifting_up(
|
|
DdManager * table,
|
|
int x,
|
|
int x_low,
|
|
int initial_size)
|
|
{
|
|
Move *moves;
|
|
Move *move;
|
|
int y;
|
|
int size;
|
|
int limit_size = initial_size;
|
|
int i, gytop;
|
|
|
|
moves = NULL;
|
|
y = cuddZddNextLow(table, x);
|
|
while (y >= x_low) {
|
|
gytop = table->subtableZ[y].next;
|
|
if (cuddZddSymmCheck(table, y, x)) {
|
|
/* Symmetry found, attach symm groups */
|
|
table->subtableZ[y].next = x;
|
|
i = table->subtableZ[x].next;
|
|
while (table->subtableZ[i].next != (unsigned) x)
|
|
i = table->subtableZ[i].next;
|
|
table->subtableZ[i].next = gytop;
|
|
}
|
|
else if ((table->subtableZ[x].next == (unsigned) x) &&
|
|
(table->subtableZ[y].next == (unsigned) y)) {
|
|
/* x and y have self symmetry */
|
|
size = cuddZddSwapInPlace(table, y, x);
|
|
if (size == 0)
|
|
goto cuddZddSymmSifting_upOutOfMem;
|
|
move = (Move *)cuddDynamicAllocNode(table);
|
|
if (move == NULL)
|
|
goto cuddZddSymmSifting_upOutOfMem;
|
|
move->x = y;
|
|
move->y = x;
|
|
move->size = size;
|
|
move->next = moves;
|
|
moves = move;
|
|
if ((double)size >
|
|
(double)limit_size * table->maxGrowth)
|
|
return(moves);
|
|
if (size < limit_size)
|
|
limit_size = size;
|
|
}
|
|
else { /* Group move */
|
|
size = zdd_group_move(table, y, x, &moves);
|
|
if ((double)size >
|
|
(double)limit_size * table->maxGrowth)
|
|
return(moves);
|
|
if (size < limit_size)
|
|
limit_size = size;
|
|
}
|
|
x = gytop;
|
|
y = cuddZddNextLow(table, x);
|
|
}
|
|
|
|
return(moves);
|
|
|
|
cuddZddSymmSifting_upOutOfMem:
|
|
while (moves != NULL) {
|
|
move = moves->next;
|
|
cuddDeallocMove(table, moves);
|
|
moves = move;
|
|
}
|
|
return(ZDD_MV_OOM);
|
|
|
|
} /* end of cuddZddSymmSifting_up */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Moves x down until either it reaches the bound (x_high) or
|
|
the size of the ZDD heap increases too much.]
|
|
|
|
Description [Moves x down until either it reaches the bound (x_high)
|
|
or the size of the ZDD heap increases too much. Assumes that x is the
|
|
bottom of a symmetry group. Checks x for symmetry to the adjacent
|
|
variables. If symmetry is found, the symmetry group of x is merged
|
|
with the symmetry group of the other variable. Returns the set of
|
|
moves in case of success; ZDD_MV_OOM if memory is full.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static Move *
|
|
cuddZddSymmSifting_down(
|
|
DdManager * table,
|
|
int x,
|
|
int x_high,
|
|
int initial_size)
|
|
{
|
|
Move *moves;
|
|
Move *move;
|
|
int y;
|
|
int size;
|
|
int limit_size = initial_size;
|
|
int i, gxtop, gybot;
|
|
|
|
moves = NULL;
|
|
y = cuddZddNextHigh(table, x);
|
|
while (y <= x_high) {
|
|
gybot = table->subtableZ[y].next;
|
|
while (table->subtableZ[gybot].next != (unsigned) y)
|
|
gybot = table->subtableZ[gybot].next;
|
|
if (cuddZddSymmCheck(table, x, y)) {
|
|
/* Symmetry found, attach symm groups */
|
|
gxtop = table->subtableZ[x].next;
|
|
table->subtableZ[x].next = y;
|
|
i = table->subtableZ[y].next;
|
|
while (table->subtableZ[i].next != (unsigned) y)
|
|
i = table->subtableZ[i].next;
|
|
table->subtableZ[i].next = gxtop;
|
|
}
|
|
else if ((table->subtableZ[x].next == (unsigned) x) &&
|
|
(table->subtableZ[y].next == (unsigned) y)) {
|
|
/* x and y have self symmetry */
|
|
size = cuddZddSwapInPlace(table, x, y);
|
|
if (size == 0)
|
|
goto cuddZddSymmSifting_downOutOfMem;
|
|
move = (Move *)cuddDynamicAllocNode(table);
|
|
if (move == NULL)
|
|
goto cuddZddSymmSifting_downOutOfMem;
|
|
move->x = x;
|
|
move->y = y;
|
|
move->size = size;
|
|
move->next = moves;
|
|
moves = move;
|
|
if ((double)size >
|
|
(double)limit_size * table->maxGrowth)
|
|
return(moves);
|
|
if (size < limit_size)
|
|
limit_size = size;
|
|
x = y;
|
|
y = cuddZddNextHigh(table, x);
|
|
}
|
|
else { /* Group move */
|
|
size = zdd_group_move(table, x, y, &moves);
|
|
if ((double)size >
|
|
(double)limit_size * table->maxGrowth)
|
|
return(moves);
|
|
if (size < limit_size)
|
|
limit_size = size;
|
|
}
|
|
x = gybot;
|
|
y = cuddZddNextHigh(table, x);
|
|
}
|
|
|
|
return(moves);
|
|
|
|
cuddZddSymmSifting_downOutOfMem:
|
|
while (moves != NULL) {
|
|
move = moves->next;
|
|
cuddDeallocMove(table, moves);
|
|
moves = move;
|
|
}
|
|
return(ZDD_MV_OOM);
|
|
|
|
} /* end of cuddZddSymmSifting_down */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Given a set of moves, returns the ZDD heap to the position
|
|
giving the minimum size.]
|
|
|
|
Description [Given a set of moves, returns the ZDD heap to the
|
|
position giving the minimum size. In case of ties, returns to the
|
|
closest position giving the minimum size. Returns 1 in case of
|
|
success; 0 otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static int
|
|
cuddZddSymmSiftingBackward(
|
|
DdManager * table,
|
|
Move * moves,
|
|
int size)
|
|
{
|
|
int i;
|
|
int i_best;
|
|
Move *move;
|
|
int res;
|
|
|
|
i_best = -1;
|
|
for (move = moves, i = 0; move != NULL; move = move->next, i++) {
|
|
if (move->size < size) {
|
|
i_best = i;
|
|
size = move->size;
|
|
}
|
|
}
|
|
|
|
for (move = moves, i = 0; move != NULL; move = move->next, i++) {
|
|
if (i == i_best) break;
|
|
if ((table->subtableZ[move->x].next == move->x) &&
|
|
(table->subtableZ[move->y].next == move->y)) {
|
|
res = cuddZddSwapInPlace(table, move->x, move->y);
|
|
if (!res) return(0);
|
|
}
|
|
else { /* Group move necessary */
|
|
res = zdd_group_move_backward(table, move->x, move->y);
|
|
}
|
|
if (i_best == -1 && res == size)
|
|
break;
|
|
}
|
|
|
|
return(1);
|
|
|
|
} /* end of cuddZddSymmSiftingBackward */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Swaps two groups.]
|
|
|
|
Description [Swaps two groups. x is assumed to be the bottom variable
|
|
of the first group. y is assumed to be the top variable of the second
|
|
group. Updates the list of moves. Returns the number of keys in the
|
|
table if successful; 0 otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static int
|
|
zdd_group_move(
|
|
DdManager * table,
|
|
int x,
|
|
int y,
|
|
Move ** moves)
|
|
{
|
|
Move *move;
|
|
int size;
|
|
int i, temp, gxtop, gxbot, gybot, yprev;
|
|
int swapx, swapy;
|
|
|
|
#ifdef DD_DEBUG
|
|
assert(x < y); /* we assume that x < y */
|
|
#endif
|
|
/* Find top and bottom for the two groups. */
|
|
gxtop = table->subtableZ[x].next;
|
|
gxbot = x;
|
|
gybot = table->subtableZ[y].next;
|
|
while (table->subtableZ[gybot].next != (unsigned) y)
|
|
gybot = table->subtableZ[gybot].next;
|
|
yprev = gybot;
|
|
|
|
while (x <= y) {
|
|
while (y > gxtop) {
|
|
/* Set correct symmetries. */
|
|
temp = table->subtableZ[x].next;
|
|
if (temp == x)
|
|
temp = y;
|
|
i = gxtop;
|
|
for (;;) {
|
|
if (table->subtableZ[i].next == (unsigned) x) {
|
|
table->subtableZ[i].next = y;
|
|
break;
|
|
} else {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
}
|
|
if (table->subtableZ[y].next != (unsigned) y) {
|
|
table->subtableZ[x].next = table->subtableZ[y].next;
|
|
} else {
|
|
table->subtableZ[x].next = x;
|
|
}
|
|
|
|
if (yprev != y) {
|
|
table->subtableZ[yprev].next = x;
|
|
} else {
|
|
yprev = x;
|
|
}
|
|
table->subtableZ[y].next = temp;
|
|
|
|
size = cuddZddSwapInPlace(table, x, y);
|
|
if (size == 0)
|
|
goto zdd_group_moveOutOfMem;
|
|
swapx = x;
|
|
swapy = y;
|
|
y = x;
|
|
x--;
|
|
} /* while y > gxtop */
|
|
|
|
/* Trying to find the next y. */
|
|
if (table->subtableZ[y].next <= (unsigned) y) {
|
|
gybot = y;
|
|
} else {
|
|
y = table->subtableZ[y].next;
|
|
}
|
|
|
|
yprev = gxtop;
|
|
gxtop++;
|
|
gxbot++;
|
|
x = gxbot;
|
|
} /* while x <= y, end of group movement */
|
|
move = (Move *)cuddDynamicAllocNode(table);
|
|
if (move == NULL)
|
|
goto zdd_group_moveOutOfMem;
|
|
move->x = swapx;
|
|
move->y = swapy;
|
|
move->size = table->keysZ;
|
|
move->next = *moves;
|
|
*moves = move;
|
|
|
|
return(table->keysZ);
|
|
|
|
zdd_group_moveOutOfMem:
|
|
while (*moves != NULL) {
|
|
move = (*moves)->next;
|
|
cuddDeallocMove(table, *moves);
|
|
*moves = move;
|
|
}
|
|
return(0);
|
|
|
|
} /* end of zdd_group_move */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Undoes the swap of two groups.]
|
|
|
|
Description [Undoes the swap of two groups. x is assumed to be the
|
|
bottom variable of the first group. y is assumed to be the top
|
|
variable of the second group. Returns 1 if successful; 0 otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso []
|
|
|
|
******************************************************************************/
|
|
static int
|
|
zdd_group_move_backward(
|
|
DdManager * table,
|
|
int x,
|
|
int y)
|
|
{
|
|
int size;
|
|
int i, temp, gxtop, gxbot, gybot, yprev;
|
|
|
|
#ifdef DD_DEBUG
|
|
assert(x < y); /* we assume that x < y */
|
|
#endif
|
|
/* Find top and bottom of the two groups. */
|
|
gxtop = table->subtableZ[x].next;
|
|
gxbot = x;
|
|
gybot = table->subtableZ[y].next;
|
|
while (table->subtableZ[gybot].next != (unsigned) y)
|
|
gybot = table->subtableZ[gybot].next;
|
|
yprev = gybot;
|
|
|
|
while (x <= y) {
|
|
while (y > gxtop) {
|
|
/* Set correct symmetries. */
|
|
temp = table->subtableZ[x].next;
|
|
if (temp == x)
|
|
temp = y;
|
|
i = gxtop;
|
|
for (;;) {
|
|
if (table->subtableZ[i].next == (unsigned) x) {
|
|
table->subtableZ[i].next = y;
|
|
break;
|
|
} else {
|
|
i = table->subtableZ[i].next;
|
|
}
|
|
}
|
|
if (table->subtableZ[y].next != (unsigned) y) {
|
|
table->subtableZ[x].next = table->subtableZ[y].next;
|
|
} else {
|
|
table->subtableZ[x].next = x;
|
|
}
|
|
|
|
if (yprev != y) {
|
|
table->subtableZ[yprev].next = x;
|
|
} else {
|
|
yprev = x;
|
|
}
|
|
table->subtableZ[y].next = temp;
|
|
|
|
size = cuddZddSwapInPlace(table, x, y);
|
|
if (size == 0)
|
|
return(0);
|
|
y = x;
|
|
x--;
|
|
} /* while y > gxtop */
|
|
|
|
/* Trying to find the next y. */
|
|
if (table->subtableZ[y].next <= (unsigned) y) {
|
|
gybot = y;
|
|
} else {
|
|
y = table->subtableZ[y].next;
|
|
}
|
|
|
|
yprev = gxtop;
|
|
gxtop++;
|
|
gxbot++;
|
|
x = gxbot;
|
|
} /* while x <= y, end of group movement backward */
|
|
|
|
return(size);
|
|
|
|
} /* end of zdd_group_move_backward */
|
|
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Counts numbers of symmetric variables and symmetry
|
|
groups.]
|
|
|
|
Description []
|
|
|
|
SideEffects [None]
|
|
|
|
******************************************************************************/
|
|
static void
|
|
cuddZddSymmSummary(
|
|
DdManager * table,
|
|
int lower,
|
|
int upper,
|
|
int * symvars,
|
|
int * symgroups)
|
|
{
|
|
int i,x,gbot;
|
|
int TotalSymm = 0;
|
|
int TotalSymmGroups = 0;
|
|
|
|
for (i = lower; i <= upper; i++) {
|
|
if (table->subtableZ[i].next != (unsigned) i) {
|
|
TotalSymmGroups++;
|
|
x = i;
|
|
do {
|
|
TotalSymm++;
|
|
gbot = x;
|
|
x = table->subtableZ[x].next;
|
|
} while (x != i);
|
|
#ifdef DD_DEBUG
|
|
assert(table->subtableZ[gbot].next == (unsigned) i);
|
|
#endif
|
|
i = gbot;
|
|
}
|
|
}
|
|
*symvars = TotalSymm;
|
|
*symgroups = TotalSymmGroups;
|
|
|
|
return;
|
|
|
|
} /* end of cuddZddSymmSummary */
|
|
|