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 
14 #include "ortools/flatzinc/model.h"
15 
16 #include <cstdint>
17 #include <limits>
18 #include <set>
19 #include <vector>
20 
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/str_format.h"
24 #include "absl/strings/str_join.h"
25 #include "ortools/base/map_util.h"
26 #include "ortools/base/stl_util.h"
27 #include "ortools/util/logging.h"
28 
29 namespace operations_research {
30 namespace fz {
31 // ----- Domain -----
32 
33 Domain 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 
52 Domain 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 
68 Domain 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 
86 Domain 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 
107 Domain 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 
122 bool Domain::IntersectWithDomain(const Domain& domain) {
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 
152 bool 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 
200 bool 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 (gtl::ContainsKey(other_values, 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;
290  float_values = {value};
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 
318  CHECK(is_float);
319  is_interval = false;
320  float_values.clear();
321  return true;
322 }
323 
324 bool Domain::HasOneValue() const {
325  return (values.size() == 1 || (values.size() == 2 && values[0] == values[1]));
326 }
327 
328 bool Domain::empty() const {
329  return is_interval ? (values.size() == 2 && values[0] > values[1])
330  : values.empty();
331 }
332 
333 int64_t Domain::Min() const {
334  CHECK(!empty());
336  : values.front();
337 }
338 
339 int64_t Domain::Max() const {
340  CHECK(!empty());
342  : values.back();
343 }
344 
345 int64_t Domain::Value() const {
346  CHECK(HasOneValue());
347  return values.front();
348 }
349 
350 bool Domain::IsAllInt64() const {
351  return is_interval &&
352  (values.empty() || (values[0] == std::numeric_limits<int64_t>::min() &&
354 }
355 
356 bool 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 
368 namespace {
369 bool 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 
380 bool 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 (gtl::ContainsKey(container, value)) {
397  return true;
398  }
399  }
400  return false;
401  }
402 }
403 
404 bool 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 
418 bool 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 
430 bool Domain::RemoveValue(int64_t value) {
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 
461 std::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 
497 Argument 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 
505 Argument 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 
512 Argument 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 
526 Argument 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 
559 Argument 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 
567 Argument 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 
574 std::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 
607 bool Argument::IsVariable() const { return type == VAR_REF; }
608 
609 bool Argument::HasOneValue() const {
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 
615 int64_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 
670 bool 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  }
675  case Argument::INT_INTERVAL: {
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 
688 int64_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 
717 Variable* Argument::VarAt(int pos) const {
718  return type == VAR_REF_ARRAY ? variables[pos] : nullptr;
719 }
720 
721 // ----- Variable -----
722 
723 Variable::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 
731 bool 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 
741 std::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 
753 std::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 
763 void 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 
787 Annotation 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 
796 Annotation 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 
816 Annotation 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 
825 Annotation 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 
849 Annotation 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 
858 Annotation 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 
867 void 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 
876 std::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 
946 std::string SolutionOutputSpecs::DebugString() const {
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 
963 Variable* 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 
985 void 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 
992 void 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 
1001 void 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 
1020 std::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.
1099 void 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
static Argument FromDomain(const Domain &domain)
Definition: model.cc:539
#define CHECK(condition)
Definition: base/logging.h:491
static Argument FloatInterval(double lb, double ub)
Definition: model.cc:559
std::vector< double > float_values
static Argument FloatList(std::vector< double > floats)
Definition: model.cc:567
int64_t min
Definition: alldiff_cst.cc:139
std::string DebugString() const
Definition: model.cc:753
bool IntersectWithInterval(int64_t interval_min, int64_t interval_max)
Definition: model.cc:152
static Domain IntegerValue(int64_t value)
Definition: model.cc:46
Variable * AddConstant(int64_t value)
Definition: model.cc:971
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:63
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
const int FATAL
Definition: log_severity.h:32
bool OverlapsIntInterval(int64_t lb, int64_t ub) const
Definition: model.cc:404
static Domain SetOfInterval(int64_t included_min, int64_t included_max)
Definition: model.cc:86
static Annotation Identifier(const std::string &id)
Definition: model.cc:796
const std::string name
void FlattenAnnotations(const Annotation &ann, std::vector< Annotation > *out)
Definition: model.cc:1099
std::string DebugString() const
Definition: model.cc:461
static SolutionOutputSpecs SingleVariable(const std::string &name, Variable *variable, bool display_as_boolean)
Definition: model.cc:918
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Definition: model.cc:1006
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
Definition: model.cc:1013
int64_t ValueAt(int pos) const
Definition: model.cc:688
#define LOG(severity)
Definition: base/logging.h:416
bool IntersectWithSingleton(int64_t value)
Definition: model.cc:148
bool Contains(int64_t value) const
Definition: model.cc:356
static Domain SetOfIntegerList(std::vector< int64_t > values)
Definition: model.cc:68
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
std::string DebugString() const
Definition: model.cc:876
static Domain SetOfAllInt64()
Definition: model.cc:74
static Domain Interval(int64_t included_min, int64_t included_max)
Definition: model.cc:52
void AppendAllVariables(std::vector< Variable * > *vars) const
Definition: model.cc:867
std::string DebugString() const
Definition: model.cc:741
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
static Domain Boolean()
Definition: model.cc:60
std::string DebugString() const
Definition: model.cc:574
int64_t max
Definition: alldiff_cst.cc:140
bool OverlapsIntList(const std::vector< int64_t > &vec) const
Definition: model.cc:380
static Domain AllInt64()
Definition: model.cc:40
bool IsFunctionCallWithIdentifier(const std::string &identifier) const
static Annotation FunctionCall(const std::string &id)
Definition: model.cc:816
static Annotation Interval(int64_t interval_min, int64_t interval_max)
Definition: model.cc:825
bool IntersectWithListOfIntegers(const std::vector< int64_t > &integers)
Definition: model.cc:200
static Domain EmptyDomain()
Definition: model.cc:98
static Argument IntegerValue(int64_t value)
Definition: model.cc:490
static Annotation IntegerValue(int64_t value)
Definition: model.cc:833
static Annotation VarRefArray(std::vector< Variable * > variables)
Definition: model.cc:849
IntVar *const objective_
Definition: search.cc:2966
static Annotation AnnotationList(std::vector< Annotation > list)
Definition: model.cc:787
std::string JoinNameFieldPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:58
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
void STLDeleteElements(T *container)
Definition: stl_util.h:372
void AddOutput(SolutionOutputSpecs output)
Definition: model.cc:997
bool Contains(int64_t value) const
Definition: model.cc:670
std::vector< Variable * > variables
bool Merge(const std::string &other_name, const Domain &other_domain, bool other_temporary)
Definition: model.cc:731
Variable * VarAt(int pos) const
Definition: model.cc:717
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
std::string JoinDebugString(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:38
static Argument VarRefArray(std::vector< Variable * > vars)
Definition: model.cc:526
std::vector< int64_t > values
static Domain SetOfBoolean()
Definition: model.cc:92
static SolutionOutputSpecs MultiDimensionalArray(const std::string &name, std::vector< Bounds > bounds, std::vector< Variable * > flat_variables, bool display_as_boolean)
Definition: model.cc:927
static Domain IntegerList(std::vector< int64_t > values)
Definition: model.cc:33
#define DCHECK(condition)
Definition: base/logging.h:885
static Argument VoidArgument()
Definition: model.cc:533
static SolutionOutputSpecs VoidOutput()
Definition: model.cc:939
static Annotation VarRef(Variable *const var)
Definition: model.cc:840
static Argument VarRef(Variable *const var)
Definition: model.cc:519
static Domain AllFloats()
Definition: model.cc:100
static Argument DomainList(std::vector< Domain > domains)
Definition: model.cc:512
Variable * AddFloatConstant(double value)
Definition: model.cc:978
bool RemoveValue(int64_t value)
Definition: model.cc:430
void Satisfy(std::vector< Annotation > search_annotations)
Definition: model.cc:1001
std::vector< Annotation > annotations
Collection of objects used to extend the Constraint Solver library.
static Annotation Empty()
Definition: model.cc:779
static Domain FloatInterval(double lb, double ub)
Definition: model.cc:107
std::vector< Variable * > variables
static Argument FloatValue(double value)
Definition: model.cc:552
std::vector< Argument > arguments
static Domain FloatValue(double value)
Definition: model.cc:115
IntVar * var
Definition: expr_array.cc:1874
Variable * AddVariable(const std::string &name, const Domain &domain, bool defined)
Definition: model.cc:963
bool IntersectWithFloatDomain(const Domain &domain)
Definition: model.cc:247
const bool maximize_
Definition: search.cc:2508
static Annotation FunctionCallWithArguments(const std::string &id, std::vector< Annotation > args)
Definition: model.cc:805
static Argument Interval(int64_t imin, int64_t imax)
Definition: model.cc:497
bool IntersectWithDomain(const Domain &domain)
Definition: model.cc:122
void RemoveArg(int arg_pos)
Definition: model.cc:763
static Argument IntegerList(std::vector< int64_t > values)
Definition: model.cc:505
static Domain SetOfIntegerValue(int64_t value)
Definition: model.cc:80
static Annotation String(const std::string &str)
Definition: model.cc:858
std::string DebugString() const
Definition: model.cc:1020
int64_t value
std::vector< int64_t > values
const Constraint * ct
Variable * Var() const
Definition: model.cc:713
void AddConstraint(const std::string &id, std::vector< Argument > arguments, bool is_domain)
Definition: model.cc:985
bool OverlapsDomain(const Domain &other) const
Definition: model.cc:418