OR-Tools  9.6
encoding.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 // Algorithms to encode constraints into their SAT representation. Currently,
15 // this contains one possible encoding of a cardinality constraint as used by
16 // the core-based optimization algorithm in optimization.h.
17 //
18 // This is also known as the incremental totalizer encoding in the literature.
19 
20 #ifndef OR_TOOLS_SAT_ENCODING_H_
21 #define OR_TOOLS_SAT_ENCODING_H_
22 
23 #include <cstdint>
24 #include <deque>
25 #include <functional>
26 #include <string>
27 #include <vector>
28 
30 #include "ortools/base/logging.h"
31 #include "ortools/base/macros.h"
32 #include "ortools/sat/boolean_problem.pb.h"
34 #include "ortools/sat/sat_base.h"
35 #include "ortools/sat/sat_solver.h"
37 
38 namespace operations_research {
39 namespace sat {
40 
41 // This class represents a number in [0, ub]. The encoding uses ub binary
42 // variables x_i with i in [0, ub) where x_i means that the number is > i. It is
43 // called an EncodingNode, because it represents one node of the tree used to
44 // encode a cardinality constraint.
45 //
46 // In practice, not all literals are explicitly created:
47 // - Only the literals in [lb, current_ub) are "active" at a given time.
48 // - The represented number is known to be >= lb.
49 // - It may be greater than current_ub, but the extra literals will be only
50 // created lazily. In all our solves, the literal current_ub - 1, will always
51 // be assumed to false (i.e. the number will be <= current_ub - 1).
52 // - Note that lb may increase and ub decrease as more information is learned
53 // about this node by the sat solver.
54 //
55 // This is roughly based on the cardinality constraint encoding described in:
56 // Bailleux and Yacine Boufkhad, "Efficient CNF Encoding of Boolean Cardinality
57 // Constraints", In Proc. of CP 2003, pages 108-122, 2003.
58 class EncodingNode {
59  public:
61 
62  // Constructs a EncodingNode of size one, just formed by the given literal.
63  explicit EncodingNode(Literal l);
64 
65  // Constructs a node with value in [lb, ub].
66  // New literal "<=x" will be constructed using create_lit(x).
67  EncodingNode(int lb, int ub, std::function<Literal(int x)> create_lit);
68 
69  // Creates a "full" encoding node on n new variables, the represented number
70  // beeing in [lb, ub = lb + n). The variables are added to the given solver
71  // with the basic implications linking them:
72  // literal(0) >= ... >= literal(n-1)
74  SatSolver* solver);
75 
76  // Creates a "lazy" encoding node representing the sum of a and b.
77  // Only one literals will be created by this operation. Note that no clauses
78  // linking it with a or b are added by this function.
81  EncodingNode* b);
82 
83  // Returns a literal with the meaning 'this node number is > i'.
84  // The given i must be in [lb_, current_ub).
85  Literal GreaterThan(int i) const { return literal(i - lb_); }
86 
87  // Accessors to size() and literals in [lb, current_ub).
88  int size() const { return literals_.size(); }
89  Literal literal(int i) const {
90  CHECK_GE(i, 0);
91  CHECK_LT(i, literals_.size());
92  return literals_[i];
93  }
94 
95  // Sort by decreasing depth first and then by increasing variable index.
96  // This is meant to be used by the priority queue in MergeAllNodesWithPQ().
97  bool operator<(const EncodingNode& other) const {
98  return depth_ > other.depth_ ||
99  (depth_ == other.depth_ && other.for_sorting_ > for_sorting_);
100  }
101 
102  // Creates a new literals and increases current_ub.
103  // Returns false if we were already at the upper bound for this node.
104  bool IncreaseCurrentUB(SatSolver* solver);
105 
106  // Removes the left-side literals fixed to 1. Note that this increases lb_ and
107  // reduces the number of active literals. It also removes any right-side
108  // literals fixed to 0. If such a literal exists, ub is updated accordingly.
109  //
110  // Return the overall weight increase.
111  Coefficient Reduce(const SatSolver& solver);
112 
113  // GetAssumption() might need to create new literals.
114  bool AssumptionIs(Literal other) const;
116  bool HasNoWeight() const;
117  void IncreaseWeightLb();
118 
119  // Fix any literal that would cause the weight of this node to go over the
120  // gap.
121  void ApplyWeightUpperBound(Coefficient gap, SatSolver* solver);
122 
124  weight_lb_ = lb_;
125  weight_ = w;
126  }
127  Coefficient weight() const { return weight_; }
128 
129  // The depth is mainly used as an heuristic to decide which nodes to merge
130  // first. See the < operator.
131  void set_depth(int depth) { depth_ = depth; }
132  int depth() const { return depth_; }
133 
134  int lb() const { return lb_; }
135  int current_ub() const { return lb_ + literals_.size(); }
136  int ub() const { return ub_; }
137  EncodingNode* child_a() const { return child_a_; }
138  EncodingNode* child_b() const { return child_b_; }
139 
140  // We use the solver to display the current values of the literals.
141  std::string DebugString(const VariablesAssignment& assignment) const;
142 
143  private:
144  int depth_ = 0;
145  int lb_ = 0;
146  int ub_ = 1;
147  BooleanVariable for_sorting_;
148 
149  // The weight is only applies for literal >= this lb.
150  int weight_lb_ = 0;
151 
152  Coefficient weight_;
153  EncodingNode* child_a_ = nullptr;
154  EncodingNode* child_b_ = nullptr;
155 
156  // If not null, will be used instead of creating new variable directly.
157  std::function<Literal(int x)> create_lit_ = nullptr;
158 
159  // The literals of this node in order.
160  std::vector<Literal> literals_;
161 };
162 
163 // Merges the two given EncodingNodes by creating a new node that corresponds to
164 // the sum of the two given ones. Only the left-most binary variable is created
165 // for the parent node, the other ones will be created later when needed.
166 EncodingNode LazyMerge(EncodingNode* a, EncodingNode* b, SatSolver* solver);
167 
168 // Increases the size of the given node by one. To keep all the needed relations
169 // with its children, we also need to increase their size by one, and so on
170 // recursively. Also adds all the necessary clauses linking the newly added
171 // literals.
172 void IncreaseNodeSize(EncodingNode* node, SatSolver* solver);
173 
174 // Merges the two given EncodingNode by creating a new node that corresponds to
175 // the sum of the two given ones. The given upper_bound is interpreted as a
176 // bound on this sum, and allows creating fewer binary variables.
177 EncodingNode FullMerge(Coefficient upper_bound, EncodingNode* a,
178  EncodingNode* b, SatSolver* solver);
179 
180 // Merges all the given nodes two by two until there is only one left. Returns
181 // the final node which encodes the sum of all the given nodes.
183  const std::vector<EncodingNode*>& nodes,
184  SatSolver* solver,
185  std::deque<EncodingNode>* repository);
186 
187 // Same as MergeAllNodesWithDeque() but use a priority queue to merge in
188 // priority nodes with smaller sizes. This also enforce that the sum of nodes
189 // is greater than its lower bound.
191  Coefficient weight, const std::vector<EncodingNode*>& nodes,
192  SatSolver* solver, std::deque<EncodingNode>* repository);
193 
194 // Returns a vector with one new EncodingNode by variable in the given
195 // objective. Sets the offset to the negated sum of the negative coefficient,
196 // because in this case we negate the literals to have only positive
197 // coefficients.
198 std::vector<EncodingNode*> CreateInitialEncodingNodes(
199  const std::vector<Literal>& literals,
200  const std::vector<Coefficient>& coeffs, Coefficient* offset,
201  std::deque<EncodingNode>* repository);
202 std::vector<EncodingNode*> CreateInitialEncodingNodes(
203  const LinearObjective& objective_proto, Coefficient* offset,
204  std::deque<EncodingNode>* repository);
205 
206 // Reduces the nodes using the now fixed literals, update the lower-bound, and
207 // returns the set of assumptions for the next round of the core-based
208 // algorithm. Returns an empty set of assumptions if everything is fixed.
209 std::vector<Literal> ReduceNodesAndExtractAssumptions(
210  Coefficient upper_bound, Coefficient stratified_lower_bound,
211  Coefficient* lower_bound, std::vector<EncodingNode*>* nodes,
212  SatSolver* solver);
213 
214 // Returns the minimum weight of the nodes in the core. Note that the literal in
215 // the core must appear in the same order as the one in nodes.
216 Coefficient ComputeCoreMinWeight(const std::vector<EncodingNode*>& nodes,
217  const std::vector<Literal>& core);
218 
219 // Returns the maximum node weight under the given upper_bound. Returns zero if
220 // no such weight exist (note that a node weight is strictly positive, so this
221 // make sense).
222 Coefficient MaxNodeWeightSmallerThan(const std::vector<EncodingNode*>& nodes,
224 
225 // Updates the encoding using the given core. The literals in the core must
226 // match the order in nodes. Returns false if the model become infeasible.
227 bool ProcessCore(const std::vector<Literal>& core, Coefficient min_weight,
228  std::deque<EncodingNode>* repository,
229  std::vector<EncodingNode*>* nodes, SatSolver* solver);
230 
231 // There is more than one way to create new assumptions and encode the
232 // information from this core. This is slightly different from ProcessCore() and
233 // follow the algorithm used by many of the top max-SAT solver under the name
234 // incremental OLL. This is described in:
235 // António Morgado, Carmine Dodaro, Joao Marques-Silva. "Core-Guided MaxSAT
236 // with Soft Cardinality Constraints". CP 2014. pp. 564-573.
237 // António Morgado, Alexey Ignatiev, Joao Marques-Silva. "MSCG: Robust
238 // Core-Guided MaxSAT Solving." JSAT 9. 2014. pp. 129-134.
239 //
240 // TODO(user): The last time this was tested, it was however not as good as the
241 // ProcessCore() version. That might change as we code/change more heuristic, so
242 // we keep it around.
243 bool ProcessCoreWithAlternativeEncoding(const std::vector<Literal>& core,
244  Coefficient min_weight,
245  std::deque<EncodingNode>* repository,
246  std::vector<EncodingNode*>* nodes,
247  SatSolver* solver);
248 
249 } // namespace sat
250 } // namespace operations_research
251 
252 #endif // OR_TOOLS_SAT_ENCODING_H_
void InitializeLazyCoreNode(Coefficient weight, EncodingNode *a, EncodingNode *b)
Definition: encoding.cc:85
Literal GetAssumption(SatSolver *solver)
Definition: encoding.cc:155
EncodingNode * child_a() const
Definition: encoding.h:137
bool IncreaseCurrentUB(SatSolver *solver)
Definition: encoding.cc:100
void ApplyWeightUpperBound(Coefficient gap, SatSolver *solver)
Definition: encoding.cc:136
bool AssumptionIs(Literal other) const
Definition: encoding.cc:149
Coefficient Reduce(const SatSolver &solver)
Definition: encoding.cc:116
void InitializeLazyNode(EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:69
void InitializeFullNode(int n, EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:49
Literal literal(int i) const
Definition: encoding.h:89
std::string DebugString(const VariablesAssignment &assignment) const
Definition: encoding.cc:174
EncodingNode * child_b() const
Definition: encoding.h:138
Literal GreaterThan(int i) const
Definition: encoding.h:85
bool operator<(const EncodingNode &other) const
Definition: encoding.h:97
int64_t b
int64_t a
std::tuple< int64_t, int64_t, const double > Coefficient
Coefficient ComputeCoreMinWeight(const std::vector< EncodingNode * > &nodes, const std::vector< Literal > &core)
Definition: encoding.cc:525
EncodingNode * MergeAllNodesWithDeque(Coefficient upper_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
Definition: encoding.cc:359
EncodingNode * LazyMergeAllNodeWithPQAndIncreaseLb(Coefficient weight, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
Definition: encoding.cc:381
std::vector< Literal > ReduceNodesAndExtractAssumptions(Coefficient upper_bound, Coefficient stratified_lower_bound, Coefficient *lower_bound, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Definition: encoding.cc:471
void IncreaseNodeSize(EncodingNode *node, SatSolver *solver)
Definition: encoding.cc:209
EncodingNode LazyMerge(EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:199
EncodingNode FullMerge(Coefficient upper_bound, EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:308
bool ProcessCore(const std::vector< Literal > &core, Coefficient min_weight, std::deque< EncodingNode > *repository, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Definition: encoding.cc:551
Coefficient MaxNodeWeightSmallerThan(const std::vector< EncodingNode * > &nodes, Coefficient upper_bound)
Definition: encoding.cc:539
bool ProcessCoreWithAlternativeEncoding(const std::vector< Literal > &core, Coefficient min_weight, std::deque< EncodingNode > *repository, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Definition: encoding.cc:599
std::vector< EncodingNode * > CreateInitialEncodingNodes(const std::vector< Literal > &literals, const std::vector< Coefficient > &coeffs, Coefficient *offset, std::deque< EncodingNode > *repository)
Definition: encoding.cc:409
Collection of objects used to extend the Constraint Solver library.
int64_t weight
Definition: pack.cc:510
IntVar * upper_bound
Definition: routing.cc:1087
IntVar * lower_bound
Definition: routing.cc:1086
int nodes