|
|
|
@ -2,36 +2,22 @@
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <float.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include "gtsp.h"
|
|
|
|
|
#include "geometry.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "flow.h"
|
|
|
|
|
#include "branch_and_cut.h"
|
|
|
|
|
#include "math.h"
|
|
|
|
|
#include "gtsp-subtour.h"
|
|
|
|
|
|
|
|
|
|
double *OPTIMAL_X = 0;
|
|
|
|
|
|
|
|
|
|
static int get_edge_num(int node_count, int from, int to)
|
|
|
|
|
{
|
|
|
|
|
int idx = node_count;
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < from; k++)
|
|
|
|
|
idx += node_count - k - 1;
|
|
|
|
|
|
|
|
|
|
idx += to - from - 1;
|
|
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GTSP_init_data(struct GTSP *data)
|
|
|
|
|
{
|
|
|
|
|
int rval = 0;
|
|
|
|
|
|
|
|
|
|
data->clusters = 0;
|
|
|
|
|
data->cluster_count = 0;
|
|
|
|
|
data->x_coordinates = 0;
|
|
|
|
|
data->y_coordinates = 0;
|
|
|
|
|
|
|
|
|
|
data->graph = (struct Graph *) malloc(sizeof(struct Graph));
|
|
|
|
|
abort_if(!data->graph, "could not allocate data->graph");
|
|
|
|
@ -52,8 +38,6 @@ void GTSP_free(struct GTSP *data)
|
|
|
|
|
free(data->graph);
|
|
|
|
|
|
|
|
|
|
if (data->clusters) free(data->clusters);
|
|
|
|
|
if (data->x_coordinates) free(data->x_coordinates);
|
|
|
|
|
if (data->y_coordinates) free(data->y_coordinates);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GTSP_create_random_problem(
|
|
|
|
@ -156,11 +140,17 @@ abort_if(!data->graph, "could not allocate data->graph");
|
|
|
|
|
data->graph = graph;
|
|
|
|
|
data->clusters = clusters;
|
|
|
|
|
data->cluster_count = cluster_count;
|
|
|
|
|
<<<<<<< HEAD
|
|
|
|
|
data->x_coordinates = x_coords;
|
|
|
|
|
data->y_coordinates = y_coords;
|
|
|
|
|
data->dist_matrix = dist_matrix;
|
|
|
|
|
data->vertex_set = cluster_member;
|
|
|
|
|
|
|
|
|
|
=======
|
|
|
|
|
graph->x_coordinates = x_coords;
|
|
|
|
|
graph->y_coordinates = y_coords;
|
|
|
|
|
|
|
|
|
|
>>>>>>> 550b5f1912da4f50cd0bd0a7d2f19cdbf695b180
|
|
|
|
|
CLEANUP:
|
|
|
|
|
if (weights) free(weights);
|
|
|
|
|
if (edges) free(edges);
|
|
|
|
@ -226,43 +216,47 @@ int GTSP_init_lp(struct LP *lp, struct GTSP *data)
|
|
|
|
|
int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data)
|
|
|
|
|
{
|
|
|
|
|
int rval = 0;
|
|
|
|
|
|
|
|
|
|
int round = 0;
|
|
|
|
|
|
|
|
|
|
int violation_total = 3;
|
|
|
|
|
int violation_current = 0;
|
|
|
|
|
double violations[] = {1.0, 0.1, LP_EPSILON};
|
|
|
|
|
int current_round = 0;
|
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
round++;
|
|
|
|
|
if (current_round > 0)
|
|
|
|
|
{
|
|
|
|
|
int is_infeasible;
|
|
|
|
|
rval = LP_optimize(lp, &is_infeasible);
|
|
|
|
|
abort_if(rval, "LP_optimize failed");
|
|
|
|
|
|
|
|
|
|
if (is_infeasible) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current_round++;
|
|
|
|
|
|
|
|
|
|
log_debug("Finding subtour cuts, round %d, violation %.4lf...\n", round,
|
|
|
|
|
violations[violation_current]);
|
|
|
|
|
int original_cut_pool_size;
|
|
|
|
|
int added_cuts_count;
|
|
|
|
|
|
|
|
|
|
int original_cut_pool_size = lp->cut_pool_size;
|
|
|
|
|
rval = find_exact_subtour_cuts(lp, data, violations[violation_current]);
|
|
|
|
|
original_cut_pool_size = lp->cut_pool_size;
|
|
|
|
|
log_debug("Finding subtour cuts, round %d...\n", current_round);
|
|
|
|
|
|
|
|
|
|
rval = find_exact_subtour_cuts(lp, data, LP_EPSILON);
|
|
|
|
|
abort_if(rval, "find_exact_subtour_cuts failed");
|
|
|
|
|
|
|
|
|
|
if (lp->cut_pool_size - original_cut_pool_size == 0)
|
|
|
|
|
{
|
|
|
|
|
if (++violation_current < violation_total)
|
|
|
|
|
{
|
|
|
|
|
log_debug("No cuts found. Decreasing minimum cut violation.\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
log_debug("No additional cuts found.\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
added_cuts_count = lp->cut_pool_size - original_cut_pool_size;
|
|
|
|
|
if (added_cuts_count > 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_COMB_INEQUALITIES
|
|
|
|
|
original_cut_pool_size = lp->cut_pool_size;
|
|
|
|
|
log_debug("Finding comb cuts, round %d...\n", current_round);
|
|
|
|
|
|
|
|
|
|
rval = find_comb_cuts(lp, data);
|
|
|
|
|
abort_if(rval, "find_comb_cuts failed");
|
|
|
|
|
|
|
|
|
|
int is_infeasible;
|
|
|
|
|
rval = LP_optimize(lp, &is_infeasible);
|
|
|
|
|
abort_if(rval, "LP_optimize failed");
|
|
|
|
|
added_cuts_count = lp->cut_pool_size - original_cut_pool_size;
|
|
|
|
|
if (added_cuts_count > 0)
|
|
|
|
|
continue;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (is_infeasible) break;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLEANUP:
|
|
|
|
@ -278,12 +272,14 @@ int GTSP_write_problem(struct GTSP *data, char *filename)
|
|
|
|
|
file = fopen(filename, "w");
|
|
|
|
|
abort_if(!file, "could not open file");
|
|
|
|
|
|
|
|
|
|
fprintf(file, "%d %d\n", data->graph->node_count, data->cluster_count);
|
|
|
|
|
const struct Graph *graph = data->graph;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < data->graph->node_count; i++)
|
|
|
|
|
fprintf(file, "%d %d\n", graph->node_count, data->cluster_count);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < graph->node_count; i++)
|
|
|
|
|
{
|
|
|
|
|
fprintf(file, "%.2lf %.2lf %d\n", data->x_coordinates[i],
|
|
|
|
|
data->y_coordinates[i], data->clusters[i]);
|
|
|
|
|
fprintf(file, "%.2lf %.2lf %d\n", graph->x_coordinates[i],
|
|
|
|
|
graph->y_coordinates[i], data->clusters[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CLEANUP:
|
|
|
|
@ -322,12 +318,13 @@ int GTSP_write_solution(struct GTSP *data, char *filename, double *x)
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GTSP_read_solution(char *filename, double **p_x)
|
|
|
|
|
int GTSP_read_solution(struct GTSP *gtsp, char *filename, double **p_x)
|
|
|
|
|
{
|
|
|
|
|
int rval = 0;
|
|
|
|
|
|
|
|
|
|
int node_count;
|
|
|
|
|
int edge_count;
|
|
|
|
|
int *edge_map = 0;
|
|
|
|
|
|
|
|
|
|
double *x;
|
|
|
|
|
|
|
|
|
@ -351,31 +348,138 @@ int GTSP_read_solution(char *filename, double **p_x)
|
|
|
|
|
rval = fscanf(file, "%d", &edge_count);
|
|
|
|
|
abort_if(rval != 1, "invalid input format (positive edge count)");
|
|
|
|
|
|
|
|
|
|
edge_map = (int *) malloc(node_count * node_count * sizeof(int));
|
|
|
|
|
abort_if(!edge_map, "could not allocate edge_map");
|
|
|
|
|
|
|
|
|
|
int k = node_count;
|
|
|
|
|
for (int i = 0; i < node_count; i++)
|
|
|
|
|
{
|
|
|
|
|
for (int j = i + 1; j < node_count; j++)
|
|
|
|
|
{
|
|
|
|
|
if (gtsp->clusters[i] == gtsp->clusters[j]) continue;
|
|
|
|
|
edge_map[i * node_count + j] = k;
|
|
|
|
|
edge_map[j * node_count + i] = k;
|
|
|
|
|
k++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < edge_count; i++)
|
|
|
|
|
{
|
|
|
|
|
int from, to, edge;
|
|
|
|
|
rval = fscanf(file, "%d %d", &from, &to);
|
|
|
|
|
abort_if(rval != 2, "invalid input format (edge endpoints)");
|
|
|
|
|
double val;
|
|
|
|
|
rval = fscanf(file, "%d %d %lf", &from, &to, &val);
|
|
|
|
|
abort_if(rval != 3, "invalid input format (edge endpoints)");
|
|
|
|
|
|
|
|
|
|
if (from > to) swap(from, to);
|
|
|
|
|
|
|
|
|
|
edge = get_edge_num(node_count, from, to);
|
|
|
|
|
edge = edge_map[from * node_count + to];
|
|
|
|
|
abort_if(edge > num_cols, "invalid edge");
|
|
|
|
|
|
|
|
|
|
x[from] += 0.5;
|
|
|
|
|
x[to] += 0.5;
|
|
|
|
|
x[edge] = 1;
|
|
|
|
|
x[from] += val / 2;
|
|
|
|
|
x[to] += val / 2;
|
|
|
|
|
x[edge] = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < num_cols; i++)
|
|
|
|
|
{
|
|
|
|
|
if (x[i] <= LP_EPSILON) continue;
|
|
|
|
|
log_debug(" x%-3d = %.2f\n", i, x[i]);
|
|
|
|
|
log_debug(" x%-5d = %.6f\n", i, x[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*p_x = x;
|
|
|
|
|
rval = 0;
|
|
|
|
|
|
|
|
|
|
CLEANUP:
|
|
|
|
|
if (edge_map) free(edge_map);
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GTSP_check_solution(struct GTSP *data, double *x)
|
|
|
|
|
{
|
|
|
|
|
int rval = 0;
|
|
|
|
|
int *cluster_mark = 0;
|
|
|
|
|
|
|
|
|
|
struct Node **stack = 0;
|
|
|
|
|
int stack_top = 0;
|
|
|
|
|
|
|
|
|
|
struct Graph *graph = data->graph;
|
|
|
|
|
const int node_count = graph->node_count;
|
|
|
|
|
const int edge_count = graph->edge_count;
|
|
|
|
|
|
|
|
|
|
cluster_mark = (int *) malloc(data->cluster_count * sizeof(int));
|
|
|
|
|
abort_if(!cluster_mark, "could not allocate cluster_mark");
|
|
|
|
|
|
|
|
|
|
stack = (struct Node **) malloc(graph->node_count * sizeof(struct Node *));
|
|
|
|
|
abort_if(!stack, "could not allocate stack");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < node_count + edge_count; i++)
|
|
|
|
|
{
|
|
|
|
|
abort_iff(x[i] < 1.0 - LP_EPSILON && x[i] > LP_EPSILON,
|
|
|
|
|
"solution is not integral: x%d = %.4lf", i, x[i]);
|
|
|
|
|
|
|
|
|
|
abort_iff(x[i] > 1.0 + LP_EPSILON || x[i] < 0.0 - LP_EPSILON,
|
|
|
|
|
"value out of bounds: x%d = %.4lf", i, x[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < node_count; i++)
|
|
|
|
|
graph->nodes[i].mark = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < data->cluster_count; i++)
|
|
|
|
|
cluster_mark[i] = 0;
|
|
|
|
|
|
|
|
|
|
int initial;
|
|
|
|
|
for (initial = 0; initial < node_count; initial++)
|
|
|
|
|
if (x[initial] > 1.0 - LP_EPSILON) break;
|
|
|
|
|
|
|
|
|
|
abort_if(initial == node_count, "no initial node");
|
|
|
|
|
|
|
|
|
|
stack[stack_top++] = &graph->nodes[initial];
|
|
|
|
|
graph->nodes[initial].mark = 1;
|
|
|
|
|
|
|
|
|
|
while (stack_top > 0)
|
|
|
|
|
{
|
|
|
|
|
struct Node *n = stack[--stack_top];
|
|
|
|
|
cluster_mark[data->clusters[n->index]]++;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < n->degree; i++)
|
|
|
|
|
{
|
|
|
|
|
struct Adjacency *adj = &n->adj[i];
|
|
|
|
|
struct Node *neighbor = adj->neighbor;
|
|
|
|
|
|
|
|
|
|
if (neighbor->mark) continue;
|
|
|
|
|
if (x[node_count + adj->edge->index] < LP_EPSILON) continue;
|
|
|
|
|
|
|
|
|
|
stack[stack_top++] = neighbor;
|
|
|
|
|
neighbor->mark = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < data->cluster_count; i++)
|
|
|
|
|
abort_if(cluster_mark[i] != 1, "cluster not visited exactly one time");
|
|
|
|
|
|
|
|
|
|
log_info(" solution is valid\n");
|
|
|
|
|
|
|
|
|
|
CLEANUP:
|
|
|
|
|
if (stack) free(stack);
|
|
|
|
|
if (cluster_mark) free(cluster_mark);
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GTSP_solution_found(struct GTSP *data, double *x)
|
|
|
|
|
{
|
|
|
|
|
int rval = 0;
|
|
|
|
|
|
|
|
|
|
char filename[100];
|
|
|
|
|
|
|
|
|
|
sprintf(filename, "tmp/gtsp-m%d-n%d-s%d.out", data->cluster_count,
|
|
|
|
|
data->graph->node_count, SEED);
|
|
|
|
|
|
|
|
|
|
log_info("Writting solution to file %s\n", filename);
|
|
|
|
|
rval = GTSP_write_solution(data, filename, x);
|
|
|
|
|
abort_if(rval, "GTSP_write_solution failed");
|
|
|
|
|
|
|
|
|
|
log_info("Checking solution...\n");
|
|
|
|
|
rval = GTSP_check_solution(data, x);
|
|
|
|
|
abort_if(rval, "GTSP_check_solution failed");
|
|
|
|
|
|
|
|
|
|
CLEANUP:
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
@ -387,11 +491,12 @@ static const struct option options_tab[] = {{"help", no_argument, 0, 'h'},
|
|
|
|
|
{"optimal", required_argument, 0, 'x'},
|
|
|
|
|
{"seed", required_argument, 0, 's'},
|
|
|
|
|
{(char *) 0, (int) 0, (int *) 0, (int) 0}};
|
|
|
|
|
|
|
|
|
|
static int input_node_count = -1;
|
|
|
|
|
static int input_cluster_count = -1;
|
|
|
|
|
static int grid_size = 100;
|
|
|
|
|
|
|
|
|
|
static char input_x_filename[1000] = {0};
|
|
|
|
|
|
|
|
|
|
static void GTSP_print_usage()
|
|
|
|
|
{
|
|
|
|
|
printf("Parameters:\n");
|
|
|
|
@ -433,8 +538,7 @@ static int GTSP_parse_args(int argc, char **argv)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'x':
|
|
|
|
|
rval = GTSP_read_solution(optarg, &OPTIMAL_X);
|
|
|
|
|
abort_if(rval, "GTSP_read_solution failed");
|
|
|
|
|
strcpy(input_x_filename, optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
@ -443,12 +547,14 @@ static int GTSP_parse_args(int argc, char **argv)
|
|
|
|
|
|
|
|
|
|
case ':':
|
|
|
|
|
fprintf(stderr, "option '-%c' requires an argument\n", optopt);
|
|
|
|
|
return 1;
|
|
|
|
|
rval = 1;
|
|
|
|
|
goto CLEANUP;
|
|
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "option '-%c' is invalid\n", optopt);
|
|
|
|
|
return 1;
|
|
|
|
|
rval = 1;
|
|
|
|
|
goto CLEANUP;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -481,18 +587,6 @@ static int GTSP_parse_args(int argc, char **argv)
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GTSP_solution_found(struct GTSP *data, double *x)
|
|
|
|
|
{
|
|
|
|
|
int rval = 0;
|
|
|
|
|
|
|
|
|
|
log_info("Writting integral solution to file gtsp.out\n");
|
|
|
|
|
rval = GTSP_write_solution(data, "gtsp.out", x);
|
|
|
|
|
abort_if(rval, "GTSP_write_solution failed");
|
|
|
|
|
|
|
|
|
|
CLEANUP:
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double FLOW_CPU_TIME = 0;
|
|
|
|
|
double LP_SOLVE_TIME = 0;
|
|
|
|
|
double LP_CUT_POOL_TIME = 0;
|
|
|
|
@ -527,11 +621,20 @@ int GTSP_main(int argc, char **argv)
|
|
|
|
|
rval = GTSP_create_random_problem(input_node_count, input_cluster_count,
|
|
|
|
|
grid_size, &data);
|
|
|
|
|
abort_if(rval, "GTSP_create_random_problem failed");
|
|
|
|
|
<<<<<<< HEAD
|
|
|
|
|
int init_val ;
|
|
|
|
|
|
|
|
|
|
init_val = inital_tour_value(&data);
|
|
|
|
|
log_info("Writing random instance to file gtsp.in\n");
|
|
|
|
|
rval = GTSP_write_problem(&data, "gtsp.in");
|
|
|
|
|
=======
|
|
|
|
|
|
|
|
|
|
char filename[100];
|
|
|
|
|
sprintf(filename, "input/gtsp-m%d-n%d-s%d.in", input_cluster_count,
|
|
|
|
|
input_node_count, SEED);
|
|
|
|
|
log_info("Writing random instance to file %s\n", filename);
|
|
|
|
|
rval = GTSP_write_problem(&data, filename);
|
|
|
|
|
>>>>>>> 550b5f1912da4f50cd0bd0a7d2f19cdbf695b180
|
|
|
|
|
abort_if(rval, "GTSP_write_problem failed");
|
|
|
|
|
|
|
|
|
|
bnc.best_obj_val = init_val;
|
|
|
|
@ -542,11 +645,28 @@ int GTSP_main(int argc, char **argv)
|
|
|
|
|
bnc.problem_solution_found = (int (*)(
|
|
|
|
|
void *, double *)) GTSP_solution_found;
|
|
|
|
|
|
|
|
|
|
if (OPTIMAL_X)
|
|
|
|
|
double opt_val = 0.0;
|
|
|
|
|
|
|
|
|
|
if (strlen(input_x_filename) == 0)
|
|
|
|
|
{
|
|
|
|
|
sprintf(input_x_filename, "optimal/gtsp-m%d-n%d-s%d.out",
|
|
|
|
|
input_cluster_count, input_node_count, SEED);
|
|
|
|
|
|
|
|
|
|
FILE *file = fopen(input_x_filename, "r");
|
|
|
|
|
|
|
|
|
|
if (!file)
|
|
|
|
|
input_x_filename[0] = 0;
|
|
|
|
|
else
|
|
|
|
|
fclose(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strlen(input_x_filename) > 0)
|
|
|
|
|
{
|
|
|
|
|
rval = GTSP_read_solution(&data, input_x_filename, &OPTIMAL_X);
|
|
|
|
|
abort_if(rval, "GTSP_read_solution failed");
|
|
|
|
|
|
|
|
|
|
log_info("Optimal solution is available. Cuts will be checked.\n");
|
|
|
|
|
|
|
|
|
|
double opt_val = 0.0;
|
|
|
|
|
for (int i = 0; i < data.graph->edge_count; i++)
|
|
|
|
|
{
|
|
|
|
|
struct Edge *e = &data.graph->edges[i];
|
|
|
|
@ -560,9 +680,9 @@ int GTSP_main(int argc, char **argv)
|
|
|
|
|
rval = BNC_init_lp(&bnc);
|
|
|
|
|
abort_if(rval, "BNC_init_lp failed");
|
|
|
|
|
|
|
|
|
|
log_info("Writing LP to file gtsp.lp...\n");
|
|
|
|
|
rval = LP_write(bnc.lp, "gtsp.lp");
|
|
|
|
|
abort_if(rval, "LP_write failed");
|
|
|
|
|
// log_info("Writing LP to file gtsp.lp...\n");
|
|
|
|
|
// rval = LP_write(bnc.lp, "gtsp.lp");
|
|
|
|
|
// abort_if(rval, "LP_write failed");
|
|
|
|
|
|
|
|
|
|
log_info("Starting branch-and-cut solver...\n");
|
|
|
|
|
rval = BNC_solve(&bnc);
|
|
|
|
@ -573,6 +693,13 @@ int GTSP_main(int argc, char **argv)
|
|
|
|
|
log_info("Optimal integral solution:\n");
|
|
|
|
|
log_info(" obj value = %.2lf **\n", bnc.best_obj_val);
|
|
|
|
|
|
|
|
|
|
if (OPTIMAL_X)
|
|
|
|
|
{
|
|
|
|
|
abort_iff(bnc.best_obj_val > opt_val,
|
|
|
|
|
"Solution is not optimal: %.4lf > %.4lf", bnc.best_obj_val,
|
|
|
|
|
opt_val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_info("Branch-and-bound nodes: %d\n", BNC_NODE_COUNT);
|
|
|
|
|
log_info("Max-flow calls: %d\n", FLOW_MAX_FLOW_COUNT);
|
|
|
|
|
log_info("Max-flow computation time: %.2lf\n", FLOW_CPU_TIME);
|
|
|
|
|