From bd6c747ef9eb68af0ee0dcb0aa23b000929f2cbb Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Fri, 21 Jun 2019 15:12:14 +0200 Subject: [PATCH] cleanup cuts code --- ortools/sat/cp_model_solver.cc | 10 +++--- ortools/sat/cuts.cc | 56 ++++++++++++++++++---------------- ortools/sat/cuts.h | 4 +-- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index 7ca1e50193..899a5822c7 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -24,12 +24,6 @@ #include #include -#include "absl/memory/memory.h" -#include "glog/vlog_is_on.h" -#include "ortools/sat/cuts.h" -#include "ortools/sat/sat_parameters.pb.h" -#include "ortools/util/saturated_arithmetic.h" - #if !defined(__PORTABLE_PLATFORM__) #include "absl/synchronization/notification.h" #include "google/protobuf/text_format.h" @@ -37,10 +31,12 @@ #endif // __PORTABLE_PLATFORM__ #include "absl/container/flat_hash_set.h" +#include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "absl/synchronization/mutex.h" +#include "glog/vlog_is_on.h" #include "ortools/base/cleanup.h" #include "ortools/base/commandlineflags.h" #include "ortools/base/int_type.h" @@ -63,6 +59,7 @@ #include "ortools/sat/cp_model_presolve.h" #include "ortools/sat/cp_model_search.h" #include "ortools/sat/cp_model_utils.h" +#include "ortools/sat/cuts.h" #include "ortools/sat/drat_checker.h" #include "ortools/sat/drat_proof_handler.h" #include "ortools/sat/integer.h" @@ -76,6 +73,7 @@ #include "ortools/sat/probing.h" #include "ortools/sat/rins.h" #include "ortools/sat/sat_base.h" +#include "ortools/sat/sat_parameters.pb.h" #include "ortools/sat/sat_solver.h" #include "ortools/sat/simplification.h" #include "ortools/sat/synchronization.h" diff --git a/ortools/sat/cuts.cc b/ortools/sat/cuts.cc index b1f6933eb3..6b1cbd7daa 100644 --- a/ortools/sat/cuts.cc +++ b/ortools/sat/cuts.cc @@ -809,23 +809,21 @@ CutGenerator CreatePositiveMultiplicationCutGenerator(IntegerVariable z, return; } - const double x_value = lp_values[x]; - const double y_value = lp_values[y]; - const double z_value = lp_values[z]; + const double x_lp_value = lp_values[x]; + const double y_lp_value = lp_values[y]; + const double z_lp_value = lp_values[z]; - // TODO: As the bounds change monotonically, these cuts dominate any - // previous one. try to keep a reference to the cut and replace it. - // Alternatively, add an API for a level-zero bound change callback. - - // We implement the McCormick relaxation of bilinear constraints. + // TODO(user): As the bounds change monotonically, these cuts + // dominate any previous one. try to keep a reference to the cut and + // replace it. Alternatively, add an API for a level-zero bound change + // callback. // Cut -z + x_coeff * x + y_coeff* y <= rhs - auto try_add_above_cut = [manager, z_value, x_value, y_value, x, y, z, - lp_values](int64 x_coeff, int64 y_coeff, - int64 rhs) { - if (-z_value + x_value * x_coeff + y_value * y_coeff >= + auto try_add_above_cut = [manager, z_lp_value, x_lp_value, y_lp_value, + x, y, z, lp_values]( + int64 x_coeff, int64 y_coeff, int64 rhs) { + if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff >= rhs + kMinCutViolation) { - // cut: -z + x * x_coeff + y * y_coeff <= rhs LinearConstraint cut; cut.vars.push_back(z); cut.coeffs.push_back(IntegerValue(-1)); @@ -844,12 +842,11 @@ CutGenerator CreatePositiveMultiplicationCutGenerator(IntegerVariable z, }; // Cut -z + x_coeff * x + y_coeff* y >= rhs - auto try_add_below_cut = [manager, z_value, x_value, y_value, x, y, z, - lp_values](int64 x_coeff, int64 y_coeff, - int64 rhs) { - if (-z_value + x_value * x_coeff + y_value * y_coeff <= + auto try_add_below_cut = [manager, z_lp_value, x_lp_value, y_lp_value, + x, y, z, lp_values]( + int64 x_coeff, int64 y_coeff, int64 rhs) { + if (-z_lp_value + x_lp_value * x_coeff + y_lp_value * y_coeff <= rhs - kMinCutViolation) { - // cut: -z + x * x_coeff + y * y_coeff >= rhs LinearConstraint cut; cut.vars.push_back(z); cut.coeffs.push_back(IntegerValue(-1)); @@ -867,10 +864,15 @@ CutGenerator CreatePositiveMultiplicationCutGenerator(IntegerVariable z, } }; - // McCormick cuts. + // McCormick relaxation of bilinear constraints. These 4 cuts are the + // exact facets of the x * y polyhedron for a bounded x and y. + // + // Each cut correspond to plane that contains two of the line + // (x=x_lb), (x=x_ub), (y=y_lb), (y=y_ub). The easiest to + // understand them is to draw the x*y curves and see the 4 + // planes that correspond to the convex hull of the graph. try_add_above_cut(y_lb, x_lb, x_lb * y_lb); try_add_above_cut(y_ub, x_ub, x_ub * y_ub); - try_add_below_cut(y_ub, x_lb, x_lb * y_ub); try_add_below_cut(y_lb, x_ub, x_ub * y_lb); }; @@ -885,7 +887,7 @@ CutGenerator CreateSquareCutGenerator(IntegerVariable y, IntegerVariable x, IntegerTrail* integer_trail = model->GetOrCreate(); result.generate_cuts = - [y, x, model, integer_trail]( + [y, x, integer_trail]( const gtl::ITIVector& lp_values, LinearConstraintManager* manager) { const int64 x_ub = integer_trail->LevelZeroUpperBound(x).value(); @@ -894,14 +896,14 @@ CutGenerator CreateSquareCutGenerator(IntegerVariable y, IntegerVariable x, if (x_lb == x_ub) return; // Check for potential overflows. - if (x_ub > int64{1000000000}) return; + if (x_ub > (int64{1} << 31)) return; DCHECK_GE(x_lb, 0); const double y_value = lp_values[y]; const double x_value = lp_values[x]; // First cut: target should be below the line: - // (x_lb, val_lb ^ 2) to (x_ub, x_ub ^ 2). + // (x_lb, x_lb ^ 2) to (x_ub, x_ub ^ 2). // The slope of that line is (ub^2 - lb^2) / (ub - lb) = ub + lb. const int64 y_lb = x_lb * x_lb; const int64 above_slope = x_ub + x_lb; @@ -921,14 +923,16 @@ CutGenerator CreateSquareCutGenerator(IntegerVariable y, IntegerVariable x, // Second cut: target should be above all the lines // (value, value ^ 2) to (value + 1, (value + 1) ^ 2) // The slope of that line is 2 * value + 1 + // + // Note that we only add one of these cuts. The one for x_lp_value in + // [value, value + 1]. const int64 x_floor = static_cast(std::floor(x_value)); const int64 below_slope = 2 * x_floor + 1; const double min_y = below_slope * x_value - x_floor - x_floor * x_floor; if (min_y >= y_value + kMinCutViolation) { - // cut: target >= below_slope * (x - x_floor) + - // x_floor * x_floor - // : target >= below_slope * x - x_floor ^ 2 - x_floor + // cut: y >= below_slope * (x - x_floor) + x_floor ^ 2 + // : y >= below_slope * x - x_floor ^ 2 - x_floor LinearConstraint below_cut; below_cut.vars.push_back(y); below_cut.coeffs.push_back(IntegerValue(1)); diff --git a/ortools/sat/cuts.h b/ortools/sat/cuts.h index 361a17a5c9..2d2af1c136 100644 --- a/ortools/sat/cuts.h +++ b/ortools/sat/cuts.h @@ -255,13 +255,13 @@ CutGenerator CreateKnapsackCoverCutGenerator( const std::vector& base_constraints, const std::vector& vars, Model* model); -// A cut generator for z = x * y (x and y >= 0) +// A cut generator for z = x * y (x and y >= 0). CutGenerator CreatePositiveMultiplicationCutGenerator(IntegerVariable z, IntegerVariable x, IntegerVariable y, Model* model); -// A cut generator for y = x ^ 2. (x >= 0). +// A cut generator for y = x ^ 2 (x >= 0). // It will dynamically add a linear inequality to push y closer to the parabola. CutGenerator CreateSquareCutGenerator(IntegerVariable y, IntegerVariable x, Model* model);