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