OR-Tools  9.6
linear_propagation.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_SAT_LINEAR_PROPAGATION_H_
15 #define OR_TOOLS_SAT_LINEAR_PROPAGATION_H_
16 
17 #include <deque>
18 #include <functional>
19 #include <ostream>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "absl/container/inlined_vector.h"
25 #include "absl/types/span.h"
27 #include "ortools/sat/integer.h"
28 #include "ortools/sat/sat_base.h"
29 #include "ortools/sat/sat_solver.h"
31 
32 namespace operations_research {
33 namespace sat {
34 
35 DEFINE_STRONG_INDEX_TYPE(EnforcementId);
36 
37 // A FIFO queue that allows some form of reordering of its element.
39  public:
41 
42  // Note that this requires the queue to be empty or to never have been poped
43  // before.
44  void IncreaseSize(int n);
45 
46  int Pop();
47  void Push(int id);
48 
49  bool empty() const { return left_ == right_; }
50  bool Contains(int id) const { return pos_[id] != -1; }
51 
52  // Reorder the given element to match their given order. They must all be in
53  // the queue.
54  void Reorder(absl::Span<const int> order);
55  void ReorderDense(absl::Span<const int> order);
56 
57  // Sorts the given elements by their current position from the top.
58  // Elements should all be in the queue.
59  void SortByPos(absl::Span<int> elements);
60 
61  private:
62  // The queue is stored in [left_, right_) with eventual wrap around % size.
63  // The positions of each element is in pos_[element] and never changes during
64  // normal operation. A position of -1 means that the element is not in the
65  // queue.
66  std::vector<int> pos_;
67  std::vector<int> queue_;
68  int left_ = 0;
69  int right_ = 0;
70 
71  std::vector<int> tmp_positions_;
72  std::vector<int> tmp_order_;
73 };
74 
75 // An enforced constraint can be in one of these 4 states.
76 // Note that we rely on the integer encoding to take 2 bits for optimization.
78  // One enforcement literal is false.
79  IS_FALSE = 0,
80  // More than two literals are unassigned.
82  // All enforcement literals are true but one.
84  // All enforcement literals are true.
86 };
87 
88 std::ostream& operator<<(std::ostream& os, const EnforcementStatus& e);
89 
90 // This is meant as an helper to deal with enforcement for any constraint.
92  public:
94 
95  // SatPropagator interface.
96  bool Propagate(Trail* trail) final;
97  void Untrail(const Trail& trail, int trail_index) final;
98 
99  // Adds a new constraint to the class and register a callback that will
100  // be called on status change. Note that we also call the callback with the
101  // initial status if different from CANNOT_PROPAGATE when added.
102  //
103  // It is better to not call this for empty enforcement list, but you can. A
104  // negative id means the level zero status will never change, and only the
105  // first call to callback() should be necessary, we don't save it.
106  EnforcementId Register(
107  absl::Span<const Literal> enforcement,
108  std::function<void(EnforcementStatus)> callback = nullptr);
109 
110  // Add the enforcement reason to the given vector.
111  void AddEnforcementReason(EnforcementId id,
112  std::vector<Literal>* reason) const;
113 
114  // Try to propagate when the enforced constraint is not satisfiable.
115  // This is currently in O(enforcement_size).
116  ABSL_MUST_USE_RESULT bool PropagateWhenFalse(
117  EnforcementId id, absl::Span<const Literal> literal_reason,
118  absl::Span<const IntegerLiteral> integer_reason);
119 
120  EnforcementStatus Status(EnforcementId id) const { return statuses_[id]; }
121 
122  private:
123  absl::Span<Literal> GetSpan(EnforcementId id);
124  absl::Span<const Literal> GetSpan(EnforcementId id) const;
125  void ChangeStatus(EnforcementId id, EnforcementStatus new_status);
126 
127  // Returns kNoLiteralIndex if nothing need to change or a new literal to
128  // watch. This also calls the registered callback.
129  LiteralIndex ProcessIdOnTrue(Literal watched, EnforcementId id);
130 
131  // External classes.
132  const Trail& trail_;
133  const VariablesAssignment& assignment_;
134  IntegerTrail* integer_trail_;
135  RevIntRepository* rev_int_repository_;
136 
137  // All enforcement will be copied there, and we will create Span out of this.
138  // Note that we don't store the span so that we are not invalidated on buffer_
139  // resizing.
141  std::vector<Literal> buffer_;
142 
144  absl::StrongVector<EnforcementId, std::function<void(EnforcementStatus)>>
145  callbacks_;
146 
147  // Used to restore status and call callback on untrail.
148  std::vector<std::pair<EnforcementId, EnforcementStatus>> untrail_stack_;
149  int rev_stack_size_ = 0;
150  int64_t rev_stamp_ = 0;
151 
152  // We use a two watcher scheme.
154  watcher_;
155 
156  std::vector<Literal> temp_literals_;
157  std::vector<Literal> temp_reason_;
158 };
159 
160 // This is meant to supersede both IntegerSumLE and the PrecedencePropagator.
161 //
162 // TODO(user): This is a work in progress and is currently incomplete:
163 // - Lack more incremental support for faster propag.
164 // - Lack detection and propagation of at least one of these linear is true
165 // which can be used to propagate more bound if a variable appear in all these
166 // constraint.
168  public:
169  explicit LinearPropagator(Model* model);
170  ~LinearPropagator() override;
171  bool Propagate() final;
172  void SetLevel(int level) final;
173 
174  // Adds a new constraint to the propagator.
175  void AddConstraint(absl::Span<const Literal> enforcement_literals,
176  absl::Span<const IntegerVariable> vars,
177  absl::Span<const IntegerValue> coeffs,
178  IntegerValue upper_bound);
179 
180  private:
181  // We try to pack the struct as much as possible. Using a maximum size of
182  // 1 << 29 should be okay since we split long constraint anyway. Technically
183  // we could use int16_t or even int8_t if we wanted, but we just need to make
184  // sure we do split ALL constraints, not just the one from the initial mode.
185  //
186  // TODO(user): We could also move some less often used fields out. like
187  // initial size and enf_id that are only needed when we push something.
188  struct ConstraintInfo {
189  unsigned int enf_status : 2;
190  bool all_coeffs_are_one : 1;
191  unsigned int initial_size : 29; // Const. The size including all terms.
192 
193  EnforcementId enf_id; // Const. The id in enforcement_propagator_.
194  int start; // Const. The start of the constraint in the buffers.
195  int rev_size; // The size of the non-fixed terms.
196  IntegerValue rev_rhs; // The current rhs, updated on fixed terms.
197  };
198 
199 #if !defined(_MSC_VER)
200  static_assert(sizeof(ConstraintInfo) == 24,
201  "ERROR_ConstraintInfo_is_not_well_compacted");
202 #endif // !defined(_MSC_VER)
203 
204  absl::Span<IntegerValue> GetCoeffs(const ConstraintInfo& info);
205  absl::Span<IntegerVariable> GetVariables(const ConstraintInfo& info);
206 
207  // Returns false on conflict.
208  ABSL_MUST_USE_RESULT bool PropagateOneConstraint(int id);
209  ABSL_MUST_USE_RESULT bool ReportConflictingCycle();
210  ABSL_MUST_USE_RESULT bool DisassembleSubtree(int root_id, int num_pushed);
211 
212  void ClearPropagatedBy();
213  void CanonicalizeConstraint(int id);
214  void AddToQueueIfNeeded(int id);
215  void AddWatchedToQueue(IntegerVariable var);
216  void SetPropagatedBy(IntegerVariable var, int id);
217  std::string ConstraintDebugString(int id);
218 
219  // External class needed.
220  Trail* trail_;
221  IntegerTrail* integer_trail_;
222  EnforcementPropagator* enforcement_propagator_;
223  GenericLiteralWatcher* watcher_;
224  TimeLimit* time_limit_;
225  RevIntRepository* rev_int_repository_;
226  RevIntegerValueRepository* rev_integer_value_repository_;
227  SharedStatistics* shared_stats_ = nullptr;
228  const int watcher_id_;
229 
230  // To know when we backtracked. See SetLevel().
231  int previous_level_ = 0;
232 
233  // The key to our incrementality. This will be cleared once the propagation
234  // is done, and automatically updated by the integer_trail_ with all the
235  // IntegerVariable that changed since the last clear.
236  SparseBitset<IntegerVariable> modified_vars_;
237 
238  // Per constraint info used during propagation.
239  std::vector<ConstraintInfo> infos_;
240 
241  // Buffer of the constraints data.
242  //
243  // TODO(user): A lot of constrains have all their coeffs at one, we could
244  // exploit this.
245  std::vector<IntegerVariable> variables_buffer_;
246  std::vector<IntegerValue> coeffs_buffer_;
247  std::vector<IntegerValue> buffer_of_ones_;
248 
249  // Filled by PropagateOneConstraint().
250  std::vector<IntegerValue> max_variations_;
251 
252  // For reasons computation. Parallel vectors.
253  std::vector<IntegerLiteral> integer_reason_;
254  std::vector<IntegerValue> reason_coeffs_;
255  std::vector<Literal> literal_reason_;
256 
257  // Queue of constraint to propagate.
258  std::vector<bool> in_queue_;
259  CustomFifoQueue propagation_queue_;
260 
261  int rev_at_false_size_ = 0;
262  std::vector<int> in_queue_and_at_false_;
263 
264  // Watchers.
267  var_to_constraint_ids_;
268 
269  // For an heuristic similar to Tarjan contribution to Bellman-Ford algorithm.
270  // We mark for each variable the last constraint that pushed it, and also keep
271  // the count of propagated variable for each constraint.
272  SparseBitset<IntegerVariable> propagated_by_was_set_;
274  std::vector<int> id_to_propagation_count_;
275 
276  // Used by DissasembleSubtreeAndAddToQueue().
277  struct DissasembleQueueEntry {
278  int id;
279  IntegerVariable var;
280  };
281  std::vector<DissasembleQueueEntry> disassemble_queue_;
282  std::vector<std::pair<int, IntegerVariable>> disassemble_branch_;
283  std::vector<std::pair<IntegerVariable, IntegerValue>> disassemble_candidates_;
284  std::vector<int> tmp_to_reorder_;
285  SparseBitset<int> disassemble_to_reorder_;
286  std::vector<int> disassemble_reverse_topo_order_;
287 
288  // Staging queue.
289  // Initially, we add the constraint to the priority queue, and we extract
290  // them one by one, each time reaching the propagation fixed point.
291  std::vector<bool> pq_was_added_;
292  bool pq_in_heap_form_ = false;
293  std::vector<int> pq_;
294  std::vector<int> pq_to_clean_;
295 
296  // Stats. Allow to track the time a constraint is scanned more than once.
297  // This is only used in --v 1.
298  SparseBitset<int> id_scanned_at_least_once_;
299  int64_t num_extra_scans_ = 0;
300 
301  // Stats.
302  int64_t num_pushes_ = 0;
303  int64_t num_enforcement_pushes_ = 0;
304  int64_t num_simple_cycles_ = 0;
305  int64_t num_complex_cycles_ = 0;
306  int64_t num_scanned_ = 0;
307  int64_t num_explored_in_disassemble_ = 0;
308  int64_t num_reordered_ = 0;
309  int64_t num_bool_aborts_ = 0;
310  int64_t num_ignored_ = 0;
311 };
312 
313 } // namespace sat
314 } // namespace operations_research
315 
316 #endif // OR_TOOLS_SAT_LINEAR_PROPAGATION_H_
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
void ReorderDense(absl::Span< const int > order)
void SortByPos(absl::Span< int > elements)
void Reorder(absl::Span< const int > order)
EnforcementId Register(absl::Span< const Literal > enforcement, std::function< void(EnforcementStatus)> callback=nullptr)
ABSL_MUST_USE_RESULT bool PropagateWhenFalse(EnforcementId id, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
EnforcementStatus Status(EnforcementId id) const
void AddEnforcementReason(EnforcementId id, std::vector< Literal > *reason) const
void Untrail(const Trail &trail, int trail_index) final
void AddConstraint(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coeffs, IntegerValue upper_bound)
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
MPCallback * callback
DEFINE_STRONG_INDEX_TYPE(ClauseIndex)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition: cp_model.cc:88
Collection of objects used to extend the Constraint Solver library.
IntVar * upper_bound
Definition: routing.cc:1087
int64_t start