Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
dynamic_partition.cc
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
15
16#include <algorithm>
17#include <cstdint>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "absl/log/check.h"
23#include "absl/strings/str_join.h"
24#include "absl/types/span.h"
25#include "ortools/base/murmur.h"
26
27namespace operations_research {
28
29namespace {
30uint64_t FprintOfInt32(int i) {
31 return util_hash::MurmurHash64(reinterpret_cast<const char*>(&i),
32 sizeof(int));
33}
34} // namespace
35
37 DCHECK_GE(num_elements, 0);
38 element_.assign(num_elements, -1);
39 index_of_.assign(num_elements, -1);
40 for (int i = 0; i < num_elements; ++i) {
41 element_[i] = i;
42 index_of_[i] = i;
43 }
44 part_of_.assign(num_elements, 0);
45 uint64_t fprint = 0;
46 for (int i = 0; i < num_elements; ++i) fprint ^= FprintOfInt32(i);
47 part_.push_back(Part(/*start_index=*/0, /*end_index=*/num_elements,
48 /*parent_part=*/0,
49 /*fprint=*/fprint));
50}
51
53 const std::vector<int>& initial_part_of_element) {
54 if (initial_part_of_element.empty()) return;
55 part_of_ = initial_part_of_element;
56 const int n = part_of_.size();
57 const int num_parts = 1 + *std::max_element(part_of_.begin(), part_of_.end());
58 DCHECK_EQ(0, *std::min_element(part_of_.begin(), part_of_.end()));
59 part_.resize(num_parts);
60
61 // Compute the part fingerprints.
62 for (int i = 0; i < n; ++i) part_[part_of_[i]].fprint ^= FprintOfInt32(i);
63
64 // Compute the actual start indices of each part, knowing that we'll sort
65 // them as they were given implicitly in "initial_part_of_element".
66 // The code looks a bit weird to do it in-place, with no additional memory.
67 for (int p = 0; p < num_parts; ++p) {
68 part_[p].end_index = 0; // Temporarily utilized as size_of_part.
69 part_[p].parent_part = p;
70 }
71 for (const int p : part_of_) ++part_[p].end_index; // size_of_part
72 int sum_part_sizes = 0;
73 for (int p = 0; p < num_parts; ++p) {
74 part_[p].start_index = sum_part_sizes;
75 sum_part_sizes += part_[p].end_index; // size of part.
76 }
77
78 // Now that we have the correct start indices, we set the end indices to the
79 // start indices, and incrementally add all elements to their part, adjusting
80 // the end indices as we go.
81 for (Part& part : part_) part.end_index = part.start_index;
82 element_.assign(n, -1);
83 index_of_.assign(n, -1);
84 for (int element = 0; element < n; ++element) {
85 Part* const part = &part_[part_of_[element]];
86 element_[part->end_index] = element;
87 index_of_[element] = part->end_index;
88 ++part->end_index;
89 }
90
91 // Verify that we did it right.
92 // TODO(user): either remove this or factor it out if it can be used
93 // elsewhere.
94 DCHECK_EQ(0, part_[0].start_index);
95 DCHECK_EQ(NumElements(), part_[NumParts() - 1].end_index);
96 for (int p = 1; p < NumParts(); ++p) {
97 DCHECK_EQ(part_[p - 1].end_index, part_[p].start_index);
98 }
99}
100
101void DynamicPartition::Refine(absl::Span<const int> distinguished_subset) {
102 // tmp_counter_of_part_[i] will contain the number of
103 // elements in distinguished_subset that were part of part #i.
104 tmp_counter_of_part_.resize(NumParts(), 0);
105 // We remember the Parts that were actually affected.
106 tmp_affected_parts_.clear();
107 for (const int element : distinguished_subset) {
108 DCHECK_GE(element, 0);
109 DCHECK_LT(element, NumElements());
110 const int part = part_of_[element];
111 const int num_distinguished_elements_in_part = ++tmp_counter_of_part_[part];
112 // Is this the first time that we touch this element's part?
113 if (num_distinguished_elements_in_part == 1) {
114 // TODO(user): optimize the common singleton case.
115 tmp_affected_parts_.push_back(part);
116 }
117 // Move the element to the end of its current Part.
118 const int old_index = index_of_[element];
119 const int new_index =
120 part_[part].end_index - num_distinguished_elements_in_part;
121 DCHECK_GE(new_index, old_index)
122 << "Duplicate element given to Refine(): " << element;
123 // Perform the swap, keeping index_of_ up to date.
124 index_of_[element] = new_index;
125 index_of_[element_[new_index]] = old_index;
126 std::swap(element_[old_index], element_[new_index]);
127 }
128
129 // Sort affected parts. This is important to behave as advertised in the .h.
130 // TODO(user): automatically switch to an O(N) sort when it's faster
131 // than this one, which is O(K log K) with K = tmp_affected_parts_.size().
132 std::sort(tmp_affected_parts_.begin(), tmp_affected_parts_.end());
133
134 // Iterate on each affected part and split it, or keep it intact if all
135 // of its elements were distinguished.
136 for (const int part : tmp_affected_parts_) {
137 const int start_index = part_[part].start_index;
138 const int end_index = part_[part].end_index;
139 const int split_index = end_index - tmp_counter_of_part_[part];
140 tmp_counter_of_part_[part] = 0; // Clean up after us.
141 DCHECK_GE(split_index, start_index);
142 DCHECK_LT(split_index, end_index);
143
144 // Do nothing if all elements were distinguished.
145 if (split_index == start_index) continue;
146
147 // Compute the fingerprint of the new part.
148 uint64_t new_fprint = 0;
149 for (int i = split_index; i < end_index; ++i) {
150 new_fprint ^= FprintOfInt32(element_[i]);
151 }
152
153 const int new_part = NumParts();
154
155 // Perform the split.
156 part_[part].end_index = split_index;
157 part_[part].fprint ^= new_fprint;
158 part_.push_back(Part(/*start_index*/ split_index, /*end_index*/ end_index,
159 /*parent_part*/ part, new_fprint));
160 for (const int element : ElementsInPart(new_part)) {
161 part_of_[element] = new_part;
162 }
163 }
164}
165
167 DCHECK_GE(NumParts(), original_num_parts);
168 DCHECK_GE(original_num_parts, 1);
169 while (NumParts() > original_num_parts) {
170 const int part_index = NumParts() - 1;
171 const Part& part = part_[part_index];
172 const int parent_part_index = part.parent_part;
173 DCHECK_LT(parent_part_index, part_index) << "UndoRefineUntilNumPartsEqual()"
174 " called with "
175 "'original_num_parts' too low";
176
177 // Update the part contents: actually merge "part" onto its parent.
178 for (const int element : ElementsInPart(part_index)) {
179 part_of_[element] = parent_part_index;
180 }
181 Part* const parent_part = &part_[parent_part_index];
182 DCHECK_EQ(part.start_index, parent_part->end_index);
183 parent_part->end_index = part.end_index;
184 parent_part->fprint ^= part.fprint;
185 part_.pop_back();
186 }
187}
188
190 bool sort_parts_lexicographically) const {
191 std::vector<std::vector<int>> parts;
192 for (int i = 0; i < NumParts(); ++i) {
193 IterablePart iterable_part = ElementsInPart(i);
194 parts.emplace_back(iterable_part.begin(), iterable_part.end());
195 std::sort(parts.back().begin(), parts.back().end());
196 }
197 if (sort_parts_lexicographically) {
198 std::sort(parts.begin(), parts.end());
199 }
200 std::string out;
201 for (const std::vector<int>& part : parts) {
202 if (!out.empty()) out += " | ";
203 out += absl::StrJoin(part, " ");
204 }
205 return out;
206}
207
208void MergingPartition::Reset(int num_nodes) {
209 DCHECK_GE(num_nodes, 0);
210 part_size_.assign(num_nodes, 1);
211 parent_.assign(num_nodes, -1);
212 for (int i = 0; i < num_nodes; ++i) parent_[i] = i;
213 tmp_part_bit_.assign(num_nodes, false);
214}
215
216int MergingPartition::MergePartsOf(int node1, int node2) {
217 DCHECK_GE(node1, 0);
218 DCHECK_GE(node2, 0);
219 DCHECK_LT(node1, NumNodes());
220 DCHECK_LT(node2, NumNodes());
221 int root1 = GetRoot(node1);
222 int root2 = GetRoot(node2);
223 if (root1 == root2) return -1;
224 int s1 = part_size_[root1];
225 int s2 = part_size_[root2];
226 // Attach the smaller part to the larger one. Break ties by root index.
227 if (s1 < s2 || (s1 == s2 && root1 > root2)) {
228 std::swap(root1, root2);
229 std::swap(s1, s2);
230 }
231
232 // Update the part size. Don't change part_size_[root2]: it won't be used
233 // again by further merges.
234 part_size_[root1] += part_size_[root2];
235 SetParentAlongPathToRoot(node1, root1);
236 SetParentAlongPathToRoot(node2, root1);
237 return root2;
238}
239
241 DCHECK_GE(node, 0);
242 DCHECK_LT(node, NumNodes());
243 const int root = GetRoot(node);
244 SetParentAlongPathToRoot(node, root);
245 return root;
246}
247
249 int num_nodes_kept = 0;
250 for (const int node : *nodes) {
251 const int representative = GetRootAndCompressPath(node);
252 if (!tmp_part_bit_[representative]) {
253 tmp_part_bit_[representative] = true;
254 (*nodes)[num_nodes_kept++] = node;
255 }
256 }
257 nodes->resize(num_nodes_kept);
258
259 // Clean up the tmp_part_bit_ vector. Since we've already compressed the
260 // paths (if backtracking was enabled), no need to do it again.
261 for (const int node : *nodes) tmp_part_bit_[GetRoot(node)] = false;
262}
263
265 std::vector<int>* node_equivalence_classes) {
266 node_equivalence_classes->assign(NumNodes(), -1);
267 int num_roots = 0;
268 for (int node = 0; node < NumNodes(); ++node) {
269 const int root = GetRootAndCompressPath(node);
270 if ((*node_equivalence_classes)[root] < 0) {
271 (*node_equivalence_classes)[root] = num_roots;
272 ++num_roots;
273 }
274 (*node_equivalence_classes)[node] = (*node_equivalence_classes)[root];
275 }
276 return num_roots;
277}
278
280 std::vector<std::vector<int>> sorted_parts(NumNodes());
281 for (int i = 0; i < NumNodes(); ++i) {
282 sorted_parts[GetRootAndCompressPath(i)].push_back(i);
283 }
284 for (std::vector<int>& part : sorted_parts) {
285 std::sort(part.begin(), part.end());
286 }
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} // namespace operations_research
std::string DebugString(bool sort_parts_lexicographically) const
void UndoRefineUntilNumPartsEqual(int original_num_parts)
void Refine(absl::Span< const int > distinguished_subset)
IterablePart ElementsInPart(int i) const
*** Implementation of inline methods of the above classes. ***
void KeepOnlyOneNodePerPart(std::vector< int > *nodes)
int FillEquivalenceClasses(std::vector< int > *node_equivalence_classes)
void Refine(absl::Span< const int > distinguished_subset)
In SWIG mode, we don't want anything besides these top-level includes.
uint64_t MurmurHash64(const char *buf, const size_t len)
Definition murmur.h:22
ColIndex representative
int nodes
std::vector< int >::const_iterator begin() const
std::vector< int >::const_iterator end() const