libfgen
0.1.15
Library for optimization using a genetic algorithm or particle swarm optimization
|
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