1 /*
2 * Copyright (c) 2007 - 2015 Joseph Gaeddert
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23 //
24 // gasearch.c
25 //
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31
32 #include "liquid.internal.h"
33
34 #define LIQUID_GA_SEARCH_MAX_POPULATION_SIZE (1024)
35 #define LIQUID_GA_SEARCH_MAX_CHROMOSOME_SIZE (32)
36
37 #define LIQUID_DEBUG_GA_SEARCH 0
38
39 // Create a simple gasearch object; parameters are specified internally
40 // _utility : chromosome fitness utility function
41 // _userdata : user data, void pointer passed to _utility() callback
42 // _parent : initial population parent chromosome, governs precision, etc.
43 // _minmax : search direction
gasearch_create(gasearch_utility _utility,void * _userdata,chromosome _parent,int _minmax)44 gasearch gasearch_create(gasearch_utility _utility,
45 void * _userdata,
46 chromosome _parent,
47 int _minmax)
48 {
49 return gasearch_create_advanced(_utility,
50 _userdata,
51 _parent,
52 _minmax,
53 16, // population size
54 0.02f); // mutation rate
55 }
56
57 // Create a gasearch object, specifying search parameters
58 // _utility : chromosome fitness utility function
59 // _userdata : user data, void pointer passed to _utility() callback
60 // _parent : initial population parent chromosome, governs precision, etc.
61 // _minmax : search direction
62 // _population_size : number of chromosomes in population
63 // _mutation_rate : probability of mutating chromosomes
gasearch_create_advanced(gasearch_utility _utility,void * _userdata,chromosome _parent,int _minmax,unsigned int _population_size,float _mutation_rate)64 gasearch gasearch_create_advanced(gasearch_utility _utility,
65 void * _userdata,
66 chromosome _parent,
67 int _minmax,
68 unsigned int _population_size,
69 float _mutation_rate)
70 {
71 gasearch ga;
72 ga = (gasearch) malloc( sizeof(struct gasearch_s) );
73
74 if (_population_size > LIQUID_GA_SEARCH_MAX_POPULATION_SIZE) {
75 fprintf(stderr,"error: gasearch_create(), population size exceeds maximum\n");
76 exit(1);
77 } else if (_mutation_rate < 0.0f || _mutation_rate > 1.0f) {
78 fprintf(stderr,"error: gasearch_create(), mutation rate must be in [0,1]\n");
79 exit(1);
80 }
81
82 // initialize public values
83 ga->userdata = _userdata;
84 ga->num_parameters = _parent->num_traits;
85 ga->population_size = _population_size;
86 ga->mutation_rate = _mutation_rate;
87 ga->get_utility = _utility;
88 ga->minimize = ( _minmax==LIQUID_OPTIM_MINIMIZE ) ? 1 : 0;
89
90 ga->bits_per_chromosome = _parent->num_bits;
91
92 // initialize selection size be be 25% of population, minimum of 2
93 ga->selection_size = ( ga->population_size >> 2 ) < 2 ? 2 : ga->population_size >> 2;
94
95 // allocate internal arrays
96 ga->population = (chromosome*) malloc( sizeof(chromosome)*(ga->population_size) );
97 ga->utility = (float*) calloc( sizeof(float), ga->population_size );
98
99 // create optimum chromosome (clone)
100 ga->c = chromosome_create_clone(_parent);
101
102 //printf("num_parameters: %d\n", ga->num_parameters);
103 //printf("population_size: %d\n", ga->population_size);
104 //printf("\nbits_per_chromosome: %d\n", ga->bits_per_chromosome);
105
106 // create population
107 unsigned int i;
108 for (i=0; i<ga->population_size; i++)
109 ga->population[i] = chromosome_create_clone(_parent);
110
111 // initialize population to random, preserving first chromosome
112 for (i=1; i<ga->population_size; i++)
113 chromosome_init_random( ga->population[i] );
114
115 // evaluate population
116 gasearch_evaluate(ga);
117
118 // rank chromosomes
119 gasearch_rank(ga);
120
121 // set global utility optimum
122 ga->utility_opt = ga->utility[0];
123
124 // return object
125 return ga;
126 }
127
128 // destroy a gasearch object
gasearch_destroy(gasearch _g)129 void gasearch_destroy(gasearch _g)
130 {
131 unsigned int i;
132 for (i=0; i<_g->population_size; i++)
133 chromosome_destroy( _g->population[i] );
134 free(_g->population);
135
136 // destroy optimum chromosome
137 chromosome_destroy(_g->c);
138
139 free(_g->utility);
140 free(_g);
141 }
142
143 // print search parameter internals
gasearch_print(gasearch _g)144 void gasearch_print(gasearch _g)
145 {
146 printf("ga search :\n");
147 printf(" num traits : %u\n", _g->num_parameters);
148 printf(" bits/chromosome : %u\n", _g->bits_per_chromosome);
149 printf(" population size : %u\n", _g->population_size);
150 printf(" selection size : %u\n", _g->selection_size);
151 printf(" mutation rate : %12.8f\n", _g->mutation_rate);
152 printf("population:\n");
153 unsigned int i;
154 for (i=0; i<_g->population_size; i++) {
155 printf("%4u: [%8.4f] ", i, _g->utility[i]);
156 chromosome_printf( _g->population[i] );
157 }
158 }
159
160 // set population/selection size
161 // _q : ga search object
162 // _population_size : new population size (number of chromosomes)
163 // _selection_size : selection size (number of parents for new generation)
gasearch_set_population_size(gasearch _g,unsigned int _population_size,unsigned int _selection_size)164 void gasearch_set_population_size(gasearch _g,
165 unsigned int _population_size,
166 unsigned int _selection_size)
167 {
168 // validate input
169 if (_population_size < 2) {
170 fprintf(stderr,"error: gasearch_set_population_size(), population must be at least 2\n");
171 exit(1);
172 } else if (_selection_size == 0) {
173 fprintf(stderr,"error: gasearch_set_population_size(), selection size must be greater than zero\n");
174 exit(1);
175 } else if (_selection_size >= _population_size) {
176 fprintf(stderr,"error: gasearch_set_population_size(), selection size must be less than population\n");
177 exit(1);
178 }
179
180 // re-size arrays
181 _g->population = (chromosome*) realloc( _g->population, _population_size*sizeof(chromosome) );
182 _g->utility = (float*) realloc( _g->utility, _population_size*sizeof(float) );
183
184 // initialize new chromosomes (copies)
185 if (_population_size > _g->population_size) {
186
187 unsigned int i;
188 unsigned int k = _g->population_size-1; // least optimal
189
190 for (i=_g->population_size; i<_population_size; i++) {
191 // clone chromosome, copying internal values
192 _g->population[i] = chromosome_create_clone(_g->population[k]);
193
194 // copy utility
195 _g->utility[i] = _g->utility[k];
196 }
197 }
198
199 // set internal variables
200 _g->population_size = _population_size;
201 _g->selection_size = _selection_size;
202 }
203
204 // set mutation rate
gasearch_set_mutation_rate(gasearch _g,float _mutation_rate)205 void gasearch_set_mutation_rate(gasearch _g,
206 float _mutation_rate)
207 {
208 if (_mutation_rate < 0.0f || _mutation_rate > 1.0f) {
209 fprintf(stderr,"error: gasearch_set_mutation_rate(), mutation rate must be in [0,1]\n");
210 exit(1);
211 }
212
213 _g->mutation_rate = _mutation_rate;
214 }
215
216 // Execute the search
217 // _g : ga search object
218 // _max_iterations : maximum number of iterations to run before bailing
219 // _tarutility : target utility
gasearch_run(gasearch _g,unsigned int _max_iterations,float _tarutility)220 float gasearch_run(gasearch _g,
221 unsigned int _max_iterations,
222 float _tarutility)
223 {
224 unsigned int i=0;
225 do {
226 i++;
227 gasearch_evolve(_g);
228 } while (
229 optim_threshold_switch(_g->utility[0], _tarutility, _g->minimize) &&
230 i < _max_iterations);
231
232 // return optimum utility
233 return _g->utility_opt;
234 }
235
236 // iterate over one evolution of the search algorithm
gasearch_evolve(gasearch _g)237 void gasearch_evolve(gasearch _g)
238 {
239 // Inject random chromosome at end
240 chromosome_init_random(_g->population[_g->population_size-1]);
241
242 // Crossover
243 gasearch_crossover(_g);
244
245 // Mutation
246 gasearch_mutate(_g);
247
248 // Evaluation
249 gasearch_evaluate(_g);
250
251 // Rank
252 gasearch_rank(_g);
253
254 if ( optim_threshold_switch(_g->utility_opt,
255 _g->utility[0],
256 _g->minimize) )
257 {
258 // update optimum
259 _g->utility_opt = _g->utility[0];
260
261 // copy optimum chromosome
262 chromosome_copy(_g->population[0], _g->c);
263
264 #if LIQUID_DEBUG_GA_SEARCH
265 printf(" utility: %0.2E", _g->utility_opt);
266 chromosome_printf(_g->c);
267 #endif
268 }
269 }
270
271 // get optimal chromosome
272 // _g : ga search object
273 // _c : output optimal chromosome
274 // _utility_opt : fitness of _c
gasearch_getopt(gasearch _g,chromosome _c,float * _utility_opt)275 void gasearch_getopt(gasearch _g,
276 chromosome _c,
277 float * _utility_opt)
278 {
279 // copy optimum chromosome
280 chromosome_copy(_g->c, _c);
281
282 // copy optimum utility
283 *_utility_opt = _g->utility_opt;
284 }
285
286 // evaluate fitness of entire population
gasearch_evaluate(gasearch _g)287 void gasearch_evaluate(gasearch _g)
288 {
289 unsigned int i;
290 for (i=0; i<_g->population_size; i++)
291 _g->utility[i] = _g->get_utility(_g->userdata, _g->population[i]);
292 }
293
294 // crossover population
gasearch_crossover(gasearch _g)295 void gasearch_crossover(gasearch _g)
296 {
297 chromosome p1, p2; // parental chromosomes
298 chromosome c; // child chromosome
299 unsigned int threshold;
300
301 unsigned int i;
302 for (i=_g->selection_size; i<_g->population_size; i++) {
303 // ensure fittest member is used at least once as parent
304 p1 = (i==_g->selection_size) ? _g->population[0] : _g->population[rand() % _g->selection_size];
305 p2 = _g->population[rand() % _g->selection_size];
306 threshold = rand() % _g->bits_per_chromosome;
307
308 c = _g->population[i];
309
310 //printf(" gasearch_crossover, p1: %d, p2: %d, c: %d\n", p1, p2, c);
311 chromosome_crossover(p1, p2, c, threshold);
312 }
313 }
314
315 // mutate population
gasearch_mutate(gasearch _g)316 void gasearch_mutate(gasearch _g)
317 {
318 unsigned int i;
319 unsigned int index;
320
321 // mutate all but first (best) chromosome
322 //for (i=_g->selection_size; i<_g->population_size; i++) {
323 for (i=1; i<_g->population_size; i++) {
324 // generate random number and mutate if within mutation_rate range
325 unsigned int num_mutations = 0;
326 // force at least one mutation (otherwise nothing has changed)
327 while ( randf() < _g->mutation_rate || num_mutations == 0) {
328 // generate random mutation index
329 index = rand() % _g->bits_per_chromosome;
330
331 // mutate chromosome at index
332 chromosome_mutate( _g->population[i], index );
333
334 //
335 num_mutations++;
336
337 if (num_mutations == _g->bits_per_chromosome)
338 break;
339 }
340 }
341 }
342
343 // rank population by fitness
gasearch_rank(gasearch _g)344 void gasearch_rank(gasearch _g)
345 {
346 unsigned int i, j;
347 float u_tmp; // temporary utility placeholder
348 chromosome c_tmp; // temporary chromosome placeholder (pointer)
349
350 for (i=0; i<_g->population_size; i++) {
351 for (j=_g->population_size-1; j>i; j--) {
352 if ( optim_threshold_switch(_g->utility[j], _g->utility[j-1], !(_g->minimize)) ) {
353 // swap chromosome pointers
354 c_tmp = _g->population[j];
355 _g->population[j] = _g->population[j-1];
356 _g->population[j-1] = c_tmp;
357
358 // swap utility values
359 u_tmp = _g->utility[j];
360 _g->utility[j] = _g->utility[j-1];
361 _g->utility[j-1] = u_tmp;
362 }
363 }
364 }
365 }
366
367