[CP-SAT] tweak to lb_tree_search

This commit is contained in:
Laurent Perron
2021-11-04 18:58:34 +01:00
parent 89dc88bfdd
commit 3b745ab2c1
4 changed files with 64 additions and 52 deletions

View File

@@ -77,16 +77,11 @@ void IntegerSumLE::FillIntegerReason() {
}
std::pair<IntegerValue, IntegerValue> IntegerSumLE::ConditionalLb(
IntegerVariable bool_view, IntegerVariable target_var) const {
if (integer_trail_->LowerBound(bool_view) != 0 &&
integer_trail_->UpperBound(bool_view) != 1) {
return {kMinIntegerValue, kMinIntegerValue};
}
IntegerLiteral integer_literal, IntegerVariable target_var) const {
// Recall that all our coefficient are positive.
bool bool_view_present = false;
bool bool_view_present_positively = false;
IntegerValue view_coeff;
bool literal_var_present = false;
bool literal_var_present_positively = false;
IntegerValue var_coeff;
bool target_var_present_negatively = false;
IntegerValue target_coeff;
@@ -104,21 +99,34 @@ std::pair<IntegerValue, IntegerValue> IntegerSumLE::ConditionalLb(
const IntegerValue lb = integer_trail_->LowerBound(var);
implied_lb += coeff * lb;
if (PositiveVariable(var) == PositiveVariable(bool_view)) {
view_coeff = coeff;
bool_view_present = true;
bool_view_present_positively = (var == bool_view);
if (PositiveVariable(var) == PositiveVariable(integer_literal.var)) {
var_coeff = coeff;
literal_var_present = true;
literal_var_present_positively = (var == integer_literal.var);
}
}
if (!bool_view_present || !target_var_present_negatively) {
if (!literal_var_present || !target_var_present_negatively) {
return {kMinIntegerValue, kMinIntegerValue};
}
if (bool_view_present_positively) {
// A literal means var >= bound.
if (literal_var_present_positively) {
// We have var_coeff * var in the expression, the literal is var >= bound.
// When it is false, it is not relevant as implied_lb used var >= lb.
// When it is true, the diff is bound - lb.
const IntegerValue diff = std::max(
IntegerValue(0), integer_literal.bound -
integer_trail_->LowerBound(integer_literal.var));
return {CeilRatio(implied_lb, target_coeff),
CeilRatio(implied_lb + view_coeff, target_coeff)};
CeilRatio(implied_lb + var_coeff * diff, target_coeff)};
} else {
return {CeilRatio(implied_lb + view_coeff, target_coeff),
// We have var_coeff * -var in the expression, the literal is var >= bound.
// When it is true, it is not relevant as implied_lb used -var >= -ub.
// And when it is false it means var < bound, so -var >= -bound + 1
const IntegerValue diff = std::max(
IntegerValue(0), integer_trail_->UpperBound(integer_literal.var) -
integer_literal.bound + 1);
return {CeilRatio(implied_lb + var_coeff * diff, target_coeff),
CeilRatio(implied_lb, target_coeff)};
}
}

View File

@@ -72,10 +72,11 @@ class IntegerSumLE : public PropagatorInterface {
bool PropagateAtLevelZero();
// This is a pretty usage specific function. Returns the implied lower bound
// on var if the bool_view take the value 0 or 1. If the variables do not
// appear both in the linear inequality, this returns two kMinIntegerValue.
// on target_var if the given integer literal is false (resp. true). If the
// variables do not appear both in the linear inequality, this returns two
// kMinIntegerValue.
std::pair<IntegerValue, IntegerValue> ConditionalLb(
IntegerVariable bool_view, IntegerVariable target_var) const;
IntegerLiteral integer_literal, IntegerVariable target_var) const;
private:
// Fills integer_reason_ with all the current lower_bounds. The real

View File

@@ -416,42 +416,41 @@ SatSolver::Status LbTreeSearch::Search(
// and also allow for a more incremental LP solving since we do less back
// and forth.
//
// TODO(user): The code to recover that is a bit convoluted.
// TODO(user): The code to recover that is a bit convoluted. Alternatively
// Maybe we should do a "fast" propagation without the LP in each branch.
// That will work as long as we keep these optimal LP constraints around
// and propagate them.
//
// TODO(user): Incorporate this in the heuristic so we choose more Boolean
// inside these LP explanations?
if (lp_constraint_ != nullptr) {
IntegerSumLE* last_rc = lp_constraint_->LatestOptimalConstraintOrNull();
if (last_rc != nullptr) {
const IntegerVariable pos_view =
integer_encoder_->GetLiteralView(Literal(decision));
if (pos_view != kNoIntegerVariable) {
const std::pair<IntegerValue, IntegerValue> bounds =
last_rc->ConditionalLb(pos_view, objective_var_);
Node& node = nodes_[n];
if (bounds.first > node.false_objective) {
++num_rc_detected_;
node.UpdateFalseObjective(bounds.first);
}
if (bounds.second > node.true_objective) {
++num_rc_detected_;
node.UpdateTrueObjective(bounds.second);
}
}
// Note that this return literal EQUIVALENT to the decision, not just
// implied by it. We need that for correctness.
int num_tests = 0;
for (const IntegerLiteral integer_literal :
integer_encoder_->GetIntegerLiterals(Literal(decision))) {
if (integer_trail_->IsCurrentlyIgnored(integer_literal.var)) continue;
const IntegerVariable neg_view =
integer_encoder_->GetLiteralView(Literal(decision).Negated());
if (neg_view != kNoIntegerVariable) {
const std::pair<IntegerValue, IntegerValue> bounds =
last_rc->ConditionalLb(neg_view, objective_var_);
Node& node = nodes_[n];
if (bounds.first > node.true_objective) {
++num_rc_detected_;
node.UpdateTrueObjective(bounds.second);
}
if (bounds.second > node.false_objective) {
++num_rc_detected_;
node.UpdateFalseObjective(bounds.second);
}
// To avoid bad corner case. Not sure it ever triggers.
if (++num_tests > 10) break;
// TODO(user): we could consider earlier constraint instead of just
// looking at the last one, but experiments didn't really show a big
// gain.
const auto& cts = lp_constraint_->OptimalConstraints();
if (cts.empty()) continue;
const std::unique_ptr<IntegerSumLE>& rc = cts.back();
const std::pair<IntegerValue, IntegerValue> bounds =
rc->ConditionalLb(integer_literal, objective_var_);
Node& node = nodes_[n];
if (bounds.first > node.false_objective) {
++num_rc_detected_;
node.UpdateFalseObjective(bounds.first);
}
if (bounds.second > node.true_objective) {
++num_rc_detected_;
node.UpdateTrueObjective(bounds.second);
}
}
}

View File

@@ -231,6 +231,10 @@ class LinearProgrammingConstraint : public PropagatorInterface,
return optimal_constraints_.back().get();
}
const std::vector<std::unique_ptr<IntegerSumLE>>& OptimalConstraints() const {
return optimal_constraints_;
}
private:
// Helper methods for branching. Returns true if branching on the given
// variable helps with more propagation or finds a conflict.