lil-gp Bug Patches

I have identified two chief bugs in lil-gpl 1.1 beta while working in multi-threaded mode. Until they are resolved, here are (ugly) patches to the code which should work nicely. Peter Anderson has also identified a bug in lilgp's ERC facility; I've not verified this bug, but his patch is located here.

The Tree Evaluation Facility

This appears to be a very serious bug; all the function set_current_individual() does is modify a global static pointer. This creates a very dangerous race condition when individuals are being evaluated simultaneously. The patch is to move this global variable into the thread-safe globaldata structure. This means you will have to first add to your globaldata structure the line...

individual* current_individual;

You should not access this line in your evaluation code, even though it's in the globaldata structure. Once this line has been added, follow the following patch instructions.

Open the file eval.c. Change the passage...


static individual * current_individual;

void set_current_individual ( individual *ind )
{
  current_individual = ind;
}

...to...


#if !defined(POSIX_MT) && !defined(SOLARIS_MT)
static individual * current_individual;
#define CURRENT_INDIVIDUAL current_individual
#else
#define CURRENT_INDIVIDUAL ((get_globaldata())->current_individual)
#endif

void set_current_individual ( individual *ind )
{
  CURRENT_INDIVIDUAL = ind;
}

...next, there are THREE passages you'll need to change. They're all identical. All three times it occurs, change...


arg->d = evaluate_tree ( current_individual->tr[f->evaltree].data,
                        f->evaltree );

...to...


arg->d = evaluate_tree ( CURRENT_INDIVIDUAL->tr[f->evaltree].data,
                        f->evaltree );

Lastly, you need to access your globaldata structure differently than you did in the past. Comment out the line in app.c where it says...

extern globaldata g;

...(or whatever). To access your "global" variable, you now use the new threadsafe function get_globaldata() as in:

                    void app_eval_fitness ( individual *ind )
                        {
                         globaldata* g=get_globaldata();
                         set_current_individual ( ind );
                         ...

The Random Facility

lil-gp's random facility is not threadsafe and cannot be called from your evaluation code in multi-threaded mode without some patching. The patch for this wraps the random facility in a mutex so only one thread can call the random function at a time. Assuming threads aren't re-seeing the generator, the only function that needs to be wrapped is random_double() because the other random functions operate on it. Note that this patch only works for POSIX Threads. You'll have to code your own Solaris Threads patch, sorry. Here are the patch instructions.

First open main.c, and change the passage


/* maximum number of nodes per individual.  -1 if no limit is
   enforced. */
int ind_nodelimit;

int main ( int argc, char **argv )
{

     multipop *mpop;
     int startgen;
     char *param;
     event start, end, diff;
     event eval, breed;
     int startfromcheckpoint;

#ifdef MEMORY_LOG
     /* dump all memory allocations to a file. */
     mlog = fopen ( "memory.log", "w" );
#endif

...to...


/* maximum number of nodes per individual.  -1 if no limit is
   enforced. */
int ind_nodelimit;

extern pthread_mutex_t random_mutex;  /* Mutexes the random code */

int main ( int argc, char **argv )
{

     multipop *mpop;
     int startgen;
     char *param;
     event start, end, diff;
     event eval, breed;
     int startfromcheckpoint;

     pthread_mutex_init(&random_mutex,NULL); /* Initialize the random mutex */

#ifdef MEMORY_LOG
     /* dump all memory allocations to a file. */
     mlog = fopen ( "memory.log", "w" );
#endif

Then modify the passage (at the end of main())...


     /* print memory/time statistics and close output files. */
     output_system_stats ( &diff, &eval, &breed );
     close_output_streams();

#ifdef MEMORY_LOG
     fclose ( mlog );
#endif

     /* all done. */
     return 0;
}

...to...


     /* print memory/time statistics and close output files. */
     output_system_stats ( &diff, &eval, &breed );
     close_output_streams();

#ifdef MEMORY_LOG
     fclose ( mlog );
#endif

     pthread_mutex_destroy(&random_mutex);  /* Destroy random mutex */

     /* all done. */
     return 0;
}

Next, open the file random.c Change the passage


static double mz = 0.0;
static double ma[55];       /* the number 55 is special -- see Knuth. */
static int inext, inextp;

...to...


static double mz = 0.0;
static double ma[55];       /* the number 55 is special -- see Knuth. */
static int inext, inextp;

pthread_mutex_t random_mutex;

and change the function...


double random_double ( void )
{

     double mj;
     double res;

     inext = (inext+1)%55;
     inextp = (inextp+1)%55;
     
     mj = ma[inext] - ma[inextp];
     if ( mj < mz )
          mj = mj + mbig;
     ma[inext] = mj;

     res=mbig;

     return mj/res;
}

...to...


double random_double ( void )
{
  /* There's a race condition on this, so we need to mutex it! */

     double mj;
     double res;

     pthread_mutex_lock(&random_mutex);

     inext = (inext+1)%55;
     inextp = (inextp+1)%55;
     
     mj = ma[inext] - ma[inextp];
     if ( mj < mz )
          mj = mj + mbig;
     ma[inext] = mj;

     res=mbig;

     pthread_mutex_unlock(&random_mutex);
     return mj/res;
}