Global: collectd → libsortnetwork
[sort-networks.git] / src / sn-evolution-cut.c
1 /**
2  * libsortnetwork - src/sn-evolution-cut.c
3  * Copyright (C) 2008-2010  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <ff at octo.it>
20  **/
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <inttypes.h>
28 #include <string.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <assert.h>
36 #include <limits.h>
37
38 #include <pthread.h>
39
40 #include <population.h>
41
42 #include "sn_network.h"
43 #include "sn_random.h"
44
45 #if !defined(__GNUC__) || !__GNUC__
46 # define __attribute__(x) /**/
47 #endif
48
49 typedef int individuum_t;
50
51 static int inputs_num = 0;
52 static int outputs_num = 0;
53 static int cuts_num = 0;
54
55 static char *initial_input_file = NULL;
56 static const sn_network_t *initial_network = NULL;
57 static char *best_output_file = NULL;
58
59 static int stats_interval = 0;
60
61 static int max_population_size = 128;
62 static population_t *population = NULL;
63
64 static int evolution_threads_num = 4;
65
66 static int do_loop = 0;
67 static uint64_t iteration_counter = 0;
68
69 static void sigint_handler (int signal __attribute__((unused)))
70 {
71   do_loop++;
72 } /* void sigint_handler */
73
74 static void exit_usage (const char *name)
75 {
76   printf ("Usage: %s [options]\n"
77       "\n"
78       "Valid options are:\n"
79       "  -i <file>     Initial input file (REQUIRED)\n"
80       "  -o <file>     Write the current best solution to <file>\n"
81       "  -O <num>      Number of outputs (default: inputs / 2)\n"
82       "  -p <num>      Size of the population (default: 128)\n"
83       "  -P <peer>     Send individuals to <peer> (may be repeated)\n"
84       "  -t <num>      Number of threads (default: 4)\n"
85       "\n",
86       name);
87   exit (1);
88 } /* void exit_usage */
89
90 int read_options (int argc, char **argv) /* {{{ */
91 {
92   int option;
93
94   while ((option = getopt (argc, argv, "i:o:O:p:P:s:t:h")) != -1)
95   {
96     switch (option)
97     {
98       case 'i':
99       {
100         if (initial_input_file != NULL)
101           free (initial_input_file);
102         initial_input_file = strdup (optarg);
103         break;
104       }
105
106       case 'o':
107       {
108         if (best_output_file != NULL)
109           free (best_output_file);
110         best_output_file = strdup (optarg);
111         break;
112       }
113
114       case 'O':
115       {
116         int tmp = atoi (optarg);
117         if (tmp > 0)
118           outputs_num = tmp;
119         break;
120       }
121
122       case 'p':
123       {
124         int tmp = atoi (optarg);
125         if (tmp > 0)
126         {
127           max_population_size = tmp;
128           population_set_size (population, (size_t) max_population_size);
129         }
130         break;
131       }
132
133       case 'P':
134       {
135         int status;
136
137         status = population_add_peer (population, optarg, /* port = */ NULL);
138         if (status != 0)
139         {
140           fprintf (stderr, "population_add_peer failed with status %i.\n",
141               status);
142         }
143         break;
144       }
145
146       case 's':
147       {
148         int tmp = atoi (optarg);
149         if (tmp > 0)
150           stats_interval = tmp;
151         break;
152       }
153
154       case 't':
155       {
156         int tmp = atoi (optarg);
157         if (tmp >= 1)
158           evolution_threads_num = tmp;
159         break;
160       }
161
162       case 'h':
163       default:
164         exit_usage (argv[0]);
165     }
166   }
167
168   return (0);
169 } /* }}} int read_options */
170
171 static int apply_cut (sn_network_t *n, int input) /* {{{ */
172 {
173   enum sn_network_cut_dir_e dir = DIR_MAX;
174
175   if (input < 0)
176   {
177     dir = DIR_MIN;
178     input *= (-1);
179   }
180   input--;
181
182   return (sn_network_cut_at (n, input, dir));
183 } /* }}} int apply_cut */
184
185 static int apply (sn_network_t *n, const individuum_t *ind) /* {{{ */
186 {
187   int i;
188
189   for (i = 0; i < cuts_num; i++)
190     apply_cut (n, ind[i]);
191
192   return (0);
193 } /* }}} int apply */
194
195 static int rate_network (const sn_network_t *n) /* {{{ */
196 {
197   int rate;
198   int i;
199
200   rate = SN_NETWORK_STAGE_NUM (n) * SN_NETWORK_INPUT_NUM (n);
201   for (i = 0; i < SN_NETWORK_STAGE_NUM (n); i++)
202   {
203     sn_stage_t *s = SN_NETWORK_STAGE_GET (n, i);
204     rate += SN_STAGE_COMP_NUM (s);
205   }
206
207   return (rate);
208 } /* }}} int rate_network */
209
210 static sn_network_t *individuum_to_network (const individuum_t *ind) /* {{{ */
211 {
212   sn_network_t *n;
213
214   n = sn_network_clone (initial_network);
215   if (n == NULL)
216     return (NULL);
217
218   apply (n, ind);
219
220   sn_network_normalize (n);
221   sn_network_compress (n);
222
223   return (n);
224 } /* }}} sn_network_t *individuum_to_network */
225
226 static int ind_rate (const void *arg) /* {{{ */
227 {
228   sn_network_t *n;
229   int status;
230
231   n = individuum_to_network (arg);
232   if (n == NULL)
233     return (-1);
234
235   status = rate_network (n);
236   sn_network_destroy (n);
237
238   return (status);
239 } /* }}} int ind_rate */
240
241 static void *ind_copy (const void *in) /* {{{ */
242 {
243   size_t s = sizeof (individuum_t) * cuts_num;
244   void *out;
245
246   out = malloc (s);
247   if (out == NULL)
248     return (NULL);
249
250   memcpy (out, in, s);
251   return (out);
252 } /* }}} void *ind_copy */
253
254 static void ind_free (void *ind) /* {{{ */
255 {
256   if (ind != NULL)
257     free (ind);
258 } /* }}} void ind_free */
259
260 static individuum_t *recombine (individuum_t *i0, individuum_t *i1) /* {{{ */
261 {
262   individuum_t *offspring;
263   int cut_at;
264   int i;
265
266   if ((i0 == NULL) || (i1 == NULL))
267     return (NULL);
268
269   offspring = malloc (sizeof (*offspring) * cuts_num);
270   if (offspring == NULL)
271     return (NULL);
272   memset (offspring, 0, sizeof (*offspring) * cuts_num);
273
274   cut_at = sn_bounded_random (0, cuts_num);
275   for (i = 0; i < cuts_num; i++)
276   {
277     if (i < cut_at)
278       offspring[i] = i0[i];
279     else
280       offspring[i] = i1[i];
281   }
282
283   return (offspring);
284 } /* }}} individuum_t *recombine */
285
286 static void random_cut (individuum_t *ind, int index) /* {{{ */
287 {
288   int max_input = inputs_num - index;
289
290   ind[index] = 0;
291   while (ind[index] == 0)
292     ind[index] = sn_bounded_random ((-1) * max_input, max_input);
293 } /* }}} void random_cut */
294
295 static void mutate (individuum_t *ind) /* {{{ */
296 {
297   int reset_input;
298   int i;
299
300   reset_input = sn_bounded_random (0, 3 * cuts_num);
301   for (i = 0; i < cuts_num; i++)
302   {
303     if (reset_input == i)
304       random_cut (ind, i);
305
306     if (sn_bounded_random (0, 100))
307       ind[i] *= (-1);
308   }
309 } /* }}} void mutate */
310
311 static int create_offspring (void) /* {{{ */
312 {
313   individuum_t *i0;
314   individuum_t *i1;
315   individuum_t *i2;
316
317   i0 = population_get_random (population);
318   i1 = population_get_random (population);
319
320   i2 = recombine (i0, i1);
321   mutate (i2);
322
323   population_insert (population, i2);
324
325   free (i0);
326   free (i1);
327   free (i2);
328
329   return (0);
330 } /* }}} int create_offspring */
331
332 static void *evolution_thread (void *arg __attribute__((unused))) /* {{{ */
333 {
334   while (do_loop == 0)
335   {
336     create_offspring ();
337     /* XXX: Not synchronized! */
338     iteration_counter++;
339   }
340
341   return ((void *) 0);
342 } /* }}} int start_evolution */
343
344 static int evolution_start (int threads_num) /* {{{ */
345 {
346   pthread_t threads[threads_num]; /* C99 ftw! */
347   int i;
348
349   for (i = 0; i < threads_num; i++)
350   {
351     int status;
352
353     status = pthread_create (&threads[i], /* attr = */ NULL,
354         evolution_thread, /* arg = */ NULL);
355     if (status != 0)
356     {
357       fprintf (stderr, "evolution_start: pthread_create[%i] failed "
358           "with status %i.\n",
359           i, status);
360       threads[i] = 0;
361     }
362   }
363
364   while (do_loop == 0)
365   {
366     int status;
367     
368     status = sleep (1);
369     if (status == 0)
370     {
371       individuum_t *ind;
372       sn_network_t *n;
373       int stages_num;
374       int comparators_num;
375       int rating;
376       int iter;
377
378       iter = iteration_counter;
379
380       ind = population_get_fittest (population);
381       n = individuum_to_network (ind);
382
383       rating = rate_network (n);
384
385       stages_num = SN_NETWORK_STAGE_NUM (n);
386       comparators_num = 0;
387       for (i = 0; i < stages_num; i++)
388       {
389         sn_stage_t *s;
390
391         s = SN_NETWORK_STAGE_GET (n, i);
392         comparators_num += SN_STAGE_COMP_NUM (s);
393       }
394
395       sn_network_destroy (n);
396       ind_free (ind);
397
398       printf ("Best after approximately %i iterations: "
399           "%i comparators in %i stages. Rating: %i.\n",
400           iter, comparators_num, stages_num, rating);
401     }
402   }
403
404   for (i = 0; i < threads_num; i++)
405   {
406     if (threads[i] == 0)
407       continue;
408     pthread_join (threads[i], /* value_ptr = */ NULL);
409   }
410
411   return (0);
412 } /* }}} int evolution_start */
413
414 int main (int argc, char **argv) /* {{{ */
415 {
416   struct sigaction sigint_action;
417   struct sigaction sigterm_action;
418   int i;
419
420   population = population_create (ind_rate, ind_copy, ind_free);
421   if (population == NULL)
422   {
423     fprintf (stderr, "population_create failed.\n");
424     return (1);
425   }
426
427 #if 0
428   population_set_serialization (population,
429       (pi_serialize_f) sn_network_serialize,
430       (pi_unserialize_f) sn_network_unserialize);
431 #endif
432
433   read_options (argc, argv);
434   if (initial_input_file == NULL)
435     exit_usage (argv[0]);
436
437   memset (&sigint_action, '\0', sizeof (sigint_action));
438   sigint_action.sa_handler = sigint_handler;
439   sigaction (SIGINT, &sigint_action, NULL);
440
441   memset (&sigterm_action, '\0', sizeof (sigterm_action));
442   sigterm_action.sa_handler = sigint_handler;
443   sigaction (SIGTERM, &sigterm_action, NULL);
444
445   /* population_start_listen_thread (population, NULL, NULL); */
446
447   initial_network = sn_network_read_file (initial_input_file);
448   if (initial_network == NULL)
449   {
450     fprintf (stderr, "Reading input file failed.\n");
451     exit (EXIT_FAILURE);
452   }
453
454   inputs_num = SN_NETWORK_INPUT_NUM(initial_network);
455   if (outputs_num == 0)
456     outputs_num = inputs_num / 2;
457
458   if (outputs_num >= inputs_num)
459   {
460     fprintf (stderr, "Number of inputs (%i) is smaller than "
461         "(or equal to) the number of outputs (%i).\n",
462         inputs_num, outputs_num);
463     exit (EXIT_FAILURE);
464   }
465
466   cuts_num = inputs_num - outputs_num;
467   assert (cuts_num >= 1);
468
469   for (i = 0; i < max_population_size; i++)
470   { /* create a random initial individuum */
471     individuum_t *ind;
472     int i;
473
474     ind = malloc (sizeof (*ind) * cuts_num);
475     if (ind == NULL)
476     {
477       fprintf (stderr, "malloc failed.\n");
478       exit (EXIT_FAILURE);
479     }
480
481     for (i = 0; i < cuts_num; i++)
482       random_cut (ind, i);
483
484     population_insert (population, ind);
485     ind_free (ind);
486   }
487
488   printf ("Current configuration:\n"
489       "  Initial network:   %s\n"
490       "  Number of inputs:  %3i\n"
491       "  Number of outputs: %3i\n"
492       "  Population size:   %3i\n"
493       "=======================\n",
494       initial_input_file, inputs_num, outputs_num, max_population_size);
495
496   evolution_start (evolution_threads_num);
497
498   printf ("Exiting after %"PRIu64" iterations.\n",
499       iteration_counter);
500
501   {
502     individuum_t *ind;
503     sn_network_t *n;
504
505     ind = population_get_fittest (population);
506     if (ind != NULL)
507     {
508       n = individuum_to_network (ind);
509       if (best_output_file != NULL)
510         sn_network_write_file (n, best_output_file);
511       sn_network_show (n);
512       sn_network_destroy (n);
513     }
514   }
515
516   population_destroy (population);
517
518   return (0);
519 } /* }}} int main */
520
521 /* vim: set shiftwidth=2 softtabstop=2 et fdm=marker : */