Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cp_model_utils.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <cmath>
17#include <cstdint>
18#include <cstdlib>
19#include <functional>
20#include <numeric>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/base/optimization.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/flags/flag.h"
29#include "absl/functional/function_ref.h"
30#include "absl/log/check.h"
31#include "absl/strings/str_cat.h"
32#include "absl/strings/string_view.h"
33#include "absl/types/span.h"
34#include "google/protobuf/descriptor.h"
35#include "google/protobuf/message.h"
36#include "google/protobuf/text_format.h"
42
43ABSL_FLAG(bool, cp_model_dump_models, false,
44 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
45 "protos (original model, presolved model, mapping model) in text "
46 "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|"
47 "mapping_model}.pb.txt.");
48
49#if defined(_MSC_VER)
50ABSL_FLAG(std::string, cp_model_dump_prefix, ".\\",
51 "Prefix filename for all dumped files");
52#else
53ABSL_FLAG(std::string, cp_model_dump_prefix, "/tmp/",
54 "Prefix filename for all dumped files");
55#endif
56
57ABSL_FLAG(bool, cp_model_dump_submodels, false,
58 "DEBUG ONLY. When set to true, solve will dump all "
59 "lns or objective_shaving submodels proto in text format to "
60 "'FLAGS_cp_model_dump_prefix'xxx.pb.txt.");
61
62namespace operations_research {
63namespace sat {
64
65namespace {
66
67template <typename IntList>
68void AddIndices(const IntList& indices, std::vector<int>* output) {
69 output->insert(output->end(), indices.begin(), indices.end());
70}
71
72} // namespace
73
74int64_t LinearExpressionGcd(const LinearExpressionProto& expr, int64_t gcd) {
75 gcd = std::gcd(gcd, std::abs(expr.offset()));
76 for (const int64_t coeff : expr.coeffs()) {
77 gcd = std::gcd(gcd, std::abs(coeff));
78 }
79 return gcd;
80}
81
82void DivideLinearExpression(int64_t divisor, LinearExpressionProto* expr) {
83 CHECK_NE(divisor, 0);
84 if (divisor == 1) return;
85
86 DCHECK_EQ(expr->offset() % divisor, 0);
87 expr->set_offset(expr->offset() / divisor);
88 for (int i = 0; i < expr->vars_size(); ++i) {
89 DCHECK_EQ(expr->coeffs(i) % divisor, 0);
90 expr->set_coeffs(i, expr->coeffs(i) / divisor);
91 }
92}
93
95 LinearExpressionProto* output_negated_expr) {
96 output_negated_expr->Clear();
97 for (int i = 0; i < input_expr.vars_size(); ++i) {
98 output_negated_expr->add_vars(NegatedRef(input_expr.vars(i)));
99 output_negated_expr->add_coeffs(input_expr.coeffs(i));
100 }
101 output_negated_expr->set_offset(-input_expr.offset());
102}
103
105 IndexReferences output;
107 return output;
108}
109
111 std::vector<int>* variables,
112 std::vector<int>* literals) {
113 variables->clear();
114 literals->clear();
115 switch (ct.constraint_case()) {
117 AddIndices(ct.bool_or().literals(), literals);
118 break;
120 AddIndices(ct.bool_and().literals(), literals);
121 break;
123 AddIndices(ct.at_most_one().literals(), literals);
124 break;
126 AddIndices(ct.exactly_one().literals(), literals);
127 break;
129 AddIndices(ct.bool_xor().literals(), literals);
130 break;
132 AddIndices(ct.int_div().target().vars(), variables);
133 for (const LinearExpressionProto& expr : ct.int_div().exprs()) {
134 AddIndices(expr.vars(), variables);
135 }
136 break;
138 AddIndices(ct.int_mod().target().vars(), variables);
139 for (const LinearExpressionProto& expr : ct.int_mod().exprs()) {
140 AddIndices(expr.vars(), variables);
141 }
142 break;
144 AddIndices(ct.lin_max().target().vars(), variables);
145 for (const LinearExpressionProto& expr : ct.lin_max().exprs()) {
146 AddIndices(expr.vars(), variables);
147 }
148 break;
149 }
151 AddIndices(ct.int_prod().target().vars(), variables);
152 for (const LinearExpressionProto& expr : ct.int_prod().exprs()) {
153 AddIndices(expr.vars(), variables);
154 }
155 break;
157 AddIndices(ct.linear().vars(), variables);
158 break;
160 for (const LinearExpressionProto& expr : ct.all_diff().exprs()) {
161 AddIndices(expr.vars(), variables);
162 }
163 break;
165 AddIndices(ct.dummy_constraint().vars(), variables);
166 break;
168 if (ct.element().index() != 0 || ct.element().target() != 0 ||
169 !ct.element().vars().empty()) {
170 variables->push_back(ct.element().index());
171 variables->push_back(ct.element().target());
172 AddIndices(ct.element().vars(), variables);
173 } else if (ct.element().has_linear_index() ||
174 ct.element().has_linear_target() ||
175 !ct.element().exprs().empty()) {
176 AddIndices(ct.element().linear_index().vars(), variables);
177 AddIndices(ct.element().linear_target().vars(), variables);
178 for (const LinearExpressionProto& expr : ct.element().exprs()) {
179 AddIndices(expr.vars(), variables);
180 }
181 }
182 break;
184 AddIndices(ct.circuit().literals(), literals);
185 break;
187 AddIndices(ct.routes().literals(), literals);
188 // The node expressions are not used by the constraint itself.
189 break;
191 AddIndices(ct.inverse().f_direct(), variables);
192 AddIndices(ct.inverse().f_inverse(), variables);
193 break;
195 for (const LinearExpressionProto& time : ct.reservoir().time_exprs()) {
196 AddIndices(time.vars(), variables);
197 }
198 for (const LinearExpressionProto& level :
199 ct.reservoir().level_changes()) {
200 AddIndices(level.vars(), variables);
201 }
202 AddIndices(ct.reservoir().active_literals(), literals);
203 break;
205 if (!ct.table().vars().empty()) {
206 AddIndices(ct.table().vars(), variables);
207 } else {
208 for (const LinearExpressionProto& expr : ct.table().exprs()) {
209 AddIndices(expr.vars(), variables);
210 }
211 }
212 break;
214 if (!ct.automaton().vars().empty()) {
215 AddIndices(ct.automaton().vars(), variables);
216 } else {
217 for (const LinearExpressionProto& expr : ct.automaton().exprs()) {
218 AddIndices(expr.vars(), variables);
219 }
220 }
221 break;
223 AddIndices(ct.interval().start().vars(), variables);
224 AddIndices(ct.interval().size().vars(), variables);
225 AddIndices(ct.interval().end().vars(), variables);
226 break;
228 break;
230 break;
232 AddIndices(ct.cumulative().capacity().vars(), variables);
233 for (const LinearExpressionProto& demand : ct.cumulative().demands()) {
234 AddIndices(demand.vars(), variables);
235 }
236 break;
238 break;
239 }
240}
241
242#define APPLY_TO_SINGULAR_FIELD(ct_name, field_name) \
243 { \
244 int temp = ct->mutable_##ct_name()->field_name(); \
245 f(&temp); \
246 ct->mutable_##ct_name()->set_##field_name(temp); \
247 }
248
249#define APPLY_TO_REPEATED_FIELD(ct_name, field_name) \
250 { \
251 for (int& r : *ct->mutable_##ct_name()->mutable_##field_name()) f(&r); \
252 }
253
254void ApplyToAllLiteralIndices(absl::FunctionRef<void(int*)> f,
255 ConstraintProto* ct) {
256 for (int& r : *ct->mutable_enforcement_literal()) f(&r);
257 switch (ct->constraint_case()) {
259 APPLY_TO_REPEATED_FIELD(bool_or, literals);
260 break;
262 APPLY_TO_REPEATED_FIELD(bool_and, literals);
263 break;
265 APPLY_TO_REPEATED_FIELD(at_most_one, literals);
266 break;
268 APPLY_TO_REPEATED_FIELD(exactly_one, literals);
269 break;
271 APPLY_TO_REPEATED_FIELD(bool_xor, literals);
272 break;
274 break;
276 break;
278 break;
280 break;
282 break;
284 break;
286 break;
288 break;
290 APPLY_TO_REPEATED_FIELD(circuit, literals);
291 break;
293 APPLY_TO_REPEATED_FIELD(routes, literals);
294 break;
296 break;
298 APPLY_TO_REPEATED_FIELD(reservoir, active_literals);
299 break;
301 break;
303 break;
305 break;
307 break;
309 break;
311 break;
313 break;
314 }
315}
316
317void ApplyToAllVariableIndices(absl::FunctionRef<void(int*)> f,
318 ConstraintProto* ct) {
319 switch (ct->constraint_case()) {
321 break;
323 break;
325 break;
327 break;
329 break;
331 APPLY_TO_REPEATED_FIELD(int_div, target()->mutable_vars);
332 for (int i = 0; i < ct->int_div().exprs_size(); ++i) {
333 APPLY_TO_REPEATED_FIELD(int_div, exprs(i)->mutable_vars);
334 }
335 break;
337 APPLY_TO_REPEATED_FIELD(int_mod, target()->mutable_vars);
338 for (int i = 0; i < ct->int_mod().exprs_size(); ++i) {
339 APPLY_TO_REPEATED_FIELD(int_mod, exprs(i)->mutable_vars);
340 }
341 break;
343 APPLY_TO_REPEATED_FIELD(lin_max, target()->mutable_vars);
344 for (int i = 0; i < ct->lin_max().exprs_size(); ++i) {
345 APPLY_TO_REPEATED_FIELD(lin_max, exprs(i)->mutable_vars);
346 }
347 break;
349 APPLY_TO_REPEATED_FIELD(int_prod, target()->mutable_vars);
350 for (int i = 0; i < ct->int_prod().exprs_size(); ++i) {
351 APPLY_TO_REPEATED_FIELD(int_prod, exprs(i)->mutable_vars);
352 }
353 break;
355 APPLY_TO_REPEATED_FIELD(linear, vars);
356 break;
358 for (int i = 0; i < ct->all_diff().exprs_size(); ++i) {
359 APPLY_TO_REPEATED_FIELD(all_diff, exprs(i)->mutable_vars);
360 }
361 break;
363 APPLY_TO_REPEATED_FIELD(dummy_constraint, vars);
364 break;
366 if (ct->element().index() != 0 || ct->element().target() != 0 ||
367 !ct->element().vars().empty()) {
368 APPLY_TO_SINGULAR_FIELD(element, index);
369 APPLY_TO_SINGULAR_FIELD(element, target);
370 APPLY_TO_REPEATED_FIELD(element, vars);
371 } else if (ct->element().has_linear_index() ||
372 ct->element().has_linear_target() ||
373 !ct->element().exprs().empty()) {
374 APPLY_TO_REPEATED_FIELD(element, linear_index()->mutable_vars);
375 APPLY_TO_REPEATED_FIELD(element, linear_target()->mutable_vars);
376 for (int i = 0; i < ct->element().exprs_size(); ++i) {
377 APPLY_TO_REPEATED_FIELD(element, exprs(i)->mutable_vars);
378 }
379 }
380 break;
382 break;
384 break;
386 APPLY_TO_REPEATED_FIELD(inverse, f_direct);
387 APPLY_TO_REPEATED_FIELD(inverse, f_inverse);
388 break;
390 for (int i = 0; i < ct->reservoir().time_exprs_size(); ++i) {
391 APPLY_TO_REPEATED_FIELD(reservoir, time_exprs(i)->mutable_vars);
392 }
393 for (int i = 0; i < ct->reservoir().level_changes_size(); ++i) {
394 APPLY_TO_REPEATED_FIELD(reservoir, level_changes(i)->mutable_vars);
395 }
396 break;
398 if (!ct->table().vars().empty()) {
399 APPLY_TO_REPEATED_FIELD(table, vars);
400 } else {
401 for (int i = 0; i < ct->table().exprs_size(); ++i) {
402 APPLY_TO_REPEATED_FIELD(table, exprs(i)->mutable_vars);
403 }
404 }
405 break;
407 if (!ct->automaton().vars().empty()) {
408 APPLY_TO_REPEATED_FIELD(automaton, vars);
409 } else {
410 for (int i = 0; i < ct->automaton().exprs_size(); ++i) {
411 APPLY_TO_REPEATED_FIELD(automaton, exprs(i)->mutable_vars);
412 }
413 }
414 break;
416 APPLY_TO_REPEATED_FIELD(interval, start()->mutable_vars);
417 APPLY_TO_REPEATED_FIELD(interval, size()->mutable_vars);
418 APPLY_TO_REPEATED_FIELD(interval, end()->mutable_vars);
419 break;
421 break;
423 break;
425 APPLY_TO_REPEATED_FIELD(cumulative, capacity()->mutable_vars);
426 for (int i = 0; i < ct->cumulative().demands_size(); ++i) {
427 for (int& r :
429 f(&r);
430 }
431 }
432 break;
434 break;
435 }
436}
437
438void ApplyToAllIntervalIndices(absl::FunctionRef<void(int*)> f,
439 ConstraintProto* ct) {
440 switch (ct->constraint_case()) {
442 break;
444 break;
446 break;
448 break;
450 break;
452 break;
454 break;
456 break;
458 break;
460 break;
462 break;
464 break;
466 break;
468 break;
470 break;
472 break;
474 break;
476 break;
478 break;
480 break;
482 APPLY_TO_REPEATED_FIELD(no_overlap, intervals);
483 break;
485 APPLY_TO_REPEATED_FIELD(no_overlap_2d, x_intervals);
486 APPLY_TO_REPEATED_FIELD(no_overlap_2d, y_intervals);
487 break;
489 APPLY_TO_REPEATED_FIELD(cumulative, intervals);
490 break;
492 break;
493 }
494}
495
496#undef APPLY_TO_SINGULAR_FIELD
497#undef APPLY_TO_REPEATED_FIELD
498
499absl::string_view ConstraintCaseName(
500 ConstraintProto::ConstraintCase constraint_case) {
501 switch (constraint_case) {
503 return "kBoolOr";
505 return "kBoolAnd";
507 return "kAtMostOne";
509 return "kExactlyOne";
511 return "kBoolXor";
513 return "kIntDiv";
515 return "kIntMod";
517 return "kLinMax";
519 return "kIntProd";
521 return "kLinear";
523 return "kAllDiff";
525 return "kDummyConstraint";
527 return "kElement";
529 return "kCircuit";
531 return "kRoutes";
533 return "kInverse";
535 return "kReservoir";
537 return "kTable";
539 return "kAutomaton";
541 return "kInterval";
543 return "kNoOverlap";
545 return "kNoOverlap2D";
547 return "kCumulative";
549 return "kEmpty";
550 }
551 ABSL_UNREACHABLE();
552}
553
554std::vector<int> UsedVariables(const ConstraintProto& ct) {
555 std::vector<int> result;
556 GetReferencesUsedByConstraint(ct, &result, &result);
557 for (int& ref : result) {
558 ref = PositiveRef(ref);
559 }
560 for (const int lit : ct.enforcement_literal()) {
561 result.push_back(PositiveRef(lit));
562 }
564 return result;
565}
566
567std::vector<int> UsedIntervals(const ConstraintProto& ct) {
568 std::vector<int> used_intervals;
569 switch (ct.constraint_case()) {
571 break;
573 break;
575 break;
577 break;
579 break;
581 break;
583 break;
585 break;
587 break;
589 break;
591 break;
593 break;
595 break;
597 break;
599 break;
601 break;
603 break;
605 break;
607 break;
609 break;
611 AddIndices(ct.no_overlap().intervals(), &used_intervals);
612 break;
614 AddIndices(ct.no_overlap_2d().x_intervals(), &used_intervals);
615 AddIndices(ct.no_overlap_2d().y_intervals(), &used_intervals);
616 break;
618 AddIndices(ct.cumulative().intervals(), &used_intervals);
619 break;
621 break;
622 }
623 gtl::STLSortAndRemoveDuplicates(&used_intervals);
624 return used_intervals;
625}
626
628 absl::Span<const int64_t> solution) {
629 int64_t objective_value = 0;
630 for (int i = 0; i < objective.vars_size(); ++i) {
631 int64_t coeff = objective.coeffs(i);
632 const int ref = objective.vars(i);
633 const int var = PositiveRef(ref);
634 if (!RefIsPositive(ref)) coeff = -coeff;
635 objective_value += coeff * solution[var];
636 }
637 return objective_value;
638}
639
641 return expr.offset() == 0 && expr.vars_size() == 1 &&
642 std::abs(expr.coeffs(0)) == 1;
643}
644
646 return expr.vars_size() <= 1;
647}
648
649// Returns the reference the expression can be reduced to. It will DCHECK that
650// ExpressionContainsSingleRef(expr) is true.
652 DCHECK(ExpressionContainsSingleRef(expr));
653 return expr.coeffs(0) == 1 ? expr.vars(0) : NegatedRef(expr.vars(0));
654}
655
657 int64_t coefficient,
658 LinearConstraintProto* linear) {
659 for (int i = 0; i < expr.vars_size(); ++i) {
660 linear->add_vars(expr.vars(i));
661 linear->add_coeffs(expr.coeffs(i) * coefficient);
662 }
663 DCHECK(!linear->domain().empty());
664 const int64_t shift = coefficient * expr.offset();
665 if (shift != 0) {
667 linear);
668 }
669}
670
671void AddWeightedLiteralToLinearConstraint(int lit, int64_t coeff,
672 LinearConstraintProto* linear,
673 int64_t* offset) {
674 if (coeff == 0) return;
675 if (RefIsPositive(lit)) {
676 linear->add_vars(lit);
677 linear->add_coeffs(coeff);
678 } else {
679 linear->add_vars(NegatedRef(lit));
680 linear->add_coeffs(-coeff);
681 *offset += coeff;
682 }
683}
684
685void LiteralsToLinear(absl::Span<const int> literals, int64_t lb, int64_t ub,
686 LinearConstraintProto* linear) {
687 linear->Clear();
688 for (const int lit : literals) {
689 if (RefIsPositive(lit)) {
690 linear->add_vars(lit);
691 linear->add_coeffs(1);
692 } else {
693 linear->add_vars(NegatedRef(lit));
694 linear->add_coeffs(-1);
695 lb -= 1;
696 ub -= 1;
697 }
698 }
699 linear->add_domain(lb);
700 linear->add_domain(ub);
701}
702
704 const LinearExpressionProto& expr, int64_t coefficient,
705 LinearConstraintProto* linear) {
706 for (int i = 0; i < expr.vars_size(); ++i) {
707 linear->add_vars(expr.vars(i));
708 const int64_t prod = CapProd(expr.coeffs(i), coefficient);
709 if (AtMinOrMaxInt64(prod)) return false;
710 linear->add_coeffs(prod);
711 }
712 DCHECK(!linear->domain().empty());
713
714 const int64_t shift = CapProd(coefficient, expr.offset());
715 if (AtMinOrMaxInt64(shift)) return false;
716 Domain d = ReadDomainFromProto(*linear).AdditionWith(Domain(-shift));
717 if (AtMinOrMaxInt64(d.Min()) || AtMinOrMaxInt64(d.Max())) return false;
718 FillDomainInProto(d, linear);
719 return true;
720}
721
724 int64_t b_scaling) {
725 if (a.vars_size() != b.vars_size()) return false;
726 if (a.offset() != b.offset() * b_scaling) return false;
727 absl::flat_hash_map<int, int64_t> coeffs;
728 for (int i = 0; i < a.vars_size(); ++i) {
729 coeffs[a.vars(i)] += a.coeffs(i);
730 coeffs[b.vars(i)] += -b.coeffs(i) * b_scaling;
731 }
732
733 for (const auto [var, coeff] : coeffs) {
734 if (coeff != 0) return false;
735 }
736 return true;
737}
738
740 uint64_t seed) {
741 uint64_t fp = seed;
742 if (!lin.vars().empty()) {
743 fp = FingerprintRepeatedField(lin.vars(), fp);
744 fp = FingerprintRepeatedField(lin.coeffs(), fp);
745 }
746 fp = FingerprintSingleField(lin.offset(), fp);
747 return fp;
748}
749
750uint64_t FingerprintModel(const CpModelProto& model, uint64_t seed) {
751 uint64_t fp = seed;
752 for (const IntegerVariableProto& var_proto : model.variables()) {
753 fp = FingerprintRepeatedField(var_proto.domain(), fp);
754 }
755 for (const ConstraintProto& ct : model.constraints()) {
756 if (!ct.enforcement_literal().empty()) {
757 fp = FingerprintRepeatedField(ct.enforcement_literal(), fp);
758 }
759 switch (ct.constraint_case()) {
761 fp = FingerprintRepeatedField(ct.bool_or().literals(), fp);
762 break;
764 fp = FingerprintRepeatedField(ct.bool_and().literals(), fp);
765 break;
767 fp = FingerprintRepeatedField(ct.at_most_one().literals(), fp);
768 break;
770 fp = FingerprintRepeatedField(ct.exactly_one().literals(), fp);
771 break;
773 fp = FingerprintRepeatedField(ct.bool_xor().literals(), fp);
774 break;
776 fp = FingerprintExpression(ct.int_div().target(), fp);
777 for (const LinearExpressionProto& expr : ct.int_div().exprs()) {
778 fp = FingerprintExpression(expr, fp);
779 }
780 break;
782 fp = FingerprintExpression(ct.int_mod().target(), fp);
783 for (const LinearExpressionProto& expr : ct.int_mod().exprs()) {
784 fp = FingerprintExpression(expr, fp);
785 }
786 break;
788 fp = FingerprintExpression(ct.lin_max().target(), fp);
789 for (const LinearExpressionProto& expr : ct.lin_max().exprs()) {
790 fp = FingerprintExpression(expr, fp);
791 }
792 break;
793 }
795 fp = FingerprintExpression(ct.int_prod().target(), fp);
796 for (const LinearExpressionProto& expr : ct.int_prod().exprs()) {
797 fp = FingerprintExpression(expr, fp);
798 }
799 break;
801 fp = FingerprintRepeatedField(ct.linear().vars(), fp);
802 fp = FingerprintRepeatedField(ct.linear().coeffs(), fp);
803 fp = FingerprintRepeatedField(ct.linear().domain(), fp);
804 break;
806 for (const LinearExpressionProto& expr : ct.all_diff().exprs()) {
807 fp = FingerprintExpression(expr, fp);
808 }
809 break;
811 break;
813 fp = FingerprintSingleField(ct.element().index(), fp);
814 fp = FingerprintSingleField(ct.element().target(), fp);
815 fp = FingerprintRepeatedField(ct.element().vars(), fp);
816 fp = FingerprintExpression(ct.element().linear_index(), fp);
817 fp = FingerprintExpression(ct.element().linear_target(), fp);
818 for (const LinearExpressionProto& expr : ct.element().exprs()) {
819 fp = FingerprintExpression(expr, fp);
820 }
821 break;
823 fp = FingerprintRepeatedField(ct.circuit().heads(), fp);
824 fp = FingerprintRepeatedField(ct.circuit().tails(), fp);
825 fp = FingerprintRepeatedField(ct.circuit().literals(), fp);
826 break;
828 fp = FingerprintRepeatedField(ct.routes().heads(), fp);
829 fp = FingerprintRepeatedField(ct.routes().tails(), fp);
830 fp = FingerprintRepeatedField(ct.routes().literals(), fp);
831 break;
833 fp = FingerprintRepeatedField(ct.inverse().f_direct(), fp);
834 fp = FingerprintRepeatedField(ct.inverse().f_inverse(), fp);
835 break;
837 fp = FingerprintSingleField(ct.reservoir().min_level(), fp);
838 fp = FingerprintSingleField(ct.reservoir().max_level(), fp);
839 for (const LinearExpressionProto& expr : ct.reservoir().time_exprs()) {
840 fp = FingerprintExpression(expr, fp);
841 }
842 for (const LinearExpressionProto& expr :
843 ct.reservoir().level_changes()) {
844 fp = FingerprintExpression(expr, fp);
845 }
846 break;
848 if (!ct.table().vars().empty()) {
849 fp = FingerprintRepeatedField(ct.table().vars(), fp);
850 } else {
851 for (const LinearExpressionProto& expr : ct.table().exprs()) {
852 fp = FingerprintExpression(expr, fp);
853 }
854 }
855 fp = FingerprintRepeatedField(ct.table().values(), fp);
856 fp = FingerprintSingleField(ct.table().negated(), fp);
857 break;
859 fp = FingerprintSingleField(ct.automaton().starting_state(), fp);
860 fp = FingerprintRepeatedField(ct.automaton().final_states(), fp);
861 fp = FingerprintRepeatedField(ct.automaton().transition_tail(), fp);
862 fp = FingerprintRepeatedField(ct.automaton().transition_head(), fp);
863 fp = FingerprintRepeatedField(ct.automaton().transition_label(), fp);
864 if (!ct.automaton().vars().empty()) {
865 fp = FingerprintRepeatedField(ct.automaton().vars(), fp);
866 } else {
867 for (const LinearExpressionProto& expr : ct.automaton().exprs()) {
868 fp = FingerprintExpression(expr, fp);
869 }
870 }
871 break;
873 fp = FingerprintExpression(ct.interval().start(), fp);
874 fp = FingerprintExpression(ct.interval().size(), fp);
875 fp = FingerprintExpression(ct.interval().end(), fp);
876 break;
878 fp = FingerprintRepeatedField(ct.no_overlap().intervals(), fp);
879 break;
881 fp = FingerprintRepeatedField(ct.no_overlap_2d().x_intervals(), fp);
882 fp = FingerprintRepeatedField(ct.no_overlap_2d().y_intervals(), fp);
883 break;
885 fp = FingerprintRepeatedField(ct.cumulative().intervals(), fp);
886 fp = FingerprintExpression(ct.cumulative().capacity(), fp);
887 for (const LinearExpressionProto& demand : ct.cumulative().demands()) {
888 fp = FingerprintExpression(demand, fp);
889 }
890 break;
892 break;
893 }
894 }
895
896 // Fingerprint the objective.
897 if (model.has_objective()) {
898 fp = FingerprintRepeatedField(model.objective().vars(), fp);
899 fp = FingerprintRepeatedField(model.objective().coeffs(), fp);
900 fp = FingerprintSingleField(model.objective().offset(), fp);
902 fp = FingerprintRepeatedField(model.objective().domain(), fp);
903 } else if (model.has_floating_point_objective()) {
905 fp =
908 fp =
910 }
911
912 if (model.has_solution_hint()) {
913 fp = FingerprintRepeatedField(model.solution_hint().vars(), fp);
915 }
916
917 // TODO(user): Should we fingerprint decision strategies?
918
919 return fp;
920}
921
922#if !defined(__PORTABLE_PLATFORM__)
923namespace {
924
925// We need to print " { " instead of " {\n" to inline our variables like:
926//
927// variables { domain: [0, 1] }
928//
929// instead of
930//
931// variables {
932// domain: [0, 1] }
933class InlineFieldPrinter
934 : public google::protobuf::TextFormat::FastFieldValuePrinter {
935 void PrintMessageStart(const google::protobuf::Message& /*message*/,
936 int /*field_index*/, int /*field_count*/,
937 bool /*single_line_mode*/,
938 google::protobuf::TextFormat::BaseTextGenerator*
939 generator) const override {
940 generator->PrintLiteral(" { ");
941 }
942};
943
944class InlineMessagePrinter
945 : public google::protobuf::TextFormat::MessagePrinter {
946 public:
947 InlineMessagePrinter() {
948 printer_.SetSingleLineMode(true);
949 printer_.SetUseShortRepeatedPrimitives(true);
950 }
951
952 void Print(const google::protobuf::Message& message,
953 bool /*single_line_mode*/,
954 google::protobuf::TextFormat::BaseTextGenerator* generator)
955 const override {
956 buffer_.clear();
957 printer_.PrintToString(message, &buffer_);
958 generator->Print(buffer_.data(), buffer_.size());
959 }
960
961 private:
962 google::protobuf::TextFormat::Printer printer_;
963 mutable std::string buffer_;
964};
965
966// Register a InlineFieldPrinter() for all the fields containing the message we
967// want to print in one line.
968void RegisterFieldPrinters(
969 const google::protobuf::Descriptor* descriptor,
970 absl::flat_hash_set<const google::protobuf::Descriptor*>* descriptors,
971 google::protobuf::TextFormat::Printer* printer) {
972 // Recursion stopper.
973 if (!descriptors->insert(descriptor).second) return;
974
975 for (int i = 0; i < descriptor->field_count(); ++i) {
976 const google::protobuf::FieldDescriptor* field = descriptor->field(i);
977 if (field->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE) {
978 if (field->message_type() == IntegerVariableProto::descriptor() ||
979 field->message_type() == LinearExpressionProto::descriptor()) {
980 printer->RegisterFieldValuePrinter(field, new InlineFieldPrinter());
981 } else {
982 RegisterFieldPrinters(field->message_type(), descriptors, printer);
983 }
984 }
985 }
986}
987
988} // namespace
989
990void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer* printer) {
991 printer->SetUseShortRepeatedPrimitives(true);
992 absl::flat_hash_set<const google::protobuf::Descriptor*> descriptors;
993 RegisterFieldPrinters(CpModelProto::descriptor(), &descriptors, printer);
994 printer->RegisterMessagePrinter(IntegerVariableProto::descriptor(),
995 new InlineMessagePrinter());
996 printer->RegisterMessagePrinter(LinearExpressionProto::descriptor(),
997 new InlineMessagePrinter());
998}
999#endif // !defined(__PORTABLE_PLATFORM__)
1000
1001namespace {
1002bool ModelHasOnlyClausesAndBooleanVariables(const CpModelProto& cp_model,
1003 int* num_clauses) {
1004 const int num_vars = cp_model.variables().size();
1005 *num_clauses = 0;
1006 for (int v = 0; v < num_vars; ++v) {
1007 const auto& domain = cp_model.variables(v).domain();
1008 if (domain.size() != 2) return false;
1009 const int64_t lb = domain.Get(0);
1010 const int64_t ub = domain.Get(1);
1011 if (lb == ub) {
1012 if (lb != 0 && lb != 1) return false;
1013 ++(*num_clauses);
1014 } else if (lb != 0 || ub != 1) {
1015 return false;
1016 }
1017 }
1018 for (const ConstraintProto& ct : cp_model.constraints()) {
1019 if (ct.constraint_case() == ConstraintProto::kBoolOr) {
1020 ++(*num_clauses);
1021 } else if (ct.constraint_case() == ConstraintProto::kBoolAnd) {
1022 *num_clauses += ct.bool_and().literals().size();
1023 } else {
1024 return false;
1025 }
1026 }
1027 return true;
1028}
1029
1030bool ModelIsMaxSat(const CpModelProto& cp_model) {
1031 // We should only have only bool_or and bool_and, and an integral objective.
1032 int num_clauses = 0;
1033 if (!cp_model.has_objective()) return false;
1034 const CpObjectiveProto& obj = cp_model.objective();
1035 const double scaling = obj.scaling_factor() == 0 ? 1.0 : obj.scaling_factor();
1036 if (std::round(scaling) != scaling) return false;
1037 if (std::round(obj.offset()) != obj.offset()) return false;
1038 return ModelHasOnlyClausesAndBooleanVariables(cp_model, &num_clauses);
1039}
1040
1041bool ModelIsPureSat(const CpModelProto& cp_model, int* num_clauses) {
1042 // We just ignore the objective if there is one, and the model should just
1043 // contain bool_or and bool_and.
1044 return ModelHasOnlyClausesAndBooleanVariables(cp_model, num_clauses);
1045}
1046
1047void ConvertSatCpModelProtoToClauses(
1048 const CpModelProto& cp_model,
1049 std::function<void(const std::vector<Literal>&)> add_clause) {
1050 const int num_vars = cp_model.variables().size();
1051 for (int v = 0; v < num_vars; ++v) {
1052 const auto& domain = cp_model.variables(v).domain();
1053 const int64_t lb = domain.Get(0);
1054 const int64_t ub = domain.Get(1);
1055 if (lb == ub) {
1056 add_clause({Literal(BooleanVariable(v), lb == 1)});
1057 }
1058 }
1059 auto literal = [](int lit) {
1060 return Literal(BooleanVariable(PositiveRef(lit)), RefIsPositive(lit));
1061 };
1062 std::vector<Literal> clause;
1063 for (const ConstraintProto& ct : cp_model.constraints()) {
1064 if (ct.constraint_case() == ConstraintProto::kBoolOr) {
1065 clause.clear();
1066 for (const int lit : ct.enforcement_literal()) {
1067 clause.push_back(literal(lit).Negated());
1068 }
1069 for (const int lit : ct.bool_or().literals()) {
1070 clause.push_back(literal(lit));
1071 }
1072 add_clause(clause);
1073 } else if (ct.constraint_case() == ConstraintProto::kBoolAnd) {
1074 for (const int lit : ct.bool_and().literals()) {
1075 clause.clear();
1076 for (const int lit : ct.enforcement_literal()) {
1077 clause.push_back(literal(lit).Negated());
1078 }
1079 clause.push_back(literal(lit));
1080 add_clause(clause);
1081 }
1082 }
1083 }
1084}
1085} // namespace
1086
1087bool ConvertCpModelProtoToCnf(const CpModelProto& cp_model, std::string* out) {
1088 out->clear();
1089
1090 const int num_vars = cp_model.variables().size();
1091 int num_clauses = 0;
1092 if (!ModelIsPureSat(cp_model, &num_clauses)) return false;
1093
1094 absl::StrAppend(out, "p cnf ", num_vars, " ", num_clauses, "\n");
1095 ConvertSatCpModelProtoToClauses(
1096 cp_model, [&out](absl::Span<const Literal> clause) {
1097 for (const Literal lit : clause) {
1098 absl::StrAppend(out, lit.SignedValue(), " ");
1099 }
1100 absl::StrAppend(out, "0\n");
1101 });
1102 return true;
1103}
1104
1105bool ConvertCpModelProtoToWCnf(const CpModelProto& cp_model, std::string* out) {
1106 out->clear();
1107
1108 if (!ModelIsMaxSat(cp_model)) return false;
1109
1110 ConvertSatCpModelProtoToClauses(
1111 cp_model, [&out](const std::vector<Literal>& clause) {
1112 absl::StrAppend(out, "h ");
1113 for (const Literal lit : clause) {
1114 absl::StrAppend(out, lit.SignedValue(), " ");
1115 }
1116 absl::StrAppend(out, "0\n");
1117 });
1118
1119 // Add the objective for MaxSAT problems.
1120 const CpObjectiveProto& obj = cp_model.objective();
1121
1122 // MaxSAT is maximization, CP-SAT is minimization by default.
1123 // All weights must be > 0.
1124 //
1125 // If we maximize (b1 + b2) in CP-SAT, we will have
1126 // obj = minimize(-b1 - b2), scaling factor = -1.
1127 // In wcnf, we want 1 b1 0; 1 b2 0;
1128 //
1129 // If we minimize (b1 + b2), in CP-SAT, we will have
1130 // obj = b1 + b2, scaling factor = 1 (or non set).
1131 // In wcnf, we want 1 -b1 0; 1 -b2 0;
1132 //
1133 // Note that the objective displayed by a max-sat solve will thus not match
1134 // the real objective (it misses an offset and possibly a negation).
1135 for (int i = 0; i < obj.vars().size(); ++i) {
1136 const int64_t opp_coeff = -obj.coeffs(i);
1137 const Literal lit = Literal(BooleanVariable(PositiveRef(obj.vars(i))),
1138 RefIsPositive(obj.vars(i)));
1139 if (opp_coeff > 0) {
1140 absl::StrAppend(out, opp_coeff, " ", lit.SignedValue(), " 0\n");
1141 } else if (opp_coeff < 0) {
1142 absl::StrAppend(out, -opp_coeff, " ", lit.Negated().SignedValue(),
1143 " 0\n");
1144 }
1145 }
1146 return true;
1147}
1148
1149int CombineSeed(int base_seed, int64_t delta) {
1150 CHECK_GE(delta, 0);
1151 const uint64_t fp = FingerprintSingleField(delta, kDefaultFingerprintSeed);
1152 return static_cast<int>(FingerprintSingleField(base_seed, fp) & (0x7FFFFFFF));
1153}
1154
1157 ct.lin_max().exprs_size() != 2 || ct.lin_max().target().vars_size() > 1 ||
1158 ct.lin_max().exprs(0).vars_size() != 1 ||
1159 ct.lin_max().exprs(1).vars_size() != 1) {
1160 return false;
1161 }
1162
1163 const LinearArgumentProto& lin_max = ct.lin_max();
1164 if (lin_max.exprs(0).offset() != -lin_max.exprs(1).offset()) return false;
1165 if (PositiveRef(lin_max.exprs(0).vars(0)) !=
1166 PositiveRef(lin_max.exprs(1).vars(0))) {
1167 return false;
1168 }
1169
1170 const int64_t left_coeff = RefIsPositive(lin_max.exprs(0).vars(0))
1171 ? lin_max.exprs(0).coeffs(0)
1172 : -lin_max.exprs(0).coeffs(0);
1173 const int64_t right_coeff = RefIsPositive(lin_max.exprs(1).vars(0))
1174 ? lin_max.exprs(1).coeffs(0)
1175 : -lin_max.exprs(1).coeffs(0);
1176 return left_coeff == -right_coeff;
1177}
1178
1179} // namespace sat
1180} // namespace operations_research
Domain AdditionWith(const Domain &domain) const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::TableConstraintProto & table() const
const ::operations_research::sat::BoolArgumentProto & bool_xor() const
const ::operations_research::sat::InverseConstraintProto & inverse() const
::operations_research::sat::CumulativeConstraintProto *PROTOBUF_NONNULL mutable_cumulative()
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
const ::operations_research::sat::NoOverlapConstraintProto & no_overlap() const
const ::operations_research::sat::ListOfVariablesProto & dummy_constraint() const
const ::operations_research::sat::AllDifferentConstraintProto & all_diff() const
const ::operations_research::sat::LinearConstraintProto & linear() const
const ::operations_research::sat::BoolArgumentProto & at_most_one() const
const ::operations_research::sat::IntervalConstraintProto & interval() const
const ::operations_research::sat::RoutesConstraintProto & routes() const
const ::operations_research::sat::LinearArgumentProto & lin_max() const
const ::operations_research::sat::ElementConstraintProto & element() const
const ::operations_research::sat::BoolArgumentProto & bool_and() const
const ::operations_research::sat::BoolArgumentProto & bool_or() const
const ::operations_research::sat::LinearArgumentProto & int_mod() const
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_enforcement_literal()
const ::operations_research::sat::LinearArgumentProto & int_prod() const
const ::operations_research::sat::LinearArgumentProto & int_div() const
const ::operations_research::sat::AutomatonConstraintProto & automaton() const
const ::operations_research::sat::CircuitConstraintProto & circuit() const
const ::operations_research::sat::CumulativeConstraintProto & cumulative() const
const ::operations_research::sat::ReservoirConstraintProto & reservoir() const
const ::operations_research::sat::NoOverlap2DConstraintProto & no_overlap_2d() const
static const ::google::protobuf::Descriptor *PROTOBUF_NONNULL descriptor()
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::FloatObjectiveProto & floating_point_objective() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
const ::operations_research::sat::PartialVariableAssignment & solution_hint() const
const ::operations_research::sat::CpObjectiveProto & objective() const
const ::operations_research::sat::LinearExpressionProto & demands(int index) const
::operations_research::sat::LinearExpressionProto *PROTOBUF_NONNULL mutable_demands(int index)
const ::operations_research::sat::LinearExpressionProto & capacity() const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & linear_target() const
const ::operations_research::sat::LinearExpressionProto & linear_index() const
static const ::google::protobuf::Descriptor *PROTOBUF_NONNULL descriptor()
const ::operations_research::sat::LinearExpressionProto & size() const
const ::operations_research::sat::LinearExpressionProto & end() const
const ::operations_research::sat::LinearExpressionProto & start() const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & target() const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_vars()
void set_coeffs(int index, ::int64_t value)
static const ::google::protobuf::Descriptor *PROTOBUF_NONNULL descriptor()
const ::operations_research::sat::LinearExpressionProto & time_exprs(int index) const
const ::operations_research::sat::LinearExpressionProto & level_changes(int index) const
const ::operations_research::sat::LinearExpressionProto & exprs(int index) const
#define APPLY_TO_SINGULAR_FIELD(ct_name, field_name)
ABSL_FLAG(bool, cp_model_dump_models, false, "DEBUG ONLY. When set to true, SolveCpModel() will dump its model " "protos (original model, presolved model, mapping model) in text " "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|" "mapping_model}.pb.txt.")
#define APPLY_TO_REPEATED_FIELD(ct_name, field_name)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:55
void SetToNegatedLinearExpression(const LinearExpressionProto &input_expr, LinearExpressionProto *output_negated_expr)
constexpr uint64_t kDefaultFingerprintSeed
uint64_t FingerprintRepeatedField(const google::protobuf::RepeatedField< T > &sequence, uint64_t seed)
void ApplyToAllVariableIndices(absl::FunctionRef< void(int *)> f, ConstraintProto *ct)
bool LinearExpressionProtosAreEqual(const LinearExpressionProto &a, const LinearExpressionProto &b, int64_t b_scaling)
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, absl::Span< const int64_t > solution)
bool ConvertCpModelProtoToWCnf(const CpModelProto &cp_model, std::string *out)
void ApplyToAllLiteralIndices(absl::FunctionRef< void(int *)> f, ConstraintProto *ct)
int CombineSeed(int base_seed, int64_t delta)
bool SafeAddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)
uint64_t FingerprintModel(const CpModelProto &model, uint64_t seed)
uint64_t FingerprintSingleField(const T &field, uint64_t seed)
bool ConvertCpModelProtoToCnf(const CpModelProto &cp_model, std::string *out)
uint64_t FingerprintExpression(const LinearExpressionProto &lin, uint64_t seed)
bool ExpressionIsAffine(const LinearExpressionProto &expr)
std::vector< int > UsedVariables(const ConstraintProto &ct)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
void LiteralsToLinear(absl::Span< const int > literals, int64_t lb, int64_t ub, LinearConstraintProto *linear)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void SetupTextFormatPrinter(google::protobuf::TextFormat::Printer *printer)
absl::string_view ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
bool ExpressionContainsSingleRef(const LinearExpressionProto &expr)
int64_t LinearExpressionGcd(const LinearExpressionProto &expr, int64_t gcd)
void DivideLinearExpression(int64_t divisor, LinearExpressionProto *expr)
void AddLinearExpressionToLinearConstraint(const LinearExpressionProto &expr, int64_t coefficient, LinearConstraintProto *linear)
int GetSingleRefFromExpression(const LinearExpressionProto &expr)
bool IsAffineIntAbs(const ConstraintProto &ct)
void ApplyToAllIntervalIndices(absl::FunctionRef< void(int *)> f, ConstraintProto *ct)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
void AddWeightedLiteralToLinearConstraint(int lit, int64_t coeff, LinearConstraintProto *linear, int64_t *offset)
OR-Tools root namespace.
bool AtMinOrMaxInt64(int64_t x)
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
ClosedInterval::Iterator end(ClosedInterval interval)
int64_t CapProd(int64_t x, int64_t y)