Google OR-Tools v9.15
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-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdint>
18#include <limits>
19#include <string>
20#include <utility>
21#include <vector>
22
23#include "absl/base/optimization.h"
24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.h"
26#include "absl/log/log.h"
27#include "absl/strings/str_cat.h"
28#include "absl/strings/str_format.h"
29#include "absl/strings/str_join.h"
30#include "absl/strings/string_view.h"
31#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
156 return IntersectWithInterval(value, value);
157}
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;
297 float_values = {value};
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
437bool Domain::RemoveValue(int64_t value) {
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 std::string prefix = "";
470 if (is_fixed_set) {
471 prefix = "fixed_set of ";
472 } else if (is_a_set) {
473 prefix = "set of ";
474 }
475
476 if (is_float) {
477 switch (float_values.size()) {
478 case 0:
479 return "float";
480 case 1:
481 return absl::StrCat(prefix, float_values[0]);
482 case 2:
483 return absl::StrCat(prefix, "[", float_values[0], "..", float_values[1],
484 "]");
485 default:
486 LOG(DFATAL) << "Error with float domain";
487 return "error_float";
488 }
489 }
490 if (is_interval) {
491 if (values.empty()) {
492 return absl::StrCat(prefix, "int");
493 } else {
494 return absl::StrCat(prefix, "[", values[0], "..", values[1], "]");
495 }
496 } else if (values.size() == 1) {
497 return absl::StrCat(prefix, values.back());
498 } else {
499 return absl::StrFormat("%s[%s]", prefix, absl::StrJoin(values, ", "));
500 }
501}
502
503// ----- Argument -----
504
506 Argument result;
507 result.type = INT_VALUE;
508 result.values.push_back(value);
509 return result;
510}
511
512Argument Argument::Interval(int64_t imin, int64_t imax) {
513 Argument result;
514 result.type = INT_INTERVAL;
515 result.values.push_back(imin);
516 result.values.push_back(imax);
517 return result;
518}
519
520Argument Argument::IntegerList(std::vector<int64_t> values) {
521 Argument result;
522 result.type = INT_LIST;
523 result.values = std::move(values);
524 return result;
525}
526
528 Argument result;
529 result.type = DOMAIN_LIST;
530 result.domains = std::move(domains);
531 return result;
532}
533
535 Argument result;
536 result.type = VAR_REF;
537 result.variables.push_back(var);
538 return result;
539}
540
541Argument Argument::VarRefArray(std::vector<Variable*> vars) {
542 Argument result;
543 result.type = VAR_REF_ARRAY;
544 result.variables = std::move(vars);
545 return result;
546}
547
549 Argument result;
550 result.type = VOID_ARGUMENT;
551 return result;
552}
553
555 if (domain.is_interval) {
556 if (domain.values.empty()) {
557 return Argument::Interval(std::numeric_limits<int64_t>::min(),
558 std::numeric_limits<int64_t>::max());
559 } else {
560 return Argument::Interval(domain.values[0], domain.values[1]);
561 }
562 } else {
563 return Argument::IntegerList(domain.values);
564 }
565}
566
568 Argument result;
569 result.type = FLOAT_VALUE;
570 result.floats.push_back(value);
571 return result;
572}
573
574Argument Argument::FloatInterval(double lb, double ub) {
575 Argument result;
576 result.type = FLOAT_INTERVAL;
577 result.floats.push_back(lb);
578 result.floats.push_back(ub);
579 return result;
580}
581
582Argument Argument::FloatList(std::vector<double> floats) {
583 Argument result;
584 result.type = FLOAT_LIST;
585 result.floats = std::move(floats);
586 return result;
587}
588
589std::string Argument::DebugString() const {
590 switch (type) {
591 case INT_VALUE:
592 return absl::StrFormat("%d", values[0]);
593 case INT_INTERVAL:
594 return absl::StrFormat("[%d..%d]", values[0], values[1]);
595 case INT_LIST:
596 return absl::StrFormat("[%s]", absl::StrJoin(values, ", "));
597 case DOMAIN_LIST:
598 return absl::StrFormat("[%s]", JoinDebugString(domains, ", "));
599 case VAR_REF:
600 return variables[0]->name;
601 case VAR_REF_ARRAY: {
602 std::string result = "[";
603 for (int i = 0; i < variables.size(); ++i) {
604 result.append(variables[i]->name);
605 result.append(i != variables.size() - 1 ? ", " : "]");
606 }
607 return result;
608 }
609 case VOID_ARGUMENT:
610 return "VoidArgument";
611 case FLOAT_VALUE:
612 return absl::StrCat(floats[0]);
613 case FLOAT_INTERVAL:
614 return absl::StrCat("[", floats[0], "..", floats[1], "]");
615 case FLOAT_LIST:
616 return absl::StrFormat("[%s]", absl::StrJoin(floats, ", "));
617 }
618 LOG(FATAL) << "Unhandled case in DebugString " << static_cast<int>(type);
619 ABSL_UNREACHABLE();
620}
621
622bool Argument::IsVariable() const { return type == VAR_REF; }
623
625 return (type == INT_VALUE || (type == INT_LIST && values.size() == 1) ||
626 (type == INT_INTERVAL && values[0] == values[1]) ||
627 (type == VAR_REF && variables[0]->domain.HasOneValue()));
628}
629
630int64_t Argument::Value() const {
631 DCHECK(HasOneValue()) << "Value() called on unbound Argument: "
632 << DebugString();
633 switch (type) {
634 case INT_VALUE:
635 case INT_INTERVAL:
636 case INT_LIST:
637 return values[0];
638 case VAR_REF:
639 return variables[0]->domain.values[0];
640 default:
641 break;
642 }
643 ABSL_UNREACHABLE();
644}
645
647 switch (type) {
648 case INT_VALUE:
649 return false;
650 case INT_INTERVAL:
651 return false;
652 case INT_LIST:
653 return true;
654 case DOMAIN_LIST: {
655 for (const Domain& domain : domains) {
656 if (!domain.HasOneValue()) {
657 return false;
658 }
659 }
660 return true;
661 }
662 case VAR_REF:
663 return false;
664 case VAR_REF_ARRAY: {
665 for (Variable* var : variables) {
666 if (!var->domain.HasOneValue()) {
667 return false;
668 }
669 }
670 return true;
671 }
672 case VOID_ARGUMENT:
673 return false;
674 case FLOAT_VALUE:
675 return false;
676 case FLOAT_INTERVAL:
677 return false;
678 case FLOAT_LIST:
679 return false;
680 }
681 ABSL_UNREACHABLE();
682}
683
684bool Argument::Contains(int64_t value) const {
685 switch (type) {
687 return std::find(values.begin(), values.end(), value) != values.end();
689 return value >= values.front() && value <= values.back();
691 return value == values.front();
692 default:
693 break;
694 }
695
696 ABSL_UNREACHABLE();
697}
698
699int64_t Argument::ValueAt(int pos) const {
700 switch (type) {
701 case INT_LIST:
702 CHECK_GE(pos, 0);
703 CHECK_LT(pos, values.size());
704 return values[pos];
705 case DOMAIN_LIST: {
706 CHECK_GE(pos, 0);
707 CHECK_LT(pos, domains.size());
708 return domains[pos].Value();
709 }
710 case VAR_REF_ARRAY: {
711 CHECK_GE(pos, 0);
712 CHECK_LT(pos, variables.size());
713 return variables[pos]->domain.Value();
714 }
715 default:
716 break;
717 }
718 ABSL_UNREACHABLE();
719}
720
721bool Argument::HasOneValueAt(int pos) const {
722 switch (type) {
723 case INT_LIST:
724 CHECK_GE(pos, 0);
725 CHECK_LT(pos, values.size());
726 return true;
727 case DOMAIN_LIST: {
728 CHECK_GE(pos, 0);
729 CHECK_LT(pos, domains.size());
730 return domains[pos].HasOneValue();
731 }
732 case VAR_REF_ARRAY: {
733 CHECK_GE(pos, 0);
734 CHECK_LT(pos, variables.size());
735 return variables[pos]->domain.HasOneValue();
736 }
737 default:
738 break;
739 }
740 ABSL_UNREACHABLE();
741}
742
744 return type == VAR_REF ? variables[0] : nullptr;
745}
746
747Variable* Argument::VarAt(int pos) const {
748 return type == VAR_REF_ARRAY ? variables[pos] : nullptr;
749}
750
751int Argument::Size() const {
752 switch (type) {
753 case INT_LIST:
754 return values.size();
755 case DOMAIN_LIST: {
756 return domains.size();
757 }
758 case VAR_REF_ARRAY: {
759 return variables.size();
760 }
761 case VOID_ARGUMENT: {
762 return 0;
763 }
764 case FLOAT_LIST: {
765 return floats.size();
766 }
767 default:
768 break;
769 }
770 ABSL_UNREACHABLE();
771}
772
773// ----- Variable -----
774
775Variable::Variable(absl::string_view name_, const Domain& domain_,
776 bool temporary_)
777 : name(name_), domain(domain_), temporary(temporary_), active(true) {
778 if (!domain.is_interval) {
779 gtl::STLSortAndRemoveDuplicates(&domain.values);
780 }
781}
782
783bool Variable::Merge(absl::string_view other_name, const Domain& other_domain,
784 bool other_temporary) {
785 if (temporary && !other_temporary) {
786 temporary = false;
787 name = other_name;
788 }
789 domain.IntersectWithDomain(other_domain);
790 return true;
791}
792
793std::string Variable::DebugString() const {
794 if (!domain.is_interval && domain.values.size() == 1) {
795 return absl::StrFormat("% d", domain.values.back());
796 } else {
797 return absl::StrFormat("%s(%s%s)%s", name, domain.DebugString(),
798 temporary ? ", temporary" : "",
799 active ? "" : " [removed during presolve]");
800 }
801}
802
803// ----- Constraint -----
804
805std::string Constraint::DebugString() const {
806 const std::string strong = strong_propagation ? " strong propagation" : "";
807 const std::string presolve_status_str =
808 active ? "" : " [removed during presolve]";
809 const std::string symmetric_breaking_str =
810 is_symmetric_breaking ? " symmetric breaking" : "";
811 const std::string redundant_str = is_redundant ? " redundant" : "";
812 return absl::StrCat(type, "(", JoinDebugString(arguments, ", "), ")", strong,
813 presolve_status_str, symmetric_breaking_str,
814 redundant_str);
815}
816
817void Constraint::RemoveArg(int arg_pos) {
818 arguments.erase(arguments.begin() + arg_pos);
819}
820
822 active = false;
823 // TODO(user): Reclaim arguments and memory.
824}
825
827 type = "false_constraint";
828 arguments.clear();
829}
830
831// ----- Annotation -----
832
834 Annotation result;
835 result.type = ANNOTATION_LIST;
836 result.interval_min = 0;
837 result.interval_max = 0;
838 return result;
839}
840
841Annotation Annotation::AnnotationList(std::vector<Annotation> list) {
842 Annotation result;
843 result.type = ANNOTATION_LIST;
844 result.interval_min = 0;
845 result.interval_max = 0;
846 result.annotations = std::move(list);
847 return result;
848}
849
850Annotation Annotation::Identifier(absl::string_view id) {
851 Annotation result;
852 result.type = IDENTIFIER;
853 result.interval_min = 0;
854 result.interval_max = 0;
855 result.id = id;
856 return result;
857}
858
860 std::vector<Annotation> args) {
861 Annotation result;
862 result.type = FUNCTION_CALL;
863 result.interval_min = 0;
864 result.interval_max = 0;
865 result.id = id;
866 result.annotations = std::move(args);
867 return result;
868}
869
871 Annotation result;
872 result.type = FUNCTION_CALL;
873 result.interval_min = 0;
874 result.interval_max = 0;
875 result.id = id;
876 return result;
877}
878
880 Annotation result;
881 result.type = INTERVAL;
882 result.interval_min = interval_min;
883 result.interval_max = interval_max;
884 return result;
885}
886
888 Annotation result;
889 result.type = INT_VALUE;
890 result.interval_min = value;
891 return result;
892}
893
894Annotation Annotation::IntegerList(const std::vector<int64_t>& values) {
895 LOG(INFO) << "Create INT_LIST";
896 Annotation result;
897 result.type = INT_LIST;
898 result.values = values;
899 return result;
900}
901
903 Annotation result;
904 result.type = VAR_REF;
905 result.interval_min = 0;
906 result.interval_max = 0;
907 result.variables.push_back(var);
908 return result;
909}
910
912 Annotation result;
913 result.type = VAR_REF_ARRAY;
914 result.interval_min = 0;
915 result.interval_max = 0;
916 result.variables = std::move(variables);
917 return result;
918}
919
920Annotation Annotation::String(absl::string_view str) {
921 Annotation result;
922 result.type = STRING_VALUE;
923 result.interval_min = 0;
924 result.interval_max = 0;
925 result.string_value = str;
926 return result;
927}
928
929void Annotation::AppendAllVariables(std::vector<Variable*>* const vars) const {
930 for (const Annotation& ann : annotations) {
931 ann.AppendAllVariables(vars);
932 }
933 if (!variables.empty()) {
934 vars->insert(vars->end(), variables.begin(), variables.end());
935 }
936}
937
938std::string Annotation::DebugString() const {
939 switch (type) {
940 case ANNOTATION_LIST:
941 return absl::StrFormat("[%s]", JoinDebugString(annotations, ", "));
942 case IDENTIFIER:
943 return id;
944 case FUNCTION_CALL:
945 return absl::StrFormat("%s(%s)", id, JoinDebugString(annotations, ", "));
946 case INTERVAL:
947 return absl::StrFormat("%d..%d", interval_min, interval_max);
948 case INT_VALUE:
949 return absl::StrCat(interval_min);
950 case INT_LIST:
951 return absl::StrFormat("[%s]", absl::StrJoin(values, ", "));
952 case VAR_REF:
953 return variables.front()->name;
954 case VAR_REF_ARRAY: {
955 std::string result = "[";
956 for (int i = 0; i < variables.size(); ++i) {
957 result.append(variables[i]->DebugString());
958 result.append(i != variables.size() - 1 ? ", " : "]");
959 }
960 return result;
961 }
962 case STRING_VALUE:
963 return absl::StrFormat("\"%s\"", string_value);
964 }
965 LOG(FATAL) << "Unhandled case in DebugString " << static_cast<int>(type);
966 ABSL_UNREACHABLE();
967}
968
969// ----- SolutionOutputSpecs -----
970
972 return absl::StrFormat("%d..%d", min_value, max_value);
973}
974
976 absl::string_view name, Variable* variable, bool display_as_boolean) {
977 SolutionOutputSpecs result;
978 result.name = name;
979 result.variable = variable;
981 return result;
982}
983
985 absl::string_view name, std::vector<Bounds> bounds,
986 std::vector<Variable*> flat_variables, bool display_as_boolean) {
987 SolutionOutputSpecs result;
988 result.variable = nullptr;
989 result.name = name;
990 result.bounds = std::move(bounds);
991 result.flat_variables = std::move(flat_variables);
993 return result;
994}
995
997 SolutionOutputSpecs result;
998 result.variable = nullptr;
999 result.display_as_boolean = false;
1000 return result;
1001}
1002
1004 if (variable != nullptr) {
1005 return absl::StrFormat("output_var(%s)", variable->name);
1006 } else {
1007 return absl::StrFormat("output_array([%s] [%s])",
1008 JoinDebugString(bounds, ", "),
1010 }
1011}
1012
1013// ----- Model -----
1014
1016 gtl::STLDeleteElements(&variables_);
1017 gtl::STLDeleteElements(&constraints_);
1018}
1019
1020Variable* Model::AddVariable(absl::string_view name, const Domain& domain,
1021 bool defined, bool set_is_fixed) {
1022 Variable* const var = new Variable(name, domain, defined);
1023 if (set_is_fixed) {
1024 var->domain.is_a_set = true;
1025 var->domain.is_fixed_set = true;
1026 }
1027 variables_.push_back(var);
1028 return var;
1029}
1030
1031// TODO(user): Create only once constant per value.
1033 Variable* const var =
1034 new Variable(absl::StrCat(value), Domain::IntegerValue(value), true);
1035 variables_.push_back(var);
1036 return var;
1037}
1038
1040 Variable* const var =
1041 new Variable(absl::StrCat(value), Domain::FloatValue(value), true);
1042 variables_.push_back(var);
1043 return var;
1044}
1045
1046void Model::AddConstraint(absl::string_view id, std::vector<Argument> arguments,
1047 bool is_domain, bool symmetry, bool redundant) {
1048 Constraint* const constraint =
1049 new Constraint(id, std::move(arguments), is_domain, symmetry, redundant);
1050 constraints_.push_back(constraint);
1051}
1052
1053void Model::AddConstraint(absl::string_view id,
1054 std::vector<Argument> arguments) {
1055 AddConstraint(id, std::move(arguments), false, false, false);
1056}
1057
1059 output_.push_back(std::move(output));
1060}
1061
1062void Model::Satisfy(std::vector<Annotation> search_annotations) {
1063 objective_ = nullptr;
1064 search_annotations_ = std::move(search_annotations);
1065}
1066
1068 std::vector<Annotation> search_annotations) {
1069 objective_ = obj;
1070 maximize_ = false;
1071 search_annotations_ = std::move(search_annotations);
1072}
1073
1075 std::vector<Annotation> search_annotations) {
1076 objective_ = obj;
1077 maximize_ = true;
1078 search_annotations_ = std::move(search_annotations);
1079}
1080
1081std::string Model::DebugString() const {
1082 std::string output = absl::StrFormat("Model %s\nVariables\n", name_);
1083 for (int i = 0; i < variables_.size(); ++i) {
1084 absl::StrAppendFormat(&output, " %s\n", variables_[i]->DebugString());
1085 }
1086 output.append("Constraints\n");
1087 for (int i = 0; i < constraints_.size(); ++i) {
1088 if (constraints_[i] != nullptr) {
1089 absl::StrAppendFormat(&output, " %s\n", constraints_[i]->DebugString());
1090 }
1091 }
1092 if (objective_ != nullptr) {
1093 absl::StrAppendFormat(&output, "%s %s\n %s\n",
1094 maximize_ ? "Maximize" : "Minimize", objective_->name,
1095 JoinDebugString(search_annotations_, ", "));
1096 } else if (!float_objective_variables_.empty()) {
1097 absl::StrAppendFormat(&output, "%s [%s] * [%s] + %f\n %s\n",
1098 maximize_ ? "Maximize" : "Minimize",
1099 JoinDebugStringPtr(float_objective_variables_, ", "),
1100 absl::StrJoin(float_objective_coefficients_, ", "),
1101 float_objective_offset_,
1102 JoinDebugString(search_annotations_, ", "));
1103
1104 } else {
1105 absl::StrAppendFormat(&output, "Satisfy\n %s\n",
1106 JoinDebugString(search_annotations_, ", "));
1107 }
1108 output.append("Output\n");
1109 for (int i = 0; i < output_.size(); ++i) {
1110 absl::StrAppendFormat(&output, " %s\n", output_[i].DebugString());
1111 }
1112
1113 return output;
1114}
1115
1117 for (Variable* var : variables_) {
1118 if (var->domain.empty()) {
1119 return true;
1120 }
1121 }
1122 for (Constraint* ct : constraints_) {
1123 if (ct->type == "false_constraint") {
1124 return true;
1125 }
1126 }
1127
1128 return false;
1129}
1130
1131// ----- Model statistics -----
1132
1134 SOLVER_LOG(logger_, "Model ", model_.name());
1135
1136 // Variables.
1137 int num_bool_vars = 0;
1138 int num_int_vars = 0;
1139 int num_float_vars = 0;
1140 int num_set_vars = 0;
1141 for (Variable* var : model_.variables()) {
1142 if (var->domain.is_float) {
1143 ++num_float_vars;
1144 } else if (var->domain.is_a_set) {
1145 ++num_set_vars;
1146 } else if (var->domain.display_as_boolean) {
1147 ++num_bool_vars;
1148 } else {
1149 ++num_int_vars;
1150 }
1151 }
1152 if (num_bool_vars > 0) {
1153 SOLVER_LOG(logger_, " - boolean variables:", num_bool_vars);
1154 }
1155 if (num_int_vars > 0) {
1156 SOLVER_LOG(logger_, " - integer variables:", num_int_vars);
1157 }
1158 if (num_float_vars > 0) {
1159 SOLVER_LOG(logger_, " - float variables:", num_float_vars);
1160 }
1161 if (num_set_vars > 0) {
1162 SOLVER_LOG(logger_, " - set variables:", num_set_vars);
1163 }
1164 SOLVER_LOG(logger_);
1165
1166 // Constraints.
1167 int num_redundant_constraints = 0;
1168 int num_symmetry_breaking_constraints = 0;
1169 for (Constraint* const ct : model_.constraints()) {
1170 if (ct != nullptr && ct->active) {
1171 if (ct->is_redundant) {
1172 ++num_redundant_constraints;
1173 }
1174 if (ct->is_symmetric_breaking) {
1175 ++num_symmetry_breaking_constraints;
1176 }
1177 }
1178 }
1179 for (const auto& it : constraints_per_type_) {
1180 SOLVER_LOG(logger_, " - ", it.first, ": ", it.second.size());
1181 }
1182 SOLVER_LOG(logger_);
1183 if (num_redundant_constraints > 0) {
1184 SOLVER_LOG(logger_,
1185 " - redundant constraints: ", num_redundant_constraints);
1186 }
1187 if (num_symmetry_breaking_constraints > 0) {
1188 SOLVER_LOG(logger_, " - symmetry breaking constraints: ",
1189 num_symmetry_breaking_constraints);
1190 }
1191 if (model_.objective() == nullptr) {
1192 SOLVER_LOG(logger_, " - Satisfaction problem");
1193 } else {
1194 SOLVER_LOG(logger_, " - ",
1195 (model_.maximize() ? "Maximization" : "Minimization"),
1196 " problem");
1197 }
1198 SOLVER_LOG(logger_);
1199}
1200
1202 constraints_per_type_.clear();
1203 constraints_per_variables_.clear();
1204 for (Constraint* const ct : model_.constraints()) {
1205 if (ct != nullptr && ct->active) {
1206 constraints_per_type_[ct->type].push_back(ct);
1207 absl::flat_hash_set<const Variable*> marked;
1208 for (const Argument& arg : ct->arguments) {
1209 for (Variable* const var : arg.variables) {
1210 marked.insert(var);
1211 }
1212 }
1213 for (const Variable* const var : marked) {
1214 constraints_per_variables_[var].push_back(ct);
1215 }
1216 }
1217 }
1218}
1219
1220// Flatten Search annotations.
1221void FlattenAnnotations(const Annotation& ann, std::vector<Annotation>* out) {
1222 if (ann.type == Annotation::ANNOTATION_LIST ||
1223 ann.IsFunctionCallWithIdentifier("seq_search")) {
1224 for (const Annotation& inner : ann.annotations) {
1225 FlattenAnnotations(inner, out);
1226 }
1227 } else {
1228 out->push_back(ann);
1229 }
1230}
1231
1232} // namespace fz
1233} // namespace operations_research
void AddConstraint(absl::string_view id, std::vector< Argument > arguments, bool is_domain, bool symmetry, bool redundant)
Definition model.cc:1046
const std::vector< SolutionOutputSpecs > & output() const
Definition model.h:381
Variable * AddVariable(absl::string_view name, const Domain &domain, bool defined, bool set_is_fixed=false)
Definition model.cc:1020
Variable * AddFloatConstant(double value)
Definition model.cc:1039
std::string DebugString() const
Definition model.cc:1081
void AddOutput(SolutionOutputSpecs output)
Definition model.cc:1058
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
Definition model.cc:1074
void Satisfy(std::vector< Annotation > search_annotations)
Definition model.cc:1062
const std::string & name() const
Definition model.h:404
const std::vector< Annotation > & search_annotations() const
Definition model.h:378
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Definition model.cc:1067
Variable * AddConstant(int64_t value)
Definition model.cc:1032
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:55
void STLDeleteElements(T *container)
Definition stl_util.h:369
void FlattenAnnotations(const Annotation &ann, std::vector< Annotation > *out)
Definition model.cc:1221
OR-Tools root namespace.
std::string JoinDebugStringPtr(const std::vector< T > &v, absl::string_view separator)
std::string JoinNameFieldPtr(const std::vector< T > &v, absl::string_view separator)
std::string JoinDebugString(const std::vector< T > &v, absl::string_view separator)
static Annotation String(absl::string_view str)
Definition model.cc:920
static Annotation FunctionCall(absl::string_view id)
Definition model.cc:870
void AppendAllVariables(std::vector< Variable * > *vars) const
Definition model.cc:929
static Annotation FunctionCallWithArguments(absl::string_view id, std::vector< Annotation > args)
Definition model.cc:859
static Annotation Empty()
Definition model.cc:833
static Annotation AnnotationList(std::vector< Annotation > list)
Definition model.cc:841
static Annotation IntegerList(const std::vector< int64_t > &values)
Definition model.cc:894
static Annotation VarRefArray(std::vector< Variable * > variables)
Definition model.cc:911
static Annotation Identifier(absl::string_view id)
Definition model.cc:850
std::vector< Variable * > variables
Definition model.h:305
std::vector< int64_t > values
Definition model.h:306
bool IsFunctionCallWithIdentifier(absl::string_view identifier) const
Definition model.h:292
static Annotation VarRef(Variable *var)
Definition model.cc:902
static Annotation IntegerValue(int64_t value)
Definition model.cc:887
static Annotation Interval(int64_t interval_min, int64_t interval_max)
Definition model.cc:879
std::vector< Annotation > annotations
Definition model.h:304
std::string DebugString() const
Definition model.cc:938
std::vector< Variable * > variables
Definition model.h:212
static Argument FloatList(std::vector< double > floats)
Definition model.cc:582
static Argument FloatValue(double value)
Definition model.cc:567
std::vector< Domain > domains
Definition model.h:213
bool HasOneValueAt(int pos) const
Definition model.cc:721
Variable * VarAt(int pos) const
Definition model.cc:747
std::string DebugString() const
Definition model.cc:589
static Argument VoidArgument()
Definition model.cc:548
static Argument IntegerValue(int64_t value)
Definition model.cc:505
int64_t ValueAt(int pos) const
Definition model.cc:699
static Argument Interval(int64_t imin, int64_t imax)
Definition model.cc:512
static Argument FromDomain(const Domain &domain)
Definition model.cc:554
static Argument IntegerList(std::vector< int64_t > values)
Definition model.cc:520
static Argument VarRef(Variable *var)
Definition model.cc:534
static Argument FloatInterval(double lb, double ub)
Definition model.cc:574
std::vector< double > floats
Definition model.h:214
bool Contains(int64_t value) const
Definition model.cc:684
static Argument DomainList(std::vector< Domain > domains)
Definition model.cc:527
static Argument VarRefArray(std::vector< Variable * > vars)
Definition model.cc:541
std::vector< int64_t > values
Definition model.h:211
std::string DebugString() const
Definition model.cc:805
std::vector< Argument > arguments
Definition model.h:241
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)
Definition model.cc:40
bool OverlapsDomain(const Domain &other) const
Definition model.cc:425
static Domain IntegerValue(int64_t value)
Definition model.cc:53
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
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
std::vector< int64_t > values
Definition model.h:106
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
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
static Domain AllInt64()
Definition model.cc:47
bool IntersectWithListOfIntegers(absl::Span< const int64_t > integers)
Definition model.cc:207
bool OverlapsIntList(const std::vector< int64_t > &vec) const
Definition model.cc:387
std::vector< Variable * > flat_variables
Definition model.h:338
static SolutionOutputSpecs MultiDimensionalArray(absl::string_view name, std::vector< Bounds > bounds, std::vector< Variable * > flat_variables, bool display_as_boolean)
Definition model.cc:984
static SolutionOutputSpecs VoidOutput()
Definition model.cc:996
static SolutionOutputSpecs SingleVariable(absl::string_view name, Variable *variable, bool display_as_boolean)
Definition model.cc:975
bool Merge(absl::string_view other_name, const Domain &other_domain, bool other_temporary)
Definition model.cc:783
std::string DebugString() const
Definition model.cc:793
#define SOLVER_LOG(logger,...)
Definition logging.h:114