Skip to content

Commit

Permalink
[CP-SAT] new Lns; tweak log
Browse files Browse the repository at this point in the history
  • Loading branch information
lperron committed Jun 26, 2023
1 parent 556eeb7 commit f36a11c
Show file tree
Hide file tree
Showing 16 changed files with 309 additions and 127 deletions.
1 change: 1 addition & 0 deletions ortools/sat/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,7 @@ cc_library(
deps = [
"//ortools/base",
"//ortools/base:threadpool",
"//ortools/util:stats",
"//ortools/util:time_limit",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
Expand Down
37 changes: 24 additions & 13 deletions ortools/sat/cp_model_lns.cc
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ void NeighborhoodGeneratorHelper::RecomputeHelperData() {
// linear constraint somewhere else. This is not the case if we have a fixed
// size optional interval variable. But it should not matter as the
// intervals are replaced by their underlying variables in the scheduling
// constrainst.
// constraints.
if (constraints[ct_index].constraint_case() == ConstraintProto::kInterval) {
continue;
}
Expand Down Expand Up @@ -555,7 +555,7 @@ struct Demand {

// Because of the binary splitting of the capacity in the procedure used to
// extract precedences out of a cumulative constraint, processing bigger
// heigts first will decrease its probability of being split across the 2
// heights first will decrease its probability of being split across the 2
// halves of the current split.
bool operator<(const Demand& other) const {
return std::tie(start, height, end) <
Expand Down Expand Up @@ -1171,6 +1171,10 @@ void NeighborhoodGenerator::Synchronize() {
++num_consecutive_non_improving_calls_;
}

if (data.base_objective > data.new_objective) {
++num_improving_calls_;
}

// TODO(user): Weight more recent data.
// degrade the current average to forget old learnings.
const double gain_per_time_unit =
Expand Down Expand Up @@ -1255,18 +1259,21 @@ Neighborhood RelaxRandomConstraintsGenerator::Generate(
const int num_active_vars =
helper_.ActiveVariablesWhileHoldingLock().size();
const int target_size = std::ceil(difficulty * num_active_vars);
DCHECK_GT(target_size, 0);
if (target_size == num_active_vars) return helper_.FullNeighborhood();
// TODO(user): Clean-up when target_size == 0.

for (const int constraint_index : active_constraints) {
// TODO(user): randomize order of variable addition when close to the
// limit.
for (const int var : helper_.ConstraintToVar()[constraint_index]) {
if (visited_variables_set[var]) continue;
visited_variables_set[var] = true;
if (helper_.IsActive(var)) {
relaxed_variables.push_back(var);
if (relaxed_variables.size() == target_size) break;
if (relaxed_variables.size() >= target_size) break;
}
}
if (relaxed_variables.size() == target_size) break;
if (relaxed_variables.size() >= target_size) break;
}
}

Expand Down Expand Up @@ -1295,13 +1302,17 @@ Neighborhood VariableGraphNeighborhoodGenerator::Generate(
// We read the exact number while locked.
const int num_active_vars =
helper_.ActiveVariablesWhileHoldingLock().size();
const int num_objective_variables =
helper_.ActiveObjectiveVariablesWhileHoldingLock().size();
const int target_size = std::ceil(difficulty * num_active_vars);
if (target_size == 0) return helper_.FullNeighborhood();
if (target_size == num_active_vars) return helper_.FullNeighborhood();

const int first_var =
helper_.ActiveVariablesWhileHoldingLock()[absl::Uniform<int>(
random, 0, num_active_vars)];

num_objective_variables > 0 // Prefer objective variables.
? helper_.ActiveObjectiveVariablesWhileHoldingLock()
[absl::Uniform<int>(random, 0, num_objective_variables)]
: helper_.ActiveVariablesWhileHoldingLock()[absl::Uniform<int>(
random, 0, num_active_vars)];
visited_variables_set[first_var] = true;
visited_variables.push_back(first_var);
relaxed_variables.push_back(first_var);
Expand Down Expand Up @@ -1362,7 +1373,7 @@ Neighborhood ConstraintGraphNeighborhoodGenerator::Generate(
const int num_active_vars =
helper_.ActiveVariablesWhileHoldingLock().size();
const int target_size = std::ceil(difficulty * num_active_vars);
if (target_size == 0) return helper_.FullNeighborhood();
if (target_size == num_active_vars) return helper_.FullNeighborhood();

// Start by a random constraint.
const int num_active_constraints = helper_.ConstraintToVar().size();
Expand Down Expand Up @@ -1393,7 +1404,7 @@ Neighborhood ConstraintGraphNeighborhoodGenerator::Generate(
if (helper_.IsActive(var)) {
relaxed_variables.push_back(var);
}
if (relaxed_variables.size() == target_size) break;
if (relaxed_variables.size() >= target_size) break;

for (const int ct : helper_.VarToConstraint()[var]) {
if (added_constraints[ct]) continue;
Expand Down Expand Up @@ -1425,7 +1436,7 @@ Neighborhood DecompositionGraphNeighborhoodGenerator::Generate(
const int num_active_vars =
helper_.ActiveVariablesWhileHoldingLock().size();
const int target_size = std::ceil(difficulty * num_active_vars);
if (target_size == 0) return helper_.FullNeighborhood();
if (target_size == num_active_vars) return helper_.FullNeighborhood();

const int num_vars = helper_.VarToConstraint().size();
const int num_constraints = helper_.ConstraintToVar().size();
Expand Down Expand Up @@ -1917,7 +1928,7 @@ Neighborhood RoutingFullPathNeighborhoodGenerator::Generate(
}

bool RelaxationInducedNeighborhoodGenerator::ReadyToGenerate() const {
return (incomplete_solutions_->HasNewSolution() ||
return (incomplete_solutions_->HasSolution() ||
lp_solutions_->NumSolutions() > 0);
}

Expand Down
22 changes: 19 additions & 3 deletions ortools/sat/cp_model_lns.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ class NeighborhoodGeneratorHelper : public SubSolver {
return active_variables_;
}

// Returns the vector of active objective variables. The graph_mutex_ must be
// locked before calling this method.
std::vector<int> ActiveObjectiveVariablesWhileHoldingLock() const
ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
std::vector<int> result;
result = active_objective_variables_;
return result;
}

// Constraints <-> Variables graph.
// Note that only non-constant variable are listed here.
const std::vector<std::vector<int>>& ConstraintToVar() const
Expand Down Expand Up @@ -203,8 +212,8 @@ class NeighborhoodGeneratorHelper : public SubSolver {
// cumulative, or as a dimension of a no_overlap_2d constraint.
std::vector<std::vector<int>> GetUniqueIntervalSets() const;

// Returns one sub-vector per circuit or per single vehicle ciruit in a routes
// constraints. Each circuit is non empty, and does not contain any
// Returns one sub-vector per circuit or per single vehicle circuit in a
// routes constraints. Each circuit is non empty, and does not contain any
// self-looping arcs. Path are sorted, starting from the arc with the lowest
// tail index, and going in sequence up to the last arc before the circuit is
// closed. Each entry correspond to the arc literal on the circuit.
Expand Down Expand Up @@ -357,7 +366,7 @@ class NeighborhoodGenerator {
// The difficulty when this neighborhood was generated.
double difficulty = 0.0;

// The determinitic time limit given to the solver for this neighborhood.
// The deterministic time limit given to the solver for this neighborhood.
double deterministic_limit = 0.0;

// The time it took to solve this neighborhood.
Expand Down Expand Up @@ -410,6 +419,12 @@ class NeighborhoodGenerator {
return num_fully_solved_calls_;
}

// Out of num_calls(), how many improved the given solution.
int64_t num_improving_calls() const {
absl::MutexLock mutex_lock(&generator_mutex_);
return num_improving_calls_;
}

// The current difficulty of this generator
double difficulty() const {
absl::MutexLock mutex_lock(&generator_mutex_);
Expand Down Expand Up @@ -445,6 +460,7 @@ class NeighborhoodGenerator {
// Only updated on Synchronize().
int64_t num_calls_ = 0;
int64_t num_fully_solved_calls_ = 0;
int64_t num_improving_calls_ = 0;
int64_t num_consecutive_non_improving_calls_ = 0;
double deterministic_time_ = 0.0;
double current_average_ = 0.0;
Expand Down
15 changes: 0 additions & 15 deletions ortools/sat/cp_model_presolve.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/mathutil.h"
#include "ortools/base/stl_util.h"
Expand Down Expand Up @@ -709,20 +708,6 @@ bool CpModelPresolver::PresolveLinMax(ConstraintProto* ct) {
if (context_->ModelIsUnsat()) return false;
if (HasEnforcementLiteral(*ct)) return false;

int64_t min_offset = std::numeric_limits<int64_t>::max();
for (const LinearExpressionProto& expr : ct->lin_max().exprs()) {
min_offset = std::min(min_offset, expr.offset());
}
if (min_offset != std::numeric_limits<int64_t>::max() && min_offset != 0) {
LinearArgumentProto* lin_max = ct->mutable_lin_max();
lin_max->mutable_target()->set_offset(lin_max->target().offset() -
min_offset);
for (LinearExpressionProto& expr : *(lin_max->mutable_exprs())) {
expr.set_offset(expr.offset() - min_offset);
}
context_->UpdateRuleStats("lin_max: shift offset");
}

const LinearExpressionProto& target = ct->lin_max().target();

// x = max(x, xi...) => forall i, x >= xi.
Expand Down
4 changes: 2 additions & 2 deletions ortools/sat/cp_model_search.cc
Original file line number Diff line number Diff line change
Expand Up @@ -818,15 +818,15 @@ std::vector<SatParameters> GetFirstSolutionParams(
new_params.set_randomize_search(true);
new_params.set_search_randomization_tolerance(num_random + 1);
new_params.set_random_seed(ValidSumSeed(base_seed, 2 * num_random + 1));
new_params.set_name(absl::StrCat("random_", num_random));
new_params.set_name("random");
num_random++;
} else { // Random quick restart.
new_params.set_search_branching(
SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH);
new_params.set_randomize_search(true);
new_params.set_search_randomization_tolerance(num_random_qr + 1);
new_params.set_random_seed(ValidSumSeed(base_seed, 2 * num_random_qr));
new_params.set_name(absl::StrCat("random_quick_restart_", num_random_qr));
new_params.set_name("random_quick_restart");
num_random_qr++;
}
result.push_back(new_params);
Expand Down
Loading

0 comments on commit f36a11c

Please sign in to comment.