From cb580892b95b91f8f40be8dd50a9682c961171a7 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Fri, 27 Mar 2015 19:35:00 -0400 Subject: [PATCH] Implement simple cut pool; avoid duplicate cuts --- src/gtsp-subtour.c | 48 +++++++++++++------------- src/gtsp-subtour.h | 4 --- src/gtsp.c | 15 +++----- src/lp.c | 86 +++++++++++++++++++++++++++++++++++++++++----- src/lp.h | 19 ++++++++++ src/util.h | 2 +- 6 files changed, 127 insertions(+), 47 deletions(-) diff --git a/src/gtsp-subtour.c b/src/gtsp-subtour.c index 9f78e30..d9f9d28 100644 --- a/src/gtsp-subtour.c +++ b/src/gtsp-subtour.c @@ -114,7 +114,6 @@ int static add_subtour_cut( double rhs = 2.0 - 2.0 * type; int newnz = cut_edges_count + type; - int rmatbeg = 0; int *rmatind = 0; double *rmatval = 0; @@ -155,18 +154,27 @@ int static add_subtour_cut( 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"); + struct Row *cut = 0; + cut = (struct Row *) malloc(sizeof(struct Row)); + abort_if(!cut, "could not allocate cut"); + + cut->nz = newnz; + 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 (rmatval) free(rmatval); - if (rmatind) free(rmatind); return rval; } int find_exact_subtour_cuts( - struct LP *lp, struct GTSP *data, int *total_added_cuts, double min_cut_violation) + struct LP *lp, + struct GTSP *data, + double min_cut_violation) { int rval = 0; @@ -190,10 +198,11 @@ int find_exact_subtour_cuts( struct Graph digraph; graph_init(&digraph); - int digraph_edge_count = 4 * graph->edge_count + 2 * graph->node_count + 2 * data->cluster_count; + int original_cut_pool_size = lp->cut_pool_size; + capacities = (double *) malloc(digraph_edge_count * sizeof(double)); abort_if(!capacities, "could not allocate capacities"); @@ -202,29 +211,31 @@ int find_exact_subtour_cuts( // Constraints (2.1) rval = find_exact_subtour_cuts_cluster_to_cluster(lp, data, &digraph, - capacities, &added_cuts_count, min_cut_violation); + capacities, min_cut_violation); abort_if(rval, "find_exact_subtour_cuts_cluster_to_cluster failed"); + added_cuts_count = lp->cut_pool_size - original_cut_pool_size; log_debug("Added %d cluster-to-cluster subtour cuts\n", added_cuts_count); - (*total_added_cuts) += added_cuts_count; if (added_cuts_count > 0) goto CLEANUP; // Constraints (2.2) + original_cut_pool_size = lp->cut_pool_size; rval = find_exact_subtour_cuts_node_to_cluster(lp, data, x, &digraph, - capacities, &added_cuts_count, min_cut_violation); + capacities, min_cut_violation); abort_if(rval, "find_exact_subtour_cuts_node_to_cluster failed"); + added_cuts_count = lp->cut_pool_size - original_cut_pool_size; log_debug("Added %d node-to-cluster subtour cuts\n", added_cuts_count); - (*total_added_cuts) += added_cuts_count; if (added_cuts_count > 0) goto CLEANUP; // Constraints (2.3) + original_cut_pool_size = lp->cut_pool_size; rval = find_exact_subtour_cuts_node_to_node(lp, data, x, &digraph, - capacities, &added_cuts_count, min_cut_violation); + capacities, min_cut_violation); abort_if(rval, "find_exact_subtour_cuts_node_to_node failed"); + added_cuts_count = lp->cut_pool_size - original_cut_pool_size; log_debug("Added %d node-to-node subtour cuts\n", added_cuts_count); - (*total_added_cuts) += added_cuts_count; CLEANUP: graph_free(&digraph); @@ -239,7 +250,6 @@ int find_exact_subtour_cuts_node_to_node( double *x, struct Graph *digraph, double *capacities, - int *added_cuts_count, double min_cut_violation) { int rval = 0; @@ -309,8 +319,6 @@ int find_exact_subtour_cuts_node_to_node( rval = add_subtour_cut(lp, graph, from, to, cut_edges, cut_edges_count, 2); abort_if(rval, "add_subtour_cut failed"); - - (*added_cuts_count)++; } CLEANUP: @@ -325,7 +333,6 @@ int find_exact_subtour_cuts_node_to_cluster( double *x, struct Graph *digraph, double *capacities, - int *added_cuts_count, double min_cut_violation) { int rval = 0; @@ -396,7 +403,6 @@ int find_exact_subtour_cuts_node_to_cluster( cut_edges_count, 1); abort_if(rval, "add_subtour_cut failed"); - (*added_cuts_count)++; cuts_count++; } } @@ -412,7 +418,6 @@ int find_exact_subtour_cuts_cluster_to_cluster( struct GTSP *data, struct Graph *digraph, double *capacities, - int *added_cuts_count, double min_cut_violation) { int rval = 0; @@ -480,11 +485,8 @@ int find_exact_subtour_cuts_cluster_to_cluster( 0); abort_if(rval, "add_subtour_cut failed"); - (*added_cuts_count)++; cuts_count++; } - -// if(cuts_count > 0) break; } CLEANUP: diff --git a/src/gtsp-subtour.h b/src/gtsp-subtour.h index 3833ce0..42cfef1 100644 --- a/src/gtsp-subtour.h +++ b/src/gtsp-subtour.h @@ -12,7 +12,6 @@ int find_exact_subtour_cuts_cluster_to_cluster( struct GTSP *data, struct Graph *digraph, double *capacities, - int *added_cuts_count, double min_cut_violation); int find_exact_subtour_cuts_node_to_cluster( @@ -21,7 +20,6 @@ int find_exact_subtour_cuts_node_to_cluster( double *x, struct Graph *digraph, double *capacities, - int *added_cuts_count, double min_cut_violation); int find_exact_subtour_cuts_node_to_node( @@ -30,13 +28,11 @@ int find_exact_subtour_cuts_node_to_node( double *x, struct Graph *digraph, double *capacities, - int *added_cuts_count, double min_cut_violation); int find_exact_subtour_cuts( struct LP *lp, struct GTSP *data, - int *total_added_cuts, double min_cut_violation); #endif //_PROJECT_GTSP_SUBTOUR_H_ diff --git a/src/gtsp.c b/src/gtsp.c index e6ad0c0..27b53ba 100644 --- a/src/gtsp.c +++ b/src/gtsp.c @@ -188,7 +188,6 @@ int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data) int rval = 0; int round = 0; - int added_cuts_count = 0; int violation_total = 3; int violation_current = 0; @@ -197,19 +196,17 @@ int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data) while (1) { round++; - int added_cuts_this_round = 0; log_debug("Finding subtour cuts, round %d, violation %.4lf...\n", round, violations[violation_current]); - rval = find_exact_subtour_cuts(lp, data, &added_cuts_this_round, - violations[violation_current]); + int original_cut_pool_size = lp->cut_pool_size; + rval = find_exact_subtour_cuts(lp, data, violations[violation_current]); abort_if(rval, "find_exact_subtour_cuts failed"); - if (added_cuts_this_round == 0) + if (lp->cut_pool_size - original_cut_pool_size == 0) { - ++violation_current; - if (violation_current < violation_total) + if (++violation_current < violation_total) { log_debug("No cuts found. Decreasing minimum cut violation.\n"); continue; @@ -234,7 +231,7 @@ int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data) abort_if(rval, "LP_get_obj_val failed"); log_debug(" obj val = %.4lf\n", obj_val); - log_debug(" time = %.2lf\n", time_after_lp-time_before_lp); + log_debug(" time = %.2lf\n", time_after_lp - time_before_lp); if (time_after_lp - time_before_lp > 10.0) { @@ -248,8 +245,6 @@ int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data) abort_if(rval, "LP_optimize failed"); if (is_infeasible) break; - - added_cuts_count += added_cuts_this_round; } CLEANUP: diff --git a/src/lp.c b/src/lp.c index adbb59b..ad7bd9f 100644 --- a/src/lp.c +++ b/src/lp.c @@ -6,6 +6,7 @@ #include "util.h" #define LP_EPSILON 0.000001 +#define MAX_CUT_POOL_SIZE 100000 int LP_open(struct LP *lp) { @@ -13,11 +14,11 @@ int LP_open(struct LP *lp) lp->cplex_lp = (CPXLPptr) NULL; lp->cplex_env = CPXopenCPLEX(&rval); - if (rval) - { - fprintf(stderr, "CPXopenCPLEX failed, return code %d\n", rval); - goto CLEANUP; - } + abort_if(rval, "CPXopenCPLEX failed"); + + lp->cut_pool = (struct Row **) malloc( + MAX_CUT_POOL_SIZE * sizeof(struct Row *)); + abort_if(!lp->cut_pool, "could not allocate cut_pool"); CLEANUP: return rval; @@ -33,6 +34,7 @@ void LP_free(struct LP *lp) CPXcloseCPLEX(&lp->cplex_env); lp->cplex_env = 0; + if (lp->cut_pool) free(lp->cut_pool); } int LP_create(struct LP *lp, const char *name) @@ -158,9 +160,9 @@ int LP_remove_slacks(struct LP *lp, int first_row, double max_slack) int *should_remove = 0; int numrows = CPXgetnumrows(lp->cplex_env, lp->cplex_lp); - if(numrows < 5000) return 0; + if (numrows < 5000) return 0; - should_remove = (int *) malloc((numrows+1) * sizeof(int)); + should_remove = (int *) malloc((numrows + 1) * sizeof(int)); abort_if(!should_remove, "could not allocate should_remove"); slacks = (double *) malloc(numrows * sizeof(double)); @@ -190,12 +192,12 @@ int LP_remove_slacks(struct LP *lp, int first_row, double max_slack) rval = CPXdelrows(lp->cplex_env, lp->cplex_lp, start - count, end - count); abort_if(rval, "CPXdelrows failed"); - log_verbose(" %d %d (%d)\n", start, end, end-start+1); + log_verbose(" %d %d (%d)\n", start, end, end - start + 1); count += end - start + 1; } - start = i+1; + start = i + 1; end = i; } } @@ -252,3 +254,69 @@ int LP_write(struct LP *lp, const char *fname) CLEANUP: return rval; } + +#define return_if_neq(a, b) \ + if((a)<(b)) return -1; \ + if((a)>(b)) return 1; + +#define return_if_neq_epsilon(a, b) \ + if((a+LP_EPSILON)<(b)) return -1; \ + if((a-LP_EPSILON)>(b)) return 1; + +int compare_cuts(struct Row *cut1, struct Row *cut2) +{ + return_if_neq(cut1->nz, cut2->nz); + + for (int i = 0; i < cut1->nz; i++) + { + return_if_neq(cut1->rmatind[i], cut2->rmatind[i]); + return_if_neq_epsilon(cut1->rmatval[i], cut2->rmatval[i]); + } + + return 0; +} + +int LP_add_cut(struct LP *lp, struct Row *cut) +{ + int rval = 0; + + rval = LP_update_hash(cut); + abort_if(rval, "LP_update_hash failed"); + + for (int i = 0; i < lp->cut_pool_size; i++) + { + if (lp->cut_pool[i]->hash != cut->hash) continue; + if (!compare_cuts(lp->cut_pool[i], cut)) + { + free(cut->rmatval); + free(cut->rmatind); + free(cut); + return 0; + } + } + + lp->cut_pool[lp->cut_pool_size++] = cut; + + int rmatbeg = 0; + rval = LP_add_rows(lp, 1, cut->nz, &cut->rhs, &cut->sense, &rmatbeg, + cut->rmatind, cut->rmatval); + abort_if(rval, "LP_add_rows failed"); + + CLEANUP: + return rval; +} + +int LP_update_hash(struct Row *cut) +{ + unsigned long hash = 0; + + for (int i = 0; i < cut->nz; i++) + { + hash += cut->rmatind[i] * 65521; + hash %= 4294967291; + } + + cut->hash = hash; + + return 0; +} \ No newline at end of file diff --git a/src/lp.h b/src/lp.h index 44ebfe8..64e35f5 100644 --- a/src/lp.h +++ b/src/lp.h @@ -9,6 +9,21 @@ struct LP { CPXENVptr cplex_env; CPXLPptr cplex_lp; + + int cut_pool_size; + struct Row **cut_pool; +}; + +struct Row +{ + unsigned long hash; +// int cplex_row_index; + + int nz; + char sense; + double rhs; + double *rmatval; + int *rmatind; }; static const int MAX_NAME_LENGTH = 100; @@ -56,4 +71,8 @@ int LP_get_num_cols(struct LP *lp); int LP_remove_slacks(struct LP *lp, int start, double max_slack); +int LP_update_hash(struct Row *cut); + +int LP_add_cut(struct LP *lp, struct Row *cut); + #endif \ No newline at end of file diff --git a/src/util.h b/src/util.h index f0ca093..61552cb 100644 --- a/src/util.h +++ b/src/util.h @@ -9,7 +9,7 @@ #define LOG_LEVEL_DEBUG 40 #define LOG_LEVEL_VERBOSE 50 -#define LOG_LEVEL LOG_LEVEL_DEBUG +#define LOG_LEVEL LOG_LEVEL_INFO #if LOG_LEVEL < LOG_LEVEL_VERBOSE #define log_verbose(...)