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.
		
		
		
		
		
			
		
			
				
					
					
						
							1670 lines
						
					
					
						
							46 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							1670 lines
						
					
					
						
							46 KiB
						
					
					
				| /** | |
|   @file | |
|  | |
|   @ingroup cudd | |
|  | |
|   @brief Functions for symmetry-based variable reordering. | |
|  | |
|   @author Shipra Panda, Fabio Somenzi | |
|  | |
|   @copyright@parblock | |
|   Copyright (c) 1995-2015, Regents of the University of Colorado | |
|  | |
|   All rights reserved. | |
|  | |
|   Redistribution and use in source and binary forms, with or without | |
|   modification, are permitted provided that the following conditions | |
|   are met: | |
|  | |
|   Redistributions of source code must retain the above copyright | |
|   notice, this list of conditions and the following disclaimer. | |
|  | |
|   Redistributions in binary form must reproduce the above copyright | |
|   notice, this list of conditions and the following disclaimer in the | |
|   documentation and/or other materials provided with the distribution. | |
|  | |
|   Neither the name of the University of Colorado nor the names of its | |
|   contributors may be used to endorse or promote products derived from | |
|   this software without specific prior written permission. | |
|  | |
|   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
|   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
|   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
|   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
|   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
|   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
|   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
|   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
|   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
|   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
|   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
|   POSSIBILITY OF SUCH DAMAGE. | |
|   @endparblock | |
|  | |
| */ | |
| 
 | |
| #include "util.h" | |
| #include "cuddInt.h" | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Constant declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| #define MV_OOM (Move *)1 | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Stucture declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Type declarations                                                         */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Variable declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Macro declarations                                                        */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /** \cond */ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Static function prototypes                                                */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| static int ddSymmUniqueCompare (void const *ptrX, void const *ptrY); | |
| static int ddSymmSiftingAux (DdManager *table, int x, int xLow, int xHigh); | |
| static int ddSymmSiftingConvAux (DdManager *table, int x, int xLow, int xHigh); | |
| static Move * ddSymmSiftingUp (DdManager *table, int y, int xLow); | |
| static Move * ddSymmSiftingDown (DdManager *table, int x, int xHigh); | |
| static int ddSymmGroupMove (DdManager *table, int x, int y, Move **moves); | |
| static int ddSymmGroupMoveBackward (DdManager *table, int x, int y); | |
| static int ddSymmSiftingBackward (DdManager *table, Move *moves, int size); | |
| static void ddSymmSummary (DdManager *table, int lower, int upper, int *symvars, int *symgroups); | |
| 
 | |
| /** \endcond */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of exported functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Prints statistics on symmetric variables. | |
|  | |
|   @details The information is accurate only if this function is called | |
|   right after reordering with methods CUDD_REORDER_SYMM_SIFT or | |
|   CUDD_REORDER_SYMM_SIFT_CONV. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| void | |
| Cudd_SymmProfile( | |
|   DdManager * table, | |
|   int  lower, | |
|   int  upper) | |
| { | |
|     int i,x,gbot; | |
|     int TotalSymm = 0; | |
|     int TotalSymmGroups = 0; | |
| 
 | |
|     for (i = lower; i <= upper; i++) { | |
| 	if (table->subtables[i].next != (unsigned) i) { | |
| 	    x = i; | |
| 	    (void) fprintf(table->out,"Group:"); | |
| 	    do { | |
| 		(void) fprintf(table->out,"  %d",table->invperm[x]); | |
| 		TotalSymm++; | |
| 		gbot = x; | |
| 		x = table->subtables[x].next; | |
| 	    } while (x != i); | |
| 	    TotalSymmGroups++; | |
| #ifdef DD_DEBUG | |
| 	    assert(table->subtables[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_SymmProfile */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of internal functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Checks for symmetry of x and y. | |
|  | |
|   @details Ignores projection functions, unless they are isolated. | |
|  | |
|   @return 1 in case of symmetry; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| int | |
| cuddSymmCheck( | |
|   DdManager * table, | |
|   int  x, | |
|   int  y) | |
| { | |
|     DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10; | |
|     int comple;		/* f0 is complemented */ | |
|     int xsymmy;		/* x and y may be positively symmetric */ | |
|     int xsymmyp;	/* x and y may be negatively symmetric */ | |
|     int arccount;	/* number of arcs from layer x to layer y */ | |
|     int TotalRefCount;	/* total reference count of layer y minus 1 */ | |
|     int yindex; | |
|     int i; | |
|     DdNodePtr *list; | |
|     int slots; | |
|     DdNode *sentinel = &(table->sentinel); | |
| #ifdef DD_DEBUG | |
|     int xindex; | |
| #endif | |
|  | |
|     /* Checks that x and y are not the projection functions. | |
|     ** For x it is sufficient to check whether there is only one | |
|     ** node; indeed, if there is one node, it is the projection function | |
|     ** and it cannot point to y. Hence, if y isn't just the projection | |
|     ** function, it has one arc coming from a layer different from x. | |
|     */ | |
|     if (table->subtables[x].keys == 1) { | |
| 	return(0); | |
|     } | |
|     yindex = table->invperm[y]; | |
|     if (table->subtables[y].keys == 1) { | |
| 	if (table->vars[yindex]->ref == 1) | |
| 	    return(0); | |
|     } | |
| 
 | |
|     xsymmy = xsymmyp = 1; | |
|     arccount = 0; | |
|     slots = table->subtables[x].slots; | |
|     list = table->subtables[x].nodelist; | |
|     for (i = 0; i < slots; i++) { | |
| 	f = list[i]; | |
| 	while (f != sentinel) { | |
| 	    /* Find f1, f0, f11, f10, f01, f00. */ | |
| 	    f1 = cuddT(f); | |
| 	    f0 = Cudd_Regular(cuddE(f)); | |
| 	    comple = Cudd_IsComplement(cuddE(f)); | |
| 	    if ((int) f1->index == yindex) { | |
| 		arccount++; | |
| 		f11 = cuddT(f1); f10 = cuddE(f1); | |
| 	    } else { | |
| 		if ((int) f0->index != yindex) { | |
| 		    /* If f is an isolated projection function it is | |
| 		    ** allowed to bypass layer y. | |
| 		    */ | |
| 		    if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1) | |
| 			return(0); /* f bypasses layer y */ | |
| 		} | |
| 		f11 = f10 = f1; | |
| 	    } | |
| 	    if ((int) f0->index == yindex) { | |
| 		arccount++; | |
| 		f01 = cuddT(f0); f00 = cuddE(f0); | |
| 	    } else { | |
| 		f01 = f00 = f0; | |
| 	    } | |
| 	    if (comple) { | |
| 		f01 = Cudd_Not(f01); | |
| 		f00 = Cudd_Not(f00); | |
| 	    } | |
| 
 | |
| 	    if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1) { | |
| 		xsymmy &= f01 == f10; | |
| 		xsymmyp &= f11 == f00; | |
| 		if ((xsymmy == 0) && (xsymmyp == 0)) | |
| 		    return(0); | |
| 	    } | |
| 
 | |
| 	    f = f->next; | |
| 	} /* while */ | |
|     } /* for */ | |
| 
 | |
|     /* Calculate the total reference counts of y */ | |
|     TotalRefCount = -1; /* -1 for projection function */ | |
|     slots = table->subtables[y].slots; | |
|     list = table->subtables[y].nodelist; | |
|     for (i = 0; i < slots; i++) { | |
| 	f = list[i]; | |
| 	while (f != sentinel) { | |
| 	    TotalRefCount += f->ref; | |
| 	    f = f->next; | |
| 	} | |
|     } | |
| 
 | |
| #if defined(DD_DEBUG) && defined(DD_VERBOSE) | |
|     if (arccount == TotalRefCount) { | |
| 	xindex = table->invperm[x]; | |
| 	(void) fprintf(table->out, | |
| 		       "Found symmetry! x =%d\ty = %d\tPos(%d,%d)\n", | |
| 		       xindex,yindex,x,y); | |
|     } | |
| #endif | |
|  | |
|     return(arccount == TotalRefCount); | |
| 
 | |
| } /* end of cuddSymmCheck */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Symmetric sifting algorithm. | |
|  | |
|   @details 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 DD heap and grouping variables that are symmetric. | |
|     <li> Select the best permutation. | |
|     <li> Repeat 3 and 4 for all variables. | |
|     </ol> | |
|  | |
|   @return 1 plus the number of symmetric variables if successful; 0 | |
|   otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see cuddSymmSiftingConv | |
|  | |
| */ | |
| int | |
| cuddSymmSifting( | |
|   DdManager * table, | |
|   int  lower, | |
|   int  upper) | |
| { | |
|     int		i; | |
|     IndexKey	*var; | |
|     int		size; | |
|     int		x; | |
|     int		result; | |
|     int		symvars; | |
|     int		symgroups; | |
| #ifdef DD_STATS | |
|     int		previousSize; | |
| #endif | |
|  | |
|     size = table->size; | |
| 
 | |
|     /* Find order in which to sift variables. */ | |
|     var = ALLOC(IndexKey,size); | |
|     if (var == NULL) { | |
| 	table->errorCode = CUDD_MEMORY_OUT; | |
| 	goto ddSymmSiftingOutOfMem; | |
|     } | |
| 
 | |
|     for (i = 0; i < size; i++) { | |
| 	x = table->perm[i]; | |
| 	var[i].index = i; | |
| 	var[i].keys = table->subtables[x].keys; | |
|     } | |
| 
 | |
|     util_qsort(var,size,sizeof(IndexKey),ddSymmUniqueCompare); | |
| 
 | |
|     /* Initialize the symmetry of each subtable to itself. */ | |
|     for (i = lower; i <= upper; i++) { | |
| 	table->subtables[i].next = i; | |
|     } | |
| 
 | |
|     for (i = 0; i < ddMin(table->siftMaxVar,size); i++) { | |
| 	if (table->ddTotalNumberSwapping >= table->siftMaxSwap) | |
| 	    break; | |
|         if (util_cpu_time() - table->startTime > table->timeLimit) { | |
|             table->autoDyn = 0; /* prevent further reordering */ | |
|             break; | |
|         } | |
|         if (table->terminationCallback != NULL && | |
|             table->terminationCallback(table->tcbArg)) { | |
|             table->autoDyn = 0; /* prevent further reordering */ | |
|             break; | |
|         } | |
| 	x = table->perm[var[i].index]; | |
| #ifdef DD_STATS | |
| 	previousSize = (int) (table->keys - table->isolated); | |
| #endif | |
| 	if (x < lower || x > upper) continue; | |
| 	if (table->subtables[x].next == (unsigned) x) { | |
| 	    result = ddSymmSiftingAux(table,x,lower,upper); | |
| 	    if (!result) goto ddSymmSiftingOutOfMem; | |
| #ifdef DD_STATS | |
| 	    if (table->keys < (unsigned) previousSize + table->isolated) { | |
| 		(void) fprintf(table->out,"-"); | |
| 	    } else if (table->keys > (unsigned) previousSize + | |
| 		       table->isolated) { | |
| 		(void) fprintf(table->out,"+"); /* should never happen */ | |
| 	    } else { | |
| 		(void) fprintf(table->out,"="); | |
| 	    } | |
| 	    fflush(table->out); | |
| #endif | |
| 	} | |
|     } | |
| 
 | |
|     FREE(var); | |
| 
 | |
|     ddSymmSummary(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", | |
| 		   symgroups); | |
| #endif | |
|  | |
|     return(1+symvars); | |
| 
 | |
| ddSymmSiftingOutOfMem: | |
| 
 | |
|     if (var != NULL) FREE(var); | |
| 
 | |
|     return(0); | |
| 
 | |
| } /* end of cuddSymmSifting */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Symmetric sifting to convergence algorithm. | |
|  | |
|   @details 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 %DD 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> | |
|  | |
|   @return 1 plus the number of symmetric variables if successful; 0 | |
|   otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see cuddSymmSifting | |
|  | |
| */ | |
| int | |
| cuddSymmSiftingConv( | |
|   DdManager * table, | |
|   int  lower, | |
|   int  upper) | |
| { | |
|     int		i; | |
|     IndexKey	*var; | |
|     int		size; | |
|     int		x; | |
|     int		result; | |
|     int		symvars; | |
|     int		symgroups; | |
|     int		classes; | |
|     int		initialSize; | |
| #ifdef DD_STATS | |
|     int		previousSize; | |
| #endif | |
|  | |
|     initialSize = (int) (table->keys - table->isolated); | |
| 
 | |
|     size = table->size; | |
| 
 | |
|     /* Find order in which to sift variables. */ | |
|     var = ALLOC(IndexKey,size); | |
|     if (var == NULL) { | |
| 	table->errorCode = CUDD_MEMORY_OUT; | |
| 	goto ddSymmSiftingConvOutOfMem; | |
|     } | |
| 
 | |
|     for (i = 0; i < size; i++) { | |
| 	x = table->perm[i]; | |
| 	var[i].index = i; | |
| 	var[i].keys = table->subtables[x].keys; | |
|     } | |
| 
 | |
|     util_qsort(var,size,sizeof(IndexKey),ddSymmUniqueCompare); | |
| 
 | |
|     /* Initialize the symmetry of each subtable to itself | |
|     ** for first pass of converging symmetric sifting. | |
|     */ | |
|     for (i = lower; i <= upper; i++) { | |
| 	table->subtables[i].next = i; | |
|     } | |
| 
 | |
|     for (i = 0; i < ddMin(table->siftMaxVar, table->size); i++) { | |
| 	if (table->ddTotalNumberSwapping >= table->siftMaxSwap) | |
| 	    break; | |
|         if (util_cpu_time() - table->startTime > table->timeLimit) { | |
|             table->autoDyn = 0; /* prevent further reordering */ | |
|             break; | |
|         } | |
|         if (table->terminationCallback != NULL && | |
|             table->terminationCallback(table->tcbArg)) { | |
|             table->autoDyn = 0; /* prevent further reordering */ | |
|             break; | |
|         } | |
| 	x = table->perm[var[i].index]; | |
| 	if (x < lower || x > upper) continue; | |
| 	/* Only sift if not in symmetry group already. */ | |
| 	if (table->subtables[x].next == (unsigned) x) { | |
| #ifdef DD_STATS | |
|           previousSize = (int) (table->keys - table->isolated); | |
| #endif | |
| 	    result = ddSymmSiftingAux(table,x,lower,upper); | |
| 	    if (!result) goto ddSymmSiftingConvOutOfMem; | |
| #ifdef DD_STATS | |
| 	    if (table->keys < (unsigned) previousSize + table->isolated) { | |
| 		(void) fprintf(table->out,"-"); | |
| 	    } else if (table->keys > (unsigned) previousSize + | |
| 		       table->isolated) { | |
| 		(void) fprintf(table->out,"+"); | |
| 	    } else { | |
| 		(void) fprintf(table->out,"="); | |
| 	    } | |
| 	    fflush(table->out); | |
| #endif | |
| 	} | |
|     } | |
| 
 | |
|     /* Sifting now until convergence. */ | |
|     while ((unsigned) initialSize > table->keys - table->isolated) { | |
|       initialSize = (int) (table->keys - table->isolated); | |
| #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->subtables[x].next) { | |
| 		x = table->subtables[x].next; | |
| 	    } | |
| 	    /* Here x is the largest index in a group. | |
| 	    ** Groups consist of adjacent variables. | |
| 	    ** Hence, the next increment of x will move it to a new group. | |
| 	    */ | |
| 	    i = table->invperm[x]; | |
| 	    var[classes].keys = table->subtables[x].keys; | |
| 	    var[classes].index = i; | |
| 	} | |
| 
 | |
| 	util_qsort(var,classes,sizeof(IndexKey),ddSymmUniqueCompare); | |
| 
 | |
| 	/* Now sift. */ | |
| 	for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) { | |
| 	    if (table->ddTotalNumberSwapping >= table->siftMaxSwap) | |
| 		break; | |
|             if (util_cpu_time() - table->startTime > table->timeLimit) { | |
|               table->autoDyn = 0; /* prevent further reordering */ | |
|               break; | |
|             } | |
|             if (table->terminationCallback != NULL && | |
|                 table->terminationCallback(table->tcbArg)) { | |
|                 table->autoDyn = 0; /* prevent further reordering */ | |
|                 break; | |
|             } | |
| 	    x = table->perm[var[i].index]; | |
| 	    if ((unsigned) x >= table->subtables[x].next) { | |
| #ifdef DD_STATS | |
|               previousSize = (int) (table->keys - table->isolated); | |
| #endif | |
| 		result = ddSymmSiftingConvAux(table,x,lower,upper); | |
| 		if (!result ) goto ddSymmSiftingConvOutOfMem; | |
| #ifdef DD_STATS | |
| 		if (table->keys < (unsigned) previousSize + table->isolated) { | |
| 		    (void) fprintf(table->out,"-"); | |
| 		} else if (table->keys > (unsigned) previousSize + | |
| 			   table->isolated) { | |
| 		    (void) fprintf(table->out,"+"); | |
| 		} else { | |
| 		    (void) fprintf(table->out,"="); | |
| 		} | |
| 		fflush(table->out); | |
| #endif | |
| 	    } | |
| 	} /* for */ | |
|     } | |
| 
 | |
|     ddSymmSummary(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", | |
| 		   symgroups); | |
| #endif | |
|  | |
|     FREE(var); | |
| 
 | |
|     return(1+symvars); | |
| 
 | |
| ddSymmSiftingConvOutOfMem: | |
| 
 | |
|     if (var != NULL) FREE(var); | |
| 
 | |
|     return(0); | |
| 
 | |
| } /* end of cuddSymmSiftingConv */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of static functions                                            */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Comparison function used by qsort. | |
|  | |
|   @details Used to order the variables according to the number of keys | |
|   in the subtables. | |
|  | |
|   @return the difference in number of keys between the two variables | |
|   being compared. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| ddSymmUniqueCompare( | |
|   void const * ptrX, | |
|   void const * ptrY) | |
| { | |
|     IndexKey const * pX = (IndexKey const *) ptrX; | |
|     IndexKey const * pY = (IndexKey const *) ptrY; | |
| #if 0 | |
|     if (pY->keys == pX->keys) { | |
| 	return(pX->index - pY->index); | |
|     } | |
| #endif | |
|     return(pY->keys - pX->keys); | |
| 
 | |
| } /* end of ddSymmUniqueCompare */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Given xLow <= x <= xHigh moves x up and down between the | |
|   boundaries. | |
|  | |
|   @details Finds the best position and does the required changes. | |
|   Assumes that x is not part of a symmetry group. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| ddSymmSiftingAux( | |
|   DdManager * table, | |
|   int  x, | |
|   int  xLow, | |
|   int  xHigh) | |
| { | |
|     Move *move; | |
|     Move *moveUp;	/* list of up moves */ | |
|     Move *moveDown;	/* list of down moves */ | |
|     int	 initialSize; | |
|     int	 result; | |
|     int  i; | |
|     int  topbot;	/* index to either top or bottom of symmetry group */ | |
|     int  initGroupSize, finalGroupSize; | |
| 
 | |
| 
 | |
| #ifdef DD_DEBUG | |
|     /* check for previously detected symmetry */ | |
|     assert(table->subtables[x].next == (unsigned) x); | |
| #endif | |
|  | |
|     initialSize = (int) (table->keys - table->isolated); | |
| 
 | |
|     moveDown = NULL; | |
|     moveUp = NULL; | |
| 
 | |
|     if ((x - xLow) > (xHigh - x)) { | |
| 	/* Will go down first, unless x == xHigh: | |
| 	** Look for consecutive symmetries above x. | |
| 	*/ | |
| 	for (i = x; i > xLow; i--) { | |
| 	    if (!cuddSymmCheck(table,i-1,i)) | |
| 		break; | |
| 	    topbot = table->subtables[i-1].next; /* find top of i-1's group */ | |
| 	    table->subtables[i-1].next = i; | |
| 	    table->subtables[x].next = topbot; /* x is bottom of group so its */ | |
| 					       /* next is top of i-1's group */ | |
| 	    i = topbot + 1; /* add 1 for i--; new i is top of symm group */ | |
| 	} | |
|     } else { | |
| 	/* Will go up first unless x == xlow: | |
| 	** Look for consecutive symmetries below x. | |
| 	*/ | |
| 	for (i = x; i < xHigh; i++) { | |
| 	    if (!cuddSymmCheck(table,i,i+1)) | |
| 		break; | |
| 	    /* find bottom of i+1's symm group */ | |
| 	    topbot = i + 1; | |
| 	    while ((unsigned) topbot < table->subtables[topbot].next) { | |
| 		topbot = table->subtables[topbot].next; | |
| 	    } | |
| 	    table->subtables[topbot].next = table->subtables[i].next; | |
| 	    table->subtables[i].next = i + 1; | |
| 	    i = topbot - 1; /* subtract 1 for i++; new i is bottom of group */ | |
| 	} | |
|     } | |
| 
 | |
|     /* Now x may be in the middle of a symmetry group. | |
|     ** Find bottom of x's symm group. | |
|     */ | |
|     while ((unsigned) x < table->subtables[x].next) | |
| 	x = table->subtables[x].next; | |
| 
 | |
|     if (x == xLow) { /* Sift down */ | |
| 
 | |
| #ifdef DD_DEBUG | |
| 	/* x must be a singleton */ | |
| 	assert((unsigned) x == table->subtables[x].next); | |
| #endif | |
| 	if (x == xHigh) return(1);	/* just one variable */ | |
| 
 | |
| 	initGroupSize = 1; | |
| 
 | |
| 	moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	    /* after this point x --> xHigh, unless early term */ | |
| 	if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; | |
| 	if (moveDown == NULL) return(1); | |
| 
 | |
| 	x = moveDown->y; | |
| 	/* Find bottom of x's group */ | |
| 	i = x; | |
| 	while ((unsigned) i < table->subtables[i].next) { | |
| 	    i = table->subtables[i].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the top of the symmetry group and i the bottom */ | |
| 	assert((unsigned) i >= table->subtables[i].next); | |
| 	assert((unsigned) x == table->subtables[i].next); | |
| #endif | |
| 	finalGroupSize = i - x + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetry groups detected, return to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} else { | |
|           initialSize = (int) (table->keys - table->isolated); | |
| 	    moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} | |
| 	if (!result) goto ddSymmSiftingAuxOutOfMem; | |
| 
 | |
|     } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ | |
| 	/* Find top of x's symm group */ | |
| 	i = x;				/* bottom */ | |
| 	x = table->subtables[x].next;	/* top */ | |
| 
 | |
| 	if (x == xLow) return(1); /* just one big group */ | |
| 
 | |
| 	initGroupSize = i - x + 1; | |
| 
 | |
| 	moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	    /* after this point x --> xLow, unless early term */ | |
| 	if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; | |
| 	if (moveUp == NULL) return(1); | |
| 
 | |
| 	x = moveUp->x; | |
| 	/* Find top of x's group */ | |
| 	i = table->subtables[x].next; | |
| #ifdef DD_DEBUG | |
| 	/* x should be the bottom of the symmetry group and i the top */ | |
| 	assert((unsigned) x >= table->subtables[x].next); | |
| 	assert((unsigned) i == table->subtables[x].next); | |
| #endif | |
| 	finalGroupSize = x - i + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetry groups detected, return to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} else { | |
|           initialSize = (int) (table->keys - table->isolated); | |
| 	    moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} | |
| 	if (!result) goto ddSymmSiftingAuxOutOfMem; | |
| 
 | |
|     } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ | |
| 
 | |
| 	moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	/* at this point x == xHigh, unless early term */ | |
| 	if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; | |
| 
 | |
| 	if (moveDown != NULL) { | |
| 	    x = moveDown->y;	/* x is top here */ | |
| 	    i = x; | |
| 	    while ((unsigned) i < table->subtables[i].next) { | |
| 		i = table->subtables[i].next; | |
| 	    } | |
| 	} else { | |
| 	    i = x; | |
| 	    while ((unsigned) i < table->subtables[i].next) { | |
| 		i = table->subtables[i].next; | |
| 	    } | |
| 	    x = table->subtables[i].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the top of the symmetry group and i the bottom */ | |
| 	assert((unsigned) i >= table->subtables[i].next); | |
| 	assert((unsigned) x == table->subtables[i].next); | |
| #endif | |
| 	initGroupSize = i - x + 1; | |
| 
 | |
| 	moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; | |
| 
 | |
| 	if (moveUp != NULL) { | |
| 	    x = moveUp->x; | |
| 	    i = table->subtables[x].next; | |
| 	} else { | |
| 	    i = x; | |
| 	    while ((unsigned) x < table->subtables[x].next) | |
| 		x = table->subtables[x].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the bottom of the symmetry group and i the top */ | |
| 	assert((unsigned) x >= table->subtables[x].next); | |
| 	assert((unsigned) i == table->subtables[x].next); | |
| #endif | |
| 	finalGroupSize = x - i + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetry groups detected, return to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} else { | |
| 	    while (moveDown != NULL) { | |
| 		move = moveDown->next; | |
| 		cuddDeallocMove(table, moveDown); | |
| 		moveDown = move; | |
| 	    } | |
| 	    initialSize = (int) (table->keys - table->isolated); | |
| 	    moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} | |
| 	if (!result) goto ddSymmSiftingAuxOutOfMem; | |
| 
 | |
|     } else { /* moving up first: shorter */ | |
| 	/* Find top of x's symmetry group */ | |
| 	x = table->subtables[x].next; | |
| 
 | |
| 	moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	/* at this point x == xHigh, unless early term */ | |
| 	if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; | |
| 
 | |
| 	if (moveUp != NULL) { | |
| 	    x = moveUp->x; | |
| 	    i = table->subtables[x].next; | |
| 	} else { | |
| 	    while ((unsigned) x < table->subtables[x].next) | |
| 		x = table->subtables[x].next; | |
| 	    i = table->subtables[x].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x is bottom of the symmetry group and i is top */ | |
| 	assert((unsigned) x >= table->subtables[x].next); | |
| 	assert((unsigned) i == table->subtables[x].next); | |
| #endif | |
| 	initGroupSize = x - i + 1; | |
| 
 | |
| 	moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; | |
| 
 | |
| 	if (moveDown != NULL) { | |
| 	    x = moveDown->y; | |
| 	    i = x; | |
| 	    while ((unsigned) i < table->subtables[i].next) { | |
| 		i = table->subtables[i].next; | |
| 	    } | |
| 	} else { | |
| 	    i = x; | |
| 	    x = table->subtables[x].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the top of the symmetry group and i the bottom */ | |
| 	assert((unsigned) i >= table->subtables[i].next); | |
| 	assert((unsigned) x == table->subtables[i].next); | |
| #endif | |
| 	finalGroupSize = i - x + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetries detected, go back to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} else { | |
| 	    while (moveUp != NULL) { | |
| 		move = moveUp->next; | |
| 		cuddDeallocMove(table, moveUp); | |
| 		moveUp = move; | |
| 	    } | |
| 	    initialSize = (int) (table->keys - table->isolated); | |
| 	    moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} | |
| 	if (!result) goto ddSymmSiftingAuxOutOfMem; | |
|     } | |
| 
 | |
|     while (moveDown != NULL) { | |
| 	move = moveDown->next; | |
| 	cuddDeallocMove(table, moveDown); | |
| 	moveDown = move; | |
|     } | |
|     while (moveUp != NULL) { | |
| 	move = moveUp->next; | |
| 	cuddDeallocMove(table, moveUp); | |
| 	moveUp = move; | |
|     } | |
| 
 | |
|     return(1); | |
| 
 | |
| ddSymmSiftingAuxOutOfMem: | |
|     if (moveDown != MV_OOM) { | |
| 	while (moveDown != NULL) { | |
| 	    move = moveDown->next; | |
| 	    cuddDeallocMove(table, moveDown); | |
| 	    moveDown = move; | |
| 	} | |
|     } | |
|     if (moveUp != MV_OOM) { | |
| 	while (moveUp != NULL) { | |
| 	    move = moveUp->next; | |
| 	    cuddDeallocMove(table, moveUp); | |
| 	    moveUp = move; | |
| 	} | |
|     } | |
| 
 | |
|     return(0); | |
| 
 | |
| } /* end of ddSymmSiftingAux */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Given xLow <= x <= xHigh moves x up and down between the | |
|   boundaries. | |
|  | |
|   @details 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. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| ddSymmSiftingConvAux( | |
|   DdManager * table, | |
|   int  x, | |
|   int  xLow, | |
|   int  xHigh) | |
| { | |
|     Move *move; | |
|     Move *moveUp;	/* list of up moves */ | |
|     Move *moveDown;	/* list of down moves */ | |
|     int	 initialSize; | |
|     int	 result; | |
|     int  i; | |
|     int  initGroupSize, finalGroupSize; | |
| 
 | |
| 
 | |
|     initialSize = (int) (table->keys - table->isolated); | |
| 
 | |
|     moveDown = NULL; | |
|     moveUp = NULL; | |
| 
 | |
|     if (x == xLow) { /* Sift down */ | |
| #ifdef DD_DEBUG | |
| 	/* x is bottom of symmetry group */ | |
| 	assert((unsigned) x >= table->subtables[x].next); | |
| #endif | |
| 	i = table->subtables[x].next; | |
| 	initGroupSize = x - i + 1; | |
| 
 | |
| 	moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	/* at this point x == xHigh, unless early term */ | |
| 	if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; | |
| 	if (moveDown == NULL) return(1); | |
| 
 | |
| 	x = moveDown->y; | |
| 	i = x; | |
| 	while ((unsigned) i < table->subtables[i].next) { | |
| 	    i = table->subtables[i].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the top of the symmetric group and i the bottom */ | |
| 	assert((unsigned) i >= table->subtables[i].next); | |
| 	assert((unsigned) x == table->subtables[i].next); | |
| #endif | |
| 	finalGroupSize = i - x + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetries detected, go back to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} else { | |
|           initialSize = (int) (table->keys - table->isolated); | |
| 	    moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} | |
| 	if (!result) goto ddSymmSiftingConvAuxOutOfMem; | |
| 
 | |
|     } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ | |
| 	/* Find top of x's symm group */ | |
| 	while ((unsigned) x < table->subtables[x].next) | |
| 	    x = table->subtables[x].next; | |
| 	i = x;				/* bottom */ | |
| 	x = table->subtables[x].next;	/* top */ | |
| 
 | |
| 	if (x == xLow) return(1); | |
| 
 | |
| 	initGroupSize = i - x + 1; | |
| 
 | |
| 	moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	    /* at this point x == xLow, unless early term */ | |
| 	if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; | |
| 	if (moveUp == NULL) return(1); | |
| 
 | |
| 	x = moveUp->x; | |
| 	i = table->subtables[x].next; | |
| #ifdef DD_DEBUG | |
| 	/* x should be the bottom of the symmetry group and i the top */ | |
| 	assert((unsigned) x >= table->subtables[x].next); | |
| 	assert((unsigned) i == table->subtables[x].next); | |
| #endif | |
| 	finalGroupSize = x - i + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetry groups detected, return to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} else { | |
|           initialSize = (int) (table->keys - table->isolated); | |
| 	    moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} | |
| 	if (!result) | |
| 	    goto ddSymmSiftingConvAuxOutOfMem; | |
| 
 | |
|     } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ | |
| 	moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	    /* at this point x == xHigh, unless early term */ | |
| 	if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; | |
| 
 | |
| 	if (moveDown != NULL) { | |
| 	    x = moveDown->y; | |
| 	    i = x; | |
| 	    while ((unsigned) i < table->subtables[i].next) { | |
| 		i = table->subtables[i].next; | |
| 	    } | |
| 	} else { | |
| 	    while ((unsigned) x < table->subtables[x].next) | |
| 		x = table->subtables[x].next; | |
| 	    i = x; | |
| 	    x = table->subtables[x].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the top of the symmetry group and i the bottom */ | |
| 	assert((unsigned) i >= table->subtables[i].next); | |
| 	assert((unsigned) x == table->subtables[i].next); | |
| #endif | |
| 	initGroupSize = i - x + 1; | |
| 
 | |
| 	moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; | |
| 
 | |
| 	if (moveUp != NULL) { | |
| 	    x = moveUp->x; | |
| 	    i = table->subtables[x].next; | |
| 	} else { | |
| 	    i = x; | |
| 	    while ((unsigned) x < table->subtables[x].next) | |
| 		x = table->subtables[x].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the bottom of the symmetry group and i the top */ | |
| 	assert((unsigned) x >= table->subtables[x].next); | |
| 	assert((unsigned) i == table->subtables[x].next); | |
| #endif | |
| 	finalGroupSize = x - i + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetry groups detected, return to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} else { | |
| 	    while (moveDown != NULL) { | |
| 		move = moveDown->next; | |
| 		cuddDeallocMove(table, moveDown); | |
| 		moveDown = move; | |
| 	    } | |
| 	    initialSize = (int) (table->keys - table->isolated); | |
| 	    moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} | |
| 	if (!result) goto ddSymmSiftingConvAuxOutOfMem; | |
| 
 | |
|     } else { /* moving up first: shorter */ | |
| 	/* Find top of x's symmetry group */ | |
| 	x = table->subtables[x].next; | |
| 
 | |
| 	moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	/* at this point x == xHigh, unless early term */ | |
| 	if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; | |
| 
 | |
| 	if (moveUp != NULL) { | |
| 	    x = moveUp->x; | |
| 	    i = table->subtables[x].next; | |
| 	} else { | |
| 	    i = x; | |
| 	    while ((unsigned) x < table->subtables[x].next) | |
| 		x = table->subtables[x].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x is bottom of the symmetry group and i is top */ | |
| 	assert((unsigned) x >= table->subtables[x].next); | |
| 	assert((unsigned) i == table->subtables[x].next); | |
| #endif | |
| 	initGroupSize = x - i + 1; | |
| 
 | |
| 	moveDown = ddSymmSiftingDown(table,x,xHigh); | |
| 	if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; | |
| 
 | |
| 	if (moveDown != NULL) { | |
| 	    x = moveDown->y; | |
| 	    i = x; | |
| 	    while ((unsigned) i < table->subtables[i].next) { | |
| 		i = table->subtables[i].next; | |
| 	    } | |
| 	} else { | |
| 	    i = x; | |
| 	    x = table->subtables[x].next; | |
| 	} | |
| #ifdef DD_DEBUG | |
| 	/* x should be the top of the symmetry group and i the bottom */ | |
| 	assert((unsigned) i >= table->subtables[i].next); | |
| 	assert((unsigned) x == table->subtables[i].next); | |
| #endif | |
| 	finalGroupSize = i - x + 1; | |
| 
 | |
| 	if (initGroupSize == finalGroupSize) { | |
| 	    /* No new symmetries detected, go back to best position */ | |
| 	    result = ddSymmSiftingBackward(table,moveDown,initialSize); | |
| 	} else { | |
| 	    while (moveUp != NULL) { | |
| 		move = moveUp->next; | |
| 		cuddDeallocMove(table, moveUp); | |
| 		moveUp = move; | |
| 	    } | |
| 	    initialSize = (int) (table->keys - table->isolated); | |
| 	    moveUp = ddSymmSiftingUp(table,x,xLow); | |
| 	    result = ddSymmSiftingBackward(table,moveUp,initialSize); | |
| 	} | |
| 	if (!result) goto ddSymmSiftingConvAuxOutOfMem; | |
|     } | |
| 
 | |
|     while (moveDown != NULL) { | |
| 	move = moveDown->next; | |
| 	cuddDeallocMove(table, moveDown); | |
| 	moveDown = move; | |
|     } | |
|     while (moveUp != NULL) { | |
| 	move = moveUp->next; | |
| 	cuddDeallocMove(table, moveUp); | |
| 	moveUp = move; | |
|     } | |
| 
 | |
|     return(1); | |
| 
 | |
| ddSymmSiftingConvAuxOutOfMem: | |
|     if (moveDown != MV_OOM) { | |
| 	while (moveDown != NULL) { | |
| 	    move = moveDown->next; | |
| 	    cuddDeallocMove(table, moveDown); | |
| 	    moveDown = move; | |
| 	} | |
|     } | |
|     if (moveUp != MV_OOM) { | |
| 	while (moveUp != NULL) { | |
| 	    move = moveUp->next; | |
| 	    cuddDeallocMove(table, moveUp); | |
| 	    moveUp = move; | |
| 	} | |
|     } | |
| 
 | |
|     return(0); | |
| 
 | |
| } /* end of ddSymmSiftingConvAux */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Moves x up until either it reaches the bound (xLow) or | |
|   the size of the %DD heap increases too much. | |
|  | |
|   @details 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. | |
|  | |
|   @return the set of moves in case of success; MV_OOM if memory is | |
|   full. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static Move * | |
| ddSymmSiftingUp( | |
|   DdManager * table, | |
|   int  y, | |
|   int  xLow) | |
| { | |
|     Move *moves; | |
|     Move *move; | |
|     int	 x; | |
|     int	 size; | |
|     int  i; | |
|     int  gxtop,gybot; | |
|     int  limitSize; | |
|     int  xindex, yindex; | |
|     int  zindex; | |
|     int  z; | |
|     int  isolated; | |
|     int  L;	/* lower bound on DD size */ | |
| #ifdef DD_DEBUG | |
|     int  checkL; | |
| #endif | |
|  | |
| 
 | |
|     moves = NULL; | |
|     yindex = table->invperm[y]; | |
| 
 | |
|     /* Initialize the lower bound. | |
|     ** The part of the DD below the bottom of y' group will not change. | |
|     ** The part of the DD above y that does not interact with y will not | |
|     ** change. The rest may vanish in the best case, except for | |
|     ** the nodes at level xLow, which will not vanish, regardless. | |
|     */ | |
|     limitSize = L = (int) (table->keys - table->isolated); | |
|     gybot = y; | |
|     while ((unsigned) gybot < table->subtables[gybot].next) | |
| 	gybot = table->subtables[gybot].next; | |
|     for (z = xLow + 1; z <= gybot; z++) { | |
| 	zindex = table->invperm[z]; | |
| 	if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { | |
| 	    isolated = table->vars[zindex]->ref == 1; | |
| 	    L -= (int) table->subtables[z].keys - isolated; | |
| 	} | |
|     } | |
| 
 | |
|     x = cuddNextLow(table,y); | |
|     while (x >= xLow && L <= limitSize) { | |
| #ifdef DD_DEBUG | |
| 	gybot = y; | |
| 	while ((unsigned) gybot < table->subtables[gybot].next) | |
| 	    gybot = table->subtables[gybot].next; | |
| 	checkL = (int) (table->keys - table->isolated); | |
| 	for (z = xLow + 1; z <= gybot; z++) { | |
| 	    zindex = table->invperm[z]; | |
| 	    if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { | |
| 		isolated = table->vars[zindex]->ref == 1; | |
| 		checkL -= (int) table->subtables[z].keys - isolated; | |
| 	    } | |
| 	} | |
| 	assert(L == checkL); | |
| #endif | |
| 	gxtop = table->subtables[x].next; | |
| 	if (cuddSymmCheck(table,x,y)) { | |
| 	    /* Symmetry found, attach symm groups */ | |
| 	    table->subtables[x].next = y; | |
| 	    i = table->subtables[y].next; | |
| 	    while (table->subtables[i].next != (unsigned) y) | |
| 		i = table->subtables[i].next; | |
| 	    table->subtables[i].next = gxtop; | |
| 	} else if (table->subtables[x].next == (unsigned) x && | |
| 		   table->subtables[y].next == (unsigned) y) { | |
| 	    /* x and y have self symmetry */ | |
| 	    xindex = table->invperm[x]; | |
| 	    size = cuddSwapInPlace(table,x,y); | |
| #ifdef DD_DEBUG | |
| 	    assert(table->subtables[x].next == (unsigned) x); | |
| 	    assert(table->subtables[y].next == (unsigned) y); | |
| #endif | |
| 	    if (size == 0) goto ddSymmSiftingUpOutOfMem; | |
| 	    /* Update the lower bound. */ | |
| 	    if (cuddTestInteract(table,xindex,yindex)) { | |
| 		isolated = table->vars[xindex]->ref == 1; | |
| 		L += (int) table->subtables[y].keys - isolated; | |
| 	    } | |
| 	    move = (Move *) cuddDynamicAllocNode(table); | |
| 	    if (move == NULL) goto ddSymmSiftingUpOutOfMem; | |
| 	    move->x = x; | |
| 	    move->y = y; | |
| 	    move->size = size; | |
| 	    move->next = moves; | |
| 	    moves = move; | |
| 	    if ((double) size > (double) limitSize * table->maxGrowth) | |
| 		return(moves); | |
| 	    if (size < limitSize) limitSize = size; | |
| 	} else { /* Group move */ | |
| 	    size = ddSymmGroupMove(table,x,y,&moves); | |
| 	    if (size == 0) goto ddSymmSiftingUpOutOfMem; | |
| 	    /* Update the lower bound. */ | |
| 	    z = moves->y; | |
| 	    do { | |
| 		zindex = table->invperm[z]; | |
| 		if (cuddTestInteract(table,zindex,yindex)) { | |
| 		    isolated = table->vars[zindex]->ref == 1; | |
| 		    L += (int) table->subtables[z].keys - isolated; | |
| 		} | |
| 		z = table->subtables[z].next; | |
| 	    } while (z != (int) moves->y); | |
| 	    if ((double) size > (double) limitSize * table->maxGrowth) | |
| 		return(moves); | |
| 	    if (size < limitSize) limitSize = size; | |
| 	} | |
| 	y = gxtop; | |
| 	x = cuddNextLow(table,y); | |
|     } | |
| 
 | |
|     return(moves); | |
| 
 | |
| ddSymmSiftingUpOutOfMem: | |
|     while (moves != NULL) { | |
| 	move = moves->next; | |
| 	cuddDeallocMove(table, moves); | |
| 	moves = move; | |
|     } | |
|     return(MV_OOM); | |
| 
 | |
| } /* end of ddSymmSiftingUp */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Moves x down until either it reaches the bound (xHigh) or | |
|   the size of the %DD heap increases too much. | |
|  | |
|   @details 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. | |
|  | |
|   @return the set of moves in case of success; MV_OOM if memory is | |
|   full. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static Move * | |
| ddSymmSiftingDown( | |
|   DdManager * table, | |
|   int  x, | |
|   int  xHigh) | |
| { | |
|     Move *moves; | |
|     Move *move; | |
|     int	 y; | |
|     int	 size; | |
|     int  limitSize; | |
|     int  gxtop,gybot; | |
|     int  R;	/* upper bound on node decrease */ | |
|     int  xindex, yindex; | |
|     int  isolated; | |
|     int  z; | |
|     int  zindex; | |
| #ifdef DD_DEBUG | |
|     int  checkR; | |
| #endif | |
|  | |
|     moves = NULL; | |
|     /* Initialize R */ | |
|     xindex = table->invperm[x]; | |
|     gxtop = table->subtables[x].next; | |
|     limitSize = size = (int) (table->keys - table->isolated); | |
|     R = 0; | |
|     for (z = xHigh; z > gxtop; z--) { | |
| 	zindex = table->invperm[z]; | |
| 	if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { | |
| 	    isolated = table->vars[zindex]->ref == 1; | |
| 	    R += (int) table->subtables[z].keys - isolated; | |
| 	} | |
|     } | |
| 
 | |
|     y = cuddNextHigh(table,x); | |
|     while (y <= xHigh && size - R < limitSize) { | |
| #ifdef DD_DEBUG | |
| 	gxtop = table->subtables[x].next; | |
| 	checkR = 0; | |
| 	for (z = xHigh; z > gxtop; z--) { | |
| 	    zindex = table->invperm[z]; | |
| 	    if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { | |
| 		isolated = table->vars[zindex]->ref == 1; | |
| 		checkR += (int) table->subtables[z].keys - isolated; | |
| 	    } | |
| 	} | |
| 	assert(R == checkR); | |
| #endif | |
| 	gybot = table->subtables[y].next; | |
| 	while (table->subtables[gybot].next != (unsigned) y) | |
| 	    gybot = table->subtables[gybot].next; | |
| 	if (cuddSymmCheck(table,x,y)) { | |
| 	    /* Symmetry found, attach symm groups */ | |
| 	    gxtop = table->subtables[x].next; | |
| 	    table->subtables[x].next = y; | |
| 	    table->subtables[gybot].next = gxtop; | |
| 	} else if (table->subtables[x].next == (unsigned) x && | |
| 		   table->subtables[y].next == (unsigned) y) { | |
| 	    /* x and y have self symmetry */ | |
| 	    /* Update upper bound on node decrease. */ | |
| 	    yindex = table->invperm[y]; | |
| 	    if (cuddTestInteract(table,xindex,yindex)) { | |
| 		isolated = table->vars[yindex]->ref == 1; | |
| 		R -= (int) table->subtables[y].keys - isolated; | |
| 	    } | |
| 	    size = cuddSwapInPlace(table,x,y); | |
| #ifdef DD_DEBUG | |
| 	    assert(table->subtables[x].next == (unsigned) x); | |
| 	    assert(table->subtables[y].next == (unsigned) y); | |
| #endif | |
| 	    if (size == 0) goto ddSymmSiftingDownOutOfMem; | |
| 	    move = (Move *) cuddDynamicAllocNode(table); | |
| 	    if (move == NULL) goto ddSymmSiftingDownOutOfMem; | |
| 	    move->x = x; | |
| 	    move->y = y; | |
| 	    move->size = size; | |
| 	    move->next = moves; | |
| 	    moves = move; | |
| 	    if ((double) size > (double) limitSize * table->maxGrowth) | |
| 		return(moves); | |
| 	    if (size < limitSize) limitSize = size; | |
| 	} else { /* Group move */ | |
| 	    /* Update upper bound on node decrease: first phase. */ | |
| 	    gxtop = table->subtables[x].next; | |
| 	    z = gxtop + 1; | |
| 	    do { | |
| 		zindex = table->invperm[z]; | |
| 		if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { | |
| 		    isolated = table->vars[zindex]->ref == 1; | |
| 		    R -= (int) table->subtables[z].keys - isolated; | |
| 		} | |
| 		z++; | |
| 	    } while (z <= gybot); | |
| 	    size = ddSymmGroupMove(table,x,y,&moves); | |
| 	    if (size == 0) goto ddSymmSiftingDownOutOfMem; | |
| 	    if ((double) size > (double) limitSize * table->maxGrowth) | |
| 		return(moves); | |
| 	    if (size < limitSize) limitSize = size; | |
| 	    /* Update upper bound on node decrease: second phase. */ | |
| 	    gxtop = table->subtables[gybot].next; | |
| 	    for (z = gxtop + 1; z <= gybot; z++) { | |
| 		zindex = table->invperm[z]; | |
| 		if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { | |
| 		    isolated = table->vars[zindex]->ref == 1; | |
| 		    R += (int) table->subtables[z].keys - isolated; | |
| 		} | |
| 	    } | |
| 	} | |
| 	x = gybot; | |
| 	y = cuddNextHigh(table,x); | |
|     } | |
| 
 | |
|     return(moves); | |
| 
 | |
| ddSymmSiftingDownOutOfMem: | |
|     while (moves != NULL) { | |
| 	move = moves->next; | |
| 	cuddDeallocMove(table, moves); | |
| 	moves = move; | |
|     } | |
|     return(MV_OOM); | |
| 
 | |
| } /* end of ddSymmSiftingDown */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Swaps two groups. | |
|  | |
|   @details 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. | |
|  | |
|   @return the number of keys in the table if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| ddSymmGroupMove( | |
|   DdManager * table, | |
|   int  x, | |
|   int  y, | |
|   Move ** moves) | |
| { | |
|     Move *move; | |
|     int	 size = 0; | |
|     int  i,j; | |
|     int  xtop,xbot,xsize,ytop,ybot,ysize,newxtop; | |
|     int  swapx = 0, swapy = 0; | |
| 
 | |
| #ifdef DD_DEBUG | |
|     assert(x < y);	/* we assume that x < y */ | |
| #endif | |
|     /* Find top, bottom, and size for the two groups. */ | |
|     xbot = x; | |
|     xtop = table->subtables[x].next; | |
|     xsize = xbot - xtop + 1; | |
|     ybot = y; | |
|     while ((unsigned) ybot < table->subtables[ybot].next) | |
| 	ybot = table->subtables[ybot].next; | |
|     ytop = y; | |
|     ysize = ybot - ytop + 1; | |
| 
 | |
|     /* Sift the variables of the second group up through the first group. */ | |
|     for (i = 1; i <= ysize; i++) { | |
| 	for (j = 1; j <= xsize; j++) { | |
| 	    size = cuddSwapInPlace(table,x,y); | |
| 	    if (size == 0) return(0); | |
| 	    swapx = x; swapy = y; | |
| 	    y = x; | |
| 	    x = y - 1; | |
| 	} | |
| 	y = ytop + i; | |
| 	x = y - 1; | |
|     } | |
| 
 | |
|     /* fix symmetries */ | |
|     y = xtop; /* ytop is now where xtop used to be */ | |
|     for (i = 0; i < ysize-1 ; i++) { | |
| 	table->subtables[y].next = y + 1; | |
| 	y = y + 1; | |
|     } | |
|     table->subtables[y].next = xtop; /* y is bottom of its group, join */ | |
| 				     /* its symmetry to top of its group */ | |
|     x = y + 1; | |
|     newxtop = x; | |
|     for (i = 0; i < xsize - 1 ; i++) { | |
| 	table->subtables[x].next = x + 1; | |
| 	x = x + 1; | |
|     } | |
|     table->subtables[x].next = newxtop; /* x is bottom of its group, join */ | |
| 					/* its symmetry to top of its group */ | |
|     /* Store group move */ | |
|     move = (Move *) cuddDynamicAllocNode(table); | |
|     if (move == NULL) return(0); | |
|     move->x = swapx; | |
|     move->y = swapy; | |
|     move->size = size; | |
|     move->next = *moves; | |
|     *moves = move; | |
| 
 | |
|     return(size); | |
| 
 | |
| } /* end of ddSymmGroupMove */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Undoes the swap of two groups. | |
|  | |
|   @details x is assumed to be the bottom variable of the first | |
|   group. y is assumed to be the top variable of the second group. | |
|  | |
|   @return the number of keys in the table if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| ddSymmGroupMoveBackward( | |
|   DdManager * table, | |
|   int  x, | |
|   int  y) | |
| { | |
|   int	size = (int) (table->keys - table->isolated); | |
|     int i,j; | |
|     int	xtop,xbot,xsize,ytop,ybot,ysize,newxtop; | |
| 
 | |
| #ifdef DD_DEBUG | |
|     assert(x < y); /* We assume that x < y */ | |
| #endif | |
|  | |
|     /* Find top, bottom, and size for the two groups. */ | |
|     xbot = x; | |
|     xtop = table->subtables[x].next; | |
|     xsize = xbot - xtop + 1; | |
|     ybot = y; | |
|     while ((unsigned) ybot < table->subtables[ybot].next) | |
| 	ybot = table->subtables[ybot].next; | |
|     ytop = y; | |
|     ysize = ybot - ytop + 1; | |
| #ifdef DD_DEBUG | |
|     assert(xsize > 0); | |
|     assert(ysize > 0); | |
| #endif | |
|  | |
|     /* Sift the variables of the second group up through the first group. */ | |
|     for (i = 1; i <= ysize; i++) { | |
| 	for (j = 1; j <= xsize; j++) { | |
| 	    size = cuddSwapInPlace(table,x,y); | |
| 	    if (size == 0) return(0); | |
| 	    y = x; | |
| 	    x = cuddNextLow(table,y); | |
| 	} | |
| 	y = ytop + i; | |
| 	x = y - 1; | |
|     } | |
| 
 | |
|     /* Fix symmetries. */ | |
|     y = xtop; | |
|     for (i = 0; i < ysize-1 ; i++) { | |
| 	table->subtables[y].next = y + 1; | |
| 	y = y + 1; | |
|     } | |
|     table->subtables[y].next = xtop; /* y is bottom of its group, join */ | |
| 				     /* its symmetry to top of its group */ | |
|     x = y + 1; | |
|     newxtop = x; | |
|     for (i = 0; i < xsize-1 ; i++) { | |
| 	table->subtables[x].next = x + 1; | |
| 	x = x + 1; | |
|     } | |
|     table->subtables[x].next = newxtop; /* x is bottom of its group, join */ | |
| 					/* its symmetry to top of its group */ | |
| 
 | |
|     return(size); | |
| 
 | |
| } /* end of ddSymmGroupMoveBackward */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Given a set of moves, returns the %DD heap to the position | |
|   giving the minimum size. | |
|  | |
|   @details In case of ties, returns to the closest position giving the | |
|   minimum size. | |
|  | |
|   @return 1 in case of success; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| ddSymmSiftingBackward( | |
|   DdManager * table, | |
|   Move * moves, | |
|   int  size) | |
| { | |
|     Move *move; | |
|     int  res; | |
| 
 | |
|     for (move = moves; move != NULL; move = move->next) { | |
| 	if (move->size < size) { | |
| 	    size = move->size; | |
| 	} | |
|     } | |
| 
 | |
|     for (move = moves; move != NULL; move = move->next) { | |
| 	if (move->size == size) return(1); | |
| 	if (table->subtables[move->x].next == move->x && table->subtables[move->y].next == move->y) { | |
| 	    res = cuddSwapInPlace(table,(int)move->x,(int)move->y); | |
| #ifdef DD_DEBUG | |
| 	    assert(table->subtables[move->x].next == move->x); | |
| 	    assert(table->subtables[move->y].next == move->y); | |
| #endif | |
| 	} else { /* Group move necessary */ | |
| 	    res = ddSymmGroupMoveBackward(table,(int)move->x,(int)move->y); | |
| 	} | |
| 	if (!res) return(0); | |
|     } | |
| 
 | |
|     return(1); | |
| 
 | |
| } /* end of ddSymmSiftingBackward */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Counts numbers of symmetric variables and symmetry groups. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static void | |
| ddSymmSummary( | |
|   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->subtables[i].next != (unsigned) i) { | |
| 	    TotalSymmGroups++; | |
| 	    x = i; | |
| 	    do { | |
| 		TotalSymm++; | |
| 		gbot = x; | |
| 		x = table->subtables[x].next; | |
| 	    } while (x != i); | |
| #ifdef DD_DEBUG | |
| 	    assert(table->subtables[gbot].next == (unsigned) i); | |
| #endif | |
| 	    i = gbot; | |
| 	} | |
|     } | |
|     *symvars = TotalSymm; | |
|     *symgroups = TotalSymmGroups; | |
| 
 | |
|     return; | |
| 
 | |
| } /* end of ddSymmSummary */
 |