From 8af349c1068706b88f434fa6125183f7769f4f6d Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Wed, 25 Mar 2015 06:32:19 -0400 Subject: [PATCH] Additional subtour cuts --- src/branch_and_cut.c | 22 ++- src/branch_and_cut.h | 4 + src/flow.c | 6 +- src/flow.h | 2 + src/graph.c | 9 +- src/gtsp.c | 411 ++++++++++++++++++++++++++++++++++--------- src/util.h | 12 +- 7 files changed, 368 insertions(+), 98 deletions(-) diff --git a/src/branch_and_cut.c b/src/branch_and_cut.c index 751f276..d422593 100644 --- a/src/branch_and_cut.c +++ b/src/branch_and_cut.c @@ -5,6 +5,8 @@ #include "branch_and_cut.h" #include "util.h" +int BNC_NODE_COUNT = 0; + static int BNC_solve_node(struct BNC *bnc, int depth); static int BNC_branch_node(struct BNC *bnc, double *x, int depth); @@ -40,6 +42,7 @@ void BNC_free(struct BNC *bnc) LP_free(bnc->lp); free(bnc->lp); } + if (bnc->best_x) free(bnc->best_x); } int BNC_init_lp(struct BNC *bnc) @@ -72,6 +75,8 @@ static int BNC_solve_node(struct BNC *bnc, int depth) struct LP *lp = bnc->lp; double *best_val = &bnc->best_obj_val; + BNC_NODE_COUNT++; + int rval = 0; double *x = (double *) NULL; @@ -91,10 +96,12 @@ static int BNC_solve_node(struct BNC *bnc, int depth) rval = LP_get_obj_val(lp, &objval); abort_if(rval, "LP_get_obj_val failed\n"); - log_debug(" objective value = %.2f\n", objval); + log_debug(" obj value = %.2f\n", objval); - if (objval > *best_val) + if (objval > *best_val + LP_EPSILON) { + + log_debug("Branch pruned by bound (%.2lf > %.2lf).\n", objval, *best_val); rval = 0; @@ -128,17 +135,20 @@ static int BNC_solve_node(struct BNC *bnc, int depth) { log_debug(" solution is integral\n"); - if (objval < *best_val) + if (objval + LP_EPSILON < *best_val) { + if (bnc->best_x) free(bnc->best_x); *best_val = objval; bnc->best_x = x; x = 0; log_info("Found a better integral solution:\n"); - log_info(" objval = %.2lf **\n", objval); + log_info(" obj val = %.2lf **\n", objval); + + if (bnc->problem_solution_found) + bnc->problem_solution_found(bnc->problem_data, bnc->best_x); } - } - else + } else { log_debug(" solution is fractional\n"); rval = BNC_branch_node(bnc, x, depth); diff --git a/src/branch_and_cut.h b/src/branch_and_cut.h index 2be9b00..02f3b69 100644 --- a/src/branch_and_cut.h +++ b/src/branch_and_cut.h @@ -17,6 +17,8 @@ struct BNC int (*problem_init_lp)(struct LP *, void *); int (*problem_add_cutting_planes)(struct LP *, void *); + + int (*problem_solution_found)(void *data, double *x); }; int BNC_init(struct BNC *bnc); @@ -27,4 +29,6 @@ int BNC_init_lp(struct BNC *bnc); void BNC_free(struct BNC *bnc); +extern int BNC_NODE_COUNT; + #endif //_PROJECT_BRANCH_AND_CUT_H_ diff --git a/src/flow.c b/src/flow.c index 57f2905..4a59164 100644 --- a/src/flow.c +++ b/src/flow.c @@ -4,6 +4,8 @@ #include "gtsp.h" #include "util.h" +int FLOW_MAX_FLOW_COUNT = 0; + int flow_mark_reachable_nodes( const struct Graph *graph, double *residual_caps, struct Node *from) { @@ -62,11 +64,13 @@ int flow_find_max_flow( { int rval = 0; + FLOW_MAX_FLOW_COUNT++; + for (int i = 0; i < digraph->node_count; i++) digraph->nodes[i].mark = 0; log_verbose("Input graph:\n"); - graph_dump(digraph); +// graph_dump(digraph); log_verbose("Solving flow problem:\n"); diff --git a/src/flow.h b/src/flow.h index f3a9595..895c709 100644 --- a/src/flow.h +++ b/src/flow.h @@ -25,6 +25,8 @@ int flow_mark_reachable_nodes( int flow_main(int argc, char **argv); +extern int FLOW_MAX_FLOW_COUNT; + #include "graph.h" #endif //_PROJECT_FLOW_H_ diff --git a/src/graph.c b/src/graph.c index a7656e8..633c0a7 100644 --- a/src/graph.c +++ b/src/graph.c @@ -17,6 +17,7 @@ void graph_free(struct Graph *graph) { if (!graph) return; + if (graph->edges) free(graph->edges); if (graph->nodes) free(graph->nodes); if (graph->adj) free(graph->adj); } @@ -201,7 +202,8 @@ int graph_dump(struct Graph *graph) { int rval = 0; - log_debug("node_count: %d edge_count: %d\n", graph->node_count, graph->edge_count); + log_debug("node_count: %d edge_count: %d\n", graph->node_count, + graph->edge_count); for (int i = 0; i < graph->node_count; i++) { @@ -212,8 +214,9 @@ int graph_dump(struct Graph *graph) for (int i = 0; i < graph->edge_count; i++) { struct Edge *e = &graph->edges[i]; - log_debug("%3d (%d, %d) weight: %d ", e->index, e->from->index, e->to->index, e->weight); - #if LOG_LEVEL >= LOG_LEVEL_VERBOSE + log_debug("%3d (%d, %d) weight: %d ", e->index, e->from->index, + e->to->index, e->weight); + #if LOG_LEVEL >= LOG_LEVEL_DEBUG if (e->reverse) printf("reverse: %d ", e->reverse->index); printf("\n"); #endif diff --git a/src/gtsp.c b/src/gtsp.c index 53c75cf..73674fc 100644 --- a/src/gtsp.c +++ b/src/gtsp.c @@ -32,11 +32,10 @@ int GTSP_init_data(struct GTSP *data) void GTSP_free(struct GTSP *data) { if (!data) return; - if (data->graph) - { - graph_free(data->graph); - free(data->graph); - } + + graph_free(data->graph); + 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); @@ -106,9 +105,10 @@ int GTSP_create_random_problem( data->y_coordinates = y_coords; CLEANUP: + if (weights) free(weights); + if (edges) free(edges); if (rval) { - if (edges) free(edges); if (clusters) free(clusters); } return rval; @@ -202,10 +202,117 @@ int GTSP_add_subtour_elimination_cut( rmatind[cut_edges_count + 1] = to->index; rmatval[cut_edges_count + 1] = -2.0; - log_debug("Generated cut:\n"); + log_verbose("Generated cut:\n"); + for (int i = 0; i < newnz; i++) + log_verbose("%8.2f x%d\n", rmatval[i], rmatind[i]); + log_verbose(" %c %.2lf\n", sense, rhs); + + if (OPTIMAL_X) + { + double sum = 0; + for (int i = 0; i < newnz; i++) + sum += rmatval[i] * OPTIMAL_X[rmatind[i]]; + abort_if(sum <= rhs - LP_EPSILON, "cannot add invalid cut"); + } + + rval = LP_add_rows(lp, 1, newnz, &rhs, &sense, &rmatbeg, rmatind, rmatval); + abort_if(rval, "LP_add_rows failed"); + + CLEANUP: + if (rmatval) free(rmatval); + if (rmatind) free(rmatind); + return rval; +} + +int GTSP_add_subtour_elimination_cut_2( + struct LP *lp, + struct Graph *graph, + struct Node *from, + struct Node *to, + struct Edge **cut_edges, + int cut_edges_count) +{ + int rval = 0; + + char sense = 'G'; + double rhs = 0.0; + int newnz = cut_edges_count + 1; + + int rmatbeg = 0; + int *rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc(newnz * sizeof(int)); + abort_if(!rmatind, "could not allocate rmatind"); + + rmatval = (double *) malloc(newnz * sizeof(double)); + abort_if(!rmatval, "could not allocate rmatval"); + + for (int i = 0; i < cut_edges_count; i++) + { + rmatind[i] = cut_edges[i]->index + graph->node_count; + rmatval[i] = 1.0; + } + + rmatind[cut_edges_count] = from->index; + rmatval[cut_edges_count] = -2.0; + + log_verbose("Generated cut:\n"); + for (int i = 0; i < newnz; i++) + log_verbose("%8.2f x%d\n", rmatval[i], rmatind[i]); + log_verbose(" %c %.2lf\n", sense, rhs); + + if (OPTIMAL_X) + { + double sum = 0; + for (int i = 0; i < newnz; i++) + sum += rmatval[i] * OPTIMAL_X[rmatind[i]]; + abort_if(sum <= rhs - LP_EPSILON, "cannot add invalid cut"); + } + + rval = LP_add_rows(lp, 1, newnz, &rhs, &sense, &rmatbeg, rmatind, rmatval); + abort_if(rval, "LP_add_rows failed"); + + CLEANUP: + if (rmatval) free(rmatval); + if (rmatind) free(rmatind); + return rval; +} + +int GTSP_add_subtour_elimination_cut_3( + struct LP *lp, + struct Graph *graph, + struct Node *from, + struct Node *to, + struct Edge **cut_edges, + int cut_edges_count) +{ + int rval = 0; + + char sense = 'G'; + double rhs = 2.0; + int newnz = cut_edges_count; + + int rmatbeg = 0; + int *rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc(newnz * sizeof(int)); + abort_if(!rmatind, "could not allocate rmatind"); + + rmatval = (double *) malloc(newnz * sizeof(double)); + abort_if(!rmatval, "could not allocate rmatval"); + + for (int i = 0; i < cut_edges_count; i++) + { + rmatind[i] = cut_edges[i]->index + graph->node_count; + rmatval[i] = 1.0; + } + + log_verbose("Generated cut:\n"); for (int i = 0; i < newnz; i++) - log_debug("%8.2f x%d\n", rmatval[i], rmatind[i]); - log_debug(" %c %.2lf\n", sense, rhs); + log_verbose("%8.2f x%d\n", rmatval[i], rmatind[i]); + log_verbose(" %c %.2lf\n", sense, rhs); if (OPTIMAL_X) { @@ -252,19 +359,23 @@ int GTSP_find_exact_subtour_elimination_cuts( struct Graph digraph; graph_init(&digraph); - digraph_edges = (int *) malloc(8 * graph->edge_count * sizeof(int)); - flow = (double *) malloc(4 * graph->edge_count * sizeof(double)); - capacities = (double *) malloc(4 * graph->edge_count * sizeof(double)); + int digraph_edge_count = 4 * graph->edge_count + 2 * graph->node_count; + int digraph_node_count = node_count + data->cluster_count; + + digraph_edges = (int *) malloc(2 * digraph_edge_count * sizeof(int)); + flow = (double *) malloc(digraph_edge_count * sizeof(double)); + capacities = (double *) malloc(digraph_edge_count * sizeof(double)); cut_edges = - (struct Edge **) malloc( - 4 * graph->edge_count * sizeof(struct Edge *)); + (struct Edge **) malloc(digraph_edge_count * sizeof(struct Edge *)); abort_if(!digraph_edges, "could not allocate digraph_edges"); abort_if(!flow, "could not allocate flow"); abort_if(!capacities, "could not allocate capacities"); abort_if(!cut_edges, "could not allocate cut_edges"); - // Create four directed edges for each edge of the original graph. + // Create four directed edges for each edge of the original graph + int ke = 0; + int kc = 0; for (int i = 0; i < graph->edge_count; i++) { assert(node_count + i < num_cols); @@ -273,39 +384,56 @@ int GTSP_find_exact_subtour_elimination_cuts( int from = e->from->index; int to = e->to->index; - digraph_edges[8 * i] = from; - digraph_edges[8 * i + 1] = to; - capacities[4 * i] = x[node_count + i]; + digraph_edges[ke++] = from; + digraph_edges[ke++] = to; + capacities[kc++] = x[node_count + i]; - digraph_edges[8 * i + 2] = to; - digraph_edges[8 * i + 3] = from; - capacities[4 * i + 1] = 0; + digraph_edges[ke++] = to; + digraph_edges[ke++] = from; + capacities[kc++] = 0; - digraph_edges[8 * i + 4] = to; - digraph_edges[8 * i + 5] = from; - capacities[4 * i + 2] = x[node_count + i]; + digraph_edges[ke++] = to; + digraph_edges[ke++] = from; + capacities[kc++] = x[node_count + i]; - digraph_edges[8 * i + 6] = from; - digraph_edges[8 * i + 7] = to; - capacities[4 * i + 3] = 0; + digraph_edges[ke++] = from; + digraph_edges[ke++] = to; + capacities[kc++] = 0; } - rval = graph_build(node_count, 4 * graph->edge_count, digraph_edges, 1, &digraph); + // Create an extra node for each cluster and connect it to the vertices + // of the cluster through some edge with very high capacity + for (int i = 0; i < node_count; i++) + { + struct Node *n = &graph->nodes[i]; + int cl = data->clusters[n->index]; + + digraph_edges[ke++] = n->index; + digraph_edges[ke++] = node_count + cl; + capacities[kc++] = 1e100; + + digraph_edges[ke++] = node_count + cl; + digraph_edges[ke++] = n->index; + capacities[kc++] = 1e100; + } + + assert(ke == 2 * digraph_edge_count); + assert(kc == digraph_edge_count); + + rval = graph_build(digraph_node_count, digraph_edge_count, digraph_edges, 1, + &digraph); abort_if(rval, "graph_build failed"); - for (int i = 0; i < graph->edge_count; i++) + for (int i = 0; i < digraph_edge_count; i += 2) { - digraph.edges[4 * i].reverse = &digraph.edges[4 * i + 1]; - digraph.edges[4 * i + 1].reverse = &digraph.edges[4 * i]; - - digraph.edges[4 * i + 2].reverse = &digraph.edges[4 * i + 3]; - digraph.edges[4 * i + 3].reverse = &digraph.edges[4 * i + 2]; + digraph.edges[i].reverse = &digraph.edges[i + 1]; + digraph.edges[i + 1].reverse = &digraph.edges[i]; } int max_x_index = 0; double max_x = DBL_MIN; - for (int i = 0; i < graph->node_count; i++) + for (int i = 0; i < node_count; i++) { struct Node *n = &graph->nodes[i]; if (x[n->index] > max_x) @@ -315,52 +443,148 @@ int GTSP_find_exact_subtour_elimination_cuts( } } - int i = max_x_index; - for (int j = 0; j < digraph.node_count; j++) + // Constraints (2.3) { - if (i == j) continue; + int i = max_x_index; + + for (int j = 0; j < node_count; j++) + { + if (i == j) continue; - if (clusters[i] == clusters[j]) continue; - if (x[i] + x[j] - 1 <= LP_EPSILON) continue; + if (clusters[i] == clusters[j]) continue; + if (x[i] + x[j] - 1 <= LP_EPSILON) continue; - struct Node *from = &digraph.nodes[i]; - struct Node *to = &digraph.nodes[j]; + struct Node *from = &digraph.nodes[i]; + struct Node *to = &digraph.nodes[j]; - log_verbose("Calculating max flow from %d to %to\n", from->index, - to->index); - double flow_value; - rval = flow_find_max_flow(&digraph, capacities, from, to, flow, - &flow_value); - abort_if(rval, "flow_find_max_flow failed"); + log_verbose("Calculating max flow from node %d to node %to\n", + from->index, to->index); + double flow_value; + rval = flow_find_max_flow(&digraph, capacities, from, to, flow, + &flow_value); + abort_if(rval, "flow_find_max_flow failed"); - log_verbose(" %.2lf\n", flow_value); + log_verbose(" %.2lf\n", flow_value); - if (flow_value >= 2 * (x[i] + x[j] - 1) - LP_EPSILON) continue; + if (flow_value >= 2 * (x[i] + x[j] - 1) - LP_EPSILON) continue; - log_verbose("violation: %.2lf >= %.2lf\n", flow_value, - 2 * (x[i] + x[j] - 1)); + log_verbose("violation: %.2lf >= %.2lf\n", flow_value, + 2 * (x[i] + x[j] - 1)); - int cut_edges_count; - rval = get_cut_edges_from_marks(&digraph, &cut_edges_count, cut_edges); - abort_if(rval, "get_cut_edges_from_marks failed"); + int cut_edges_count; + rval = get_cut_edges_from_marks(&digraph, &cut_edges_count, + cut_edges); + abort_if(rval, "get_cut_edges_from_marks failed"); - log_verbose("Adding cut for i=%d j=%d, cut edges:\n", i, j); - for (int k = 0; k < cut_edges_count/2; k++) + log_verbose("Adding cut for i=%d j=%d, cut edges:\n", i, j); + for (int k = 0; k < cut_edges_count / 2; k++) + { + cut_edges[k] = &graph->edges[cut_edges[k * 2]->index / 4]; + log_verbose(" %d %d\n", cut_edges[k * 2]->from->index, + cut_edges[k * 2]->to->index); + } + + rval = GTSP_add_subtour_elimination_cut(lp, graph, from, to, + cut_edges, cut_edges_count / 2); + abort_if(rval, "GTSP_add_subtour_elimination_cut failed"); + + (*added_cuts_count)++; + goto CLEANUP; + } + } + + // Constraints (2.2) + for (int i = 0; i < node_count; i++) + { + for (int j = 0; j < data->cluster_count; j++) { - cut_edges[k] = &graph->edges[cut_edges[k*2]->index / 4]; - log_verbose(" %d %d\n", cut_edges[k*2]->from->index, - cut_edges[k*2]->to->index); + if (clusters[i] == j) continue; + if (x[i] < LP_EPSILON) continue; + + struct Node *from = &digraph.nodes[i]; + struct Node *to = &digraph.nodes[node_count + j]; + + log_verbose("Calculating max flow from node %d to cluster %to\n", i, + j); + double flow_value; + rval = flow_find_max_flow(&digraph, capacities, from, to, flow, + &flow_value); + abort_if(rval, "flow_find_max_flow failed"); + + log_verbose(" %.2lf\n", flow_value); + + if (flow_value >= 2 * x[i] - LP_EPSILON) continue; + + log_verbose("violation: %.2lf >= %.2lf\n", flow_value, 2 * x[i]); + + int cut_edges_count; + rval = get_cut_edges_from_marks(&digraph, &cut_edges_count, + cut_edges); + abort_if(rval, "get_cut_edges_from_marks failed"); + + log_verbose("Adding cut for i=%d j=%d, cut edges:\n", i, j); + for (int k = 0; k < cut_edges_count / 2; k++) + { + cut_edges[k] = &graph->edges[cut_edges[k * 2]->index / 4]; + log_verbose(" %d %d\n", cut_edges[k * 2]->from->index, + cut_edges[k * 2]->to->index); + } + + rval = GTSP_add_subtour_elimination_cut_2(lp, graph, from, to, + cut_edges, cut_edges_count / 2); + abort_if(rval, "GTSP_add_subtour_elimination_cut failed"); + + (*added_cuts_count)++; + goto CLEANUP; } + } - rval = GTSP_add_subtour_elimination_cut(lp, graph, from, to, cut_edges, - cut_edges_count/2); - abort_if(rval, "GTSP_add_subtour_elimination_cut failed"); + // Constraints (2.1) + for (int i = 0; i < data->cluster_count; i++) + { + for (int j = i + 1; j < data->cluster_count; j++) + { + struct Node *from = &digraph.nodes[node_count + i]; + struct Node *to = &digraph.nodes[node_count + j]; + + log_verbose("Calculating max flow from cluster %d to cluster %to\n", + i, j); + double flow_value; + rval = flow_find_max_flow(&digraph, capacities, from, to, flow, + &flow_value); + abort_if(rval, "flow_find_max_flow failed"); + + log_verbose(" %.2lf\n", flow_value); + + if (flow_value >= 2 - LP_EPSILON) continue; + + log_verbose("violation: %.2lf >= 2\n", flow_value); + + int cut_edges_count; + rval = get_cut_edges_from_marks(&digraph, &cut_edges_count, + cut_edges); + abort_if(rval, "get_cut_edges_from_marks failed"); + + log_verbose("Adding cut for i=%d j=%d, cut edges:\n", i, j); + for (int k = 0; k < cut_edges_count / 2; k++) + { + cut_edges[k] = &graph->edges[cut_edges[k * 2]->index / 4]; + log_verbose(" %d %d\n", cut_edges[k * 2]->from->index, + cut_edges[k * 2]->to->index); + } - (*added_cuts_count)++; + rval = GTSP_add_subtour_elimination_cut_3(lp, graph, from, to, + cut_edges, cut_edges_count / 2); + abort_if(rval, "GTSP_add_subtour_elimination_cut failed"); + + (*added_cuts_count)++; + goto CLEANUP; + } } CLEANUP: + graph_free(&digraph); if (digraph_edges) free(digraph_edges); if (flow) free(flow); if (cut_edges) free(cut_edges); @@ -421,8 +645,7 @@ int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data) { log_debug("Found %d subtour elimination cuts using exact " "separation\n", added_cuts_count); - } - else break; + } else break; } CLEANUP: @@ -534,15 +757,15 @@ int GTSP_read_x(char *filename, double **p_x) edge = get_edge_num(node_count, from, to); abort_if(edge > num_cols, "invalid edge"); - x[from] = x[ - to] = 1.0; + x[from] += 0.5; + x[to] += 0.5; x[edge] = 1; } for (int i = 0; i < num_cols; i++) { if (x[i] <= LP_EPSILON) continue; - log_verbose(" x%-3d = %.2f\n", i, x[i]); + log_debug(" x%-3d = %.2f\n", i, x[i]); } *p_x = x; @@ -552,14 +775,13 @@ int GTSP_read_x(char *filename, double **p_x) return rval; } -static const struct option options_tab[] = { - {"help", no_argument, 0, 'h'}, {"nodes", required_argument, 0, 'n'}, - {"clusters", required_argument, 0, 'm'}, - {"grid-size", required_argument, 0, 'g'}, - {"optimal", required_argument, 0, 'x'}, - {"seed", required_argument, 0, 's'}, - {(char *) 0, (int) 0, (int *) 0, (int) 0} -}; +static const struct option options_tab[] = + {{"help", no_argument, 0, 'h'}, {"nodes", required_argument, 0, 'n'}, + {"clusters", required_argument, 0, 'm'}, + {"grid-size", required_argument, 0, 'g'}, + {"optimal", required_argument, 0, 'x'}, + {"seed", required_argument, 0, 's'}, + {(char *) 0, (int) 0, (int *) 0, (int) 0}}; static int input_node_count = 20; static int input_cluster_count = 5; @@ -623,6 +845,18 @@ 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 solution to file gtsp.out\n"); + rval = GTSP_write_solution(data, "gtsp.out", x); + abort_if(rval, "GTSP_write_solution failed"); + + CLEANUP: + return rval; +} + int GTSP_main(int argc, char **argv) { int rval = 0; @@ -630,7 +864,7 @@ int GTSP_main(int argc, char **argv) struct BNC bnc; struct GTSP data; - SEED = (unsigned int) get_real_time() % 10000; + SEED = (unsigned int) get_real_time() % 1000000; rval = GTSP_init_data(&data); abort_if(rval, "GTSP_init_data failed"); @@ -650,8 +884,7 @@ int GTSP_main(int argc, char **argv) log_info(" grid_size = %d\n", grid_size); rval = GTSP_create_random_problem(input_node_count, input_cluster_count, - grid_size, - &data); + grid_size, &data); abort_if(rval, "GTSP_create_random_problem failed"); log_info("Writing random instance to file gtsp.in\n"); @@ -663,10 +896,23 @@ int GTSP_main(int argc, char **argv) bnc.problem_init_lp = (int (*)(struct LP *, void *)) GTSP_init_lp; bnc.problem_add_cutting_planes = (int (*)(struct LP *, void *)) GTSP_add_cutting_planes; + bnc.problem_solution_found = + (int (*)(void *, double *)) GTSP_solution_found; if (OPTIMAL_X) + { 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]; + opt_val += OPTIMAL_X[i + input_node_count] * e->weight; + } + + log_info(" opt = %.2lf\n", opt_val); + } + log_info("Initializing LP...\n"); rval = BNC_init_lp(&bnc); abort_if(rval, "BNC_init_lp failed"); @@ -684,8 +930,9 @@ int GTSP_main(int argc, char **argv) log_info("Optimal integral solution:\n"); log_info(" obj value = %.2lf **\n", bnc.best_obj_val); - rval = GTSP_write_solution(&data, "gtsp.out", bnc.best_x); - abort_if(rval, "GTSP_write_solution failed"); + log_info("Branch-and-bound nodes: %d\n", BNC_NODE_COUNT); + log_info("Max-flow computations: %d\n", FLOW_MAX_FLOW_COUNT); + CLEANUP: GTSP_free(&data); diff --git a/src/util.h b/src/util.h index 43f3bb3..8a2bad6 100644 --- a/src/util.h +++ b/src/util.h @@ -11,18 +11,18 @@ #define LOG_LEVEL LOG_LEVEL_INFO -#if LOG_LEVEL < LOG_LEVEL_DEBUG -#define log_debug(...) -#else -#define log_debug(...) time_printf( __VA_ARGS__) -#endif - #if LOG_LEVEL < LOG_LEVEL_VERBOSE #define log_verbose(...) #else #define log_verbose(...) time_printf( __VA_ARGS__) #endif +#if LOG_LEVEL < LOG_LEVEL_DEBUG +#define log_debug(...) +#else +#define log_debug(...) time_printf( __VA_ARGS__) +#endif + #if LOG_LEVEL < LOG_LEVEL_INFO #define log_info(...) #else