29 #if !defined(__PORTABLE_PLATFORM__)
30 #include "google/protobuf/descriptor.h"
32 #include "absl/container/btree_set.h"
33 #include "absl/container/flat_hash_map.h"
34 #include "absl/numeric/int128.h"
35 #include "absl/random/bit_gen_ref.h"
36 #include "absl/random/distributions.h"
37 #include "absl/types/span.h"
41 #include "ortools/sat/sat_parameters.pb.h"
49 std::string s = absl::StrCat(num);
51 const int size = s.size();
52 for (
int i = 0; i < size; ++i) {
53 if (i > 0 && (size - i) % 3 == 0) {
63 #if !defined(__PORTABLE_PLATFORM__)
65 const google::protobuf::EnumDescriptor* order_d =
66 SatParameters::VariableOrder_descriptor();
68 static_cast<SatParameters::VariableOrder
>(
69 order_d->value(absl::Uniform(random, 0, order_d->value_count()))
73 const google::protobuf::EnumDescriptor* polarity_d =
74 SatParameters::Polarity_descriptor();
75 parameters->set_initial_polarity(
static_cast<SatParameters::Polarity
>(
76 polarity_d->value(absl::Uniform(random, 0, polarity_d->value_count()))
80 parameters->set_use_phase_saving(absl::Bernoulli(random, 0.5));
81 parameters->set_random_polarity_ratio(absl::Bernoulli(random, 0.5) ? 0.01
83 parameters->set_random_branches_ratio(absl::Bernoulli(random, 0.5) ? 0.01
94 void QuotientAndRemainder(int64_t
a, int64_t
b, int64_t& q, int64_t& r) {
108 int64_t r[2] = {m, x};
109 int64_t t[2] = {0, 1};
122 for (; r[i ^ 1] != 0; i ^= 1) {
123 QuotientAndRemainder(r[i], r[i ^ 1], q, r[i]);
124 t[i] -= t[i ^ 1] * q;
128 if (r[i] != 1)
return 0;
132 if (t[i] < 0) t[i] += m;
138 const int64_t r = x % m;
139 return r < 0 ? r + m : r;
147 if (rhs == 0 || mod == 1)
return 0;
148 DCHECK_EQ(std::gcd(std::abs(coeff), mod), 1);
157 CHECK_NE(inverse, 0);
160 const absl::int128 p = absl::int128{inverse} * absl::int128{rhs};
161 return static_cast<int64_t
>(p % absl::int128{mod});
165 int64_t& x0, int64_t& y0) {
171 const int64_t gcd = std::gcd(std::abs(
a), std::abs(
b));
172 if (cte % gcd != 0)
return false;
188 if (cte < 0 && x0 != 0) x0 -= std::abs(
b);
194 const absl::int128 t = absl::int128{cte} - absl::int128{
a} * absl::int128{x0};
195 DCHECK_EQ(t % absl::int128{
b}, absl::int128{0});
200 const absl::int128 r = t / absl::int128{
b};
204 y0 =
static_cast<int64_t
>(r);
213 static_cast<int64_t
>(std::floor(std::sqrt(
static_cast<double>(
a))));
214 while (
CapProd(result, result) >
a) --result;
215 while (
CapProd(result + 1, result + 1) <=
a) ++result;
222 static_cast<int64_t
>(std::ceil(std::sqrt(
static_cast<double>(
a))));
223 while (
CapProd(result, result) <
a) ++result;
224 while ((result - 1) * (result - 1) >=
a) --result;
230 int64_t result =
value / base * base;
231 if (
value - result > base / 2) result += base;
236 int64_t base,
const std::vector<int64_t>& coeffs,
237 const std::vector<int64_t>& lbs,
const std::vector<int64_t>& ubs,
238 int64_t rhs, int64_t* new_rhs) {
240 int64_t max_activity = 0;
242 int64_t min_error = 0;
243 const int num_terms = coeffs.size();
244 if (num_terms == 0)
return false;
245 for (
int i = 0; i < num_terms; ++i) {
246 const int64_t coeff = coeffs[i];
249 max_activity += coeff * ubs[i];
250 max_x += closest / base * ubs[i];
252 const int64_t error = coeff - closest;
254 min_error += error * lbs[i];
256 min_error += error * ubs[i];
260 if (max_activity <= rhs) {
267 int64_t max_error_if_invalid = 0;
268 const int64_t slack = max_activity - rhs - 1;
269 for (
int i = 0; i < num_terms; ++i) {
270 const int64_t coeff = coeffs[i];
272 const int64_t error = coeff - closest;
274 max_error_if_invalid += error * ubs[i];
276 const int64_t lb =
std::max(lbs[i], ubs[i] - slack / coeff);
277 max_error_if_invalid += error * lb;
292 const int64_t infeasibility_bound =
296 return *new_rhs < infeasibility_bound;
300 const absl::btree_set<LiteralIndex>& processed,
int relevant_prefix_size,
301 std::vector<Literal>* literals) {
302 if (literals->empty())
return -1;
303 if (!processed.contains(literals->back().Index())) {
304 return std::min<int>(relevant_prefix_size, literals->size());
314 int num_processed = 0;
315 int num_not_processed = 0;
316 int target_prefix_size = literals->size() - 1;
317 for (
int i = literals->size() - 1; i >= 0; i--) {
318 if (processed.contains((*literals)[i].Index())) {
322 target_prefix_size = i;
324 if (num_not_processed >= num_processed)
break;
326 if (num_not_processed == 0)
return -1;
327 target_prefix_size =
std::min(target_prefix_size, relevant_prefix_size);
331 std::stable_partition(
332 literals->begin() + target_prefix_size, literals->end(),
333 [&processed](
Literal l) { return processed.contains(l.Index()); });
334 return target_prefix_size;
339 average_ = reset_value;
344 average_ += (new_record - average_) / num_records_;
349 average_ = (num_records_ == 1)
351 : (new_record + decaying_factor_ * (average_ - new_record));
355 records_.push_front(record);
356 if (records_.size() > record_limit_) {
362 CHECK_GT(records_.size(), 0);
363 CHECK_LE(percent, 100.0);
364 CHECK_GE(percent, 0.0);
365 std::vector<double> sorted_records(records_.begin(), records_.end());
366 std::sort(sorted_records.begin(), sorted_records.end());
367 const int num_records = sorted_records.size();
369 const double percentile_rank =
370 static_cast<double>(num_records) * percent / 100.0 - 0.5;
371 if (percentile_rank <= 0) {
372 return sorted_records.front();
373 }
else if (percentile_rank >= num_records - 1) {
374 return sorted_records.back();
377 DCHECK_GE(num_records, 2);
378 DCHECK_LT(percentile_rank, num_records - 1);
379 const int lower_rank =
static_cast<int>(std::floor(percentile_rank));
380 DCHECK_LT(lower_rank, num_records - 1);
381 return sorted_records[lower_rank] +
382 (percentile_rank - lower_rank) *
383 (sorted_records[lower_rank + 1] - sorted_records[lower_rank]);
387 std::vector<std::vector<int64_t>>* tuples) {
388 if (tuples->empty())
return;
393 const int num_vars = (*tuples)[0].size();
395 std::vector<int> to_remove;
396 std::vector<int64_t> tuple_minus_var_i(num_vars - 1);
397 for (
int i = 0; i < num_vars; ++i) {
398 const int domain_size = domain_sizes[i];
399 if (domain_size == 1)
continue;
400 absl::flat_hash_map<const std::vector<int64_t>, std::vector<int>>
401 masked_tuples_to_indices;
402 for (
int t = 0; t < tuples->size(); ++t) {
404 for (
int j = 0; j < num_vars; ++j) {
405 if (i == j)
continue;
406 tuple_minus_var_i[out++] = (*tuples)[t][j];
408 masked_tuples_to_indices[tuple_minus_var_i].push_back(t);
411 for (
const auto& it : masked_tuples_to_indices) {
412 if (it.second.size() != domain_size)
continue;
414 to_remove.insert(to_remove.end(), it.second.begin() + 1, it.second.end());
416 std::sort(to_remove.begin(), to_remove.end(), std::greater<int>());
417 for (
const int t : to_remove) {
418 (*tuples)[t] = tuples->back();
428 expanded_sums_.clear();
434 if (
value == 0)
return;
435 if (
value > bound_)
return;
436 gcd_ = std::gcd(gcd_,
value);
437 AddChoicesInternal({
value});
442 for (
const int64_t c : choices) {
448 if (current_max_ == bound_)
return;
451 filtered_values_.clear();
452 for (
const int64_t c : choices) {
453 if (c == 0 || c > bound_)
continue;
454 filtered_values_.push_back(c);
455 gcd_ = std::gcd(gcd_, c);
457 if (filtered_values_.empty())
return;
460 std::sort(filtered_values_.begin(), filtered_values_.end());
461 AddChoicesInternal(filtered_values_);
466 DCHECK_GE(max_value, 0);
468 if (coeff == 0 || max_value == 0)
return;
469 if (coeff > bound_)
return;
470 if (current_max_ == bound_)
return;
471 gcd_ = std::gcd(gcd_, coeff);
474 if (num_values > 10) {
477 expanded_sums_.clear();
482 filtered_values_.clear();
483 for (
int multiple = 1; multiple <= num_values; ++multiple) {
484 const int64_t v = multiple * coeff;
486 current_max_ = bound_;
489 filtered_values_.push_back(v);
491 AddChoicesInternal(filtered_values_);
494 void MaxBoundedSubsetSum::AddChoicesInternal(absl::Span<const int64_t> values) {
496 if (!sums_.empty() && sums_.size() <= kMaxComplexityPerAdd) {
497 const int old_size = sums_.size();
498 for (
int i = 0; i < old_size; ++i) {
499 for (
const int64_t
value : values) {
500 const int64_t s = sums_[i] +
value;
501 if (s > bound_)
break;
504 current_max_ =
std::max(current_max_, s);
505 if (current_max_ == bound_)
return;
512 if (bound_ <= kMaxComplexityPerAdd) {
513 if (!sums_.empty()) {
514 expanded_sums_.assign(bound_ + 1,
false);
515 for (
const int64_t s : sums_) {
516 expanded_sums_[s] =
true;
522 if (!expanded_sums_.empty()) {
523 for (int64_t i = bound_ - 1; i >= 0; --i) {
524 if (!expanded_sums_[i])
continue;
525 for (
const int64_t
value : values) {
526 if (i +
value > bound_)
break;
528 expanded_sums_[i +
value] =
true;
530 if (current_max_ == bound_)
return;
540 current_max_ = bound_;
547 const std::vector<Domain>& domains,
const std::vector<int64_t>& coeffs,
548 const std::vector<int64_t>& costs,
const Domain& rhs) {
549 const int num_vars = domains.size();
550 if (num_vars == 0)
return {};
552 int64_t min_activity = 0;
553 int64_t max_domain_size = 0;
554 for (
int i = 0; i < num_vars; ++i) {
555 max_domain_size =
std::max(max_domain_size, domains[i].Size());
557 min_activity += coeffs[i] * domains[i].Min();
559 min_activity += coeffs[i] * domains[i].Max();
568 const int64_t num_values = rhs.
Max() - min_activity + 1;
569 if (num_values < 0) {
578 const int64_t max_work_per_variable =
std::min(num_values, max_domain_size);
579 if (rhs.
Max() - min_activity > 1e6)
return {};
580 if (num_vars * num_values * max_work_per_variable > 1e8)
return {};
586 for (
int i = 0; i < num_vars; ++i) {
588 domains_.push_back(domains[i].AdditionWith(
Domain(-domains[i].Min())));
589 coeffs_.push_back(coeffs[i]);
590 costs_.push_back(costs[i]);
593 domains[i].Negation().AdditionWith(
Domain(domains[i].Max())));
594 coeffs_.push_back(-coeffs[i]);
595 costs_.push_back(-costs[i]);
603 for (
int i = 0; i < num_vars; ++i) {
605 result.
solution[i] += domains[i].Min();
615 int64_t num_values,
const Domain& rhs) {
616 const int num_vars = domains_.size();
619 var_activity_states_.assign(num_vars, std::vector<State>(num_values));
622 for (
const int64_t v : domains_[0].Values()) {
623 const int64_t
value = v * coeffs_[0];
625 if (
value >= num_values)
break;
626 var_activity_states_[0][
value].cost = v * costs_[0];
627 var_activity_states_[0][
value].value = v;
631 for (
int i = 1; i < num_vars; ++i) {
632 const std::vector<State>& prev = var_activity_states_[i - 1];
633 std::vector<State>& current = var_activity_states_[i];
634 for (
int prev_value = 0; prev_value < num_values; ++prev_value) {
638 for (
const int64_t v : domains_[i].Values()) {
639 const int64_t
value = prev_value + v * coeffs_[i];
641 if (
value >= num_values)
break;
642 const int64_t new_cost = prev[prev_value].cost + v * costs_[i];
644 current[
value].cost = new_cost;
645 current[
value].value = v;
652 result.solved =
true;
655 int64_t best_activity;
656 for (
int v = 0; v < num_values; ++v) {
659 if (var_activity_states_.back()[v].cost < best_cost) {
660 best_cost = var_activity_states_.back()[v].cost;
666 result.infeasible =
true;
671 result.solution.resize(num_vars);
672 int64_t current_activity = best_activity;
673 for (
int i = num_vars - 1; i >= 0; --i) {
674 const int64_t var_value = var_activity_states_[i][current_activity].value;
675 result.solution[i] = var_value;
676 current_activity -= coeffs_[i] * var_value;
690 void FullyCompressTuplesRecursive(
691 absl::Span<const int64_t> domain_sizes,
692 absl::Span<std::vector<int64_t>> tuples,
693 std::vector<absl::InlinedVector<int64_t, 2>>* reversed_suffix,
694 std::vector<std::vector<absl::InlinedVector<int64_t, 2>>>* output) {
696 absl::InlinedVector<int64_t, 2> values;
699 bool operator<(
const TempData& other)
const {
700 return values < other.values;
703 std::vector<TempData> temp_data;
705 CHECK(!tuples.empty());
706 CHECK(!tuples[0].empty());
707 const int64_t domain_size = domain_sizes[tuples[0].size() - 1];
710 std::sort(tuples.begin(), tuples.end());
711 for (
int i = 0; i < tuples.size();) {
713 temp_data.push_back({{tuples[
start].back()},
start});
714 tuples[
start].pop_back();
715 for (++i; i < tuples.size(); ++i) {
716 const int64_t v = tuples[i].back();
717 tuples[i].pop_back();
718 if (tuples[i] == tuples[
start]) {
719 temp_data.back().values.push_back(v);
721 tuples[i].push_back(v);
728 for (
const int64_t v : temp_data.back().values) {
730 temp_data.back().values.clear();
738 if (temp_data.back().values.size() == domain_size) {
739 temp_data.back().values.clear();
743 if (temp_data.size() == 1) {
744 output->push_back({});
745 for (
const int64_t v : tuples[temp_data[0].
index]) {
747 output->back().push_back({});
749 output->back().push_back({v});
752 output->back().push_back(temp_data[0].values);
753 for (
int i = reversed_suffix->size(); --i >= 0;) {
754 output->back().push_back((*reversed_suffix)[i]);
761 std::sort(temp_data.begin(), temp_data.end());
762 std::vector<std::vector<int64_t>> temp_tuples;
763 for (
int i = 0; i < temp_data.size();) {
764 reversed_suffix->push_back(temp_data[i].values);
767 for (; i < temp_data.size(); i++) {
768 if (temp_data[
start].values != temp_data[i].values)
break;
769 temp_tuples.push_back(tuples[temp_data[i].
index]);
771 FullyCompressTuplesRecursive(domain_sizes, absl::MakeSpan(temp_tuples),
772 reversed_suffix, output);
773 reversed_suffix->pop_back();
784 absl::Span<const int64_t> domain_sizes,
785 std::vector<std::vector<int64_t>>* tuples) {
786 std::vector<absl::InlinedVector<int64_t, 2>> reversed_suffix;
787 std::vector<std::vector<absl::InlinedVector<int64_t, 2>>> output;
788 FullyCompressTuplesRecursive(domain_sizes, absl::MakeSpan(*tuples),
789 &reversed_suffix, &output);
We call domain any subset of Int64 = [kint64min, kint64max].
bool Contains(int64_t value) const
Returns true iff value is in Domain.
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
int64_t Max() const
Returns the max value of the domain.
static IntegralType CeilOfRatio(IntegralType numerator, IntegralType denominator)
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
Result Solve(const std::vector< Domain > &domains, const std::vector< int64_t > &coeffs, const std::vector< int64_t > &costs, const Domain &rhs)
void AddData(double new_record)
void AddData(double new_record)
void Reset(double reset_value)
void AddChoices(absl::Span< const int64_t > choices)
void Reset(int64_t bound)
void AddMultiples(int64_t coeff, int64_t max_value)
double GetPercentile(double percent)
void AddRecord(double record)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
int64_t ClosestMultiple(int64_t value, int64_t base)
void CompressTuples(absl::Span< const int64_t > domain_sizes, std::vector< std::vector< int64_t >> *tuples)
std::vector< std::vector< absl::InlinedVector< int64_t, 2 > > > FullyCompressTuples(absl::Span< const int64_t > domain_sizes, std::vector< std::vector< int64_t >> *tuples)
int64_t PositiveMod(int64_t x, int64_t m)
IntType FloorOfRatio(IntType numerator, IntType denominator)
int64_t CeilSquareRoot(int64_t a)
bool SolveDiophantineEquationOfSizeTwo(int64_t &a, int64_t &b, int64_t &cte, int64_t &x0, int64_t &y0)
std::string FormatCounter(int64_t num)
int64_t FloorSquareRoot(int64_t a)
constexpr int64_t kTableAnyValue
int64_t ModularInverse(int64_t x, int64_t m)
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
bool LinearInequalityCanBeReducedWithClosestMultiple(int64_t base, const std::vector< int64_t > &coeffs, const std::vector< int64_t > &lbs, const std::vector< int64_t > &ubs, int64_t rhs, int64_t *new_rhs)
Collection of objects used to extend the Constraint Solver library.
int64_t CapProd(int64_t x, int64_t y)
std::vector< int64_t > solution