29#include "absl/algorithm/container.h"
30#include "absl/base/no_destructor.h"
31#include "absl/container/btree_map.h"
32#include "absl/container/btree_set.h"
33#include "absl/container/flat_hash_map.h"
34#include "absl/container/flat_hash_set.h"
35#include "absl/flags/flag.h"
36#include "absl/log/check.h"
37#include "absl/log/log.h"
38#include "absl/strings/match.h"
39#include "absl/strings/str_cat.h"
40#include "absl/strings/str_join.h"
41#include "absl/types/span.h"
42#include "google/protobuf/arena.h"
57 "Default max value for unbounded integer variables.");
59 "If true, enable interleaved workers when num_workers is 1.");
61 "Use lighter encodings for the model");
70static const int kNoVar = std::numeric_limits<int>::min();
78 std::vector<int> var_indices;
79 std::vector<int64_t> sorted_values;
80 int card_var_index = kNoVar;
81 std::optional<int> fixed_card = std::nullopt;
83 int find_value_index(int64_t value)
const {
85 std::lower_bound(sorted_values.begin(), sorted_values.end(), value);
86 if (it == sorted_values.end() || *it != value)
return -1;
87 return it - sorted_values.begin();
92struct CpModelProtoWithMapping {
93 CpModelProtoWithMapping(
bool sat_enumeration)
94 : arena(std::make_unique<google::protobuf::Arena>()),
95 proto(*google::protobuf::Arena::Create<CpModelProto>(arena.get())),
96 sat_enumeration_(sat_enumeration) {}
102 int NewIntVar(int64_t min_value, int64_t max_value);
105 int LookupConstant(int64_t value);
107 void AddImplication(absl::Span<const int> e,
int l) {
108 ConstraintProto* ct = proto.add_constraints();
109 for (
int e_lit : e) {
110 ct->add_enforcement_literal(e_lit);
112 ct->mutable_bool_and()->add_literals(l);
117 int LookupVar(
const fz::Argument& argument);
118 LinearExpressionProto LookupExpr(
const fz::Argument& argument,
119 bool negate =
false);
120 LinearExpressionProto LookupExprAt(
const fz::Argument& argument,
int pos,
121 bool negate =
false);
122 std::vector<int> LookupVars(
const fz::Argument& argument);
123 VarOrValue LookupVarOrValue(
const fz::Argument& argument);
124 std::vector<VarOrValue> LookupVarsOrValues(
const fz::Argument& argument);
127 bool VariableIsBoolean(
int var)
const;
130 void ExtractSetConstraint(
const fz::Constraint& fz_ct);
131 bool ConstraintContainsSetVariables(
const fz::Constraint& constraint)
const;
132 std::shared_ptr<SetVariable> LookupSetVar(
const fz::Argument& argument);
133 std::shared_ptr<SetVariable> LookupSetVarAt(
const fz::Argument& argument,
137 int GetOrCreateEncodingLiteral(
int var, int64_t value);
138 std::optional<int> GetEncodingLiteral(
int var, int64_t value);
139 void AddVarEqValueLiteral(
int var, int64_t value,
int literal);
140 void AddVarGeValueLiteral(
int var, int64_t value,
int literal);
141 void AddVarGtValueLiteral(
int var, int64_t value,
int literal);
142 void AddVarLeValueLiteral(
int var, int64_t value,
int literal);
143 void AddVarLtValueLiteral(
int var, int64_t value,
int literal);
146 int GetOrCreateLiteralForVarEqVar(
int var1,
int var2);
147 void AddVarEqVarLiteral(
int var1,
int var2,
int literal);
148 std::optional<int> GetVarEqVarLiteral(
int var1,
int var2);
149 int GetOrCreateLiteralForVarNeVar(
int var1,
int var2);
150 std::optional<int> GetVarNeVarLiteral(
int var1,
int var2);
151 void AddVarNeVarLiteral(
int var1,
int var2,
int literal);
152 int GetOrCreateLiteralForVarLeVar(
int var1,
int var2);
153 std::optional<int> GetVarLeVarLiteral(
int var1,
int var2);
154 void AddVarLeVarLiteral(
int var1,
int var2,
int literal);
155 int GetOrCreateLiteralForVarLtVar(
int var1,
int var2);
156 std::optional<int> GetVarLtVarLiteral(
int var1,
int var2);
157 void AddVarLtVarLiteral(
int var1,
int var2,
int literal);
158 void AddVarOrVarLiteral(
int var1,
int var2,
int literal);
159 void AddVarAndVarLiteral(
int var1,
int var2,
int literal);
162 absl::btree_map<int64_t, int> GetFullEncoding(
int var);
167 std::vector<int> CreateIntervals(absl::Span<const VarOrValue> starts,
168 absl::Span<const VarOrValue> sizes);
177 std::vector<int> CreateNonZeroOrOptionalIntervals(
178 absl::Span<const VarOrValue> starts, absl::Span<const VarOrValue> sizes);
185 int GetOrCreateOptionalInterval(VarOrValue start_var, VarOrValue size,
190 int NonZeroLiteralFrom(VarOrValue size);
194 ConstraintProto* AddEnforcedConstraint(
int literal);
197 LinearConstraintProto* AddLinearConstraint(
198 absl::Span<const int> enforcement_literals,
const Domain& domain,
199 absl::Span<
const std::pair<int, int64_t>> terms = {});
202 void AddLexOrdering(absl::Span<const int> x, absl::Span<const int> y,
203 int enforcement_literal,
bool accepts_equals);
206 void AddLiteralVectorsNotEqual(absl::Span<const int> x_array,
207 absl::Span<const int> y_array);
210 void AddTermToLinearConstraint(
int var, int64_t coeff,
211 LinearConstraintProto* ct);
212 void FillAMinusBInDomain(
const std::vector<int64_t>& domain,
213 const fz::Constraint& fz_ct, ConstraintProto* ct);
214 void FillLinearConstraintWithGivenDomain(absl::Span<const int64_t> domain,
215 const fz::Constraint& fz_ct,
216 ConstraintProto* ct);
217 void BuildTableFromDomainIntLinEq(
const fz::Constraint& fz_ct,
218 ConstraintProto* ct);
220 void FirstPass(
const fz::Constraint& fz_ct);
221 void FillConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
222 void FillReifiedOrImpliedConstraint(
const fz::Constraint& fz_ct);
223 void AddAllEncodingConstraints();
226 void AlwaysFalseConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
227 void BoolClauseConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
228 void BoolXorConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
229 void ArrayBoolXorConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
230 void BoolOrIntLeConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
231 void BoolOrIntGeConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
232 void BoolOrIntLtConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
233 void BoolOrIntGtConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
234 void BoolEqConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
235 void BoolNeConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
236 void IntNeConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
237 void IntLinEqConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
238 void BoolLinEqConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
239 void BoolOrIntLinLeConstraint(
const fz::Constraint& fz_ct,
240 ConstraintProto* ct);
241 void IntLinLtConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
242 void IntLinGeConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
243 void IntLinGtConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
244 void IntLinNeConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
245 void SetCardConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
246 void SetInConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
247 void SetInNegatedConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
248 void IntMinConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
249 void ArrayIntMinConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
250 void IntMaxConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
251 void ArrayIntMaxConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
252 void IntTimesConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
253 void IntAbsConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
254 void IntPlusConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
255 void IntDivConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
256 void IntModConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
257 void ArrayElementConstraint(
const fz::Constraint& fz_ct, ConstraintProto* ct);
258 void OrToolsConstantElementConstraint(
const fz::Constraint& fz_ct,
259 ConstraintProto* ct);
260 void OrToolsVariableElementConstraint(
const fz::Constraint& fz_ct,
261 ConstraintProto* ct);
262 void OrToolsTableIntConstraint(
const fz::Constraint& fz_ct,
263 ConstraintProto* ct);
264 void OrToolsRegular(
const fz::Constraint& fz_ct, ConstraintProto* ct);
265 void OrToolsArgMax(
const fz::Constraint& fz_ct, ConstraintProto* ct);
266 void FznAllDifferentInt(
const fz::Constraint& fz_ct, ConstraintProto* ct);
267 void FznValuePrecedeInt(
const fz::Constraint& fz_ct, ConstraintProto* ct);
268 void OrToolsCountEqCst(
const fz::Constraint& fz_ct, ConstraintProto* ct);
269 void OrToolsCountEq(
const fz::Constraint& fz_ct, ConstraintProto* ct);
270 void OrToolsCircuit(
const fz::Constraint& fz_ct, ConstraintProto* ct);
271 void OrToolsInverse(
const fz::Constraint& fz_ct, ConstraintProto* ct);
272 void OrToolsLexOrdering(
const fz::Constraint& fz_ct, ConstraintProto* ct);
273 void OrToolsPrecedeChainInt(
const fz::Constraint& fz_ct, ConstraintProto* ct);
274 void FznDisjunctive(
const fz::Constraint& fz_ct, ConstraintProto* ct);
275 void FznDisjunctiveStrict(
const fz::Constraint& fz_ct, ConstraintProto* ct);
276 void FznCumulative(
const fz::Constraint& fz_ct, ConstraintProto* ct);
277 void OrToolsCumulativeOpt(
const fz::Constraint& fz_ct, ConstraintProto* ct);
278 void OrToolsDisjunctiveStrictOpt(
const fz::Constraint& fz_ct,
279 ConstraintProto* ct);
280 void FznDiffn(
const fz::Constraint& fz_ct, ConstraintProto* ct);
281 void OrToolsNetworkFlow(
const fz::Constraint& fz_ct, ConstraintProto* ct);
282 void OrToolsBinPacking(
const fz::Constraint& fz_ct, ConstraintProto* ct);
283 void OrToolsBinPackingCapa(
const fz::Constraint& fz_ct, ConstraintProto* ct);
284 void OrToolsBinPackingLoad(
const fz::Constraint& fz_ct, ConstraintProto* ct);
285 void OrToolsNvalue(
const fz::Constraint& fz_ct, ConstraintProto* ct);
286 void OrToolsGlobalCardinality(
const fz::Constraint& fz_ct,
287 ConstraintProto* ct);
288 void OrToolsGlobalCardinalityLowUp(
const fz::Constraint& fz_ct,
289 ConstraintProto* ct);
292 void ArraySetElementConstraint(
const fz::Constraint& fz_ct);
293 void ArrayVarSetElementConstraint(
const fz::Constraint& fz_ct);
294 void FznAllDifferentSetConstraint(
const fz::Constraint& fz_ct);
295 void FznAllDisjointConstraint(
const fz::Constraint& fz_ct);
296 void FznDisjointConstraint(
const fz::Constraint& fz_ct);
297 void FznPartitionSetConstraint(
const fz::Constraint& fz_ct);
298 void SetCardConstraint(
const fz::Constraint& fz_ct);
299 void SetInConstraint(
const fz::Constraint& fz_ct);
300 void SetInReifConstraint(
const fz::Constraint& fz_ct);
301 void SetOpConstraint(
const fz::Constraint& fz_ct);
302 void SetSubSupersetEqConstraint(
const fz::Constraint& fz_ct);
303 void SetNeConstraint(
const fz::Constraint& fz_ct);
304 void SetSubSupersetEqReifConstraint(
const fz::Constraint& fz_ct);
305 void SetLexOrderingConstraint(
const fz::Constraint& fz_ct);
310 void PutSetBooleansInCommonDomain(
311 absl::Span<
const std::shared_ptr<SetVariable>> set_vars,
312 absl::Span<std::vector<int>*
const> var_booleans);
316 void TranslateSearchAnnotations(
317 absl::Span<const fz::Annotation> search_annotations,
318 SolverLogger* logger);
321 std::unique_ptr<google::protobuf::Arena> arena;
323 SatParameters parameters;
324 const bool sat_enumeration_;
327 absl::flat_hash_map<fz::Variable*, int> fz_var_to_index;
328 absl::flat_hash_map<int64_t, int> constant_value_to_index;
329 absl::flat_hash_map<std::tuple<int, int64_t, int, int64_t, int>,
int>
330 interval_key_to_index;
331 absl::flat_hash_map<int, int> var_to_lit_implies_greater_than_zero;
333 absl::btree_map<int, absl::btree_map<int64_t, int>> encoding_literals;
334 absl::btree_map<int, absl::btree_map<int64_t, int>> var_le_value_to_literal;
336 absl::flat_hash_map<std::pair<int, int>,
int> var_eq_var_to_literal;
337 absl::flat_hash_map<std::pair<int, int>,
int> var_lt_var_to_literal;
338 absl::flat_hash_map<std::pair<int, int>,
int> var_or_var_to_literal;
339 absl::flat_hash_map<std::pair<int, int>,
int> var_and_var_to_literal;
341 absl::flat_hash_map<fz::Variable*, std::shared_ptr<SetVariable>>
344 int num_var_op_value_reif_cached = 0;
345 int num_var_op_var_reif_cached = 0;
346 int num_var_op_var_imp_cached = 0;
347 int num_full_encodings = 0;
348 int num_light_encodings = 0;
349 int num_partial_encodings = 0;
352int CpModelProtoWithMapping::NewBoolVar() {
return NewIntVar(0, 1); }
354int CpModelProtoWithMapping::NewIntVar(int64_t min_value, int64_t max_value) {
360int CpModelProtoWithMapping::LookupConstant(int64_t value) {
361 if (constant_value_to_index.contains(value)) {
362 return constant_value_to_index[value];
366 const int index = NewIntVar(value, value);
367 constant_value_to_index[value] = index;
371int CpModelProtoWithMapping::LookupVar(
const fz::Argument& argument) {
372 if (argument.HasOneValue())
return LookupConstant(argument.Value());
373 CHECK_EQ(argument.type, fz::Argument::VAR_REF);
374 return fz_var_to_index[argument.Var()];
377LinearExpressionProto CpModelProtoWithMapping::LookupExpr(
378 const fz::Argument& argument,
bool negate) {
379 LinearExpressionProto expr;
380 if (argument.HasOneValue()) {
381 const int64_t value = argument.Value();
382 expr.set_offset(negate ? -value : value);
384 expr.add_vars(LookupVar(argument));
385 expr.add_coeffs(negate ? -1 : 1);
390LinearExpressionProto CpModelProtoWithMapping::LookupExprAt(
391 const fz::Argument& argument,
int pos,
bool negate) {
392 LinearExpressionProto expr;
393 if (argument.HasOneValueAt(pos)) {
394 const int64_t value = argument.ValueAt(pos);
395 expr.set_offset(negate ? -value : value);
397 expr.add_vars(fz_var_to_index[argument.VarAt(pos)]);
398 expr.add_coeffs(negate ? -1 : 1);
403std::vector<int> CpModelProtoWithMapping::LookupVars(
404 const fz::Argument& argument) {
405 std::vector<int> result;
406 if (argument.type == fz::Argument::VOID_ARGUMENT)
return result;
407 if (argument.type == fz::Argument::INT_LIST) {
408 for (int64_t value : argument.values) {
409 result.push_back(LookupConstant(value));
411 }
else if (argument.type == fz::Argument::INT_VALUE) {
412 result.push_back(LookupConstant(argument.Value()));
414 CHECK_EQ(argument.type, fz::Argument::VAR_REF_ARRAY);
415 for (fz::Variable* var : argument.variables) {
416 CHECK(var !=
nullptr);
417 result.push_back(fz_var_to_index[var]);
423VarOrValue CpModelProtoWithMapping::LookupVarOrValue(
424 const fz::Argument& argument) {
425 if (argument.type == fz::Argument::INT_VALUE) {
426 return {kNoVar, argument.Value()};
428 CHECK_EQ(argument.type, fz::Argument::VAR_REF);
429 fz::Variable* var = argument.Var();
430 CHECK(var !=
nullptr);
431 if (var->domain.HasOneValue()) {
432 return {kNoVar, var->domain.Value()};
434 return {fz_var_to_index[var], 0};
439std::vector<VarOrValue> CpModelProtoWithMapping::LookupVarsOrValues(
440 const fz::Argument& argument) {
441 std::vector<VarOrValue> result;
442 const int no_var = kNoVar;
443 if (argument.type == fz::Argument::VOID_ARGUMENT)
return result;
444 if (argument.type == fz::Argument::INT_LIST) {
445 for (int64_t value : argument.values) {
446 result.push_back({no_var, value});
448 }
else if (argument.type == fz::Argument::INT_VALUE) {
449 result.push_back({no_var, argument.Value()});
451 CHECK_EQ(argument.type, fz::Argument::VAR_REF_ARRAY);
452 for (fz::Variable* var : argument.variables) {
453 CHECK(var !=
nullptr);
454 if (var->domain.HasOneValue()) {
455 result.push_back({no_var, var->domain.Value()});
457 result.push_back({fz_var_to_index[var], 0});
464bool CpModelProtoWithMapping::VariableIsBoolean(
int var)
const {
466 const IntegerVariableProto& var_proto = proto.
variables(var);
467 return var_proto.
domain_size() == 2 && var_proto.domain(0) >= 0 &&
468 var_proto.domain(1) <= 1;
471bool CpModelProtoWithMapping::ConstraintContainsSetVariables(
472 const fz::Constraint& constraint)
const {
473 for (
const fz::Argument& argument : constraint.arguments) {
474 for (fz::Variable* var : argument.variables) {
475 if (var !=
nullptr && var->domain.is_a_set)
return true;
481std::shared_ptr<SetVariable> CpModelProtoWithMapping::LookupSetVar(
482 const fz::Argument& argument) {
483 if (argument.type == fz::Argument::VAR_REF) {
484 CHECK(argument.Var()->domain.is_a_set);
485 return set_variables[argument.Var()];
487 std::shared_ptr<SetVariable> result = std::make_shared<SetVariable>();
488 const int true_literal = LookupConstant(1);
489 if (argument.type == fz::Argument::INT_INTERVAL) {
490 for (int64_t value = argument.values[0]; value <= argument.values[1];
492 result->sorted_values.push_back(value);
493 result->var_indices.push_back(true_literal);
495 }
else if (argument.type == fz::Argument::INT_LIST) {
496 for (int64_t value : argument.values) {
497 result->sorted_values.push_back(value);
498 result->var_indices.push_back(true_literal);
501 LOG(FATAL) <<
"LookupSetVar:Unsupported argument type '" << argument.type
508std::shared_ptr<SetVariable> CpModelProtoWithMapping::LookupSetVarAt(
509 const fz::Argument& argument,
int pos) {
510 if (argument.type == fz::Argument::VAR_REF_ARRAY) {
511 CHECK(argument.variables[pos]->domain.is_a_set);
512 return set_variables[argument.variables[pos]];
514 LOG(FATAL) <<
"LookupSetVarAt:Unsupported argument type '" << argument.type
519ConstraintProto* CpModelProtoWithMapping::AddEnforcedConstraint(
int literal) {
521 if (literal != kNoVar) {
522 result->add_enforcement_literal(literal);
527LinearConstraintProto* CpModelProtoWithMapping::AddLinearConstraint(
528 absl::Span<const int> enforcement_literals,
const Domain& domain,
529 absl::Span<
const std::pair<int, int64_t>> terms) {
531 for (
const int literal : enforcement_literals) {
532 result->add_enforcement_literal(literal);
535 for (
const auto& [var, coeff] : terms) {
536 AddTermToLinearConstraint(var, coeff, result->mutable_linear());
538 return result->mutable_linear();
541void CpModelProtoWithMapping::AddTermToLinearConstraint(
542 int var, int64_t coeff, LinearConstraintProto* ct) {
543 CHECK(!ct->domain().empty());
544 if (coeff == 0)
return;
547 ct->add_coeffs(coeff);
550 ct->add_coeffs(-coeff);
552 for (
int i = 0;
i < ct->domain_size(); ++
i) {
553 const int64_t
b = ct->domain(i);
554 if (b == std::numeric_limits<int64_t>::min() ||
555 b == std::numeric_limits<int64_t>::max()) {
558 ct->set_domain(i, b - coeff);
563void CpModelProtoWithMapping::AddLexOrdering(absl::Span<const int> x,
564 absl::Span<const int> y,
565 int enforcement_literal,
566 bool accepts_equals) {
567 const int min_size = std::min(
x.size(), y.size());
568 std::vector<int> hold(min_size + 1);
569 hold[0] = enforcement_literal;
570 for (
int i = 1;
i < min_size; ++
i) hold[i] = NewBoolVar();
571 const bool hold_sentinel_is_true =
572 x.size() < y.size() || (
x.size() == y.size() && accepts_equals);
573 hold[min_size] = LookupConstant(hold_sentinel_is_true ? 1 : 0);
575 if (!sat_enumeration_ && absl::GetFlag(FLAGS_fz_use_light_encoding)) {
577 const Domain le_domain(std::numeric_limits<int64_t>::min(), 0);
578 const Domain lt_domain(std::numeric_limits<int64_t>::min(), -1);
579 for (
int i = 0;
i < min_size; ++
i) {
581 AddLinearConstraint({hold[
i]}, le_domain, {{
x[
i], 1}, {y[
i], -1}});
584 const int is_lt = NewBoolVar();
585 AddLinearConstraint({is_lt}, lt_domain, {{
x[
i], 1}, {y[
i], -1}});
587 ConstraintProto* chain = AddEnforcedConstraint(hold[i]);
588 chain->mutable_bool_or()->add_literals(is_lt);
589 chain->mutable_bool_or()->add_literals(hold[i + 1]);
591 ++num_light_encodings;
593 std::vector<int> is_lt(min_size);
594 std::vector<int> is_le(min_size);
595 for (
int i = 0;
i < min_size; ++
i) {
596 is_le[
i] = GetOrCreateLiteralForVarLeVar(x[i], y[i]);
597 is_lt[
i] = GetOrCreateLiteralForVarLtVar(x[i], y[i]);
600 for (
int i = 0;
i < min_size; ++
i) {
602 AddImplication({hold[
i]}, is_le[
i]);
605 ConstraintProto* chain = AddEnforcedConstraint(hold[i]);
606 chain->mutable_bool_or()->add_literals(is_lt[i]);
607 chain->mutable_bool_or()->add_literals(hold[i + 1]);
610 if (i + 1 < min_size) {
612 AddImplication({is_lt[
i]},
NegatedRef(hold[i + 1]));
622void CpModelProtoWithMapping::AddLiteralVectorsNotEqual(
623 absl::Span<const int> x_array, absl::Span<const int> y_array) {
624 const int size = x_array.size();
625 CHECK_EQ(size, y_array.size());
626 if (sat_enumeration_ || !absl::GetFlag(FLAGS_fz_use_light_encoding)) {
627 BoolArgumentProto* at_least_one_different =
629 for (
int i = 0;
i < x_array.size(); ++
i) {
630 at_least_one_different->add_literals(
631 GetOrCreateLiteralForVarNeVar(x_array[i], y_array[i]));
633 ++num_light_encodings;
637 std::vector<int> is_ne(size);
638 for (
int i = 0;
i < size; ++
i) {
641 is_ne[
i] = NewBoolVar();
642 AddLinearConstraint({is_ne[
i]}, Domain(1),
643 {{x_array[
i], 1}, {y_array[
i], 1}});
646 std::vector<int> hold(size + 1);
647 hold[0] = LookupConstant(1);
648 for (
int i = 1;
i < size; ++
i) hold[i] = NewBoolVar();
649 hold[size] = LookupConstant(0);
651 for (
int i = 0;
i < size; ++
i) {
653 ConstraintProto* chain = AddEnforcedConstraint(hold[i]);
654 chain->mutable_bool_or()->add_literals(is_ne[i]);
655 chain->mutable_bool_or()->add_literals(hold[i + 1]);
662std::optional<int> CpModelProtoWithMapping::GetEncodingLiteral(
int var,
665 const IntegerVariableProto& var_proto = proto.
variables(var);
671 if (!domain.
Contains(value))
return LookupConstant(0);
675 if (domain.Size() == 1)
return LookupConstant(1);
678 if (domain.Size() == 2 && domain.
Min() == 0 && domain.
Max() == 1) {
682 const auto it = encoding_literals.find(var);
683 if (it != encoding_literals.end()) {
684 const auto it2 = it->second.find(value);
685 if (it2 != it->second.end())
return it2->second;
690int CpModelProtoWithMapping::GetOrCreateEncodingLiteral(
int var,
693 const std::optional<int> existing_literal = GetEncodingLiteral(var, value);
694 if (existing_literal.has_value()) {
695 CHECK_NE(existing_literal.value(), kNoVar);
696 return existing_literal.value();
699 const IntegerVariableProto& var_proto = proto.
variables(var);
702 CHECK(domain.Size() > 2 ||
703 (domain.Size() == 2 && (domain.
Min() != 0 || domain.
Max() != 1)));
705 const int lit = NewBoolVar();
706 if (domain.Size() == 2) {
708 encoding_literals[var][domain.
Max()] = lit;
711 encoding_literals[var][value] = lit;
716void CpModelProtoWithMapping::AddVarEqValueLiteral(
int var, int64_t value,
719 const std::optional<int> existing_literal = GetEncodingLiteral(var, value);
720 if (existing_literal.has_value()) {
721 AddLinearConstraint({}, Domain(0),
722 {{lit, 1}, {existing_literal.value(), -1}});
723 ++num_var_op_value_reif_cached;
725 const IntegerVariableProto& var_proto = proto.
variables(var);
728 if (domain.Size() == 2) {
729 CHECK(domain.
Min() != 0 || domain.
Max() != 1);
730 if (value == domain.
Min()) {
731 encoding_literals[var][domain.
Min()] = lit;
735 encoding_literals[var][domain.
Max()] = lit;
738 encoding_literals[var][value] = lit;
743void CpModelProtoWithMapping::AddVarGeValueLiteral(
int var, int64_t value,
745 AddVarLeValueLiteral(var, value - 1,
NegatedRef(lit));
748void CpModelProtoWithMapping::AddVarGtValueLiteral(
int var, int64_t value,
750 AddVarLeValueLiteral(var, value,
NegatedRef(lit));
753void CpModelProtoWithMapping::AddVarLtValueLiteral(
int var, int64_t value,
755 AddVarLeValueLiteral(var, value - 1, lit);
758void CpModelProtoWithMapping::AddVarLeValueLiteral(
int var, int64_t value,
761 const auto it = var_le_value_to_literal.find(var);
762 if (it != var_le_value_to_literal.end()) {
763 const auto it2 = it->second.find(value);
764 if (it2 != it->second.end()) {
765 AddLinearConstraint({}, Domain(0), {{lit, 1}, {it2->second, -1}});
766 ++num_var_op_value_reif_cached;
770 var_le_value_to_literal[var][value] = lit;
773absl::btree_map<int64_t, int> CpModelProtoWithMapping::GetFullEncoding(
775 absl::btree_map<int64_t, int> result;
777 for (int64_t value : domain.Values()) {
778 result[value] = GetOrCreateEncodingLiteral(var, value);
783void CpModelProtoWithMapping::AddAllEncodingConstraints() {
784 const bool light_encoding = absl::GetFlag(FLAGS_fz_use_light_encoding);
785 for (
const auto& [var, encoding] : encoding_literals) {
787 CHECK(domain.Size() > 2 ||
788 (domain.Size() == 2 && (domain.
Min() != 0 || domain.
Max() != 1)));
790 if (domain.Size() == encoding.size()) ++num_full_encodings;
792 if (domain.Size() == 2) {
793 CHECK_EQ(encoding.size(), 2);
794 const int lit = encoding.at(domain.
Max());
796 AddLinearConstraint({}, Domain(domain.
Min()),
797 {{var, 1}, {lit, domain.
Min() - domain.
Max()}});
801 if (domain.Size() == encoding.size() && light_encoding) {
803 for (
const auto& [value, lit] : encoding) {
804 exo->add_literals(lit);
805 AddLinearConstraint({lit}, Domain(value), {{var, 1}});
807 }
else if (encoding.size() > domain.Size() / 2 && light_encoding) {
810 for (
const int64_t value : domain.Values()) {
811 const int lit = GetOrCreateEncodingLiteral(var, value);
812 exo->add_literals(lit);
813 AddLinearConstraint({lit}, Domain(value), {{var, 1}});
815 ++num_partial_encodings;
817 for (
const auto& [value, lit] : encoding) {
818 AddLinearConstraint({lit}, Domain(value), {{var, 1}});
819 AddLinearConstraint({
NegatedRef(lit)}, Domain(value).Complement(),
825 for (
const auto& [var, encoding] : var_le_value_to_literal) {
827 for (
const auto& [value, lit] : encoding) {
831 if (value == domain.
Min()) {
832 const std::optional<int> encoding =
833 GetEncodingLiteral(var, domain.
Min());
834 if (encoding.has_value()) {
835 AddLinearConstraint({}, Domain(0),
836 {{lit, 1}, {encoding.value(), -1}});
837 ++num_var_op_value_reif_cached;
840 if (value == domain.
Max() - 1 && !light_encoding) {
842 const std::optional<int> encoding_lit =
843 GetEncodingLiteral(var, domain.
Max());
844 if (encoding_lit.has_value()) {
847 {{lit, 1}, {
NegatedRef(encoding_lit.value()), -1}});
848 ++num_var_op_value_reif_cached;
853 const Domain le_domain(std::numeric_limits<int64_t>::min(), value);
854 AddLinearConstraint({lit}, le_domain, {{var, 1}});
855 AddLinearConstraint({
NegatedRef(lit)}, le_domain.Complement(),
863int CpModelProtoWithMapping::GetOrCreateLiteralForVarEqVar(
int var1,
int var2) {
864 CHECK_NE(var1, kNoVar);
865 CHECK_NE(var2, kNoVar);
866 if (var1 > var2) std::swap(var1, var2);
867 if (var1 == var2)
return LookupConstant(1);
869 const std::pair<int, int> key = {var1, var2};
870 const auto it = var_eq_var_to_literal.find(key);
871 if (it != var_eq_var_to_literal.end())
return it->second;
873 const int lit = NewBoolVar();
874 AddVarEqVarLiteral(var1, var2, lit);
878int CpModelProtoWithMapping::GetOrCreateLiteralForVarNeVar(
int var1,
int var2) {
879 return NegatedRef(GetOrCreateLiteralForVarEqVar(var1, var2));
882void CpModelProtoWithMapping::AddVarEqVarLiteral(
int var1,
int var2,
int lit) {
883 CHECK_NE(var1, kNoVar);
884 CHECK_NE(var2, kNoVar);
886 AddImplication({}, lit);
889 if (var1 > var2) std::swap(var1, var2);
891 const std::pair<int, int> key = {var1, var2};
892 const auto it = var_eq_var_to_literal.find(key);
893 if (it != var_eq_var_to_literal.end()) {
894 AddLinearConstraint({}, Domain(0), {{lit, 1}, {it->second, -1}});
895 ++num_var_op_var_reif_cached;
897 if (VariableIsBoolean(var1) && VariableIsBoolean(var2)) {
898 AddLinearConstraint({lit}, Domain(0), {{var1, 1}, {var2, -1}});
899 AddLinearConstraint({
NegatedRef(lit)}, Domain(1), {{var1, 1}, {var2, 1}});
901 AddLinearConstraint({lit}, Domain(0), {{var1, 1}, {var2, -1}});
902 AddLinearConstraint({
NegatedRef(lit)}, Domain(0).Complement(),
903 {{var1, 1}, {var2, -1}});
905 var_eq_var_to_literal[key] = lit;
909void CpModelProtoWithMapping::AddVarNeVarLiteral(
int var1,
int var2,
int lit) {
910 AddVarEqVarLiteral(var1, var2,
NegatedRef(lit));
913std::optional<int> CpModelProtoWithMapping::GetVarEqVarLiteral(
int var1,
915 CHECK_NE(var1, kNoVar);
916 CHECK_NE(var2, kNoVar);
917 if (var1 == var2)
return LookupConstant(1);
918 if (var1 > var2) std::swap(var1, var2);
919 const std::pair<int, int> key = {var1, var2};
920 const auto it = var_eq_var_to_literal.find(key);
921 if (it != var_eq_var_to_literal.end())
return it->second;
925std::optional<int> CpModelProtoWithMapping::GetVarNeVarLiteral(
int var1,
927 const std::optional<int> lit = GetVarEqVarLiteral(var1, var2);
928 if (lit.has_value())
return NegatedRef(lit.value());
932int CpModelProtoWithMapping::GetOrCreateLiteralForVarLtVar(
int var1,
int var2) {
933 CHECK_NE(var1, kNoVar);
934 CHECK_NE(var2, kNoVar);
935 if (var1 == var2)
return LookupConstant(0);
937 const std::pair<int, int> key = {var1, var2};
938 const auto it = var_lt_var_to_literal.find(key);
939 if (it != var_lt_var_to_literal.end())
return it->second;
941 const int lit = NewBoolVar();
942 AddVarLtVarLiteral(var1, var2, lit);
946void CpModelProtoWithMapping::AddVarLtVarLiteral(
int var1,
int var2,
int lit) {
947 CHECK_NE(var1, kNoVar);
948 CHECK_NE(var2, kNoVar);
954 const std::pair<int, int> key = {var1, var2};
955 const auto it = var_lt_var_to_literal.find(key);
956 if (it != var_lt_var_to_literal.end()) {
957 AddLinearConstraint({}, Domain(0), {{lit, 1}, {it->second, -1}});
958 ++num_var_op_var_reif_cached;
960 var_lt_var_to_literal[key] = lit;
962 if (VariableIsBoolean(var1) && VariableIsBoolean(var2)) {
964 BoolArgumentProto* is_lt = AddEnforcedConstraint(lit)->mutable_bool_and();
966 is_lt->add_literals(var2);
969 BoolArgumentProto* is_ge =
970 AddEnforcedConstraint(
NegatedRef(lit))->mutable_bool_or();
971 is_ge->add_literals(var1);
974 AddLinearConstraint({lit},
975 Domain(std::numeric_limits<int64_t>::min(), -1),
976 {{var1, 1}, {var2, -1}});
978 Domain(0, std::numeric_limits<int64_t>::max()),
979 {{var1, 1}, {var2, -1}});
984std::optional<int> CpModelProtoWithMapping::GetVarLtVarLiteral(
int var1,
986 CHECK_NE(var1, kNoVar);
987 CHECK_NE(var2, kNoVar);
988 if (var1 == var2)
return LookupConstant(0);
989 const std::pair<int, int> key = {var1, var2};
990 const auto it = var_lt_var_to_literal.find(key);
991 if (it != var_lt_var_to_literal.end())
return it->second;
995int CpModelProtoWithMapping::GetOrCreateLiteralForVarLeVar(
int var1,
int var2) {
996 CHECK_NE(var1, kNoVar);
997 CHECK_NE(var2, kNoVar);
998 if (var1 == var2)
return LookupConstant(1);
999 return NegatedRef(GetOrCreateLiteralForVarLtVar(var2, var1));
1002void CpModelProtoWithMapping::AddVarLeVarLiteral(
int var1,
int var2,
int lit) {
1003 CHECK_NE(var1, kNoVar);
1004 CHECK_NE(var2, kNoVar);
1006 AddImplication({}, lit);
1010 AddVarLtVarLiteral(var2, var1,
NegatedRef(lit));
1013std::optional<int> CpModelProtoWithMapping::GetVarLeVarLiteral(
int var1,
1015 CHECK_NE(var1, kNoVar);
1016 CHECK_NE(var2, kNoVar);
1017 if (var1 == var2)
return LookupConstant(1);
1019 const std::optional<int> lit = GetVarLtVarLiteral(var2, var1);
1020 if (lit.has_value())
return NegatedRef(lit.value());
1021 return std::nullopt;
1024void CpModelProtoWithMapping::AddVarOrVarLiteral(
int var1,
int var2,
int lit) {
1025 CHECK_NE(var1, kNoVar);
1026 CHECK_NE(var2, kNoVar);
1027 CHECK(VariableIsBoolean(var1));
1028 CHECK(VariableIsBoolean(var2));
1030 AddLinearConstraint({}, Domain(0), {{var1, 1}, {lit, -1}});
1033 if (var1 > var2) std::swap(var1, var2);
1035 const std::pair<int, int> key = {var1, var2};
1036 const auto it = var_or_var_to_literal.find(key);
1037 if (it != var_or_var_to_literal.end()) {
1038 AddLinearConstraint({}, Domain(0), {{lit, 1}, {it->second, -1}});
1039 ++num_var_op_var_reif_cached;
1041 AddImplication({var1}, lit);
1042 AddImplication({var2}, lit);
1044 var_or_var_to_literal[key] = lit;
1048void CpModelProtoWithMapping::AddVarAndVarLiteral(
int var1,
int var2,
int lit) {
1049 CHECK_NE(var1, kNoVar);
1050 CHECK_NE(var2, kNoVar);
1051 CHECK(VariableIsBoolean(var1));
1052 CHECK(VariableIsBoolean(var2));
1054 AddLinearConstraint({}, Domain(0), {{var1, 1}, {lit, -1}});
1057 if (var1 > var2) std::swap(var1, var2);
1059 const std::pair<int, int> key = {var1, var2};
1060 const auto it = var_and_var_to_literal.find(key);
1061 if (it != var_and_var_to_literal.end()) {
1062 AddLinearConstraint({}, Domain(0), {{lit, 1}, {it->second, -1}});
1063 ++num_var_op_var_reif_cached;
1065 AddImplication({lit}, var1);
1066 AddImplication({lit}, var2);
1067 AddImplication({var1, var2}, lit);
1068 var_and_var_to_literal[key] = lit;
1072int CpModelProtoWithMapping::GetOrCreateOptionalInterval(VarOrValue start,
1076 const std::tuple<int, int64_t, int, int64_t, int> key =
1077 std::make_tuple(start.var, start.value, size.var, size.value, opt_var);
1078 const auto [it, inserted] =
1079 interval_key_to_index.insert({key, interval_index});
1084 if (size.var == kNoVar || start.var == kNoVar) {
1085 auto* interval = AddEnforcedConstraint(opt_var)->mutable_interval();
1086 if (start.var != kNoVar) {
1087 interval->mutable_start()->add_vars(start.var);
1088 interval->mutable_start()->add_coeffs(1);
1089 interval->mutable_end()->add_vars(start.var);
1090 interval->mutable_end()->add_coeffs(1);
1092 interval->mutable_start()->set_offset(start.value);
1093 interval->mutable_end()->set_offset(start.value);
1096 if (size.var != kNoVar) {
1097 interval->mutable_size()->add_vars(size.var);
1098 interval->mutable_size()->add_coeffs(1);
1099 interval->mutable_end()->add_vars(size.var);
1100 interval->mutable_end()->add_coeffs(1);
1102 interval->mutable_size()->set_offset(size.value);
1103 interval->mutable_end()->set_offset(size.value +
1104 interval->end().offset());
1106 return interval_index;
1115 auto* interval = AddEnforcedConstraint(opt_var)->mutable_interval();
1116 interval->mutable_start()->add_vars(start.var);
1117 interval->mutable_start()->add_coeffs(1);
1118 interval->mutable_size()->add_vars(size.var);
1119 interval->mutable_size()->add_coeffs(1);
1120 interval->mutable_end()->add_vars(end_var);
1121 interval->mutable_end()->add_coeffs(1);
1122 return interval_index;
1126std::vector<int> CpModelProtoWithMapping::CreateIntervals(
1127 absl::Span<const VarOrValue> starts, absl::Span<const VarOrValue> sizes) {
1128 std::vector<int> intervals;
1129 intervals.reserve(starts.size());
1130 for (
int i = 0;
i < starts.size(); ++
i) {
1131 intervals.push_back(
1132 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
1137std::vector<int> CpModelProtoWithMapping::CreateNonZeroOrOptionalIntervals(
1138 absl::Span<const VarOrValue> starts, absl::Span<const VarOrValue> sizes) {
1139 std::vector<int> intervals;
1140 for (
int i = 0;
i < starts.size(); ++
i) {
1141 const int opt_var = NonZeroLiteralFrom(sizes[i]);
1142 intervals.push_back(
1143 GetOrCreateOptionalInterval(starts[i], sizes[i], opt_var));
1148int CpModelProtoWithMapping::NonZeroLiteralFrom(VarOrValue size) {
1149 if (size.var == kNoVar) {
1150 return LookupConstant(size.value > 0);
1152 const auto it = var_to_lit_implies_greater_than_zero.find(size.var);
1153 if (it != var_to_lit_implies_greater_than_zero.end()) {
1157 const IntegerVariableProto& var_proto = proto.
variables(size.var);
1159 CHECK_GE(domain.
Min(), 0);
1160 if (domain.
Min() > 0)
return LookupConstant(1);
1161 if (domain.
Max() == 0) {
1162 return LookupConstant(0);
1165 const int var_greater_than_zero_lit = NewBoolVar();
1167 AddLinearConstraint({var_greater_than_zero_lit},
1168 domain.IntersectionWith({1, domain.
Max()}),
1170 AddLinearConstraint({
NegatedRef(var_greater_than_zero_lit)}, Domain(0),
1173 var_to_lit_implies_greater_than_zero[size.var] = var_greater_than_zero_lit;
1174 return var_greater_than_zero_lit;
1177void CpModelProtoWithMapping::FillAMinusBInDomain(
1178 const std::vector<int64_t>& domain,
const fz::Constraint& fz_ct,
1179 ConstraintProto* ct) {
1180 auto* arg = ct->mutable_linear();
1181 if (fz_ct.arguments[1].type == fz::Argument::INT_VALUE) {
1182 const int64_t value = fz_ct.arguments[1].Value();
1183 const int var_a = LookupVar(fz_ct.arguments[0]);
1184 for (
const int64_t domain_bound : domain) {
1185 if (domain_bound == std::numeric_limits<int64_t>::min() ||
1186 domain_bound == std::numeric_limits<int64_t>::max()) {
1187 arg->add_domain(domain_bound);
1189 arg->add_domain(domain_bound + value);
1192 AddTermToLinearConstraint(var_a, 1, arg);
1193 }
else if (fz_ct.arguments[0].type == fz::Argument::INT_VALUE) {
1194 const int64_t value = fz_ct.arguments[0].Value();
1195 const int var_b = LookupVar(fz_ct.arguments[1]);
1197 if (domain_bound == std::numeric_limits<int64_t>::min()) {
1198 arg->add_domain(std::numeric_limits<int64_t>::max());
1199 }
else if (domain_bound == std::numeric_limits<int64_t>::max()) {
1200 arg->add_domain(std::numeric_limits<int64_t>::min());
1202 arg->add_domain(value - domain_bound);
1205 AddTermToLinearConstraint(var_b, 1, arg);
1207 for (
const int64_t domain_bound : domain) arg->add_domain(domain_bound);
1208 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[0]), 1, arg);
1209 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[1]), -1, arg);
1213void CpModelProtoWithMapping::FillLinearConstraintWithGivenDomain(
1214 absl::Span<const int64_t> domain,
const fz::Constraint& fz_ct,
1215 ConstraintProto* ct) {
1216 auto* arg = ct->mutable_linear();
1217 for (
const int64_t domain_bound : domain) arg->add_domain(domain_bound);
1218 std::vector<int> vars = LookupVars(fz_ct.arguments[1]);
1219 for (
int i = 0;
i < vars.size(); ++
i) {
1220 AddTermToLinearConstraint(vars[i], fz_ct.arguments[0].values[i], arg);
1224void CpModelProtoWithMapping::BuildTableFromDomainIntLinEq(
1225 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1226 const std::vector<int64_t>& coeffs = fz_ct.arguments[0].values;
1227 const std::vector<int> vars = LookupVars(fz_ct.arguments[1]);
1228 const int rhs = fz_ct.arguments[2].Value();
1229 CHECK_EQ(coeffs.back(), -1);
1230 for (
const int var : vars) {
1231 LinearExpressionProto* expr = ct->mutable_table()->add_exprs();
1232 expr->add_vars(var);
1233 expr->add_coeffs(1);
1236 switch (vars.size()) {
1240 for (
const int64_t v0 : domain0.Values()) {
1241 for (
const int64_t v1 : domain1.Values()) {
1242 const int64_t v2 = coeffs[0] * v0 + coeffs[1] * v1 - rhs;
1243 ct->mutable_table()->add_values(v0);
1244 ct->mutable_table()->add_values(v1);
1245 ct->mutable_table()->add_values(v2);
1254 for (
const int64_t v0 : domain0.Values()) {
1255 for (
const int64_t v1 : domain1.Values()) {
1256 for (
const int64_t v2 : domain2.Values()) {
1258 coeffs[0] * v0 + coeffs[1] * v1 + coeffs[2] * v2 - rhs;
1259 ct->mutable_table()->add_values(v0);
1260 ct->mutable_table()->add_values(v1);
1261 ct->mutable_table()->add_values(v2);
1262 ct->mutable_table()->add_values(v3);
1269 LOG(FATAL) <<
"Unsupported size";
1273void CpModelProtoWithMapping::AlwaysFalseConstraint(
const fz::Constraint& fz_ct,
1274 ConstraintProto* ct) {
1276 ct->mutable_bool_or();
1279void CpModelProtoWithMapping::BoolClauseConstraint(
const fz::Constraint& fz_ct,
1280 ConstraintProto* ct) {
1281 auto* arg = ct->mutable_bool_or();
1282 for (
const int var : LookupVars(fz_ct.arguments[0])) {
1283 arg->add_literals(var);
1285 for (
const int var : LookupVars(fz_ct.arguments[1])) {
1289void CpModelProtoWithMapping::BoolXorConstraint(
const fz::Constraint& fz_ct,
1290 ConstraintProto* ct) {
1293 const int a = LookupVar(fz_ct.arguments[0]);
1294 const int b = LookupVar(fz_ct.arguments[1]);
1295 const int x = LookupVar(fz_ct.arguments[2]);
1299 auto*
const refute = ct->mutable_linear();
1301 AddTermToLinearConstraint(a, 1, refute);
1302 AddTermToLinearConstraint(b, -1, refute);
1305 AddLinearConstraint({
x}, Domain(1), {{a, 1}, {
b, 1}});
1307void CpModelProtoWithMapping::ArrayBoolXorConstraint(
1308 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1309 auto* arg = ct->mutable_bool_xor();
1310 for (
const int var : LookupVars(fz_ct.arguments[0])) {
1311 arg->add_literals(var);
1315void CpModelProtoWithMapping::BoolOrIntLeConstraint(
const fz::Constraint& fz_ct,
1316 ConstraintProto* ct) {
1317 FillAMinusBInDomain({std::numeric_limits<int64_t>::min(), 0}, fz_ct, ct);
1320void CpModelProtoWithMapping::BoolOrIntGeConstraint(
const fz::Constraint& fz_ct,
1321 ConstraintProto* ct) {
1322 FillAMinusBInDomain({0, std::numeric_limits<int64_t>::max()}, fz_ct, ct);
1325void CpModelProtoWithMapping::BoolOrIntLtConstraint(
const fz::Constraint& fz_ct,
1326 ConstraintProto* ct) {
1327 FillAMinusBInDomain({std::numeric_limits<int64_t>::min(), -1}, fz_ct, ct);
1330void CpModelProtoWithMapping::BoolOrIntGtConstraint(
const fz::Constraint& fz_ct,
1331 ConstraintProto* ct) {
1332 FillAMinusBInDomain({1, std::numeric_limits<int64_t>::max()}, fz_ct, ct);
1335void CpModelProtoWithMapping::BoolEqConstraint(
const fz::Constraint& fz_ct,
1336 ConstraintProto* ct) {
1337 FillAMinusBInDomain({0, 0}, fz_ct, ct);
1340void CpModelProtoWithMapping::BoolNeConstraint(
const fz::Constraint& fz_ct,
1341 ConstraintProto* ct) {
1342 auto* arg = ct->mutable_linear();
1344 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[0]), 1, arg);
1345 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[1]), 1, arg);
1348void CpModelProtoWithMapping::IntNeConstraint(
const fz::Constraint& fz_ct,
1349 ConstraintProto* ct) {
1350 FillAMinusBInDomain({std::numeric_limits<int64_t>::min(), -1, 1,
1351 std::numeric_limits<int64_t>::max()},
1355void CpModelProtoWithMapping::IntLinEqConstraint(
const fz::Constraint& fz_ct,
1356 ConstraintProto* ct) {
1358 if (fz_ct.strong_propagation && fz_ct.arguments[0].Size() >= 3 &&
1359 fz_ct.arguments[0].Size() <= 4 &&
1360 fz_ct.arguments[0].values.back() == -1) {
1361 BuildTableFromDomainIntLinEq(fz_ct, ct);
1363 const int64_t rhs = fz_ct.arguments[2].values[0];
1364 FillLinearConstraintWithGivenDomain({rhs, rhs}, fz_ct, ct);
1368void CpModelProtoWithMapping::BoolLinEqConstraint(
const fz::Constraint& fz_ct,
1369 ConstraintProto* ct) {
1370 auto* arg = ct->mutable_linear();
1371 const std::vector<int> vars = LookupVars(fz_ct.arguments[1]);
1372 if (fz_ct.arguments[2].IsVariable()) {
1374 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[2]), -1, arg);
1376 const int64_t v = fz_ct.arguments[2].Value();
1379 for (
int i = 0;
i < vars.size(); ++
i) {
1380 AddTermToLinearConstraint(vars[i], fz_ct.arguments[0].values[i], arg);
1384void CpModelProtoWithMapping::BoolOrIntLinLeConstraint(
1385 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1386 const int64_t rhs = fz_ct.arguments[2].values[0];
1387 FillLinearConstraintWithGivenDomain(
1388 {std::numeric_limits<int64_t>::min(), rhs}, fz_ct, ct);
1391void CpModelProtoWithMapping::IntLinLtConstraint(
const fz::Constraint& fz_ct,
1392 ConstraintProto* ct) {
1393 const int64_t rhs = fz_ct.arguments[2].values[0];
1394 FillLinearConstraintWithGivenDomain(
1395 {std::numeric_limits<int64_t>::min(), rhs - 1}, fz_ct, ct);
1398void CpModelProtoWithMapping::IntLinGeConstraint(
const fz::Constraint& fz_ct,
1399 ConstraintProto* ct) {
1400 const int64_t rhs = fz_ct.arguments[2].values[0];
1401 FillLinearConstraintWithGivenDomain(
1402 {rhs, std::numeric_limits<int64_t>::max()}, fz_ct, ct);
1405void CpModelProtoWithMapping::IntLinGtConstraint(
const fz::Constraint& fz_ct,
1406 ConstraintProto* ct) {
1407 const int64_t rhs = fz_ct.arguments[2].values[0];
1408 FillLinearConstraintWithGivenDomain(
1409 {rhs + 1, std::numeric_limits<int64_t>::max()}, fz_ct, ct);
1412void CpModelProtoWithMapping::IntLinNeConstraint(
const fz::Constraint& fz_ct,
1413 ConstraintProto* ct) {
1414 const int64_t rhs = fz_ct.arguments[2].values[0];
1415 FillLinearConstraintWithGivenDomain(
1416 {std::numeric_limits<int64_t>::min(), rhs - 1, rhs + 1,
1417 std::numeric_limits<int64_t>::max()},
1421void CpModelProtoWithMapping::SetCardConstraint(
const fz::Constraint& fz_ct,
1422 ConstraintProto* ct) {
1423 int64_t set_size = 0;
1424 if (fz_ct.arguments[0].type == fz::Argument::INT_LIST) {
1425 set_size = fz_ct.arguments[0].values.size();
1426 }
else if (fz_ct.arguments[0].type == fz::Argument::INT_INTERVAL) {
1427 set_size = fz_ct.arguments[0].values[1] - fz_ct.arguments[0].values[0] + 1;
1429 LOG(FATAL) <<
"Wrong format" << fz_ct.DebugString();
1432 auto* arg = ct->mutable_linear();
1434 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[1]), 1, arg);
1437void CpModelProtoWithMapping::SetInConstraint(
const fz::Constraint& fz_ct,
1438 ConstraintProto* ct) {
1439 auto* arg = ct->mutable_linear();
1440 arg->add_vars(LookupVar(fz_ct.arguments[0]));
1442 if (fz_ct.arguments[1].type == fz::Argument::INT_LIST) {
1444 fz_ct.arguments[1].values.begin(),
1445 fz_ct.arguments[1].values.end()}),
1447 }
else if (fz_ct.arguments[1].type == fz::Argument::INT_INTERVAL) {
1449 fz_ct.arguments[1].values[1], arg);
1451 LOG(FATAL) <<
"Wrong format";
1455void CpModelProtoWithMapping::SetInNegatedConstraint(
1456 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1457 auto* arg = ct->mutable_linear();
1458 if (fz_ct.arguments[1].type == fz::Argument::INT_LIST) {
1460 fz_ct.arguments[1].values.begin(),
1461 fz_ct.arguments[1].values.end()})
1464 }
else if (fz_ct.arguments[1].type == fz::Argument::INT_INTERVAL) {
1466 Domain(fz_ct.arguments[1].values[0], fz_ct.arguments[1].values[1])
1470 LOG(FATAL) <<
"Wrong format";
1472 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[0]), 1, arg);
1475void CpModelProtoWithMapping::IntMinConstraint(
const fz::Constraint& fz_ct,
1476 ConstraintProto* ct) {
1477 auto* arg = ct->mutable_lin_max();
1478 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0],
true);
1479 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1],
true);
1480 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2],
true);
1483void CpModelProtoWithMapping::ArrayIntMinConstraint(
const fz::Constraint& fz_ct,
1484 ConstraintProto* ct) {
1485 auto* arg = ct->mutable_lin_max();
1486 *arg->mutable_target() = LookupExpr(fz_ct.arguments[0],
true);
1487 for (
int i = 0;
i < fz_ct.arguments[1].Size(); ++
i) {
1488 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[1], i,
true);
1492void CpModelProtoWithMapping::IntMaxConstraint(
const fz::Constraint& fz_ct,
1493 ConstraintProto* ct) {
1494 auto* arg = ct->mutable_lin_max();
1495 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
1496 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
1497 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
1500void CpModelProtoWithMapping::ArrayIntMaxConstraint(
const fz::Constraint& fz_ct,
1501 ConstraintProto* ct) {
1502 auto* arg = ct->mutable_lin_max();
1503 *arg->mutable_target() = LookupExpr(fz_ct.arguments[0]);
1504 for (
int i = 0;
i < fz_ct.arguments[1].Size(); ++
i) {
1505 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[1], i);
1509void CpModelProtoWithMapping::IntTimesConstraint(
const fz::Constraint& fz_ct,
1510 ConstraintProto* ct) {
1511 auto* arg = ct->mutable_int_prod();
1512 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
1513 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
1514 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
1517void CpModelProtoWithMapping::IntAbsConstraint(
const fz::Constraint& fz_ct,
1518 ConstraintProto* ct) {
1519 auto* arg = ct->mutable_lin_max();
1520 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
1521 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0],
true);
1522 *arg->mutable_target() = LookupExpr(fz_ct.arguments[1]);
1525void CpModelProtoWithMapping::IntPlusConstraint(
const fz::Constraint& fz_ct,
1526 ConstraintProto* ct) {
1527 auto* arg = ct->mutable_linear();
1529 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[0]), 1, arg);
1530 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[1]), 1, arg);
1531 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[2]), -1, arg);
1534void CpModelProtoWithMapping::IntDivConstraint(
const fz::Constraint& fz_ct,
1535 ConstraintProto* ct) {
1536 auto* arg = ct->mutable_int_div();
1537 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
1538 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
1539 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
1542void CpModelProtoWithMapping::IntModConstraint(
const fz::Constraint& fz_ct,
1543 ConstraintProto* ct) {
1544 auto* arg = ct->mutable_int_mod();
1545 *arg->add_exprs() = LookupExpr(fz_ct.arguments[0]);
1546 *arg->add_exprs() = LookupExpr(fz_ct.arguments[1]);
1547 *arg->mutable_target() = LookupExpr(fz_ct.arguments[2]);
1550void CpModelProtoWithMapping::ArrayElementConstraint(
1551 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1552 auto* arg = ct->mutable_element();
1553 *arg->mutable_linear_index() = LookupExpr(fz_ct.arguments[0]);
1554 if (!absl::EndsWith(fz_ct.type,
"_nonshifted")) {
1555 arg->mutable_linear_index()->set_offset(arg->linear_index().offset() - 1);
1557 *arg->mutable_linear_target() = LookupExpr(fz_ct.arguments[2]);
1559 for (
const VarOrValue elem : LookupVarsOrValues(fz_ct.arguments[1])) {
1560 auto* elem_proto = ct->mutable_element()->add_exprs();
1561 if (elem.var != kNoVar) {
1562 elem_proto->add_vars(elem.var);
1563 elem_proto->add_coeffs(1);
1565 elem_proto->set_offset(elem.value);
1570void CpModelProtoWithMapping::OrToolsConstantElementConstraint(
1571 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1572 auto* arg = ct->mutable_element();
1575 *arg->mutable_linear_index() = LookupExpr(fz_ct.arguments[0]);
1576 const int64_t index_min = fz_ct.arguments[1].values[0];
1577 arg->mutable_linear_index()->set_offset(arg->linear_index().offset() -
1581 *arg->mutable_linear_target() = LookupExpr(fz_ct.arguments[3]);
1584 for (
const int64_t value : fz_ct.arguments[2].values) {
1585 ct->mutable_element()->add_exprs()->set_offset(value);
1589void CpModelProtoWithMapping::OrToolsVariableElementConstraint(
1590 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1591 auto* arg = ct->mutable_element();
1594 *arg->mutable_linear_index() = LookupExpr(fz_ct.arguments[0]);
1595 const int64_t index_min = fz_ct.arguments[1].values[0];
1596 arg->mutable_linear_index()->set_offset(arg->linear_index().offset() -
1600 *arg->mutable_linear_target() = LookupExpr(fz_ct.arguments[3]);
1603 for (
const VarOrValue elem : LookupVarsOrValues(fz_ct.arguments[2])) {
1604 auto* elem_proto = ct->mutable_element()->add_exprs();
1605 if (elem.var != kNoVar) {
1606 elem_proto->add_vars(elem.var);
1607 elem_proto->add_coeffs(1);
1609 elem_proto->set_offset(elem.value);
1614void CpModelProtoWithMapping::OrToolsTableIntConstraint(
1615 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1616 auto* arg = ct->mutable_table();
1617 for (
const VarOrValue v : LookupVarsOrValues(fz_ct.arguments[0])) {
1618 LinearExpressionProto* expr = arg->add_exprs();
1619 if (v.var != kNoVar) {
1620 expr->add_vars(v.var);
1621 expr->add_coeffs(1);
1623 expr->set_offset(v.value);
1626 for (
const int64_t value : fz_ct.arguments[1].values) arg->add_values(value);
1629void CpModelProtoWithMapping::OrToolsRegular(
const fz::Constraint& fz_ct,
1630 ConstraintProto* ct) {
1631 auto* arg = ct->mutable_automaton();
1632 for (
const VarOrValue v : LookupVarsOrValues(fz_ct.arguments[0])) {
1633 LinearExpressionProto* expr = arg->add_exprs();
1634 if (v.var != kNoVar) {
1635 expr->add_vars(v.var);
1636 expr->add_coeffs(1);
1638 expr->set_offset(v.value);
1643 const int num_states = fz_ct.arguments[1].Value();
1644 const int num_values = fz_ct.arguments[2].Value();
1645 for (
int i = 1;
i <= num_states; ++
i) {
1646 for (
int j = 1; j <= num_values; ++j) {
1647 CHECK_LT(count, fz_ct.arguments[3].values.size());
1648 const int next = fz_ct.arguments[3].values[count++];
1649 if (next == 0)
continue;
1650 arg->add_transition_tail(i);
1651 arg->add_transition_label(j);
1652 arg->add_transition_head(next);
1656 arg->set_starting_state(fz_ct.arguments[4].Value());
1657 switch (fz_ct.arguments[5].type) {
1658 case fz::Argument::INT_VALUE: {
1659 arg->add_final_states(fz_ct.arguments[5].values[0]);
1662 case fz::Argument::INT_INTERVAL: {
1663 for (
int v = fz_ct.arguments[5].values[0];
1664 v <= fz_ct.arguments[5].values[1]; ++v) {
1665 arg->add_final_states(v);
1669 case fz::Argument::INT_LIST: {
1670 for (
const int v : fz_ct.arguments[5].values) {
1671 arg->add_final_states(v);
1676 LOG(FATAL) <<
"Wrong constraint " << fz_ct.DebugString();
1681void CpModelProtoWithMapping::OrToolsArgMax(
const fz::Constraint& fz_ct,
1682 ConstraintProto* ct) {
1683 const std::vector<int>
x = LookupVars(fz_ct.arguments[0]);
1684 const int num_vars =
x.size();
1685 const int z = LookupVar(fz_ct.arguments[1]);
1686 const int64_t min_index = fz_ct.arguments[2].Value();
1687 const int64_t multiplier = fz_ct.arguments[3].Value();
1688 DCHECK_EQ(std::abs(multiplier), 1);
1689 int64_t min_range = std::numeric_limits<int64_t>::max();
1690 int64_t max_range = std::numeric_limits<int64_t>::min();
1691 auto* max_array = ct->mutable_lin_max();
1692 for (
int i = 0;
i < num_vars; ++
i) {
1693 const IntegerVariableProto& var_proto = proto.
variables(x[i]);
1694 const int64_t min_var = var_proto.
domain(0);
1695 const int64_t max_var = var_proto.domain(var_proto.domain_size() - 1);
1696 const int64_t offset = num_vars -
i;
1697 int64_t min_expr_range = min_var * multiplier * num_vars + offset;
1698 int64_t max_expr_range = max_var * multiplier * num_vars + offset;
1699 if (multiplier == -1) std::swap(min_expr_range, max_expr_range);
1700 min_range = std::min(min_range, min_expr_range);
1701 max_range = std::max(max_range, max_expr_range);
1702 auto* exprs = max_array->add_exprs();
1703 exprs->add_vars(x[i]);
1704 exprs->add_coeffs(num_vars * multiplier);
1705 exprs->set_offset(offset);
1707 const int max_var = NewIntVar(min_range, max_range);
1713 max_array->mutable_target()->add_vars(max_var);
1714 max_array->mutable_target()->add_coeffs(1);
1716 for (
const auto& [value, literal] : GetFullEncoding(z)) {
1717 if (value < min_index || value >= min_index + num_vars) {
1720 const int64_t index = value - min_index;
1721 AddLinearConstraint({literal}, Domain(index - num_vars),
1722 {{
x[index], num_vars * multiplier}, {max_var, -1}});
1723 AddLinearConstraint(
1725 {std::numeric_limits<int64_t>::min(), index - num_vars - 1},
1726 {{
x[index], num_vars * multiplier}, {max_var, -1}});
1730void CpModelProtoWithMapping::FznAllDifferentInt(
const fz::Constraint& fz_ct,
1731 ConstraintProto* ct) {
1732 auto* arg = ct->mutable_all_diff();
1733 for (
int i = 0;
i < fz_ct.arguments[0].Size(); ++
i) {
1734 *arg->add_exprs() = LookupExprAt(fz_ct.arguments[0], i);
1738void CpModelProtoWithMapping::FznValuePrecedeInt(
const fz::Constraint& fz_ct,
1739 ConstraintProto* ct) {
1740 const int64_t before = fz_ct.arguments[0].Value();
1741 const int64_t after = fz_ct.arguments[1].Value();
1742 const std::vector<int>
x = LookupVars(fz_ct.arguments[2]);
1743 if (
x.empty())
return;
1745 std::vector<int> hold(
x.size());
1746 hold[0] = LookupConstant(0);
1747 for (
int i = 1;
i <
x.size(); ++
i) hold[i] = NewBoolVar();
1749 if (absl::GetFlag(FLAGS_fz_use_light_encoding) && !sat_enumeration_) {
1750 for (
int i = 0;
i <
x.size(); ++
i) {
1751 const int is_before = GetOrCreateEncodingLiteral(x[i], before);
1752 if (i + 1 <
x.size()) {
1753 AddImplication({is_before}, hold[
i + 1]);
1754 AddLinearConstraint({
NegatedRef(is_before)}, Domain(0),
1755 {{hold[
i], 1}, {hold[
i + 1], -1}});
1757 AddLinearConstraint({
NegatedRef(hold[i])}, Domain(after).Complement(),
1760 ++num_light_encodings;
1762 for (
int i = 0;
i <
x.size(); ++
i) {
1763 const int is_before = GetOrCreateEncodingLiteral(x[i], before);
1764 const int is_after = GetOrCreateEncodingLiteral(x[i], after);
1765 if (i + 1 <
x.size()) {
1766 AddImplication({is_before}, hold[
i + 1]);
1767 AddLinearConstraint({
NegatedRef(is_before)}, Domain(0),
1768 {{hold[
i], 1}, {hold[
i + 1], -1}});
1775void CpModelProtoWithMapping::OrToolsCountEqCst(
const fz::Constraint& fz_ct,
1776 ConstraintProto* ct) {
1777 const std::vector<VarOrValue> counts = LookupVarsOrValues(fz_ct.arguments[0]);
1778 const int64_t value = fz_ct.arguments[1].Value();
1779 const VarOrValue target = LookupVarOrValue(fz_ct.arguments[2]);
1780 LinearConstraintProto* arg = ct->mutable_linear();
1781 int64_t fixed_contributions = 0;
1782 for (
const VarOrValue& count : counts) {
1783 if (count.var == kNoVar) {
1784 fixed_contributions += count.value == value ? 1 : 0;
1788 if (target.var == kNoVar) {
1792 AddTermToLinearConstraint(target.var, -1, arg);
1795 for (
const VarOrValue& count : counts) {
1796 if (count.var != kNoVar) {
1797 AddTermToLinearConstraint(GetOrCreateEncodingLiteral(count.var, value), 1,
1803void CpModelProtoWithMapping::OrToolsCountEq(
const fz::Constraint& fz_ct,
1804 ConstraintProto* ct) {
1805 const std::vector<VarOrValue> counts = LookupVarsOrValues(fz_ct.arguments[0]);
1806 const int var = LookupVar(fz_ct.arguments[1]);
1807 const VarOrValue target = LookupVarOrValue(fz_ct.arguments[2]);
1808 LinearConstraintProto* arg = ct->mutable_linear();
1809 if (target.var == kNoVar) {
1813 AddTermToLinearConstraint(target.var, -1, arg);
1815 for (
const VarOrValue& count : counts) {
1816 const int bool_var = count.var == kNoVar
1817 ? GetOrCreateEncodingLiteral(var, count.value)
1818 : GetOrCreateLiteralForVarEqVar(var, count.var);
1819 AddTermToLinearConstraint(bool_var, 1, arg);
1823void CpModelProtoWithMapping::OrToolsCircuit(
const fz::Constraint& fz_ct,
1824 ConstraintProto* ct) {
1825 const int64_t min_index = fz_ct.arguments[1].Value();
1826 const int size = std::max(fz_ct.arguments[0].values.size(),
1827 fz_ct.arguments[0].variables.size());
1829 const int64_t max_index = min_index + size - 1;
1831 auto* circuit_arg = ct->mutable_circuit();
1835 int64_t index = min_index;
1836 const bool is_circuit = (fz_ct.type ==
"ortools_circuit");
1837 for (
const int var : LookupVars(fz_ct.arguments[0])) {
1841 domain = domain.IntersectionWith({min_index, max_index});
1844 domain = domain.IntersectionWith(Domain::FromIntervals(
1845 {{std::numeric_limits<int64_t>::min(), index - 1},
1846 {index + 1, std::numeric_limits<int64_t>::max()}}));
1850 for (
const ClosedInterval interval : domain) {
1851 for (int64_t value = interval.start; value <= interval.end; ++value) {
1860 circuit_arg->add_tails(index);
1861 circuit_arg->add_heads(value);
1862 circuit_arg->add_literals(literal);
1864 AddLinearConstraint({literal}, Domain(value), {{var, 1}});
1865 AddLinearConstraint({
NegatedRef(literal)}, Domain(value).Complement(),
1874void CpModelProtoWithMapping::OrToolsInverse(
const fz::Constraint& fz_ct,
1875 ConstraintProto* ct) {
1876 auto* arg = ct->mutable_inverse();
1878 const auto direct_variables = LookupVars(fz_ct.arguments[0]);
1879 const auto inverse_variables = LookupVars(fz_ct.arguments[1]);
1880 const int base_direct = fz_ct.arguments[2].Value();
1881 const int base_inverse = fz_ct.arguments[3].Value();
1883 CHECK_EQ(direct_variables.size(), inverse_variables.size());
1884 const int num_variables = direct_variables.size();
1885 const int end_direct = base_direct + num_variables;
1886 const int end_inverse = base_inverse + num_variables;
1908 const int arity = std::max(base_inverse, base_direct) + num_variables;
1909 for (
int i = 0;
i < arity; ++
i) {
1911 if (i < base_direct) {
1912 if (i < base_inverse) {
1913 arg->add_f_direct(LookupConstant(i));
1914 }
else if (i >= base_inverse) {
1915 arg->add_f_direct(LookupConstant(i + num_variables));
1917 }
else if (i >= base_direct && i < end_direct) {
1918 arg->add_f_direct(direct_variables[i - base_direct]);
1920 arg->add_f_direct(LookupConstant(i - num_variables));
1924 if (i < base_inverse) {
1925 if (i < base_direct) {
1926 arg->add_f_inverse(LookupConstant(i));
1927 }
else if (i >= base_direct) {
1928 arg->add_f_inverse(LookupConstant(i + num_variables));
1930 }
else if (i >= base_inverse && i < end_inverse) {
1931 arg->add_f_inverse(inverse_variables[i - base_inverse]);
1933 arg->add_f_inverse(LookupConstant(i - num_variables));
1938void CpModelProtoWithMapping::OrToolsLexOrdering(
const fz::Constraint& fz_ct,
1939 ConstraintProto* ct) {
1940 const std::vector<int>
x = LookupVars(fz_ct.arguments[0]);
1941 const std::vector<int> y = LookupVars(fz_ct.arguments[1]);
1942 const bool accepts_equals = fz_ct.type ==
"ortools_lex_lesseq_bool" ||
1943 fz_ct.type ==
"ortools_lex_lesseq_int";
1944 const int false_literal = LookupConstant(0);
1945 AddLexOrdering(x, y,
NegatedRef(false_literal), accepts_equals);
1948void CpModelProtoWithMapping::OrToolsPrecedeChainInt(
1949 const fz::Constraint& fz_ct, ConstraintProto* ct) {
1950 std::vector<int64_t> values;
1951 if (fz_ct.arguments[0].type == fz::Argument::INT_INTERVAL) {
1952 values.reserve(fz_ct.arguments[0].values[1] - fz_ct.arguments[0].values[0] +
1954 for (int64_t value = fz_ct.arguments[0].values[0];
1955 value <= fz_ct.arguments[0].values[1]; ++value) {
1956 values.push_back(value);
1958 }
else if (fz_ct.arguments[0].type == fz::Argument::INT_LIST) {
1959 values = fz_ct.arguments[0].values;
1961 LOG(FATAL) <<
"Unsupported argument type: " << fz_ct.arguments[0].type
1962 <<
" for " << fz_ct.arguments[0].DebugString();
1964 const std::vector<int>
x = LookupVars(fz_ct.arguments[1]);
1966 if (
x.empty())
return;
1967 if (values.size() <= 1)
return;
1969 std::vector<int> row;
1970 for (
int r = 0; r + 1 < values.size(); ++r) {
1972 const int64_t before = values[r];
1973 const int64_t after = values[r + 1];
1974 row.resize(
x.size());
1975 for (
int i = 0;
i <
x.size(); ++
i) {
1976 row[
i] = (
i <= r ? LookupConstant(0) : NewBoolVar());
1979 for (
int i = 0;
i <
x.size(); ++
i) {
1980 const int is_before = GetOrCreateEncodingLiteral(x[i], before);
1981 const int is_after = GetOrCreateEncodingLiteral(x[i], after);
1982 if (i + 1 <
x.size()) {
1983 AddImplication({is_before}, row[
i + 1]);
1984 AddLinearConstraint({
NegatedRef(is_before)}, Domain(0),
1985 {{row[
i], 1}, {row[
i + 1], -1}});
1992void CpModelProtoWithMapping::FznDisjunctive(
const fz::Constraint& fz_ct,
1993 ConstraintProto* ct) {
1994 const std::vector<VarOrValue> starts = LookupVarsOrValues(fz_ct.arguments[0]);
1995 const std::vector<VarOrValue> sizes = LookupVarsOrValues(fz_ct.arguments[1]);
1997 auto* arg = ct->mutable_cumulative();
1998 arg->mutable_capacity()->set_offset(1);
1999 for (
int i = 0;
i < starts.size(); ++
i) {
2001 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
2002 arg->add_demands()->set_offset(1);
2006void CpModelProtoWithMapping::FznDisjunctiveStrict(
const fz::Constraint& fz_ct,
2007 ConstraintProto* ct) {
2008 const std::vector<VarOrValue> starts = LookupVarsOrValues(fz_ct.arguments[0]);
2009 const std::vector<VarOrValue> sizes = LookupVarsOrValues(fz_ct.arguments[1]);
2011 auto* arg = ct->mutable_no_overlap();
2012 for (
int i = 0;
i < starts.size(); ++
i) {
2014 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
2018void CpModelProtoWithMapping::FznCumulative(
const fz::Constraint& fz_ct,
2019 ConstraintProto* ct) {
2020 const std::vector<VarOrValue> starts = LookupVarsOrValues(fz_ct.arguments[0]);
2021 const std::vector<VarOrValue> sizes = LookupVarsOrValues(fz_ct.arguments[1]);
2022 const std::vector<VarOrValue> demands =
2023 LookupVarsOrValues(fz_ct.arguments[2]);
2025 auto* arg = ct->mutable_cumulative();
2026 if (fz_ct.arguments[3].HasOneValue()) {
2027 arg->mutable_capacity()->set_offset(fz_ct.arguments[3].Value());
2029 arg->mutable_capacity()->add_vars(LookupVar(fz_ct.arguments[3]));
2030 arg->mutable_capacity()->add_coeffs(1);
2032 for (
int i = 0;
i < starts.size(); ++
i) {
2035 if (demands[i].var != kNoVar &&
2039 fz_ct.arguments[3].HasOneValue() && fz_ct.arguments[3].Value() == 1) {
2041 GetOrCreateOptionalInterval(starts[i], sizes[i], demands[i].var));
2042 arg->add_demands()->set_offset(1);
2045 GetOrCreateOptionalInterval(starts[i], sizes[i], kNoVar));
2046 LinearExpressionProto* demand = arg->add_demands();
2047 if (demands[i].var == kNoVar) {
2048 demand->set_offset(demands[i].value);
2050 demand->add_vars(demands[i].var);
2051 demand->add_coeffs(1);
2057void CpModelProtoWithMapping::OrToolsCumulativeOpt(
const fz::Constraint& fz_ct,
2058 ConstraintProto* ct) {
2059 const std::vector<int> occurs = LookupVars(fz_ct.arguments[0]);
2060 const std::vector<VarOrValue> starts = LookupVarsOrValues(fz_ct.arguments[1]);
2061 const std::vector<VarOrValue> durations =
2062 LookupVarsOrValues(fz_ct.arguments[2]);
2063 const std::vector<VarOrValue> demands =
2064 LookupVarsOrValues(fz_ct.arguments[3]);
2066 auto* arg = ct->mutable_cumulative();
2067 if (fz_ct.arguments[3].HasOneValue()) {
2068 arg->mutable_capacity()->set_offset(fz_ct.arguments[4].Value());
2070 arg->mutable_capacity()->add_vars(LookupVar(fz_ct.arguments[4]));
2071 arg->mutable_capacity()->add_coeffs(1);
2073 for (
int i = 0;
i < starts.size(); ++
i) {
2075 GetOrCreateOptionalInterval(starts[i], durations[i], occurs[i]));
2076 LinearExpressionProto* demand = arg->add_demands();
2077 if (demands[i].var == kNoVar) {
2078 demand->set_offset(demands[i].value);
2080 demand->add_vars(demands[i].var);
2081 demand->add_coeffs(1);
2086void CpModelProtoWithMapping::OrToolsDisjunctiveStrictOpt(
2087 const fz::Constraint& fz_ct, ConstraintProto* ct) {
2088 const std::vector<int> occurs = LookupVars(fz_ct.arguments[0]);
2089 const std::vector<VarOrValue> starts = LookupVarsOrValues(fz_ct.arguments[1]);
2090 const std::vector<VarOrValue> durations =
2091 LookupVarsOrValues(fz_ct.arguments[2]);
2093 auto* arg = ct->mutable_no_overlap();
2094 for (
int i = 0;
i < starts.size(); ++
i) {
2096 GetOrCreateOptionalInterval(starts[i], durations[i], occurs[i]));
2100void CpModelProtoWithMapping::FznDiffn(
const fz::Constraint& fz_ct,
2101 ConstraintProto* ct) {
2102 const bool is_strict = fz_ct.type ==
"fzn_diffn";
2103 const std::vector<VarOrValue>
x = LookupVarsOrValues(fz_ct.arguments[0]);
2104 const std::vector<VarOrValue> y = LookupVarsOrValues(fz_ct.arguments[1]);
2105 const std::vector<VarOrValue> dx = LookupVarsOrValues(fz_ct.arguments[2]);
2106 const std::vector<VarOrValue> dy = LookupVarsOrValues(fz_ct.arguments[3]);
2107 const std::vector<int> x_intervals =
2108 is_strict ? CreateIntervals(x, dx)
2109 : CreateNonZeroOrOptionalIntervals(
x, dx);
2110 const std::vector<int> y_intervals =
2111 is_strict ? CreateIntervals(y, dy)
2112 : CreateNonZeroOrOptionalIntervals(y, dy);
2113 auto* arg = ct->mutable_no_overlap_2d();
2114 for (
int i = 0;
i <
x.size(); ++
i) {
2115 arg->add_x_intervals(x_intervals[i]);
2116 arg->add_y_intervals(y_intervals[i]);
2120void CpModelProtoWithMapping::OrToolsNetworkFlow(
const fz::Constraint& fz_ct,
2121 ConstraintProto* ct) {
2124 const bool has_cost = fz_ct.type ==
"ortools_network_flow_cost";
2125 const std::vector<int> flow = LookupVars(fz_ct.arguments[3]);
2128 const int num_nodes = fz_ct.arguments[1].values.size();
2129 const int base_node = fz_ct.arguments[2].Value();
2130 std::vector<std::vector<int>> flows_per_node(num_nodes);
2131 std::vector<std::vector<int>> coeffs_per_node(num_nodes);
2133 const int num_arcs = fz_ct.arguments[0].values.size() / 2;
2134 for (
int arc = 0; arc < num_arcs; arc++) {
2135 const int tail = fz_ct.arguments[0].values[2 * arc] - base_node;
2136 const int head = fz_ct.arguments[0].values[2 * arc + 1] - base_node;
2137 if (tail == head)
continue;
2139 flows_per_node[tail].push_back(flow[arc]);
2140 coeffs_per_node[tail].push_back(1);
2141 flows_per_node[head].push_back(flow[arc]);
2142 coeffs_per_node[head].push_back(-1);
2144 for (
int node = 0; node < num_nodes; node++) {
2146 AddLinearConstraint({}, Domain(fz_ct.arguments[1].values[node]));
2147 for (
int i = 0;
i < flows_per_node[node].size(); ++
i) {
2148 AddTermToLinearConstraint(flows_per_node[node][i],
2149 coeffs_per_node[node][i], arg);
2154 auto* arg = AddLinearConstraint({}, Domain(0));
2155 for (
int arc = 0; arc < num_arcs; arc++) {
2156 AddTermToLinearConstraint(flow[arc], fz_ct.arguments[4].values[arc], arg);
2158 AddTermToLinearConstraint(LookupVar(fz_ct.arguments[5]), -1, arg);
2162void CpModelProtoWithMapping::OrToolsBinPacking(
const fz::Constraint& fz_ct,
2163 ConstraintProto* ct) {
2164 const int capacity = fz_ct.arguments[0].Value();
2165 const std::vector<int> positions = LookupVars(fz_ct.arguments[1]);
2166 CHECK_EQ(fz_ct.arguments[2].type, fz::Argument::INT_LIST);
2167 const std::vector<int64_t> weights = fz_ct.arguments[2].values;
2169 std::vector<absl::btree_map<int64_t, int>> bin_encodings;
2170 absl::btree_set<int64_t> all_bin_indices;
2171 bin_encodings.reserve(positions.size());
2172 for (
int p = 0; p < positions.size(); ++p) {
2173 absl::btree_map<int64_t, int> encoding = GetFullEncoding(positions[p]);
2174 for (
const auto& [value, literal] : encoding) {
2175 all_bin_indices.insert(value);
2177 bin_encodings.push_back(std::move(encoding));
2180 for (
const int b : all_bin_indices) {
2181 LinearConstraintProto* lin = AddLinearConstraint({}, {0, capacity});
2182 for (
int p = 0; p < positions.size(); ++p) {
2183 const auto it = bin_encodings[p].find(b);
2184 if (it != bin_encodings[p].
end()) {
2185 AddTermToLinearConstraint(it->second, weights[p], lin);
2191void CpModelProtoWithMapping::OrToolsBinPackingCapa(
const fz::Constraint& fz_ct,
2192 ConstraintProto* ct) {
2193 const std::vector<int64_t>& capacities = fz_ct.arguments[0].values;
2194 const std::vector<int> bins = LookupVars(fz_ct.arguments[1]);
2195 CHECK_EQ(fz_ct.arguments[2].type, fz::Argument::INT_INTERVAL);
2196 const int first_bin = fz_ct.arguments[2].values[0];
2197 const int last_bin = fz_ct.arguments[2].values[1];
2198 CHECK_EQ(fz_ct.arguments[3].type, fz::Argument::INT_LIST);
2199 const std::vector<int64_t> weights = fz_ct.arguments[3].values;
2201 std::vector<absl::btree_map<int64_t, int>> bin_encodings;
2202 bin_encodings.reserve(bins.size());
2203 for (
int bin = 0; bin < bins.size(); ++bin) {
2204 absl::btree_map<int64_t, int> encoding = GetFullEncoding(bins[bin]);
2205 for (
const auto& [value, literal] : encoding) {
2206 if (value < first_bin || value > last_bin) {
2210 bin_encodings.push_back(std::move(encoding));
2213 for (
int b = first_bin;
b <= last_bin; ++
b) {
2214 LinearConstraintProto* lin =
2215 AddLinearConstraint({}, {0, capacities[
b - first_bin]});
2216 for (
int bin = 0; bin < bins.size(); ++bin) {
2217 const auto it = bin_encodings[bin].find(b);
2218 if (it != bin_encodings[bin].
end()) {
2219 AddTermToLinearConstraint(it->second, weights[bin], lin);
2225void CpModelProtoWithMapping::OrToolsBinPackingLoad(
const fz::Constraint& fz_ct,
2226 ConstraintProto* ct) {
2227 const std::vector<int>& loads = LookupVars(fz_ct.arguments[0]);
2228 const std::vector<int> positions = LookupVars(fz_ct.arguments[1]);
2229 if (fz_ct.arguments[3].values.empty()) {
2230 for (
const int load : loads) {
2231 AddLinearConstraint({}, {0, 0}, {{load, 1}});
2235 CHECK_EQ(fz_ct.arguments[2].type, fz::Argument::INT_INTERVAL);
2236 const int first_bin = fz_ct.arguments[2].values[0];
2237 const int last_bin = fz_ct.arguments[2].values[1];
2238 CHECK_EQ(fz_ct.arguments[3].type, fz::Argument::INT_LIST);
2239 const std::vector<int64_t> weights = fz_ct.arguments[3].values;
2240 CHECK_EQ(weights.size(), positions.size());
2241 CHECK_EQ(loads.size(), last_bin - first_bin + 1);
2243 std::vector<absl::btree_map<int64_t, int>> bin_encodings;
2244 bin_encodings.reserve(positions.size());
2245 for (
int p = 0; p < positions.size(); ++p) {
2246 absl::btree_map<int64_t, int> encoding = GetFullEncoding(positions[p]);
2247 for (
const auto& [value, literal] : encoding) {
2248 if (value < first_bin || value > last_bin) {
2252 bin_encodings.push_back(std::move(encoding));
2255 for (
int b = first_bin;
b <= last_bin; ++
b) {
2256 LinearConstraintProto* lin = AddLinearConstraint({}, Domain(0));
2257 AddTermToLinearConstraint(loads[b - first_bin], -1, lin);
2258 for (
int p = 0; p < positions.size(); ++p) {
2259 const auto it = bin_encodings[p].find(b);
2260 if (it != bin_encodings[p].
end()) {
2261 AddTermToLinearConstraint(it->second, weights[p], lin);
2267 int64_t total_load = 0;
2268 for (int64_t weight : weights) {
2269 total_load += weight;
2271 LinearConstraintProto* lin = AddLinearConstraint({}, Domain(total_load));
2272 for (
int i = 0;
i < loads.size(); ++
i) {
2273 AddTermToLinearConstraint(loads[i], 1, lin);
2277void CpModelProtoWithMapping::OrToolsNvalue(
const fz::Constraint& fz_ct,
2278 ConstraintProto* ct) {
2279 const int card = LookupVar(fz_ct.arguments[0]);
2280 const std::vector<int>&
x = LookupVars(fz_ct.arguments[1]);
2282 LinearConstraintProto* global_cardinality = ct->mutable_linear();
2285 absl::btree_map<int64_t, std::vector<int>> value_to_literals;
2286 for (
int i = 0;
i <
x.size(); ++
i) {
2287 const absl::btree_map<int64_t, int> encoding = GetFullEncoding(x[i]);
2288 for (
const auto& [value, literal] : encoding) {
2289 value_to_literals[value].push_back(literal);
2290 AddTermToLinearConstraint(literal, 1, global_cardinality);
2295 const int64_t max_size = std::min(
x.size(), value_to_literals.size());
2296 AddLinearConstraint({}, {1, max_size}, {{card, 1}});
2298 LinearConstraintProto* lin = AddLinearConstraint({}, Domain(0), {{card, -1}});
2299 for (
const auto& [value, literals] : value_to_literals) {
2300 const int is_present = NewBoolVar();
2301 AddTermToLinearConstraint(is_present, 1, lin);
2303 BoolArgumentProto* is_present_constraint =
2304 AddEnforcedConstraint(is_present)->mutable_bool_or();
2305 for (
const int literal : literals) {
2306 AddImplication({literal}, is_present);
2307 is_present_constraint->add_literals(literal);
2312void CpModelProtoWithMapping::OrToolsGlobalCardinality(
2313 const fz::Constraint& fz_ct, ConstraintProto* ct) {
2314 const std::vector<int>
x = LookupVars(fz_ct.arguments[0]);
2315 CHECK_EQ(fz_ct.arguments[1].type, fz::Argument::INT_LIST);
2316 const std::vector<int64_t>& values = fz_ct.arguments[1].values;
2317 const std::vector<int> cards = LookupVars(fz_ct.arguments[2]);
2318 const bool is_closed = fz_ct.arguments[3].Value() != 0;
2320 const absl::flat_hash_set<int64_t> all_values(values.begin(), values.end());
2321 absl::flat_hash_map<int64_t, std::vector<int>> value_to_literals;
2322 bool exact_cover =
true;
2324 for (
const int x_var : x) {
2325 const absl::btree_map<int64_t, int> encoding = GetFullEncoding(x_var);
2326 for (
const auto& [value, literal] : encoding) {
2327 if (all_values.contains(value)) {
2328 value_to_literals[value].push_back(literal);
2329 }
else if (is_closed) {
2332 exact_cover =
false;
2337 for (
int i = 0;
i < cards.size(); ++
i) {
2338 const int64_t value = values[
i];
2339 const int card = cards[
i];
2340 LinearConstraintProto* lin =
2341 AddLinearConstraint({}, Domain(0), {{card, -1}});
2342 for (
const int literal : value_to_literals[value]) {
2343 AddTermToLinearConstraint(literal, 1, lin);
2348 LinearConstraintProto* cover = AddLinearConstraint({}, Domain(
x.size()));
2349 for (
const int card : cards) {
2350 AddTermToLinearConstraint(card, 1, cover);
2355void CpModelProtoWithMapping::OrToolsGlobalCardinalityLowUp(
2356 const fz::Constraint& fz_ct, ConstraintProto* ct) {
2357 const std::vector<int>
x = LookupVars(fz_ct.arguments[0]);
2358 CHECK(fz_ct.arguments[1].type == fz::Argument::INT_LIST ||
2359 fz_ct.arguments[1].type == fz::Argument::VOID_ARGUMENT);
2360 const std::vector<int64_t>& values = fz_ct.arguments[1].values;
2361 absl::Span<const int64_t> lbs = fz_ct.arguments[2].values;
2362 absl::Span<const int64_t> ubs = fz_ct.arguments[3].values;
2363 const bool is_closed = fz_ct.arguments[4].Value() != 0;
2365 const absl::flat_hash_set<int64_t> all_values(values.begin(), values.end());
2366 absl::flat_hash_map<int64_t, std::vector<int>> value_to_literals;
2367 bool exact_cover =
true;
2369 for (
const int x_var : x) {
2370 const absl::btree_map<int64_t, int> encoding = GetFullEncoding(x_var);
2371 for (
const auto& [value, literal] : encoding) {
2372 if (all_values.contains(value)) {
2373 value_to_literals[value].push_back(literal);
2374 }
else if (is_closed) {
2377 exact_cover =
false;
2384 const int64_t sum_lbs = absl::c_accumulate(lbs, 0);
2385 const int64_t sum_ubs = absl::c_accumulate(ubs, 0);
2386 if (exact_cover && sum_lbs ==
x.size()) {
2388 }
else if (exact_cover && sum_ubs ==
x.size()) {
2392 for (
int i = 0;
i < values.size(); ++
i) {
2393 LinearConstraintProto* lin = AddLinearConstraint({}, {lbs[
i], ubs[
i]});
2394 for (
const int literal : value_to_literals[values[i]]) {
2395 AddTermToLinearConstraint(literal, 1, lin);
2400using ConstraintToMethodMapType =
2401 absl::flat_hash_map<std::string_view,
2402 void (CpModelProtoWithMapping::*)(
const fz::Constraint&,
2405const ConstraintToMethodMapType& GetConstraintMap() {
2406 using MPMap = CpModelProtoWithMapping;
2407 static const absl::NoDestructor<ConstraintToMethodMapType> kConstraintMap({
2408 {
"false_constraint", &MPMap::AlwaysFalseConstraint},
2409 {
"bool_clause", &MPMap::BoolClauseConstraint},
2410 {
"bool_xor", &MPMap::BoolXorConstraint},
2411 {
"array_bool_xor", &MPMap::ArrayBoolXorConstraint},
2412 {
"bool_le", &MPMap::BoolOrIntLeConstraint},
2413 {
"int_le", &MPMap::BoolOrIntLeConstraint},
2414 {
"bool_ge", &MPMap::BoolOrIntGeConstraint},
2415 {
"int_ge", &MPMap::BoolOrIntGeConstraint},
2416 {
"bool_lt", &MPMap::BoolOrIntLtConstraint},
2417 {
"int_lt", &MPMap::BoolOrIntLtConstraint},
2418 {
"bool_gt", &MPMap::BoolOrIntGtConstraint},
2419 {
"int_gt", &MPMap::BoolOrIntGtConstraint},
2420 {
"bool_eq", &MPMap::BoolEqConstraint},
2421 {
"int_eq", &MPMap::BoolEqConstraint},
2422 {
"bool2int", &MPMap::BoolEqConstraint},
2423 {
"bool_ne", &MPMap::BoolNeConstraint},
2424 {
"bool_not", &MPMap::BoolNeConstraint},
2425 {
"int_ne", &MPMap::IntNeConstraint},
2426 {
"int_lin_eq", &MPMap::IntLinEqConstraint},
2427 {
"bool_lin_eq", &MPMap::BoolLinEqConstraint},
2428 {
"int_lin_le", &MPMap::BoolOrIntLinLeConstraint},
2429 {
"bool_lin_le", &MPMap::BoolOrIntLinLeConstraint},
2430 {
"int_lin_lt", &MPMap::IntLinLtConstraint},
2431 {
"int_lin_ge", &MPMap::IntLinGeConstraint},
2432 {
"int_lin_gt", &MPMap::IntLinGtConstraint},
2433 {
"int_lin_ne", &MPMap::IntLinNeConstraint},
2434 {
"set_card", &MPMap::SetCardConstraint},
2435 {
"set_in", &MPMap::SetInConstraint},
2436 {
"set_in_negated", &MPMap::SetInNegatedConstraint},
2437 {
"int_min", &MPMap::IntMinConstraint},
2438 {
"array_int_minimum", &MPMap::ArrayIntMinConstraint},
2439 {
"minimum_int", &MPMap::ArrayIntMinConstraint},
2440 {
"int_max", &MPMap::IntMaxConstraint},
2441 {
"array_int_maximum", &MPMap::ArrayIntMaxConstraint},
2442 {
"maximum_int", &MPMap::ArrayIntMaxConstraint},
2443 {
"int_times", &MPMap::IntTimesConstraint},
2444 {
"int_abs", &MPMap::IntAbsConstraint},
2445 {
"int_plus", &MPMap::IntPlusConstraint},
2446 {
"int_div", &MPMap::IntDivConstraint},
2447 {
"int_mod", &MPMap::IntModConstraint},
2448 {
"array_int_element", &MPMap::ArrayElementConstraint},
2449 {
"array_bool_element", &MPMap::ArrayElementConstraint},
2450 {
"array_var_int_element", &MPMap::ArrayElementConstraint},
2451 {
"array_var_bool_element", &MPMap::ArrayElementConstraint},
2452 {
"array_int_element_nonshifted", &MPMap::ArrayElementConstraint},
2453 {
"ortools_array_int_element", &MPMap::OrToolsConstantElementConstraint},
2454 {
"ortools_array_bool_element", &MPMap::OrToolsConstantElementConstraint},
2455 {
"ortools_array_var_int_element",
2456 &MPMap::OrToolsVariableElementConstraint},
2457 {
"ortools_array_var_bool_element",
2458 &MPMap::OrToolsVariableElementConstraint},
2459 {
"ortools_table_bool", &MPMap::OrToolsTableIntConstraint},
2460 {
"ortools_table_int", &MPMap::OrToolsTableIntConstraint},
2461 {
"ortools_regular", &MPMap::OrToolsRegular},
2462 {
"ortools_arg_max_int", &MPMap::OrToolsArgMax},
2463 {
"ortools_arg_max_bool", &MPMap::OrToolsArgMax},
2464 {
"fzn_all_different_int", &MPMap::FznAllDifferentInt},
2465 {
"fzn_value_precede_int", &MPMap::FznValuePrecedeInt},
2466 {
"ortools_count_eq_cst", &MPMap::OrToolsCountEqCst},
2467 {
"ortools_count_eq", &MPMap::OrToolsCountEq},
2468 {
"ortools_circuit", &MPMap::OrToolsCircuit},
2469 {
"ortools_subcircuit", &MPMap::OrToolsCircuit},
2470 {
"ortools_inverse", &MPMap::OrToolsInverse},
2471 {
"ortools_lex_less_int", &MPMap::OrToolsLexOrdering},
2472 {
"ortools_lex_less_bool", &MPMap::OrToolsLexOrdering},
2473 {
"ortools_lex_lesseq_int", &MPMap::OrToolsLexOrdering},
2474 {
"ortools_lex_lesseq_bool", &MPMap::OrToolsLexOrdering},
2475 {
"ortools_precede_chain_int", &MPMap::OrToolsPrecedeChainInt},
2476 {
"fzn_disjunctive", &MPMap::FznDisjunctive},
2477 {
"fzn_disjunctive_strict", &MPMap::FznDisjunctiveStrict},
2478 {
"fzn_cumulative", &MPMap::FznCumulative},
2479 {
"ortools_cumulative_opt", &MPMap::OrToolsCumulativeOpt},
2480 {
"ortools_disjunctive_strict_opt", &MPMap::OrToolsDisjunctiveStrictOpt},
2481 {
"fzn_diffn", &MPMap::FznDiffn},
2482 {
"fzn_diffn_nonstrict", &MPMap::FznDiffn},
2483 {
"ortools_network_flow", &MPMap::OrToolsNetworkFlow},
2484 {
"ortools_network_flow_cost", &MPMap::OrToolsNetworkFlow},
2485 {
"ortools_bin_packing", &MPMap::OrToolsBinPacking},
2486 {
"ortools_bin_packing_capa", &MPMap::OrToolsBinPackingCapa},
2487 {
"ortools_bin_packing_load", &MPMap::OrToolsBinPackingLoad},
2488 {
"ortools_nvalue", &MPMap::OrToolsNvalue},
2489 {
"ortools_global_cardinality", &MPMap::OrToolsGlobalCardinality},
2490 {
"ortools_global_cardinality_low_up",
2491 &MPMap::OrToolsGlobalCardinalityLowUp},
2493 return *kConstraintMap;
2496void CpModelProtoWithMapping::FillConstraint(
const fz::Constraint& fz_ct,
2497 ConstraintProto* ct) {
2498 auto it = GetConstraintMap().find(fz_ct.type);
2499 if (it != GetConstraintMap().
end()) {
2500 auto method_ptr = it->second;
2501 (this->*method_ptr)(fz_ct, ct);
2503 LOG(FATAL) <<
" Not supported " << fz_ct.type;
2507void CpModelProtoWithMapping::PutSetBooleansInCommonDomain(
2508 absl::Span<
const std::shared_ptr<SetVariable>> set_vars,
2509 absl::Span<std::vector<int>*
const> var_booleans) {
2510 CHECK_EQ(set_vars.size(), var_booleans.size());
2511 std::vector<int64_t> all_values;
2512 for (
const std::shared_ptr<SetVariable>& set_var : set_vars) {
2513 all_values.insert(all_values.end(), set_var->sorted_values.begin(),
2514 set_var->sorted_values.end());
2517 absl::flat_hash_map<int64_t, int> value_to_index;
2518 for (
int i = 0;
i < all_values.size(); ++
i) {
2519 value_to_index[all_values[
i]] =
i;
2521 const int false_literal = LookupConstant(0);
2522 for (
int set_var_index = 0; set_var_index < var_booleans.size();
2524 std::vector<int>* literals = var_booleans[set_var_index];
2525 std::shared_ptr<SetVariable> set_var = set_vars[set_var_index];
2526 *literals = std::vector<int>(all_values.size(), false_literal);
2527 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
2528 (*literals)[value_to_index[set_var->sorted_values[
i]]] =
2529 set_var->var_indices[
i];
2534void CpModelProtoWithMapping::FirstPass(
const fz::Constraint& fz_ct) {
2535 if (fz_ct.type ==
"set_card") {
2536 if (!fz_ct.arguments[0].IsVariable() ||
2537 !fz_ct.arguments[0].Var()->domain.is_a_set ||
2538 !fz_ct.arguments[1].HasOneValue()) {
2541 std::shared_ptr<SetVariable> set_var = LookupSetVar(fz_ct.arguments[0]);
2542 const int64_t fixed_card = fz_ct.arguments[1].Value();
2543 set_var->fixed_card = fixed_card;
2547void CpModelProtoWithMapping::ArraySetElementConstraint(
2548 const fz::Constraint& fz_ct) {
2549 const int index = LookupVar(fz_ct.arguments[0]);
2551 const std::shared_ptr<SetVariable> target_var =
2552 LookupSetVar(fz_ct.arguments[2]);
2553 const absl::flat_hash_set<int64_t> target_values(
2554 target_var->sorted_values.begin(), target_var->sorted_values.end());
2555 absl::flat_hash_map<int64_t, std::vector<int64_t>> value_to_supports;
2556 const int64_t min_index = index_domain.
Min();
2557 for (
const int64_t value : index_domain.Values()) {
2558 const int64_t offset = value - min_index;
2559 bool index_is_valid =
true;
2560 const fz::Domain& set_domain = fz_ct.arguments[1].domains[offset];
2561 if (set_domain.is_interval) {
2562 for (int64_t v = set_domain.values[0]; v <= set_domain.values[1]; ++v) {
2563 if (!target_values.contains(v)) {
2564 index_is_valid =
false;
2568 if (!index_is_valid)
continue;
2570 for (int64_t v = set_domain.values[0]; v <= set_domain.values[1]; ++v) {
2571 value_to_supports[v].push_back(value);
2574 for (
const int64_t v : set_domain.values) {
2575 if (!target_values.contains(v)) {
2576 index_is_valid =
false;
2580 if (!index_is_valid)
continue;
2582 for (
const int64_t v : set_domain.values) {
2583 value_to_supports[v].push_back(value);
2588 for (
int i = 0;
i < target_var->sorted_values.size(); ++
i) {
2589 const int64_t target_value = target_var->sorted_values[
i];
2590 const int target_literal = target_var->var_indices[
i];
2591 const auto it = value_to_supports.find(target_value);
2592 if (it == value_to_supports.end()) {
2595 }
else if (it->second.size() == index_domain.Size()) {
2598 const Domain true_indices = Domain::FromValues(it->second);
2599 AddLinearConstraint({target_literal}, true_indices, {{index, 1}});
2600 AddLinearConstraint({
NegatedRef(target_literal)},
2601 true_indices.Complement(), {{index, 1}});
2606void CpModelProtoWithMapping::ArrayVarSetElementConstraint(
2607 const fz::Constraint& fz_ct) {
2608 const int index = LookupVar(fz_ct.arguments[0]);
2610 const int64_t min_index = index_domain.
Min();
2612 const std::shared_ptr<SetVariable> target_var =
2613 LookupSetVar(fz_ct.arguments[2]);
2614 absl::btree_map<int64_t, int> target_values_to_literals;
2615 for (
int i = 0;
i < target_var->sorted_values.size(); ++
i) {
2616 target_values_to_literals[target_var->sorted_values[
i]] =
2617 target_var->var_indices[
i];
2620 BoolArgumentProto* exactly_one =
2622 for (
const int64_t value : index_domain.Values()) {
2623 const int index_literal = GetOrCreateEncodingLiteral(index, value);
2624 exactly_one->add_literals(index_literal);
2626 std::shared_ptr<SetVariable> set_var =
2627 LookupSetVarAt(fz_ct.arguments[1], value - min_index);
2628 absl::btree_map<int64_t, int> set_values_to_literals;
2629 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
2630 set_values_to_literals[set_var->sorted_values[
i]] =
2631 set_var->var_indices[
i];
2634 for (
const auto& [value, set_literal] : set_values_to_literals) {
2635 const auto it = target_values_to_literals.find(value);
2636 if (it == target_values_to_literals.end()) {
2638 AddImplication({index_literal},
NegatedRef(set_literal));
2641 AddImplication({index_literal, set_literal}, it->second);
2642 AddImplication({index_literal, it->second}, set_literal);
2647 for (
const auto& [value, target_literal] : target_values_to_literals) {
2648 if (!set_values_to_literals.contains(value)) {
2649 AddImplication({index_literal},
NegatedRef(target_literal));
2655void CpModelProtoWithMapping::FznAllDifferentSetConstraint(
2656 const fz::Constraint& fz_ct) {
2657 const int num_vars = fz_ct.arguments[0].Size();
2658 if (num_vars == 0)
return;
2660 std::vector<std::shared_ptr<SetVariable>> set_vars;
2661 set_vars.reserve(num_vars);
2662 for (
int i = 0;
i < num_vars; ++
i) {
2663 set_vars.push_back(LookupSetVarAt(fz_ct.arguments[0], i));
2665 std::vector<std::vector<int>> var_booleans(num_vars);
2666 std::vector<std::vector<int>*> var_booleans_ptrs(num_vars);
2667 for (
int i = 0;
i < set_vars.size(); ++
i) {
2668 var_booleans_ptrs[
i] = &var_booleans[
i];
2670 PutSetBooleansInCommonDomain(set_vars, var_booleans_ptrs);
2671 for (
int i = 0;
i + 1 < num_vars; ++
i) {
2672 for (
int j = i + 1; j < num_vars; ++j) {
2673 AddLiteralVectorsNotEqual(var_booleans[i], var_booleans[j]);
2678void CpModelProtoWithMapping::FznAllDisjointConstraint(
2679 const fz::Constraint& fz_ct) {
2680 const int num_vars = fz_ct.arguments[0].Size();
2681 if (num_vars == 0)
return;
2683 absl::btree_map<int64_t, std::vector<int>> value_to_candidates;
2684 for (
int i = 0;
i < num_vars; ++
i) {
2685 const std::shared_ptr<SetVariable> set_var =
2686 LookupSetVarAt(fz_ct.arguments[0], i);
2687 for (
int j = 0; j < set_var->sorted_values.size(); ++j) {
2688 const int64_t value = set_var->sorted_values[j];
2689 const int literal = set_var->var_indices[j];
2690 value_to_candidates[value].push_back(literal);
2693 for (
const auto& [value, candidates] : value_to_candidates) {
2695 for (
const int literal : candidates) {
2696 amo->add_literals(literal);
2701void CpModelProtoWithMapping::FznDisjointConstraint(
2702 const fz::Constraint& fz_ct) {
2703 std::vector<int> x_literals, y_literals;
2704 PutSetBooleansInCommonDomain(
2705 {LookupSetVar(fz_ct.arguments[0]), LookupSetVar(fz_ct.arguments[1])},
2706 {&x_literals, &y_literals});
2707 for (
int i = 0;
i < x_literals.size(); ++
i) {
2708 BoolArgumentProto* at_least_one_false =
2711 at_least_one_false->add_literals(
NegatedRef(y_literals[i]));
2715void CpModelProtoWithMapping::FznPartitionSetConstraint(
2716 const fz::Constraint& fz_ct) {
2717 const int num_vars = fz_ct.arguments[0].Size();
2718 if (num_vars == 0)
return;
2720 absl::flat_hash_set<int64_t> universe;
2721 if (fz_ct.arguments[1].type == fz::Argument::INT_INTERVAL) {
2722 for (int64_t v = fz_ct.arguments[1].values[0];
2723 v <= fz_ct.arguments[1].values[1]; ++v) {
2727 CHECK_EQ(fz_ct.arguments[1].type, fz::Argument::INT_LIST);
2728 universe = absl::flat_hash_set<int64_t>(fz_ct.arguments[1].values.begin(),
2729 fz_ct.arguments[1].values.end());
2732 absl::btree_map<int64_t, std::vector<int>> value_to_candidates;
2733 for (
int i = 0;
i < num_vars; ++
i) {
2734 const std::shared_ptr<SetVariable> set_var =
2735 LookupSetVarAt(fz_ct.arguments[0], i);
2736 for (
int j = 0; j < set_var->sorted_values.size(); ++j) {
2737 const int64_t value = set_var->sorted_values[j];
2738 const int literal = set_var->var_indices[j];
2739 if (!universe.contains(value)) {
2742 value_to_candidates[value].push_back(literal);
2746 for (
const auto& [value, candidates] : value_to_candidates) {
2748 for (
const int literal : candidates) {
2749 ex1->add_literals(literal);
2754void CpModelProtoWithMapping::SetCardConstraint(
const fz::Constraint& fz_ct) {
2755 std::shared_ptr<SetVariable> set_var = LookupSetVar(fz_ct.arguments[0]);
2756 VarOrValue var_or_value = LookupVarOrValue(fz_ct.arguments[1]);
2757 if (set_var->card_var_index == kNoVar) {
2759 if (var_or_value.var == kNoVar) {
2760 set_var->card_var_index = LookupConstant(var_or_value.value);
2763 set_var->card_var_index = var_or_value.var;
2765 AddTermToLinearConstraint(var_or_value.var, -1, ct->mutable_linear());
2767 for (
const int bool_var : set_var->var_indices) {
2768 AddTermToLinearConstraint(bool_var, 1, ct->mutable_linear());
2771 if (var_or_value.var == kNoVar) {
2772 AddLinearConstraint({}, Domain(var_or_value.value),
2773 {{set_var->card_var_index, 1}});
2775 AddLinearConstraint(
2777 {{set_var->card_var_index, 1}, {var_or_value.var, -1}});
2782void CpModelProtoWithMapping::SetInConstraint(
const fz::Constraint& fz_ct) {
2783 const VarOrValue var_or_value = LookupVarOrValue(fz_ct.arguments[0]);
2784 std::shared_ptr<SetVariable> set_var = LookupSetVar(fz_ct.arguments[1]);
2785 if (var_or_value.var == kNoVar) {
2786 const int index = set_var->find_value_index(var_or_value.value);
2788 CHECK_NE(index, -1);
2789 AddImplication({}, set_var->var_indices[index]);
2790 }
else if (set_var->fixed_card.has_value() &&
2791 set_var->fixed_card.value() == 1) {
2796 absl::flat_hash_map<int64_t, int> set_value_to_literal;
2797 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
2798 set_value_to_literal[set_var->sorted_values[
i]] = set_var->var_indices[
i];
2802 AddLinearConstraint({}, Domain::FromValues(set_var->sorted_values),
2803 {{var_or_value.var, 1}});
2805 absl::flat_hash_set<int> var_values;
2806 const Domain var_domain =
2808 for (
const int64_t value : var_domain.Values()) {
2809 const auto it = set_value_to_literal.find(value);
2811 if (it == set_value_to_literal.end())
continue;
2812 AddVarEqValueLiteral(var_or_value.var, value, it->second);
2813 var_values.insert(value);
2817 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
2818 if (!var_values.contains(set_var->sorted_values[i])) {
2819 AddImplication({},
NegatedRef(set_var->var_indices[i]));
2828 AddLinearConstraint({}, Domain::FromValues(set_var->sorted_values),
2829 {{var_or_value.var, 1}});
2832 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
2833 AddLinearConstraint({
NegatedRef(set_var->var_indices[i])},
2834 {Domain(set_var->sorted_values[i]).Complement()},
2835 {{var_or_value.var, 1}});
2840void CpModelProtoWithMapping::SetInReifConstraint(
const fz::Constraint& fz_ct) {
2841 VarOrValue var_or_value = LookupVarOrValue(fz_ct.arguments[0]);
2842 std::shared_ptr<const SetVariable> set_var = LookupSetVar(fz_ct.arguments[1]);
2843 const int enforcement_literal = LookupVar(fz_ct.arguments[2]);
2844 if (var_or_value.var == kNoVar) {
2845 const int index = set_var->find_value_index(var_or_value.value);
2847 AddImplication({},
NegatedRef(enforcement_literal));
2849 AddLinearConstraint(
2851 {{set_var->var_indices[index], 1}, {enforcement_literal, -1}});
2855 Domain set_domain = Domain::FromValues(set_var->sorted_values);
2856 AddLinearConstraint({enforcement_literal}, set_domain,
2857 {{var_or_value.var, 1}});
2862 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
2863 const int64_t value = set_var->sorted_values[
i];
2864 const int set_value_literal = set_var->var_indices[
i];
2866 const int not_var_value_literal =
2867 NegatedRef(GetOrCreateEncodingLiteral(var_or_value.var, value));
2869 AddImplication({enforcement_literal,
NegatedRef(set_value_literal)},
2870 not_var_value_literal);
2871 AddImplication({
NegatedRef(enforcement_literal), set_value_literal},
2872 not_var_value_literal);
2877void CpModelProtoWithMapping::SetOpConstraint(
const fz::Constraint& fz_ct) {
2878 std::vector<int> x_literals, y_literals, r_literals;
2879 PutSetBooleansInCommonDomain(
2880 {LookupSetVar(fz_ct.arguments[0]), LookupSetVar(fz_ct.arguments[1]),
2881 LookupSetVar(fz_ct.arguments[2])},
2882 {&x_literals, &y_literals, &r_literals});
2883 if (fz_ct.type ==
"set_intersect") {
2884 for (
int i = 0;
i < x_literals.size(); ++
i) {
2885 AddVarAndVarLiteral(x_literals[i], y_literals[i], r_literals[i]);
2887 }
else if (fz_ct.type ==
"set_union") {
2888 for (
int i = 0;
i < x_literals.size(); ++
i) {
2889 AddVarOrVarLiteral(x_literals[i], y_literals[i], r_literals[i]);
2891 }
else if (fz_ct.type ==
"set_symdiff") {
2892 for (
int i = 0;
i < x_literals.size(); ++
i) {
2893 AddVarEqVarLiteral(x_literals[i], y_literals[i],
2896 }
else if (fz_ct.type ==
"set_diff") {
2897 for (
int i = 0;
i < x_literals.size(); ++
i) {
2898 AddVarLtVarLiteral(y_literals[i], x_literals[i], r_literals[i]);
2903void CpModelProtoWithMapping::SetSubSupersetEqConstraint(
2904 const fz::Constraint& fz_ct) {
2905 std::vector<int> x_literals, y_literals;
2906 PutSetBooleansInCommonDomain(
2907 {LookupSetVar(fz_ct.arguments[0]), LookupSetVar(fz_ct.arguments[1])},
2908 {&x_literals, &y_literals});
2911 for (
int i = 0;
i < x_literals.size(); ++
i) {
2912 const int x_lit = x_literals[
i];
2913 const int y_lit = y_literals[
i];
2914 if (fz_ct.type ==
"set_subset") {
2915 AddImplication({x_lit}, y_lit);
2916 }
else if (fz_ct.type ==
"set_superset") {
2917 AddImplication({y_lit}, x_lit);
2918 }
else if (fz_ct.type ==
"set_eq") {
2920 AddLinearConstraint({}, Domain(0), {{x_lit, 1}, {y_lit, -1}});
2922 LOG(FATAL) <<
"Unsupported " << fz_ct.type;
2927void CpModelProtoWithMapping::SetNeConstraint(
const fz::Constraint& fz_ct) {
2928 std::vector<int> x_literals, y_literals;
2929 PutSetBooleansInCommonDomain(
2930 {LookupSetVar(fz_ct.arguments[0]), LookupSetVar(fz_ct.arguments[1])},
2931 {&x_literals, &y_literals});
2932 AddLiteralVectorsNotEqual(x_literals, y_literals);
2935void CpModelProtoWithMapping::SetSubSupersetEqReifConstraint(
2936 const fz::Constraint& fz_ct) {
2937 std::vector<int> x_literals, y_literals;
2938 PutSetBooleansInCommonDomain(
2939 {LookupSetVar(fz_ct.arguments[0]), LookupSetVar(fz_ct.arguments[1])},
2940 {&x_literals, &y_literals});
2941 const int enforcement_literal = LookupVar(fz_ct.arguments[2]);
2942 const int num_values = x_literals.size();
2944 int e = enforcement_literal;
2945 if (fz_ct.type ==
"set_ne_reif") {
2948 BoolArgumentProto* all_true = AddEnforcedConstraint(e)->mutable_bool_and();
2949 BoolArgumentProto* one_false =
2950 AddEnforcedConstraint(
NegatedRef(e))->mutable_bool_or();
2951 for (
int i = 0;
i < num_values; ++
i) {
2953 if (fz_ct.type ==
"set_eq_reif" || fz_ct.type ==
"set_ne_reif") {
2954 lit = GetOrCreateLiteralForVarEqVar(x_literals[i], y_literals[i]);
2955 }
else if (fz_ct.type ==
"set_subset_reif") {
2956 lit = GetOrCreateLiteralForVarLeVar(x_literals[i], y_literals[i]);
2958 DCHECK_EQ(fz_ct.type,
"set_superset_reif");
2959 lit = GetOrCreateLiteralForVarLeVar(y_literals[i], x_literals[i]);
2961 all_true->add_literals(lit);
2966void CpModelProtoWithMapping::SetLexOrderingConstraint(
2967 const fz::Constraint& fz_ct) {
3015 std::vector<int> orig_x_literals, orig_y_literals;
3016 PutSetBooleansInCommonDomain(
3017 {LookupSetVar(fz_ct.arguments[0]), LookupSetVar(fz_ct.arguments[1])},
3018 {&orig_x_literals, &orig_y_literals});
3020 std::vector<int> x_literals, y_literals;
3021 for (
int set_idx = 0; set_idx < 2; ++set_idx) {
3022 const std::vector<int>& orig_literals =
3023 set_idx == 0 ? orig_x_literals : orig_y_literals;
3024 std::vector<int>& literals = set_idx == 0 ? x_literals : y_literals;
3025 literals.reserve(2 * orig_literals.size());
3027 for (
int i = 0;
i < orig_literals.size(); ++
i) {
3028 const int empty_suffix_lit = NewBoolVar();
3029 literals.push_back(empty_suffix_lit);
3030 literals.push_back(orig_literals[i]);
3035 empty_suffix_ct_rev->mutable_bool_and()->add_literals(empty_suffix_lit);
3036 for (
int j = i; j < orig_literals.size(); ++j) {
3037 empty_suffix_ct->mutable_bool_and()->add_literals(
3039 empty_suffix_ct_rev->add_enforcement_literal(
3045 const bool accept_equals =
3046 fz_ct.type ==
"set_le" || fz_ct.type ==
"set_le_reif";
3047 const bool is_reif =
3048 fz_ct.type ==
"set_le_reif" || fz_ct.type ==
"set_lt_reif";
3049 const int enforcement_literal =
3050 is_reif ? LookupVar(fz_ct.arguments[2]) : LookupConstant(1);
3051 AddLexOrdering(y_literals, x_literals, enforcement_literal, accept_equals);
3059 AddLexOrdering(x_literals, y_literals,
NegatedRef(enforcement_literal),
3064using ConstraintToSetMethodMapType =
3065 absl::flat_hash_map<std::string_view, void (CpModelProtoWithMapping::*)(
3066 const fz::Constraint&)>;
3068const ConstraintToSetMethodMapType& GetSetConstraintMap() {
3069 using MPMap = CpModelProtoWithMapping;
3070 static const absl::NoDestructor<ConstraintToSetMethodMapType> kConstraintMap({
3071 {
"array_set_element", &MPMap::ArraySetElementConstraint},
3072 {
"array_var_set_element", &MPMap::ArrayVarSetElementConstraint},
3073 {
"fzn_all_different_set", &MPMap::FznAllDifferentSetConstraint},
3074 {
"fzn_all_disjoint", &MPMap::FznAllDisjointConstraint},
3075 {
"fzn_disjoint", &MPMap::FznDisjointConstraint},
3076 {
"fzn_partition_set", &MPMap::FznPartitionSetConstraint},
3077 {
"set_card", &MPMap::SetCardConstraint},
3078 {
"set_in", &MPMap::SetInConstraint},
3079 {
"set_in_reif", &MPMap::SetInReifConstraint},
3080 {
"set_intersect", &MPMap::SetOpConstraint},
3081 {
"set_union", &MPMap::SetOpConstraint},
3082 {
"set_symdiff", &MPMap::SetOpConstraint},
3083 {
"set_diff", &MPMap::SetOpConstraint},
3084 {
"set_subset", &MPMap::SetSubSupersetEqConstraint},
3085 {
"set_superset", &MPMap::SetSubSupersetEqConstraint},
3086 {
"set_eq", &MPMap::SetSubSupersetEqConstraint},
3087 {
"set_ne", &MPMap::SetNeConstraint},
3088 {
"set_eq_reif", &MPMap::SetSubSupersetEqReifConstraint},
3089 {
"set_ne_reif", &MPMap::SetSubSupersetEqReifConstraint},
3090 {
"set_subset_reif", &MPMap::SetSubSupersetEqReifConstraint},
3091 {
"set_superset_reif", &MPMap::SetSubSupersetEqReifConstraint},
3092 {
"set_le", &MPMap::SetLexOrderingConstraint},
3093 {
"set_lt", &MPMap::SetLexOrderingConstraint},
3094 {
"set_le_reif", &MPMap::SetLexOrderingConstraint},
3095 {
"set_lt_reif", &MPMap::SetLexOrderingConstraint},
3097 return *kConstraintMap;
3100void CpModelProtoWithMapping::ExtractSetConstraint(
3101 const fz::Constraint& fz_ct) {
3102 auto it = GetSetConstraintMap().find(fz_ct.type);
3103 if (it != GetSetConstraintMap().
end()) {
3104 auto method_ptr = it->second;
3105 (this->*method_ptr)(fz_ct);
3107 LOG(FATAL) <<
"Not supported " << fz_ct.DebugString();
3111void CpModelProtoWithMapping::FillReifiedOrImpliedConstraint(
3112 const fz::Constraint& fz_ct) {
3114 if (fz_ct.type ==
"int_eq_reif" || fz_ct.type ==
"bool_eq_reif") {
3115 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3116 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3117 if (left.var == kNoVar) std::swap(left, right);
3118 const int e = LookupVar(fz_ct.arguments[2]);
3119 if (left.var == kNoVar) {
3120 CHECK_EQ(right.var, kNoVar);
3121 if (left.value == right.value) {
3122 AddImplication({}, e);
3126 }
else if (right.var == kNoVar) {
3127 AddVarEqValueLiteral(left.var, right.value, e);
3129 AddVarEqVarLiteral(left.var, right.var, e);
3131 }
else if (fz_ct.type ==
"int_eq_imp" || fz_ct.type ==
"bool_eq_imp") {
3132 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3133 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3134 if (left.var == kNoVar) std::swap(left, right);
3135 const int e = LookupVar(fz_ct.arguments[2]);
3136 if (left.var == kNoVar) {
3137 CHECK_EQ(right.var, kNoVar);
3138 if (left.value != right.value) {
3141 }
else if (right.var == kNoVar) {
3142 const std::optional<int> lit = GetEncodingLiteral(left.var, right.value);
3143 if (lit.has_value()) {
3144 AddImplication({e}, lit.value());
3145 ++num_var_op_var_imp_cached;
3147 AddLinearConstraint({e}, Domain(right.value), {{left.var, 1}});
3150 const std::optional<int> lit = GetVarEqVarLiteral(left.var, right.var);
3151 if (lit.has_value()) {
3152 AddImplication({e}, lit.value());
3153 ++num_var_op_var_imp_cached;
3155 AddLinearConstraint({e}, Domain(0), {{left.var, 1}, {right.var, -1}});
3158 }
else if (fz_ct.type ==
"int_ne_reif" || fz_ct.type ==
"bool_ne_reif") {
3159 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3160 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3161 if (left.var == kNoVar) std::swap(left, right);
3162 const int e = LookupVar(fz_ct.arguments[2]);
3163 if (left.var == kNoVar) {
3164 CHECK_EQ(right.var, kNoVar);
3165 if (left.value != right.value) {
3166 AddImplication({}, e);
3170 }
else if (right.var == kNoVar) {
3171 AddVarEqValueLiteral(left.var, right.value,
NegatedRef(e));
3173 AddVarEqVarLiteral(left.var, right.var,
NegatedRef(e));
3175 }
else if (fz_ct.type ==
"int_ne_imp" || fz_ct.type ==
"bool_ne_imp") {
3176 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3177 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3178 if (left.var == kNoVar) std::swap(left, right);
3179 const int e = LookupVar(fz_ct.arguments[2]);
3180 if (left.var == kNoVar) {
3181 CHECK_EQ(right.var, kNoVar);
3182 if (left.value == right.value) {
3185 }
else if (right.var == kNoVar) {
3186 const std::optional<int> lit = GetEncodingLiteral(left.var, right.value);
3187 if (lit.has_value()) {
3188 AddImplication({e},
NegatedRef(lit.value()));
3189 ++num_var_op_var_imp_cached;
3191 AddLinearConstraint({e}, Domain(right.value).Complement(),
3195 const std::optional<int> lit = GetVarNeVarLiteral(left.var, right.var);
3196 if (lit.has_value()) {
3197 AddImplication({e}, lit.value());
3198 ++num_var_op_var_imp_cached;
3200 AddLinearConstraint({e}, Domain(0).Complement(),
3201 {{left.var, 1}, {right.var, -1}});
3204 }
else if (fz_ct.type ==
"int_le_reif" || fz_ct.type ==
"bool_le_reif") {
3205 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3206 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3207 const int e = LookupVar(fz_ct.arguments[2]);
3208 if (left.var == kNoVar) {
3209 if (right.var == kNoVar) {
3210 if (left.value <= right.value) {
3211 AddImplication({}, e);
3216 AddVarGeValueLiteral(right.var, left.value, e);
3218 }
else if (right.var == kNoVar) {
3219 AddVarLeValueLiteral(left.var, right.value, e);
3221 AddVarLeVarLiteral(left.var, right.var, e);
3223 }
else if (fz_ct.type ==
"int_le_imp" || fz_ct.type ==
"bool_le_imp") {
3224 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3225 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3226 const int e = LookupVar(fz_ct.arguments[2]);
3227 if (left.var == kNoVar) {
3228 if (right.var == kNoVar) {
3229 if (left.value > right.value) {
3233 AddLinearConstraint({e},
3234 {left.value, std::numeric_limits<int64_t>::max()},
3237 }
else if (right.var == kNoVar) {
3238 AddLinearConstraint({e},
3239 {std::numeric_limits<int64_t>::min(), right.value},
3242 const std::optional<int> lit = GetVarLeVarLiteral(left.var, right.var);
3243 if (lit.has_value()) {
3244 AddImplication({e}, lit.value());
3245 ++num_var_op_var_imp_cached;
3247 AddLinearConstraint({e}, {std::numeric_limits<int64_t>::min(), 0},
3248 {{left.var, 1}, {right.var, -1}});
3251 }
else if (fz_ct.type ==
"int_ge_reif" || fz_ct.type ==
"bool_ge_reif") {
3252 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3253 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3254 const int e = LookupVar(fz_ct.arguments[2]);
3255 if (left.var == kNoVar) {
3256 if (right.var == kNoVar) {
3257 if (left.value >= right.value) {
3258 AddImplication({}, e);
3263 AddVarLeValueLiteral(right.var, left.value, e);
3265 }
else if (right.var == kNoVar) {
3266 AddVarGeValueLiteral(left.var, right.value, e);
3268 AddVarLeVarLiteral(right.var, left.var, e);
3270 }
else if (fz_ct.type ==
"int_lt_reif" || fz_ct.type ==
"bool_lt_reif") {
3271 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3272 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3273 const int e = LookupVar(fz_ct.arguments[2]);
3274 if (left.var == kNoVar) {
3275 if (right.var == kNoVar) {
3276 if (left.value < right.value) {
3277 AddImplication({}, e);
3282 AddVarGtValueLiteral(right.var, left.value, e);
3284 }
else if (right.var == kNoVar) {
3285 AddVarLtValueLiteral(left.var, right.value, e);
3287 AddVarLtVarLiteral(left.var, right.var, e);
3289 }
else if (fz_ct.type ==
"int_gt_reif" || fz_ct.type ==
"bool_gt_reif") {
3290 VarOrValue left = LookupVarOrValue(fz_ct.arguments[0]);
3291 VarOrValue right = LookupVarOrValue(fz_ct.arguments[1]);
3292 const int e = LookupVar(fz_ct.arguments[2]);
3293 if (left.var == kNoVar) {
3294 if (right.var == kNoVar) {
3295 if (left.value < right.value) {
3296 AddImplication({}, e);
3301 AddVarLtValueLiteral(right.var, left.value, e);
3303 }
else if (right.var == kNoVar) {
3304 AddVarGtValueLiteral(left.var, right.value, e);
3306 AddVarLtVarLiteral(right.var, left.var, e);
3308 }
else if (fz_ct.type ==
"array_bool_or") {
3309 const std::vector<int> literals = LookupVars(fz_ct.arguments[0]);
3310 const int e = LookupVar(fz_ct.arguments[1]);
3311 switch (literals.size()) {
3317 AddLinearConstraint({}, Domain(0), {{literals[0], 1}, {e, -1}});
3321 AddVarOrVarLiteral(literals[0], literals[1], e);
3325 ConstraintProto* imply = AddEnforcedConstraint(e);
3326 for (
int i = 0;
i < literals.size(); ++
i) {
3327 imply->mutable_bool_or()->add_literals(literals[i]);
3329 ConstraintProto* refute = AddEnforcedConstraint(
NegatedRef(e));
3330 for (
int i = 0;
i < literals.size(); ++
i) {
3331 refute->mutable_bool_and()->add_literals(
NegatedRef(literals[i]));
3335 }
else if (fz_ct.type ==
"array_bool_and") {
3336 const std::vector<int> literals = LookupVars(fz_ct.arguments[0]);
3337 const int e = LookupVar(fz_ct.arguments[1]);
3338 switch (literals.size()) {
3340 AddImplication({}, e);
3344 AddLinearConstraint({}, Domain(0), {{literals[0], 1}, {e, -1}});
3348 AddVarAndVarLiteral(literals[0], literals[1], e);
3352 ConstraintProto* imply = AddEnforcedConstraint(e);
3353 for (
int i = 0;
i < literals.size(); ++
i) {
3354 imply->mutable_bool_and()->add_literals(literals[i]);
3356 ConstraintProto* refute = AddEnforcedConstraint(
NegatedRef(e));
3357 for (
int i = 0;
i < literals.size(); ++
i) {
3358 refute->mutable_bool_or()->add_literals(
NegatedRef(literals[i]));
3366 std::string simplified_type;
3367 if (absl::EndsWith(fz_ct.type,
"_reif")) {
3369 simplified_type = fz_ct.type.substr(0, fz_ct.type.size() - 5);
3370 }
else if (absl::EndsWith(fz_ct.type,
"_imp")) {
3372 simplified_type = fz_ct.type.substr(0, fz_ct.type.size() - 4);
3375 simplified_type = fz_ct.type;
3379 fz::Constraint copy = fz_ct;
3380 copy.type = simplified_type;
3383 FillConstraint(copy, ct);
3386 std::string negated_type;
3389 if (simplified_type ==
"set_in") {
3390 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[2]));
3391 negated_type =
"set_in_negated";
3392 }
else if (simplified_type ==
"bool_eq" || simplified_type ==
"int_eq") {
3393 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[2]));
3394 negated_type =
"int_ne";
3395 }
else if (simplified_type ==
"bool_ne" || simplified_type ==
"int_ne") {
3396 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[2]));
3397 negated_type =
"int_eq";
3398 }
else if (simplified_type ==
"bool_le" || simplified_type ==
"int_le") {
3399 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[2]));
3400 negated_type =
"int_gt";
3401 }
else if (simplified_type ==
"bool_lt" || simplified_type ==
"int_lt") {
3402 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[2]));
3403 negated_type =
"int_ge";
3404 }
else if (simplified_type ==
"bool_ge" || simplified_type ==
"int_ge") {
3405 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[2]));
3406 negated_type =
"int_lt";
3407 }
else if (simplified_type ==
"bool_gt" || simplified_type ==
"int_gt") {
3408 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[2]));
3409 negated_type =
"int_le";
3410 }
else if (simplified_type ==
"int_lin_eq") {
3411 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[3]));
3412 negated_type =
"int_lin_ne";
3413 }
else if (simplified_type ==
"int_lin_ne") {
3414 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[3]));
3415 negated_type =
"int_lin_eq";
3416 }
else if (simplified_type ==
"int_lin_le") {
3417 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[3]));
3418 negated_type =
"int_lin_gt";
3419 }
else if (simplified_type ==
"int_lin_ge") {
3420 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[3]));
3421 negated_type =
"int_lin_lt";
3422 }
else if (simplified_type ==
"int_lin_lt") {
3423 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[3]));
3424 negated_type =
"int_lin_ge";
3425 }
else if (simplified_type ==
"int_lin_gt") {
3426 ct->add_enforcement_literal(LookupVar(fz_ct.arguments[3]));
3427 negated_type =
"int_lin_le";
3429 LOG(FATAL) <<
"Unsupported " << simplified_type;
3433 if (absl::EndsWith(fz_ct.type,
"_imp"))
return;
3437 ConstraintProto* negated_ct =
3438 AddEnforcedConstraint(
NegatedRef(ct->enforcement_literal(0)));
3439 negated_ct->set_name(fz_ct.type +
" (negated)");
3440 copy.type = negated_type;
3441 FillConstraint(copy, negated_ct);
3445void CpModelProtoWithMapping::TranslateSearchAnnotations(
3446 absl::Span<const fz::Annotation> search_annotations, SolverLogger* logger) {
3447 std::vector<fz::Annotation> flat_annotations;
3448 for (
const fz::Annotation& annotation : search_annotations) {
3449 fz::FlattenAnnotations(annotation, &flat_annotations);
3453 absl::flat_hash_set<int> hinted_vars;
3455 for (
const fz::Annotation& annotation : flat_annotations) {
3456 if (annotation.IsFunctionCallWithIdentifier(
"warm_start")) {
3457 CHECK_EQ(2, annotation.annotations.size());
3458 const fz::Annotation& vars = annotation.annotations[0];
3459 const fz::Annotation& values = annotation.annotations[1];
3460 if (vars.type != fz::Annotation::VAR_REF_ARRAY ||
3461 values.type != fz::Annotation::INT_LIST) {
3464 for (
int i = 0;
i < vars.variables.size(); ++
i) {
3465 fz::Variable* fz_var = vars.variables[
i];
3466 const int var = fz_var_to_index.at(fz_var);
3467 const int64_t value = values.values[
i];
3468 if (hinted_vars.insert(var).second) {
3473 }
else if (annotation.IsFunctionCallWithIdentifier(
"int_search") ||
3474 annotation.IsFunctionCallWithIdentifier(
"bool_search")) {
3475 const std::vector<fz::Annotation>& args = annotation.annotations;
3476 std::vector<fz::Variable*> vars;
3477 args[0].AppendAllVariables(&vars);
3480 for (fz::Variable* v : vars) {
3481 strategy->add_variables(fz_var_to_index.at(v));
3484 const fz::Annotation& choose = args[1];
3485 if (choose.id ==
"input_order") {
3486 strategy->set_variable_selection_strategy(
3487 DecisionStrategyProto::CHOOSE_FIRST);
3488 }
else if (choose.id ==
"first_fail") {
3489 strategy->set_variable_selection_strategy(
3490 DecisionStrategyProto::CHOOSE_MIN_DOMAIN_SIZE);
3491 }
else if (choose.id ==
"anti_first_fail") {
3492 strategy->set_variable_selection_strategy(
3493 DecisionStrategyProto::CHOOSE_MAX_DOMAIN_SIZE);
3494 }
else if (choose.id ==
"smallest") {
3495 strategy->set_variable_selection_strategy(
3496 DecisionStrategyProto::CHOOSE_LOWEST_MIN);
3497 }
else if (choose.id ==
"largest") {
3498 strategy->set_variable_selection_strategy(
3499 DecisionStrategyProto::CHOOSE_HIGHEST_MAX);
3501 SOLVER_LOG(logger,
"Unsupported variable selection strategy '",
3502 choose.id,
"', falling back to 'smallest'");
3503 strategy->set_variable_selection_strategy(
3504 DecisionStrategyProto::CHOOSE_LOWEST_MIN);
3507 const fz::Annotation& select = args[2];
3508 if (select.id ==
"indomain_min" || select.id ==
"indomain") {
3509 strategy->set_domain_reduction_strategy(
3510 DecisionStrategyProto::SELECT_MIN_VALUE);
3511 }
else if (select.id ==
"indomain_max") {
3512 strategy->set_domain_reduction_strategy(
3513 DecisionStrategyProto::SELECT_MAX_VALUE);
3514 }
else if (select.id ==
"indomain_split") {
3515 strategy->set_domain_reduction_strategy(
3516 DecisionStrategyProto::SELECT_LOWER_HALF);
3517 }
else if (select.id ==
"indomain_reverse_split") {
3518 strategy->set_domain_reduction_strategy(
3519 DecisionStrategyProto::SELECT_UPPER_HALF);
3520 }
else if (select.id ==
"indomain_median") {
3521 strategy->set_domain_reduction_strategy(
3522 DecisionStrategyProto::SELECT_MEDIAN_VALUE);
3524 SOLVER_LOG(logger,
"Unsupported value selection strategy '", select.id,
3525 "', falling back to 'indomain_min'");
3526 strategy->set_domain_reduction_strategy(
3527 DecisionStrategyProto::SELECT_MIN_VALUE);
3529 }
else if (annotation.IsFunctionCallWithIdentifier(
"set_search")) {
3530 const std::vector<fz::Annotation>& args = annotation.annotations;
3531 std::vector<fz::Variable*> vars;
3532 args[0].AppendAllVariables(&vars);
3535 for (fz::Variable* v : vars) {
3536 std::shared_ptr<SetVariable> set_var = set_variables.at(v);
3537 strategy->mutable_variables()->Add(set_var->var_indices.begin(),
3538 set_var->var_indices.end());
3541 const fz::Annotation& choose = args[1];
3542 if (choose.id ==
"input_order") {
3543 strategy->set_variable_selection_strategy(
3544 DecisionStrategyProto::CHOOSE_FIRST);
3546 SOLVER_LOG(logger,
"Unsupported set variable selection strategy '",
3547 choose.id,
"', falling back to 'input_order'");
3548 strategy->set_variable_selection_strategy(
3549 DecisionStrategyProto::CHOOSE_FIRST);
3552 const fz::Annotation& select = args[2];
3553 if (select.id ==
"indomain_min" || select.id ==
"indomain") {
3554 strategy->set_domain_reduction_strategy(
3555 DecisionStrategyProto::SELECT_MIN_VALUE);
3556 }
else if (select.id ==
"indomain_max") {
3557 strategy->set_domain_reduction_strategy(
3558 DecisionStrategyProto::SELECT_MAX_VALUE);
3560 SOLVER_LOG(logger,
"Unsupported value selection strategy '", select.id,
3561 "', falling back to 'indomain_min'");
3562 strategy->set_domain_reduction_strategy(
3563 DecisionStrategyProto::SELECT_MIN_VALUE);
3570std::string SolutionString(
3571 const fz::SolutionOutputSpecs& output,
3572 const std::function<int64_t(fz::Variable*)>& value_func,
3573 const std::function<std::vector<int64_t>(fz::Variable*)>& set_evaluator,
3574 double objective_value) {
3575 if (output.variable !=
nullptr && !output.variable->domain.is_float &&
3576 !output.variable->domain.is_a_set) {
3577 const int64_t value = value_func(output.variable);
3578 if (output.display_as_boolean) {
3579 return absl::StrCat(output.name,
" = ", value == 1 ?
"true" :
"false",
3582 return absl::StrCat(output.name,
" = ", value,
";");
3584 }
else if (output.variable !=
nullptr && output.variable->domain.is_a_set) {
3585 const std::vector<int64_t> values = set_evaluator(output.variable);
3586 return absl::StrCat(output.name,
" = {", absl::StrJoin(values,
","),
"};");
3587 }
else if (output.variable !=
nullptr && output.variable->domain.is_float) {
3588 return absl::StrCat(output.name,
" = ", objective_value,
";");
3590 const int bound_size = output.bounds.size();
3591 std::string result =
3592 absl::StrCat(output.name,
" = array", bound_size,
"d(");
3593 for (
int i = 0;
i < bound_size; ++
i) {
3594 if (output.bounds[
i].max_value >= output.bounds[
i].min_value) {
3595 absl::StrAppend(&result, output.bounds[
i].min_value,
"..",
3596 output.bounds[
i].max_value,
", ");
3598 result.append(
"{},");
3602 for (
int i = 0;
i < output.flat_variables.size(); ++
i) {
3603 if (output.flat_variables[
i]->domain.is_a_set) {
3604 const std::vector<int64_t> values =
3605 set_evaluator(output.flat_variables[
i]);
3606 absl::StrAppend(&result,
"{", absl::StrJoin(values,
","),
"}");
3608 const int64_t value = value_func(output.flat_variables[
i]);
3609 if (output.display_as_boolean) {
3610 result.append(value ?
"true" :
"false");
3612 absl::StrAppend(&result, value);
3615 if (
i != output.flat_variables.size() - 1) {
3616 result.append(
", ");
3619 result.append(
"]);");
3625std::string SolutionString(
3626 const fz::Model& model,
3627 const std::function<int64_t(fz::Variable*)>& value_func,
3628 const std::function<std::vector<int64_t>(fz::Variable*)>& set_evaluator,
3629 double objective_value) {
3630 std::string solution_string;
3631 for (
const auto& output_spec : model.output()) {
3632 solution_string.append(SolutionString(output_spec, value_func,
3633 set_evaluator, objective_value));
3634 solution_string.append(
"\n");
3636 return solution_string;
3639void OutputFlatzincStats(
const CpSolverResponse& response,
3640 SolverLogger* solution_logger) {
3642 "%%%mzn-stat: objective=", response.objective_value());
3644 "%%%mzn-stat: objectiveBound=", response.best_objective_bound());
3646 "%%%mzn-stat: boolVariables=", response.num_booleans());
3648 "%%%mzn-stat: failures=", response.num_conflicts());
3650 solution_logger,
"%%%mzn-stat: propagations=",
3651 response.num_binary_propagations() + response.num_integer_propagations());
3652 SOLVER_LOG(solution_logger,
"%%%mzn-stat: solveTime=", response.wall_time());
3661 if (!ct->active)
continue;
3662 if (ct->type ==
"int2float") {
3663 ct->type =
"int_eq";
3664 fz::Domain& float_domain = ct->arguments[1].variables[0]->domain;
3666 for (
const double float_value : float_domain.
float_values) {
3667 float_domain.
values.push_back(
static_cast<int64_t
>(float_value));
3677 if (!var->active)
continue;
3678 if (var->domain.is_float) {
3679 CHECK(float_objective_var ==
nullptr);
3680 float_objective_var = var;
3685 if (float_objective_var !=
nullptr) {
3687 if (!ct->active)
continue;
3688 if (ct->type ==
"float_lin_eq") {
3689 CHECK(float_objective_ct ==
nullptr);
3690 float_objective_ct = ct;
3696 if (float_objective_ct !=
nullptr || float_objective_var !=
nullptr) {
3697 CHECK(float_objective_ct !=
nullptr);
3698 CHECK(float_objective_var !=
nullptr);
3699 const int arity = float_objective_ct->
arguments[0].Size();
3700 CHECK_EQ(float_objective_ct->
arguments[1].variables[arity - 1],
3701 float_objective_var);
3702 CHECK_EQ(float_objective_ct->
arguments[0].floats[arity - 1], -1.0);
3703 for (
int i = 0;
i + 1 < arity; ++
i) {
3705 float_objective_ct->
arguments[1].variables[
i],
3709 -float_objective_ct->
arguments[2].floats[0]);
3711 float_objective_var->
active =
false;
3712 float_objective_ct->
active =
false;
3722 const bool enumerate_all_solutions_of_a_sat_problem =
3724 CpModelProtoWithMapping m(enumerate_all_solutions_of_a_sat_problem);
3725 m.proto.set_name(fz_model.
name());
3732 if (!fz_var->active)
continue;
3733 CHECK(!fz_var->domain.is_float) <<
"CP-SAT does not support float "
3736 if (fz_var->domain.is_a_set) {
3737 std::shared_ptr<SetVariable> set_var = std::make_shared<SetVariable>();
3740 if (fz_var->domain.is_interval) {
3741 set_var->sorted_values.reserve(fz_var->domain.values[1] -
3742 fz_var->domain.values[0] + 1);
3743 for (int64_t value = fz_var->domain.values[0];
3744 value <= fz_var->domain.values[1]; ++value) {
3745 set_var->sorted_values.push_back(value);
3748 set_var->sorted_values.reserve(fz_var->domain.values.size());
3749 for (int64_t value : fz_var->domain.values) {
3750 set_var->sorted_values.push_back(value);
3755 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
3756 if (fz_var->domain.is_fixed_set) {
3757 set_var->var_indices.push_back(m.LookupConstant(1));
3759 set_var->var_indices.push_back(m.NewBoolVar());
3762 m.set_variables[fz_var] = set_var;
3764 m.fz_var_to_index[fz_var] = m.proto.variables_size();
3767 if (fz_var->domain.is_interval) {
3768 if (fz_var->domain.values.empty()) {
3775 LOG_FIRST_N(WARNING, 1) <<
"Using flag --fz_int_max for "
3776 "unbounded integer variables.";
3777 LOG_FIRST_N(WARNING, 1)
3778 <<
" actual domain is [" << -absl::GetFlag(FLAGS_fz_int_max)
3779 <<
".." << absl::GetFlag(FLAGS_fz_int_max) <<
"]";
3781 absl::GetFlag(FLAGS_fz_int_max), var);
3796 m.FirstPass(*fz_ct);
3801 if (fz_ct ==
nullptr || !fz_ct->active)
continue;
3802 if (m.ConstraintContainsSetVariables(*fz_ct)) {
3803 m.ExtractSetConstraint(*fz_ct);
3805 if (absl::EndsWith(fz_ct->type,
"_reif") ||
3806 absl::EndsWith(fz_ct->type,
"_imp") ||
3807 fz_ct->type ==
"array_bool_or" || fz_ct->type ==
"array_bool_and") {
3808 m.FillReifiedOrImpliedConstraint(*fz_ct);
3812 m.FillConstraint(*fz_ct, ct);
3818 m.AddAllEncodingConstraints();
3847 " - #var_op_value_reif cached: ", m.num_var_op_value_reif_cached);
3849 " - #var_op_var_reif cached: ", m.num_var_op_var_reif_cached);
3851 " - #var_op_var_imp cached: ", m.num_var_op_var_reif_cached);
3852 SOLVER_LOG(logger,
" - #full domain encodings: ", m.num_full_encodings);
3853 if (absl::GetFlag(FLAGS_fz_use_light_encoding)) {
3855 " - #partial domain encodings: ", m.num_partial_encodings);
3857 " - #light constraint encodings: ", m.num_light_encodings);
3862 m.parameters.set_enumerate_all_solutions(
true);
3869 m.parameters.set_max_domain_size_when_encoding_eq_neq_constraints(32);
3872 int num_workers = 1;
3879 "Search for all solutions of a SAT problem in parallel is not "
3880 "supported. Switching back to sequential search.");
3888 "The number of search workers, is not specified. For better "
3889 "performances, please set the number of workers to 8, 16, or "
3890 "more depending on the number of cores of your computer.");
3898 m.parameters.set_keep_all_feasible_solutions_in_presolve(
true);
3902 (absl::GetFlag(FLAGS_force_interleave_search) || p.
ortools_mode)) {
3903 m.parameters.set_interleave_search(
true);
3904 m.parameters.set_use_rins_lns(
false);
3905 m.parameters.add_subsolvers(
"default_lp");
3906 m.parameters.add_subsolvers(
"max_lp");
3907 m.parameters.add_subsolvers(
"quick_restart");
3908 m.parameters.add_subsolvers(
"core_or_no_lp");
3909 m.parameters.set_num_violation_ls(1);
3910 if (!m.proto.search_strategy().empty()) {
3911 m.parameters.add_subsolvers(
"fixed");
3914 }
else if (num_workers > 1 && num_workers < 8 && p.
ortools_mode) {
3915 SOLVER_LOG(logger,
"Bumping number of workers from ", num_workers,
" to 8");
3918 m.parameters.set_num_workers(num_workers);
3927 m.parameters.MergeFrom(sat_params);
3933 solution_observer = [&fz_model, &m, &p,
3935 const std::string solution_string = SolutionString(
3938 return r.solution(m.fz_var_to_index.at(v));
3941 std::vector<int64_t> values;
3942 const std::shared_ptr<SetVariable> set_var = m.set_variables.at(v);
3943 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
3944 if (r.solution(set_var->var_indices[
i]) == 1) {
3945 values.push_back(set_var->sorted_values[
i]);
3950 r.objective_value());
3951 SOLVER_LOG(solution_logger, solution_string);
3952 if (p.check_all_solutions) {
3953 CHECK(CheckSolution(
3956 return r.solution(m.fz_var_to_index.at(v));
3959 std::vector<int64_t> values;
3960 const std::shared_ptr<SetVariable> set_var =
3961 m.set_variables.at(v);
3962 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
3963 if (r.solution(set_var->var_indices[
i]) == 1) {
3964 values.push_back(set_var->sorted_values[
i]);
3971 if (p.display_statistics) {
3972 OutputFlatzincStats(r, solution_logger);
3979 if (solution_observer !=
nullptr) {
3988 CHECK(CheckSolution(
3991 return response.
solution(m.fz_var_to_index.at(v));
3994 std::vector<int64_t> values;
3995 const std::shared_ptr<SetVariable> set_var = m.set_variables.at(v);
3996 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
3997 if (response.
solution(set_var->var_indices[
i]) == 1) {
3998 values.push_back(set_var->sorted_values[
i]);
4013 const std::string solution_string = SolutionString(
4016 return response.
solution(m.fz_var_to_index.at(v));
4019 std::vector<int64_t> values;
4020 const std::shared_ptr<SetVariable> set_var =
4021 m.set_variables.at(v);
4022 for (
int i = 0;
i < set_var->sorted_values.size(); ++
i) {
4023 if (response.
solution(set_var->var_indices[
i]) == 1) {
4024 values.push_back(set_var->sorted_values[
i]);
4030 SOLVER_LOG(solution_logger, solution_string);
4033 const bool should_print_optimal =
4037 if (should_print_optimal) {
4041 SOLVER_LOG(solution_logger,
"=====UNSATISFIABLE=====");
4044 VLOG(1) <<
"%% Error message = '" << error_message <<
"'";
4045 if (absl::StrContains(error_message,
"overflow")) {
4046 SOLVER_LOG(solution_logger,
"=====OVERFLOW=====");
4048 SOLVER_LOG(solution_logger,
"=====MODEL INVALID=====");
4054 OutputFlatzincStats(response, solution_logger);
static Domain FromValues(std::vector< int64_t > values)
Domain AdditionWith(const Domain &domain) const
const std::vector< double > & float_objective_coefficients() const
void AddFloatingPointObjectiveTerm(Variable *var, double coeff)
const std::vector< Variable * > & float_objective_variables() const
const std::vector< Constraint * > & constraints() const
void SetFloatingPointObjectiveOffset(double offset)
const std::vector< Variable * > & variables() const
double float_objective_offset() const
const std::string & name() const
const std::vector< Annotation > & search_annotations() const
Variable * objective() const
void add_literals(::int32_t value)
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_bool_and()
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_exactly_one()
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_bool_or()
void add_enforcement_literal(::int32_t value)
void set_name(Arg_ &&arg, Args_... args)
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_at_most_one()
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
int constraints_size() const
::operations_research::sat::IntegerVariableProto *PROTOBUF_NONNULL add_variables()
::operations_research::sat::PartialVariableAssignment *PROTOBUF_NONNULL mutable_solution_hint()
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL add_constraints()
int variables_size() const
::operations_research::sat::IntegerVariableProto *PROTOBUF_NONNULL mutable_variables(int index)
::operations_research::sat::DecisionStrategyProto *PROTOBUF_NONNULL add_search_strategy()
void set_scaling_factor(double value)
void add_coeffs(::int64_t value)
void add_vars(::int32_t value)
::operations_research::sat::CpSolverStatus status() const
double objective_value() const
::int64_t solution(int index) const
void set_offset(double value)
void add_coeffs(double value)
void set_maximize(bool value)
void add_vars(::int32_t value)
void set_name(Arg_ &&arg, Args_... args)
::int64_t domain(int index) const
T Add(std::function< T(Model *)> f)
void add_values(::int64_t value)
void add_vars(::int32_t value)
static constexpr SearchBranching AUTOMATIC_SEARCH
static constexpr SearchBranching FIXED_SEARCH
ABSL_FLAG(int64_t, fz_int_max, int64_t{1}<< 40, "Default max value for unbounded integer variables.")
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
ReverseView< Container > reversed_view(const Container &c)
std::string ValidateCpModel(const CpModelProto &model, bool after_presolve)
std::function< SatParameters(Model *)> NewSatParameters(absl::string_view params)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
CpSolverResponse SolveFzWithCpModelProto(const fz::Model &fz_model, const fz::FlatzincSatParameters &p, const SatParameters &sat_params, Model *sat_model, SolverLogger *solution_logger)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &callback)
void ProcessFloatingPointOVariablesAndObjective(fz::Model *fz_model)
ClosedInterval::Iterator end(ClosedInterval interval)
bool Contains(int64_t value) const
std::vector< Argument > arguments
std::vector< int64_t > values
std::vector< double > float_values
bool search_all_solutions
double max_time_in_seconds
bool display_all_solutions
#define SOLVER_LOG(logger,...)