Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
circuit.h
Go to the documentation of this file.
1// Copyright 2010-2024 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_CIRCUIT_H_
15#define OR_TOOLS_SAT_CIRCUIT_H_
16
17#include <functional>
18#include <memory>
19#include <utility>
20#include <vector>
21
22#include "absl/container/btree_set.h"
23#include "absl/container/flat_hash_map.h"
25#include "ortools/base/types.h"
27#include "ortools/sat/integer.h"
28#include "ortools/sat/model.h"
30#include "ortools/util/rev.h"
32
33namespace operations_research {
34namespace sat {
35
36// Circuit/sub-circuit constraint.
37//
38// Nodes that are not in the unique allowed sub-circuit must point to themseves.
39// A nodes that has no self-arc must thus be inside the sub-circuit. If there is
40// no self-arc at all, then this constraint forces the circuit to go through all
41// the nodes. Multi-arcs are NOT supported.
42//
43// Important: for correctness, this constraint requires that "exactly one"
44// constraints have been added for all the incoming (resp. outgoing) arcs of
45// each node. Also, such constraint must propagate before this one.
47 public:
48 struct Options {
49 // Hack for the VRP to allow for more than one sub-circuit and forces all
50 // the subcircuits to go through the node zero.
52 };
53
54 // The constraints take a sparse representation of a graph on [0, n). Each arc
55 // being present when the given literal is true.
56 CircuitPropagator(int num_nodes, const std::vector<int>& tails,
57 const std::vector<int>& heads,
58 const std::vector<Literal>& literals, Options options,
59 Model* model);
60
61 // This type is neither copyable nor movable.
64
65 void SetLevel(int level) final;
66 bool Propagate() final;
67 bool IncrementalPropagate(const std::vector<int>& watch_indices) final;
69
70 private:
71 // Updates the structures when the given arc is added to the paths.
72 void AddArc(int tail, int head, LiteralIndex literal_index);
73
74 // Clears and fills reason with the literals of the arcs that form a path from
75 // the given node. The path can be a cycle, but in this case it must end at
76 // start (not like a rho shape).
77 void FillReasonForPath(int start_node, std::vector<Literal>* reason) const;
78
79 const int num_nodes_;
80 const Options options_;
81 Trail* trail_;
82 const VariablesAssignment& assignment_;
83
84 // We use this to query in O(1) for an arc existence. The self-arcs are
85 // accessed often, so we use a more efficient std::vector<> for them. Note
86 // that we do not add self-arcs to graph_.
87 //
88 // TODO(user): for large dense graph, using a matrix is faster and uses less
89 // memory. If the need arise we can have the two implementations.
90 std::vector<LiteralIndex> self_arcs_;
91 absl::flat_hash_map<std::pair<int, int>, Literal> graph_;
92
93 // Data used to interpret the watch indices passed to IncrementalPropagate().
94 struct Arc {
95 int tail;
96 int head;
97 };
98 std::vector<Literal> watch_index_to_literal_;
99 CompactVectorVector<int, Arc> watch_index_to_arcs_;
100
101 // Current partial chains of arc that are present.
102 std::vector<int> next_; // -1 if not assigned yet.
103 std::vector<int> prev_; // -1 if not assigned yet.
104 std::vector<LiteralIndex> next_literal_;
105
106 // Backtrack support for the partial chains of arcs, level_ends_[level] is an
107 // index in added_arcs_;
108 std::vector<int> level_ends_;
109 std::vector<Arc> added_arcs_;
110
111 // Reversible list of node that must be in a cycle. A node must be in a cycle
112 // iff self_arcs_[node] is false. This graph entry can be used as a reason.
113 int rev_must_be_in_cycle_size_ = 0;
114 std::vector<int> must_be_in_cycle_;
115
116 // Temporary vectors.
117 std::vector<bool> processed_;
118 std::vector<bool> in_current_path_;
119};
120
121// Enforce the fact that there is no cycle in the given directed graph.
123 public:
124 NoCyclePropagator(int num_nodes, const std::vector<int>& tails,
125 const std::vector<int>& heads,
126 const std::vector<Literal>& literals, Model* model);
127
128 void SetLevel(int level) final;
129 bool Propagate() final;
130 bool IncrementalPropagate(const std::vector<int>& watch_indices) final;
131
132 private:
133 void RegisterWith(GenericLiteralWatcher* watcher);
134
135 const int num_nodes_;
136 Trail* trail_;
137 const VariablesAssignment& assignment_;
138
139 // The set of all watched literals and to what arc they correspond.
140 std::vector<Literal> watch_index_to_literal_;
141 std::vector<std::vector<std::pair<int, int>>> watch_index_to_arcs_;
142
143 // This will only contains the subgraph with all the arc at true.
144 // We maintain it incrementally and update this on SetLevel()/Propagate().
145 std::vector<std::vector<int>> graph_;
146 std::vector<std::vector<Literal>> graph_literals_;
147
148 // For now we redo a strongly connected component on the graph formed of arcs
149 // at one.
150 //
151 // TODO(user): code a true algo.
152 std::vector<std::vector<int>> components_;
154 std::vector<std::vector<int>>>
155 ssc_;
156
157 // SAT incremental state.
158 std::vector<int> level_ends_;
159 std::vector<int> touched_nodes_;
160};
161
162// This constraint ensures that the graph is a covering of all nodes by
163// circuits and loops, such that all circuits contain exactly one distinguished
164// node. Those distinguished nodes are meant to be depots.
165//
166// This constraint does not need ExactlyOnePerRowAndPerColumn() to be correct,
167// but it does not propagate degree deductions (only fails if a node has more
168// than one outgoing arc or more than one incoming arc), so that adding
169// ExactlyOnePerRowAndPerColumn() should work better.
170//
171// TODO(user): Make distinguished nodes an array of Boolean variables,
172// so this can be used for facility location problems.
174 public:
175 CircuitCoveringPropagator(std::vector<std::vector<Literal>> graph,
176 const std::vector<int>& distinguished_nodes,
177 Model* model);
178
179 void SetLevel(int level) final;
180 bool Propagate() final;
181 bool IncrementalPropagate(const std::vector<int>& watch_indices) final;
182 void RegisterWith(GenericLiteralWatcher* watcher);
183
184 private:
185 // Adds all literals on the path/circuit from tail to head in the graph of
186 // literals set to true.
187 // next_[i] should be filled with a node j s.t. graph_[i][j] is true, or -1.
188 void FillFixedPathInReason(int start, int end, std::vector<Literal>* reason);
189
190 // Input data.
191 const std::vector<std::vector<Literal>> graph_;
192 const int num_nodes_;
193 std::vector<bool> node_is_distinguished_;
194
195 // SAT incremental state.
196 Trail* trail_;
197 std::vector<std::pair<int, int>> watch_index_to_arc_;
198 std::vector<std::pair<int, int>> fixed_arcs_;
199 std::vector<int> level_ends_;
200
201 // Used in Propagate() to represent paths and circuits.
202 std::vector<int> next_;
203 std::vector<int> prev_;
204 std::vector<bool> visited_;
205};
206
207// Changes the node indices so that we get a graph in [0, num_nodes) where every
208// node has at least one incoming or outgoing arc. Returns the number of nodes.
209template <class IntContainer>
210int ReindexArcs(IntContainer* tails, IntContainer* heads,
211 absl::flat_hash_map<int, int>* mapping_output = nullptr) {
212 const int num_arcs = tails->size();
213 if (num_arcs == 0) return 0;
214
215 // Put all nodes in a set.
216 absl::btree_set<int> nodes;
217 for (int arc = 0; arc < num_arcs; ++arc) {
218 nodes.insert((*tails)[arc]);
219 nodes.insert((*heads)[arc]);
220 }
221
222 // Compute the new indices while keeping a stable order.
223 int new_index = 0;
224 absl::flat_hash_map<int, int> mapping;
225 for (const int node : nodes) {
226 mapping[node] = new_index++;
227 }
228
229 // Remap the arcs.
230 for (int arc = 0; arc < num_arcs; ++arc) {
231 (*tails)[arc] = mapping[(*tails)[arc]];
232 (*heads)[arc] = mapping[(*heads)[arc]];
233 }
234
235 if (mapping_output != nullptr) {
236 *mapping_output = std::move(mapping);
237 }
238
239 return nodes.size();
240}
241
242// ============================================================================
243// Model based functions.
244// ============================================================================
245
246// This just wraps CircuitPropagator. See the comment there to see what this
247// does. Note that any nodes with no outgoing or no incoming arc will cause the
248// problem to be UNSAT. One can call ReindexArcs() first to ignore such nodes.
249void LoadSubcircuitConstraint(int num_nodes, const std::vector<int>& tails,
250 const std::vector<int>& heads,
251 const std::vector<Literal>& literals,
252 Model* model,
253 bool multiple_subcircuit_through_zero = false);
254
255// TODO(user): Change to a sparse API like for the function above.
256std::function<void(Model*)> ExactlyOnePerRowAndPerColumn(
257 const std::vector<std::vector<Literal>>& graph);
258std::function<void(Model*)> CircuitCovering(
259 const std::vector<std::vector<Literal>>& graph,
260 const std::vector<int>& distinguished_nodes);
261
262} // namespace sat
263} // namespace operations_research
264
265#endif // OR_TOOLS_SAT_CIRCUIT_H_
CircuitCoveringPropagator(std::vector< std::vector< Literal > > graph, const std::vector< int > &distinguished_nodes, Model *model)
Definition circuit.cc:484
void RegisterWith(GenericLiteralWatcher *watcher)
Definition circuit.cc:496
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition circuit.cc:529
void RegisterWith(GenericLiteralWatcher *watcher)
Definition circuit.cc:110
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition circuit.cc:170
CircuitPropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Options options, Model *model)
Definition circuit.cc:35
CircuitPropagator & operator=(const CircuitPropagator &)=delete
CircuitPropagator(const CircuitPropagator &)=delete
This type is neither copyable nor movable.
Enforce the fact that there is no cycle in the given directed graph.
Definition circuit.h:122
NoCyclePropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
Definition circuit.cc:359
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition circuit.cc:433
Base class for CP like propagators.
Definition integer.h:1414
GraphType graph
GRBmodel * model
int arc
std::function< void(Model *)> CircuitCovering(const std::vector< std::vector< Literal > > &graph, const std::vector< int > &distinguished_nodes)
Definition circuit.cc:697
void LoadSubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model, bool multiple_subcircuit_through_zero)
Definition circuit.cc:647
int ReindexArcs(IntContainer *tails, IntContainer *heads, absl::flat_hash_map< int, int > *mapping_output=nullptr)
Definition circuit.h:210
std::function< void(Model *)> ExactlyOnePerRowAndPerColumn(const std::vector< std::vector< Literal > > &graph)
Definition circuit.cc:628
In SWIG mode, we don't want anything besides these top-level includes.
STL namespace.
int head
int tail
int nodes
std::optional< int64_t > end
int64_t start