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