OR-Tools  9.6
arc_flow_builder.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 
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <set>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/container/flat_hash_map.h"
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/str_join.h"
26 #include "ortools/base/map_util.h"
27 #include "ortools/base/stl_util.h"
29 
30 namespace operations_research {
31 namespace packing {
32 namespace {
33 
34 class ArcFlowBuilder {
35  public:
36  // Same arguments as BuildArcFlowGraph(): see the .h.
37  ArcFlowBuilder(const std::vector<int>& bin_dimensions,
38  const std::vector<std::vector<int>>& item_dimensions_by_type,
39  const std::vector<int>& demand_by_type);
40 
41  // Builds the arc-flow graph.
42  ArcFlowGraph BuildVectorBinPackingGraph();
43 
44  // For debugging purposes.tring(
45  // Returns the number of states explored in the dynamic programming phase.
46  int64_t NumDpStates() const;
47 
48  private:
49  // All items data, regrouped for sorting purposes.
50  struct Item {
51  std::vector<int> dimensions;
52  int demand;
54 
55  // Used to sort items by relative size.
56  double NormalizedSize(const std::vector<int>& bin_dimensions) const;
57  };
58 
59  // State of the dynamic programming algorithm.
60  struct DpState {
63  std::vector<int> used_dimensions;
64  // DP State indices of the states that can be obtained by moving
65  // either "right" to (cur_item_index, cur_item_quantity++) or "up"
66  // to (cur_item_index++, cur_item_quantity=0). -1 if impossible.
68  int up_child;
69  };
70 
71  // Add item iteratively to create all possible nodes in a forward pass.
72  void ForwardCreationPass(DpState* dp_state);
73  // Scan DP-nodes backward to relabels each nodes by increasing them as much
74  // as possible.
75  void BackwardCompressionPass(int state_index);
76  // Relabel nodes by decreasing them as much as possible.
77  void ForwardCompressionPass(const std::vector<int>& source_node);
78 
79  // Can we fit one more item in the bin?
80  bool CanFitNewItem(const std::vector<int>& used_dimensions, int item) const;
81  // Create a new used_dimensions that is used_dimensions + item dimensions.
82  std::vector<int> AddItem(const std::vector<int>& used_dimensions,
83  int item) const;
84 
85  // DpState helpers.
86  int LookupOrCreateDpState(int item, int quantity,
87  const std::vector<int>& used_dimensions);
88 
89  const std::vector<int> bin_dimensions_;
90  std::vector<Item> items_;
91 
92  typedef absl::flat_hash_map<std::vector<int>, int> VectorIntIntMap;
93  int GetOrCreateNode(const std::vector<int>& used_dimensions);
94 
95  // We store all DP states in a dense vector, and remember their index
96  // in the dp_state_index_ map (we use a tri-dimensional indexing because
97  // it's faster for the hash part).
98  std::vector<DpState*> dp_states_;
99  std::vector<std::vector<VectorIntIntMap>> dp_state_index_;
100 
101  // The ArcFlowGraph will have nodes which will correspond to "some"
102  // of the vector<int> representing the partial bin usages encountered during
103  // the algo. These two data structures map one to the other (note that nodes
104  // are dense integers).
105  absl::flat_hash_map<std::vector<int>, int> node_indices_;
106  std::vector<std::vector<int>> nodes_;
107 
108  std::set<ArcFlowGraph::Arc> arcs_;
109 };
110 
111 double ArcFlowBuilder::Item::NormalizedSize(
112  const std::vector<int>& bin_dimensions) const {
113  double size = 0.0;
114  for (int i = 0; i < bin_dimensions.size(); ++i) {
115  size += static_cast<double>(dimensions[i]) / bin_dimensions[i];
116  }
117  return size;
118 }
119 
120 int64_t ArcFlowBuilder::NumDpStates() const {
121  int64_t res = 1; // We do not store the initial state.
122  for (const auto& it1 : dp_state_index_) {
123  for (const auto& it2 : it1) {
124  res += it2.size();
125  }
126  }
127  return res;
128 }
129 
130 ArcFlowBuilder::ArcFlowBuilder(
131  const std::vector<int>& bin_dimensions,
132  const std::vector<std::vector<int>>& item_dimensions_by_type,
133  const std::vector<int>& demand_by_type)
134  : bin_dimensions_(bin_dimensions) {
135  // Checks dimensions.
136  for (int i = 0; i < bin_dimensions.size(); ++i) {
137  CHECK_GT(bin_dimensions[i], 0);
138  }
139 
140  const int num_items = item_dimensions_by_type.size();
141  items_.resize(num_items);
142  for (int i = 0; i < num_items; ++i) {
143  items_[i].dimensions = item_dimensions_by_type[i];
144  items_[i].demand = demand_by_type[i];
145  items_[i].original_index = i;
146  }
147  std::sort(items_.begin(), items_.end(), [&](const Item& a, const Item& b) {
148  return a.NormalizedSize(bin_dimensions_) >
149  b.NormalizedSize(bin_dimensions_);
150  });
151 }
152 
153 bool ArcFlowBuilder::CanFitNewItem(const std::vector<int>& used_dimensions,
154  int item) const {
155  for (int d = 0; d < bin_dimensions_.size(); ++d) {
156  if (used_dimensions[d] + items_[item].dimensions[d] > bin_dimensions_[d]) {
157  return false;
158  }
159  }
160  return true;
161 }
162 
163 std::vector<int> ArcFlowBuilder::AddItem(
164  const std::vector<int>& used_dimensions, int item) const {
165  DCHECK(CanFitNewItem(used_dimensions, item));
166  std::vector<int> result = used_dimensions;
167  for (int d = 0; d < bin_dimensions_.size(); ++d) {
168  result[d] += items_[item].dimensions[d];
169  }
170  return result;
171 }
172 
173 int ArcFlowBuilder::GetOrCreateNode(const std::vector<int>& used_dimensions) {
174  const auto& it = node_indices_.find(used_dimensions);
175  if (it != node_indices_.end()) {
176  return it->second;
177  }
178  const int index = node_indices_.size();
179  node_indices_[used_dimensions] = index;
180  nodes_.push_back(used_dimensions);
181  return index;
182 }
183 
184 ArcFlowGraph ArcFlowBuilder::BuildVectorBinPackingGraph() {
185  // Initialize the DP states map.
186  dp_state_index_.resize(items_.size());
187  for (int i = 0; i < items_.size(); ++i) {
188  dp_state_index_[i].resize(items_[i].demand + 1);
189  }
190 
191  // Explore all possible DP states (starting from the initial 'empty' state),
192  // and remember their ancestry.
193  std::vector<int> zero(bin_dimensions_.size(), 0);
194  dp_states_.push_back(new DpState({0, 0, zero, -1, -1}));
195  for (int i = 0; i < dp_states_.size(); ++i) {
196  ForwardCreationPass(dp_states_[i]);
197  }
198 
199  // We can clear the dp_state_index map as it will not be used anymore.
200  // From now on, we will use the dp_states.used_dimensions to store the new
201  // labels in the backward pass.
202  const int64_t num_dp_states = NumDpStates();
203  dp_state_index_.clear();
204 
205  // Backwards pass: "push" the bin dimensions as far as possible.
206  const int num_states = dp_states_.size();
207  std::vector<std::pair<int, int>> flat_deps;
208  for (int i = 0; i < dp_states_.size(); ++i) {
209  if (dp_states_[i]->up_child != -1) {
210  flat_deps.push_back(std::make_pair(dp_states_[i]->up_child, i));
211  }
212  if (dp_states_[i]->right_child != -1) {
213  flat_deps.push_back(std::make_pair(dp_states_[i]->right_child, i));
214  }
215  }
216  const std::vector<int> sorted_work =
218  for (const int w : sorted_work) {
219  BackwardCompressionPass(w);
220  }
221 
222  // ForwardCreationPass again, push the bin dimensions as low as possible.
223  const std::vector<int> source_node = dp_states_[0]->used_dimensions;
224  // We can now delete the states stored in dp_states_.
225  gtl::STLDeleteElements(&dp_states_);
226  ForwardCompressionPass(source_node);
227 
228  // We need to connect all nodes that corresponds to at least one item selected
229  // to the sink node.
230  const int sink_node_index = nodes_.size() - 1;
231  for (int node = 1; node < sink_node_index; ++node) {
232  arcs_.insert({node, sink_node_index, -1});
233  }
234 
235  ArcFlowGraph result;
236  result.arcs.assign(arcs_.begin(), arcs_.end());
237  result.nodes.assign(nodes_.begin(), nodes_.end());
238  result.num_dp_states = num_dp_states;
239  return result;
240 }
241 
242 int ArcFlowBuilder::LookupOrCreateDpState(
243  int item, int quantity, const std::vector<int>& used_dimensions) {
244  VectorIntIntMap& map = dp_state_index_[item][quantity];
245  const int index =
246  map.insert({used_dimensions, dp_states_.size()}).first->second;
247  if (index == dp_states_.size()) {
248  dp_states_.push_back(
249  new DpState({item, quantity, used_dimensions, -1, -1}));
250  }
251  return index;
252 }
253 
254 void ArcFlowBuilder::ForwardCreationPass(DpState* dp_state) {
255  const int item = dp_state->cur_item_index;
256  const int quantity = dp_state->cur_item_quantity;
257  const std::vector<int>& used_dimensions = dp_state->used_dimensions;
258 
259  // Explore path up.
260  if (item < items_.size() - 1) {
261  dp_state->up_child = LookupOrCreateDpState(item + 1, 0, used_dimensions);
262  } else {
263  dp_state->up_child = -1;
264  }
265 
266  // Explore path right.
267  if (quantity < items_[item].demand && CanFitNewItem(used_dimensions, item)) {
268  const std::vector<int> added = AddItem(used_dimensions, item);
269  dp_state->right_child = LookupOrCreateDpState(item, quantity + 1, added);
270  } else {
271  dp_state->right_child = -1;
272  }
273 }
274 
275 void ArcFlowBuilder::BackwardCompressionPass(int state_index) {
276  // The goal of this function is to fill this.
277  std::vector<int>& result = dp_states_[state_index]->used_dimensions;
278 
279  // Inherit our result from the result one step up.
280  const int up_index = dp_states_[state_index]->up_child;
281  const std::vector<int>& result_up =
282  up_index == -1 ? bin_dimensions_ : dp_states_[up_index]->used_dimensions;
283  result = result_up;
284 
285  // Adjust our result from the result one step right.
286  const int right_index = dp_states_[state_index]->right_child;
287  if (right_index == -1) return; // We're done.
288  const std::vector<int>& result_right =
289  dp_states_[right_index]->used_dimensions;
290  const Item& item = items_[dp_states_[state_index]->cur_item_index];
291  for (int d = 0; d < bin_dimensions_.size(); ++d) {
292  result[d] = std::min(result[d], result_right[d] - item.dimensions[d]);
293  }
294 
295  // Insert the arc from the node to the "right" node.
296  const int node = GetOrCreateNode(result);
297  const int right_node = GetOrCreateNode(result_right);
298  DCHECK_NE(node, right_node);
299  arcs_.insert({node, right_node, item.original_index});
300  // Also insert the 'dotted' arc from the node to the "up" node (if different).
301  if (result != result_up) {
302  const int up_node = GetOrCreateNode(result_up);
303  arcs_.insert({node, up_node, -1});
304  }
305 }
306 
307 // Reverse version of the backward pass.
308 // Revisit states forward, and relabel nodes with the longest path in each
309 // dimensions from the source. The only meaningfull difference is that we use
310 // arcs and nodes, instead of dp_states.
311 void ArcFlowBuilder::ForwardCompressionPass(
312  const std::vector<int>& source_node) {
313  const int num_nodes = node_indices_.size();
314  const int num_dims = bin_dimensions_.size();
315  std::set<ArcFlowGraph::Arc> new_arcs;
316  std::vector<std::vector<int>> new_nodes;
317  VectorIntIntMap new_node_indices;
318  std::vector<int> node_remap(num_nodes, -1);
319  // We need to revert the sorting of items as arcs store the original index.
320  std::vector<int> reverse_item_index_map(items_.size(), -1);
321  for (int i = 0; i < items_.size(); ++i) {
322  reverse_item_index_map[items_[i].original_index] = i;
323  }
324 
325  std::vector<std::pair<int, int>> forward_deps;
326  std::vector<std::vector<ArcFlowGraph::Arc>> incoming_arcs(num_nodes);
327  for (const ArcFlowGraph::Arc& arc : arcs_) {
328  forward_deps.push_back(std::make_pair(arc.source, arc.destination));
329  incoming_arcs[arc.destination].push_back(arc);
330  }
331 
332  const std::vector<int> sorted_work =
333  util::graph::DenseIntStableTopologicalSortOrDie(num_nodes, forward_deps);
334 
335  const int old_source_node = GetOrCreateNode(source_node);
336  const int old_sink_node = GetOrCreateNode(bin_dimensions_);
337  CHECK_EQ(sorted_work.front(), old_source_node);
338  CHECK_EQ(sorted_work.back(), old_sink_node);
339 
340  // Process nodes in order and remap state to max(previous_state + item
341  // dimensions).
342  for (const int w : sorted_work) {
343  std::vector<int> new_used(num_dims, 0);
344  if (w == sorted_work.back()) { // Do not compress the sink node.
345  new_used = bin_dimensions_;
346  } else {
347  for (const ArcFlowGraph::Arc& arc : incoming_arcs[w]) {
348  const int item =
349  arc.item_index == -1 ? -1 : reverse_item_index_map[arc.item_index];
350  const int prev_node = node_remap[arc.source];
351  const std::vector<int>& prev = new_nodes[prev_node];
352  DCHECK_NE(prev_node, -1);
353  for (int d = 0; d < num_dims; ++d) {
354  if (item != -1) {
355  new_used[d] =
356  std::max(new_used[d], prev[d] + items_[item].dimensions[d]);
357  } else {
358  new_used[d] = std::max(new_used[d], prev[d]);
359  }
360  }
361  }
362  }
363  const auto& it = new_node_indices.find(new_used);
364  if (it != new_node_indices.end()) {
365  node_remap[w] = it->second;
366  } else {
367  const int new_index = new_nodes.size();
368  new_nodes.push_back(new_used);
369  new_node_indices[new_used] = new_index;
370  node_remap[w] = new_index;
371  }
372  }
373  // Remap arcs.
374  for (const ArcFlowGraph::Arc& arc : arcs_) {
375  CHECK_NE(node_remap[arc.source], -1);
376  CHECK_NE(node_remap[arc.destination], -1);
377  // Remove loss arcs between merged nodes.
378  if (arc.item_index == -1 &&
379  node_remap[arc.source] == node_remap[arc.destination])
380  continue;
381  new_arcs.insert(
382  {node_remap[arc.source], node_remap[arc.destination], arc.item_index});
383  }
384  VLOG(1) << "Reduced nodes from " << num_nodes << " to " << new_nodes.size();
385  VLOG(1) << "Reduced arcs from " << arcs_.size() << " to " << new_arcs.size();
386  nodes_ = new_nodes;
387  arcs_ = new_arcs;
388  CHECK_NE(node_remap[old_source_node], -1);
389  CHECK_EQ(0, node_remap[old_source_node]);
390  CHECK_NE(node_remap[old_sink_node], -1);
391  CHECK_EQ(nodes_.size() - 1, node_remap[old_sink_node]);
392 }
393 
394 } // namespace
395 
396 bool ArcFlowGraph::Arc::operator<(const ArcFlowGraph::Arc& other) const {
397  if (source != other.source) return source < other.source;
398  if (destination != other.destination) return destination < other.destination;
399  return item_index < other.item_index;
400 }
401 
403  const std::vector<int>& bin_dimensions,
404  const std::vector<std::vector<int>>& item_dimensions_by_type,
405  const std::vector<int>& demand_by_type) {
406  ArcFlowBuilder afb(bin_dimensions, item_dimensions_by_type, demand_by_type);
407  return afb.BuildVectorBinPackingGraph();
408 }
409 
410 } // namespace packing
411 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
int demand
int up_child
int right_child
int original_index
int cur_item_index
std::vector< int > dimensions
int cur_item_quantity
std::vector< int > used_dimensions
int64_t b
int64_t a
int arc
int index
void STLDeleteElements(T *container)
Definition: stl_util.h:372
ArcFlowGraph BuildArcFlowGraph(const std::vector< int > &bin_dimensions, const std::vector< std::vector< int >> &item_dimensions_by_type, const std::vector< int > &demand_by_type)
Collection of objects used to extend the Constraint Solver library.
std::vector< int > DenseIntStableTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int >> &arcs)
if(!yyg->yy_init)
Definition: parser.yy.cc:965
#define VLOG(verboselevel)
Definition: vlog.h:39