libfgen  0.1.15
Library for optimization using a genetic algorithm or particle swarm optimization
steady_state.c
00001 /*
00002     steady_state.c -- implements a steady-state genetic algorithm.
00003 
00004     fgen -- Library for optimization using a genetic algorithm or particle swarm optimization.
00005     Copyright 2013, Harm Hanemaaijer <fgenfb at yahoo.com>
00006 
00007     This file is part of fgen.
00008 
00009     fgen is free software: you can redistribute it and/or modify it
00010     under the terms of the GNU Lesser General Public License as published
00011     by the Free Software Foundation, either version 3 of the License, or
00012     (at your option) any later version.
00013 
00014     fgen is distributed in the hope that it will be useful, but WITHOUT
00015     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00016     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00017     License for more details.
00018 
00019     You should have received a copy of the GNU Lesser General Public
00020     License along with fgen.  If not, see <http://www.gnu.org/licenses/>.
00021 
00022 */
00023 
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #ifdef __GNUC__
00027 #include <unistd.h>
00028 #endif
00029 #include <math.h>
00030 #include <limits.h>
00031 #include <float.h>
00032 #include <pthread.h>
00033 #include "fgen.h"
00034 #include "parameters.h"
00035 #include "population.h"
00036 #include "selection.h"
00037 #include "crossover.h"
00038 #include "mutation.h"
00039 #include "random.h"
00040 #include "cache.h"
00041 #include "migration.h"
00042 #include "error.h"
00043 #include "win32_compat.h"
00044 
00045 static int TournamentSelection(FgenPopulation *pop) {
00046         int winner;
00047         int j;
00048         double best_fitness = NEGATIVE_INFINITY_DOUBLE;
00049         FgenIndividual **ind = pop->ind;
00050         for (j = 0; j < pop->tournament_size; j++) {
00051                 int k = fgen_random_n(pop->rng, pop->size);
00052                 CalculateIndividualFitness(pop, ind[k]);
00053                 if (ind[k]->fitness > best_fitness) {
00054                         winner = k;
00055                         best_fitness = ind[k]->fitness;
00056                 }
00057         }
00058         return winner;
00059 }
00060 
00061 static int TournamentSelectionWorst(FgenPopulation *pop) {
00062         int winner;
00063         int j;
00064         double worst_fitness = POSITIVE_INFINITY_DOUBLE;
00065         FgenIndividual **ind = pop->ind;
00066         for (j = 0; j < pop->tournament_size; j++) {
00067                 int k = fgen_random_n(pop->rng, pop->size);
00068                 CalculateIndividualFitness(pop, ind[k]);
00069                 if (ind[k]->fitness < worst_fitness) {
00070                         winner = k;
00071                         worst_fitness = ind[k]->fitness;
00072                 }
00073         }
00074         return winner;
00075 }
00076 
00077 static void DoCrossoverSteadyState(FgenPopulation *pop, int i, int j, int k, int l) {
00078         if (pop->crossover_probability == 0 || pop->fgen_crossover_func == fgen_crossover_noop)
00079                 return;
00080         int r;
00081         FgenIndividual **ind = pop->ind;
00082         r = fgen_random_8(pop->rng);
00083         if (r < pop->crossover_probability) {
00084                 /* Perform crossover operation. */
00085                 FgenIndividual *new_ind1 = NewIndividual(pop);
00086                 FgenIndividual *new_ind2 = NewIndividual(pop);
00087                 pop->fgen_crossover_func(pop, ind[i]->bitstring, ind[j]->bitstring, new_ind1->bitstring,
00088                         new_ind2->bitstring);
00089                 /* Replace two random individuals. */
00090                 FreeIndividual(ind[k]);
00091                 ind[k] = new_ind1;
00092                 FreeIndividual(ind[l]);
00093                 ind[l] = new_ind2;
00094         }
00095         else {
00096                 FreeIndividual(ind[k]);
00097                 ind[k] = ind[i];
00098                 ind[i]->refcount++;
00099                 FreeIndividual(ind[l]);
00100                 ind[l] = ind[j];
00101                 ind[j]->refcount++;
00102         }
00103 }
00104 
00105 static void DoMutationSteadyState(FgenPopulation *pop, int i, int j) {
00106         FgenIndividual *new_ind = NewIndividual(pop);
00107         CopyIndividualBitstring(pop, pop->ind[i], new_ind);
00108         pop->fgen_mutation_func(pop, pop->ind[i]->bitstring, new_ind->bitstring);
00109         FreeIndividual(pop->ind[i]);
00110         pop->ind[i] = new_ind;
00111         new_ind = NewIndividual(pop);
00112         CopyIndividualBitstring(pop, pop->ind[j], new_ind);
00113         pop->fgen_mutation_func(pop, pop->ind[j]->bitstring, new_ind->bitstring);
00114         FreeIndividual(pop->ind[j]);
00115         pop->ind[j] = new_ind;
00116 }
00117 
00118 static void SteadyStateGeneration(FgenPopulation *pop) {
00119         /* Selection: select two random unique individuals using tournament selection. */
00120         int i = TournamentSelection(pop);
00121         int j;
00122         do {
00123                 j = TournamentSelection(pop);
00124         } while (j == i);
00125         /* Select two random unique individuals that will be replaced using tournament selection */
00126         /* where the worst individual in the tournament is selected. */
00127         int k;
00128         do {
00129                 if (pop->selection_type & FGEN_KILL_TOURNAMENT_ELEMENT)
00130                         k = TournamentSelectionWorst(pop);
00131                 else
00132                         k = fgen_random_n(pop->rng, pop->size);
00133         } while (k == i || k == j);
00134         int l;
00135         do {
00136                 if (pop->selection_type & FGEN_KILL_TOURNAMENT_ELEMENT)
00137                         l = TournamentSelectionWorst(pop);
00138                 else
00139                         l = fgen_random_n(pop->rng, pop->size);
00140         } while (l == i || l == j || l == k);
00141 
00142         DoCrossoverSteadyState(pop, i, j, k, l);
00143 
00144         DoMutationSteadyState(pop, k, l);
00145 }
00146 
00161 void fgen_run_steady_state(FgenPopulation *pop, int max_generation) {
00162         if ((pop->selection_type & FGEN_STOCHASTIC_TYPE_MASK) != FGEN_TOURNAMENT)
00163                 gen_error("Error: fgen_run_steady_state: only tournament selection allowed.");
00164         if (pop->selection_type & FGEN_ELITIST_ELEMENT)
00165                 gen_error("Error: fgen_run_steady_state: elitism not supported in steady-state evolution.");
00166         if (pop->selection_type & FGEN_EXTINCTION_ELEMENT)
00167                 gen_error("Error: fgen_run_steady_state: extinction not supported in steady-state evolution.");
00168 
00169         CreateInitialPopulation(pop);
00170 
00171         pop->generation = 0;
00172         pop->stop_signalled = 0;
00173         pop->fgen_generation_callback_func(pop, pop->generation);
00174         if (pop->stop_signalled)
00175                 goto end;
00176 
00177         for (;;) {
00178                 SteadyStateGeneration(pop);
00179 
00180                 pop->generation++;
00181                 if (pop->generation % pop->generation_callback_interval == 0)
00182                         pop->fgen_generation_callback_func(pop, pop->generation);
00183                 if ((max_generation != - 1 && pop->generation >= max_generation) || pop->stop_signalled)
00184                         break;
00185         }
00186 end :
00187         CalculatePopulationFitness(pop, NULL, NULL);
00188 }
00189 
00205 void fgen_run_steady_state_archipelago(int nu_pops, FgenPopulation **pops, int max_generation) {
00206         int i;
00207         for (i = 0; i < nu_pops; i++)  {
00208                 if ((pops[i]->selection_type & FGEN_STOCHASTIC_TYPE_MASK) != FGEN_TOURNAMENT)
00209                         gen_error("Error: fgen_run_steady_state_archipelago: only tournament selection allowed.");
00210                 if (pops[i]->selection_type & FGEN_ELITIST_ELEMENT)
00211                         gen_error("Error: fgen_run_steady_state_archipelago: elitism not supported in steady-state evolution.");
00212                 if (pops[i]->selection_type & FGEN_EXTINCTION_ELEMENT)
00213                         gen_error("Error: fgen_run_steady_state_archipelago: extinction not supported in steady-state "
00214                                 "evolution.");
00215         }
00216 
00217         /* Randomize the seeds of the random number generators of the populations after */
00218         /* the first one based on random number from the first random number generator. */
00219         for (i = 1; i < nu_pops; i++)
00220                 fgen_random_seed_rng(pops[i]->rng, fgen_random_32(pops[0]->rng));
00221 
00222         for (i = 0; i < nu_pops; i++) {
00223                 CreateInitialPopulation(pops[i]);
00224                 pops[i]->generation = 0;
00225                 pops[i]->stop_signalled = 0;
00226                 pops[i]->island = i;
00227                 pops[i]->fgen_generation_callback_func(pops[i], pops[i]->generation);
00228         }
00229         for (i = 0; i < nu_pops; i++)
00230                 if (pops[i]->stop_signalled)
00231                         goto end;
00232 
00233         for (;;) {
00234                 for (i = 0; i < nu_pops; i++) {
00235                         SteadyStateGeneration(pops[i]);
00236 
00237                         pops[i]->generation++;
00238                 }
00239 
00240                 if (pops[0]->migration_interval != 0)
00241                         if (pops[0]->generation % pops[0]->migration_interval == 0)
00242                                 DoMigration(nu_pops, pops);
00243 
00244                 for (i = 0; i < nu_pops; i++)
00245                         if (pops[i]->generation % pops[i]->generation_callback_interval == 0)
00246                                 pops[i]->fgen_generation_callback_func(pops[i], pops[i]->generation);
00247                 for (i = 0; i < nu_pops; i++)
00248                         if ((max_generation != - 1 && pops[i]->generation >= max_generation) || pops[i]->stop_signalled)
00249                                 goto end;
00250         }
00251 end :
00252         for (i = 0; i < nu_pops; i++)
00253                 CalculatePopulationFitness(pops[i], NULL, NULL);
00254 }
00255 
00256 /* Threaded steady-state archipelago. This is almost identical to the generational threaded archipelago in ga.c. */
00257 
00258 static void *fgen_steady_state_create_initial_population_thread(void *threadarg) {
00259         FgenPopulation *pop = (FgenPopulation *)threadarg;
00260         CreateInitialPopulation(pop);
00261         CalculatePopulationFitness(pop, NULL, NULL);
00262         pthread_exit(NULL);
00263         return NULL;
00264 }
00265 
00266 typedef struct {
00267         FgenPopulation *pop;
00268         int nu_generations;
00269         pthread_cond_t *condition_ready;
00270         pthread_cond_t *condition_start;
00271         pthread_cond_t *condition_end;
00272         pthread_cond_t *condition_continue;
00273         pthread_mutex_t *mutex_ready;
00274         pthread_mutex_t *mutex_start;
00275         pthread_mutex_t *mutex_end;
00276         pthread_mutex_t *mutex_continue;
00277         int *ready_signalled_count;
00278         int *start_signalled;
00279         int *end_signalled_count;
00280         int *continue_signalled;
00281 } ThreadData;
00282 
00283 static void *fgen_steady_state_do_multiple_generations_cond_thread(void *threadarg) {
00284         ThreadData *thread_data = (ThreadData *)threadarg;
00285         FgenPopulation *pop = thread_data->pop;
00286         for (;;) {
00287                 int nu_generations;
00288                 int i;
00289                 /* Give the ready signal. */
00290                 pthread_mutex_lock(thread_data->mutex_ready);
00291                 (*thread_data->ready_signalled_count)++;
00292                 pthread_cond_signal(thread_data->condition_ready);
00293                 pthread_mutex_unlock(thread_data->mutex_ready);
00294                 /* Wait for the start signal. */
00295                 pthread_mutex_lock(thread_data->mutex_start);
00296                 while (!(*thread_data->start_signalled))
00297                         pthread_cond_wait(thread_data->condition_start, thread_data->mutex_start);
00298                 pthread_mutex_unlock(thread_data->mutex_start);
00299                 /* Do the work. */
00300                 nu_generations = thread_data->nu_generations;
00301                 if (nu_generations == 0)
00302                         pthread_exit(NULL);
00303                 i = 0;
00304                 for (i = 0; i < nu_generations; i++) {
00305                         SteadyStateGeneration(pop);
00306                         pop->generation++;
00307                         /* A stop may be signalled from the fitness function. */
00308                         if (pop->stop_signalled) {
00309                                 pop->generation += nu_generations - i - 1;
00310                                 break;
00311                         }
00312                 }
00313                 /* Take advantage of the multiple threads to update the fitness now, instead of */
00314                 /* later in single-threaded mode. */
00315                 CalculatePopulationFitness(pop, NULL, NULL);
00316                 /* Signal the end of the work. */
00317                 pthread_mutex_lock(thread_data->mutex_end);
00318                 (*thread_data->end_signalled_count)++;
00319                 pthread_cond_signal(thread_data->condition_end);
00320                 pthread_mutex_unlock(thread_data->mutex_end);
00321                 /* Wait for the continue signal. */
00322                 pthread_mutex_lock(thread_data->mutex_continue);
00323                 while (!(*thread_data->continue_signalled))
00324                         pthread_cond_wait(thread_data->condition_continue, thread_data->mutex_continue);
00325                 pthread_mutex_unlock(thread_data->mutex_continue);
00326         }
00327 }
00328 
00340 void fgen_run_steady_state_archipelago_threaded(int nu_pops, FgenPopulation **pops, int max_generation) {
00341         int i;
00342         pthread_t *thread;
00343         int max_threads;
00344         ThreadData *thread_data;
00345         pthread_cond_t *condition_ready;
00346         pthread_cond_t *condition_start;
00347         pthread_cond_t *condition_end;
00348         pthread_cond_t *condition_continue;
00349         pthread_mutex_t *mutex_ready;
00350         pthread_mutex_t *mutex_start;
00351         pthread_mutex_t *mutex_end;
00352         pthread_mutex_t *mutex_continue;
00353         int ready_signalled_count;
00354         int *start_signalled;
00355         int end_signalled_count;
00356         int continue_signalled;
00357 
00358         for (i = 0; i < nu_pops; i++)  {
00359                 if ((pops[i]->selection_type & FGEN_STOCHASTIC_TYPE_MASK) != FGEN_TOURNAMENT)
00360                         gen_error("Error: fgen_run_steady_state_archipelago_threaded_: only tournament selection allowed.");
00361                 if (pops[i]->selection_type & FGEN_ELITIST_ELEMENT)
00362                         gen_error("Error: fgen_run_steady_state_archipelago_threaded: elitism not supported in steady-state "
00363                                 "evolution.");
00364                 if (pops[i]->selection_type & FGEN_EXTINCTION_ELEMENT)
00365                         gen_error("Error: fgen_run_steady_state_archipelago_threaded: extinction not supported "
00366                                 "in steady-state evolution.");
00367         }
00368 
00369         /* Randomize the seeds of the random number generators of the populations after */
00370         /* the first one based on random number from the first random number generator. */
00371         for (i = 1; i < nu_pops; i++)
00372                 fgen_random_seed_rng(pops[i]->rng, fgen_random_32(pops[0]->rng));
00373 
00374         /* In practice, just running a lot of threads seems to be at least as efficient as imposing a maximum */
00375         /* on the amount of concurrently active threads. */
00376 //      max_threads = sysconf(_SC_NPROCESSORS_ONLN);
00377 //      for (i = 0; i < nu_pops; i++)
00378 //              pops[i]->max_threads = max_threads;
00379 //      printf("fgen: number of CPU cores detected = %d.\n", max_threads);
00380         max_threads = nu_pops;
00381 
00382         thread = (pthread_t *)malloc(sizeof(pthread_t) * nu_pops);
00383         thread_data = (ThreadData *)malloc(sizeof(ThreadData) * nu_pops);
00384         condition_ready = (pthread_cond_t *)malloc(sizeof(pthread_cond_t));
00385         condition_start = (pthread_cond_t *)malloc(sizeof(pthread_cond_t) * nu_pops);
00386         condition_end = (pthread_cond_t *)malloc(sizeof(pthread_cond_t));
00387         condition_continue = (pthread_cond_t *)malloc(sizeof(pthread_cond_t));
00388         mutex_ready = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
00389         mutex_start = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
00390         mutex_end = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
00391         mutex_continue = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
00392         start_signalled = (int *)malloc(sizeof(int) * nu_pops);
00393 
00394         /* Create the initial populations and calculate fitness, threaded. */
00395         for (i = 0; i < nu_pops; i++) {
00396                 pops[i]->generation = 0;
00397                 pops[i]->stop_signalled = 0;
00398                 pops[i]->island = i;
00399                 pthread_create(&thread[i], NULL, fgen_steady_state_create_initial_population_thread, pops[i]);
00400         }
00401         for (i = 0; i < nu_pops; i++)
00402                 pthread_join(thread[i], NULL);
00403         for (i = 0; i < nu_pops; i++)
00404                 pops[i]->fgen_generation_callback_func(pops[i], pops[i]->generation);
00405         for (i = 0; i < nu_pops; i++)
00406                 if (pops[i]->stop_signalled)
00407                         goto end2;
00408 
00409         /* Initialize the condition variables and mutex. */
00410         pthread_cond_init(condition_ready, NULL);
00411         for (i = 0; i < nu_pops; i++)
00412                 pthread_cond_init(&condition_start[i], NULL);
00413         pthread_cond_init(condition_end, NULL);
00414         pthread_cond_init(condition_continue, NULL);
00415         pthread_mutex_init(mutex_ready, NULL);
00416         pthread_mutex_init(mutex_start, NULL);
00417         pthread_mutex_init(mutex_end, NULL);
00418         pthread_mutex_init(mutex_continue, NULL);
00419 
00420         /* Create a worker thread for each island. */
00421         ready_signalled_count = 0;
00422         for (i = 0; i < nu_pops; i++) {
00423                 start_signalled[i] = 0;
00424                 thread_data[i].pop = pops[i];
00425                 thread_data[i].condition_ready = condition_ready;
00426                 thread_data[i].condition_start = &condition_start[i];
00427                 thread_data[i].condition_end = condition_end;
00428                 thread_data[i].condition_continue = condition_continue;
00429                 thread_data[i].mutex_ready = mutex_ready;
00430                 thread_data[i].mutex_start = mutex_start;
00431                 thread_data[i].mutex_end = mutex_end;
00432                 thread_data[i].mutex_continue = mutex_continue;
00433                 thread_data[i].ready_signalled_count = &ready_signalled_count;
00434                 thread_data[i].start_signalled = &start_signalled[i];
00435                 thread_data[i].end_signalled_count = &end_signalled_count;
00436                 thread_data[i].continue_signalled = &continue_signalled;
00437                 pthread_create(&thread[i], NULL, fgen_steady_state_do_multiple_generations_cond_thread,
00438                         &thread_data[i]);
00439         }
00440 
00441         for (;;) {
00442                 int nu_generations_to_run;
00443                 int starting_thread;
00444 
00445                 /* Figure out how many generations can be run without having to call the generation */
00446                 /* callback function or do migration, and then run them concurrently. */
00447                 if (max_generation == - 1)
00448                         nu_generations_to_run = INT_MAX - pops[0]->generation;
00449                 else
00450                         nu_generations_to_run = max_generation - pops[0]->generation;
00451                 for (i = pops[0]->generation + 1; i < pops[0]->generation + nu_generations_to_run; i++)
00452                         if ((pops[0]->migration_interval != 0 && i % pops[0]->migration_interval == 0) ||
00453                         i % pops[0]->generation_callback_interval == 0) {
00454                                 nu_generations_to_run = i - pops[0]->generation;
00455                                 break;
00456                         }
00457 
00458                 /* Check whether all the workers are ready. */
00459                 pthread_mutex_lock(mutex_ready);
00460                 while (ready_signalled_count < nu_pops) {
00461                         pthread_cond_wait(condition_ready, mutex_ready);
00462                 }
00463                 pthread_mutex_unlock(mutex_ready);
00464                 ready_signalled_count = 0;
00465                 continue_signalled = 0;
00466 
00467                 /* Run max_threads concurrent threads. Of the nu_pops threads, only allow max_threads to be */
00468                 /* active at the same time. */
00469                 for (starting_thread = 0; starting_thread < nu_pops; starting_thread += max_threads) {
00470                         /* Calculate the number of active threads. */
00471                         int nu_threads = max_threads;
00472                         if (starting_thread + max_threads > nu_pops)
00473                                 nu_threads = nu_pops - starting_thread;
00474                         /* Activate the worker threads for each selected island. */
00475                         for (i = starting_thread; i < starting_thread + max_threads && i < nu_pops; i++)
00476                                 thread_data[i].nu_generations = nu_generations_to_run;
00477                         end_signalled_count = 0;
00478                         for (i = starting_thread; i < starting_thread + max_threads && i < nu_pops; i++) {
00479                                 pthread_mutex_lock(mutex_start);
00480                                 start_signalled[i] = 1;
00481                                 pthread_cond_signal(&condition_start[i]);
00482                                 pthread_mutex_unlock(mutex_start);
00483                         }
00484 
00485                         /* Wait for all of them to finish. */
00486                         pthread_mutex_lock(mutex_end);
00487                         while (end_signalled_count < nu_threads) {
00488                                 pthread_cond_wait(condition_end, mutex_end);
00489                         }
00490                         pthread_mutex_unlock(mutex_end);
00491         
00492                         for (i = starting_thread; i < starting_thread + max_threads && i < nu_pops; i++)
00493                                 start_signalled[i] = 0;
00494                 }
00495                 /* Give the signal to continue. */
00496                 pthread_mutex_lock(mutex_continue);
00497                 continue_signalled = 1;
00498                 pthread_cond_broadcast(condition_continue);
00499                 pthread_mutex_unlock(mutex_continue);
00500 
00501                 if (pops[0]->migration_interval != 0)
00502                         if (pops[0]->generation % pops[0]->migration_interval == 0)
00503                                 DoMigration(nu_pops, pops);
00504 
00505                 for (i = 0; i < nu_pops; i++) {
00506                         if (pops[i]->generation % pops[i]->generation_callback_interval == 0)
00507                                 pops[i]->fgen_generation_callback_func(pops[i], pops[i]->generation);
00508                 }
00509                 for (i = 0; i < nu_pops; i++)
00510                         if ((max_generation != - 1 && pops[i]->generation >= max_generation) || pops[i]->stop_signalled)
00511                                 goto end;
00512         }
00513 end :
00514         /* Instruct the threads to exit. */
00515         for (i = 0; i < nu_pops; i++)
00516                 thread_data[i].nu_generations = 0;
00517         for (i = 0; i < nu_pops; i++) {
00518                 pthread_mutex_lock(mutex_start);
00519                 start_signalled[i] = 1;
00520                 pthread_cond_signal(&condition_start[i]);
00521                 pthread_mutex_unlock(mutex_start);
00522         }
00523         for (i = 0; i < nu_pops; i++)
00524                 pthread_join(thread[i], NULL);
00525         /* Free all data structures. */
00526         pthread_cond_destroy(condition_ready);
00527         for (i = 0; i < nu_pops; i++)
00528                 pthread_cond_destroy(&condition_start[i]);
00529         pthread_cond_destroy(condition_end);
00530         pthread_cond_destroy(condition_continue);
00531         pthread_mutex_destroy(mutex_ready);
00532         pthread_mutex_destroy(mutex_start);
00533         pthread_mutex_destroy(mutex_end);
00534         pthread_mutex_destroy(mutex_continue);
00535 end2 :
00536         free(thread);
00537         free(thread_data);
00538         free(condition_ready);
00539         free(condition_start);
00540         free(condition_end);
00541         free(condition_continue);
00542         free(mutex_ready);
00543         free(mutex_start);
00544         free(mutex_end);
00545         free(mutex_continue);
00546         free(start_signalled);
00547 }
00548 
 All Data Structures Functions Variables