OR-Tools  9.2
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 (other_values.contains(val)) {
235  if (new_values.empty() || val != new_values.back()) {
236  new_values.push_back(val);
237  }
238  } else {
239  changed = true;
240  }
241  }
242  values.swap(new_values);
243  return changed;
244  }
245 }
246 
248  CHECK(domain.is_float);
249  if (!is_interval && float_values.empty()) {
250  // Empty domain. Nothing to do.
251  return false;
252  }
253  if (!domain.is_interval && domain.float_values.empty()) {
254  return SetEmptyFloatDomain();
255  }
256  if (domain.is_interval && domain.float_values.empty()) {
257  // domain is all floats. Nothing to do.
258  return false;
259  }
260 
261  if (is_interval && float_values.empty()) { // Currently all floats.
262  // Copy the domain.
263  is_interval = domain.is_interval;
264  float_values = domain.float_values;
265  return true;
266  }
267 
268  if (is_interval) {
269  // this is a double interval.
270  CHECK_EQ(2, float_values.size());
271  if (domain.is_interval) {
272  bool changed = false;
273  if (float_values[0] < domain.float_values[0]) {
274  float_values[0] = domain.float_values[0];
275  changed = true;
276  }
277  if (float_values[1] > domain.float_values[1]) {
278  float_values[1] = domain.float_values[1];
279  changed = true;
280  }
281  if (float_values[0] > float_values[1]) {
282  return SetEmptyFloatDomain();
283  }
284  return changed;
285  } else {
286  CHECK_EQ(1, domain.float_values.size());
287  const double value = domain.float_values[0];
288  if (value >= float_values[0] && value <= float_values[1]) {
289  is_interval = false;
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 (container.contains(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  return domains[pos].Value();
698  }
699  case VAR_REF_ARRAY: {
700  CHECK_GE(pos, 0);
701  CHECK_LT(pos, variables.size());
702  return variables[pos]->domain.Value();
703  }
704  default: {
705  LOG(FATAL) << "Should not be here";
706  return 0;
707  }
708  }
709 }
710 
711 bool Argument::HasOneValueAt(int pos) const {
712  switch (type) {
713  case INT_LIST:
714  CHECK_GE(pos, 0);
715  CHECK_LT(pos, values.size());
716  return true;
717  case DOMAIN_LIST: {
718  CHECK_GE(pos, 0);
719  CHECK_LT(pos, domains.size());
720  return domains[pos].HasOneValue();
721  }
722  case VAR_REF_ARRAY: {
723  CHECK_GE(pos, 0);
724  CHECK_LT(pos, variables.size());
725  return variables[pos]->domain.HasOneValue();
726  }
727  default: {
728  LOG(FATAL) << "Should not be here";
729  return false;
730  }
731  }
732 }
733 
735  return type == VAR_REF ? variables[0] : nullptr;
736 }
737 
738 Variable* Argument::VarAt(int pos) const {
739  return type == VAR_REF_ARRAY ? variables[pos] : nullptr;
740 }
741 
742 int Argument::Size() const {
743  switch (type) {
744  case INT_LIST:
745  return values.size();
746  case DOMAIN_LIST: {
747  return domains.size();
748  }
749  case VAR_REF_ARRAY: {
750  return variables.size();
751  }
752  case VOID_ARGUMENT: {
753  return 0;
754  }
755  default: {
756  LOG(FATAL) << "Should not be here";
757  return 0;
758  }
759  }
760 }
761 
762 // ----- Variable -----
763 
764 Variable::Variable(const std::string& name_, const Domain& domain_,
765  bool temporary_)
766  : name(name_), domain(domain_), temporary(temporary_), active(true) {
767  if (!domain.is_interval) {
768  gtl::STLSortAndRemoveDuplicates(&domain.values);
769  }
770 }
771 
772 bool Variable::Merge(const std::string& other_name, const Domain& other_domain,
773  bool other_temporary) {
774  if (temporary && !other_temporary) {
775  temporary = false;
776  name = other_name;
777  }
778  domain.IntersectWithDomain(other_domain);
779  return true;
780 }
781 
782 std::string Variable::DebugString() const {
783  if (!domain.is_interval && domain.values.size() == 1) {
784  return absl::StrFormat("% d", domain.values.back());
785  } else {
786  return absl::StrFormat("%s(%s%s)%s", name, domain.DebugString(),
787  temporary ? ", temporary" : "",
788  active ? "" : " [removed during presolve]");
789  }
790 }
791 
792 // ----- Constraint -----
793 
794 std::string Constraint::DebugString() const {
795  const std::string strong = strong_propagation ? "strong propagation" : "";
796  const std::string presolve_status_str =
797  active ? ""
798  : (presolve_propagation_done ? "[propagated during presolve]"
799  : "[removed during presolve]");
800  return absl::StrFormat("%s(%s)%s %s", type, JoinDebugString(arguments, ", "),
801  strong, presolve_status_str);
802 }
803 
804 void Constraint::RemoveArg(int arg_pos) {
805  arguments.erase(arguments.begin() + arg_pos);
806 }
807 
809  active = false;
810  // TODO(user): Reclaim arguments and memory.
811 }
812 
814  type = "false_constraint";
815  arguments.clear();
816 }
817 
818 // ----- Annotation -----
819 
821  Annotation result;
822  result.type = ANNOTATION_LIST;
823  result.interval_min = 0;
824  result.interval_max = 0;
825  return result;
826 }
827 
828 Annotation Annotation::AnnotationList(std::vector<Annotation> list) {
829  Annotation result;
830  result.type = ANNOTATION_LIST;
831  result.interval_min = 0;
832  result.interval_max = 0;
833  result.annotations = std::move(list);
834  return result;
835 }
836 
837 Annotation Annotation::Identifier(const std::string& id) {
838  Annotation result;
839  result.type = IDENTIFIER;
840  result.interval_min = 0;
841  result.interval_max = 0;
842  result.id = id;
843  return result;
844 }
845 
847  std::vector<Annotation> args) {
848  Annotation result;
849  result.type = FUNCTION_CALL;
850  result.interval_min = 0;
851  result.interval_max = 0;
852  result.id = id;
853  result.annotations = std::move(args);
854  return result;
855 }
856 
857 Annotation Annotation::FunctionCall(const std::string& id) {
858  Annotation result;
859  result.type = FUNCTION_CALL;
860  result.interval_min = 0;
861  result.interval_max = 0;
862  result.id = id;
863  return result;
864 }
865 
866 Annotation Annotation::Interval(int64_t interval_min, int64_t interval_max) {
867  Annotation result;
868  result.type = INTERVAL;
869  result.interval_min = interval_min;
870  result.interval_max = interval_max;
871  return result;
872 }
873 
875  Annotation result;
876  result.type = INT_VALUE;
877  result.interval_min = value;
878  return result;
879 }
880 
882  Annotation result;
883  result.type = VAR_REF;
884  result.interval_min = 0;
885  result.interval_max = 0;
886  result.variables.push_back(var);
887  return result;
888 }
889 
890 Annotation Annotation::VarRefArray(std::vector<Variable*> variables) {
891  Annotation result;
892  result.type = VAR_REF_ARRAY;
893  result.interval_min = 0;
894  result.interval_max = 0;
895  result.variables = std::move(variables);
896  return result;
897 }
898 
899 Annotation Annotation::String(const std::string& str) {
900  Annotation result;
901  result.type = STRING_VALUE;
902  result.interval_min = 0;
903  result.interval_max = 0;
904  result.string_value = str;
905  return result;
906 }
907 
908 void Annotation::AppendAllVariables(std::vector<Variable*>* const vars) const {
909  for (const Annotation& ann : annotations) {
910  ann.AppendAllVariables(vars);
911  }
912  if (!variables.empty()) {
913  vars->insert(vars->end(), variables.begin(), variables.end());
914  }
915 }
916 
917 std::string Annotation::DebugString() const {
918  switch (type) {
919  case ANNOTATION_LIST: {
920  return absl::StrFormat("[%s]", JoinDebugString(annotations, ", "));
921  }
922  case IDENTIFIER: {
923  return id;
924  }
925  case FUNCTION_CALL: {
926  return absl::StrFormat("%s(%s)", id, JoinDebugString(annotations, ", "));
927  }
928  case INTERVAL: {
929  return absl::StrFormat("%d..%d", interval_min, interval_max);
930  }
931  case INT_VALUE: {
932  return absl::StrCat(interval_min);
933  }
934  case VAR_REF: {
935  return variables.front()->name;
936  }
937  case VAR_REF_ARRAY: {
938  std::string result = "[";
939  for (int i = 0; i < variables.size(); ++i) {
940  result.append(variables[i]->DebugString());
941  result.append(i != variables.size() - 1 ? ", " : "]");
942  }
943  return result;
944  }
945  case STRING_VALUE: {
946  return absl::StrFormat("\"%s\"", string_value);
947  }
948  }
949  LOG(FATAL) << "Unhandled case in DebugString " << static_cast<int>(type);
950  return "";
951 }
952 
953 // ----- SolutionOutputSpecs -----
954 
956  return absl::StrFormat("%d..%d", min_value, max_value);
957 }
958 
960  const std::string& name, Variable* variable, bool display_as_boolean) {
961  SolutionOutputSpecs result;
962  result.name = name;
963  result.variable = variable;
965  return result;
966 }
967 
969  const std::string& name, std::vector<Bounds> bounds,
970  std::vector<Variable*> flat_variables, bool display_as_boolean) {
971  SolutionOutputSpecs result;
972  result.variable = nullptr;
973  result.name = name;
974  result.bounds = std::move(bounds);
975  result.flat_variables = std::move(flat_variables);
977  return result;
978 }
979 
981  SolutionOutputSpecs result;
982  result.variable = nullptr;
983  result.display_as_boolean = false;
984  return result;
985 }
986 
987 std::string SolutionOutputSpecs::DebugString() const {
988  if (variable != nullptr) {
989  return absl::StrFormat("output_var(%s)", variable->name);
990  } else {
991  return absl::StrFormat("output_array([%s] [%s])",
992  JoinDebugString(bounds, ", "),
994  }
995 }
996 
997 // ----- Model -----
998 
1000  gtl::STLDeleteElements(&variables_);
1001  gtl::STLDeleteElements(&constraints_);
1002 }
1003 
1004 Variable* Model::AddVariable(const std::string& name, const Domain& domain,
1005  bool defined) {
1006  Variable* const var = new Variable(name, domain, defined);
1007  variables_.push_back(var);
1008  return var;
1009 }
1010 
1011 // TODO(user): Create only once constant per value.
1013  Variable* const var =
1014  new Variable(absl::StrCat(value), Domain::IntegerValue(value), true);
1015  variables_.push_back(var);
1016  return var;
1017 }
1018 
1020  Variable* const var =
1021  new Variable(absl::StrCat(value), Domain::FloatValue(value), true);
1022  variables_.push_back(var);
1023  return var;
1024 }
1025 
1026 void Model::AddConstraint(const std::string& id,
1027  std::vector<Argument> arguments, bool is_domain) {
1028  Constraint* const constraint =
1029  new Constraint(id, std::move(arguments), is_domain);
1030  constraints_.push_back(constraint);
1031 }
1032 
1033 void Model::AddConstraint(const std::string& id,
1034  std::vector<Argument> arguments) {
1035  AddConstraint(id, std::move(arguments), false);
1036 }
1037 
1039  output_.push_back(std::move(output));
1040 }
1041 
1042 void Model::Satisfy(std::vector<Annotation> search_annotations) {
1043  objective_ = nullptr;
1044  search_annotations_ = std::move(search_annotations);
1045 }
1046 
1048  std::vector<Annotation> search_annotations) {
1049  objective_ = obj;
1050  maximize_ = false;
1051  search_annotations_ = std::move(search_annotations);
1052 }
1053 
1055  std::vector<Annotation> search_annotations) {
1056  objective_ = obj;
1057  maximize_ = true;
1058  search_annotations_ = std::move(search_annotations);
1059 }
1060 
1061 std::string Model::DebugString() const {
1062  std::string output = absl::StrFormat("Model %s\nVariables\n", name_);
1063  for (int i = 0; i < variables_.size(); ++i) {
1064  absl::StrAppendFormat(&output, " %s\n", variables_[i]->DebugString());
1065  }
1066  output.append("Constraints\n");
1067  for (int i = 0; i < constraints_.size(); ++i) {
1068  if (constraints_[i] != nullptr) {
1069  absl::StrAppendFormat(&output, " %s\n", constraints_[i]->DebugString());
1070  }
1071  }
1072  if (objective_ != nullptr) {
1073  absl::StrAppendFormat(&output, "%s %s\n %s\n",
1074  maximize_ ? "Maximize" : "Minimize", objective_->name,
1075  JoinDebugString(search_annotations_, ", "));
1076  } else {
1077  absl::StrAppendFormat(&output, "Satisfy\n %s\n",
1078  JoinDebugString(search_annotations_, ", "));
1079  }
1080  output.append("Output\n");
1081  for (int i = 0; i < output_.size(); ++i) {
1082  absl::StrAppendFormat(&output, " %s\n", output_[i].DebugString());
1083  }
1084 
1085  return output;
1086 }
1087 
1089  for (Variable* var : variables_) {
1090  if (var->domain.empty()) {
1091  return true;
1092  }
1093  }
1094  for (Constraint* ct : constraints_) {
1095  if (ct->type == "false_constraint") {
1096  return true;
1097  }
1098  }
1099 
1100  return false;
1101 }
1102 
1103 // ----- Model statistics -----
1104 
1106  SOLVER_LOG(logger_, "Model ", model_.name());
1107  for (const auto& it : constraints_per_type_) {
1108  SOLVER_LOG(logger_, " - ", it.first, ": ", it.second.size());
1109  }
1110  if (model_.objective() == nullptr) {
1111  SOLVER_LOG(logger_, " - Satisfaction problem");
1112  } else {
1113  SOLVER_LOG(logger_, " - ",
1114  (model_.maximize() ? "Maximization" : "Minimization"),
1115  " problem");
1116  }
1117  SOLVER_LOG(logger_);
1118 }
1119 
1121  constraints_per_type_.clear();
1122  constraints_per_variables_.clear();
1123  for (Constraint* const ct : model_.constraints()) {
1124  if (ct != nullptr && ct->active) {
1125  constraints_per_type_[ct->type].push_back(ct);
1126  absl::flat_hash_set<const Variable*> marked;
1127  for (const Argument& arg : ct->arguments) {
1128  for (Variable* const var : arg.variables) {
1129  marked.insert(var);
1130  }
1131  }
1132  for (const Variable* const var : marked) {
1133  constraints_per_variables_[var].push_back(ct);
1134  }
1135  }
1136  }
1137 }
1138 
1139 // Flatten Search annotations.
1140 void FlattenAnnotations(const Annotation& ann, std::vector<Annotation>* out) {
1141  if (ann.type == Annotation::ANNOTATION_LIST ||
1142  ann.IsFunctionCallWithIdentifier("seq_search")) {
1143  for (const Annotation& inner : ann.annotations) {
1144  FlattenAnnotations(inner, out);
1145  }
1146  } else {
1147  out->push_back(ann);
1148  }
1149 }
1150 
1151 } // namespace fz
1152 } // namespace operations_research
static Argument FromDomain(const Domain &domain)
Definition: model.cc:539
#define CHECK(condition)
Definition: base/logging.h:495
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:794
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:1012
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69
#define CHECK_GE(val1, val2)
Definition: base/logging.h:706
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:837
const std::string name
void FlattenAnnotations(const Annotation &ann, std::vector< Annotation > *out)
Definition: model.cc:1140
std::string DebugString() const
Definition: model.cc:461
static SolutionOutputSpecs SingleVariable(const std::string &name, Variable *variable, bool display_as_boolean)
Definition: model.cc:959
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Definition: model.cc:1047
bool HasOneValueAt(int pos) const
Definition: model.cc:711
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
Definition: model.cc:1054
int64_t ValueAt(int pos) const
Definition: model.cc:688
#define LOG(severity)
Definition: base/logging.h:420
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:917
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:908
std::string DebugString() const
Definition: model.cc:782
#define CHECK_LT(val1, val2)
Definition: base/logging.h:705
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:857
static Annotation Interval(int64_t interval_min, int64_t interval_max)
Definition: model.cc:866
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:874
static Annotation VarRefArray(std::vector< Variable * > variables)
Definition: model.cc:890
IntVar *const objective_
Definition: search.cc:3017
static Annotation AnnotationList(std::vector< Annotation > list)
Definition: model.cc:828
std::string JoinNameFieldPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:58
void STLDeleteElements(T *container)
Definition: stl_util.h:372
void AddOutput(SolutionOutputSpecs output)
Definition: model.cc:1038
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:772
Variable * VarAt(int pos) const
Definition: model.cc:738
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
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:968
static Domain IntegerList(std::vector< int64_t > values)
Definition: model.cc:33
#define DCHECK(condition)
Definition: base/logging.h:889
static Argument VoidArgument()
Definition: model.cc:533
static SolutionOutputSpecs VoidOutput()
Definition: model.cc:980
static Annotation VarRef(Variable *const var)
Definition: model.cc:881
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:1019
bool RemoveValue(int64_t value)
Definition: model.cc:430
void Satisfy(std::vector< Annotation > search_annotations)
Definition: model.cc:1042
std::vector< Annotation > annotations
Collection of objects used to extend the Constraint Solver library.
static Annotation Empty()
Definition: model.cc:820
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:1004
bool IntersectWithFloatDomain(const Domain &domain)
Definition: model.cc:247
const bool maximize_
Definition: search.cc:2559
static Annotation FunctionCallWithArguments(const std::string &id, std::vector< Annotation > args)
Definition: model.cc:846
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:804
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:899
std::string DebugString() const
Definition: model.cc:1061
int64_t value
std::vector< int64_t > values
const Constraint * ct
Variable * Var() const
Definition: model.cc:734
void AddConstraint(const std::string &id, std::vector< Argument > arguments, bool is_domain)
Definition: model.cc:1026
bool OverlapsDomain(const Domain &other) const
Definition: model.cc:418