OR-Tools  9.1
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"
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 CHECK(domains[pos].HasOneValue());
698 return domains[pos].Value();
699 }
700 case VAR_REF_ARRAY: {
701 CHECK_GE(pos, 0);
702 CHECK_LT(pos, variables.size());
703 CHECK(variables[pos]->domain.HasOneValue());
704 return variables[pos]->domain.Value();
705 }
706 default: {
707 LOG(FATAL) << "Should not be here";
708 return 0;
709 }
710 }
711}
712
714 return type == VAR_REF ? variables[0] : nullptr;
715}
716
717Variable* Argument::VarAt(int pos) const {
718 return type == VAR_REF_ARRAY ? variables[pos] : nullptr;
719}
720
721// ----- Variable -----
722
723Variable::Variable(const std::string& name_, const Domain& domain_,
724 bool temporary_)
725 : name(name_), domain(domain_), temporary(temporary_), active(true) {
726 if (!domain.is_interval) {
727 gtl::STLSortAndRemoveDuplicates(&domain.values);
728 }
729}
730
731bool Variable::Merge(const std::string& other_name, const Domain& other_domain,
732 bool other_temporary) {
733 if (temporary && !other_temporary) {
734 temporary = false;
735 name = other_name;
736 }
737 domain.IntersectWithDomain(other_domain);
738 return true;
739}
740
741std::string Variable::DebugString() const {
742 if (!domain.is_interval && domain.values.size() == 1) {
743 return absl::StrFormat("% d", domain.values.back());
744 } else {
745 return absl::StrFormat("%s(%s%s)%s", name, domain.DebugString(),
746 temporary ? ", temporary" : "",
747 active ? "" : " [removed during presolve]");
748 }
749}
750
751// ----- Constraint -----
752
753std::string Constraint::DebugString() const {
754 const std::string strong = strong_propagation ? "strong propagation" : "";
755 const std::string presolve_status_str =
756 active ? ""
757 : (presolve_propagation_done ? "[propagated during presolve]"
758 : "[removed during presolve]");
759 return absl::StrFormat("%s(%s)%s %s", type, JoinDebugString(arguments, ", "),
760 strong, presolve_status_str);
761}
762
763void Constraint::RemoveArg(int arg_pos) {
764 arguments.erase(arguments.begin() + arg_pos);
765}
766
768 active = false;
769 // TODO(user): Reclaim arguments and memory.
770}
771
773 type = "false_constraint";
774 arguments.clear();
775}
776
777// ----- Annotation -----
778
780 Annotation result;
781 result.type = ANNOTATION_LIST;
782 result.interval_min = 0;
783 result.interval_max = 0;
784 return result;
785}
786
787Annotation Annotation::AnnotationList(std::vector<Annotation> list) {
788 Annotation result;
789 result.type = ANNOTATION_LIST;
790 result.interval_min = 0;
791 result.interval_max = 0;
792 result.annotations = std::move(list);
793 return result;
794}
795
796Annotation Annotation::Identifier(const std::string& id) {
797 Annotation result;
798 result.type = IDENTIFIER;
799 result.interval_min = 0;
800 result.interval_max = 0;
801 result.id = id;
802 return result;
803}
804
806 std::vector<Annotation> args) {
807 Annotation result;
808 result.type = FUNCTION_CALL;
809 result.interval_min = 0;
810 result.interval_max = 0;
811 result.id = id;
812 result.annotations = std::move(args);
813 return result;
814}
815
816Annotation Annotation::FunctionCall(const std::string& id) {
817 Annotation result;
818 result.type = FUNCTION_CALL;
819 result.interval_min = 0;
820 result.interval_max = 0;
821 result.id = id;
822 return result;
823}
824
825Annotation Annotation::Interval(int64_t interval_min, int64_t interval_max) {
826 Annotation result;
827 result.type = INTERVAL;
828 result.interval_min = interval_min;
829 result.interval_max = interval_max;
830 return result;
831}
832
834 Annotation result;
835 result.type = INT_VALUE;
836 result.interval_min = value;
837 return result;
838}
839
841 Annotation result;
842 result.type = VAR_REF;
843 result.interval_min = 0;
844 result.interval_max = 0;
845 result.variables.push_back(var);
846 return result;
847}
848
849Annotation Annotation::VarRefArray(std::vector<Variable*> variables) {
850 Annotation result;
851 result.type = VAR_REF_ARRAY;
852 result.interval_min = 0;
853 result.interval_max = 0;
854 result.variables = std::move(variables);
855 return result;
856}
857
858Annotation Annotation::String(const std::string& str) {
859 Annotation result;
860 result.type = STRING_VALUE;
861 result.interval_min = 0;
862 result.interval_max = 0;
863 result.string_value = str;
864 return result;
865}
866
867void Annotation::AppendAllVariables(std::vector<Variable*>* const vars) const {
868 for (const Annotation& ann : annotations) {
869 ann.AppendAllVariables(vars);
870 }
871 if (!variables.empty()) {
872 vars->insert(vars->end(), variables.begin(), variables.end());
873 }
874}
875
876std::string Annotation::DebugString() const {
877 switch (type) {
878 case ANNOTATION_LIST: {
879 return absl::StrFormat("[%s]", JoinDebugString(annotations, ", "));
880 }
881 case IDENTIFIER: {
882 return id;
883 }
884 case FUNCTION_CALL: {
885 return absl::StrFormat("%s(%s)", id, JoinDebugString(annotations, ", "));
886 }
887 case INTERVAL: {
888 return absl::StrFormat("%d..%d", interval_min, interval_max);
889 }
890 case INT_VALUE: {
891 return absl::StrCat(interval_min);
892 }
893 case VAR_REF: {
894 return variables.front()->name;
895 }
896 case VAR_REF_ARRAY: {
897 std::string result = "[";
898 for (int i = 0; i < variables.size(); ++i) {
899 result.append(variables[i]->DebugString());
900 result.append(i != variables.size() - 1 ? ", " : "]");
901 }
902 return result;
903 }
904 case STRING_VALUE: {
905 return absl::StrFormat("\"%s\"", string_value);
906 }
907 }
908 LOG(FATAL) << "Unhandled case in DebugString " << static_cast<int>(type);
909 return "";
910}
911
912// ----- SolutionOutputSpecs -----
913
915 return absl::StrFormat("%d..%d", min_value, max_value);
916}
917
919 const std::string& name, Variable* variable, bool display_as_boolean) {
920 SolutionOutputSpecs result;
921 result.name = name;
922 result.variable = variable;
924 return result;
925}
926
928 const std::string& name, std::vector<Bounds> bounds,
929 std::vector<Variable*> flat_variables, bool display_as_boolean) {
930 SolutionOutputSpecs result;
931 result.variable = nullptr;
932 result.name = name;
933 result.bounds = std::move(bounds);
934 result.flat_variables = std::move(flat_variables);
936 return result;
937}
938
940 SolutionOutputSpecs result;
941 result.variable = nullptr;
942 result.display_as_boolean = false;
943 return result;
944}
945
947 if (variable != nullptr) {
948 return absl::StrFormat("output_var(%s)", variable->name);
949 } else {
950 return absl::StrFormat("output_array([%s] [%s])",
951 JoinDebugString(bounds, ", "),
953 }
954}
955
956// ----- Model -----
957
959 gtl::STLDeleteElements(&variables_);
960 gtl::STLDeleteElements(&constraints_);
961}
962
963Variable* Model::AddVariable(const std::string& name, const Domain& domain,
964 bool defined) {
965 Variable* const var = new Variable(name, domain, defined);
966 variables_.push_back(var);
967 return var;
968}
969
970// TODO(user): Create only once constant per value.
972 Variable* const var =
973 new Variable(absl::StrCat(value), Domain::IntegerValue(value), true);
974 variables_.push_back(var);
975 return var;
976}
977
979 Variable* const var =
980 new Variable(absl::StrCat(value), Domain::FloatValue(value), true);
981 variables_.push_back(var);
982 return var;
983}
984
985void Model::AddConstraint(const std::string& id,
986 std::vector<Argument> arguments, bool is_domain) {
987 Constraint* const constraint =
988 new Constraint(id, std::move(arguments), is_domain);
989 constraints_.push_back(constraint);
990}
991
992void Model::AddConstraint(const std::string& id,
993 std::vector<Argument> arguments) {
994 AddConstraint(id, std::move(arguments), false);
995}
996
998 output_.push_back(std::move(output));
999}
1000
1001void Model::Satisfy(std::vector<Annotation> search_annotations) {
1002 objective_ = nullptr;
1003 search_annotations_ = std::move(search_annotations);
1004}
1005
1007 std::vector<Annotation> search_annotations) {
1008 objective_ = obj;
1009 maximize_ = false;
1010 search_annotations_ = std::move(search_annotations);
1011}
1012
1014 std::vector<Annotation> search_annotations) {
1015 objective_ = obj;
1016 maximize_ = true;
1017 search_annotations_ = std::move(search_annotations);
1018}
1019
1020std::string Model::DebugString() const {
1021 std::string output = absl::StrFormat("Model %s\nVariables\n", name_);
1022 for (int i = 0; i < variables_.size(); ++i) {
1023 absl::StrAppendFormat(&output, " %s\n", variables_[i]->DebugString());
1024 }
1025 output.append("Constraints\n");
1026 for (int i = 0; i < constraints_.size(); ++i) {
1027 if (constraints_[i] != nullptr) {
1028 absl::StrAppendFormat(&output, " %s\n", constraints_[i]->DebugString());
1029 }
1030 }
1031 if (objective_ != nullptr) {
1032 absl::StrAppendFormat(&output, "%s %s\n %s\n",
1033 maximize_ ? "Maximize" : "Minimize", objective_->name,
1034 JoinDebugString(search_annotations_, ", "));
1035 } else {
1036 absl::StrAppendFormat(&output, "Satisfy\n %s\n",
1037 JoinDebugString(search_annotations_, ", "));
1038 }
1039 output.append("Output\n");
1040 for (int i = 0; i < output_.size(); ++i) {
1041 absl::StrAppendFormat(&output, " %s\n", output_[i].DebugString());
1042 }
1043
1044 return output;
1045}
1046
1048 for (Variable* var : variables_) {
1049 if (var->domain.empty()) {
1050 return true;
1051 }
1052 }
1053 for (Constraint* ct : constraints_) {
1054 if (ct->type == "false_constraint") {
1055 return true;
1056 }
1057 }
1058
1059 return false;
1060}
1061
1062// ----- Model statistics -----
1063
1065 SOLVER_LOG(logger_, "Model ", model_.name());
1066 for (const auto& it : constraints_per_type_) {
1067 SOLVER_LOG(logger_, " - ", it.first, ": ", it.second.size());
1068 }
1069 if (model_.objective() == nullptr) {
1070 SOLVER_LOG(logger_, " - Satisfaction problem");
1071 } else {
1072 SOLVER_LOG(logger_, " - ",
1073 (model_.maximize() ? "Maximization" : "Minimization"),
1074 " problem");
1075 }
1076 SOLVER_LOG(logger_);
1077}
1078
1080 constraints_per_type_.clear();
1081 constraints_per_variables_.clear();
1082 for (Constraint* const ct : model_.constraints()) {
1083 if (ct != nullptr && ct->active) {
1084 constraints_per_type_[ct->type].push_back(ct);
1085 absl::flat_hash_set<const Variable*> marked;
1086 for (const Argument& arg : ct->arguments) {
1087 for (Variable* const var : arg.variables) {
1088 marked.insert(var);
1089 }
1090 }
1091 for (const Variable* const var : marked) {
1092 constraints_per_variables_[var].push_back(ct);
1093 }
1094 }
1095 }
1096}
1097
1098// Flatten Search annotations.
1099void FlattenAnnotations(const Annotation& ann, std::vector<Annotation>* out) {
1100 if (ann.type == Annotation::ANNOTATION_LIST ||
1101 ann.IsFunctionCallWithIdentifier("seq_search")) {
1102 for (const Annotation& inner : ann.annotations) {
1103 FlattenAnnotations(inner, out);
1104 }
1105 } else {
1106 out->push_back(ann);
1107 }
1108}
1109
1110} // namespace fz
1111} // 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:491
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
#define LOG(severity)
Definition: base/logging.h:416
#define DCHECK(condition)
Definition: base/logging.h:885
Variable * AddVariable(const std::string &name, const Domain &domain, bool defined)
Definition: model.cc:963
Variable * AddConstant(int64_t value)
Definition: model.cc:971
void Satisfy(std::vector< Annotation > search_annotations)
Definition: model.cc:1001
std::string DebugString() const
Definition: model.cc:1020
void AddOutput(SolutionOutputSpecs output)
Definition: model.cc:997
void AddConstraint(const std::string &id, std::vector< Argument > arguments, bool is_domain)
Definition: model.cc:985
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
Definition: model.cc:1013
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Definition: model.cc:1006
Variable * AddFloatConstant(double value)
Definition: model.cc:978
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)
Definition: model.cc:1099
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:2508
IntVar *const objective_
Definition: search.cc:2966
static Annotation IntegerValue(int64_t value)
Definition: model.cc:833
void AppendAllVariables(std::vector< Variable * > *vars) const
Definition: model.cc:867
std::string DebugString() const
Definition: model.cc:876
static Annotation FunctionCall(const std::string &id)
Definition: model.cc:816
static Annotation AnnotationList(std::vector< Annotation > list)
Definition: model.cc:787
std::vector< Variable * > variables
static Annotation String(const std::string &str)
Definition: model.cc:858
static Annotation Identifier(const std::string &id)
Definition: model.cc:796
bool IsFunctionCallWithIdentifier(const std::string &identifier) const
std::vector< Annotation > annotations
static Annotation Interval(int64_t interval_min, int64_t interval_max)
Definition: model.cc:825
static Annotation VarRefArray(std::vector< Variable * > variables)
Definition: model.cc:849
static Annotation VarRef(Variable *const var)
Definition: model.cc:840
static Annotation Empty()
Definition: model.cc:779
static Annotation FunctionCallWithArguments(const std::string &id, std::vector< Annotation > args)
Definition: model.cc:805
static Argument FloatInterval(double lb, double ub)
Definition: model.cc:559
static Argument DomainList(std::vector< Domain > domains)
Definition: model.cc:512
Variable * VarAt(int pos) const
Definition: model.cc:717
static Argument VarRef(Variable *const var)
Definition: model.cc:519
bool Contains(int64_t value) const
Definition: model.cc:670
static Argument IntegerList(std::vector< int64_t > values)
Definition: model.cc:505
static Argument VoidArgument()
Definition: model.cc:533
static Argument VarRefArray(std::vector< Variable * > vars)
Definition: model.cc:526
static Argument IntegerValue(int64_t value)
Definition: model.cc:490
static Argument Interval(int64_t imin, int64_t imax)
Definition: model.cc:497
std::string DebugString() const
Definition: model.cc:574
std::vector< Variable * > variables
static Argument FloatValue(double value)
Definition: model.cc:552
std::vector< int64_t > values
int64_t ValueAt(int pos) const
Definition: model.cc:688
static Argument FloatList(std::vector< double > floats)
Definition: model.cc:567
Variable * Var() const
Definition: model.cc:713
static Argument FromDomain(const Domain &domain)
Definition: model.cc:539
void RemoveArg(int arg_pos)
Definition: model.cc:763
std::string DebugString() const
Definition: model.cc:753
std::vector< Argument > arguments
static Domain IntegerValue(int64_t value)
Definition: model.cc:46
static Domain EmptyDomain()
Definition: model.cc:98
bool Contains(int64_t value) const
Definition: model.cc:356
bool OverlapsDomain(const Domain &other) const
Definition: model.cc:418
static Domain SetOfAllInt64()
Definition: model.cc:74
static Domain Boolean()
Definition: model.cc:60
bool IntersectWithSingleton(int64_t value)
Definition: model.cc:148
static Domain SetOfInterval(int64_t included_min, int64_t included_max)
Definition: model.cc:86
static Domain IntegerList(std::vector< int64_t > values)
Definition: model.cc:33
std::string DebugString() const
Definition: model.cc:461
bool IntersectWithInterval(int64_t interval_min, int64_t interval_max)
Definition: model.cc:152
static Domain AllInt64()
Definition: model.cc:40
bool IntersectWithFloatDomain(const Domain &domain)
Definition: model.cc:247
bool IntersectWithDomain(const Domain &domain)
Definition: model.cc:122
std::vector< double > float_values
static Domain FloatInterval(double lb, double ub)
Definition: model.cc:107
bool OverlapsIntInterval(int64_t lb, int64_t ub) const
Definition: model.cc:404
static Domain SetOfIntegerValue(int64_t value)
Definition: model.cc:80
bool OverlapsIntList(const std::vector< int64_t > &vec) const
Definition: model.cc:380
static Domain Interval(int64_t included_min, int64_t included_max)
Definition: model.cc:52
std::vector< int64_t > values
static Domain SetOfBoolean()
Definition: model.cc:92
static Domain SetOfIntegerList(std::vector< int64_t > values)
Definition: model.cc:68
static Domain FloatValue(double value)
Definition: model.cc:115
bool IntersectWithListOfIntegers(const std::vector< int64_t > &integers)
Definition: model.cc:200
static Domain AllFloats()
Definition: model.cc:100
bool RemoveValue(int64_t value)
Definition: model.cc:430
static SolutionOutputSpecs VoidOutput()
Definition: model.cc:939
static SolutionOutputSpecs SingleVariable(const std::string &name, Variable *variable, bool display_as_boolean)
Definition: model.cc:918
static SolutionOutputSpecs MultiDimensionalArray(const std::string &name, std::vector< Bounds > bounds, std::vector< Variable * > flat_variables, bool display_as_boolean)
Definition: model.cc:927
std::string DebugString() const
Definition: model.cc:741
bool Merge(const std::string &other_name, const Domain &other_domain, bool other_temporary)
Definition: model.cc:731
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:63