1 /* GAUL Genetic Programming Tutorial - stage 1 */
3 /* Evolve a simple four function calculator from 2 text files. Input text file
4 * is a list of simple math questions (1+1=, 4-1=, 3*3=, 12/2=). output text
5 * file contains correct answers (2, 3, 9, 6). Each member of the population
6 * would need to input the text file and produce its own output file. The
7 * fitness function would need to compare each population output file the
8 * correct math output file. Input file will only have 2 arguments per question
9 * and only the four basic functions (+,-,*,/). */
11 /* Stage1: Loop, parser code non-evolved, chromosome is mapping from characters to operations */
16 /* Number of equations in the file. Just to make the code simpler. */
19 /* Reference results. */
20 static int refresults
[EQNO
];
22 /* Loads reference results. */
24 load_reference_results(void)
27 FILE *f
= fopen("eq.out", "r");
35 /* Read one line at a time. */
37 while (fgets(line
, sizeof(line
), f
)) {
38 /* Convert line to number and store it
39 * in the refresults[] array. */
40 refresults
[eq
++] = atoi(line
);
48 /* Chromosome format: The chromosome here is a four-item array. The items
54 * Each item value is a number from 0 to 3. The numbers correspond
61 * Therefore, the correct chromosome is { 0, 1, 2, 3 }. */
64 /* Initializes a single entity randomly. */
66 seed_entity(population
*pop
, entity
*entity
)
68 int *intchromosome
= (int *) entity
->chromosome
[0];
70 for (int i
= 0; i
< pop
->len_chromosomes
; i
++)
71 intchromosome
[i
] = random_int(4);
76 /* Mutates an entity. */
78 mutate_entity(population
*pop
, entity
*father
, entity
*son
)
80 /* Copy genome from father. */
81 int *intchromosome_father
= (int *) father
->chromosome
[0];
82 int *intchromosome_son
= (int *) son
->chromosome
[0];
83 for (int i
= 0; i
< pop
->len_chromosomes
; i
++)
84 intchromosome_son
[i
] = intchromosome_father
[i
];
86 /* Select mutation locus. */
87 int point
= random_int(pop
->len_chromosomes
);
89 /* Mutate by tweaking a single allele. */
90 intchromosome_son
[point
] = random_int(4);
93 /* Execute a single entity, interpreting its chromosome.
94 * Saves the results to a number array. */
96 execute_entity(entity
*entity
, int results
[EQNO
])
99 FILE *f
= fopen("eq.in", "r");
107 /* Read one line at a time. */
109 while (fgets(line
, sizeof(line
), f
)) {
110 /* Parse line. We do not handle errors here. */
113 sscanf(line
, "%d%c%d=", &arg1
, &op
, &arg2
);
115 /* Parse operator. */
118 case '+': item
= 0; break;
119 case '-': item
= 1; break;
120 case '*': item
= 2; break;
121 case '/': item
= 3; break;
124 /* Lookup operation. */
125 int *intchromosome
= (int *) entity
->chromosome
[0];
126 int operation
= intchromosome
[item
];
128 /* Perform operation. */
131 case 0: result
= arg1
+ arg2
; break;
132 case 1: result
= arg1
- arg2
; break;
133 case 2: result
= arg1
* arg2
; break;
136 result
= arg1
/ arg2
;
138 /* Division by zero. Set the result
139 * to a value that we cannot achieve
140 * otherwise (with default ./generate
148 results
[eq
++] = result
;
156 evaluate_entity(population
*pop
, entity
*entity
)
158 /* Get results computed by our entity. */
160 execute_entity(entity
, results
);
162 /* Count number of differences to reference results. */
164 for (int i
= 0; i
< EQNO
; i
++) {
165 if (results
[i
] != refresults
[i
])
169 /* Compute entity fitness. With no differences, fitness
170 * will be 1; with no match, fitness will be 0. With
171 * e.g. 75% match, fitness will be 0.75. */
172 /* The (double) typecast makes sure / will be floating,
173 * not integer division. */
174 entity
->fitness
= 1.0 - (double) diffcount
/ EQNO
;
179 int main(int argc
, char **argv
)
181 char *beststring
= NULL
; /* Human readable form of best solution. */
182 size_t beststrlen
= 0; /* Length of beststring. */
184 /* Load results of equations to compare the entities to. */
185 load_reference_results();
187 /* Run the algorithm several times. */
188 for (int i
= 1; i
< 10; i
++) {
189 /* Make sure different random numbers are tried in each
190 * iteration, but same results are obtained over repeated
192 random_seed(i
* 16801);
193 /* The random number generator in GAUL behaves extraordinarily
194 * poorly in case it is used to generate numbers 0..2^k-1 where
195 * k is small. Its behavior improves after some time,
196 * so this drains its initial state by asking for a random
197 * number many times. */
198 for (int j
= 0; j
< 10000; j
++)
199 (void) random_rand();
201 /* Set up the initial population and define the genetic
202 * operators (names of functions for assigning fitness,
203 * mutation, crossover, etc.). */
204 population
*pop
= ga_genesis_integer(
205 4, /* const int population_size */
206 1, /* const int num_chromo */
207 4, /* const int len_chromo */
208 NULL
, /* GAgeneration_hook generation_hook */
209 NULL
, /* GAiteration_hook iteration_hook */
210 NULL
, /* GAdata_destructor data_destructor */
211 NULL
, /* GAdata_ref_incrementor data_ref_incrementor */
212 evaluate_entity
, /* GAevaluate evaluate */
213 seed_entity
, /* GAseed seed */
214 NULL
, /* GAadapt adapt */
215 ga_select_one_sus
, /* GAselect_one select_one */
216 ga_select_two_sus
, /* GAselect_two select_two */
217 mutate_entity
, /* GAmutate mutate */
218 ga_crossover_integer_singlepoints
, /* GAcrossover crossover */
219 NULL
, /* GAreplace replace */
220 NULL
/* vpointer User data */
223 /* Set parameters of the genetic algorithm. */
224 ga_population_set_parameters(
225 pop
, /* population *pop */
226 GA_SCHEME_DARWIN
, /* const ga_scheme_type scheme */
227 GA_ELITISM_PARENTS_DIE
, /* const ga_elitism_type elitism */
228 0.9, /* double crossover */
229 0.2, /* double mutation */
230 0.0 /* double migration */
233 /* Run the genetic algorithm for 10 generations. */
235 pop
, /* population *pop */
236 10 /* const int max_generations */
239 /* Print the winner. */
240 printf("The solution with seed = %d was:\n", i
);
241 beststring
= ga_chromosome_integer_to_string(pop
, ga_get_entity_from_rank(pop
, 0), beststring
, &beststrlen
);
242 printf("%s\n", beststring
);
243 printf("With score = %f\n", ga_entity_get_fitness(ga_get_entity_from_rank(pop
, 0)));