Skip to content

Commit ec08fca

Browse files
committed
[CP-SAT] work on probing search; presolve
1 parent b5cb85d commit ec08fca

16 files changed

Lines changed: 325 additions & 174 deletions

ortools/sat/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ cc_library(
394394
":symmetry_util",
395395
":synchronization",
396396
":util",
397+
"//ortools/util:running_stat",
397398
"//ortools/util:time_limit",
398399
"@abseil-cpp//absl/container:flat_hash_set",
399400
"@abseil-cpp//absl/log",
@@ -976,6 +977,7 @@ cc_library(
976977
"@abseil-cpp//absl/algorithm:container",
977978
"@abseil-cpp//absl/container:flat_hash_map",
978979
"@abseil-cpp//absl/container:flat_hash_set",
980+
"@abseil-cpp//absl/log",
979981
"@abseil-cpp//absl/log:check",
980982
"@abseil-cpp//absl/random:bit_gen_ref",
981983
"@abseil-cpp//absl/random:distributions",
@@ -1753,6 +1755,7 @@ cc_test(
17531755
size = "small",
17541756
timeout = "moderate",
17551757
srcs = ["cp_model_expand_test.cc"],
1758+
tags = ["noci"],
17561759
deps = [
17571760
":cp_model_cc_proto",
17581761
":cp_model_checker",

ortools/sat/continuous_prober.cc

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ ContinuousProber::ContinuousProber(const CpModelProto& model_proto,
7272
shared_bounds_manager_(model->Mutable<SharedBoundsManager>()),
7373
shared_stats_(model->Mutable<SharedStatistics>()),
7474
random_(*model->GetOrCreate<ModelRandomGenerator>()),
75-
base_dtime_(parameters_.shaving_deterministic_time_in_probing_search()) {
75+
base_dtime_(parameters_.shaving_deterministic_time_in_probing_search()),
76+
shaving_success_rate_(/*average_window_size=*/100) {
7677
auto* mapping = model_->GetOrCreate<CpModelMapping>();
7778
absl::flat_hash_set<BooleanVariable> visited;
7879

@@ -140,11 +141,6 @@ SatSolver::Status ContinuousProber::Probe() {
140141

141142
while (!time_limit_->LimitReached()) {
142143
const double kMaxProbingTimePerMethod = 0.1;
143-
// Store current statistics to detect shaving without any improvement.
144-
int64_t starting_shaving_literals =
145-
counters_.shaving.num_new_literals_fixed;
146-
int64_t starting_shaving_bounds = counters_.shaving.num_new_bounds;
147-
148144
std::optional<SatSolver::Status> status = ProbeIntegerVariables(
149145
time_limit_->GetElapsedDeterministicTime() + kMaxProbingTimePerMethod);
150146
if (status.has_value()) return status.value();
@@ -181,22 +177,6 @@ SatSolver::Status ContinuousProber::Probe() {
181177

182178
// Alternate between shaving and non-shaving phases.
183179
if (use_shaving_) {
184-
if (counters_.shaving.num_new_literals_fixed >
185-
starting_shaving_literals ||
186-
counters_.shaving.num_new_bounds > starting_shaving_bounds) {
187-
// We improved something. We reduce the multiplier by a factor of 2.
188-
// TODO(user): We could not go below 1.0 and increase the base
189-
// dtime.
190-
limit_multiplier_ = std::max(1.0, limit_multiplier_ / 2);
191-
192-
// Update reference counters.
193-
starting_shaving_literals = counters_.shaving.num_new_literals_fixed;
194-
starting_shaving_bounds = counters_.shaving.num_new_bounds;
195-
} else if (limit_multiplier_ < 16.0) {
196-
limit_multiplier_ *= 1.5; // Cap the maximum limit to 16 * dtime.
197-
}
198-
199-
// Alternating shaving and non-shaving iterations.
200180
use_shaving_ = false;
201181
} else {
202182
use_shaving_ = base_dtime_ > 0.0;
@@ -533,6 +513,25 @@ bool ContinuousProber::IsOrbitRepresentative(int var) const {
533513
return var_to_representative_[var] == var;
534514
}
535515

516+
void ContinuousProber::AdaptShavingMultiplier(bool success) {
517+
if (success) {
518+
shaving_success_rate_.Add(1.0);
519+
} else {
520+
shaving_success_rate_.Add(0.0);
521+
}
522+
// TODO(user): Try to wait until the window is full before adapting the
523+
// multiplier.
524+
if (--update_frequency_ <= 0) {
525+
update_frequency_ = kShavingUpdateFrequency;
526+
const double rate = shaving_success_rate_.WindowAverage();
527+
if (rate > 0.5 && limit_multiplier_ > 0.5) {
528+
limit_multiplier_ = std::max(0.5, limit_multiplier_ * 0.95);
529+
} else if (rate < 0.05 && limit_multiplier_ < 10.0) {
530+
limit_multiplier_ = std::min(20.0, limit_multiplier_ * 1.1);
531+
}
532+
}
533+
}
534+
536535
SatSolver::Status ContinuousProber::ShaveLiteral(
537536
Literal literal, bool literal_is_an_integer_bound) {
538537
if (trail_->Assignment().LiteralIsAssigned(literal)) {
@@ -548,6 +547,8 @@ SatSolver::Status ContinuousProber::ShaveLiteral(
548547
base_dtime_ * limit_multiplier_));
549548
const SatSolver::Status status =
550549
ResetAndSolveIntegerProblem({literal}, model_);
550+
AdaptShavingMultiplier(status == SatSolver::ASSUMPTIONS_UNSAT ||
551+
status == SatSolver::FEASIBLE);
551552
time_limit_->ChangeDeterministicLimit(original_dtime_limit);
552553
if (ReportStatus(status)) return status;
553554

ortools/sat/continuous_prober.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "ortools/sat/sat_parameters.pb.h"
4545
#include "ortools/sat/sat_solver.h"
4646
#include "ortools/sat/synchronization.h"
47+
#include "ortools/util/running_stat.h"
4748
#include "ortools/util/time_limit.h"
4849

4950
namespace operations_research {
@@ -70,6 +71,7 @@ class ContinuousProber {
7071
static const int kTestLimitPeriod = 20;
7172
static const int kLogPeriod = 5000;
7273
static const int kSyncPeriod = 50;
74+
static const int kShavingUpdateFrequency = 20;
7375

7476
struct MethodStats {
7577
explicit MethodStats(absl::string_view name = "") : name(name) {}
@@ -101,6 +103,7 @@ class ContinuousProber {
101103
bool ReportStatus(SatSolver::Status status);
102104
void LogStatistics();
103105
SatSolver::Status PeriodicSyncAndCheck();
106+
void AdaptShavingMultiplier(bool success);
104107

105108
MethodStats GetStats(Prober* prober) const;
106109
static void AddStats(MethodStats& total_stats, const MethodStats& start_stats,
@@ -162,6 +165,8 @@ class ContinuousProber {
162165
bool use_shaving_ = false;
163166
std::vector<std::vector<Literal>> tmp_dnf_;
164167
std::vector<Literal> tmp_literals_;
168+
RunningAverage shaving_success_rate_;
169+
int update_frequency_ = kShavingUpdateFrequency;
165170
};
166171

167172
} // namespace sat

ortools/sat/cp_model_postsolve.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <algorithm>
1717
#include <cstdint>
1818
#include <limits>
19+
#include <string>
1920
#include <vector>
2021

2122
#include "absl/log/check.h"
@@ -98,7 +99,6 @@ void PostsolveExactlyOne(const ConstraintProto& ct,
9899
// There must be one.
99100
void SetEnforcementLiteralToFalse(const ConstraintProto& ct,
100101
std::vector<Domain>* domains) {
101-
CHECK(!ct.enforcement_literal().empty()) << ProtobufShortDebugString(ct);
102102
bool has_free_enforcement_literal = false;
103103
for (const int enf : ct.enforcement_literal()) {
104104
if ((*domains)[PositiveRef(enf)].IsFixed()) continue;
@@ -111,9 +111,13 @@ void SetEnforcementLiteralToFalse(const ConstraintProto& ct,
111111
break;
112112
}
113113
if (!has_free_enforcement_literal) {
114+
std::string domain_info = "\n";
115+
for (const int var : UsedVariables(ct)) {
116+
absl::StrAppend(&domain_info, var, ":", (*domains)[var].ToString(), "\n");
117+
}
114118
LOG(FATAL)
115-
<< "Unsatisfied linear constraint with no free enforcement literal: "
116-
<< ProtobufShortDebugString(ct);
119+
<< "Unsatisfied linear constraint with no free enforcement literal:\n"
120+
<< ProtobufShortDebugString(ct) << domain_info;
117121
}
118122
}
119123

0 commit comments

Comments
 (0)