23#include "absl/container/flat_hash_map.h"
24#include "absl/container/flat_hash_set.h"
25#include "absl/strings/str_cat.h"
26#include "absl/strings/str_join.h"
42absl::flat_hash_map<IntegerValue, Literal> GetEncoding(IntegerVariable
var,
44 absl::flat_hash_map<IntegerValue, Literal> encoding;
45 IntegerEncoder* encoder =
model->GetOrCreate<IntegerEncoder>();
46 for (
const auto& entry : encoder->FullDomainEncoding(
var)) {
47 encoding[entry.value] = entry.literal;
58 const std::vector<Literal>& line_literals,
59 const std::vector<IntegerValue>& values,
60 const absl::flat_hash_map<IntegerValue, Literal>& encoding,
61 const std::vector<Literal>& tuples_with_any, Model*
model) {
62 CHECK_EQ(line_literals.size(), values.size());
63 std::vector<std::pair<IntegerValue, Literal>> pairs;
68 for (
int i = 0; i < values.size(); ++i) {
69 const IntegerValue v = values[i];
70 if (!encoding.contains(v)) {
73 pairs.emplace_back(v, line_literals[i]);
80 std::sort(pairs.begin(), pairs.end());
81 std::vector<Literal> clause = tuples_with_any;
82 for (
int i = 0; i < pairs.size();) {
84 clause.resize(tuples_with_any.size());
86 const IntegerValue
value = pairs[i].first;
87 for (; i < pairs.size() && pairs[i].first ==
value; ++i) {
88 clause.push_back(pairs[i].second);
99 absl::Span<const IntegerVariable> vars,
100 const std::vector<std::vector<int64_t>>& tuples,
101 const std::vector<absl::flat_hash_set<int64_t>>& values_per_var,
103 const int n = vars.size();
105 IntegerTrail*
const integer_trail =
model->GetOrCreate<IntegerTrail>();
107 std::vector<absl::flat_hash_map<IntegerValue, Literal>> encodings(n);
108 for (
int i = 0; i < n; ++i) {
109 const std::vector<int64_t> reached_values(values_per_var[i].begin(),
110 values_per_var[i].end());
111 integer_trail->UpdateInitialDomain(vars[i],
113 if (values_per_var.size() > 1) {
115 encodings[i] = GetEncoding(vars[i],
model);
120 if (values_per_var[0].size() == 1 || values_per_var[1].size() == 1)
return;
122 std::map<LiteralIndex, std::vector<Literal>> left_to_right;
123 std::map<LiteralIndex, std::vector<Literal>> right_to_left;
125 for (
const auto& tuple : tuples) {
126 const IntegerValue left_value(tuple[0]);
127 const IntegerValue right_value(tuple[1]);
128 if (!encodings[0].contains(left_value) ||
129 !encodings[1].contains(right_value)) {
135 left_to_right[left.Index()].push_back(right);
136 right_to_left[right.Index()].push_back(left);
139 int num_implications = 0;
140 int num_clause_added = 0;
141 int num_large_clause_added = 0;
142 std::vector<Literal> clause;
143 auto add_support_constraint =
144 [
model, &num_clause_added, &num_large_clause_added, &num_implications,
145 &clause](LiteralIndex lit,
const std::vector<Literal>& supports,
146 int max_support_size) {
147 if (supports.size() == max_support_size)
return;
148 if (supports.size() == 1) {
152 clause.assign(supports.begin(), supports.end());
153 clause.push_back(Literal(lit).Negated());
156 if (supports.size() > max_support_size / 2) {
157 num_large_clause_added++;
162 for (
const auto& it : left_to_right) {
163 add_support_constraint(it.first, it.second, values_per_var[1].size());
165 for (
const auto& it : right_to_left) {
166 add_support_constraint(it.first, it.second, values_per_var[0].size());
168 VLOG(2) <<
"Table: 2 variables, " << tuples.size() <<
" tuples encoded using "
169 << num_clause_added <<
" clauses, " << num_large_clause_added
170 <<
" large clauses, " << num_implications <<
" implications";
179void ExploreSubsetOfVariablesAndAddNegatedTables(
180 const std::vector<std::vector<int64_t>>& tuples,
181 const std::vector<std::vector<int64_t>>& var_domains,
182 absl::Span<const IntegerVariable> vars, Model*
model) {
183 const int num_vars = var_domains.size();
184 for (
int start = 0; start < num_vars; ++start) {
185 const int limit = start == 0 ? num_vars :
std::min(num_vars, start + 3);
186 for (
int end = start + 1; end < limit; ++end) {
194 int64_t max_num_prefix_tuples = 1;
195 for (
int i = start; i <= end; ++i) {
196 max_num_prefix_tuples =
197 CapProd(max_num_prefix_tuples, var_domains[i].size());
201 if (max_num_prefix_tuples > 2 * tuples.size())
break;
203 absl::flat_hash_set<absl::Span<const int64_t>> prefixes;
205 for (
const std::vector<int64_t>& tuple : tuples) {
206 prefixes.insert(absl::MakeSpan(&tuple[start], end - start + 1));
207 if (prefixes.size() == max_num_prefix_tuples) {
214 const int num_prefix_tuples = prefixes.size();
216 std::vector<std::vector<int64_t>> negated_tuples;
219 if (num_prefix_tuples < max_num_prefix_tuples &&
220 max_num_prefix_tuples < num_prefix_tuples * 2) {
221 std::vector<int64_t> tmp_tuple;
222 for (
int i = 0; i < max_num_prefix_tuples; ++i) {
225 for (
int j = start; j <= end; ++j) {
226 tmp_tuple.push_back(var_domains[j][
index % var_domains[j].size()]);
227 index /= var_domains[j].size();
229 if (!prefixes.contains(tmp_tuple)) {
230 negated_tuples.push_back(tmp_tuple);
235 negated_tuples,
model);
236 VLOG(2) <<
" add negated tables with " << created
237 <<
" tuples on the range [" << start <<
"," << end <<
"]";
251 std::vector<std::vector<int64_t>> tuples,
253 const int n = vars.size();
255 const int num_original_tuples = tuples.size();
259 std::vector<absl::flat_hash_set<int64_t>> values_per_var(n);
261 for (
int tuple_index = 0; tuple_index < num_original_tuples; ++tuple_index) {
263 for (
int i = 0; i < n; ++i) {
264 const int64_t
value = tuples[tuple_index][i];
265 if (!values_per_var[i].contains(
value) &&
273 for (
int i = 0; i < n; ++i) {
274 values_per_var[i].insert(tuples[
index][i]);
279 tuples.resize(
index);
280 const int num_valid_tuples = tuples.size();
282 if (tuples.empty()) {
288 AddSizeTwoTable(vars, tuples, values_per_var,
model);
294 int num_prefix_tuples = 0;
296 absl::flat_hash_set<absl::Span<const int64_t>> prefixes;
297 for (
const std::vector<int64_t>& tuple : tuples) {
298 prefixes.insert(absl::MakeSpan(tuple.data(), n - 1));
300 num_prefix_tuples = prefixes.size();
303 std::vector<std::vector<int64_t>> var_domains(n);
304 for (
int j = 0; j < n; ++j) {
305 var_domains[j].assign(values_per_var[j].begin(), values_per_var[j].end());
306 std::sort(var_domains[j].begin(), var_domains[j].end());
309 ExploreSubsetOfVariablesAndAddNegatedTables(tuples, var_domains, vars,
model);
316 std::vector<absl::flat_hash_map<IntegerValue, Literal>> encodings(n);
317 for (
int i = 0; i < n; ++i) {
318 const std::vector<int64_t> reached_values(values_per_var[i].begin(),
319 values_per_var[i].end());
322 if (values_per_var.size() > 1) {
324 encodings[i] = GetEncoding(vars[i],
model);
330 std::vector<int64_t> domain_sizes;
331 for (
int i = 0; i < n; ++i) {
332 domain_sizes.push_back(values_per_var[i].size());
335 const int num_compressed_tuples = tuples.size();
338 const bool prefixes_are_all_different = num_prefix_tuples == num_valid_tuples;
341 int64_t max_num_prefix_tuples = 1;
342 for (
int i = 0; i + 1 < n; ++i) {
343 max_num_prefix_tuples =
344 CapProd(max_num_prefix_tuples, values_per_var[i].size());
347 std::string
message = absl::StrCat(
348 "Table: ", n,
" variables, original tuples = ", num_original_tuples);
349 if (num_valid_tuples != num_original_tuples) {
350 absl::StrAppend(&
message,
", valid tuples = ", num_valid_tuples);
352 if (prefixes_are_all_different) {
353 if (num_prefix_tuples < max_num_prefix_tuples) {
354 absl::StrAppend(&
message,
", partial prefix = ", num_prefix_tuples,
"/",
355 max_num_prefix_tuples);
357 absl::StrAppend(&
message,
", full prefix = true");
360 absl::StrAppend(&
message,
", num prefix tuples = ", num_prefix_tuples);
362 if (num_compressed_tuples != num_valid_tuples) {
364 ", compressed tuples = ", num_compressed_tuples);
369 if (tuples.size() == 1) {
385 std::vector<Literal> tuple_literals;
386 tuple_literals.reserve(tuples.size());
387 if (tuples.size() == 2) {
389 tuple_literals.emplace_back(tuple_literals[0].Negated());
390 }
else if (tuples.size() > 2) {
391 for (
int i = 0; i < tuples.size(); ++i) {
397 std::vector<Literal> active_tuple_literals;
398 std::vector<IntegerValue> active_values;
399 std::vector<Literal> any_tuple_literals;
400 for (
int i = 0; i < n; ++i) {
401 if (values_per_var[i].size() == 1)
continue;
403 active_tuple_literals.clear();
404 active_values.clear();
405 any_tuple_literals.clear();
406 for (
int j = 0; j < tuple_literals.size(); ++j) {
407 const int64_t v = tuples[j][i];
409 if (v == any_value) {
410 any_tuple_literals.push_back(tuple_literals[j]);
412 active_tuple_literals.push_back(tuple_literals[j]);
413 active_values.push_back(IntegerValue(v));
417 if (!active_tuple_literals.empty()) {
418 ProcessOneColumn(active_tuple_literals, active_values, encodings[i],
419 any_tuple_literals,
model);
423 if (prefixes_are_all_different) {
428 std::vector<Literal> clause;
429 for (
int j = 0; j < tuples.size(); ++j) {
431 bool tuple_is_valid =
true;
432 for (
int i = 0; i + 1 < n; ++i) {
434 if (values_per_var[i].size() == 1)
continue;
436 const int64_t v = tuples[j][i];
438 if (v == any_value)
continue;
440 const IntegerValue
value(v);
441 if (!encodings[i].contains(
value)) {
442 tuple_is_valid =
false;
447 if (!tuple_is_valid)
continue;
450 const IntegerValue target_value = IntegerValue(tuples[j][n - 1]);
451 if (!encodings[n - 1].contains(target_value))
continue;
454 clause.push_back(target_literal);
461 std::vector<std::vector<int64_t>> tuples,
463 const int n = vars.size();
469 while (
index < tuples.size()) {
471 for (
int i = 0; i < n; ++i) {
472 if (!integer_trail->InitialVariableDomain(vars[i]).Contains(
479 tuples[
index] = tuples.back();
486 if (tuples.empty()) {
492 std::vector<int64_t> domain_sizes;
493 for (
int i = 0; i < n; ++i) {
494 domain_sizes.push_back(
495 integer_trail->InitialVariableDomain(vars[i]).Size());
500 std::vector<absl::flat_hash_map<int64_t, Literal>> mapping(n);
501 for (
int i = 0; i < n; ++i) {
502 for (
const auto pair : integer_encoder->PartialDomainEncoding(vars[i])) {
503 mapping[i][pair.value.value()] = pair.literal;
508 std::vector<Literal> clause;
509 for (
const std::vector<int64_t>& tuple : tuples) {
510 bool add_tuple =
true;
512 for (
int i = 0; i < n; ++i) {
513 const int64_t
value = tuple[i];
514 if (
value == any_value)
continue;
519 if (mapping[i].contains(
value)) {
528 if (value < lb || value > ub) {
533 clause.push_back(integer_encoder->GetOrCreateAssociatedLiteral(
537 clause.push_back(integer_encoder->GetOrCreateAssociatedLiteral(
539 IntegerValue(
value + 1))));
548 const std::vector<std::vector<Literal>>& literal_tuples,
549 const std::vector<Literal>& line_literals) {
551 CHECK_EQ(literal_tuples.size(), line_literals.size());
552 const int num_tuples = line_literals.size();
553 if (num_tuples == 0)
return;
554 const int tuple_size = literal_tuples[0].size();
555 if (tuple_size == 0)
return;
556 for (
int i = 1; i < num_tuples; ++i) {
557 CHECK_EQ(tuple_size, literal_tuples[i].size());
560 absl::flat_hash_map<LiteralIndex, std::vector<LiteralIndex>>
561 line_literals_per_literal;
562 for (
int i = 0; i < num_tuples; ++i) {
563 const LiteralIndex selected_index = line_literals[i].Index();
564 for (
const Literal l : literal_tuples[i]) {
565 line_literals_per_literal[l.Index()].push_back(selected_index);
571 for (
int i = 0; i < num_tuples; ++i) {
572 const Literal line_is_selected = line_literals[i];
573 for (
const Literal lit : literal_tuples[i]) {
583 for (
const auto& p : line_literals_per_literal) {
584 std::vector<Literal> clause;
585 for (
const auto&
index : p.second) {
595 const std::vector<IntegerVariable>& vars,
596 const std::vector<std::vector<int64_t>>& automaton, int64_t initial_state,
597 const std::vector<int64_t>& final_states) {
600 const int n = vars.size();
601 CHECK_GT(n, 0) <<
"No variables in TransitionConstraint().";
605 std::set<std::pair<int64_t, int64_t>> unique_transition_checker;
606 for (
const std::vector<int64_t>& transition : automaton) {
608 const std::pair<int64_t, int64_t> p{transition[0], transition[1]};
610 <<
"Duplicate outgoing transitions with value " << transition[1]
611 <<
" from state " << transition[0] <<
".";
612 unique_transition_checker.insert(p);
617 std::vector<absl::flat_hash_set<int64_t>> possible_values(n);
620 for (
const std::vector<int64_t>& transition : automaton) {
622 if (domain.Contains(transition[1])) {
623 possible_values[
time].insert(transition[1]);
629 std::vector<std::set<int64_t>> reachable_states(n + 1);
630 reachable_states[0].insert(initial_state);
631 reachable_states[n] = {final_states.begin(), final_states.end()};
638 for (
const std::vector<int64_t>& transition : automaton) {
641 reachable_states[
time + 1].insert(transition[2]);
647 std::set<int64_t> new_set;
648 for (
const std::vector<int64_t>& transition : automaton) {
653 new_set.insert(transition[0]);
655 reachable_states[
time].swap(new_set);
663 absl::flat_hash_map<IntegerValue, Literal> encoding;
664 absl::flat_hash_map<IntegerValue, Literal> in_encoding;
665 absl::flat_hash_map<IntegerValue, Literal> out_encoding;
670 std::vector<Literal> tuple_literals;
671 std::vector<IntegerValue> in_states;
672 std::vector<IntegerValue> transition_values;
673 std::vector<IntegerValue> out_states;
674 for (
const std::vector<int64_t>& transition : automaton) {
683 tuple_literals.push_back(
685 in_states.push_back(IntegerValue(transition[0]));
687 transition_values.push_back(IntegerValue(transition[1]));
691 out_states.push_back(
time + 1 == n ? IntegerValue(0)
692 : IntegerValue(transition[2]));
700 std::vector<IntegerValue> s = transition_values;
705 std::vector<int64_t> values;
706 values.reserve(s.size());
707 for (IntegerValue v : s) values.push_back(v.value());
711 encoding = GetEncoding(vars[
time],
model);
715 const int64_t unique_value = s.begin()->value();
723 std::vector<IntegerValue> s = out_states;
726 out_encoding.clear();
731 }
else if (s.size() > 1) {
732 for (
const IntegerValue state : s) {
734 out_encoding[state] = l;
744 if (!in_encoding.empty()) {
745 ProcessOneColumn(tuple_literals, in_states, in_encoding, {},
model);
747 if (!encoding.empty()) {
748 ProcessOneColumn(tuple_literals, transition_values, encoding, {},
751 if (!out_encoding.empty()) {
752 ProcessOneColumn(tuple_literals, out_states, out_encoding, {},
model);
754 in_encoding = out_encoding;
#define CHECK_EQ(val1, val2)
#define CHECK_GT(val1, val2)
#define VLOG(verboselevel)
bool Contains(int64_t value) const
Returns true iff value is in Domain.
static Domain FromValues(std::vector< int64_t > values)
Creates a domain from the union of an unsorted list of integer values.
const Domain & InitialVariableDomain(IntegerVariable var) const
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
Class that owns everything related to a particular optimization model.
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
bool ContainsKey(const Collection &collection, const Key &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
void AddTableConstraint(absl::Span< const IntegerVariable > vars, std::vector< std::vector< int64_t > > tuples, Model *model)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
std::function< void(Model *)> LiteralTableConstraint(const std::vector< std::vector< Literal > > &literal_tuples, const std::vector< Literal > &line_literals)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
void CompressTuples(absl::Span< const int64_t > domain_sizes, int64_t any_value, std::vector< std::vector< int64_t > > *tuples)
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
std::function< void(Model *)> Implication(const std::vector< Literal > &enforcement_literals, IntegerLiteral i)
void AddNegatedTableConstraint(absl::Span< const IntegerVariable > vars, std::vector< std::vector< int64_t > > tuples, Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
std::function< void(Model *)> TransitionConstraint(const std::vector< IntegerVariable > &vars, const std::vector< std::vector< int64_t > > &automaton, int64_t initial_state, const std::vector< int64_t > &final_states)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
std::function< std::vector< IntegerEncoder::ValueLiteralPair >(Model *)> FullyEncodeVariable(IntegerVariable var)
Collection of objects used to extend the Constraint Solver library.
int64_t CapProd(int64_t x, int64_t y)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
#define VLOG_IS_ON(verboselevel)