diff --git a/onerow/benchmark/generate_tables b/onerow/benchmark/generate_tables index 7beed14..d312a9a 100755 --- a/onerow/benchmark/generate_tables +++ b/onerow/benchmark/generate_tables @@ -3,28 +3,21 @@ for i in out/*yaml; do IN=${i/.pre.yaml/} IN=${IN/out\//} grep -q mip_value $i && continue - echo $IN grep $IN instances/opt.tab | awk '{ print "mip_value:\n "$2 }' >> $i done -echo " TABLE 1 " -echo "--------------------------------------------------------------------------------" -printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" instance cutsmir cutsw origgap mirperf wperf mircontrib wcontrib wimprov wtime | tee 'tables/gap.csv' +printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" instance cutsmir cutsw origgap mirperf wperf mircontrib wcontrib wimprov wtime > 'tables/gap.csv' for i in out/*.yaml; do IN=${i/.yaml/} IN=${IN/out\//} printf "%s," $IN scripts/gap.rb $i -done | sort -t',' -nrsk 8 | sed -e 's/,$//g' | tee -a 'tables/gap.csv' +done | sed -e 's/,$//g' >> 'tables/gap.csv' -echo -echo -echo " TABLE 2 " -echo "--------------------------------------------------------------------------------" -printf "%s,%s,%s,%s,%s,%s\n" instance cutsmir cutsw mirt wedget avgm | tee 'tables/speed.csv' +printf "%s,%s,%s,%s,%s,%s\n" instance cutsmir cutsw mirt wedget avgm > 'tables/speed.csv' for i in out/*.yaml; do IN=${i/.yaml/} IN=${IN/out\//} printf "%s," $IN scripts/speed.rb $i -done | sort | sed -e 's/,$//g' | tee -a 'tables/speed.csv' +done | sort | sed -e 's/,$//g' >> 'tables/speed.csv' diff --git a/onerow/benchmark/src/main.cpp b/onerow/benchmark/src/main.cpp index 86bf77d..351ab97 100644 --- a/onerow/benchmark/src/main.cpp +++ b/onerow/benchmark/src/main.cpp @@ -149,21 +149,21 @@ int main(int argc, char **argv) if (enable_gomory_cuts) { time_printf("Generating Gomory cuts...\n"); - cplexHelper.add_single_row_cuts(); + cplexHelper.add_single_row_cuts(0); cplexHelper.solve(true); } if (enable_mir_cuts) { time_printf("Generating MIR cuts...\n"); - cplexHelper.add_single_row_cuts(); + cplexHelper.add_single_row_cuts(0); cplexHelper.solve(true); } if (enable_wedge_cuts) { time_printf("Generating wedge cuts...\n"); - cplexHelper.add_single_row_cuts(); + cplexHelper.add_single_row_cuts(MAX_GOOD_ROWS); cplexHelper.solve(true); } diff --git a/onerow/library/include/onerow/cplex_helper.hpp b/onerow/library/include/onerow/cplex_helper.hpp index b9a8c15..2f997e5 100644 --- a/onerow/library/include/onerow/cplex_helper.hpp +++ b/onerow/library/include/onerow/cplex_helper.hpp @@ -69,10 +69,11 @@ public: * cuts as possible. The cuts are generated by the provided generator class. * * @tparam Generator Class used to generate the cuts. + * @param max_rows The maximum number of rows to consider. * @returns The number of cuts added. */ template - int add_single_row_cuts(); + int add_single_row_cuts(int max_rows); /** * Gets a single row from the current tableau. @@ -90,7 +91,7 @@ public: * * @returns The solution status, as returned by CPXgetstat. */ - int solve(bool save_stats = false); + void solve(bool save_stats = false); void dump_constraint(const Constraint &c, const char *msg = ""); @@ -107,7 +108,7 @@ public: void print_solution(double *x); - void find_good_rows(); + void find_good_rows(int max_rows); int n_rows; int n_cols; @@ -134,6 +135,9 @@ public: int n_good_rows; int *good_rows; + + double *reduced_costs; + double cost_cutoff; }; #include "cplex_helper.tpp" diff --git a/onerow/library/include/onerow/cplex_helper.tpp b/onerow/library/include/onerow/cplex_helper.tpp index 474375a..03e38bc 100644 --- a/onerow/library/include/onerow/cplex_helper.tpp +++ b/onerow/library/include/onerow/cplex_helper.tpp @@ -30,12 +30,17 @@ using std::endl; template -int CplexHelper::add_single_row_cuts() +int CplexHelper::add_single_row_cuts(int max_rows) { total_cuts = 0; - if(n_good_rows < 0) - find_good_rows(); + if(n_good_rows > 0) + { + n_good_rows = 0; + delete good_rows; + } + + find_good_rows(max_rows); eta_reset(); eta_count = 0; @@ -48,7 +53,6 @@ int CplexHelper::add_single_row_cuts() for (int i = 0; i < n_good_rows; i++) { Row *row = get_tableau_row(good_rows[i]); - //Row *row = good_rows[i]; Generator generator(*row); diff --git a/onerow/library/include/onerow/params.hpp b/onerow/library/include/onerow/params.hpp index 2f1340c..d9bd241 100644 --- a/onerow/library/include/onerow/params.hpp +++ b/onerow/library/include/onerow/params.hpp @@ -25,16 +25,14 @@ const long REDUCE_FACTOR_RHS = 1000000; const long REDUCE_FACTOR_R1 = 1000; const long REDUCE_FACTOR_COEFFICIENT = 1000000; -const int MAX_CUT_DEPTH = 200; +const int MAX_R1_RAYS = 1000000; +const int MAX_CUT_DEPTH = 1000000; +const int MAX_GOOD_ROWS = 1000000; const int ETA_UPDATE_INTERVAL = 300; const unsigned int MAX_CUT_BUFFER_SIZE = 100; -const double COEFFICIENT_SAFETY_MARGIN = 0.00001; - #define INTERSECTION_CUT_USE_DOUBLE - -#define ENABLE_TRIVIAL_LIFTING // #define ENABLE_EXTENDED_STATISTICS // #define PRETEND_TO_ADD_CUTS diff --git a/onerow/library/include/onerow/single_row_cut_generator.hpp b/onerow/library/include/onerow/single_row_cut_generator.hpp index 5c7bec7..22e6292 100644 --- a/onerow/library/include/onerow/single_row_cut_generator.hpp +++ b/onerow/library/include/onerow/single_row_cut_generator.hpp @@ -66,6 +66,8 @@ struct Row { int basic_var_index; bool* is_integer; + double* reduced_costs; + double cost_cutoff; }; /** diff --git a/onerow/library/src/cplex_helper.cpp b/onerow/library/src/cplex_helper.cpp index 6731ab0..426257d 100644 --- a/onerow/library/src/cplex_helper.cpp +++ b/onerow/library/src/cplex_helper.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using std::cout; using std::endl; @@ -39,7 +40,7 @@ static bool debug = false; CplexHelper::CplexHelper(CPXENVptr _env, CPXLPptr _lp) : env(_env), lp(_lp), is_integer(0), n_cuts(0), n_rows(0), ub(0), lb(0), cstat(0), cplex_rows(0), first_solution(0), current_solution(0), - optimal_solution(0), current_round(0), n_good_rows(-1) + optimal_solution(0), current_round(0), n_good_rows(-1), reduced_costs(0) { } @@ -260,6 +261,9 @@ Row* CplexHelper::get_tableau_row(int index) row->is_integer = is_integer; row->c = cplex_row_to_constraint(cplex_rows[index]); + row->reduced_costs = reduced_costs; + row->cost_cutoff = cost_cutoff; + if (optimal_solution) assert(cplex_rows[index].get_violation(optimal_solution) <= 0.001); @@ -272,7 +276,7 @@ Row* CplexHelper::get_tableau_row(int index) return row; } -int CplexHelper::solve(bool should_end_round) +void CplexHelper::solve(bool should_end_round) { // Optimize int status = CPXlpopt(env, lp); @@ -312,8 +316,6 @@ int CplexHelper::solve(bool should_end_round) if (should_end_round) Stats::set_solution(current_round++, objval, string(buffer)); - - return objval; } @@ -364,6 +366,16 @@ void CplexHelper::read_basis() CPXgetlb(env, lp, lb, 0, n_cols - 1); CPXgetbase(env, lp, cstat, rstat); + reduced_costs = new double[n_cols]; + CPXgetdj(env, lp, reduced_costs, 0, n_cols-1); + + cost_cutoff = -INFINITY; + double *costs_copy = new double[n_cols]; + memcpy(costs_copy, reduced_costs, sizeof(double) * n_cols); + std::sort(costs_copy, costs_copy + n_cols, std::greater()); + if(n_cols > MAX_R1_RAYS) cost_cutoff = costs_copy[MAX_R1_RAYS]; + delete costs_copy; + cplex_rows = new CplexRow[n_rows]; assert(cplex_rows != 0); @@ -509,10 +521,10 @@ void CplexHelper::eta_print() FINISHED:; } -void CplexHelper::find_good_rows() +void CplexHelper::find_good_rows(int max_rows) { - n_good_rows = 0; - good_rows = new int[n_rows]; + bool *is_good = new bool[n_rows]; + double *fractionality = new double[n_rows]; time_printf("Finding interesting rows...\n"); @@ -521,14 +533,37 @@ void CplexHelper::find_good_rows() { Row *row = get_tableau_row(i); - if (row->c.pi_zero.frac() != 0 && - row->is_integer[row->basic_var_index]) - { - good_rows[n_good_rows++] = i; - } + fractionality[i] = row->c.pi_zero.frac().get_double(); + fractionality[i] = fabs(fractionality[i] - 0.5); + + is_good[i] = true; + if (row->c.pi_zero.frac() == 0) is_good[i] = false; + if (!row->is_integer[row->basic_var_index]) is_good[i] = false; delete row; } - time_printf(" %d rows found\n", n_good_rows, n_rows); + if(max_rows > 0) + { + double frac_cutoff = 1.0; + std::sort(fractionality, fractionality + n_rows); + if (n_rows > max_rows) frac_cutoff = fractionality[max_rows]; + + for (int i = 0; i < n_rows; i++) + if (fractionality[i] > frac_cutoff) + is_good[i] = false; + } + + n_good_rows = 0; + good_rows = new int[n_rows]; + for (int i = 0; i < n_rows; i++) + { + if (!is_good[i]) continue; + good_rows[n_good_rows++] = i; + if(max_rows > 0 && n_good_rows >= max_rows) break; + } + + delete is_good; + delete fractionality; + time_printf(" %d rows found\n", n_good_rows); } diff --git a/onerow/library/src/wedge_cut_generator.cpp b/onerow/library/src/wedge_cut_generator.cpp index d9f1be9..2f5f59a 100644 --- a/onerow/library/src/wedge_cut_generator.cpp +++ b/onerow/library/src/wedge_cut_generator.cpp @@ -296,6 +296,9 @@ void WedgeCutGenerator::eval_next() } int r1_index = row.c.pi.index(r1_offset); + if(row.reduced_costs[r1_index] < row.cost_cutoff) + continue; + r1[0] = -row.c.pi.value(r1_offset).reduce(REDUCE_FACTOR_R1); r1[1] = 1;