src/sn-evolution2.c: Implement the -I option.
[sort-networks.git] / src / sn-evolution2.c
1 /**
2  * collectd - src/sn-evolution.c
3  * Copyright (C) 2008,2009  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 <octo at verplant.org>
20  **/
21
22 #ifndef _ISOC99_SOURCE
23 # define _ISOC99_SOURCE
24 #endif
25 #ifndef _POSIX_C_SOURCE
26 # define _POSIX_C_SOURCE 200112L
27 #endif
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <string.h>
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include <assert.h>
40 #include <limits.h>
41
42 #include <math.h>
43
44 #include <pthread.h>
45
46 #include <population.h>
47
48 #include "sn_network.h"
49 #include "sn_random.h"
50
51 #define SNE_MIN(a,b) ((a) < (b) ? (a) : (b))
52 #define SNE_MAX(a,b) ((a) > (b) ? (a) : (b))
53
54 /* Yes, this is ugly, but the GNU libc doesn't export it with the above flags.
55  * */
56 char *strdup (const char *s);
57
58 static uint64_t iteration_counter = 0;
59 static int inputs_num = -1;
60
61 static int comparators_num = -1;
62
63 static char *initial_input_file = NULL;
64 static char *best_output_file = NULL;
65
66 static int stats_interval = 0;
67
68 static int max_population_size = 128;
69 static population_t *population;
70
71 static int evolution_threads_num = 4;
72
73 static int do_loop = 0;
74
75 static void sigint_handler (int signal)
76 {
77   do_loop++;
78 } /* void sigint_handler */
79
80 static void exit_usage (const char *name)
81 {
82   printf ("Usage: %s [options]\n"
83       "\n"
84       "Valid options are:\n"
85       "  -i <inputs>       Number of inputs\n"
86       "  -c <comparators>  Number of comparators\n"
87       "  -I <file>         Initial input file\n"
88       "  -o <file>         Write the current best solution to <file>\n"
89       "  -p <num>          Size of the population (default: 128)\n"
90       "  -P <peer>         Send individuals to <peer> (may be repeated)\n"
91       "  -t <num>          Number of threads (default: 4)\n"
92       "\n",
93       name);
94   exit (1);
95 } /* void exit_usage */
96
97 int read_options (int argc, char **argv)
98 {
99   int option;
100
101   while ((option = getopt (argc, argv, "i:c:I:o:p:P:s:t:h")) != -1)
102   {
103     switch (option)
104     {
105       case 'i':
106       {
107         int tmp;
108         tmp = atoi (optarg);
109         if (tmp > 0)
110           inputs_num = tmp;
111         break;
112       }
113
114       case 'c':
115       {
116         int tmp;
117         tmp = atoi (optarg);
118         if (tmp > 0)
119           comparators_num = tmp;
120         break;
121       }
122
123       case 'I':
124       {
125         if (initial_input_file != NULL)
126           free (initial_input_file);
127         initial_input_file = strdup (optarg);
128         break;
129       }
130
131       case 'o':
132       {
133         if (best_output_file != NULL)
134           free (best_output_file);
135         best_output_file = strdup (optarg);
136         break;
137       }
138
139       case 'p':
140       {
141         int tmp = atoi (optarg);
142         if (tmp > 0)
143         {
144           max_population_size = tmp;
145           population_set_size (population, (size_t) max_population_size);
146         }
147         break;
148       }
149
150       case 'P':
151       {
152         int status;
153
154         status = population_add_peer (population, optarg, /* port = */ NULL);
155         if (status != 0)
156         {
157           fprintf (stderr, "population_add_peer failed with status %i.\n",
158               status);
159         }
160         break;
161       }
162
163       case 's':
164       {
165         int tmp = atoi (optarg);
166         if (tmp > 0)
167           stats_interval = tmp;
168         break;
169       }
170
171       case 't':
172       {
173         int tmp = atoi (optarg);
174         if (tmp >= 1)
175           evolution_threads_num = tmp;
176         break;
177       }
178
179       case 'h':
180       default:
181         exit_usage (argv[0]);
182     }
183   }
184
185   return (0);
186 } /* int read_options */
187
188 static int rate_network (sn_network_t *n) /* {{{ */
189 {
190   int test_pattern[n->inputs_num];
191   int values[n->inputs_num];
192
193   int patterns_sorted = 0;
194   int patterns_failed = 0;
195
196   memset (test_pattern, 0, sizeof (test_pattern));
197   while (42)
198   {
199     int previous;
200     int overflow;
201     int status;
202     int i;
203
204     /* Copy the current pattern and let the network sort it */
205     memcpy (values, test_pattern, sizeof (values));
206     status = sn_network_sort (n, values);
207     if (status != 0)
208       return (status);
209
210     /* Check if the array is now sorted. */
211     previous = values[0];
212     status = 0;
213     for (i = 1; i < n->inputs_num; i++)
214     {
215       if (previous > values[i])
216       {
217         patterns_failed++;
218         status = -1;
219         break;
220       }
221       previous = values[i];
222     }
223
224     if (status == 0)
225       patterns_sorted++;
226
227     /* Generate the next test pattern */
228     overflow = 1;
229     for (i = 0; i < n->inputs_num; i++)
230     {
231       if (test_pattern[i] == 0)
232       {
233         test_pattern[i] = 1;
234         overflow = 0;
235         break;
236       }
237       else
238       {
239         test_pattern[i] = 0;
240         overflow = 1;
241       }
242     }
243
244     /* Break out of the while loop if we tested all possible patterns */
245     if (overflow == 1)
246       break;
247   } /* while (42) */
248
249   /* All tests successfull */
250   return (SN_NETWORK_STAGE_NUM (n) + patterns_failed);
251 } /* }}} int rate_network */
252
253 static sn_comparator_t get_random_comparator (void) /* {{{ */
254 {
255   sn_comparator_t c;
256
257   c.min = sn_bounded_random (0, inputs_num - 1);
258   c.max = c.min;
259   while (c.max == c.min)
260     c.max = sn_bounded_random (0, inputs_num - 1);
261
262   return (c);
263 } /* }}} sn_comparator_t get_random_comparator */
264
265 static int mutate_network (sn_comparator_t *comparators, int comparators_num)
266 {
267   static int old_comparators_num = -1;
268   static int cut_off = 1000000;
269
270   int i;
271
272   if (old_comparators_num != comparators_num)
273   {
274     double p;
275
276     p = powl (0.5, (1.0 / ((double) comparators_num)));
277     cut_off = (int) (((double) 1000000) * p);
278
279     old_comparators_num = comparators_num;
280   }
281
282   for (i = 0; i < comparators_num; i++)
283     if (sn_bounded_random (0, 1000000) >= cut_off)
284       comparators[i] = get_random_comparator ();
285
286   return (0);
287 } /* int mutate_network */
288
289 static int create_offspring (void)
290 {
291   sn_network_t *p0;
292   sn_comparator_t p0_comparators[comparators_num];
293   int p0_comparators_num;
294
295   sn_network_t *p1;
296   sn_comparator_t p1_comparators[comparators_num];
297   int p1_comparators_num;
298
299   sn_network_t *n;
300   sn_comparator_t n_comparators[comparators_num];
301   int n_comparators_num;
302
303   int i;
304
305   p0 = population_get_random (population);
306   p1 = population_get_random (population);
307
308   assert (p0 != NULL);
309   assert (p1 != NULL);
310
311   p0_comparators_num = 0;
312   for (i = 0; i < SN_NETWORK_STAGE_NUM (p0); i++)
313   {
314     sn_stage_t *s;
315     int j;
316
317     s = SN_NETWORK_STAGE_GET (p0, i);
318
319     for (j = 0; j < SN_STAGE_COMP_NUM (s); j++)
320     {
321       sn_comparator_t *c;
322
323       c = SN_STAGE_COMP_GET (s, j);
324       p0_comparators[p0_comparators_num] = *c;
325       p0_comparators_num++;
326     }
327   }
328   while (p0_comparators_num < comparators_num)
329   {
330     p0_comparators[p0_comparators_num] = get_random_comparator ();
331     p0_comparators_num++;
332   }
333   sn_network_destroy (p0);
334   p0 = NULL;
335
336   p1_comparators_num = 0;
337   for (i = 0; i < SN_NETWORK_STAGE_NUM (p1); i++)
338   {
339     sn_stage_t *s;
340     int j;
341
342     s = SN_NETWORK_STAGE_GET (p1, i);
343
344     for (j = 0; j < SN_STAGE_COMP_NUM (s); j++)
345     {
346       sn_comparator_t *c;
347
348       c = SN_STAGE_COMP_GET (s, j);
349       p1_comparators[p1_comparators_num] = *c;
350       p1_comparators_num++;
351     }
352   }
353   while (p1_comparators_num < comparators_num)
354   {
355     p1_comparators[p1_comparators_num] = get_random_comparator ();
356     p1_comparators_num++;
357   }
358   sn_network_destroy (p1);
359   p1 = NULL;
360
361   n_comparators_num = sn_bounded_random (SNE_MIN (p0_comparators_num, p1_comparators_num),
362       SNE_MAX (p0_comparators_num, p1_comparators_num));
363
364   for (i = 0; i < n_comparators_num; i++)
365   {
366     if (i >= p0_comparators_num)
367       n_comparators[i] = p1_comparators[i];
368     else if (i >= p1_comparators_num)
369       n_comparators[i] = p0_comparators[i];
370     else if (sn_bounded_random (0, 1) == 0)
371       n_comparators[i] = p1_comparators[i];
372     else
373       n_comparators[i] = p0_comparators[i];
374
375   }
376
377   mutate_network (n_comparators, n_comparators_num);
378
379   n = sn_network_create (inputs_num);
380   for (i = 0; i < n_comparators_num; i++)
381     sn_network_comparator_add (n, &n_comparators[i]);
382
383   /* compress the network to get a compact representation */
384   sn_network_compress (n);
385
386   assert (SN_NETWORK_INPUT_NUM (n) == inputs_num);
387
388   population_insert (population, n);
389
390   sn_network_destroy (n);
391
392   return (0);
393 } /* int create_offspring */
394
395 static void *evolution_thread (void *arg)
396 {
397   while (do_loop == 0)
398   {
399     create_offspring ();
400     /* XXX: Not synchronized! */
401     iteration_counter++;
402   }
403
404   return ((void *) 0);
405 } /* int start_evolution */
406
407 static int evolution_start (int threads_num)
408 {
409   pthread_t threads[threads_num]; /* C99 ftw! */
410   int i;
411
412   for (i = 0; i < threads_num; i++)
413   {
414     int status;
415
416     status = pthread_create (&threads[i], /* attr = */ NULL,
417         evolution_thread, /* arg = */ NULL);
418     if (status != 0)
419     {
420       fprintf (stderr, "evolution_start: pthread_create[%i] failed "
421           "with status %i.\n",
422           i, status);
423       threads[i] = 0;
424     }
425   }
426
427   while (do_loop == 0)
428   {
429     int status;
430     
431     status = sleep (1);
432     if (status == 0)
433     {
434       sn_network_t *n;
435       int stages_num;
436       int comparators_num;
437       int rating;
438       int iter;
439
440       iter = iteration_counter;
441
442       n = population_get_fittest (population);
443
444       rating = rate_network (n);
445
446       stages_num = SN_NETWORK_STAGE_NUM (n);
447       comparators_num = 0;
448       for (i = 0; i < stages_num; i++)
449       {
450         sn_stage_t *s;
451
452         s = SN_NETWORK_STAGE_GET (n, i);
453         comparators_num += SN_STAGE_COMP_NUM (s);
454       }
455
456       sn_network_destroy (n);
457
458       printf ("Best after approximately %i iterations: "
459           "%i comparators in %i stages. Rating: %i (%i not sorted).\n",
460           iter, comparators_num, stages_num, rating,
461           rating - stages_num);
462     }
463   }
464
465   for (i = 0; i < threads_num; i++)
466   {
467     if (threads[i] == 0)
468       continue;
469     pthread_join (threads[i], /* value_ptr = */ NULL);
470   }
471
472   return (0);
473 } /* int evolution_start */
474
475 int main (int argc, char **argv)
476 {
477   struct sigaction sigint_action;
478   struct sigaction sigterm_action;
479
480   population = population_create ((pi_rate_f) rate_network,
481       (pi_copy_f) sn_network_clone,
482       (pi_free_f) sn_network_destroy);
483   if (population == NULL)
484   {
485     fprintf (stderr, "population_create failed.\n");
486     return (1);
487   }
488
489   population_set_serialization (population,
490       (pi_serialize_f) sn_network_serialize,
491       (pi_unserialize_f) sn_network_unserialize);
492
493   read_options (argc, argv);
494
495   if ((inputs_num <= 0) || (comparators_num <= 0))
496     exit_usage (argv[0]);
497
498   memset (&sigint_action, '\0', sizeof (sigint_action));
499   sigint_action.sa_handler = sigint_handler;
500   sigaction (SIGINT, &sigint_action, NULL);
501
502   memset (&sigterm_action, '\0', sizeof (sigterm_action));
503   sigterm_action.sa_handler = sigint_handler;
504   sigaction (SIGTERM, &sigterm_action, NULL);
505
506   population_start_listen_thread (population, NULL, NULL);
507
508   if (initial_input_file != NULL)
509   {
510     sn_network_t *n;
511
512     n = sn_network_read_file (initial_input_file);
513     if (n == NULL)
514     {
515       fprintf (stderr, "Cannot read network from `%s'.\n",
516           initial_input_file);
517       exit (EXIT_FAILURE);
518     }
519
520     if (n->inputs_num != inputs_num)
521     {
522       fprintf (stderr, "Network `%s' has %i inputs, but %i were configured "
523           "on the command line.\n",
524           initial_input_file, n->inputs_num, inputs_num);
525       exit (EXIT_FAILURE);
526     }
527
528     population_insert (population, n);
529     sn_network_destroy (n);
530   }
531   else /* if (initial_input_file == NULL) */
532   {
533     sn_network_t *n;
534     int i;
535
536     n = sn_network_create (inputs_num);
537     for (i = 0; i < comparators_num; i++)
538     {
539       sn_comparator_t c;
540
541       c = get_random_comparator ();
542       sn_network_comparator_add (n, &c);
543     }
544
545     population_insert (population, n);
546     sn_network_destroy (n);
547   }
548
549   printf ("Current configuration:\n"
550       "  Number of inputs:      %3i\n"
551       "  Number of comparators: %3i\n"
552       "  Population size:       %3i\n"
553       "=======================\n",
554       inputs_num, comparators_num, max_population_size);
555
556   evolution_start (evolution_threads_num);
557
558   printf ("Exiting after %llu iterations.\n",
559       (unsigned long long) iteration_counter);
560
561   {
562     sn_network_t *n;
563
564     n = population_get_fittest (population);
565     if (n != NULL)
566     {
567       if (best_output_file != NULL)
568         sn_network_write_file (n, best_output_file);
569       sn_network_show (n);
570       sn_network_destroy (n);
571     }
572   }
573
574   population_destroy (population);
575
576   return (0);
577 } /* int main */
578
579 /* vim: set shiftwidth=2 softtabstop=2 : */