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.
904 lines
26 KiB
904 lines
26 KiB
/**
|
|
@file
|
|
|
|
@ingroup cudd
|
|
|
|
@brief Genetic algorithm for variable reordering.
|
|
|
|
@details The genetic algorithm implemented here is as follows. We
|
|
start with the current %DD order. We sift this order and use this as
|
|
the reference %DD. We only keep 1 %DD around for the entire process
|
|
and simply rearrange the order of this %DD, storing the various
|
|
orders and their corresponding %DD sizes. We generate more random
|
|
orders to build an initial population. This initial population is 3
|
|
times the number of variables, with a maximum of 120. Each random
|
|
order is built (from the reference %DD) and its size stored. Each
|
|
random order is also sifted to keep the %DD sizes fairly small. Then
|
|
a crossover is performed between two orders (picked randomly) and
|
|
the two resulting DDs are built and sifted. For each new order, if
|
|
its size is smaller than any %DD in the population, it is inserted
|
|
into the population and the %DD with the largest number of nodes is
|
|
thrown out. The crossover process happens up to 50 times, and at
|
|
this point the %DD in the population with the smallest size is chosen
|
|
as the result. This %DD must then be built from the reference %DD.
|
|
|
|
@author Curt Musfeldt, Alan Shuler, 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 */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
typedef struct GeneticInfo GeneticInfo_t;
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Miscellaneous information.
|
|
*/
|
|
struct GeneticInfo {
|
|
int popsize; /**< the size of the population */
|
|
int numvars; /**< the number of variables to be ordered. */
|
|
/**
|
|
** @brief storedd stores the population orders and sizes.
|
|
**
|
|
** @details This table has two extra rows and one extras column. The
|
|
** two extra rows are used for the offspring produced by a
|
|
** crossover. Each row stores one order and its size. The order is
|
|
** stored by storing the indices of variables in the order in which
|
|
** they appear in the order. The table is in reality a
|
|
** one-dimensional array which is accessed via a macro to give the
|
|
** illusion it is a two-dimensional structure.
|
|
*/
|
|
int *storedd;
|
|
st_table *computed; /**< hash table to identify existing orders */
|
|
int *repeat; /**< how many times an order is present */
|
|
int large; /**< stores the index of the population with
|
|
** the largest number of nodes in the %DD */
|
|
int result; /**< result */
|
|
int cross; /**< the number of crossovers to perform */
|
|
};
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
** @brief Used to access the population table as if it were a
|
|
** two-dimensional structure.
|
|
*/
|
|
#define STOREDD(info,i,j) (info)->storedd[(i)*((info)->numvars+1)+(j)]
|
|
|
|
/** \cond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int make_random (DdManager *table, int lower, GeneticInfo_t * info);
|
|
static int sift_up (DdManager *table, int x, int x_low);
|
|
static int build_dd (DdManager *table, int num, int lower, int upper, GeneticInfo_t * info);
|
|
static int largest (GeneticInfo_t * info);
|
|
static int rand_int (DdManager * dd, int a);
|
|
static int array_hash (void const *array, int modulus, void const * arg);
|
|
static int array_compare (const void *array1, const void *array2, void const * arg);
|
|
static int find_best (GeneticInfo_t * info);
|
|
#ifdef DD_STATS
|
|
static double find_average_fitness (GeneticInfo_t * info);
|
|
#endif
|
|
static int PMX (DdManager * dd, int maxvar, GeneticInfo_t * info);
|
|
static int roulette (DdManager *dd, int *p1, int *p2, GeneticInfo_t * info);
|
|
|
|
/** \endcond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Genetic algorithm for %DD reordering.
|
|
|
|
@details The two children of a crossover will be stored in
|
|
storedd[popsize] and storedd[popsize+1] --- the last two slots in the
|
|
storedd array. (This will make comparisons and replacement easy.)
|
|
|
|
@return 1 in case of success; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
int
|
|
cuddGa(
|
|
DdManager * table /**< manager */,
|
|
int lower /**< lowest level to be reordered */,
|
|
int upper /**< highest level to be reorderded */)
|
|
{
|
|
int i,n,m; /* dummy/loop vars */
|
|
int index;
|
|
#ifdef DD_STATS
|
|
double average_fitness;
|
|
#endif
|
|
int small; /* index of smallest DD in population */
|
|
GeneticInfo_t info;
|
|
|
|
/* Do an initial sifting to produce at least one reasonable individual. */
|
|
if (!cuddSifting(table,lower,upper)) return(0);
|
|
|
|
/* Get the initial values. */
|
|
info.numvars = upper - lower + 1; /* number of variables to be reordered */
|
|
if (table->populationSize == 0) {
|
|
info.popsize = 3 * info.numvars; /* population size is 3 times # of vars */
|
|
if (info.popsize > 120) {
|
|
info.popsize = 120; /* Maximum population size is 120 */
|
|
}
|
|
} else {
|
|
info.popsize = table->populationSize; /* user specified value */
|
|
}
|
|
if (info.popsize < 4) info.popsize = 4; /* enforce minimum population size */
|
|
|
|
/* Allocate population table. */
|
|
info.storedd = ALLOC(int,(info.popsize+2)*(info.numvars+1));
|
|
if (info.storedd == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
return(0);
|
|
}
|
|
|
|
/* Initialize the computed table. This table is made up of two data
|
|
** structures: A hash table with the key given by the order, which says
|
|
** if a given order is present in the population; and the repeat
|
|
** vector, which says how many copies of a given order are stored in
|
|
** the population table. If there are multiple copies of an order, only
|
|
** one has a repeat count greater than 1. This copy is the one pointed
|
|
** by the computed table.
|
|
*/
|
|
info.repeat = ALLOC(int,info.popsize);
|
|
if (info.repeat == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(info.storedd);
|
|
return(0);
|
|
}
|
|
for (i = 0; i < info.popsize; i++) {
|
|
info.repeat[i] = 0;
|
|
}
|
|
info.computed = st_init_table_with_arg(array_compare,array_hash,
|
|
(void *)(ptrint) info.numvars);
|
|
if (info.computed == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
return(0);
|
|
}
|
|
|
|
/* Copy the current DD and its size to the population table. */
|
|
for (i = 0; i < info.numvars; i++) {
|
|
STOREDD(&info,0,i) = table->invperm[i+lower]; /* order of initial DD */
|
|
}
|
|
STOREDD(&info,0,info.numvars) =
|
|
(int) (table->keys - table->isolated); /* size of initial DD */
|
|
|
|
/* Store the initial order in the computed table. */
|
|
if (st_insert(info.computed,info.storedd,(void *) 0) == ST_OUT_OF_MEM) {
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
info.repeat[0]++;
|
|
|
|
/* Insert the reverse order as second element of the population. */
|
|
for (i = 0; i < info.numvars; i++) {
|
|
STOREDD(&info,1,info.numvars-1-i) = table->invperm[i+lower]; /* reverse order */
|
|
}
|
|
|
|
/* Now create the random orders. make_random fills the population
|
|
** table with random permutations. The successive loop builds and sifts
|
|
** the DDs for the reverse order and each random permutation, and stores
|
|
** the results in the computed table.
|
|
*/
|
|
if (!make_random(table,lower,&info)) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
for (i = 1; i < info.popsize; i++) {
|
|
info.result = build_dd(table,i,lower,upper,&info); /* build and sift order */
|
|
if (!info.result) {
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
if (st_lookup_int(info.computed,&STOREDD(&info,i,0),&index)) {
|
|
info.repeat[index]++;
|
|
} else {
|
|
if (st_insert(info.computed,&STOREDD(&info,i,0),(void *)(ptruint)i) ==
|
|
ST_OUT_OF_MEM) {
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
info.repeat[i]++;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
#ifdef DD_STATS
|
|
/* Print the initial population. */
|
|
(void) fprintf(table->out,"Initial population after sifting\n");
|
|
for (m = 0; m < info.popsize; m++) {
|
|
for (i = 0; i < info.numvars; i++) {
|
|
(void) fprintf(table->out," %2d",STOREDD(&info,m,i));
|
|
}
|
|
(void) fprintf(table->out," : %3d (%d)\n",
|
|
STOREDD(&info,m,numvars),info.repeat[m]);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef DD_STATS
|
|
small = find_best(&info);
|
|
average_fitness = find_average_fitness(&info);
|
|
(void) fprintf(table->out,"\nInitial population: best fitness = %d, average fitness %8.3f",STOREDD(&info,small,info.numvars),average_fitness);
|
|
#endif
|
|
|
|
/* Decide how many crossovers should be tried. */
|
|
if (table->numberXovers == 0) {
|
|
info.cross = 3*info.numvars;
|
|
if (info.cross > 60) { /* do a maximum of 50 crossovers */
|
|
info.cross = 60;
|
|
}
|
|
} else {
|
|
info.cross = table->numberXovers; /* use user specified value */
|
|
}
|
|
if (info.cross >= info.popsize) {
|
|
info.cross = info.popsize;
|
|
}
|
|
|
|
/* Perform the crossovers to get the best order. */
|
|
for (m = 0; m < info.cross; m++) {
|
|
if (!PMX(table, table->size, &info)) { /* perform one crossover */
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
/* The offsprings are left in the last two entries of the
|
|
** population table. These are now considered in turn.
|
|
*/
|
|
for (i = info.popsize; i <= info.popsize+1; i++) {
|
|
info.result = build_dd(table,i,lower,upper,&info); /* build and sift child */
|
|
if (!info.result) {
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
info.large = largest(&info); /* find the largest DD in population */
|
|
|
|
/* If the new child is smaller than the largest DD in the current
|
|
** population, enter it into the population in place of the
|
|
** largest DD.
|
|
*/
|
|
if (STOREDD(&info,i,info.numvars) <
|
|
STOREDD(&info,info.large,info.numvars)) {
|
|
/* Look up the largest DD in the computed table.
|
|
** Decrease its repetition count. If the repetition count
|
|
** goes to 0, remove the largest DD from the computed table.
|
|
*/
|
|
info.result = st_lookup_int(info.computed,&STOREDD(&info,info.large,0),&index);
|
|
if (!info.result) {
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
info.repeat[index]--;
|
|
if (info.repeat[index] == 0) {
|
|
int *pointer = &STOREDD(&info,index,0);
|
|
info.result = st_delete(info.computed, (void **) &pointer, NULL);
|
|
if (!info.result) {
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
}
|
|
/* Copy the new individual to the entry of the
|
|
** population table just made available and update the
|
|
** computed table.
|
|
*/
|
|
for (n = 0; n <= info.numvars; n++) {
|
|
STOREDD(&info,info.large,n) = STOREDD(&info,i,n);
|
|
}
|
|
if (st_lookup_int(info.computed,&STOREDD(&info,info.large,0),&index)) {
|
|
info.repeat[index]++;
|
|
} else {
|
|
if (st_insert(info.computed,&STOREDD(&info,info.large,0),
|
|
(void *)(ptruint)info.large) == ST_OUT_OF_MEM) {
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
st_free_table(info.computed);
|
|
return(0);
|
|
}
|
|
info.repeat[info.large]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find the smallest DD in the population and build it;
|
|
** that will be the result.
|
|
*/
|
|
small = find_best(&info);
|
|
|
|
/* Print stats on the final population. */
|
|
#ifdef DD_STATS
|
|
average_fitness = find_average_fitness(&info);
|
|
(void) fprintf(table->out,"\nFinal population: best fitness = %d, average fitness %8.3f",STOREDD(&info,small,info.numvars),average_fitness);
|
|
#endif
|
|
|
|
/* Clean up, build the result DD, and return. */
|
|
st_free_table(info.computed);
|
|
info.computed = NULL;
|
|
info.result = build_dd(table,small,lower,upper,&info);
|
|
FREE(info.storedd);
|
|
FREE(info.repeat);
|
|
return(info.result);
|
|
|
|
} /* end of cuddGa */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
@brief Generates the random sequences for the initial population.
|
|
|
|
@details The sequences are permutations of the indices between lower
|
|
and upper in the current order.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
make_random(
|
|
DdManager * table,
|
|
int lower,
|
|
GeneticInfo_t * info)
|
|
{
|
|
int i,j; /* loop variables */
|
|
int *used; /* is a number already in a permutation */
|
|
int next; /* next random number without repetitions */
|
|
|
|
used = ALLOC(int,info->numvars);
|
|
if (used == NULL) {
|
|
table->errorCode = CUDD_MEMORY_OUT;
|
|
return(0);
|
|
}
|
|
#if 0
|
|
#ifdef DD_STATS
|
|
(void) fprintf(table->out,"Initial population before sifting\n");
|
|
for (i = 0; i < 2; i++) {
|
|
for (j = 0; j < numvars; j++) {
|
|
(void) fprintf(table->out," %2d",STOREDD(i,j));
|
|
}
|
|
(void) fprintf(table->out,"\n");
|
|
}
|
|
#endif
|
|
#endif
|
|
for (i = 2; i < info->popsize; i++) {
|
|
for (j = 0; j < info->numvars; j++) {
|
|
used[j] = 0;
|
|
}
|
|
/* Generate a permutation of {0...numvars-1} and use it to
|
|
** permute the variables in the layesr from lower to upper.
|
|
*/
|
|
for (j = 0; j < info->numvars; j++) {
|
|
do {
|
|
next = rand_int(table,info->numvars-1);
|
|
} while (used[next] != 0);
|
|
used[next] = 1;
|
|
STOREDD(info,i,j) = table->invperm[next+lower];
|
|
}
|
|
#if 0
|
|
#ifdef DD_STATS
|
|
/* Print the order just generated. */
|
|
for (j = 0; j < numvars; j++) {
|
|
(void) fprintf(table->out," %2d",STOREDD(i,j));
|
|
}
|
|
(void) fprintf(table->out,"\n");
|
|
#endif
|
|
#endif
|
|
}
|
|
FREE(used);
|
|
return(1);
|
|
|
|
} /* end of make_random */
|
|
|
|
|
|
/**
|
|
@brief Moves one variable up.
|
|
|
|
@details Takes a variable from position x and sifts it up to
|
|
position x_low; x_low should be less than x.
|
|
|
|
@return 1 if successful; 0 otherwise
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
sift_up(
|
|
DdManager * table,
|
|
int x,
|
|
int x_low)
|
|
{
|
|
int y;
|
|
int size;
|
|
|
|
y = cuddNextLow(table,x);
|
|
while (y >= x_low) {
|
|
size = cuddSwapInPlace(table,y,x);
|
|
if (size == 0) {
|
|
return(0);
|
|
}
|
|
x = y;
|
|
y = cuddNextLow(table,x);
|
|
}
|
|
return(1);
|
|
|
|
} /* end of sift_up */
|
|
|
|
|
|
/**
|
|
@brief Builds a %DD from a given order.
|
|
|
|
@details This procedure also sifts the final order and inserts into
|
|
the array the size in nodes of the result.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
build_dd(
|
|
DdManager * table,
|
|
int num /* the index of the individual to be built */,
|
|
int lower,
|
|
int upper,
|
|
GeneticInfo_t * info)
|
|
{
|
|
int i,j; /* loop vars */
|
|
int position;
|
|
int index;
|
|
int limit; /* how large the DD for this order can grow */
|
|
int size;
|
|
|
|
/* Check the computed table. If the order already exists, it
|
|
** suffices to copy the size from the existing entry.
|
|
*/
|
|
if (info->computed &&
|
|
st_lookup_int(info->computed,&STOREDD(info,num,0),&index)) {
|
|
STOREDD(info,num,info->numvars) = STOREDD(info,index,info->numvars);
|
|
#ifdef DD_STATS
|
|
(void) fprintf(table->out,"\nCache hit for index %d", index);
|
|
#endif
|
|
return(1);
|
|
}
|
|
|
|
/* Stop if the DD grows 20 times larges than the reference size. */
|
|
limit = 20 * STOREDD(info,0,info->numvars);
|
|
|
|
/* Sift up the variables so as to build the desired permutation.
|
|
** First the variable that has to be on top is sifted to the top.
|
|
** Then the variable that has to occupy the secon position is sifted
|
|
** up to the second position, and so on.
|
|
*/
|
|
for (j = 0; j < info->numvars; j++) {
|
|
i = STOREDD(info,num,j);
|
|
position = table->perm[i];
|
|
info->result = sift_up(table,position,j+lower);
|
|
if (!info->result) return(0);
|
|
size = (int) (table->keys - table->isolated);
|
|
if (size > limit) break;
|
|
}
|
|
|
|
/* Sift the DD just built. */
|
|
#ifdef DD_STATS
|
|
(void) fprintf(table->out,"\n");
|
|
#endif
|
|
info->result = cuddSifting(table,lower,upper);
|
|
if (!info->result) return(0);
|
|
|
|
/* Copy order and size to table. */
|
|
for (j = 0; j < info->numvars; j++) {
|
|
STOREDD(info,num,j) = table->invperm[lower+j];
|
|
}
|
|
STOREDD(info,num,info->numvars) = (int) (table->keys - table->isolated); /* size of new DD */
|
|
return(1);
|
|
|
|
} /* end of build_dd */
|
|
|
|
|
|
/**
|
|
@brief Finds the largest %DD in the population.
|
|
|
|
@details If an order is repeated, it avoids choosing the copy that
|
|
is in the computed table (it has repeat[i > 1).]
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
largest(GeneticInfo_t * info)
|
|
{
|
|
int i; /* loop var */
|
|
int big; /* temporary holder to return result */
|
|
|
|
big = 0;
|
|
while (info->repeat[big] > 1) big++;
|
|
for (i = big + 1; i < info->popsize; i++) {
|
|
if (STOREDD(info,i,info->numvars) >=
|
|
STOREDD(info,big,info->numvars) && info->repeat[i] <= 1) {
|
|
big = i;
|
|
}
|
|
}
|
|
return(big);
|
|
|
|
} /* end of largest */
|
|
|
|
|
|
/**
|
|
@brief Generates a random number between 0 and the integer a.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
rand_int(
|
|
DdManager *dd,
|
|
int a)
|
|
{
|
|
return(Cudd_Random(dd) % (a+1));
|
|
|
|
} /* end of rand_int */
|
|
|
|
|
|
/**
|
|
@brief Hash function for the computed table.
|
|
|
|
@return the bucket number.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
array_hash(
|
|
void const * array,
|
|
int modulus,
|
|
void const * arg)
|
|
{
|
|
int val = 0;
|
|
int i;
|
|
int const *intarray = (int const *) array;
|
|
int const numvars = (int const)(ptrint const) arg;
|
|
|
|
for (i = 0; i < numvars; i++) {
|
|
val = val * 997 + intarray[i];
|
|
}
|
|
|
|
return(((val < 0) ? -val : val) % modulus);
|
|
|
|
} /* end of array_hash */
|
|
|
|
|
|
/**
|
|
@brief Comparison function for the computed table.
|
|
|
|
@return 0 if the two arrays are equal; 1 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
array_compare(
|
|
void const * array1,
|
|
void const * array2,
|
|
void const * arg)
|
|
{
|
|
int i;
|
|
int const *intarray1 = (int const *) array1;
|
|
int const *intarray2 = (int const *) array2;
|
|
int const numvars = (int const)(ptrint const) arg;
|
|
|
|
for (i = 0; i < numvars; i++) {
|
|
if (intarray1[i] != intarray2[i]) return(1);
|
|
}
|
|
return(0);
|
|
|
|
} /* end of array_compare */
|
|
|
|
|
|
/**
|
|
@brief Returns the index of the fittest individual.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
find_best(GeneticInfo_t * info)
|
|
{
|
|
int i,small;
|
|
|
|
small = 0;
|
|
for (i = 1; i < info->popsize; i++) {
|
|
if (STOREDD(info,i,info->numvars) < STOREDD(info,small,info->numvars)) {
|
|
small = i;
|
|
}
|
|
}
|
|
return(small);
|
|
|
|
} /* end of find_best */
|
|
|
|
|
|
/**
|
|
@brief Returns the average fitness of the population.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
#ifdef DD_STATS
|
|
static double
|
|
find_average_fitness(GeneticInfo_t * info)
|
|
{
|
|
int i;
|
|
int total_fitness = 0;
|
|
double average_fitness;
|
|
|
|
for (i = 0; i < info->popsize; i++) {
|
|
total_fitness += STOREDD(info,i,info->numvars);
|
|
}
|
|
average_fitness = (double) total_fitness / (double) info->popsize;
|
|
return(average_fitness);
|
|
|
|
} /* end of find_average_fitness */
|
|
#endif
|
|
|
|
|
|
/**
|
|
@brief Performs the crossover between two parents.
|
|
|
|
@details Performs the crossover between two randomly chosen
|
|
parents, and creates two children, x1 and x2. Uses the Partially
|
|
Matched Crossover operator.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
PMX(
|
|
DdManager * dd,
|
|
int maxvar,
|
|
GeneticInfo_t * info)
|
|
{
|
|
int cut1,cut2; /* the two cut positions (random) */
|
|
int mom,dad; /* the two randomly chosen parents */
|
|
int *inv1; /* inverse permutations for repair algo */
|
|
int *inv2;
|
|
int i; /* loop vars */
|
|
int u,v; /* aux vars */
|
|
|
|
inv1 = ALLOC(int,maxvar);
|
|
if (inv1 == NULL) {
|
|
return(0);
|
|
}
|
|
inv2 = ALLOC(int,maxvar);
|
|
if (inv2 == NULL) {
|
|
FREE(inv1);
|
|
return(0);
|
|
}
|
|
|
|
/* Choose two orders from the population using roulette wheel. */
|
|
if (!roulette(dd,&mom,&dad,info)) {
|
|
FREE(inv1);
|
|
FREE(inv2);
|
|
return(0);
|
|
}
|
|
|
|
/* Choose two random cut positions. A cut in position i means that
|
|
** the cut immediately precedes position i. If cut1 < cut2, we
|
|
** exchange the middle of the two orderings; otherwise, we
|
|
** exchange the beginnings and the ends.
|
|
*/
|
|
cut1 = rand_int(dd,info->numvars-1);
|
|
do {
|
|
cut2 = rand_int(dd,info->numvars-1);
|
|
} while (cut1 == cut2);
|
|
|
|
#if 0
|
|
/* Print out the parents. */
|
|
(void) fprintf(dd->out,
|
|
"Crossover of %d (mom) and %d (dad) between %d and %d\n",
|
|
mom,dad,cut1,cut2);
|
|
for (i = 0; i < info->numvars; i++) {
|
|
if (i == cut1 || i == cut2) (void) fprintf(dd->out,"|");
|
|
(void) fprintf(dd->out,"%2d ",STOREDD(info,mom,i));
|
|
}
|
|
(void) fprintf(dd->out,"\n");
|
|
for (i = 0; i < info->numvars; i++) {
|
|
if (i == cut1 || i == cut2) (void) fprintf(dd->out,"|");
|
|
(void) fprintf(dd->out,"%2d ",STOREDD(info,dad,i));
|
|
}
|
|
(void) fprintf(dd->out,"\n");
|
|
#endif
|
|
|
|
/* Initialize the inverse permutations: -1 means yet undetermined. */
|
|
for (i = 0; i < maxvar; i++) {
|
|
inv1[i] = -1;
|
|
inv2[i] = -1;
|
|
}
|
|
|
|
/* Copy the portions whithin the cuts. */
|
|
for (i = cut1; i != cut2; i = (i == info->numvars-1) ? 0 : i+1) {
|
|
STOREDD(info,info->popsize,i) = STOREDD(info,dad,i);
|
|
inv1[STOREDD(info,info->popsize,i)] = i;
|
|
STOREDD(info,info->popsize+1,i) = STOREDD(info,mom,i);
|
|
inv2[STOREDD(info,info->popsize+1,i)] = i;
|
|
}
|
|
|
|
/* Now apply the repair algorithm outside the cuts. */
|
|
for (i = cut2; i != cut1; i = (i == info->numvars-1 ) ? 0 : i+1) {
|
|
v = i;
|
|
do {
|
|
u = STOREDD(info,mom,v);
|
|
v = inv1[u];
|
|
} while (v != -1);
|
|
STOREDD(info,info->popsize,i) = u;
|
|
inv1[u] = i;
|
|
v = i;
|
|
do {
|
|
u = STOREDD(info,dad,v);
|
|
v = inv2[u];
|
|
} while (v != -1);
|
|
STOREDD(info,info->popsize+1,i) = u;
|
|
inv2[u] = i;
|
|
}
|
|
|
|
#if 0
|
|
/* Print the results of crossover. */
|
|
for (i = 0; i < info->numvars; i++) {
|
|
if (i == cut1 || i == cut2) (void) fprintf(table->out,"|");
|
|
(void) fprintf(table->out,"%2d ",STOREDD(info,info->popsize,i));
|
|
}
|
|
(void) fprintf(table->out,"\n");
|
|
for (i = 0; i < info->numvars; i++) {
|
|
if (i == cut1 || i == cut2) (void) fprintf(table->out,"|");
|
|
(void) fprintf(table->out,"%2d ",STOREDD(info,info->popsize+1,i));
|
|
}
|
|
(void) fprintf(table->out,"\n");
|
|
#endif
|
|
|
|
FREE(inv1);
|
|
FREE(inv2);
|
|
return(1);
|
|
|
|
} /* end of PMX */
|
|
|
|
|
|
/**
|
|
@brief Selects two distinct parents with the roulette wheel method.
|
|
|
|
@sideeffect The indices of the selected parents are returned as side
|
|
effects.
|
|
|
|
*/
|
|
static int
|
|
roulette(
|
|
DdManager * dd,
|
|
int * p1,
|
|
int * p2,
|
|
GeneticInfo_t * info)
|
|
{
|
|
double *wheel;
|
|
double spin;
|
|
int i;
|
|
|
|
wheel = ALLOC(double,info->popsize);
|
|
if (wheel == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
/* The fitness of an individual is the reciprocal of its size. */
|
|
wheel[0] = 1.0 / (double) STOREDD(info,0,info->numvars);
|
|
|
|
for (i = 1; i < info->popsize; i++) {
|
|
wheel[i] = wheel[i-1] + 1.0 / (double) STOREDD(info,i,info->numvars);
|
|
}
|
|
|
|
/* Get a random number between 0 and wheel[popsize-1] (that is,
|
|
** the sum of all fitness values. 2147483561 is the largest number
|
|
** returned by Cudd_Random.
|
|
*/
|
|
spin = wheel[info->numvars-1] * (double) Cudd_Random(dd) / 2147483561.0;
|
|
|
|
/* Find the lucky element by scanning the wheel. */
|
|
for (i = 0; i < info->popsize; i++) {
|
|
if (spin <= wheel[i]) break;
|
|
}
|
|
*p1 = i;
|
|
|
|
/* Repeat the process for the second parent, making sure it is
|
|
** distinct from the first.
|
|
*/
|
|
do {
|
|
spin = wheel[info->popsize-1] * (double) Cudd_Random(dd) / 2147483561.0;
|
|
for (i = 0; i < info->popsize; i++) {
|
|
if (spin <= wheel[i]) break;
|
|
}
|
|
} while (i == *p1);
|
|
*p2 = i;
|
|
|
|
FREE(wheel);
|
|
return(1);
|
|
|
|
} /* end of roulette */
|