spot/buddy/src/reorder.c
Alexandre Duret-Lutz 1ffdd57383 [buddy] backport some fixes from upstream
* src/fdd.c, src/imatrix.c, src/kernel.c, src/reorder.c: Here.
2015-03-11 23:45:00 +01:00

2321 lines
54 KiB
C

/*========================================================================
Copyright (C) 1996-2002 by Jorn Lind-Nielsen
All rights reserved
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, reproduce, prepare derivative
works, distribute, and display this software and its documentation
for any purpose, provided that (1) the above copyright notice and
the following two paragraphs appear in all copies of the source code
and (2) redistributions, including without limitation binaries,
reproduce these notices in the supporting documentation. Substantial
modifications to this software may be copyrighted by their authors
and need not follow the licensing terms described here, provided
that the new terms are clearly indicated in all files where they apply.
IN NO EVENT SHALL JORN LIND-NIELSEN, OR DISTRIBUTORS OF THIS
SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE
ABOVE PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
JORN LIND-NIELSEN SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO
OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
========================================================================*/
/*************************************************************************
$Header: /Volumes/CVS/repository/spot/spot/buddy/src/reorder.c,v 1.2 2003/05/05 13:45:08 aduret Exp $
FILE: reorder.c
DESCR: BDD reordering functions
AUTH: Jorn Lind
DATE: (C) january 1998
*************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <assert.h>
#include "kernel.h"
#include "bddtree.h"
#include "imatrix.h"
#include "prime.h"
/* IMPORTANT:
* The semantics of the "level" field in the BddNode struct changes during
* variable reordering in order to make a fast variable swap possible when
* two variables are independent. Instead of refering to the level of the node
* it refers to the *variable* !!!
*/
/* Change macros to reflect the above idea */
#define VAR(n) (bddnodes[n].level)
#define VARp(p) (p->level)
/* Avoid these - they are misleading! */
#undef LEVEL
#undef LEVELp
#define __USERESIZE /* FIXME */
/* Current auto reord. method and number of automatic reorderings left */
static int bddreordermethod;
static int bddreordertimes;
/* Flag for disabling reordering temporarily */
static int reorderdisabled;
/* Store for the variable relationships */
static BddTree *vartree;
static int blockid;
/* Store for the ref.cou. of the external roots */
static int *extroots;
static int extrootsize;
/* Level data */
typedef struct _levelData
{
int start; /* Start of this sub-table (entry in "bddnodes") */
int size; /* Size of this sub-table */
int maxsize; /* Max. allowed size of sub-table */
int nodenum; /* Number of nodes in this level */
} levelData;
static levelData *levels; /* Indexed by variable! */
/* Interaction matrix */
static imatrix *iactmtx;
/* Reordering information for the user */
static int verbose;
static bddinthandler reorder_handler;
static bddfilehandler reorder_filehandler;
static bddsizehandler reorder_nodenum;
/* Number of live nodes before and after a reordering session */
static int usednum_before;
static int usednum_after;
/* Kernel variables needed for reordering */
extern int bddfreepos;
extern int bddfreenum;
extern long int bddproduced;
/* Flag telling us when a node table resize is done */
static int resizedInMakenode;
/* New node hashing function for use with reordering */
#define NODEHASH(var,l,h) ((PAIR((l),(h))%levels[var].size)+levels[var].start)
/* Reordering prototypes */
static void blockdown(BddTree *);
static void addref_rec(int, char *);
static void reorder_gbc(void);
static void reorder_setLevellookup(void);
static int reorder_makenode(int, int, int);
static int reorder_varup(int);
static int reorder_vardown(int);
static int reorder_init(void);
static void reorder_done(void);
#define random(a) (rand() % (a))
/* For sorting the blocks according to some specific size value */
typedef struct s_sizePair
{
int val;
BddTree *block;
} sizePair;
/*************************************************************************
Initialize and shutdown
*************************************************************************/
void bdd_reorder_init(void)
{
reorderdisabled = 0;
vartree = NULL;
bdd_clrvarblocks();
bdd_reorder_hook(bdd_default_reohandler);
bdd_reorder_verbose(0);
bdd_autoreorder_times(BDD_REORDER_NONE, 0);
reorder_nodenum = bdd_getnodenum;
usednum_before = usednum_after = 0;
blockid = 0;
}
void bdd_reorder_done(void)
{
bddtree_del(vartree);
bdd_operator_reset();
vartree = NULL;
}
/*************************************************************************
Reordering heuristics
*************************************************************************/
/*=== Reorder using a sliding window of size 2 =========================*/
static BddTree *reorder_win2(BddTree *t)
{
BddTree *this=t, *first=t;
if (t == NULL)
return t;
if (verbose > 1)
printf("Win2 start: %d nodes\n", reorder_nodenum());
fflush(stdout);
while (this->next != NULL)
{
int best = reorder_nodenum();
blockdown(this);
if (best < reorder_nodenum())
{
blockdown(this->prev);
this = this->next;
}
else
if (first == this)
first = this->prev;
if (verbose > 1)
{
printf(".");
fflush(stdout);
}
}
if (verbose > 1)
printf("\nWin2 end: %d nodes\n", reorder_nodenum());
fflush(stdout);
return first;
}
static BddTree *reorder_win2ite(BddTree *t)
{
BddTree *this, *first=t;
int lastsize;
int c=1;
if (t == NULL)
return t;
if (verbose > 1)
printf("Win2ite start: %d nodes\n", reorder_nodenum());
do
{
lastsize = reorder_nodenum();
this = t;
while (this->next != NULL)
{
int best = reorder_nodenum();
blockdown(this);
if (best < reorder_nodenum())
{
blockdown(this->prev);
this = this->next;
}
else
if (first == this)
first = this->prev;
if (verbose > 1)
{
printf(".");
fflush(stdout);
}
}
if (verbose > 1)
printf(" %d nodes\n", reorder_nodenum());
c++;
}
while (reorder_nodenum() != lastsize);
return first;
}
/*=== Reorder using a sliding window of size 3 =========================*/
#define X(a)
static BddTree *reorder_swapwin3(BddTree *this, BddTree **first)
{
int setfirst = (this->prev == NULL ? 1 : 0);
BddTree *next = this;
int best = reorder_nodenum();
if (this->next->next == NULL) /* Only two blocks left -> win2 swap */
{
blockdown(this);
if (best < reorder_nodenum())
{
blockdown(this->prev);
next = this->next;
}
else
{
next = this;
if (setfirst)
*first = this->prev;
}
}
else /* Real win3 swap */
{
int pos = 0;
X(printf("%d: ", reorder_nodenum()));
blockdown(this); /* B A* C (4) */
X(printf("A"));
pos++;
if (best > reorder_nodenum())
{
X(printf("(%d)", reorder_nodenum()));
pos = 0;
best = reorder_nodenum();
}
blockdown(this); /* B C A* (3) */
X(printf("B"));
pos++;
if (best > reorder_nodenum())
{
X(printf("(%d)", reorder_nodenum()));
pos = 0;
best = reorder_nodenum();
}
this = this->prev->prev;
blockdown(this); /* C B* A (2) */
X(printf("C"));
pos++;
if (best > reorder_nodenum())
{
X(printf("(%d)", reorder_nodenum()));
pos = 0;
best = reorder_nodenum();
}
blockdown(this); /* C A B* (1) */
X(printf("D"));
pos++;
if (best > reorder_nodenum())
{
X(printf("(%d)", reorder_nodenum()));
pos = 0;
best = reorder_nodenum();
}
this = this->prev->prev;
blockdown(this); /* A C* B (0)*/
X(printf("E"));
pos++;
if (best > reorder_nodenum())
{
X(printf("(%d)", reorder_nodenum()));
pos = 0;
best = reorder_nodenum();
}
X(printf(" -> "));
if (pos >= 1) /* A C B -> C A* B */
{
this = this->prev;
blockdown(this);
next = this;
if (setfirst)
*first = this->prev;
X(printf("a(%d)", reorder_nodenum()));
}
if (pos >= 2) /* C A B -> C B A* */
{
blockdown(this);
next = this->prev;
if (setfirst)
*first = this->prev->prev;
X(printf("b(%d)", reorder_nodenum()));
}
if (pos >= 3) /* C B A -> B C* A */
{
this = this->prev->prev;
blockdown(this);
next = this;
if (setfirst)
*first = this->prev;
X(printf("c(%d)", reorder_nodenum()));
}
if (pos >= 4) /* B C A -> B A C* */
{
blockdown(this);
next = this->prev;
if (setfirst)
*first = this->prev->prev;
X(printf("d(%d)", reorder_nodenum()));
}
if (pos >= 5) /* B A C -> A B* C */
{
this = this->prev->prev;
blockdown(this);
next = this;
if (setfirst)
*first = this->prev;
X(printf("e(%d)", reorder_nodenum()));
}
X(printf("\n"));
}
return next;
}
static BddTree *reorder_win3(BddTree *t)
{
BddTree *this=t, *first=t;
if (t == NULL)
return t;
if (verbose > 1)
printf("Win3 start: %d nodes\n", reorder_nodenum());
fflush(stdout);
while (this->next != NULL)
{
this = reorder_swapwin3(this, &first);
if (verbose > 1)
{
printf(".");
fflush(stdout);
}
}
if (verbose > 1)
printf("\nWin3 end: %d nodes\n", reorder_nodenum());
fflush(stdout);
return first;
}
static BddTree *reorder_win3ite(BddTree *t)
{
BddTree *this, *first=t;
int lastsize;
if (t == NULL)
return t;
if (verbose > 1)
printf("Win3ite start: %d nodes\n", reorder_nodenum());
do
{
lastsize = reorder_nodenum();
this = first;
while (this->next != NULL && this->next->next != NULL)
{
this = reorder_swapwin3(this, &first);
if (verbose > 1)
{
printf(".");
fflush(stdout);
}
}
if (verbose > 1)
printf(" %d nodes\n", reorder_nodenum());
}
while (reorder_nodenum() != lastsize);
if (verbose > 1)
printf("Win3ite end: %d nodes\n", reorder_nodenum());
return first;
}
/*=== Reorder by sifting =============================================*/
/* Move a specific block up and down in the order and place at last in
the best position
*/
static void reorder_sift_bestpos(BddTree *blk, int middlePos)
{
int best = reorder_nodenum();
int maxAllowed;
int bestpos = 0;
int dirIsUp = 1;
int n;
if (bddmaxnodesize > 0)
maxAllowed = MIN(best/5+best, bddmaxnodesize-bddmaxnodeincrease-2);
else
maxAllowed = best/5+best;
/* Determine initial direction */
if (blk->pos > middlePos)
dirIsUp = 0;
/* Move block back and forth */
for (n=0 ; n<2 ; n++)
{
int first = 1;
if (dirIsUp)
{
while (blk->prev != NULL &&
(reorder_nodenum() <= maxAllowed || first))
{
first = 0;
blockdown(blk->prev);
bestpos--;
if (verbose > 1)
{
printf("-");
fflush(stdout);
}
if (reorder_nodenum() < best)
{
best = reorder_nodenum();
bestpos = 0;
if (bddmaxnodesize > 0)
maxAllowed = MIN(best/5+best,
bddmaxnodesize-bddmaxnodeincrease-2);
else
maxAllowed = best/5+best;
}
}
}
else
{
while (blk->next != NULL &&
(reorder_nodenum() <= maxAllowed || first))
{
first = 0;
blockdown(blk);
bestpos++;
if (verbose > 1)
{
printf("+");
fflush(stdout);
}
if (reorder_nodenum() < best)
{
best = reorder_nodenum();
bestpos = 0;
if (bddmaxnodesize > 0)
maxAllowed = MIN(best/5+best,
bddmaxnodesize-bddmaxnodeincrease-2);
else
maxAllowed = best/5+best;
}
}
}
if (reorder_nodenum() > maxAllowed && verbose > 1)
{
printf("!");
fflush(stdout);
}
dirIsUp = !dirIsUp;
}
/* Move to best pos */
while (bestpos < 0)
{
blockdown(blk);
bestpos++;
}
while (bestpos > 0)
{
blockdown(blk->prev);
bestpos--;
}
}
/* Go through all blocks in a specific sequence and find best
position for each of them
*/
static BddTree *reorder_sift_seq(BddTree *t, BddTree **seq, int num)
{
BddTree *this;
int n;
if (t == NULL)
return t;
for (n=0 ; n<num ; n++)
{
long c2, c1 = clock();
if (verbose > 1)
{
printf("Sift ");
if (reorder_filehandler)
reorder_filehandler(stdout, seq[n]->id);
else
printf("%d", seq[n]->id);
printf(": ");
}
reorder_sift_bestpos(seq[n], num/2);
if (verbose > 1)
printf("\n> %d nodes", reorder_nodenum());
c2 = clock();
if (verbose > 1)
printf(" (%.1f sec)\n", (float)(c2-c1)/CLOCKS_PER_SEC);
}
/* Find first block */
for (this=t ; this->prev != NULL ; this=this->prev)
/* nil */;
return this;
}
/* Compare function for sorting sifting sequence
*/
static int siftTestCmp(const void *aa, const void *bb)
{
const sizePair *a = (const sizePair*)aa;
const sizePair *b = (const sizePair*)bb;
if (a->val < b->val)
return -1;
if (a->val > b->val)
return 1;
return 0;
}
/* Find sifting sequence based on the number of nodes at each level
*/
static BddTree *reorder_sift(BddTree *t)
{
BddTree *this, **seq;
sizePair *p;
int n, num;
for (this=t,num=0 ; this!=NULL ; this=this->next)
this->pos = num++;
if ((p=NEW(sizePair,num)) == NULL)
return t;
if ((seq=NEW(BddTree*,num)) == NULL)
{
free(p);
return t;
}
for (this=t,n=0 ; this!=NULL ; this=this->next,n++)
{
int v;
/* Accumulate number of nodes for each block */
p[n].val = 0;
for (v=this->first ; v<=this->last ; v++)
p[n].val -= levels[v].nodenum;
p[n].block = this;
}
/* Sort according to the number of nodes at each level */
qsort(p, num, sizeof(sizePair), siftTestCmp);
/* Create sequence */
for (n=0 ; n<num ; n++)
seq[n] = p[n].block;
/* Do the sifting on this sequence */
t = reorder_sift_seq(t, seq, num);
free(seq);
free(p);
return t;
}
/* Do sifting iteratively until no more improvement can be found
*/
static BddTree *reorder_siftite(BddTree *t)
{
BddTree *first=t;
int lastsize;
int c=1;
if (t == NULL)
return t;
do
{
if (verbose > 1)
printf("Reorder %d\n", c++);
lastsize = reorder_nodenum();
first = reorder_sift(first);
}
while (reorder_nodenum() != lastsize);
return first;
}
/*=== Random reordering (mostly for debugging and test ) =============*/
static BddTree *reorder_random(BddTree *t)
{
BddTree *this;
BddTree **seq;
int n, num=0;
if (t == NULL)
return t;
for (this=t ; this!=NULL ; this=this->next)
num++;
seq = NEW(BddTree*,num);
for (this=t,num=0 ; this!=NULL ; this=this->next)
seq[num++] = this;
for (n=0 ; n<4*num ; n++)
{
int blk = random(num);
if (seq[blk]->next != NULL)
blockdown(seq[blk]);
}
/* Find first block */
for (this=t ; this->prev != NULL ; this=this->prev)
/* nil */;
free(seq);
if (verbose)
printf("Random order: %d nodes\n", reorder_nodenum());
return this;
}
/*************************************************************************
Swapping adjacent blocks
*************************************************************************/
static void blockdown(BddTree *left)
{
BddTree *right = left->next;
int n;
int leftsize = left->last - left->first;
int rightsize = right->last - right->first;
int leftstart = bddvar2level[left->seq[0]];
int *lseq = left->seq;
int *rseq = right->seq;
/* Move left past right */
while (bddvar2level[lseq[0]] < bddvar2level[rseq[rightsize]])
{
for (n=0 ; n<leftsize ; n++)
{
if (bddvar2level[lseq[n]]+1 != bddvar2level[lseq[n+1]]
&& bddvar2level[lseq[n]] < bddvar2level[rseq[rightsize]])
{
reorder_vardown(lseq[n]);
}
}
if (bddvar2level[lseq[leftsize]] < bddvar2level[rseq[rightsize]])
{
reorder_vardown(lseq[leftsize]);
}
}
/* Move right to where left started */
while (bddvar2level[rseq[0]] > leftstart)
{
for (n=rightsize ; n>0 ; n--)
{
if (bddvar2level[rseq[n]]-1 != bddvar2level[rseq[n-1]]
&& bddvar2level[rseq[n]] > leftstart)
{
reorder_varup(rseq[n]);
}
}
if (bddvar2level[rseq[0]] > leftstart)
reorder_varup(rseq[0]);
}
/* Swap left and right data in the order */
left->next = right->next;
right->prev = left->prev;
left->prev = right;
right->next = left;
if (right->prev != NULL)
right->prev->next = right;
if (left->next != NULL)
left->next->prev = left;
n = left->pos;
left->pos = right->pos;
right->pos = n;
}
/*************************************************************************
Kernel reordering routines
*************************************************************************/
/*=== Garbage collection for reordering ================================*/
/* Note: Node may be marked
*/
static void addref_rec(int r, char *dep)
{
if (r < 2)
return;
if (bddnodes[r].refcou == 0 || MARKED(r))
{
bddfreenum--;
/* Detect variable dependencies for the interaction matrix */
dep[VAR(r) & MARKHIDE] = 1;
/* Make sure the nodenum field is updated. Used in the initial GBC */
levels[VAR(r) & MARKHIDE].nodenum++;
addref_rec(LOW(r), dep);
addref_rec(HIGH(r), dep);
}
else
{
int n;
/* Update (from previously found) variable dependencies
* for the interaction matrix */
for (n=0 ; n<bddvarnum ; n++)
dep[n] |= imatrixDepends(iactmtx, VAR(r) & MARKHIDE, n);
}
INCREF(r);
}
static void addDependencies(char *dep)
{
int n,m;
for (n=0 ; n<bddvarnum ; n++)
{
for (m=n ; m<bddvarnum ; m++)
{
if (dep[n] && dep[m])
{
imatrixSet(iactmtx, n,m);
imatrixSet(iactmtx, m,n);
}
}
}
}
/* Make sure all nodes are recursively reference counted and store info about
nodes that are refcou. externally. This info is used at last to revert
to the standard GBC mode.
*/
static int mark_roots(void)
{
char *dep = NEW(char,bddvarnum);
int n;
for (n=2,extrootsize=0 ; n<bddnodesize ; n++)
{
/* This is where we go from .level to .var!
* - Do NOT use the LEVEL macro here. */
bddnodes[n].level = bddlevel2var[bddnodes[n].level];
if (bddnodes[n].refcou > 0)
{
SETMARK(n);
extrootsize++;
}
}
if ((extroots=(int*)(malloc(sizeof(int)*extrootsize))) == NULL)
{
free(dep);
return bdd_error(BDD_MEMORY);
}
iactmtx = imatrixNew(bddvarnum);
for (n=2,extrootsize=0 ; n<bddnodesize ; n++)
{
BddNode *node = &bddnodes[n];
if (MARKEDp(node))
{
UNMARKp(node);
extroots[extrootsize++] = n;
memset(dep,0,bddvarnum);
dep[VARp(node)] = 1;
levels[VARp(node)].nodenum++;
addref_rec(LOWp(node), dep);
addref_rec(HIGHp(node), dep);
addDependencies(dep);
}
}
/* Make sure the hash is empty. This saves a loop in the initial
GBC */
memset(bddhash, 0, bddnodesize*sizeof(*bddhash));
free(dep);
return 0;
}
/* Now that all nodes are recursively reference counted we must make sure
that the new hashing scheme is used AND that dead nodes are removed.
This is also a good time to create the interaction matrix.
*/
static void reorder_gbc(void)
{
int n;
bddfreepos = 0;
bddfreenum = 0;
/* No need to zero all hash fields - this is done in mark_roots */
for (n=bddnodesize-1 ; n>=2 ; n--)
{
register BddNode *node = &bddnodes[n];
if (node->refcou > 0)
{
register unsigned int hash;
hash = NODEHASH(VARp(node), LOWp(node), HIGHp(node));
node->next = bddhash[hash];
bddhash[hash] = n;
}
else
{
LOWp(node) = -1;
node->next = bddfreepos;
bddfreepos = n;
bddfreenum++;
}
}
}
static void reorder_setLevellookup(void)
{
int n;
for (n=0 ; n<bddvarnum ; n++)
{
#ifdef USERESIZE
levels[n].maxsize = bddnodesize / bddvarnum;
levels[n].start = n * levels[n].maxsize;
levels[n].size = MIN(levels[n].maxsize, (levels[n].nodenum*5)/4);
#else
levels[n].maxsize = bddnodesize / bddvarnum;
levels[n].start = n * levels[n].maxsize;
levels[n].size = levels[n].maxsize;
#endif
if (levels[n].size >= 4)
levels[n].size = bdd_prime_lte(levels[n].size);
#if 0
printf("L%3d: start %d, size %d, nodes %d\n", n, levels[n].start,
levels[n].size, levels[n].nodenum);
#endif
}
}
static void reorder_rehashAll(void)
{
int n;
reorder_setLevellookup();
bddfreepos = 0;
memset(bddhash, 0, sizeof(*bddhash)*bddnodesize);
for (n=bddnodesize-1 ; n>=2 ; n--)
{
register BddNode *node = &bddnodes[n];
if (node->refcou > 0)
{
register unsigned int hash;
hash = NODEHASH(VARp(node), LOWp(node), HIGHp(node));
node->next = bddhash[hash];
bddhash[hash] = n;
}
else
{
node->next = bddfreepos;
bddfreepos = n;
}
}
}
/*=== Unique table handling for reordering =============================*/
/* Note: rehashing must not take place during a makenode call. It is okay
to resize the table, but *not* to rehash it.
*/
static int reorder_makenode(int var, int low, int high)
{
register BddNode *node;
register unsigned int hash;
register int res;
#ifdef CACHESTATS
bddcachestats.uniqueAccess++;
#endif
/* Note: We know that low,high has a refcou greater than zero, so
there is no need to add reference *recursively* */
/* check whether childs are equal */
if (low == high)
{
INCREF(low);
return low;
}
/* Try to find an existing node of this kind */
hash = NODEHASH(var, low, high);
res = bddhash[hash];
while(res != 0)
{
if (LOW(res) == low && HIGH(res) == high)
{
#ifdef CACHESTATS
bddcachestats.uniqueHit++;
#endif
INCREF(res);
return res;
}
res = bddnodes[res].next;
#ifdef CACHESTATS
bddcachestats.uniqueChain++;
#endif
}
/* No existing node -> build one */
#ifdef CACHESTATS
bddcachestats.uniqueMiss++;
#endif
/* Any free nodes to use ? */
if (bddfreepos == 0)
{
if (bdderrorcond)
return 0;
/* Try to allocate more nodes - call noderesize without
* enabling rehashing.
* Note: if ever rehashing is allowed here, then remember to
* update local variable "hash" */
bdd_noderesize(0);
resizedInMakenode = 1;
/* Panic if that is not possible */
if (bddfreepos == 0)
{
bdd_error(BDD_NODENUM);
bdderrorcond = abs(BDD_NODENUM);
return 0;
}
}
/* Build new node */
res = bddfreepos;
bddfreepos = bddnodes[bddfreepos].next;
levels[var].nodenum++;
bddproduced++;
bddfreenum--;
node = &bddnodes[res];
VARp(node) = var;
LOWp(node) = low;
HIGHp(node) = high;
/* Insert node in hash chain */
node->next = bddhash[hash];
bddhash[hash] = res;
/* Make sure it is reference counted */
node->refcou = 1;
INCREF(LOWp(node));
INCREF(HIGHp(node));
return res;
}
/*=== Swapping two adjacent variables ==================================*/
/* Go through var 0 nodes. Move nodes that depends on var 1 to a separate
* chain (toBeProcessed) and let the rest stay in the table.
*/
static int reorder_downSimple(int var0)
{
int toBeProcessed = 0;
int var1 = bddlevel2var[bddvar2level[var0]+1];
int vl0 = levels[var0].start;
int size0 = levels[var0].size;
int n;
levels[var0].nodenum = 0;
for (n=0 ; n<size0 ; n++)
{
int r;
r = bddhash[n + vl0];
bddhash[n + vl0] = 0;
while (r != 0)
{
BddNode *node = &bddnodes[r];
int next = node->next;
if (VAR(LOWp(node)) != var1 && VAR(HIGHp(node)) != var1)
{
/* Node does not depend on next var, let it stay in the chain */
node->next = bddhash[n+vl0];
bddhash[n+vl0] = r;
levels[var0].nodenum++;
}
else
{
/* Node depends on next var - save it for later procesing */
node->next = toBeProcessed;
toBeProcessed = r;
#ifdef SWAPCOUNT
bddcachestats.swapCount++;
#endif
}
r = next;
}
}
return toBeProcessed;
}
/* Now process all the var 0 nodes that depends on var 1.
*
* It is extremely important that no rehashing is done inside the makenode
* calls, since this would destroy the toBeProcessed chain.
*/
static void reorder_swap(int toBeProcessed, int var0)
{
int var1 = bddlevel2var[bddvar2level[var0]+1];
while (toBeProcessed)
{
BddNode *node = &bddnodes[toBeProcessed];
int next = node->next;
int f0 = LOWp(node);
int f1 = HIGHp(node);
int f00, f01, f10, f11, hash;
/* Find the cofactors for the new nodes */
if (VAR(f0) == var1)
{
f00 = LOW(f0);
f01 = HIGH(f0);
}
else
f00 = f01 = f0;
if (VAR(f1) == var1)
{
f10 = LOW(f1);
f11 = HIGH(f1);
}
else
f10 = f11 = f1;
/* Note: makenode does refcou. */
f0 = reorder_makenode(var0, f00, f10);
f1 = reorder_makenode(var0, f01, f11);
node = &bddnodes[toBeProcessed]; /* Might change in makenode */
/* We know that the refcou of the grandchilds of this node
* is greater than one (these are f00...f11), so there is
* no need to do a recursive refcou decrease. It is also
* possible for the LOWp(node)/high nodes to come alive again,
* so deref. of the childs is delayed until the local GBC. */
DECREF(LOWp(node));
DECREF(HIGHp(node));
/* Update in-place */
VARp(node) = var1;
LOWp(node) = f0;
HIGHp(node) = f1;
levels[var1].nodenum++;
/* Rehash the node since it got new childs */
hash = NODEHASH(VARp(node), LOWp(node), HIGHp(node));
node->next = bddhash[hash];
bddhash[hash] = toBeProcessed;
toBeProcessed = next;
}
}
/* Now go through the var 1 chains. The nodes live here have survived
* the call to reorder_swap() and may stay in the chain.
* The dead nodes are reclaimed.
*/
static void reorder_localGbc(int var0)
{
int var1 = bddlevel2var[bddvar2level[var0]+1];
int vl1 = levels[var1].start;
int size1 = levels[var1].size;
int n;
for (n=0 ; n<size1 ; n++)
{
int hash = n+vl1;
int r = bddhash[hash];
bddhash[hash] = 0;
while (r)
{
BddNode *node = &bddnodes[r];
int next = node->next;
if (node->refcou > 0)
{
node->next = bddhash[hash];
bddhash[hash] = r;
}
else
{
DECREF(LOWp(node));
DECREF(HIGHp(node));
LOWp(node) = -1;
node->next = bddfreepos;
bddfreepos = r;
levels[var1].nodenum--;
bddfreenum++;
}
r = next;
}
}
}
#ifdef USERESIZE
static void reorder_swapResize(int toBeProcessed, int var0)
{
int var1 = bddlevel2var[bddvar2level[var0]+1];
while (toBeProcessed)
{
BddNode *node = &bddnodes[toBeProcessed];
int next = node->next;
int f0 = LOWp(node);
int f1 = HIGHp(node);
int f00, f01, f10, f11;
/* Find the cofactors for the new nodes */
if (VAR(f0) == var1)
{
f00 = LOW(f0);
f01 = HIGH(f0);
}
else
f00 = f01 = f0;
if (VAR(f1) == var1)
{
f10 = LOW(f1);
f11 = HIGH(f1);
}
else
f10 = f11 = f1;
/* Note: makenode does refcou. */
f0 = reorder_makenode(var0, f00, f10);
f1 = reorder_makenode(var0, f01, f11);
node = &bddnodes[toBeProcessed]; /* Might change in makenode */
/* We know that the refcou of the grandchilds of this node
* is greater than one (these are f00...f11), so there is
* no need to do a recursive refcou decrease. It is also
* possible for the LOWp(node)/high nodes to come alive again,
* so deref. of the childs is delayed until the local GBC. */
DECREF(LOWp(node));
DECREF(HIGHp(node));
/* Update in-place */
VARp(node) = var1;
LOWp(node) = f0;
HIGHp(node) = f1;
levels[var1].nodenum++;
/* Do not rehash yet since we are going to resize the hash table */
toBeProcessed = next;
}
}
static void reorder_localGbcResize(int toBeProcessed, int var0)
{
int var1 = bddlevel2var[bddvar2level[var0]+1];
int vl1 = levels[var1].start;
int size1 = levels[var1].size;
int n;
for (n=0 ; n<size1 ; n++)
{
int hash = n+vl1;
int r = bddhash[hash];
bddhash[hash] = 0;
while (r)
{
BddNode *node = &bddnodes[r];
int next = node->next;
if (node->refcou > 0)
{
node->next = toBeProcessed;
toBeProcessed = r;
}
else
{
DECREF(LOWp(node));
DECREF(HIGHp(node));
LOWp(node) = -1;
node->next = bddfreepos;
bddfreepos = r;
levels[var1].nodenum--;
bddfreenum++;
}
r = next;
}
}
/* Resize */
if (levels[var1].nodenum < levels[var1].size)
levels[var1].size = MIN(levels[var1].maxsize, levels[var1].size/2);
else
levels[var1].size = MIN(levels[var1].maxsize, levels[var1].size*2);
if (levels[var1].size >= 4)
levels[var1].size = bdd_prime_lte(levels[var1].size);
/* Rehash the remaining live nodes */
while (toBeProcessed)
{
BddNode *node = &bddnodes[toBeProcessed];
int next = node->next;
int hash = NODEHASH(VARp(node), LOWp(node), HIGHp(node));
node->next = bddhash[hash];
bddhash[hash] = toBeProcessed;
toBeProcessed = next;
}
}
#endif /* USERESIZE */
static int reorder_varup(int var)
{
if (var < 0 || var >= bddvarnum)
return bdd_error(BDD_VAR);
if (bddvar2level[var] == 0)
return 0;
return reorder_vardown( bddlevel2var[bddvar2level[var]-1]);
}
#if 0
static void sanitycheck(void)
{
int n,v,r;
int cou=0;
for (v=0 ; v<bddvarnum ; v++)
{
int vcou=0;
for (n=0 ; n<levels[v].size ; n++)
{
r = bddhash[n+levels[v].start];
while (r)
{
assert(VAR(r) == v);
r = bddnodes[r].next;
cou++;
vcou++;
}
}
assert(vcou == levels[v].nodenum);
}
for (n=2 ; n<bddnodesize ; n++)
{
if (bddnodes[n].refcou > 0)
{
assert(LEVEL(n) < LEVEL(LOW(n)));
assert(LEVEL(n) < LEVEL(HIGH(n)));
cou--;
}
}
assert(cou == 0);
}
#endif
static int reorder_vardown(int var)
{
int n, level;
if (var < 0 || var >= bddvarnum)
return bdd_error(BDD_VAR);
if ((level=bddvar2level[var]) >= bddvarnum-1)
return 0;
resizedInMakenode = 0;
if (imatrixDepends(iactmtx, var, bddlevel2var[level+1]))
{
int toBeProcessed = reorder_downSimple(var);
#ifdef USERESIZE
levelData *l = &levels[var];
if (l->nodenum < (l->size)/3 ||
l->nodenum >= (l->size*3)/2 && l->size < l->maxsize)
{
reorder_swapResize(toBeProcessed, var);
reorder_localGbcResize(toBeProcessed, var);
}
else
#endif
{
reorder_swap(toBeProcessed, var);
reorder_localGbc(var);
}
}
/* Swap the var<->level tables */
n = bddlevel2var[level];
bddlevel2var[level] = bddlevel2var[level+1];
bddlevel2var[level+1] = n;
n = bddvar2level[var];
bddvar2level[var] = bddvar2level[ bddlevel2var[level] ];
bddvar2level[ bddlevel2var[level] ] = n;
/* Update all rename pairs */
bdd_pairs_vardown(level);
if (resizedInMakenode)
reorder_rehashAll();
return 0;
}
/*************************************************************************
User reordering interface
*************************************************************************/
/*
NAME {* bdd\_swapvar *}
SECTION {* reorder *}
SHORT {* Swap two BDD variables *}
PROTO {* int bdd_swapvar(int v1, int v2) *}
DESCR {* Use {\tt bdd\_swapvar} to swap the position (in the current
variable order) of the two BDD
variables {\tt v1} and {\tt v2}. There are no constraints on the
position of the two variables before the call. This function may
{\em not} be used together with user defined variable blocks.
The swap is done by a series of adjacent variable swaps and
requires the whole node table to be rehashed twice for each call
to {\tt bdd\_swapvar}. It should therefore not be used were
efficiency is a major concern. *}
RETURN {* Zero on succes and a negative error code otherwise. *}
ALSO {* bdd\_reorder, bdd\_addvarblock *}
*/
int bdd_swapvar(int v1, int v2)
{
int l1, l2;
/* Do not swap when variable-blocks are used */
if (vartree != NULL)
return bdd_error(BDD_VARBLK);
/* Don't bother swapping x with x */
if (v1 == v2)
return 0;
/* Make sure the variable exists */
if (v1 < 0 || v1 >= bddvarnum || v2 < 0 || v2 >= bddvarnum)
return bdd_error(BDD_VAR);
l1 = bddvar2level[v1];
l2 = bddvar2level[v2];
/* Make sure v1 is before v2 */
if (l1 > l2)
{
int tmp = v1;
v1 = v2;
v2 = tmp;
l1 = bddvar2level[v1];
l2 = bddvar2level[v2];
}
reorder_init();
/* Move v1 to v2's position */
while (bddvar2level[v1] < l2)
reorder_vardown(v1);
/* Move v2 to v1's position */
while (bddvar2level[v2] > l1)
reorder_varup(v2);
reorder_done();
return 0;
}
void bdd_default_reohandler(int prestate)
{
static long c1;
if (verbose > 0)
{
if (prestate)
{
printf("Start reordering\n");
c1 = clock();
}
else
{
long c2 = clock();
printf("End reordering. Went from %d to %d nodes (%.1f sec)\n",
usednum_before, usednum_after, (float)(c2-c1)/CLOCKS_PER_SEC);
}
}
}
/*
NAME {* bdd\_disable\_reorder *}
SECTION {* reorder *}
SHORT {* Disable automatic reordering *}
PROTO {* void bdd_disable_reorder(void) *}
DESCR {* Disables automatic reordering until {\tt bdd\_enable\_reorder}
is called. Reordering is enabled by default as soon as any variable
blocks have been defined. *}
ALSO {* bdd\_enable\_reorder *}
*/
void bdd_disable_reorder(void)
{
reorderdisabled = 1;
}
/*
NAME {* bdd\_enable\_reorder *}
SECTION {* reorder *}
SHORT {* Enables automatic reordering *}
PROTO {* void bdd_enable_reorder(void) *}
DESCR {* Re-enables reordering after a call to {\tt bdd\_disable\_reorder}. *}
ALSO {* bdd\_disable\_reorder *}
*/
void bdd_enable_reorder(void)
{
reorderdisabled = 0;
}
int bdd_reorder_ready(void)
{
if (bddreordermethod == BDD_REORDER_NONE || vartree == NULL ||
bddreordertimes == 0 || reorderdisabled)
return 0;
return 1;
}
void bdd_reorder_auto(void)
{
if (!bdd_reorder_ready())
return;
if (reorder_handler != NULL)
reorder_handler(1);
bdd_reorder(bddreordermethod);
bddreordertimes--;
if (reorder_handler != NULL)
reorder_handler(0);
}
static int reorder_init(void)
{
int n;
if ((levels=NEW(levelData,bddvarnum)) == NULL)
return -1;
for (n=0 ; n<bddvarnum ; n++)
{
levels[n].start = -1;
levels[n].size = 0;
levels[n].nodenum = 0;
}
/* First mark and recursive refcou. all roots and childs. Also do some
* setup here for both setLevellookup and reorder_gbc */
if (mark_roots() < 0)
return -1;
/* Initialize the hash tables */
reorder_setLevellookup();
/* Garbage collect and rehash to new scheme */
reorder_gbc();
return 0;
}
static void reorder_done(void)
{
int n;
for (n=0 ; n<extrootsize ; n++)
SETMARK(extroots[n]);
for (n=2 ; n<bddnodesize ; n++)
{
if (MARKED(n))
UNMARK(n);
else
bddnodes[n].refcou = 0;
/* This is where we go from .var to .level again!
* - Do NOT use the LEVEL macro here. */
bddnodes[n].level = bddvar2level[bddnodes[n].level];
}
#if 0
for (n=0 ; n<bddvarnum ; n++)
printf("%3d\n", bddlevel2var[n]);
printf("\n");
#endif
#if 0
for (n=0 ; n<bddvarnum ; n++)
printf("%3d: %4d nodes , %4d entries\n", n, levels[n].nodenum,
levels[n].size);
#endif
free(extroots);
free(levels);
imatrixDelete(iactmtx);
bdd_gbc();
}
static int varseqCmp(const void *aa, const void *bb)
{
int a = bddvar2level[*((const int*)aa)];
int b = bddvar2level[*((const int*)bb)];
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
static BddTree *reorder_block(BddTree *t, int method)
{
BddTree *this;
if (t == NULL)
return NULL;
if (t->fixed == BDD_REORDER_FREE && t->nextlevel!=NULL)
{
switch(method)
{
case BDD_REORDER_WIN2:
t->nextlevel = reorder_win2(t->nextlevel);
break;
case BDD_REORDER_WIN2ITE:
t->nextlevel = reorder_win2ite(t->nextlevel);
break;
case BDD_REORDER_SIFT:
t->nextlevel = reorder_sift(t->nextlevel);
break;
case BDD_REORDER_SIFTITE:
t->nextlevel = reorder_siftite(t->nextlevel);
break;
case BDD_REORDER_WIN3:
t->nextlevel = reorder_win3(t->nextlevel);
break;
case BDD_REORDER_WIN3ITE:
t->nextlevel = reorder_win3ite(t->nextlevel);
break;
case BDD_REORDER_RANDOM:
t->nextlevel = reorder_random(t->nextlevel);
break;
}
}
for (this=t->nextlevel ; this ; this=this->next)
reorder_block(this, method);
if (t->seq != NULL)
qsort(t->seq, t->last-t->first+1, sizeof(int), varseqCmp);
return t;
}
/*
NAME {* bdd\_reorder *}
SECTION {* reorder *}
SHORT {* start dynamic reordering *}
PROTO {* void bdd_reorder(int method) *}
DESCR {* This function initiates dynamic reordering using the heuristic
defined by {\tt method}, which may be one of the following
\begin{description}
\item {\tt BDD\_REORDER\_WIN2}\\
Reordering using a sliding window of size 2. This algorithm
swaps two adjacent variable blocks and if this results in
more nodes then the two blocks are swapped back again.
Otherwise the result is kept in the variable order. This is
then repeated for all variable blocks.
\item {\tt BDD\_REORDER\_WIN2ITE}\\
The same as above but the process is repeated until no further
progress is done. Usually a fast and efficient method.
\item {\tt BDD\_REORDER\_WIN3}\\
The same as above but with a window size of 3.
\item {\tt BDD\_REORDER\_WIN2ITE}\\
The same as above but with a window size of 3.
\item {\tt BDD\_REORDER\_SIFT}\\
Reordering where each block is moved through all possible
positions. The best of these is then used as the new position.
Potentially a very slow but good method.
\item {\tt BDD\_REORDER\_SIFTITE}\\
The same as above but the process is repeated until no further
progress is done. Can be extremely slow.
\item {\tt BDD\_REORDER\_RANDOM}\\
Mostly used for debugging purpose, but may be usefull for
others. Selects a random position for each variable.
\end{description}
*}
ALSO {* bdd\_autoreorder, bdd\_reorder\_verbose, bdd\_addvarblock, bdd\_clrvarblocks *}
*/
void bdd_reorder(int method)
{
BddTree *top;
int savemethod = bddreordermethod;
int savetimes = bddreordertimes;
bddreordermethod = method;
bddreordertimes = 1;
if ((top=bddtree_new(-1)) == NULL)
return;
if (reorder_init() < 0)
return;
usednum_before = bddnodesize - bddfreenum;
top->first = 0;
top->last = bdd_varnum()-1;
top->fixed = 0;
top->next = NULL;
top->nextlevel = vartree;
reorder_block(top, method);
vartree = top->nextlevel;
free(top);
usednum_after = bddnodesize - bddfreenum;
reorder_done();
bddreordermethod = savemethod;
bddreordertimes = savetimes;
}
/*
NAME {* bdd\_reorder\_gain *}
SECTION {* reorder *}
SHORT {* Calculate the gain in size after a reordering *}
PROTO {* int bdd_reorder_gain(void) *}
DESCR {* Returns the gain in percent of the previous number of used
nodes. The value returned is
\[ (100 * (A - B)) / A \]
Where $A$ is previous number of used nodes and $B$ is current
number of used nodes.
*}
*/
int bdd_reorder_gain(void)
{
if (usednum_before == 0)
return 0;
return (100*(usednum_before - usednum_after)) / usednum_before;
}
/*
NAME {* bdd\_reorder\_hook *}
SECTION {* reorder *}
SHORT {* sets a handler for automatic reorderings *}
PROTO {* bddinthandler bdd_reorder_hook(bddinthandler handler) *}
DESCR {* Whenever automatic reordering is done, a check is done to see
if the user has supplied a handler for that event. If so then
it is called with the argument {\tt prestate} being 1 if the
handler is called immediately {\em before} reordering and
{\tt prestate} being 0 if it is called immediately after.
The default handler is
{\tt bdd\_default\_reohandler} which will print information
about the reordering.
A typical handler could look like this:
\begin{verbatim}
void reorderhandler(int prestate)
{
if (prestate)
printf("Start reordering");
else
printf("End reordering");
}
\end{verbatim} *}
RETURN {* The previous handler *}
ALSO {* bdd\_reorder, bdd\_autoreorder, bdd\_resize\_hook *}
*/
bddinthandler bdd_reorder_hook(bddinthandler handler)
{
bddinthandler tmp = reorder_handler;
reorder_handler = handler;
return tmp;
}
/*
NAME {* bdd\_blockfile\_hook *}
SECTION {* reorder *}
SHORT {* Specifies a printing callback handler *}
PROTO {* bddfilehandler bdd_blockfile_hook(bddfilehandler handler) *}
DESCR {* A printing callback handler is used to convert the variable
block identifiers into something readable by the end user. Use
{\tt bdd\_blockfile\_hook} to pass a handler to BuDDy. A typical
handler could look like this:
\begin{verbatim}
void printhandler(FILE *o, int block)
{
extern char **blocknames;
fprintf(o, "%s", blocknames[block]);
}
\end{verbatim}
\noindent
The handler is then called from {\tt bdd\_printorder} and
{\tt bdd\_reorder} (depending on the verbose level) with
the block numbers returned by {\tt bdd\_addvarblock} as arguments.
No default handler is supplied. The argument {\tt handler} may be
NULL if no handler is needed. *}
RETURN {* The old handler *}
ALSO {* bdd\_printorder *}
*/
bddfilehandler bdd_blockfile_hook(bddfilehandler handler)
{
bddfilehandler tmp = reorder_filehandler;
reorder_filehandler = handler;
return tmp;
}
/*
NAME {* bdd\_autoreorder *}
EXTRA {* bdd\_autoreorder\_times *}
SECTION {* reorder *}
SHORT {* enables automatic reordering *}
PROTO {* int bdd_autoreorder(int method)
int bdd_autoreorder_times(int method, int num) *}
DESCR {* Enables automatic reordering using {\tt method} as the reordering
method. If {\tt method} is {\tt BDD\_REORDER\_NONE} then
automatic reordering is disabled. Automatic
reordering is done every time the number of active nodes in the
node table has been doubled and works by interrupting the current
BDD operation, doing the reordering and the retrying the operation.
In the second form the argument {\tt num} specifies the allowed
number of reorderings. So if for example a "one shot" reordering
is needed, then the {\tt num} argument would be set to one.
Values for {\tt method} can be found under {\tt bdd\_reorder}.
*}
RETURN {* Returns the old value of {\tt method} *}
ALSO {* bdd\_reorder *}
*/
int bdd_autoreorder(int method)
{
int tmp = bddreordermethod;
bddreordermethod = method;
bddreordertimes = -1;
return tmp;
}
int bdd_autoreorder_times(int method, int num)
{
int tmp = bddreordermethod;
bddreordermethod = method;
bddreordertimes = num;
return tmp;
}
/*
NAME {* bdd\_var2level *}
SECTION {* reorder *}
SHORT {* Fetch the level of a specific BDD variable *}
PROTO {* int bdd_var2level(int var) *}
DESCR {* Returns the position of the variable {\tt var} in the current
variable order. *}
ALSO {* bdd\_reorder, bdd\_level2var *}
*/
int bdd_var2level(int var)
{
if (var < 0 || var >= bddvarnum)
return bdd_error(BDD_VAR);
return bddvar2level[var];
}
/*
NAME {* bdd\_level2var *}
SECTION {* reorder *}
SHORT {* Fetch the variable number of a specific level *}
PROTO {* int bdd_level2var(int level) *}
DESCR {* Returns the variable placed at position {\tt level} in the
current variable order. *}
ALSO {* bdd\_reorder, bdd\_var2level *}
*/
int bdd_level2var(int level)
{
if (level < 0 || level >= bddvarnum)
return bdd_error(BDD_VAR);
return bddlevel2var[level];
}
/*
NAME {* bdd\_getreorder\_times *}
SECTION {* reorder *}
SHORT {* Fetch the current number of allowed reorderings *}
PROTO {* int bdd_getreorder_times(void) *}
DESCR {* Returns the current number of allowed reorderings left. This
value can be defined by {\tt bdd\_autoreorder\_times}. *}
ALSO {* bdd\_reorder\_times, bdd\_getreorder\_method *}
*/
int bdd_getreorder_times(void)
{
return bddreordertimes;
}
/*
NAME {* bdd\_getreorder\_method *}
SECTION {* reorder *}
SHORT {* Fetch the current reorder method *}
PROTO {* int bdd_getreorder_method(void) *}
DESCR {* Returns the current reorder method as defined by
{\tt bdd\_autoreorder}. *}
ALSO {* bdd\_reorder, bdd\_getreorder\_times *}
*/
int bdd_getreorder_method(void)
{
return bddreordermethod;
}
/*
NAME {* bdd\_reorder\_verbose *}
SECTION {* reorder *}
SHORT {* enables verbose information about reorderings *}
PROTO {* int bdd_reorder_verbose(int v) *}
DESCR {* With {\tt bdd\_reorder\_verbose} it is possible to set the level
of information which should be printed during reordering. A value
of zero means no information, a value of one means some information
and any greater value will result in a lot of reordering
information. The default value is zero. *}
RETURN {* The old verbose level *}
ALSO {* bdd\_reorder *}
*/
int bdd_reorder_verbose(int v)
{
int tmp = verbose;
verbose = v;
return tmp;
}
/*
NAME {* bdd\_reorder\_probe *}
SECTION {* reorder *}
SHORT {* Define a handler for minimization of BDDs *}
PROTO {* bddsizehandler bdd_reorder_probe(bddsizehandler handler) *}
DESCR {* Reordering is typically done to minimize the global number of
BDD nodes in use, but it may in some cases be usefull to minimize
with respect to a specific BDD. With {\tt bdd\_reorder\_probe} it
is possible to define a callback function that calculates the
size of a specific BDD (or anything else in fact). This handler
will then be called by the reordering functions to get the current
size information. A typical handle could look like this:
\begin{verbatim}
int sizehandler(void)
{
extern BDD mybdd;
return bdd_nodecount(mybdd);
}
\end{verbatim}
No default handler is supplied. The argument {\tt handler} may be
NULL if no handler is needed. *}
*}
RETURN {* The old handler *}
ALSO {* bdd\_reorder *}
*/
bddsizehandler bdd_reorder_probe(bddsizehandler handler)
{
bddsizehandler old = reorder_nodenum;
if (handler == NULL)
return reorder_nodenum;
reorder_nodenum = handler;
return old;
}
/*
NAME {* bdd\_clrvarblocks *}
SECTION {* reorder *}
SHORT {* clears all variable blocks *}
PROTO {* void bdd_clrvarblocks(void) *}
DESCR {* Clears all the variable blocks that has been defined by calls
to bdd\_addvarblock. *}
ALSO {* bdd\_addvarblock *}
*/
void bdd_clrvarblocks(void)
{
bddtree_del(vartree);
vartree = NULL;
blockid = 0;
}
/*
NAME {* bdd\_addvarblock *}
EXTRA {* bdd\_intaddvarblock *}
SECTION {* reorder *}
SHORT {* adds a new variable block for reordering *}
PROTO {* int bdd_addvarblock(BDD var, int fixed)
int bdd_intaddvarblock(int first, int last, int fixed) *}
DESCR {* Creates a new variable block with the variables in the variable
set {\tt var}. The variables in {\tt var} must be contiguous.
In the second form the argument {\tt first} is the first variable
included in the block and {\tt last} is the last variable included
in the block. This order does not depend on current variable
order.
The variable blocks are ordered as a tree, with the largest
ranges at top and the smallest at the bottom. Example: Assume
the block 0-9 is added as the first block and then the block 0-6.
This yields the 0-9 block at the top, with the 0-6 block as a
child. If now the block 2-4 was added, it would become a child
of the 0-6 block. A block of 0-8 would be a child of the 0-9
block and have the 0-6 block as a child. Partially overlapping
blocks are not allowed.
The {\tt fixed} parameter sets the block to be fixed (no
reordering of its child blocks is allowed) or free, using
the constants {\tt BDD\_REORDER\_FIXED} and {\tt
BDD\_REORDER\_FREE}. Reordering is always done on the top
most blocks first and then recursively downwards.
The return value is an integer that can be used to identify
the block later on - with for example {\tt bdd\_blockfile\_hook}.
The values returned will be in the sequence $0,1,2,3,\ldots$.
*}
RETURN {* A non-negative identifier on success, otherwise a negative error code. *}
ALSO {* bdd\_varblockall, fdd\_intaddvarblock, bdd\_clrvarblocks *} */
int bdd_addvarblock(BDD b, int fixed)
{
BddTree *t;
int n, *v, size;
int first, last;
if ((n=bdd_scanset(b, &v, &size)) < 0)
return n;
if (size < 1)
return bdd_error(BDD_VARBLK);
first = last = v[0];
for (n=0 ; n<size ; n++)
{
if (v[n] < first)
first = v[n];
if (v[n] > last)
last = v[n];
}
if ((t=bddtree_addrange(vartree, first,last, fixed,blockid)) == NULL)
return bdd_error(BDD_VARBLK);
vartree = t;
return blockid++;
}
int bdd_intaddvarblock(int first, int last, int fixed)
{
BddTree *t;
if (first < 0 || first >= bddvarnum || last < 0 || last >= bddvarnum)
return bdd_error(BDD_VAR);
if ((t=bddtree_addrange(vartree, first,last, fixed,blockid)) == NULL)
return bdd_error(BDD_VARBLK);
vartree = t;
return blockid++;
}
/*
NAME {* bdd\_varblockall *}
SECTION {* reorder *}
SHORT {* add a variable block for all variables *}
PROTO {* void bdd_varblockall(void) *}
DESCR {* Adds a variable block for all BDD variables declared so far.
Each block contains one variable only. More variable blocks
can be added later with the use of {\tt bdd\_addvarblock} --
in this case the tree of variable blocks will have the blocks
of single variables as the leafs. *}
ALSO {* bdd\_addvarblock, bdd\_intaddvarblock *}
*/
void bdd_varblockall(void)
{
int n;
for (n=0 ; n<bddvarnum ; n++)
bdd_intaddvarblock(n,n,1);
}
/*
NAME {* bdd\_printorder *}
SECTION {* reorder *}
SHORT {* prints the current order *}
PROTO {* void bdd_printorder(void)
bdd_fprint_order(FILE *f)*}
DESCR {* Prints an indented list of the variable blocks, showing the top
most blocks to the left and the lower blocks to the right.
Example:\\
\begin{verbatim}
2{
0
1
2}
3
4
\end{verbatim}
This shows 5 variable blocks. The first one added is block zero,
which is on the same level as block one. These two blocks are then
sub-blocks of block two and block two is on the same level as
block three and four. The numbers are the identifiers returned
from {\tt bdd\_addvarblock}. The block levels depends on the
variables included in the blocks.
*}
ALSO {* bdd\_reorder, bdd\_addvarblock *}
*/
void bdd_printorder(void)
{
bdd_fprintorder(stdout);
}
/*
NAME {* bdd\_setvarorder *}
SECTION {* reorder *}
SHORT {* set a specific variable order *}
PROTO {* void bdd_setvarorder(int *neworder) *}
DESCR {* This function sets the current variable order to be the one
defined by {\tt neworder}. The parameter {\tt neworder} is
interpreted as a sequence of variable indecies and the new
variable order is exactly this sequence. The array {\em must}
contain all the variables defined so far. If for instance the
current number of variables is 3 and {\tt neworder} contains
$[1,0,2]$ then the new variable order is $v_1 < v_0 < v_2$. *}
ALSO {* bdd\_reorder, bdd\_printorder *}
*/
void bdd_setvarorder(int *neworder)
{
int level;
/* Do not set order when variable-blocks are used */
if (vartree != NULL)
{
bdd_error(BDD_VARBLK);
return;
}
reorder_init();
for (level=0 ; level<bddvarnum ; level++)
{
int lowvar = neworder[level];
while (bddvar2level[lowvar] > level)
reorder_varup(lowvar);
}
reorder_done();
}
static void print_order_rec(FILE *o, BddTree *t, int level)
{
if (t == NULL)
return;
if (t->nextlevel)
{
fprintf(o, "%*s", level*3, "");
if (reorder_filehandler)
reorder_filehandler(o,t->id);
else
fprintf(o, "%3d", t->id);
fprintf(o, "{\n");
print_order_rec(o, t->nextlevel, level+1);
fprintf(o, "%*s", level*3, "");
if (reorder_filehandler)
reorder_filehandler(o,t->id);
else
fprintf(o, "%3d", t->id);
fprintf(o, "}\n");
print_order_rec(o, t->next, level);
}
else
{
fprintf(o, "%*s", level*3, "");
if (reorder_filehandler)
reorder_filehandler(o,t->id);
else
fprintf(o, "%3d", t->id);
fprintf(o, "\n");
print_order_rec(o, t->next, level);
}
}
void bdd_fprintorder(FILE *ofile)
{
print_order_rec(ofile, vartree, 0);
}
/* EOF */