diff --git a/scripts/benchmark.sh b/scripts/benchmark.sh index d9b3e41..f6b40b2 100755 --- a/scripts/benchmark.sh +++ b/scripts/benchmark.sh @@ -1,15 +1,18 @@ #!/bin/bash n=$1 +m=$2 t=0 all_t="" -TIMEFORMAT=%U +mkdir -p out/ printf "%12s\t%12s\n" "seed" "user-time (s)" + +TIMEFORMAT=%U for i in $(seq 0 9); do - t=$( { time bin/hw2.run --gtsp -s $i -n $n > out/gtsp-n${n}-s${i}.log; } 2>&1 ) + t=$( { time bin/hw2.run --gtsp -s "$i" -n "$n" -m "$m" 2>&1 > tmp/gtsp-m${m}-n${n}-s${i}.log; } 2>&1 ) all_t="$all_t $t" printf "%12d\t%12.3f\n" $i $t done echo -echo $all_t | scripts/mean.r +echo "$all_t" | scripts/mean.r diff --git a/src/branch_and_cut.c b/src/branch_and_cut.c index 8a3a5cb..142f77e 100644 --- a/src/branch_and_cut.c +++ b/src/branch_and_cut.c @@ -85,8 +85,6 @@ static int BNC_solve_node(struct BNC *bnc, int depth) int rval = 0; double *x = (double *) NULL; - log_debug("Optimizing...\n"); - int is_infeasible; rval = LP_optimize(lp, &is_infeasible); abort_if(rval, "LP_optimize failed\n"); @@ -104,7 +102,7 @@ static int BNC_solve_node(struct BNC *bnc, int depth) if (ceil(objval) > *best_val + LP_EPSILON) { log_debug("Branch pruned by bound (%.2lf > %.2lf).\n", objval, - *best_val); + *best_val); rval = 0; goto CLEANUP; } @@ -129,7 +127,7 @@ static int BNC_solve_node(struct BNC *bnc, int depth) if (ceil(objval) > *best_val + LP_EPSILON) { log_debug("Branch pruned by bound (%.2lf > %.2lf).\n", objval, - *best_val); + *best_val); rval = 0; goto CLEANUP; } @@ -153,7 +151,10 @@ static int BNC_solve_node(struct BNC *bnc, int depth) log_info(" obj val = %.2lf **\n", objval); if (bnc->problem_solution_found) - bnc->problem_solution_found(bnc->problem_data, bnc->best_x); + { + rval = bnc->problem_solution_found(bnc->problem_data, bnc->best_x); + abort_if(rval, "problem_solution_found failed"); + } } } else @@ -178,7 +179,7 @@ static int BNC_branch_node(struct BNC *bnc, double *x, int depth) int best_branch_var = BNC_find_best_branching_var(x, num_cols); log_debug("Branching on variable x%d = %.6lf (depth %d)...\n", - best_branch_var, x[best_branch_var], depth); + best_branch_var, x[best_branch_var], depth); log_debug("Fixing variable x%d to one...\n", best_branch_var); rval = LP_change_bound(lp, best_branch_var, 'L', 1.0); @@ -208,11 +209,17 @@ static int BNC_branch_node(struct BNC *bnc, double *x, int depth) static int BNC_is_integral(double *x, int num_cols) { +#ifdef ALLOW_FRACTIONAL_SOLUTIONS + UNUSED(num_cols); + UNUSED(x); + return 1; +#else for (int i = 0; i < num_cols; i++) if (x[i] > LP_EPSILON && x[i] < 1.0 - LP_EPSILON) return 0; return 1; +#endif } static int BNC_find_best_branching_var(double *x, int num_cols) diff --git a/src/graph.c b/src/graph.c index 2ae1f9f..1439606 100644 --- a/src/graph.c +++ b/src/graph.c @@ -11,6 +11,8 @@ void graph_init(struct Graph *graph) graph->adj = 0; graph->node_count = 0; graph->edge_count = 0; + graph->x_coordinates = 0; + graph->y_coordinates = 0; } void graph_free(struct Graph *graph) @@ -20,6 +22,8 @@ void graph_free(struct Graph *graph) if (graph->edges) free(graph->edges); if (graph->nodes) free(graph->nodes); if (graph->adj) free(graph->adj); + if (graph->x_coordinates) free(graph->x_coordinates); + if (graph->y_coordinates) free(graph->y_coordinates); } int graph_build( @@ -86,7 +90,7 @@ int graph_build( n = &graph->nodes[b]; n->adj[n->degree].neighbor_index = a; n->adj[n->degree].edge_index = i; - n->adj[n->degree].neighbor = &graph->nodes[b]; + n->adj[n->degree].neighbor = &graph->nodes[a]; n->adj[n->degree].edge = &graph->edges[i]; n->degree++; } diff --git a/src/graph.h b/src/graph.h index 67eed3d..9b00587 100644 --- a/src/graph.h +++ b/src/graph.h @@ -41,6 +41,9 @@ struct Graph struct Edge *edges; struct Node *nodes; + double *x_coordinates; + double *y_coordinates; + struct Adjacency *adj; }; diff --git a/src/gtsp-comb.c b/src/gtsp-comb.c new file mode 100644 index 0000000..07855b8 --- /dev/null +++ b/src/gtsp-comb.c @@ -0,0 +1,473 @@ +#include +#include "gtsp.h" +#include "util.h" + +int add_comb_cut( + struct LP *lp, + struct Graph *graph, + int current_component, + int *clusters, + int *components, + int *component_sizes, + int *teeth, + int tooth_count, + double *x) +{ + int rval = 0; + + char sense = 'G'; + + const int node_count = graph->node_count; + const int edge_count = graph->edge_count; + + struct Row *cut = 0; + int *rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc((node_count + edge_count) * sizeof(int)); + rmatval = (double *) malloc((node_count + edge_count) * sizeof(double)); + abort_if(!rmatind, "could not allocate rmatind"); + abort_if(!rmatval, "could not allocate rmatval"); + + double rhs = -component_sizes[current_component] - tooth_count + + (tooth_count + 1) / 2; + + int nz = 0; + + // Edges inside handle + for (int i = 0; i < edge_count; i++) + { + struct Edge *e = &graph->edges[i]; + if (components[clusters[e->from->index]] != current_component) continue; + if (components[clusters[e->to->index]] != current_component) continue; + + rmatind[nz] = node_count + e->index; + rmatval[nz] = -1.0; + nz++; + + log_verbose(" handle (%d %d)\n", e->from->index, e->to->index); + } + + // Edges inside each tooth + for (int i = 0; i < edge_count; i++) + { + struct Edge *e = &graph->edges[i]; + struct Node *from = e->from; + struct Node *to = e->to; + + if (teeth[clusters[from->index]] < 0) continue; + if (teeth[clusters[to->index]] < 0) continue; + if (teeth[clusters[from->index]] != teeth[clusters[to->index]]) + continue; + + log_verbose(" tooth (%d %d)\n", e->from->index, e->to->index); + + rmatind[nz] = node_count + e->index; + rmatval[nz] = -1.0; + nz++; + } + + // Lifting of the nodes + for (int i = 0; i < node_count; i++) + { + double val; + struct Node *n = &graph->nodes[i]; + int c = clusters[n->index]; + + if (components[c] == current_component) + val = (teeth[c] < 0 ? 1.0 : 2.0); + else + val = (teeth[c] < 0 ? 0.0 : 1.0); + + if (val == 0.0) continue; + + rmatind[nz] = n->index; + rmatval[nz] = val; + nz++; + + rhs += val; + } + + log_verbose("Generated cut:\n"); + for (int i = 0; i < nz; i++) + log_verbose(" %.2lf x%d (%.4lf)\n", rmatval[i], rmatind[i], + x[rmatind[i]]); + log_verbose(" %c %.2lf\n", sense, rhs); + + if (OPTIMAL_X) + { + double sum = 0; + for (int i = 0; i < nz; i++) + sum += rmatval[i] * OPTIMAL_X[rmatind[i]]; + abort_if(sum <= rhs - LP_EPSILON, "cannot add invalid cut"); + } + + double lhs = 0.0; + for (int i = 0; i < nz; i++) + lhs += rmatval[i] * x[rmatind[i]]; + + log_verbose("Violation: %.4lf >= %.4lf\n", lhs, rhs); + + if (lhs + LP_EPSILON > rhs) goto CLEANUP; + + cut = (struct Row *) malloc(sizeof(struct Row)); + abort_if(!cut, "could not allocate cut"); + + cut->nz = nz; + cut->sense = sense; + cut->rhs = rhs; + cut->rmatval = rmatval; + cut->rmatind = rmatind; + + rval = LP_add_cut(lp, cut); + abort_if(rval, "LP_add_cut failed"); + + CLEANUP: + if (rmatind) free(rmatind); + if (rmatval) free(rmatval); + return rval; +} + +int find_components( + struct Graph *graph, double *x, int *components, int *component_sizes) +{ + int rval = 0; + struct Node **stack = 0; + + const int node_count = graph->node_count; + + for (int i = 0; i < node_count; i++) + { + components[i] = -1; + graph->nodes[i].mark = 0; + } + + int stack_top = 0; + stack = (struct Node **) malloc(node_count * sizeof(struct Node *)); + abort_if(!stack, "could not allocate stack"); + + for (int i = 0; i < node_count; i++) + { + struct Node *root = &graph->nodes[i]; + if (root->mark) continue; + + stack[stack_top++] = root; + + while (stack_top > 0) + { + struct Node *n = stack[--stack_top]; + components[n->index] = i; + + for (int j = 0; j < n->degree; j++) + { + struct Adjacency *adj = &n->adj[j]; + struct Node *neighbor = adj->neighbor; + + if (neighbor->mark) continue; + + double x_e = x[adj->edge->index]; + if (x_e < LP_EPSILON) continue; + if (x_e > 1 - LP_EPSILON) continue; + + stack[stack_top++] = neighbor; + neighbor->mark = 1; + } + } + } + + for (int i = 0; i < node_count; i++) + component_sizes[i] = 0; + + for (int i = 0; i < node_count; i++) + component_sizes[components[i]]++; + + log_verbose("Components:\n"); + for (int i = 0; i < graph->node_count; i++) + log_verbose(" %d %d\n", i, components[i]); + + log_verbose("Component sizes:\n"); + for (int i = 0; i < graph->node_count; i++) + log_verbose(" %d %d\n", i, component_sizes[i]); + + CLEANUP: + if (stack) free(stack); + return rval; +} + +int find_teeth( + struct Graph *graph, + double *x, + int current_component, + int *components, + int *teeth, + int *tooth_count) +{ + const int node_count = graph->node_count; + const int edge_count = graph->edge_count; + + for (int i = 0; i < node_count; i++) + { + graph->nodes[i].mark = 0; + teeth[i] = -1; + } + + *tooth_count = 0; + + for (int i = 0; i < edge_count; i++) + { + struct Edge *e = &graph->edges[i]; + struct Node *from = e->from; + struct Node *to = e->to; + + if (x[e->index] < 1 - LP_EPSILON) continue; + + if (to->mark || from->mark) continue; + + int z = 0; + if (components[from->index] == current_component) z++; + if (components[to->index] == current_component) z++; + if (z != 1) continue; + + to->mark = 1; + from->mark = 1; + + teeth[to->index] = *tooth_count; + teeth[from->index] = *tooth_count; + + (*tooth_count)++; + } + + return 0; +} + +int write_shrunken_graph( + double *shrunken_x, + struct Graph *shrunken_graph, + int const cluster_count); + +static int shrink_clusters( + const struct GTSP *data, + double *x, + struct Graph *shrunken_graph, + double *shrunken_x) +{ + int rval = 0; + + double *x_coords = 0; + double *y_coords = 0; + int *cluster_sizes = 0; + + const int *clusters = data->clusters; + const int cluster_count = data->cluster_count; + const struct Graph *graph = data->graph; + + int *edges = 0; + int *edge_map = 0; + int edge_count = (cluster_count * (cluster_count - 1)) / 2; + + edge_map = (int *) malloc(cluster_count * cluster_count * sizeof(int)); + abort_if(!edge_map, "could not allocate edge_map"); + + edges = (int *) malloc(2 * edge_count * sizeof(int)); + abort_if(!edges, "could not allocate edges"); + + cluster_sizes = (int *) malloc(cluster_count * sizeof(int)); + x_coords = (double *) malloc(cluster_count * sizeof(double)); + y_coords = (double *) malloc(cluster_count * sizeof(double)); + + abort_if(!cluster_sizes, "could not allocate cluster_sizes"); + abort_if(!x_coords, "could not allocate x_coords"); + abort_if(!y_coords, "could not allocate y_coords"); + + for (int i = 0; i < cluster_count; i++) + { + x_coords[i] = 0.0; + y_coords[i] = 0.0; + cluster_sizes[i] = 0; + } + + for (int i = 0; i < graph->node_count; i++) + { + struct Node *n = &graph->nodes[i]; + int c = clusters[n->index]; + + cluster_sizes[c]++; + x_coords[c] += graph->x_coordinates[n->index]; + y_coords[c] += graph->y_coordinates[n->index]; + } + + for (int i = 0; i < cluster_count; i++) + { + x_coords[i] = x_coords[i] / cluster_sizes[i]; + y_coords[i] = y_coords[i] / cluster_sizes[i]; + } + + shrunken_graph->x_coordinates = x_coords; + shrunken_graph->y_coordinates = y_coords; + + int curr_edge = 0; + for (int i = 0; i < cluster_count; i++) + { + for (int j = i + 1; j < cluster_count; j++) + { + edges[curr_edge * 2] = i; + edges[curr_edge * 2 + 1] = j; + edge_map[i * cluster_count + j] = curr_edge; + edge_map[j * cluster_count + i] = curr_edge; + curr_edge++; + } + } + + assert(curr_edge == edge_count); + + rval = graph_build(cluster_count, edge_count, edges, 0, shrunken_graph); + abort_if(rval, "graph_build failed"); + + for (int i = 0; i < edge_count; i++) + shrunken_x[i] = 0.0; + + for (int i = 0; i < graph->edge_count; i++) + { + struct Edge *e = &graph->edges[i]; + + int from = clusters[e->from->index]; + int to = clusters[e->to->index]; + int shunk_e_index = edge_map[from * cluster_count + to]; + + shrunken_x[shunk_e_index] += x[graph->node_count + e->index]; + } + + CLEANUP: + if (edges) free(edges); + if (edge_map) free(edge_map); + if (cluster_sizes) free(cluster_sizes); + return rval; +} + +int find_comb_cuts(struct LP *lp, struct GTSP *data) +{ + int rval = 0; + + double *x = 0; + double *shrunken_x = 0; + + int *teeth = 0; + int *components = 0; + int *component_sizes = 0; + + int num_cols = LP_get_num_cols(lp); + x = (double *) malloc(num_cols * sizeof(double)); + abort_if(!x, "could not allocate x"); + + rval = LP_get_x(lp, x); + abort_if(rval, "LP_get_x failed"); + + struct Graph shrunken_graph; + graph_init(&shrunken_graph); + + const int cluster_count = data->cluster_count; + const int shrunken_edge_count = (cluster_count * (cluster_count - 1)) / 2; + + shrunken_x = (double *) malloc(shrunken_edge_count * sizeof(double)); + abort_if(!shrunken_x, "could not allocate shrunken_x"); + + rval = shrink_clusters(data, x, &shrunken_graph, shrunken_x); + abort_if(rval, "shrink_clusters failed"); + + rval = write_shrunken_graph(shrunken_x, &shrunken_graph, cluster_count); + abort_if(rval, "write_shrunken_graph failed"); + + teeth = (int *) malloc(cluster_count * sizeof(int)); + components = (int *) malloc(cluster_count * sizeof(int)); + component_sizes = (int *) malloc(cluster_count * sizeof(int)); + + abort_if(!teeth, "could not allocate teeth"); + abort_if(!components, "could not allocate components"); + abort_if(!component_sizes, "could not allocate component_sizes"); + + rval = find_components(&shrunken_graph, shrunken_x, components, + component_sizes); + abort_if(rval, "find_components failed"); + + #if LOG_LEVEL >= LOG_LEVEL_DEBUG + int original_cut_pool_size = lp->cut_pool_size; + #endif + + for (int i = 0; i < cluster_count; i++) + { + if (component_sizes[i] < 3) continue; + + int tooth_count; + rval = find_teeth(&shrunken_graph, shrunken_x, i, components, teeth, + &tooth_count); + abort_if(rval, "find_teeth failed"); + + log_verbose("Component %d has %d teeth:\n", i, tooth_count); + for (int j = 0; j < cluster_count; j++) + { + if (teeth[j] < 0) continue; + log_verbose(" %d %d\n", j, teeth[j]); + } + + if (tooth_count % 2 == 0) continue; + + rval = add_comb_cut(lp, data->graph, i, data->clusters, components, + component_sizes, teeth, tooth_count, x); + abort_if(rval, "add_comb_cut failed"); + } + + log_debug(" %d combs\n", lp->cut_pool_size - original_cut_pool_size); + + CLEANUP: + graph_free(&shrunken_graph); + if (teeth) free(teeth); + if (components) free(components); + if (component_sizes) free(component_sizes); + if (shrunken_x) free(shrunken_x); + return rval; +} + +int write_shrunken_graph( + double *shrunken_x, + struct Graph *shrunken_graph, + int const cluster_count) +{ + int rval = 0; + + FILE *file = 0; + + file = fopen("gtsp-shrunken.in", "w"); + abort_if(!file, "could not open file"); + + fprintf(file, "%d %d\n", (*shrunken_graph).node_count, cluster_count); + for (int i = 0; i < (*shrunken_graph).node_count; i++) + { + fprintf(file, "%.2lf %.2lf %d\n", (*shrunken_graph).x_coordinates[i], + (*shrunken_graph).y_coordinates[i], i); + } + + fclose(file); + + file = fopen("gtsp-shrunken.out", "w"); + abort_if(!file, "could not open file"); + + int positive_edge_count = 0; + for (int i = 0; i < (*shrunken_graph).edge_count; i++) + if (shrunken_x[i] > LP_EPSILON) + positive_edge_count++; + + fprintf(file, "%d %d\n", (*shrunken_graph).node_count, + (*shrunken_graph).edge_count); + + fprintf(file, "%d\n", positive_edge_count); + + for (int i = 0; i < (*shrunken_graph).edge_count; i++) + if (shrunken_x[i] > LP_EPSILON) + fprintf(file, "%d %d %.4lf\n", + (*shrunken_graph).edges[i].from->index, + (*shrunken_graph).edges[i].to->index, shrunken_x[i]); + fclose(file); + + CLEANUP: + return rval; +} \ No newline at end of file diff --git a/src/gtsp-comb.h b/src/gtsp-comb.h new file mode 100644 index 0000000..ee600e8 --- /dev/null +++ b/src/gtsp-comb.h @@ -0,0 +1,6 @@ +#ifndef PROJECT_GTSP_COMB_H +#define PROJECT_GTSP_COMB_H + +int find_comb_cuts(struct LP *lp, struct GTSP *data); + +#endif //PROJECT_GTSP_COMB_H diff --git a/src/gtsp-subtour.c b/src/gtsp-subtour.c index fa9d1ea..13df7b6 100644 --- a/src/gtsp-subtour.c +++ b/src/gtsp-subtour.c @@ -143,7 +143,7 @@ int static add_subtour_cut( 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(" %8.2f x%d\n", rmatval[i], rmatind[i]); log_verbose(" %c %.2lf\n", sense, rhs); if (OPTIMAL_X) @@ -213,12 +213,9 @@ int find_exact_subtour_cuts( abort_if(rval, "find_exact_subtour_cuts_cluster_to_cluster failed"); added_cuts_count = lp->cut_pool_size - original_cut_pool_size; + log_debug(" %d cluster-to-cluster\n", added_cuts_count); if (added_cuts_count > 0) - { - log_debug("Added %d cluster-to-cluster subtour cuts\n", - added_cuts_count); goto CLEANUP; - } // Constraints (2.2) original_cut_pool_size = lp->cut_pool_size; @@ -227,11 +224,9 @@ int find_exact_subtour_cuts( abort_if(rval, "find_exact_subtour_cuts_node_to_cluster failed"); added_cuts_count = lp->cut_pool_size - original_cut_pool_size; + log_debug(" %d node-to-cluster\n", added_cuts_count); if (added_cuts_count > 0) - { - log_debug("Added %d node-to-cluster subtour cuts\n", added_cuts_count); goto CLEANUP; - } // Constraints (2.3) original_cut_pool_size = lp->cut_pool_size; @@ -240,11 +235,9 @@ int find_exact_subtour_cuts( abort_if(rval, "find_exact_subtour_cuts_node_to_node failed"); added_cuts_count = lp->cut_pool_size - original_cut_pool_size; + log_debug(" %d node-to-node\n", added_cuts_count); if (added_cuts_count > 0) - { - log_debug("Added %d node-to-node subtour cuts\n", added_cuts_count); goto CLEANUP; - } CLEANUP: graph_free(&digraph); diff --git a/src/gtsp.c b/src/gtsp.c index 11fdeb8..5a09bf7 100644 --- a/src/gtsp.c +++ b/src/gtsp.c @@ -2,36 +2,22 @@ #include #include #include +#include #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); diff --git a/src/gtsp.h b/src/gtsp.h index 86f1729..9292095 100644 --- a/src/gtsp.h +++ b/src/gtsp.h @@ -1,7 +1,3 @@ -// -// Created by isoron on 18/03/15. -// - #ifndef _PROJECT_GTSP_H_ #define _PROJECT_GTSP_H_ @@ -21,11 +17,14 @@ struct GTSP int *clusters; int cluster_count; +<<<<<<< HEAD double *x_coordinates; double *y_coordinates; int** dist_matrix; struct CLUSTER *vertex_set; +======= +>>>>>>> 550b5f1912da4f50cd0bd0a7d2f19cdbf695b180 }; int GTSP_create_random_problem( diff --git a/src/lp.c b/src/lp.c index f2cd0dd..67382f9 100644 --- a/src/lp.c +++ b/src/lp.c @@ -165,10 +165,11 @@ int LP_optimize(struct LP *lp, int *infeasible) *infeasible = 0; +#if LOG_LEVEL >= LOG_LEVEL_DEBUG int numrows = CPXgetnumrows(lp->cplex_env, lp->cplex_lp); int numcols = CPXgetnumcols(lp->cplex_env, lp->cplex_lp); - log_debug("Optimizing LP (%d rows %d cols)...\n", numrows, numcols); +#endif double time_before = get_current_time(); rval = CPXdualopt(lp->cplex_env, lp->cplex_lp); @@ -228,7 +229,7 @@ int LP_remove_old_cuts(struct LP *lp) log_verbose("Old cplex row index:\n"); for (int i = 0; i < lp->cut_pool_size; i++) - log_verbose(" %d\n", lp->cut_pool[i]->cplex_row_index); + log_verbose(" %d\n", lp->cut_pool[i]->cplex_row_index); log_verbose("Should remove:\n"); for (int i = 0; i < lp->cut_pool_size; i++) @@ -250,7 +251,7 @@ int LP_remove_old_cuts(struct LP *lp) { struct Row *cut = lp->cut_pool[j]; - if (cut->cplex_row_index == i - count) cut->cplex_row_index = 0; + if (cut->cplex_row_index == i - count) cut->cplex_row_index = -1; else if (cut->cplex_row_index > i - count) cut->cplex_row_index--; } @@ -259,7 +260,7 @@ int LP_remove_old_cuts(struct LP *lp) log_verbose("New cplex row index:\n"); for (int i = 0; i < lp->cut_pool_size; i++) - log_verbose(" %d\n", lp->cut_pool[i]->cplex_row_index); + log_verbose(" %d\n", lp->cut_pool[i]->cplex_row_index); int start = 0; int end = -1; @@ -298,7 +299,7 @@ int LP_remove_old_cuts(struct LP *lp) if (count > 0) { - log_info("Found and removed %d old cuts\n", count); + log_debug("Found and removed %d old cuts\n", count); rval = CPXdualopt(lp->cplex_env, lp->cplex_lp); abort_if(rval, "CPXoptimize failed"); } @@ -388,6 +389,9 @@ int LP_add_cut(struct LP *lp, struct Row *cut) if (lp->cut_pool[i]->hash != cut->hash) continue; if (!compare_cuts(lp->cut_pool[i], cut)) { + log_verbose("Discarding duplicate cut (same as cplex row %d)\n", + lp->cut_pool[i]->cplex_row_index); + free(cut->rmatval); free(cut->rmatind); free(cut); diff --git a/src/params.h b/src/params.h new file mode 100644 index 0000000..36998ea --- /dev/null +++ b/src/params.h @@ -0,0 +1,10 @@ +#ifndef PROJECT_PARAMS_H +#define PROJECT_PARAMS_H + +#define LOG_LEVEL LOG_LEVEL_DEBUG + +//#define ENABLE_COMB_INEQUALITIES + +//#define ALLOW_FRACTIONAL_SOLUTIONS + +#endif //PROJECT_PARAMS_H diff --git a/src/util.h b/src/util.h index f0ca093..57c9c01 100644 --- a/src/util.h +++ b/src/util.h @@ -2,6 +2,7 @@ #define _PROJECT_UTIL_H_ #include +#include "params.h" #define LOG_LEVEL_ERROR 10 #define LOG_LEVEL_WARNING 20 @@ -9,8 +10,11 @@ #define LOG_LEVEL_DEBUG 40 #define LOG_LEVEL_VERBOSE 50 +<<<<<<< HEAD #define LOG_LEVEL LOG_LEVEL_DEBUG +======= +>>>>>>> 550b5f1912da4f50cd0bd0a7d2f19cdbf695b180 #if LOG_LEVEL < LOG_LEVEL_VERBOSE #define log_verbose(...) #else @@ -45,6 +49,10 @@ fprintf(stderr, "%20s:%d " msg "\n", __FILE__, __LINE__); \ rval = 1; goto CLEANUP; } +#define abort_iff(cond, msg, ...) if(cond) { \ + fprintf(stderr, "%20s:%d " msg "\n", __FILE__, __LINE__, __VA_ARGS__); \ + rval = 1; goto CLEANUP; } + #define swap(x, y) do \ { unsigned char swap_temp[sizeof(x)]; \ memcpy(swap_temp,&y,sizeof(x)); \