OR-Tools  9.6
dynamic_partition.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 <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/strings/str_format.h"
23 #include "absl/strings/str_join.h"
24 #include "ortools/base/murmur.h"
25 
26 namespace operations_research {
27 
28 namespace {
29 uint64_t FprintOfInt32(int i) {
30  return util_hash::MurmurHash64(reinterpret_cast<const char*>(&i),
31  sizeof(int));
32 }
33 } // namespace
34 
36  DCHECK_GE(num_elements, 0);
37  element_.assign(num_elements, -1);
38  index_of_.assign(num_elements, -1);
39  for (int i = 0; i < num_elements; ++i) {
40  element_[i] = i;
41  index_of_[i] = i;
42  }
43  part_of_.assign(num_elements, 0);
44  uint64_t fprint = 0;
45  for (int i = 0; i < num_elements; ++i) fprint ^= FprintOfInt32(i);
46  part_.push_back(Part(/*start_index=*/0, /*end_index=*/num_elements,
47  /*parent_part=*/0,
48  /*fprint=*/fprint));
49 }
50 
52  const std::vector<int>& initial_part_of_element) {
53  if (initial_part_of_element.empty()) return;
54  part_of_ = initial_part_of_element;
55  const int n = part_of_.size();
56  const int num_parts = 1 + *std::max_element(part_of_.begin(), part_of_.end());
57  DCHECK_EQ(0, *std::min_element(part_of_.begin(), part_of_.end()));
58  part_.resize(num_parts);
59 
60  // Compute the part fingerprints.
61  for (int i = 0; i < n; ++i) part_[part_of_[i]].fprint ^= FprintOfInt32(i);
62 
63  // Compute the actual start indices of each part, knowing that we'll sort
64  // them as they were given implicitly in "initial_part_of_element".
65  // The code looks a bit weird to do it in-place, with no additional memory.
66  for (int p = 0; p < num_parts; ++p) {
67  part_[p].end_index = 0; // Temporarily utilized as size_of_part.
68  part_[p].parent_part = p;
69  }
70  for (const int p : part_of_) ++part_[p].end_index; // size_of_part
71  int sum_part_sizes = 0;
72  for (int p = 0; p < num_parts; ++p) {
73  part_[p].start_index = sum_part_sizes;
74  sum_part_sizes += part_[p].end_index; // size of part.
75  }
76 
77  // Now that we have the correct start indices, we set the end indices to the
78  // start indices, and incrementally add all elements to their part, adjusting
79  // the end indices as we go.
80  for (Part& part : part_) part.end_index = part.start_index;
81  element_.assign(n, -1);
82  index_of_.assign(n, -1);
83  for (int element = 0; element < n; ++element) {
84  Part* const part = &part_[part_of_[element]];
85  element_[part->end_index] = element;
86  index_of_[element] = part->end_index;
87  ++part->end_index;
88  }
89 
90  // Verify that we did it right.
91  // TODO(user): either remove this or factor it out if it can be used
92  // elsewhere.
93  DCHECK_EQ(0, part_[0].start_index);
94  DCHECK_EQ(NumElements(), part_[NumParts() - 1].end_index);
95  for (int p = 1; p < NumParts(); ++p) {
96  DCHECK_EQ(part_[p - 1].end_index, part_[p].start_index);
97  }
98 }
99 
100 void DynamicPartition::Refine(const std::vector<int>& distinguished_subset) {
101  // tmp_counter_of_part_[i] will contain the number of
102  // elements in distinguished_subset that were part of part #i.
103  tmp_counter_of_part_.resize(NumParts(), 0);
104  // We remember the Parts that were actually affected.
105  tmp_affected_parts_.clear();
106  for (const int element : distinguished_subset) {
107  DCHECK_GE(element, 0);
108  DCHECK_LT(element, NumElements());
109  const int part = part_of_[element];
110  const int num_distinguished_elements_in_part = ++tmp_counter_of_part_[part];
111  // Is this the first time that we touch this element's part?
112  if (num_distinguished_elements_in_part == 1) {
113  // TODO(user): optimize the common singleton case.
114  tmp_affected_parts_.push_back(part);
115  }
116  // Move the element to the end of its current Part.
117  const int old_index = index_of_[element];
118  const int new_index =
119  part_[part].end_index - num_distinguished_elements_in_part;
120  DCHECK_GE(new_index, old_index)
121  << "Duplicate element given to Refine(): " << element;
122  // Perform the swap, keeping index_of_ up to date.
123  index_of_[element] = new_index;
124  index_of_[element_[new_index]] = old_index;
125  std::swap(element_[old_index], element_[new_index]);
126  }
127 
128  // Sort affected parts. This is important to behave as advertised in the .h.
129  // TODO(user): automatically switch to an O(N) sort when it's faster
130  // than this one, which is O(K log K) with K = tmp_affected_parts_.size().
131  std::sort(tmp_affected_parts_.begin(), tmp_affected_parts_.end());
132 
133  // Iterate on each affected part and split it, or keep it intact if all
134  // of its elements were distinguished.
135  for (const int part : tmp_affected_parts_) {
136  const int start_index = part_[part].start_index;
137  const int end_index = part_[part].end_index;
138  const int split_index = end_index - tmp_counter_of_part_[part];
139  tmp_counter_of_part_[part] = 0; // Clean up after us.
140  DCHECK_GE(split_index, start_index);
141  DCHECK_LT(split_index, end_index);
142 
143  // Do nothing if all elements were distinguished.
144  if (split_index == start_index) continue;
145 
146  // Compute the fingerprint of the new part.
147  uint64_t new_fprint = 0;
148  for (int i = split_index; i < end_index; ++i) {
149  new_fprint ^= FprintOfInt32(element_[i]);
150  }
151 
152  const int new_part = NumParts();
153 
154  // Perform the split.
155  part_[part].end_index = split_index;
156  part_[part].fprint ^= new_fprint;
157  part_.push_back(Part(/*start_index*/ split_index, /*end_index*/ end_index,
158  /*parent_part*/ part, new_fprint));
159  for (const int element : ElementsInPart(new_part)) {
160  part_of_[element] = new_part;
161  }
162  }
163 }
164 
166  DCHECK_GE(NumParts(), original_num_parts);
167  DCHECK_GE(original_num_parts, 1);
168  while (NumParts() > original_num_parts) {
169  const int part_index = NumParts() - 1;
170  const Part& part = part_[part_index];
171  const int parent_part_index = part.parent_part;
172  DCHECK_LT(parent_part_index, part_index) << "UndoRefineUntilNumPartsEqual()"
173  " called with "
174  "'original_num_parts' too low";
175 
176  // Update the part contents: actually merge "part" onto its parent.
177  for (const int element : ElementsInPart(part_index)) {
178  part_of_[element] = parent_part_index;
179  }
180  Part* const parent_part = &part_[parent_part_index];
181  DCHECK_EQ(part.start_index, parent_part->end_index);
182  parent_part->end_index = part.end_index;
183  parent_part->fprint ^= part.fprint;
184  part_.pop_back();
185  }
186 }
187 
189  if (sorting != SORT_LEXICOGRAPHICALLY && sorting != SORT_BY_PART) {
190  return absl::StrFormat("Unsupported sorting: %d", sorting);
191  }
192  std::vector<std::vector<int>> parts;
193  for (int i = 0; i < NumParts(); ++i) {
194  IterablePart iterable_part = ElementsInPart(i);
195  parts.emplace_back(iterable_part.begin(), iterable_part.end());
196  std::sort(parts.back().begin(), parts.back().end());
197  }
198  if (sorting == SORT_LEXICOGRAPHICALLY) {
199  std::sort(parts.begin(), parts.end());
200  }
201  std::string out;
202  for (const std::vector<int>& part : parts) {
203  if (!out.empty()) out += " | ";
204  out += absl::StrJoin(part, " ");
205  }
206  return out;
207 }
208 
209 void MergingPartition::Reset(int num_nodes) {
210  DCHECK_GE(num_nodes, 0);
211  part_size_.assign(num_nodes, 1);
212  parent_.assign(num_nodes, -1);
213  for (int i = 0; i < num_nodes; ++i) parent_[i] = i;
214  tmp_part_bit_.assign(num_nodes, false);
215 }
216 
217 int MergingPartition::MergePartsOf(int node1, int node2) {
218  DCHECK_GE(node1, 0);
219  DCHECK_GE(node2, 0);
220  DCHECK_LT(node1, NumNodes());
221  DCHECK_LT(node2, NumNodes());
222  int root1 = GetRoot(node1);
223  int root2 = GetRoot(node2);
224  if (root1 == root2) return -1;
225  int s1 = part_size_[root1];
226  int s2 = part_size_[root2];
227  // Attach the smaller part to the larger one. Break ties by root index.
228  if (s1 < s2 || (s1 == s2 && root1 > root2)) {
229  std::swap(root1, root2);
230  std::swap(s1, s2);
231  }
232 
233  // Update the part size. Don't change part_size_[root2]: it won't be used
234  // again by further merges.
235  part_size_[root1] += part_size_[root2];
236  SetParentAlongPathToRoot(node1, root1);
237  SetParentAlongPathToRoot(node2, root1);
238  return root2;
239 }
240 
242  DCHECK_GE(node, 0);
243  DCHECK_LT(node, NumNodes());
244  const int root = GetRoot(node);
245  SetParentAlongPathToRoot(node, root);
246  return root;
247 }
248 
250  int num_nodes_kept = 0;
251  for (const int node : *nodes) {
252  const int representative = GetRootAndCompressPath(node);
253  if (!tmp_part_bit_[representative]) {
254  tmp_part_bit_[representative] = true;
255  (*nodes)[num_nodes_kept++] = node;
256  }
257  }
258  nodes->resize(num_nodes_kept);
259 
260  // Clean up the tmp_part_bit_ vector. Since we've already compressed the
261  // paths (if backtracking was enabled), no need to do it again.
262  for (const int node : *nodes) tmp_part_bit_[GetRoot(node)] = false;
263 }
264 
266  std::vector<int>* node_equivalence_classes) {
267  node_equivalence_classes->assign(NumNodes(), -1);
268  int num_roots = 0;
269  for (int node = 0; node < NumNodes(); ++node) {
270  const int root = GetRootAndCompressPath(node);
271  if ((*node_equivalence_classes)[root] < 0) {
272  (*node_equivalence_classes)[root] = num_roots;
273  ++num_roots;
274  }
275  (*node_equivalence_classes)[node] = (*node_equivalence_classes)[root];
276  }
277  return num_roots;
278 }
279 
281  std::vector<std::vector<int>> sorted_parts(NumNodes());
282  for (int i = 0; i < NumNodes(); ++i) {
283  sorted_parts[GetRootAndCompressPath(i)].push_back(i);
284  }
285  for (std::vector<int>& part : sorted_parts)
286  std::sort(part.begin(), part.end());
287  std::sort(sorted_parts.begin(), sorted_parts.end());
288  // Note: typically, a lot of elements of "sorted_parts" will be empty,
289  // but these won't be visible in the string that we construct below.
290  std::string out;
291  for (const std::vector<int>& part : sorted_parts) {
292  if (!out.empty()) out += " | ";
293  out += absl::StrJoin(part, " ");
294  }
295  return out;
296 }
297 
299  absl::Span<const int> distinguished_subset) {
300  // Compute the size of the non-empty intersection of each part with the
301  // distinguished_subset.
302  temp_to_clean_.clear();
303  std::vector<int>& local_sizes = temp_data_by_part_;
304  local_sizes.resize(size_of_part_.size(), 0);
305  for (const int element : distinguished_subset) {
306  const int part = part_of_[element];
307  if (local_sizes[part] == 0) temp_to_clean_.push_back(part);
308  local_sizes[part]++;
309  }
310 
311  // Reuse local_sizes to store new_part index or zero (no remapping).
312  // Also update the size of each part.
313  for (const int part : temp_to_clean_) {
314  if (local_sizes[part] == size_of_part_[part]) {
315  // No need to remap if the whole part is in distinguished_subset.
316  local_sizes[part] = 0;
317  continue;
318  }
319 
320  const int new_part_index = size_of_part_.size();
321  size_of_part_[part] -= local_sizes[part];
322  size_of_part_.push_back(local_sizes[part]);
323  local_sizes[part] = new_part_index;
324  }
325 
326  // For each part not completely included or excluded, split out the element
327  // from distinguished_subset into a new part.
328  for (const int element : distinguished_subset) {
329  const int new_part = local_sizes[part_of_[element]];
330  if (new_part != 0) part_of_[element] = new_part;
331  }
332 
333  // Sparse clean.
334  for (const int part : temp_to_clean_) {
335  local_sizes[part] = 0;
336  }
337 }
338 
339 std::vector<absl::Span<const int>> SimpleDynamicPartition::GetParts(
340  std::vector<int>* buffer) {
341  const int num_elements = part_of_.size();
342  const int num_parts = size_of_part_.size();
343  buffer->resize(num_elements);
344 
345  std::vector<absl::Span<const int>> result(num_parts);
346  if (result.empty()) return result;
347 
348  // Compute start of each part in buffer.
349  std::vector<int>& starts = temp_data_by_part_;
350  starts.resize(num_parts, 0);
351  for (int i = 1; i < num_parts; ++i) {
352  starts[i] = starts[i - 1] + size_of_part_[i - 1];
353  }
354 
355  // Fill result.
356  for (int i = 0; i < num_parts; ++i) {
357  result[i] = absl::MakeSpan(&(*buffer)[starts[i]], size_of_part_[i]);
358  }
359 
360  // Copy elements in order and at their place.
361  for (int element = 0; element < num_elements; ++element) {
362  (*buffer)[starts[part_of_[element]]++] = element;
363  }
364  starts.clear();
365  return result;
366 }
367 
368 } // namespace operations_research
IterablePart ElementsInPart(int i) const
void Refine(const std::vector< int > &distinguished_subset)
void UndoRefineUntilNumPartsEqual(int original_num_parts)
std::string DebugString(DebugStringSorting sorting) const
int MergePartsOf(int node1, int node2)
int FillEquivalenceClasses(std::vector< int > *node_equivalence_classes)
void KeepOnlyOneNodePerPart(std::vector< int > *nodes)
void Refine(absl::Span< const int > distinguished_subset)
std::vector< absl::Span< const int > > GetParts(std::vector< int > *buffer)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:269
Collection of objects used to extend the Constraint Solver library.
uint64_t MurmurHash64(const char *buf, const size_t len)
Definition: murmur.h:22
ColIndex representative
int nodes
std::vector< int >::const_iterator end() const
std::vector< int >::const_iterator begin() const