Crossings Minimization  1.0
min_crossings.c
Go to the documentation of this file.
1 /**
2  * @file min_crossings.c
3  * @brief Main program for crossing minimization heuristics
4  * @author Matt Stallmann
5  * @date 2008/12/29
6  * $Id: min_crossings.c 135 2016-01-13 20:27:33Z mfms $
7  */
8 
9 
10 #include<stdio.h>
11 #include<stdlib.h>
12 #include<string.h>
13 #include<unistd.h> /* getopt() */
14 #include<getopt.h> /* for Linux */
15 #include<limits.h>
16 #include<assert.h>
17 #include<libgen.h> /* basename() */
18 #include<float.h> /* DBL_MAX */
19 
20 #ifdef _OPENMP
21 #include<omp.h>
22 #endif
23 
24 #include"stats.h"
25 #include"defs.h"
26 #include"heuristics.h"
27 #include"graph_io.h"
28 #include"graph.h"
29 #include"crossings.h"
30 #include"channel.h"
31 #include"min_crossings.h"
32 #include"order.h"
33 #include"priority_edges.h"
34 #include"timing.h"
35 #include"random.h"
36 
37 // definition of command-line options with default values
38 
39 char * heuristic = "";
40 char * preprocessor = "";
41 int max_iterations = INT_MAX;
42 double runtime = 0;
43 double max_runtime = DBL_MAX;
44 double start_time = 0;
52 int capture_iteration = INT_MIN; /* because -1 is a possible iteration */
53 bool favored_edges = false;
54 bool randomize_order = false;
55 /**
56  * @todo not clear which value of this option works best; stay tuned ...
57  */
58 bool balanced_weight = false;
59 bool produce_output = false;
60 char * output_base_name = NULL;
61 
62 bool verbose = false;
63 int trace_freq = -1;
64 
65 // definition of order saving structures
71 
72 /** buffer to be used for all output file names */
74 
75 static bool do_post_processing = false;
76 
77 /**
78  * prints usage message
79  *
80  * @todo sort these alphabetically and make them more meaningful and/or use
81  * long options
82  */
83 static void printUsage( void )
84 {
85  printf( "Usage: min_crossings [opts] file.dot file.ord\n" );
86  printf( " where opts is one or more of the following\n" );
87  printf(
88  " -h (median | bary | mod_bary | mcn | sifting | mce | mce_s | mse\n"
89  " | static_bary | alt_bary | up_down_bary | rotate_bary | slab_bary {parallel barycenter versions})\n"
90  " [main heuristic - default none]\n"
91  " -p (bfs | dfs | mds) [preprocessing - default none]\n"
92  " -z if post processing (repeated swaps until no improvement) is desired\n"
93  " -i MAX_ITERATIONS [stop if no improvement]\n"
94  " -R SEED edge list, node list, or sequence of layers will be randomized\n"
95  " after each pass of mod_bary, mce, mcn, mse, sifting, etc.\n"
96  " to break ties differently when sorting; SEED is an integer seed\n"
97  " -r SECONDS = maximum runtime [stop if no improvement]\n"
98  " -c ITERATION [capture the order after this iteration in a file]\n"
99  " -P PARETO_OBJECTIVES (b_t | s_t | b_s) pair of objectives for Pareto optima\n"
100  " b = bottleneck, t = total, s = stretch (default = none)\n"
101  " -o BASE produce file(s) with name(s) BASE-h.ord, where h is the heuristic used\n"
102  " -o _ (underscore) means use the base name of the dot file\n"
103  " -w (none | avg | left) [adjust weights in barycenter, default left, but avg in parallel versions]\n"
104  " -b average the averages of the two neighboring layers when computing barycenter weights\n"
105  " [this is the default for parallel versions]\n"
106  " -s (layer | degree | random) [sifting variation - see paper]\n"
107  " -e (nodes | edges | early | one_node )\n"
108  " [mce variation - default is nodes: pass ends when all nodes are marked]\n"
109  " -g (total | max) [what sifting is based on] [default: total for sifting, mcn; max for mce]\n"
110  " [not implemented yet]\n"
111  " -v to get verbose information about the graph\n"
112  " -t trace_freq, if trace printout is desired, 0 means only at the end of a pass, > 0 sets frequency\n"
113  " -f create a special .dot file of 'favored' edges; used for visualizing\n"
114  " -k NUMBER_OF_PROCESSORS (for simulation); currently supports 0 or 1\n"
115  " [0 means unlimited and is default for parallel barycenter versions]\n"
116  " -m number of OpenMP threads [default: 1]\n"
117  );
118 }
119 
120 /**
121  * Truncate the string in buffer so that it ends before the first '.' and
122  * copy it into the buffer.
123  *
124  * @return a pointer to the position of the last '/' so as to remove the
125  * directory component of the name in the buffer
126  *
127  * Used to emulate the 'basename' command
128  *
129  * @todo there must be a better way to do this, but basename() by itself does
130  * not work.
131  */
132 static char * base_name( char * buffer )
133 {
134  char * without_directory = basename( buffer );
135  char * pointer_to_last_period = strrchr( without_directory, '.' );
136  *pointer_to_last_period = '\0';
137  return without_directory;
138 }
139 
140 static void runPreprocessor( void )
141 {
142  printf( "--- Running preprocessor %s\n", preprocessor );
143  if( strcmp( preprocessor, "" ) == 0 )
144  ; /* do nothing */
145  else if( strcmp( preprocessor, "bfs" ) == 0 )
147  else if( strcmp( preprocessor, "dfs" ) == 0 )
149  else if( strcmp( preprocessor, "mds" ) == 0 )
151  else
152  {
153  printf( "Bad preprocessor '%s'\n", preprocessor );
154  printUsage();
155  exit( EXIT_FAILURE );
156  }
157 }
158 
159 /**
160  * @todo It would be nice to have a way to run two heuristics, one after the
161  * other. Not really needed - can always use the output file of one as input
162  * to the other
163  */
164 static void runHeuristic( void )
165 {
166  printf( "=== Running heuristic %s\n", heuristic );
167  if( strcmp( heuristic, "" ) == 0 )
168  ; /* do nothing */
169  else if( strcmp( heuristic, "median" ) == 0 )
170  median();
171  else if( strcmp( heuristic, "bary" ) == 0 )
172  barycenter();
173  else if( strcmp( heuristic, "mod_bary" ) == 0 )
175  else if( strcmp( heuristic, "static_bary" ) == 0 ) {
178  balanced_weight = true;
180  }
181  else if( strcmp( heuristic, "alt_bary" ) == 0 ) {
184  balanced_weight = true;
186  }
187  else if( strcmp( heuristic, "up_down_bary" ) == 0 ) {
191  }
192  else if( strcmp( heuristic, "slab_bary" ) == 0 ) {
193  // number_of_processors determines size of slab, must be > 0
195  slabBarycenter();
196  }
197  else if( strcmp( heuristic, "rotate_bary" ) == 0 ) {
200  balanced_weight = true;
202  }
203  else if ( strcmp( heuristic, "mcn" ) == 0 )
205  else if ( strcmp( heuristic, "mce_s" ) == 0 )
207  else if ( strcmp( heuristic, "sifting" ) == 0 )
208  sifting();
209  else if( strcmp( heuristic, "mce" ) == 0 ) {
211  }
212  else if( strcmp( heuristic, "mse" ) == 0 ) {
214  }
215  else {
216  printf( "Bad heuristic '%s'\n", heuristic );
217  printUsage();
218  exit( EXIT_FAILURE );
219  }
220 }
221 
222 /**
223  * As of now, the main program does the following seqence of events -
224  * -# Read the DOT file
225  * -# Read the ORD file
226  * -# Display the attributes of the graph
227  * -# Count the number of crossings.
228  * -# Apply a preprocess and a heuristic (both optional) on the graph
229  * -# Optionally apply a post-processor that repeatedly swaps neighboring
230  * nodes until there's no more improvement
231  * -# Count the number of crossings after each phase (and save the ORD files
232  * for the minimum number of crossings in each phase)
233  */
234 int main( int argc, char * argv[] )
235 {
236  printf("################################################################\n");
237  printf("########### min_crossings, release 1.1, 2016/05/23 #############\n");
238 
239  int seed = 0;
240  int ch = -1;
242 
243  // process command-line options; these must come before the file arguments
244  // note: options that have an arg are followed by : but others are not
245  while ( (ch = getopt(argc, argv, "bc:e:fgh:i:k:o:p:P:R:r:s:t:vw:zm:")) != -1)
246  {
247  switch(ch)
248  {
249  case 'h':
250  heuristic = optarg;
251  break;
252 
253  case 'p':
254  preprocessor = optarg;
255  break;
256 
257  case 'z':
258  do_post_processing = true;
259  break;
260 
261  case 'i':
262  max_iterations = atoi( optarg );
263  standard_termination = false;
264  break;
265 
266  case 'R':
267  seed = atoi( optarg );
268  init_genrand( seed );
269  randomize_order = true;
270  break;
271 
272  case 'r':
273  max_runtime = atof( optarg );
274  standard_termination = false;
275  break;
276 
277  case 'P':
278  if ( strcmp( optarg, "b_t" ) == 0 ) pareto_objective = BOTTLENECK_TOTAL;
279  else if ( strcmp( optarg, "s_t" ) == 0 ) pareto_objective = STRETCH_TOTAL;
280  else if ( strcmp( optarg, "b_s" ) == 0 ) pareto_objective = BOTTLENECK_STRETCH;
281  else {
282  printf( "Bad value '%s' for option -P\n", optarg );
283  printUsage();
284  exit( EXIT_FAILURE );
285  }
286  break;
287 
288  case 'c':
289  capture_iteration = atoi( optarg );
290  break;
291 
292  case 'w':
293  if( strcmp( optarg, "none" ) == 0 ) adjust_weights = NONE;
294  else if( strcmp( optarg, "avg" ) == 0 ) adjust_weights = AVG;
295  else if( strcmp( optarg, "left" ) == 0 ) adjust_weights = LEFT;
296  else
297  {
298  printf( "Bad value '%s' for option -w\n", optarg );
299  printUsage();
300  exit( EXIT_FAILURE );
301  }
302  break;
303  case 'b':
304  balanced_weight = true;
305  break;
306  case 's':
307  if( strcmp( optarg, "layer" ) == 0 ) sift_option = LAYER;
308  else if( strcmp( optarg, "degree" ) == 0 ) sift_option = DEGREE;
309  else if( strcmp( optarg, "random" ) == 0 ) sift_option = RANDOM;
310  else
311  {
312  printf( "Bad value '%s' for option -s\n", optarg );
313  printUsage();
314  exit( EXIT_FAILURE );
315  }
316  break;
317  case 'e':
318  if( strcmp( optarg, "nodes" ) == 0 ) mce_option = NODES;
319  else if( strcmp( optarg, "edges" ) == 0 ) mce_option = EDGES;
320  else if( strcmp( optarg, "early" ) == 0 ) mce_option = EARLY;
321  else if( strcmp( optarg, "one_node" ) == 0 ) mce_option = ONE_NODE;
322  else
323  {
324  printf( "Bad value '%s' for option -e\n", optarg );
325  printUsage();
326  exit( EXIT_FAILURE );
327  }
328  break;
329  case 'g':
330  if( strcmp( optarg, "total" ) == 0 ) sifting_style = TOTAL;
331  else if( strcmp( optarg, "max" ) == 0 ) sifting_style = MAX;
332  else {
333  printf( "Bad value '%s' for option -g\n", optarg );
334  printUsage();
335  exit( EXIT_FAILURE );
336  }
337  break;
338  case 'k':
339  number_of_processors = atoi( optarg );
340  break;
341  case 'f':
342  favored_edges = true;
343  break;
344  case 'o':
345  produce_output = true;
346  output_base_name = calloc( strlen(optarg) + 1, sizeof(char) );
347  strcpy( output_base_name, optarg );
348  break;
349  case 'v':
350  verbose = true;
351  break;
352  case 't':
353  trace_freq = atoi( optarg );
354  break;
355  case 'm':
356  number_of_processors = atoi(optarg);
357 #ifdef _OPENMP
358  assert(number_of_processors <= omp_get_num_procs());
359 #endif
360  break;
361  default:
362  printUsage();
363  exit( EXIT_FAILURE );
364  break;
365 
366  } /* end of switch */
367  } /* end of while */
368 
369 #ifdef _OPENMP
370  // set number of OpenMP threads
371  omp_set_num_threads(number_of_processors);
372 #endif
373 
374  // start command line at first index after the options and get the two file
375  // names: dot and ord, respectively
376  argc -= optind;
377  argv += optind;
378  if( argc != 2 )
379  {
380  printf( "Wrong number of filenames (%d)\n", argc );
381  printUsage();
382  exit( EXIT_FAILURE );
383  }
384  const char * dot_file_name = argv[0];
385  const char * ord_file_name = argv[1];
386 
387  // handle special case where user specified an empty (_) base name for output
388  if ( produce_output
389  && strlen(output_base_name) == 1
390  && * output_base_name == '_' )
391  {
392  free( output_base_name );
393  char buffer[MAX_NAME_LENGTH];
394  strcpy( buffer, dot_file_name );
395 #ifdef DEBUG
396  printf( "output special case: buffer = %s, dot_file_name = %s\n",
397  buffer, dot_file_name );
398 #endif
399  char * base_name_ptr = base_name( buffer );
400 #ifdef DEBUG
401  printf( "output special case: buffer = %s, base = %s\n",
402  buffer, base_name_ptr );
403 #endif
405  = (char *) calloc( strlen(base_name_ptr) + 1, sizeof(char) );
406  strcpy( output_base_name, base_name_ptr );
407  }
408 
409  // initialize graph
410  readGraph( dot_file_name, ord_file_name );
411 
412  // create list of favored edges if appropriate
413  // do the allocations unconditionally to avoid having to check for
414  // 'favored_edges' everywhere
416  if ( favored_edges )
417  {
418  Layerptr middle_layer = layers[ number_of_layers / 2 ];
419  int middle_node_position = middle_layer->number_of_nodes / 2;
420  Nodeptr middle_node = middle_layer->nodes[ middle_node_position ];
421  createFanoutList( middle_node );
422  // create an actual file name and graph name here when the test version
423  // is successful
424  char file_name_buffer[MAX_NAME_LENGTH];
425  char graph_name_buffer[MAX_NAME_LENGTH];
426  char comment_buffer[MAX_NAME_LENGTH];
428  file_name_buffer,
429  graph_name_buffer,
430  comment_buffer
431  );
432  writeDot( file_name_buffer, graph_name_buffer, comment_buffer,
434  }
435 
436  print_graph_statistics( stdout );
437 
438  initCrossings();
439  initChannels();
443 
444  // set up structures for saving layer orders of best solutions so far
445  // (these are updated as appropriate in heuristics.c)
446  best_crossings_order = (Orderptr) calloc( 1, sizeof(struct order_struct) );
447  init_order( best_crossings_order );
448 
449  best_edge_crossings_order
450  = (Orderptr) calloc( 1, sizeof(struct order_struct) );
451  init_order( best_edge_crossings_order );
452 
453  best_total_stretch_order
454  = (Orderptr) calloc( 1, sizeof(struct order_struct) );
455  init_order( best_total_stretch_order );
456 
457  best_bottleneck_stretch_order
458  = (Orderptr) calloc( 1, sizeof(struct order_struct) );
459  init_order( best_bottleneck_stretch_order );
460 
461  best_favored_crossings_order
462  = (Orderptr) calloc( 1, sizeof(struct order_struct) );
463  init_order( best_favored_crossings_order );
464 
465  // start the clock
467 #ifdef DEBUG
468  printf( "start_time = %f\n", start_time );
469 #endif
470 
471  runPreprocessor();
474 #ifdef DEBUG
475  printf( "after preprocessor, runtime = %f\n", RUNTIME );
476 #endif
477 
478  // end of "iteration 0"
480  runHeuristic();
482 #ifdef DEBUG
483  printf( "after heuristic, runtime = %f\n", RUNTIME );
484 #endif
485 
486  if ( produce_output ) {
487  // write ordering after heuristic, before post-processing
488  restore_order( best_crossings_order );
491  }
492 
493  if ( do_post_processing ) {
494  restore_order( best_crossings_order );
496  swapping();
497 
498  if ( produce_output ) {
499  // write file with best total crossings order after post-processing
502  }
503  }
504 
506 
507 #ifdef DEBUG
509  printf("best order restored at end, crossings = %d\n", numberOfCrossings() );
510 #endif
511 
512  // write file with best order for edge crossings
513  if ( produce_output ) {
514  // write file with best max edge order after overall
515  restore_order( best_edge_crossings_order );
518  }
519 
520  if ( produce_output ) {
521  // write file with best stretch order overall
522  restore_order( best_total_stretch_order );
523  createOrdFileName( output_file_name, "-stretch" );
525  }
526 
527  if ( produce_output ) {
528  // write file with best stretch order overall
529  restore_order( best_bottleneck_stretch_order );
532  }
533 
534 #ifdef FAVORED
535  // write file with best order for favored edge crossings
536  restore_order( best_favored_crossings_order );
537  createOrdFileName( output_file_name, "-favored" );
539 #endif
540 
541  print_run_statistics( stdout );
542 
543  // deallocate all order structures
544  cleanup_order( best_crossings_order );
545  free( best_crossings_order );
546  cleanup_order( best_edge_crossings_order );
547  free( best_edge_crossings_order );
548  cleanup_order( best_total_stretch_order );
549  free( best_total_stretch_order );
550  cleanup_order( best_bottleneck_stretch_order );
551  free( best_bottleneck_stretch_order );
552 #ifdef FAVORED
553  cleanup_order( best_favored_crossings_order );
554  free( best_favored_crossings_order );
555 #endif
556 
557  /**
558  * @todo Need functions to deallocate everything (in the appropriate modules)
559  */
560  return EXIT_SUCCESS;
561 }
562 
563 /* [Last modified: 2016 05 24 at 13:39:27 GMT] */
564 
565 /* the line below is to ensure that this file gets benignly modified via
566  'make version' */
567 
568 /*xxxxxxxxxx*/
void staticBarycenter(void)
Definition: heuristics.c:492
static void printUsage(void)
Definition: min_crossings.c:83
int main(int argc, char *argv[])
static char output_file_name[MAX_NAME_LENGTH]
Definition: min_crossings.c:73
enum adjust_weights_enum adjust_weights
Definition: min_crossings.c:47
void maximumCrossingsNode(void)
Definition: heuristics.c:805
int trace_freq
Definition: min_crossings.c:63
void maximumStretchEdge(void)
Definition: heuristics.c:912
int numberOfFavoredEdges(void)
void capture_preprocessing_stats(void)
Definition: stats.c:206
void cleanup_order(Orderptr ord_info)
Definition: order.c:37
void maximumCrossingsEdge(void)
Definition: heuristics.c:875
void initCrossings(void)
Definition: crossings.c:80
Interface for functions implementing all of the heuristics Those labeled parallel are designed specif...
const Edgeptr * favoredEdges(void)
static char buffer[MAX_NAME_LENGTH]
Definition: heuristics.c:59
void swapping(void)
Definition: heuristics.c:1195
Layerptr * layers
Definition: graph_io.c:31
int number_of_nodes
Definition: graph.h:115
Functions for setting up priority edges and retrieving the number of crossings involving them...
bool produce_output
Definition: min_crossings.c:59
double getUserSeconds()
Definition: timing.c:27
void maximumCrossingsEdgeWithSifting(void)
Definition: heuristics.c:828
int capture_iteration
Definition: min_crossings.c:52
bool end_of_iteration(void)
Definition: heuristics.c:183
sifting_style_enum
Global variables, functions, and parameters specified on the command line.
Definitions common to all edge crossing heuristic source files.
void capture_post_processing_stats(void)
Definition: stats.c:228
void breadthFirstSearch(void)
Definition: heuristics.c:1065
double max_runtime
Definition: min_crossings.c:43
int max_iterations
Definition: min_crossings.c:41
void initChannels(void)
Definition: channel.c:57
sift_option_enum
enum pareto_objective_enum pareto_objective
Definition: min_crossings.c:51
enum sift_option_enum sift_option
Definition: min_crossings.c:48
bool randomize_order
Definition: min_crossings.c:54
#define RUNTIME
Definition: min_crossings.h:35
Functions for reporting various statistics about the graph and the outcomes.
void capture_heuristic_stats(void)
Definition: stats.c:217
void createOrdFileName(char *output_file_name, const char *appendix)
Definition: heuristics.c:64
char * output_base_name
Definition: min_crossings.c:60
void readGraph(const char *dot_file, const char *ord_file)
Definition: graph_io.c:363
Orderptr best_bottleneck_stretch_order
Definition: min_crossings.c:69
mce_option_enum
pareto_objective_enum
void writeDot(const char *dot_file_name, const char *graph_name, const char *header_information, Edgeptr *edge_list, int edge_list_length)
Definition: graph_io.c:424
void barycenter(void)
Definition: heuristics.c:428
implementation of utilities that maintain information about channels; eventually these will replace t...
bool favored_edges
Definition: min_crossings.c:53
adjust_weights_enum
void modifiedBarycenter(void)
Definition: heuristics.c:443
char * heuristic
Definition: min_crossings.c:39
void restore_order(Orderptr ord_info)
Definition: order.c:61
Definition of functions for reading and writing graphs.
double start_time
Definition: min_crossings.c:44
bool standard_termination
Definition: min_crossings.c:46
void sifting(void)
Definition: heuristics.c:1006
void median(void)
Definition: heuristics.c:413
void upDownBarycenter(void)
Definition: heuristics.c:570
Definition of functions that keep track of and update the number of crossings for each node...
bool verbose
Definition: min_crossings.c:62
bool balanced_weight
Definition: min_crossings.c:58
struct order_struct * Orderptr
data structure and function headers for saving/restoring order information.
static void runPreprocessor(void)
Definition of data structures and access functions for a layered graph.
double runtime
Definition: min_crossings.c:42
void capture_beginning_stats(void)
Definition: stats.c:195
void print_graph_statistics(FILE *output_stream)
Definition: stats.c:485
void createFavoredEdgeInfo(char *file_name_buffer, char *graph_name_buffer, char *comment_buffer)
static bool do_post_processing
Definition: min_crossings.c:75
enum mce_option_enum mce_option
Definition: min_crossings.c:49
Nodeptr * nodes
Definition: graph.h:116
void print_run_statistics(FILE *output_stream)
Definition: stats.c:532
int number_of_layers
Definition: graph_io.c:28
void slabBarycenter(void)
Definition: heuristics.c:649
int number_of_processors
Definition: min_crossings.c:45
void init_genrand(unsigned long s)
Definition: random.c:53
Orderptr best_total_stretch_order
Definition: min_crossings.c:68
char * preprocessor
Definition: min_crossings.c:40
void evenOddBarycenter(void)
Definition: heuristics.c:523
void init_order(Orderptr ord_info)
Definition: order.c:21
static char * base_name(char *buffer)
void init_crossing_stats(void)
Definition: stats.c:182
void updateAllCrossings(void)
Definition: crossings.c:138
#define MAX_NAME_LENGTH
Definition: defs.h:32
void middleDegreeSort(void)
Definition: heuristics.c:1098
int numberOfCrossings(void)
Definition: crossings.c:93
Orderptr best_crossings_order
Definition: min_crossings.c:66
void writeOrd(const char *ord_file)
Definition: graph_io.c:405
void initPriorityEdges(void)
static void runHeuristic(void)
void rotatingBarycenter(void)
Definition: heuristics.c:686
Header for a simple function to compute CPU user milliseconds.
void depthFirstSearch(void)
Definition: heuristics.c:1070
enum sifting_style_enum sifting_style
Definition: min_crossings.c:50
Orderptr best_favored_crossings_order
Definition: min_crossings.c:70
Orderptr best_edge_crossings_order
Definition: min_crossings.c:67
void createFanoutList(Nodeptr node)
Takes all the edges that are accessible via a path from the node and adds them to the priority list...