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