OR-Tools  9.6
flatzinc/model.h
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 #ifndef OR_TOOLS_FLATZINC_MODEL_H_
15 #define OR_TOOLS_FLATZINC_MODEL_H_
16 
17 #include <cstdint>
18 #include <map>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/strings/string_view.h"
26 #include "ortools/base/logging.h"
28 #include "ortools/util/logging.h"
30 
31 namespace operations_research {
32 namespace fz {
33 
34 struct Constraint;
35 class Model;
36 
37 // A domain represents the possible values of a variable, and its type
38 // (which carries display information, i.e. a Boolean will be displayed
39 // differently than an integer with domain {0, 1}).
40 // It can be:
41 // - an explicit list of all possible values, in which case is_interval is
42 // false. If the list is empty, then the domain is empty.
43 // - an interval, in which case is_interval is true and values.size() == 2,
44 // and the interval is [values[0], values[1]].
45 // - all integers, in which case values is empty, and is_interval is true.
46 // Note that semi-infinite intervals aren't supported.
47 // - a Boolean domain({ 0, 1 } with Boolean display tag).
48 // TODO(user): Rework domains, all int64_t should be kintmin..kint64max.
49 // It is a bit tricky though as we must take care of overflows.
50 // If is_a_set is true, then this domain has a set semantics. For a set
51 // variable, any subset of the initial set of values is a valid assignment,
52 // instead of exactly one value.
53 struct Domain {
54  // The values will be sorted and duplicate values will be removed.
55  static Domain IntegerList(std::vector<int64_t> values);
56  static Domain AllInt64();
57  static Domain IntegerValue(int64_t value);
58  static Domain Interval(int64_t included_min, int64_t included_max);
59  static Domain Boolean();
60  static Domain SetOfIntegerList(std::vector<int64_t> values);
61  static Domain SetOfAllInt64();
62  static Domain SetOfIntegerValue(int64_t value);
63  static Domain SetOfInterval(int64_t included_min, int64_t included_max);
64  static Domain SetOfBoolean();
65  static Domain EmptyDomain();
66  static Domain AllFloats();
67  static Domain FloatValue(double value);
68  static Domain FloatInterval(double lb, double ub);
69  // TODO(user): Do we need SetOfFloats() ?
70 
71  bool HasOneValue() const;
72  bool empty() const;
73 
74  // Returns the min of the domain.
75  int64_t Min() const;
76 
77  // Returns the max of the domain.
78  int64_t Max() const;
79 
80  // Returns the value of the domain. HasOneValue() must return true.
81  int64_t Value() const;
82 
83  // Returns true if the domain is [kint64min..kint64max]
84  bool IsAllInt64() const;
85 
86  // Various inclusion tests on a domain.
87  bool Contains(int64_t value) const;
88  bool OverlapsIntList(const std::vector<int64_t>& vec) const;
89  bool OverlapsIntInterval(int64_t lb, int64_t ub) const;
90  bool OverlapsDomain(const Domain& other) const;
91 
92  // All the following modifiers change the internal representation
93  // list to interval or interval to list.
94  bool IntersectWithSingleton(int64_t value);
95  bool IntersectWithDomain(const Domain& domain);
96  bool IntersectWithInterval(int64_t interval_min, int64_t interval_max);
97  bool IntersectWithListOfIntegers(const std::vector<int64_t>& integers);
98  bool IntersectWithFloatDomain(const Domain& domain);
99 
100  // Returns true iff the value did belong to the domain, and was removed.
101  // Try to remove the value. It returns true if it was actually removed.
102  // If the value is inside a large interval, then it will not be removed.
103  bool RemoveValue(int64_t value);
104  // Sets the empty float domain. Returns true.
105  bool SetEmptyFloatDomain();
106  std::string DebugString() const;
107 
108  // These should never be modified from outside the class.
109  std::vector<int64_t> values;
110  bool is_interval = false;
111  bool display_as_boolean = false;
112  // Indicates if the domain was created as a set domain.
113  bool is_a_set = false;
114  // Float domain.
115  bool is_float = false;
116  std::vector<double> float_values;
117 };
118 
119 // An int var is a name with a domain of possible values, along with
120 // some tags. Typically, an Variable is on the heap, and owned by the
121 // global Model object.
122 struct Variable {
123  // This method tries to unify two variables. This can happen during the
124  // parsing of the model or during presolve. This is possible if at least one
125  // of the two variable is not the target of a constraint. (otherwise it
126  // returns false).
127  // The semantic of the merge is the following:
128  // - the resulting domain is the intersection of the two domains.
129  // - if one variable is not temporary, the result is not temporary.
130  // - if one variable is temporary, the name is the name of the other
131  // variable. If both variables are temporary or both variables are not
132  // temporary, the name is chosen arbitrarily between the two names.
133  bool Merge(absl::string_view other_name, const Domain& other_domain,
134  bool other_temporary);
135 
136  std::string DebugString() const;
137 
138  std::string name;
140  // Indicates if the variable is a temporary variable created when flattening
141  // the model. For instance, if you write x == y * z + y, then it will be
142  // expanded into y * z == t and x = t + y. And t will be a temporary variable.
143  bool temporary : 1;
144  // Indicates if the variable should be created at all. A temporary variable
145  // can be unreachable in the active model if nobody uses it. In that case,
146  // there is no need to create it.
147  bool active : 1;
148 
149  private:
150  friend class Model;
151 
152  Variable(absl::string_view name_, const Domain& domain_, bool temporary_);
153 };
154 
155 // An argument is either an integer value, an integer domain, a
156 // reference to a variable, or an array of variable references.
157 struct Argument {
158  enum Type {
169  };
170 
171  static Argument IntegerValue(int64_t value);
172  static Argument Interval(int64_t imin, int64_t imax);
173  static Argument IntegerList(std::vector<int64_t> values);
174  static Argument DomainList(std::vector<Domain> domains);
175  static Argument FloatValue(double value);
176  static Argument FloatInterval(double lb, double ub);
177  static Argument FloatList(std::vector<double> floats);
178  static Argument VarRef(Variable* const var);
179  static Argument VarRefArray(std::vector<Variable*> vars);
180  static Argument VoidArgument();
181  static Argument FromDomain(const Domain& domain);
182 
183  std::string DebugString() const;
184 
185  // Returns true if the argument is a variable.
186  bool IsVariable() const;
187  // Returns true if the argument has only one value (integer value, integer
188  // list of size 1, interval of size 1, or variable with a singleton domain).
189  bool HasOneValue() const;
190  // Returns the value of the argument. Does DCHECK(HasOneValue()).
191  int64_t Value() const;
192  // Returns true if it an integer list, or an array of integer
193  // variables (or domain) each having only one value.
194  bool IsArrayOfValues() const;
195  // Returns true if the argument is an integer value, an integer
196  // list, or an interval, and it contains the given value.
197  // It will check that the type is actually one of the above.
198  bool Contains(int64_t value) const;
199  // Returns the value of the pos-th element.
200  int64_t ValueAt(int pos) const;
201  // Returns the variable inside the argument if the type is VAR_REF,
202  // or nullptr otherwise.
203  Variable* Var() const;
204  // Returns the variable at position pos inside the argument if the type is
205  // VAR_REF_ARRAY or nullptr otherwise.
206  Variable* VarAt(int pos) const;
207  // Returns true is the pos-th argument is fixed.
208  bool HasOneValueAt(int pos) const;
209  // Returns the number of object in the argument.
210  int Size() const;
211 
213  std::vector<int64_t> values;
214  std::vector<Variable*> variables;
215  std::vector<Domain> domains;
216  std::vector<double> floats;
217 };
218 
219 // A constraint has a type, some arguments, and a few tags. Typically, a
220 // Constraint is on the heap, and owned by the global Model object.
221 struct Constraint {
222  Constraint(absl::string_view t, std::vector<Argument> args,
223  bool strong_propag)
224  : type(t),
225  arguments(std::move(args)),
226  strong_propagation(strong_propag),
227  active(true),
228  presolve_propagation_done(false) {}
229 
230  std::string DebugString() const;
231 
232  // Helpers to be used during presolve.
233  void MarkAsInactive();
234  // Helper method to remove one argument.
235  void RemoveArg(int arg_pos);
236  // Set as a False constraint.
237  void SetAsFalse();
238 
239  // The flatzinc type of the constraint (i.e. "int_eq" for integer equality)
240  // stored as a string.
241  std::string type;
242  std::vector<Argument> arguments;
243  // Is true if the constraint should use the strongest level of propagation.
244  // This is a hint in the model. For instance, in the AllDifferent constraint,
245  // there are different algorithms to propagate with different pruning/speed
246  // ratios. When strong_propagation is true, one should use, if possible, the
247  // algorithm with the strongest pruning.
249  // Indicates if the constraint is active. Presolve can make it inactive by
250  // propagating it, or by regrouping it. Once a constraint is inactive, it is
251  // logically removed from the model, it is not extracted, and it is ignored by
252  // presolve.
253  bool active : 1;
254 
255  // Indicates if presolve has finished propagating this constraint.
257 };
258 
259 // An annotation is a set of information. It has two use cases. One during
260 // parsing to store intermediate information on model objects (i.e. the defines
261 // part of a constraint). The other use case is to store all search
262 // declarations. This persists after model parsing.
263 struct Annotation {
264  enum Type {
274  };
275 
276  static Annotation Empty();
277  static Annotation AnnotationList(std::vector<Annotation> list);
278  static Annotation Identifier(absl::string_view id);
279  static Annotation FunctionCallWithArguments(absl::string_view id,
280  std::vector<Annotation> args);
281  static Annotation FunctionCall(absl::string_view id);
282  static Annotation Interval(int64_t interval_min, int64_t interval_max);
283  static Annotation IntegerValue(int64_t value);
284  static Annotation IntegerList(const std::vector<int64_t>& values);
285  static Annotation VarRef(Variable* const var);
286  static Annotation VarRefArray(std::vector<Variable*> variables);
287  static Annotation String(absl::string_view str);
288 
289  std::string DebugString() const;
290  bool IsFunctionCallWithIdentifier(absl::string_view identifier) const {
291  return type == FUNCTION_CALL && id == identifier;
292  }
293  // Copy all the variable references contained in this annotation (and its
294  // children). Depending on the type of this annotation, there can be zero,
295  // one, or several.
296  void AppendAllVariables(std::vector<Variable*>* vars) const;
297 
299  int64_t interval_min;
300  int64_t interval_max;
301  std::string id;
302  std::vector<Annotation> annotations;
303  std::vector<Variable*> variables;
304  std::vector<int64_t> values;
305  std::string string_value;
306 };
307 
308 // Information on what should be displayed when a solution is found.
309 // It follows the flatzinc specification (www.minizinc.org).
311  struct Bounds {
312  Bounds(int64_t min_value_, int64_t max_value_)
313  : min_value(min_value_), max_value(max_value_) {}
314  std::string DebugString() const;
315  int64_t min_value;
316  int64_t max_value;
317  };
318 
319  // Will output: name = <variable value>.
320  static SolutionOutputSpecs SingleVariable(absl::string_view name,
322  bool display_as_boolean);
323  // Will output (for example):
324  // name = array2d(min1..max1, min2..max2, [list of variable values])
325  // for a 2d array (bounds.size() == 2).
327  absl::string_view name, std::vector<Bounds> bounds,
328  std::vector<Variable*> flat_variables, bool display_as_boolean);
329  // Empty output.
331 
332  std::string DebugString() const;
333 
334  std::string name;
336  std::vector<Variable*> flat_variables;
337  // These are the starts and ends of intervals for displaying (potentially
338  // multi-dimensional) arrays.
339  std::vector<Bounds> bounds;
341 };
342 
343 class Model {
344  public:
345  explicit Model(absl::string_view name)
346  : name_(name), objective_(nullptr), maximize_(true) {}
347  ~Model();
348 
349  // ----- Builder methods -----
350 
351  // The objects returned by AddVariable(), AddConstant(), and AddConstraint()
352  // are owned by the model and will remain live for its lifetime.
353  Variable* AddVariable(absl::string_view name, const Domain& domain,
354  bool defined);
355  Variable* AddConstant(int64_t value);
357  // Creates and add a constraint to the model.
358  void AddConstraint(absl::string_view id, std::vector<Argument> arguments,
359  bool is_domain);
360  void AddConstraint(absl::string_view id, std::vector<Argument> arguments);
362 
363  // Set the search annotations and the objective: either simply satisfy the
364  // problem, or minimize or maximize the given variable (which must have been
365  // added with AddVariable() already).
366  void Satisfy(std::vector<Annotation> search_annotations);
367  void Minimize(Variable* obj, std::vector<Annotation> search_annotations);
368  void Maximize(Variable* obj, std::vector<Annotation> search_annotations);
369 
370  bool IsInconsistent() const;
371 
372  // ----- Accessors and mutators -----
373 
374  const std::vector<Variable*>& variables() const { return variables_; }
375  const std::vector<Constraint*>& constraints() const { return constraints_; }
376  const std::vector<Annotation>& search_annotations() const {
377  return search_annotations_;
378  }
379 #if !defined(SWIG)
381  return util::MutableVectorIteration<Annotation>(&search_annotations_);
382  }
383 #endif
384  const std::vector<SolutionOutputSpecs>& output() const { return output_; }
385 #if !defined(SWIG)
388  }
389 #endif
390  bool maximize() const { return maximize_; }
391  Variable* objective() const { return objective_; }
392  void SetObjective(Variable* obj) { objective_ = obj; }
393 
394  // Services.
395  std::string DebugString() const;
396 
397  const std::string& name() const { return name_; }
398 
399  private:
400  const std::string name_;
401  // owned.
402  // TODO(user): use unique_ptr
403  std::vector<Variable*> variables_;
404  // owned.
405  // TODO(user): use unique_ptr
406  std::vector<Constraint*> constraints_;
407  // The objective variable (it belongs to variables_).
408  Variable* objective_;
409  bool maximize_;
410  // All search annotations are stored as a vector of Annotation.
411  std::vector<Annotation> search_annotations_;
412  std::vector<SolutionOutputSpecs> output_;
413 };
414 
415 // Stand-alone statistics class on the model.
416 // TODO(user): Clean up API to pass a Model* in argument.
418  public:
419  explicit ModelStatistics(const Model& model, SolverLogger* logger)
420  : model_(model), logger_(logger) {}
422  return constraints_per_variables_[var].size();
423  }
424  void BuildStatistics();
425  void PrintStatistics() const;
426 
427  private:
428  const Model& model_;
429  SolverLogger* logger_;
430  std::map<std::string, std::vector<Constraint*>> constraints_per_type_;
431  absl::flat_hash_map<const Variable*, std::vector<Constraint*>>
432  constraints_per_variables_;
433 };
434 
435 // Helper method to flatten Search annotations.
436 void FlattenAnnotations(const Annotation& ann, std::vector<Annotation>* out);
437 
438 } // namespace fz
439 } // namespace operations_research
440 
441 #endif // OR_TOOLS_FLATZINC_MODEL_H_
Model(absl::string_view name)
void AddConstraint(absl::string_view id, std::vector< Argument > arguments, bool is_domain)
util::MutableVectorIteration< Annotation > mutable_search_annotations()
util::MutableVectorIteration< SolutionOutputSpecs > mutable_output()
void SetObjective(Variable *obj)
Variable * AddConstant(int64_t value)
void Satisfy(std::vector< Annotation > search_annotations)
const std::vector< Constraint * > & constraints() const
const std::vector< Annotation > & search_annotations() const
void AddOutput(SolutionOutputSpecs output)
const std::vector< SolutionOutputSpecs > & output() const
Variable * AddVariable(absl::string_view name, const Domain &domain, bool defined)
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
const std::string & name() const
const std::vector< Variable * > & variables() const
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Variable * AddFloatConstant(double value)
ModelStatistics(const Model &model, SolverLogger *logger)
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
void FlattenAnnotations(const Annotation &ann, std::vector< Annotation > *out)
Collection of objects used to extend the Constraint Solver library.
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
Constraint(absl::string_view t, std::vector< Argument > args, bool strong_propag)
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)
Bounds(int64_t min_value_, int64_t max_value_)
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)