OR-Tools  9.3
flatzinc/model.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <cstdint>
17#include <limits>
18#include <set>
19#include <vector>
20
21#include "absl/container/flat_hash_set.h"
22#include "absl/strings/str_cat.h"
23#include "absl/strings/str_format.h"
24#include "absl/strings/str_join.h"
25#include "absl/strings/string_view.h"
28
29namespace operations_research {
30namespace fz {
31// ----- Domain -----
32
33Domain Domain::IntegerList(std::vector<int64_t> values) {
34 Domain result;
35 result.values = std::move(values);
37 return result;
38}
39
41 Domain result;
42 result.is_interval = true;
43 return result;
44}
45
47 Domain result;
48 result.values.push_back(value);
49 return result;
50}
51
52Domain Domain::Interval(int64_t included_min, int64_t included_max) {
53 Domain result;
54 result.is_interval = true;
55 result.values.push_back(included_min);
56 result.values.push_back(included_max);
57 return result;
58}
59
61 Domain result;
62 result.display_as_boolean = true;
63 result.values.push_back(0);
64 result.values.push_back(1);
65 return result;
66}
67
68Domain Domain::SetOfIntegerList(std::vector<int64_t> values) {
69 Domain result = IntegerList(std::move(values));
70 result.is_a_set = true;
71 return result;
72}
73
75 Domain result = AllInt64();
76 result.is_a_set = true;
77 return result;
78}
79
81 Domain result = IntegerValue(value);
82 result.is_a_set = true;
83 return result;
84}
85
86Domain Domain::SetOfInterval(int64_t included_min, int64_t included_max) {
87 Domain result = Interval(included_min, included_max);
88 result.is_a_set = true;
89 return result;
90}
91
93 Domain result = Boolean();
94 result.is_a_set = true;
95 return result;
96}
97
99
101 Domain result;
102 result.is_interval = true;
103 result.is_float = true;
104 return result;
105}
106
107Domain Domain::FloatInterval(double lb, double ub) {
108 Domain result;
109 result.is_interval = true;
110 result.is_float = true;
111 result.float_values = {lb, ub};
112 return result;
113}
114
116 Domain result;
117 result.is_float = true;
118 result.float_values.push_back(value);
119 return result;
120}
121
123 if (is_float) {
124 return IntersectWithFloatDomain(domain);
125 }
126 if (domain.is_interval) {
127 if (!domain.values.empty()) {
128 return IntersectWithInterval(domain.values[0], domain.values[1]);
129 }
130 return false;
131 }
132 if (is_interval) {
133 is_interval = false; // Other is not an interval.
134 if (values.empty()) {
135 values = domain.values;
136 } else {
137 const int64_t imin = values[0];
138 const int64_t imax = values[1];
139 values = domain.values;
140 IntersectWithInterval(imin, imax);
141 }
142 return true;
143 }
144 // now deal with the intersection of two lists of values
145 return IntersectWithListOfIntegers(domain.values);
146}
147
150}
151
152bool Domain::IntersectWithInterval(int64_t interval_min, int64_t interval_max) {
153 if (interval_min > interval_max) { // Empty interval -> empty domain.
154 is_interval = false;
155 values.clear();
156 return true;
157 } else if (is_interval) {
158 if (values.empty()) {
159 values.push_back(interval_min);
160 values.push_back(interval_max);
161 return true;
162 } else {
163 if (values[0] >= interval_min && values[1] <= interval_max) return false;
164 values[0] = std::max(values[0], interval_min);
165 values[1] = std::min(values[1], interval_max);
166 if (values[0] > values[1]) {
167 values.clear();
168 is_interval = false;
169 } else if (values[0] == values[1]) {
170 is_interval = false;
171 values.pop_back();
172 }
173 return true;
174 }
175 } else {
176 if (!values.empty()) {
177 std::sort(values.begin(), values.end());
178 std::vector<int64_t> new_values;
179 new_values.reserve(values.size());
180 bool changed = false;
181 for (const int64_t val : values) {
182 if (val > interval_max) {
183 changed = true;
184 break;
185 }
186 if (val >= interval_min &&
187 (new_values.empty() || val != new_values.back())) {
188 new_values.push_back(val);
189 } else {
190 changed = true;
191 }
192 }
193 values.swap(new_values);
194 return changed;
195 }
196 }
197 return false;
198}
199
200bool Domain::IntersectWithListOfIntegers(const std::vector<int64_t>& integers) {
201 if (is_interval) {
202 const int64_t dmin =
204 const int64_t dmax =
206 values.clear();
207 for (const int64_t v : integers) {
208 if (v >= dmin && v <= dmax) values.push_back(v);
209 }
211 if (!values.empty() &&
212 values.back() - values.front() == values.size() - 1 &&
213 values.size() >= 2) {
214 if (values.size() > 2) {
215 // Contiguous case.
216 const int64_t last = values.back();
217 values.resize(2);
218 values[1] = last;
219 }
220 return values[0] != dmin || values[1] != dmax;
221 } else {
222 // This also covers and invalid (empty) domain.
223 is_interval = false;
224 return true;
225 }
226 } else {
227 // TODO(user): Investigate faster code for small arrays.
228 std::sort(values.begin(), values.end());
229 absl::flat_hash_set<int64_t> other_values(integers.begin(), integers.end());
230 std::vector<int64_t> new_values;
231 new_values.reserve(std::min(values.size(), integers.size()));
232 bool changed = false;
233 for (const int64_t val : values) {
234 if (other_values.contains(val)) {
235 if (new_values.empty() || val != new_values.back()) {
236 new_values.push_back(val);
237 }
238 } else {
239 changed = true;
240 }
241 }
242 values.swap(new_values);
243 return changed;
244 }
245}
246
248 CHECK(domain.is_float);
249 if (!is_interval && float_values.empty()) {
250 // Empty domain. Nothing to do.
251 return false;
252 }
253 if (!domain.is_interval && domain.float_values.empty()) {
254 return SetEmptyFloatDomain();
255 }
256 if (domain.is_interval && domain.float_values.empty()) {
257 // domain is all floats. Nothing to do.
258 return false;
259 }
260
261 if (is_interval && float_values.empty()) { // Currently all floats.
262 // Copy the domain.
263 is_interval = domain.is_interval;
264 float_values = domain.float_values;
265 return true;
266 }
267
268 if (is_interval) {
269 // this is a double interval.
270 CHECK_EQ(2, float_values.size());
271 if (domain.is_interval) {
272 bool changed = false;
273 if (float_values[0] < domain.float_values[0]) {
274 float_values[0] = domain.float_values[0];
275 changed = true;
276 }
277 if (float_values[1] > domain.float_values[1]) {
278 float_values[1] = domain.float_values[1];
279 changed = true;
280 }
281 if (float_values[0] > float_values[1]) {
282 return SetEmptyFloatDomain();
283 }
284 return changed;
285 } else {
286 CHECK_EQ(1, domain.float_values.size());
287 const double value = domain.float_values[0];
288 if (value >= float_values[0] && value <= float_values[1]) {
289 is_interval = false;
291 return true;
292 }
293 return SetEmptyFloatDomain();
294 }
295 } else {
296 // this is a single double.
297 CHECK_EQ(1, float_values.size());
298 const double value = float_values[0];
299 if (domain.is_interval) {
300 CHECK_EQ(2, domain.float_values.size());
301 if (value >= domain.float_values[0] && value <= domain.float_values[1]) {
302 // value is compatible with domain.
303 return true;
304 }
305 return SetEmptyFloatDomain();
306 } else {
307 CHECK_EQ(1, domain.float_values.size());
308 if (value == domain.float_values[0]) {
309 // Same value;
310 return true;
311 }
312 return SetEmptyFloatDomain();
313 }
314 }
315}
316
319 is_interval = false;
320 float_values.clear();
321 return true;
322}
323
325 return (values.size() == 1 || (values.size() == 2 && values[0] == values[1]));
326}
327
328bool Domain::empty() const {
329 return is_interval ? (values.size() == 2 && values[0] > values[1])
330 : values.empty();
331}
332
333int64_t Domain::Min() const {
334 CHECK(!empty());
336 : values.front();
337}
338
339int64_t Domain::Max() const {
340 CHECK(!empty());
342 : values.back();
343}
344
345int64_t Domain::Value() const {
347 return values.front();
348}
349
350bool Domain::IsAllInt64() const {
351 return is_interval &&
352 (values.empty() || (values[0] == std::numeric_limits<int64_t>::min() &&
354}
355
356bool Domain::Contains(int64_t value) const {
357 if (is_interval) {
358 if (values.empty()) {
359 return true;
360 } else {
361 return value >= values[0] && value <= values[1];
362 }
363 } else {
364 return std::find(values.begin(), values.end(), value) != values.end();
365 }
366}
367
368namespace {
369bool IntervalOverlapValues(int64_t lb, int64_t ub,
370 const std::vector<int64_t>& values) {
371 for (int64_t value : values) {
372 if (lb <= value && value <= ub) {
373 return true;
374 }
375 }
376 return false;
377}
378} // namespace
379
380bool Domain::OverlapsIntList(const std::vector<int64_t>& vec) const {
381 if (IsAllInt64()) {
382 return true;
383 }
384 if (is_interval) {
385 CHECK(!values.empty());
386 return IntervalOverlapValues(values[0], values[1], vec);
387 } else {
388 // TODO(user): Better algorithm, sort and compare increasingly.
389 const std::vector<int64_t>& to_scan =
390 values.size() <= vec.size() ? values : vec;
391 const absl::flat_hash_set<int64_t> container =
392 values.size() <= vec.size()
393 ? absl::flat_hash_set<int64_t>(vec.begin(), vec.end())
394 : absl::flat_hash_set<int64_t>(values.begin(), values.end());
395 for (int64_t value : to_scan) {
396 if (container.contains(value)) {
397 return true;
398 }
399 }
400 return false;
401 }
402}
403
404bool Domain::OverlapsIntInterval(int64_t lb, int64_t ub) const {
405 if (IsAllInt64()) {
406 return true;
407 }
408 if (is_interval) {
409 CHECK(!values.empty());
410 const int64_t dlb = values[0];
411 const int64_t dub = values[1];
412 return !(dub < lb || dlb > ub);
413 } else {
414 return IntervalOverlapValues(lb, ub, values);
415 }
416}
417
418bool Domain::OverlapsDomain(const Domain& other) const {
419 if (other.is_interval) {
420 if (other.values.empty()) {
421 return true;
422 } else {
423 return OverlapsIntInterval(other.values[0], other.values[1]);
424 }
425 } else {
426 return OverlapsIntList(other.values);
427 }
428}
429
431 if (is_interval) {
432 if (values.empty()) {
433 return false;
434 } else if (value == values[0] && value != values[1]) {
435 values[0]++;
436 return true;
437 } else if (value == values[1] && value != values[0]) {
438 values[1]--;
439 return true;
440 } else if (values[1] - values[0] < 1024 && value > values[0] &&
441 value < values[1]) { // small
442 const int64_t vmax = values[1];
443 values.pop_back();
444 values.reserve(vmax - values[0]);
445 for (int64_t v = values[0] + 1; v <= vmax; ++v) {
446 if (v != value) {
447 values.push_back(v);
448 }
449 }
450 is_interval = false;
451 return true;
452 }
453 } else {
454 values.erase(std::remove(values.begin(), values.end(), value),
455 values.end());
456 return true;
457 }
458 return false;
459}
460
461std::string Domain::DebugString() const {
462 if (is_float) {
463 switch (float_values.size()) {
464 case 0:
465 return "float";
466 case 1:
467 return absl::StrCat(float_values[0]);
468 case 2:
469 return absl::StrCat("[", float_values[0], "..", float_values[1], "]");
470 default:
471 LOG(DFATAL) << "Error with float domain";
472 return "error_float";
473 }
474 }
475 if (is_interval) {
476 if (values.empty()) {
477 return "int";
478 } else {
479 return absl::StrFormat("[%d..%d]", values[0], values[1]);
480 }
481 } else if (values.size() == 1) {
482 return absl::StrCat(values.back());
483 } else {
484 return absl::StrFormat("[%s]", absl::StrJoin(values, ", "));
485 }
486}
487
488// ----- Argument -----
489
491 Argument result;
492 result.type = INT_VALUE;
493 result.values.push_back(value);
494 return result;
495}
496
497Argument Argument::Interval(int64_t imin, int64_t imax) {
498 Argument result;
499 result.type = INT_INTERVAL;
500 result.values.push_back(imin);
501 result.values.push_back(imax);
502 return result;
503}
504
505Argument Argument::IntegerList(std::vector<int64_t> values) {
506 Argument result;
507 result.type = INT_LIST;
508 result.values = std::move(values);
509 return result;
510}
511
512Argument Argument::DomainList(std::vector<Domain> domains) {
513 Argument result;
514 result.type = DOMAIN_LIST;
515 result.domains = std::move(domains);
516 return result;
517}
518
520 Argument result;
521 result.type = VAR_REF;
522 result.variables.push_back(var);
523 return result;
524}
525
526Argument Argument::VarRefArray(std::vector<Variable*> vars) {
527 Argument result;
528 result.type = VAR_REF_ARRAY;
529 result.variables = std::move(vars);
530 return result;
531}
532
534 Argument result;
535 result.type = VOID_ARGUMENT;
536 return result;
537}
538
540 if (domain.is_interval) {
541 if (domain.values.empty()) {
544 } else {
545 return Argument::Interval(domain.values[0], domain.values[1]);
546 }
547 } else {
548 return Argument::IntegerList(domain.values);
549 }
550}
551
553 Argument result;
554 result.type = FLOAT_VALUE;
555 result.floats.push_back(value);
556 return result;
557}
558
559Argument Argument::FloatInterval(double lb, double ub) {
560 Argument result;
561 result.type = FLOAT_INTERVAL;
562 result.floats.push_back(lb);
563 result.floats.push_back(ub);
564 return result;
565}
566
567Argument Argument::FloatList(std::vector<double> floats) {
568 Argument result;
569 result.type = FLOAT_LIST;
570 result.floats = std::move(floats);
571 return result;
572}
573
574std::string Argument::DebugString() const {
575 switch (type) {
576 case INT_VALUE:
577 return absl::StrFormat("%d", values[0]);
578 case INT_INTERVAL:
579 return absl::StrFormat("[%d..%d]", values[0], values[1]);
580 case INT_LIST:
581 return absl::StrFormat("[%s]", absl::StrJoin(values, ", "));
582 case DOMAIN_LIST:
583 return absl::StrFormat("[%s]", JoinDebugString(domains, ", "));
584 case VAR_REF:
585 return variables[0]->name;
586 case VAR_REF_ARRAY: {
587 std::string result = "[";
588 for (int i = 0; i < variables.size(); ++i) {
589 result.append(variables[i]->name);
590 result.append(i != variables.size() - 1 ? ", " : "]");
591 }
592 return result;
593 }
594 case VOID_ARGUMENT:
595 return "VoidArgument";
596 case FLOAT_VALUE:
597 return absl::StrCat(floats[0]);
598 case FLOAT_INTERVAL:
599 return absl::StrCat("[", floats[0], "..", floats[1], "]");
600 case FLOAT_LIST:
601 return absl::StrFormat("[%s]", absl::StrJoin(floats, ", "));
602 }
603 LOG(FATAL) << "Unhandled case in DebugString " << static_cast<int>(type);
604 return "";
605}
606
607bool Argument::IsVariable() const { return type == VAR_REF; }
608
610 return (type == INT_VALUE || (type == INT_LIST && values.size() == 1) ||
611 (type == INT_INTERVAL && values[0] == values[1]) ||
612 (type == VAR_REF && variables[0]->domain.HasOneValue()));
613}
614
615int64_t Argument::Value() const {
616 DCHECK(HasOneValue()) << "Value() called on unbound Argument: "
617 << DebugString();
618 switch (type) {
619 case INT_VALUE:
620 case INT_INTERVAL:
621 case INT_LIST:
622 return values[0];
623 case VAR_REF: {
624 return variables[0]->domain.values[0];
625 }
626 default: {
627 LOG(FATAL) << "Should not be here";
628 return 0;
629 }
630 }
631}
632
634 switch (type) {
635 case INT_VALUE:
636 return false;
637 case INT_INTERVAL:
638 return false;
639 case INT_LIST:
640 return true;
641 case DOMAIN_LIST: {
642 for (const Domain& domain : domains) {
643 if (!domain.HasOneValue()) {
644 return false;
645 }
646 }
647 return true;
648 }
649 case VAR_REF:
650 return false;
651 case VAR_REF_ARRAY: {
652 for (Variable* var : variables) {
653 if (!var->domain.HasOneValue()) {
654 return false;
655 }
656 }
657 return true;
658 }
659 case VOID_ARGUMENT:
660 return false;
661 case FLOAT_VALUE:
662 return false;
663 case FLOAT_INTERVAL:
664 return false;
665 case FLOAT_LIST:
666 return false;
667 }
668}
669
670bool Argument::Contains(int64_t value) const {
671 switch (type) {
672 case Argument::INT_LIST: {
673 return std::find(values.begin(), values.end(), value) != values.end();
674 }
676 return value >= values.front() && value <= values.back();
677 }
678 case Argument::INT_VALUE: {
679 return value == values.front();
680 }
681 default: {
682 LOG(FATAL) << "Cannot call Contains() on " << DebugString();
683 return false;
684 }
685 }
686}
687
688int64_t Argument::ValueAt(int pos) const {
689 switch (type) {
690 case INT_LIST:
691 CHECK_GE(pos, 0);
692 CHECK_LT(pos, values.size());
693 return values[pos];
694 case DOMAIN_LIST: {
695 CHECK_GE(pos, 0);
696 CHECK_LT(pos, domains.size());
697 return domains[pos].Value();
698 }
699 case VAR_REF_ARRAY: {
700 CHECK_GE(pos, 0);
701 CHECK_LT(pos, variables.size());
702 return variables[pos]->domain.Value();
703 }
704 default: {
705 LOG(FATAL) << "Should not be here";
706 return 0;
707 }
708 }
709}
710
711bool Argument::HasOneValueAt(int pos) const {
712 switch (type) {
713 case INT_LIST:
714 CHECK_GE(pos, 0);
715 CHECK_LT(pos, values.size());
716 return true;
717 case DOMAIN_LIST: {
718 CHECK_GE(pos, 0);
719 CHECK_LT(pos, domains.size());
720 return domains[pos].HasOneValue();
721 }
722 case VAR_REF_ARRAY: {
723 CHECK_GE(pos, 0);
724 CHECK_LT(pos, variables.size());
725 return variables[pos]->domain.HasOneValue();
726 }
727 default: {
728 LOG(FATAL) << "Should not be here";
729 return false;
730 }
731 }
732}
733
735 return type == VAR_REF ? variables[0] : nullptr;
736}
737
738Variable* Argument::VarAt(int pos) const {
739 return type == VAR_REF_ARRAY ? variables[pos] : nullptr;
740}
741
742int Argument::Size() const {
743 switch (type) {
744 case INT_LIST:
745 return values.size();
746 case DOMAIN_LIST: {
747 return domains.size();
748 }
749 case VAR_REF_ARRAY: {
750 return variables.size();
751 }
752 case VOID_ARGUMENT: {
753 return 0;
754 }
755 default: {
756 LOG(FATAL) << "Should not be here";
757 return 0;
758 }
759 }
760}
761
762// ----- Variable -----
763
764Variable::Variable(absl::string_view name_, const Domain& domain_,
765 bool temporary_)
766 : name(name_), domain(domain_), temporary(temporary_), active(true) {
767 if (!domain.is_interval) {
768 gtl::STLSortAndRemoveDuplicates(&domain.values);
769 }
770}
771
772bool Variable::Merge(absl::string_view other_name, const Domain& other_domain,
773 bool other_temporary) {
774 if (temporary && !other_temporary) {
775 temporary = false;
776 name = other_name;
777 }
778 domain.IntersectWithDomain(other_domain);
779 return true;
780}
781
782std::string Variable::DebugString() const {
783 if (!domain.is_interval && domain.values.size() == 1) {
784 return absl::StrFormat("% d", domain.values.back());
785 } else {
786 return absl::StrFormat("%s(%s%s)%s", name, domain.DebugString(),
787 temporary ? ", temporary" : "",
788 active ? "" : " [removed during presolve]");
789 }
790}
791
792// ----- Constraint -----
793
794std::string Constraint::DebugString() const {
795 const std::string strong = strong_propagation ? "strong propagation" : "";
796 const std::string presolve_status_str =
797 active ? ""
798 : (presolve_propagation_done ? "[propagated during presolve]"
799 : "[removed during presolve]");
800 return absl::StrFormat("%s(%s)%s %s", type, JoinDebugString(arguments, ", "),
801 strong, presolve_status_str);
802}
803
804void Constraint::RemoveArg(int arg_pos) {
805 arguments.erase(arguments.begin() + arg_pos);
806}
807
809 active = false;
810 // TODO(user): Reclaim arguments and memory.
811}
812
814 type = "false_constraint";
815 arguments.clear();
816}
817
818// ----- Annotation -----
819
821 Annotation result;
822 result.type = ANNOTATION_LIST;
823 result.interval_min = 0;
824 result.interval_max = 0;
825 return result;
826}
827
828Annotation Annotation::AnnotationList(std::vector<Annotation> list) {
829 Annotation result;
830 result.type = ANNOTATION_LIST;
831 result.interval_min = 0;
832 result.interval_max = 0;
833 result.annotations = std::move(list);
834 return result;
835}
836
837Annotation Annotation::Identifier(absl::string_view id) {
838 Annotation result;
839 result.type = IDENTIFIER;
840 result.interval_min = 0;
841 result.interval_max = 0;
842 result.id = id;
843 return result;
844}
845
847 std::vector<Annotation> args) {
848 Annotation result;
849 result.type = FUNCTION_CALL;
850 result.interval_min = 0;
851 result.interval_max = 0;
852 result.id = id;
853 result.annotations = std::move(args);
854 return result;
855}
856
858 Annotation result;
859 result.type = FUNCTION_CALL;
860 result.interval_min = 0;
861 result.interval_max = 0;
862 result.id = id;
863 return result;
864}
865
866Annotation Annotation::Interval(int64_t interval_min, int64_t interval_max) {
867 Annotation result;
868 result.type = INTERVAL;
869 result.interval_min = interval_min;
870 result.interval_max = interval_max;
871 return result;
872}
873
875 Annotation result;
876 result.type = INT_VALUE;
877 result.interval_min = value;
878 return result;
879}
880
882 Annotation result;
883 result.type = VAR_REF;
884 result.interval_min = 0;
885 result.interval_max = 0;
886 result.variables.push_back(var);
887 return result;
888}
889
890Annotation Annotation::VarRefArray(std::vector<Variable*> variables) {
891 Annotation result;
892 result.type = VAR_REF_ARRAY;
893 result.interval_min = 0;
894 result.interval_max = 0;
895 result.variables = std::move(variables);
896 return result;
897}
898
899Annotation Annotation::String(absl::string_view str) {
900 Annotation result;
901 result.type = STRING_VALUE;
902 result.interval_min = 0;
903 result.interval_max = 0;
904 result.string_value = str;
905 return result;
906}
907
908void Annotation::AppendAllVariables(std::vector<Variable*>* const vars) const {
909 for (const Annotation& ann : annotations) {
910 ann.AppendAllVariables(vars);
911 }
912 if (!variables.empty()) {
913 vars->insert(vars->end(), variables.begin(), variables.end());
914 }
915}
916
917std::string Annotation::DebugString() const {
918 switch (type) {
919 case ANNOTATION_LIST: {
920 return absl::StrFormat("[%s]", JoinDebugString(annotations, ", "));
921 }
922 case IDENTIFIER: {
923 return id;
924 }
925 case FUNCTION_CALL: {
926 return absl::StrFormat("%s(%s)", id, JoinDebugString(annotations, ", "));
927 }
928 case INTERVAL: {
929 return absl::StrFormat("%d..%d", interval_min, interval_max);
930 }
931 case INT_VALUE: {
932 return absl::StrCat(interval_min);
933 }
934 case VAR_REF: {
935 return variables.front()->name;
936 }
937 case VAR_REF_ARRAY: {
938 std::string result = "[";
939 for (int i = 0; i < variables.size(); ++i) {
940 result.append(variables[i]->DebugString());
941 result.append(i != variables.size() - 1 ? ", " : "]");
942 }
943 return result;
944 }
945 case STRING_VALUE: {
946 return absl::StrFormat("\"%s\"", string_value);
947 }
948 }
949 LOG(FATAL) << "Unhandled case in DebugString " << static_cast<int>(type);
950 return "";
951}
952
953// ----- SolutionOutputSpecs -----
954
956 return absl::StrFormat("%d..%d", min_value, max_value);
957}
958
960 absl::string_view name, Variable* variable, bool display_as_boolean) {
961 SolutionOutputSpecs result;
962 result.name = name;
963 result.variable = variable;
965 return result;
966}
967
969 absl::string_view name, std::vector<Bounds> bounds,
970 std::vector<Variable*> flat_variables, bool display_as_boolean) {
971 SolutionOutputSpecs result;
972 result.variable = nullptr;
973 result.name = name;
974 result.bounds = std::move(bounds);
975 result.flat_variables = std::move(flat_variables);
977 return result;
978}
979
981 SolutionOutputSpecs result;
982 result.variable = nullptr;
983 result.display_as_boolean = false;
984 return result;
985}
986
988 if (variable != nullptr) {
989 return absl::StrFormat("output_var(%s)", variable->name);
990 } else {
991 return absl::StrFormat("output_array([%s] [%s])",
992 JoinDebugString(bounds, ", "),
994 }
995}
996
997// ----- Model -----
998
1000 gtl::STLDeleteElements(&variables_);
1001 gtl::STLDeleteElements(&constraints_);
1002}
1003
1004Variable* Model::AddVariable(absl::string_view name, const Domain& domain,
1005 bool defined) {
1006 Variable* const var = new Variable(name, domain, defined);
1007 variables_.push_back(var);
1008 return var;
1009}
1010
1011// TODO(user): Create only once constant per value.
1013 Variable* const var =
1014 new Variable(absl::StrCat(value), Domain::IntegerValue(value), true);
1015 variables_.push_back(var);
1016 return var;
1017}
1018
1020 Variable* const var =
1021 new Variable(absl::StrCat(value), Domain::FloatValue(value), true);
1022 variables_.push_back(var);
1023 return var;
1024}
1025
1026void Model::AddConstraint(absl::string_view id, std::vector<Argument> arguments,
1027 bool is_domain) {
1028 Constraint* const constraint =
1029 new Constraint(id, std::move(arguments), is_domain);
1030 constraints_.push_back(constraint);
1031}
1032
1033void Model::AddConstraint(const std::string& id,
1034 std::vector<Argument> arguments) {
1035 AddConstraint(id, std::move(arguments), false);
1036}
1037
1039 output_.push_back(std::move(output));
1040}
1041
1042void Model::Satisfy(std::vector<Annotation> search_annotations) {
1043 objective_ = nullptr;
1044 search_annotations_ = std::move(search_annotations);
1045}
1046
1048 std::vector<Annotation> search_annotations) {
1049 objective_ = obj;
1050 maximize_ = false;
1051 search_annotations_ = std::move(search_annotations);
1052}
1053
1055 std::vector<Annotation> search_annotations) {
1056 objective_ = obj;
1057 maximize_ = true;
1058 search_annotations_ = std::move(search_annotations);
1059}
1060
1061std::string Model::DebugString() const {
1062 std::string output = absl::StrFormat("Model %s\nVariables\n", name_);
1063 for (int i = 0; i < variables_.size(); ++i) {
1064 absl::StrAppendFormat(&output, " %s\n", variables_[i]->DebugString());
1065 }
1066 output.append("Constraints\n");
1067 for (int i = 0; i < constraints_.size(); ++i) {
1068 if (constraints_[i] != nullptr) {
1069 absl::StrAppendFormat(&output, " %s\n", constraints_[i]->DebugString());
1070 }
1071 }
1072 if (objective_ != nullptr) {
1073 absl::StrAppendFormat(&output, "%s %s\n %s\n",
1074 maximize_ ? "Maximize" : "Minimize", objective_->name,
1075 JoinDebugString(search_annotations_, ", "));
1076 } else {
1077 absl::StrAppendFormat(&output, "Satisfy\n %s\n",
1078 JoinDebugString(search_annotations_, ", "));
1079 }
1080 output.append("Output\n");
1081 for (int i = 0; i < output_.size(); ++i) {
1082 absl::StrAppendFormat(&output, " %s\n", output_[i].DebugString());
1083 }
1084
1085 return output;
1086}
1087
1089 for (Variable* var : variables_) {
1090 if (var->domain.empty()) {
1091 return true;
1092 }
1093 }
1094 for (Constraint* ct : constraints_) {
1095 if (ct->type == "false_constraint") {
1096 return true;
1097 }
1098 }
1099
1100 return false;
1101}
1102
1103// ----- Model statistics -----
1104
1106 SOLVER_LOG(logger_, "Model ", model_.name());
1107 for (const auto& it : constraints_per_type_) {
1108 SOLVER_LOG(logger_, " - ", it.first, ": ", it.second.size());
1109 }
1110 if (model_.objective() == nullptr) {
1111 SOLVER_LOG(logger_, " - Satisfaction problem");
1112 } else {
1113 SOLVER_LOG(logger_, " - ",
1114 (model_.maximize() ? "Maximization" : "Minimization"),
1115 " problem");
1116 }
1117 SOLVER_LOG(logger_);
1118}
1119
1121 constraints_per_type_.clear();
1122 constraints_per_variables_.clear();
1123 for (Constraint* const ct : model_.constraints()) {
1124 if (ct != nullptr && ct->active) {
1125 constraints_per_type_[ct->type].push_back(ct);
1126 absl::flat_hash_set<const Variable*> marked;
1127 for (const Argument& arg : ct->arguments) {
1128 for (Variable* const var : arg.variables) {
1129 marked.insert(var);
1130 }
1131 }
1132 for (const Variable* const var : marked) {
1133 constraints_per_variables_[var].push_back(ct);
1134 }
1135 }
1136 }
1137}
1138
1139// Flatten Search annotations.
1140void FlattenAnnotations(const Annotation& ann, std::vector<Annotation>* out) {
1141 if (ann.type == Annotation::ANNOTATION_LIST ||
1142 ann.IsFunctionCallWithIdentifier("seq_search")) {
1143 for (const Annotation& inner : ann.annotations) {
1144 FlattenAnnotations(inner, out);
1145 }
1146 } else {
1147 out->push_back(ann);
1148 }
1149}
1150
1151} // namespace fz
1152} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_LT(val1, val2)
Definition: base/logging.h:706
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GE(val1, val2)
Definition: base/logging.h:707
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:890
void AddConstraint(absl::string_view id, std::vector< Argument > arguments, bool is_domain)
Variable * AddConstant(int64_t value)
void Satisfy(std::vector< Annotation > search_annotations)
void AddOutput(SolutionOutputSpecs output)
Variable * AddVariable(absl::string_view name, const Domain &domain, bool defined)
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Variable * AddFloatConstant(double value)
const std::string name
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
const int FATAL
Definition: log_severity.h:32
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
void STLDeleteElements(T *container)
Definition: stl_util.h:372
void FlattenAnnotations(const Annotation &ann, std::vector< Annotation > *out)
Collection of objects used to extend the Constraint Solver library.
std::string JoinNameFieldPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:58
std::string JoinDebugString(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:38
const bool maximize_
Definition: search.cc:2559
IntVar *const objective_
Definition: search.cc:3017
static Annotation IntegerValue(int64_t value)
void AppendAllVariables(std::vector< Variable * > *vars) const
static Annotation String(absl::string_view str)
static Annotation FunctionCallWithArguments(absl::string_view id, std::vector< Annotation > args)
bool IsFunctionCallWithIdentifier(absl::string_view identifier) const
static Annotation FunctionCall(absl::string_view id)
static Annotation AnnotationList(std::vector< Annotation > list)
std::vector< Variable * > variables
std::vector< Annotation > annotations
static Annotation Interval(int64_t interval_min, int64_t interval_max)
static Annotation VarRefArray(std::vector< Variable * > variables)
static Annotation VarRef(Variable *const var)
static Annotation Identifier(absl::string_view id)
static Argument FloatInterval(double lb, double ub)
static Argument DomainList(std::vector< Domain > domains)
Variable * VarAt(int pos) const
static Argument VarRef(Variable *const var)
bool Contains(int64_t value) const
static Argument IntegerList(std::vector< int64_t > values)
static Argument VarRefArray(std::vector< Variable * > vars)
static Argument IntegerValue(int64_t value)
static Argument Interval(int64_t imin, int64_t imax)
std::vector< Variable * > variables
static Argument FloatValue(double value)
std::vector< int64_t > values
int64_t ValueAt(int pos) const
static Argument FloatList(std::vector< double > floats)
static Argument FromDomain(const Domain &domain)
std::vector< Argument > arguments
static Domain IntegerValue(int64_t value)
bool Contains(int64_t value) const
bool OverlapsDomain(const Domain &other) const
bool IntersectWithSingleton(int64_t value)
static Domain SetOfInterval(int64_t included_min, int64_t included_max)
static Domain IntegerList(std::vector< int64_t > values)
bool IntersectWithInterval(int64_t interval_min, int64_t interval_max)
bool IntersectWithFloatDomain(const Domain &domain)
bool IntersectWithDomain(const Domain &domain)
std::vector< double > float_values
static Domain FloatInterval(double lb, double ub)
bool OverlapsIntInterval(int64_t lb, int64_t ub) const
static Domain SetOfIntegerValue(int64_t value)
bool OverlapsIntList(const std::vector< int64_t > &vec) const
static Domain Interval(int64_t included_min, int64_t included_max)
std::vector< int64_t > values
static Domain SetOfIntegerList(std::vector< int64_t > values)
static Domain FloatValue(double value)
bool IntersectWithListOfIntegers(const std::vector< int64_t > &integers)
static SolutionOutputSpecs MultiDimensionalArray(absl::string_view name, std::vector< Bounds > bounds, std::vector< Variable * > flat_variables, bool display_as_boolean)
static SolutionOutputSpecs SingleVariable(absl::string_view name, Variable *variable, bool display_as_boolean)
bool Merge(absl::string_view other_name, const Domain &other_domain, bool other_temporary)
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69