1fcc41dbb7f93487e5063d2227f85b824ceb16af
[sort-networks.git] / src / sn-markov.c
1 /**
2  * libsortnetwork - src/sn-markov.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 #ifndef _ISOC99_SOURCE
23 # define _ISOC99_SOURCE
24 #endif
25 #ifndef _POSIX_C_SOURCE
26 # define _POSIX_C_SOURCE 200809L
27 #endif
28 #ifndef _XOPEN_SOURCE
29 # define _XOPEN_SOURCE 700
30 #endif
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <inttypes.h>
36 #include <string.h>
37
38 #include <unistd.h>
39 #include <signal.h>
40 #include <assert.h>
41 #include <limits.h>
42 #include <errno.h>
43
44 #include "sn_network.h"
45 #include "sn_random.h"
46
47 #if !defined(__GNUC__) || !__GNUC__
48 # define __attribute__(x) /**/
49 #endif
50
51 static int inputs_num = 16;
52
53 static char *initial_input_file = NULL;
54 static char *best_output_file = NULL;
55
56 static sn_network_t *best_solution = NULL;
57 static int best_rating = INT_MAX;
58
59 static _Bool do_loop = 1;
60 static uint64_t max_iterations = 0;
61
62 static void sigint_handler (int signal __attribute__((unused)))
63 {
64   do_loop = 0;
65 } /* void sigint_handler */
66
67 static void exit_usage (const char *name, int status)
68 {
69   printf ("Usage: %s [options]\n"
70       "\n"
71       "Valid options are:\n"
72       "  -i <file>     Initial input file (REQUIRED)\n"
73       "  -o <file>     Write the current best solution to <file>\n"
74       "  -n <number>   Maximum number of steps (iterations) to perform\n"
75       "  -h            Display usage information (this message)\n"
76       "\n",
77       name);
78   exit (status);
79 } /* void exit_usage */
80
81 int read_options (int argc, char **argv)
82 {
83   int option;
84
85   while ((option = getopt (argc, argv, "i:o:n:h")) != -1)
86   {
87     switch (option)
88     {
89       case 'i':
90       {
91         if (initial_input_file != NULL)
92           free (initial_input_file);
93         initial_input_file = strdup (optarg);
94         break;
95       }
96
97       case 'o':
98       {
99         if (best_output_file != NULL)
100           free (best_output_file);
101         best_output_file = strdup (optarg);
102         break;
103       }
104
105       case 'n':
106       {
107         errno = 0;
108         max_iterations = (uint64_t) strtoull (optarg,
109             /* endptr = */ NULL,
110             /* base   = */ 0);
111         if (errno != 0)
112         {
113           fprintf (stderr, "Parsing integer argument failed: %s\n",
114               strerror (errno));
115           exit_usage (argv[0], EXIT_FAILURE);
116         }
117         break;
118       }
119
120       case 'h':
121       default:
122         exit_usage (argv[0], EXIT_SUCCESS);
123     }
124   }
125
126   return (0);
127 } /* int read_options */
128
129 static int rate_network (const sn_network_t *n)
130 {
131   int rate;
132
133   rate = SN_NETWORK_STAGE_NUM (n) * SN_NETWORK_INPUT_NUM (n);
134   rate += sn_network_get_comparator_num (n);
135
136   return (rate);
137 } /* int rate_network */
138
139 static sn_network_t *get_next (sn_network_t *n)
140 {
141   sn_network_t *nleft;
142   sn_network_t *nright;
143   sn_network_t *nret;
144
145   if (n == NULL)
146     return (NULL);
147
148   nleft = sn_network_clone (n);
149   nright = sn_network_clone (n);
150
151   assert (nleft != NULL);
152   assert (nright != NULL);
153
154   /* combine the two parents */
155   nret = sn_network_combine (nleft, nright);
156
157   sn_network_destroy (nleft);
158   sn_network_destroy (nright);
159
160   /* randomly chose an input and do a min- or max-cut. */
161   while (SN_NETWORK_INPUT_NUM (nret) > inputs_num)
162   {
163     int pos;
164     enum sn_network_cut_dir_e dir;
165
166     pos = sn_bounded_random (0, SN_NETWORK_INPUT_NUM (nret) - 1);
167     dir = (sn_bounded_random (0, 1) == 0) ? DIR_MIN : DIR_MAX;
168
169     assert ((pos >= 0) && (pos < SN_NETWORK_INPUT_NUM (nret)));
170
171     sn_network_cut_at (nret, pos, dir);
172   }
173
174   /* compress the network to get a compact representation */
175   sn_network_compress (nret);
176
177   assert (SN_NETWORK_INPUT_NUM (nret) == inputs_num);
178   return (nret);
179 } /* sn_network_t *get_next */
180
181 static void random_walk (sn_network_t *n)
182 {
183   uint64_t iteration_counter = 0;
184
185   while (do_loop)
186   {
187     sn_network_t *next = get_next (n);
188     int rating;
189
190     if (next == NULL)
191     {
192       fprintf (stderr, "get_next() failed.\n");
193       return;
194     }
195
196     rating = rate_network (next);
197     if (rating < best_rating)
198     {
199       printf ("New best rating (%i) after %"PRIu64" iterations.\n",
200           rating, iteration_counter);
201
202       best_rating = rating;
203       sn_network_destroy (best_solution);
204       best_solution = sn_network_clone (next);
205     }
206
207     sn_network_destroy (n);
208     n = next;
209     iteration_counter++;
210
211     if ((max_iterations > 0) && (iteration_counter >= max_iterations))
212       break;
213   } /* while (do_loop) */
214
215   sn_network_destroy (n);
216
217   printf ("Exiting after %"PRIu64" iterations.\n", iteration_counter);
218 } /* void random_walk */
219
220 int main (int argc, char **argv)
221 {
222   sn_network_t *start_network;
223
224   struct sigaction sigint_action;
225   struct sigaction sigterm_action;
226
227   read_options (argc, argv);
228   if (initial_input_file == NULL)
229     exit_usage (argv[0], EXIT_FAILURE);
230
231   memset (&sigint_action, '\0', sizeof (sigint_action));
232   sigint_action.sa_handler = sigint_handler;
233   sigaction (SIGINT, &sigint_action, NULL);
234
235   memset (&sigterm_action, '\0', sizeof (sigterm_action));
236   sigterm_action.sa_handler = sigint_handler;
237   sigaction (SIGTERM, &sigterm_action, NULL);
238
239   start_network = sn_network_read_file (initial_input_file);
240   if (start_network == NULL)
241   {
242     printf ("start_network == NULL\n");
243     exit (EXIT_FAILURE);
244   }
245
246   inputs_num = SN_NETWORK_INPUT_NUM(start_network);
247
248   printf ("Current configuration:\n"
249       "  Initial network:  %s\n"
250       "  Number of inputs: %3i\n"
251       "=======================\n",
252       initial_input_file, inputs_num);
253
254   random_walk (start_network);
255   start_network = NULL;
256
257   if (best_solution != NULL)
258   {
259     if (best_output_file != NULL)
260       sn_network_write_file (best_solution, best_output_file);
261
262     printf ("=== Best solution (rating: %i) ===\n", best_rating);
263     sn_network_normalize (best_solution);
264     sn_network_show (best_solution);
265     sn_network_destroy (best_solution);
266   }
267
268   exit (EXIT_SUCCESS);
269   return (0);
270 } /* int main */
271
272 /* vim: set shiftwidth=2 softtabstop=2 : */