sn-count-markov: Add tool to determine the circle length of random walks.
[sort-networks.git] / src / sn-count-markov.c
1 /**
2  * libsortnetwork - src/sn-count-markov.c
3  * Copyright (C) 2008-2011  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 <glib.h>
45
46 #include "sn_network.h"
47 #include "sn_random.h"
48
49 #if !defined(__GNUC__) || !__GNUC__
50 # define __attribute__(x) /**/
51 #endif
52
53 static int inputs_num = 16;
54
55 static char *initial_input_file = NULL;
56
57 static GTree *all_networks = NULL;
58 static uint64_t cycles_num = 0;
59
60 static _Bool do_loop = 1;
61 static uint64_t max_iterations = 0;
62
63 static void sigint_handler (int signal __attribute__((unused)))
64 {
65   do_loop = 0;
66 } /* void sigint_handler */
67
68 static gint compare_hashval (gconstpointer p0, gconstpointer p1) /* {{{ */
69 {
70   const uint64_t *i0 = (gconstpointer) p0;
71   const uint64_t *i1 = (gconstpointer) p1;
72
73   if ((*i0) < (*i1))
74     return (-1);
75   else if ((*i0) > (*i1))
76     return (1);
77   else
78     return (0);
79 } /* }}} gint account_network_compare */
80
81 static int account_network (const sn_network_t *n, uint64_t iteration) /* {{{ */
82 {
83   uint64_t  key;
84   uint64_t *key_ptr;
85   uint64_t *value_ptr;
86
87   if (n == NULL)
88     return (EINVAL);
89
90   if (iteration == 0)
91     printf ("# iteration cyclelength\n");
92
93   key = sn_network_get_hashval (n);
94   key_ptr = &key;
95
96   value_ptr = (uint64_t *) g_tree_lookup (all_networks, (gconstpointer) key_ptr);
97   if (value_ptr != NULL)
98   {
99     uint64_t cycle_length = iteration - (*value_ptr);
100     printf ("%"PRIu64" %"PRIu64"\n", iteration, cycle_length);
101
102     cycles_num++;
103     *value_ptr = iteration;
104     return (0);
105   }
106
107   key_ptr = malloc (sizeof (*key_ptr));
108   if (key_ptr == NULL)
109     return (ENOMEM);
110   *key_ptr = key;
111
112   value_ptr = malloc (sizeof (*value_ptr));
113   if (value_ptr == NULL)
114   {
115     free (key_ptr);
116     return (ENOMEM);
117   }
118   *value_ptr = iteration;
119
120   g_tree_insert (all_networks, (gpointer) key_ptr, (gpointer) value_ptr);
121   return (0);
122 } /* }}} int account_network */
123
124 static void exit_usage (const char *name, int status)
125 {
126   printf ("Usage: %s [options]\n"
127       "\n"
128       "Valid options are:\n"
129       "  -i <file>     Initial input file (REQUIRED)\n"
130       "  -o <file>     Write the current best solution to <file>\n"
131       "  -n <number>   Maximum number of steps (iterations) to perform\n"
132       "  -h            Display usage information (this message)\n"
133       "\n",
134       name);
135   exit (status);
136 } /* void exit_usage */
137
138 int read_options (int argc, char **argv)
139 {
140   int option;
141
142   while ((option = getopt (argc, argv, "i:o:n:h")) != -1)
143   {
144     switch (option)
145     {
146       case 'i':
147       {
148         if (initial_input_file != NULL)
149           free (initial_input_file);
150         initial_input_file = strdup (optarg);
151         break;
152       }
153
154       case 'n':
155       {
156         errno = 0;
157         max_iterations = (uint64_t) strtoull (optarg,
158             /* endptr = */ NULL,
159             /* base   = */ 0);
160         if (errno != 0)
161         {
162           fprintf (stderr, "Parsing integer argument failed: %s\n",
163               strerror (errno));
164           exit_usage (argv[0], EXIT_FAILURE);
165         }
166         break;
167       }
168
169       case 'h':
170       default:
171         exit_usage (argv[0], EXIT_SUCCESS);
172     }
173   }
174
175   return (0);
176 } /* int read_options */
177
178 static sn_network_t *get_next (sn_network_t *n)
179 {
180   sn_network_t *nleft;
181   sn_network_t *nright;
182   sn_network_t *nret;
183
184   if (n == NULL)
185     return (NULL);
186
187   nleft = sn_network_clone (n);
188   nright = sn_network_clone (n);
189
190   assert (nleft != NULL);
191   assert (nright != NULL);
192
193   /* combine the two parents */
194   nret = sn_network_combine (nleft, nright);
195
196   sn_network_destroy (nleft);
197   sn_network_destroy (nright);
198
199   /* randomly chose an input and do a min- or max-cut. */
200   while (SN_NETWORK_INPUT_NUM (nret) > inputs_num)
201   {
202     int pos;
203     enum sn_network_cut_dir_e dir;
204
205     pos = sn_bounded_random (0, SN_NETWORK_INPUT_NUM (nret) - 1);
206     dir = (sn_bounded_random (0, 1) == 0) ? DIR_MIN : DIR_MAX;
207
208     assert ((pos >= 0) && (pos < SN_NETWORK_INPUT_NUM (nret)));
209
210     sn_network_cut_at (nret, pos, dir);
211   }
212
213   /* Make sure one network is always represented in the same way. */
214   sn_network_unify (nret);
215
216   assert (SN_NETWORK_INPUT_NUM (nret) == inputs_num);
217   return (nret);
218 } /* sn_network_t *get_next */
219
220 static void random_walk (sn_network_t *n)
221 {
222   uint64_t iteration_counter = 0;
223
224   while (do_loop)
225   {
226     sn_network_t *next = get_next (n);
227
228     if (next == NULL)
229     {
230       fprintf (stderr, "get_next() failed.\n");
231       return;
232     }
233
234     account_network (next, iteration_counter);
235
236     sn_network_destroy (n);
237     n = next;
238     iteration_counter++;
239
240     if ((max_iterations > 0) && (iteration_counter >= max_iterations))
241       break;
242   } /* while (do_loop) */
243
244   sn_network_destroy (n);
245
246   printf ("# Exiting after %"PRIu64" iterations.\n", iteration_counter);
247 } /* void random_walk */
248
249 int main (int argc, char **argv)
250 {
251   sn_network_t *start_network;
252
253   struct sigaction sigint_action;
254   struct sigaction sigterm_action;
255
256   read_options (argc, argv);
257   if (initial_input_file == NULL)
258     exit_usage (argv[0], EXIT_FAILURE);
259
260   memset (&sigint_action, '\0', sizeof (sigint_action));
261   sigint_action.sa_handler = sigint_handler;
262   sigaction (SIGINT, &sigint_action, NULL);
263
264   memset (&sigterm_action, '\0', sizeof (sigterm_action));
265   sigterm_action.sa_handler = sigint_handler;
266   sigaction (SIGTERM, &sigterm_action, NULL);
267
268   all_networks = g_tree_new (compare_hashval);
269   if (all_networks == NULL)
270   {
271     fprintf (stderr, "g_tree_new() failed.\n");
272     exit (EXIT_FAILURE);
273   }
274
275   start_network = sn_network_read_file (initial_input_file);
276   if (start_network == NULL)
277   {
278     printf ("start_network == NULL\n");
279     exit (EXIT_FAILURE);
280   }
281
282   inputs_num = SN_NETWORK_INPUT_NUM(start_network);
283
284   random_walk (start_network);
285   start_network = NULL;
286
287   printf ("# %"PRIu64" cycles were detected.\n", cycles_num);
288
289   exit (EXIT_SUCCESS);
290   return (0);
291 } /* int main */
292
293 /* vim: set shiftwidth=2 softtabstop=2 fdm=marker : */