OR-Tools  9.6
sat/util.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #include "ortools/sat/util.h"
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <cstdint>
19 #include <cstdlib>
20 #include <deque>
21 #include <limits>
22 #include <numeric>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
28 #include "ortools/base/logging.h"
29 #if !defined(__PORTABLE_PLATFORM__)
30 #include "google/protobuf/descriptor.h"
31 #endif // __PORTABLE_PLATFORM__
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"
38 #include "ortools/base/mathutil.h"
39 #include "ortools/base/stl_util.h"
40 #include "ortools/sat/sat_base.h"
41 #include "ortools/sat/sat_parameters.pb.h"
44 
45 namespace operations_research {
46 namespace sat {
47 
48 std::string FormatCounter(int64_t num) {
49  std::string s = absl::StrCat(num);
50  std::string out;
51  const int size = s.size();
52  for (int i = 0; i < size; ++i) {
53  if (i > 0 && (size - i) % 3 == 0) {
54  out.push_back('\'');
55  }
56  out.push_back(s[i]);
57  }
58  return out;
59 }
60 
61 void RandomizeDecisionHeuristic(absl::BitGenRef random,
62  SatParameters* parameters) {
63 #if !defined(__PORTABLE_PLATFORM__)
64  // Random preferred variable order.
65  const google::protobuf::EnumDescriptor* order_d =
66  SatParameters::VariableOrder_descriptor();
67  parameters->set_preferred_variable_order(
68  static_cast<SatParameters::VariableOrder>(
69  order_d->value(absl::Uniform(random, 0, order_d->value_count()))
70  ->number()));
71 
72  // Random polarity initial value.
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()))
77  ->number()));
78 #endif // __PORTABLE_PLATFORM__
79  // Other random parameters.
80  parameters->set_use_phase_saving(absl::Bernoulli(random, 0.5));
81  parameters->set_random_polarity_ratio(absl::Bernoulli(random, 0.5) ? 0.01
82  : 0.0);
83  parameters->set_random_branches_ratio(absl::Bernoulli(random, 0.5) ? 0.01
84  : 0.0);
85 }
86 
87 namespace {
88 
89 // This will be optimized into one division. I tested that in other places:
90 // 3/ortools/sat/integer_test.cc;l=1223-1228;bpv=0
91 //
92 // Note that I am not 100% sure we need the indirection for the optimization
93 // to kick in though, but this seemed safer given our weird r[i ^ 1] inputs.
94 void QuotientAndRemainder(int64_t a, int64_t b, int64_t& q, int64_t& r) {
95  q = a / b;
96  r = a % b;
97 }
98 
99 } // namespace
100 
101 // Using the extended Euclidian algo, we find a and b such that
102 // a x + b m = gcd(x, m)
103 // https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
104 int64_t ModularInverse(int64_t x, int64_t m) {
105  DCHECK_GE(x, 0);
106  DCHECK_LT(x, m);
107 
108  int64_t r[2] = {m, x};
109  int64_t t[2] = {0, 1};
110  int64_t q;
111 
112  // We only keep the last two terms of the sequences with the "^1" trick:
113  //
114  // q = r[i-2] / r[i-1]
115  // r[i] = r[i-2] % r[i-1]
116  // t[i] = t[i-2] - t[i-1] * q
117  //
118  // We always have:
119  // - gcd(r[i], r[i - 1]) = gcd(r[i - 1], r[i - 2])
120  // - x * t[i] + m * t[i - 1] = r[i]
121  int i = 0;
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;
125  }
126 
127  // If the gcd is not one, there is no inverse, we returns 0.
128  if (r[i] != 1) return 0;
129 
130  // Correct the result so that it is in [0, m). Note that abs(t[i]) is known to
131  // be less than or equal to x / 2, and we have thorough unit-tests.
132  if (t[i] < 0) t[i] += m;
133 
134  return t[i];
135 }
136 
137 int64_t PositiveMod(int64_t x, int64_t m) {
138  const int64_t r = x % m;
139  return r < 0 ? r + m : r;
140 }
141 
142 int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs) {
143  DCHECK_NE(coeff, 0);
144  DCHECK_NE(mod, 0);
145 
146  mod = std::abs(mod);
147  if (rhs == 0 || mod == 1) return 0;
148  DCHECK_EQ(std::gcd(std::abs(coeff), mod), 1);
149 
150  // Make both in [0, mod).
151  coeff = PositiveMod(coeff, mod);
152  rhs = PositiveMod(rhs, mod);
153 
154  // From X * coeff % mod = rhs
155  // We deduce that X % mod = rhs * inverse % mod
156  const int64_t inverse = ModularInverse(coeff, mod);
157  CHECK_NE(inverse, 0);
158 
159  // We make the operation in 128 bits to be sure not to have any overflow here.
160  const absl::int128 p = absl::int128{inverse} * absl::int128{rhs};
161  return static_cast<int64_t>(p % absl::int128{mod});
162 }
163 
164 bool SolveDiophantineEquationOfSizeTwo(int64_t& a, int64_t& b, int64_t& cte,
165  int64_t& x0, int64_t& y0) {
166  CHECK_NE(a, 0);
167  CHECK_NE(b, 0);
170 
171  const int64_t gcd = std::gcd(std::abs(a), std::abs(b));
172  if (cte % gcd != 0) return false;
173  a /= gcd;
174  b /= gcd;
175  cte /= gcd;
176 
177  // The simple case where (0, 0) is a solution.
178  if (cte == 0) {
179  x0 = y0 = 0;
180  return true;
181  }
182 
183  // We solve a * X + b * Y = cte
184  // We take a valid x0 in [0, b) by considering the equation mod b.
185  x0 = ProductWithModularInverse(a, b, cte);
186 
187  // We choose x0 of the same sign as cte.
188  if (cte < 0 && x0 != 0) x0 -= std::abs(b);
189 
190  // By plugging X = x0 + b * Z
191  // We have a * (x0 + b * Z) + b * Y = cte
192  // so a * b * Z + b * Y = cte - a * x0;
193  // and y0 = (cte - a * x0) / b (with an exact division by construction).
194  const absl::int128 t = absl::int128{cte} - absl::int128{a} * absl::int128{x0};
195  DCHECK_EQ(t % absl::int128{b}, absl::int128{0});
196 
197  // Overflow-wise, there is two cases for cte > 0:
198  // - a * x0 <= cte, in this case y0 will not overflow (<= cte).
199  // - a * x0 > cte, in this case y0 will be in (-a, 0].
200  const absl::int128 r = t / absl::int128{b};
201  DCHECK_LE(r, absl::int128{std::numeric_limits<int64_t>::max()});
202  DCHECK_GE(r, absl::int128{std::numeric_limits<int64_t>::min()});
203 
204  y0 = static_cast<int64_t>(r);
205  return true;
206 }
207 
208 // TODO(user): Find better implementation? In pratice passing via double is
209 // almost always correct, but the CapProd() might be a bit slow. However this
210 // is only called when we do propagate something.
211 int64_t FloorSquareRoot(int64_t a) {
212  int64_t result =
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;
216  return result;
217 }
218 
219 // TODO(user): Find better implementation?
220 int64_t CeilSquareRoot(int64_t a) {
221  int64_t 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;
225  return result;
226 }
227 
228 int64_t ClosestMultiple(int64_t value, int64_t base) {
229  if (value < 0) return -ClosestMultiple(-value, base);
230  int64_t result = value / base * base;
231  if (value - result > base / 2) result += base;
232  return result;
233 }
234 
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) {
239  // Precompute some bounds for the equation base * X + error <= rhs.
240  int64_t max_activity = 0;
241  int64_t max_x = 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];
247  CHECK_GT(coeff, 0);
248  const int64_t closest = ClosestMultiple(coeff, base);
249  max_activity += coeff * ubs[i];
250  max_x += closest / base * ubs[i];
251 
252  const int64_t error = coeff - closest;
253  if (error >= 0) {
254  min_error += error * lbs[i];
255  } else {
256  min_error += error * ubs[i];
257  }
258  }
259 
260  if (max_activity <= rhs) {
261  // The constraint is trivially true.
262  *new_rhs = max_x;
263  return true;
264  }
265 
266  // This is the max error assuming that 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];
271  const int64_t closest = ClosestMultiple(coeff, base);
272  const int64_t error = coeff - closest;
273  if (error >= 0) {
274  max_error_if_invalid += error * ubs[i];
275  } else {
276  const int64_t lb = std::max(lbs[i], ubs[i] - slack / coeff);
277  max_error_if_invalid += error * lb;
278  }
279  }
280 
281  // We have old solution valid =>
282  // base * X + error <= rhs
283  // base * X <= rhs - error
284  // base * X <= rhs - min_error
285  // X <= new_rhs
286  *new_rhs = std::min(max_x, MathUtil::FloorOfRatio(rhs - min_error, base));
287 
288  // And we have old solution invalid =>
289  // base * X + error >= rhs + 1
290  // base * X >= rhs + 1 - max_error_if_invalid
291  // X >= infeasibility_bound
292  const int64_t infeasibility_bound =
293  MathUtil::CeilOfRatio(rhs + 1 - max_error_if_invalid, base);
294 
295  // If the two bounds can be separated, we have an equivalence !
296  return *new_rhs < infeasibility_bound;
297 }
298 
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());
305  }
306 
307  // To get O(n log n) size of suffixes, we will first process the last n/2
308  // literals, we then move all of them first and process the n/2 literals left.
309  // We use the same algorithm recursively. The sum of the suffixes' size S(n)
310  // is thus S(n/2) + n + S(n/2). That gives us the correct complexity. The code
311  // below simulates one step of this algorithm and is made to be "robust" when
312  // from one call to the next, some literals have been removed (but the order
313  // of literals is preserved).
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())) {
319  ++num_processed;
320  } else {
321  ++num_not_processed;
322  target_prefix_size = i;
323  }
324  if (num_not_processed >= num_processed) break;
325  }
326  if (num_not_processed == 0) return -1;
327  target_prefix_size = std::min(target_prefix_size, relevant_prefix_size);
328 
329  // Once a prefix size has been decided, it is always better to
330  // enqueue the literal already processed first.
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;
335 }
336 
337 void IncrementalAverage::Reset(double reset_value) {
338  num_records_ = 0;
339  average_ = reset_value;
340 }
341 
342 void IncrementalAverage::AddData(double new_record) {
343  num_records_++;
344  average_ += (new_record - average_) / num_records_;
345 }
346 
347 void ExponentialMovingAverage::AddData(double new_record) {
348  num_records_++;
349  average_ = (num_records_ == 1)
350  ? new_record
351  : (new_record + decaying_factor_ * (average_ - new_record));
352 }
353 
354 void Percentile::AddRecord(double record) {
355  records_.push_front(record);
356  if (records_.size() > record_limit_) {
357  records_.pop_back();
358  }
359 }
360 
361 double Percentile::GetPercentile(double percent) {
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();
368 
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();
375  }
376  // Interpolate.
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]);
384 }
385 
386 void CompressTuples(absl::Span<const int64_t> domain_sizes,
387  std::vector<std::vector<int64_t>>* tuples) {
388  if (tuples->empty()) return;
389 
390  // Remove duplicates if any.
392 
393  const int num_vars = (*tuples)[0].size();
394 
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) {
403  int out = 0;
404  for (int j = 0; j < num_vars; ++j) {
405  if (i == j) continue;
406  tuple_minus_var_i[out++] = (*tuples)[t][j];
407  }
408  masked_tuples_to_indices[tuple_minus_var_i].push_back(t);
409  }
410  to_remove.clear();
411  for (const auto& it : masked_tuples_to_indices) {
412  if (it.second.size() != domain_size) continue;
413  (*tuples)[it.second.front()][i] = kTableAnyValue;
414  to_remove.insert(to_remove.end(), it.second.begin() + 1, it.second.end());
415  }
416  std::sort(to_remove.begin(), to_remove.end(), std::greater<int>());
417  for (const int t : to_remove) {
418  (*tuples)[t] = tuples->back();
419  tuples->pop_back();
420  }
421  }
422 }
423 
425  DCHECK_GE(bound, 0);
426  gcd_ = 0;
427  sums_ = {0};
428  expanded_sums_.clear();
429  current_max_ = 0;
430  bound_ = bound;
431 }
432 
434  if (value == 0) return;
435  if (value > bound_) return;
436  gcd_ = std::gcd(gcd_, value);
437  AddChoicesInternal({value});
438 }
439 
440 void MaxBoundedSubsetSum::AddChoices(absl::Span<const int64_t> choices) {
441  if (DEBUG_MODE) {
442  for (const int64_t c : choices) {
443  DCHECK_GE(c, 0);
444  }
445  }
446 
447  // The max is already reachable or we aborted.
448  if (current_max_ == bound_) return;
449 
450  // Filter out zero and values greater than bound_.
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);
456  }
457  if (filtered_values_.empty()) return;
458 
459  // So we can abort early in the AddChoicesInternal() inner loops.
460  std::sort(filtered_values_.begin(), filtered_values_.end());
461  AddChoicesInternal(filtered_values_);
462 }
463 
464 void MaxBoundedSubsetSum::AddMultiples(int64_t coeff, int64_t max_value) {
465  DCHECK_GE(coeff, 0);
466  DCHECK_GE(max_value, 0);
467 
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);
472 
473  const int64_t num_values = std::min(max_value, FloorOfRatio(bound_, coeff));
474  if (num_values > 10) {
475  // We only keep GCD in this case.
476  sums_.clear();
477  expanded_sums_.clear();
478  current_max_ = FloorOfRatio(bound_, gcd_) * gcd_;
479  return;
480  }
481 
482  filtered_values_.clear();
483  for (int multiple = 1; multiple <= num_values; ++multiple) {
484  const int64_t v = multiple * coeff;
485  if (v == bound_) {
486  current_max_ = bound_;
487  return;
488  }
489  filtered_values_.push_back(v);
490  }
491  AddChoicesInternal(filtered_values_);
492 }
493 
494 void MaxBoundedSubsetSum::AddChoicesInternal(absl::Span<const int64_t> values) {
495  // Mode 1: vector of all possible sums (with duplicates).
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;
502 
503  sums_.push_back(s);
504  current_max_ = std::max(current_max_, s);
505  if (current_max_ == bound_) return; // Abort
506  }
507  }
508  return;
509  }
510 
511  // Mode 2: bitset of all possible sums.
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;
517  }
518  sums_.clear();
519  }
520 
521  // The reverse order is important to not add the current value twice.
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;
527 
528  expanded_sums_[i + value] = true;
529  current_max_ = std::max(current_max_, i + value);
530  if (current_max_ == bound_) return; // Abort
531  }
532  }
533  return;
534  }
535  }
536 
537  // Fall back to gcd_.
538  DCHECK_NE(gcd_, 0);
539  if (gcd_ == 1) {
540  current_max_ = bound_;
541  } else {
542  current_max_ = FloorOfRatio(bound_, gcd_) * gcd_;
543  }
544 }
545 
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 {};
551 
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());
556  if (coeffs[i] > 0) {
557  min_activity += coeffs[i] * domains[i].Min();
558  } else {
559  min_activity += coeffs[i] * domains[i].Max();
560  }
561  }
562 
563  // The complexity of our DP will depends on the number of "activity" values
564  // that need to be considered.
565  //
566  // TODO(user): We can also solve efficiently if max_activity - rhs.Min() is
567  // small. Implement.
568  const int64_t num_values = rhs.Max() - min_activity + 1;
569  if (num_values < 0) {
570  // Problem is clearly infeasible, we can report the result right away.
571  Result result;
572  result.solved = true;
573  result.infeasible = true;
574  return result;
575  }
576 
577  // Abort if complexity too large.
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 {};
581 
582  // Canonicalize to positive coeffs and non-negative variables.
583  domains_.clear();
584  coeffs_.clear();
585  costs_.clear();
586  for (int i = 0; i < num_vars; ++i) {
587  if (coeffs[i] > 0) {
588  domains_.push_back(domains[i].AdditionWith(Domain(-domains[i].Min())));
589  coeffs_.push_back(coeffs[i]);
590  costs_.push_back(costs[i]);
591  } else {
592  domains_.push_back(
593  domains[i].Negation().AdditionWith(Domain(domains[i].Max())));
594  coeffs_.push_back(-coeffs[i]);
595  costs_.push_back(-costs[i]);
596  }
597  }
598 
599  Result result =
600  InternalSolve(num_values, rhs.AdditionWith(Domain(-min_activity)));
601  if (result.solved && !result.infeasible) {
602  // Transform solution back.
603  for (int i = 0; i < num_vars; ++i) {
604  if (coeffs[i] > 0) {
605  result.solution[i] += domains[i].Min();
606  } else {
607  result.solution[i] = domains[i].Max() - result.solution[i];
608  }
609  }
610  }
611  return result;
612 }
613 
614 BasicKnapsackSolver::Result BasicKnapsackSolver::InternalSolve(
615  int64_t num_values, const Domain& rhs) {
616  const int num_vars = domains_.size();
617 
618  // The set of DP states that we will fill.
619  var_activity_states_.assign(num_vars, std::vector<State>(num_values));
620 
621  // Initialize with first variable.
622  for (const int64_t v : domains_[0].Values()) {
623  const int64_t value = v * coeffs_[0];
624  CHECK_GE(value, 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;
628  }
629 
630  // Fill rest of the DP states.
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) {
635  if (prev[prev_value].cost == std::numeric_limits<int64_t>::max()) {
636  continue;
637  }
638  for (const int64_t v : domains_[i].Values()) {
639  const int64_t value = prev_value + v * coeffs_[i];
640  CHECK_GE(value, 0);
641  if (value >= num_values) break;
642  const int64_t new_cost = prev[prev_value].cost + v * costs_[i];
643  if (new_cost < current[value].cost) {
644  current[value].cost = new_cost;
645  current[value].value = v;
646  }
647  }
648  }
649  }
650 
651  Result result;
652  result.solved = true;
653 
654  int64_t best_cost = std::numeric_limits<int64_t>::max();
655  int64_t best_activity;
656  for (int v = 0; v < num_values; ++v) {
657  // TODO(user): optimize this?
658  if (!rhs.Contains(v)) continue;
659  if (var_activity_states_.back()[v].cost < best_cost) {
660  best_cost = var_activity_states_.back()[v].cost;
661  best_activity = v;
662  }
663  }
664 
665  if (best_cost == std::numeric_limits<int64_t>::max()) {
666  result.infeasible = true;
667  return result;
668  }
669 
670  // Recover the values.
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;
677  }
678 
679  return result;
680 }
681 
682 namespace {
683 
684 // We will call FullyCompressTuplesRecursive() for a set of prefixes of the
685 // original tuples, each having the same suffix (in reversed_suffix).
686 //
687 // For such set, we will compress it on the last variable of the prefixes. We
688 // will then for each unique compressed set of value of that variable, call
689 // a new FullyCompressTuplesRecursive() on the corresponding subset.
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) {
695  struct TempData {
696  absl::InlinedVector<int64_t, 2> values;
697  int index;
698 
699  bool operator<(const TempData& other) const {
700  return values < other.values;
701  }
702  };
703  std::vector<TempData> temp_data;
704 
705  CHECK(!tuples.empty());
706  CHECK(!tuples[0].empty());
707  const int64_t domain_size = domain_sizes[tuples[0].size() - 1];
708 
709  // Sort tuples and regroup common prefix in temp_data.
710  std::sort(tuples.begin(), tuples.end());
711  for (int i = 0; i < tuples.size();) {
712  const int start = i;
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);
720  } else {
721  tuples[i].push_back(v);
722  break;
723  }
724  }
725 
726  // If one of the value is the special value kTableAnyValue, we convert
727  // it to the "empty means any" format.
728  for (const int64_t v : temp_data.back().values) {
729  if (v == kTableAnyValue) {
730  temp_data.back().values.clear();
731  break;
732  }
733  }
734  gtl::STLSortAndRemoveDuplicates(&temp_data.back().values);
735 
736  // If values cover the whole domain, we clear the vector. This allows to
737  // use less space and avoid creating uneeded clauses.
738  if (temp_data.back().values.size() == domain_size) {
739  temp_data.back().values.clear();
740  }
741  }
742 
743  if (temp_data.size() == 1) {
744  output->push_back({});
745  for (const int64_t v : tuples[temp_data[0].index]) {
746  if (v == kTableAnyValue) {
747  output->back().push_back({});
748  } else {
749  output->back().push_back({v});
750  }
751  }
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]);
755  }
756  return;
757  }
758 
759  // Sort temp_data and make recursive call for all tuples that share the
760  // same suffix.
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);
765  const int start = i;
766  temp_tuples.clear();
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]);
770  }
771  FullyCompressTuplesRecursive(domain_sizes, absl::MakeSpan(temp_tuples),
772  reversed_suffix, output);
773  reversed_suffix->pop_back();
774  }
775 }
776 
777 } // namespace
778 
779 // TODO(user): We can probably reuse the tuples memory always and never create
780 // new one. We should also be able to code an iterative version of this. Note
781 // however that the recursion level is bounded by the number of coluns which
782 // should be small.
783 std::vector<std::vector<absl::InlinedVector<int64_t, 2>>> FullyCompressTuples(
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);
790  return output;
791 }
792 
793 } // namespace sat
794 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
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)
Definition: mathutil.h:39
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
Definition: mathutil.h:53
Result Solve(const std::vector< Domain > &domains, const std::vector< int64_t > &coeffs, const std::vector< int64_t > &costs, const Domain &rhs)
Definition: sat/util.cc:546
void AddChoices(absl::Span< const int64_t > choices)
Definition: sat/util.cc:440
void AddMultiples(int64_t coeff, int64_t max_value)
Definition: sat/util.cc:464
double GetPercentile(double percent)
Definition: sat/util.cc:361
int64_t b
int64_t a
SatParameters parameters
int64_t value
int index
const bool DEBUG_MODE
Definition: macros.h:24
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
Definition: sat/util.cc:61
int64_t ClosestMultiple(int64_t value, int64_t base)
Definition: sat/util.cc:228
void CompressTuples(absl::Span< const int64_t > domain_sizes, std::vector< std::vector< int64_t >> *tuples)
Definition: sat/util.cc:386
std::vector< std::vector< absl::InlinedVector< int64_t, 2 > > > FullyCompressTuples(absl::Span< const int64_t > domain_sizes, std::vector< std::vector< int64_t >> *tuples)
Definition: sat/util.cc:783
int64_t PositiveMod(int64_t x, int64_t m)
Definition: sat/util.cc:137
IntType FloorOfRatio(IntType numerator, IntType denominator)
Definition: sat/util.h:433
int64_t CeilSquareRoot(int64_t a)
Definition: sat/util.cc:220
bool SolveDiophantineEquationOfSizeTwo(int64_t &a, int64_t &b, int64_t &cte, int64_t &x0, int64_t &y0)
Definition: sat/util.cc:164
std::string FormatCounter(int64_t num)
Definition: sat/util.cc:48
int64_t FloorSquareRoot(int64_t a)
Definition: sat/util.cc:211
constexpr int64_t kTableAnyValue
Definition: sat/util.h:358
int64_t ModularInverse(int64_t x, int64_t m)
Definition: sat/util.cc:104
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
Definition: sat/util.cc:142
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
Definition: sat/util.cc:299
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)
Definition: sat/util.cc:235
Collection of objects used to extend the Constraint Solver library.
int64_t CapProd(int64_t x, int64_t y)
int64_t bound
int64_t cost
int64_t start