Implement simple cut pool; avoid duplicate cuts
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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_
|
||||
|
||||
15
src/gtsp.c
15
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:
|
||||
|
||||
86
src/lp.c
86
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;
|
||||
}
|
||||
19
src/lp.h
19
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
|
||||
@@ -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(...)
|
||||
|
||||
Reference in New Issue
Block a user