Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
tuple_set.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// Set of integer tuples (fixed-size arrays, all of the same size) with
15// a basic API.
16// It supports several types of integer arrays transparently, with an
17// inherent storage based on int64_t arrays.
18//
19// The key feature is the "lazy" copy:
20// - Copying an IntTupleSet won't actually copy the data right away; we
21// will just have several IntTupleSet pointing at the same data.
22// - Modifying an IntTupleSet which shares his data with others
23// will create a new, modified instance of the data payload, and make
24// the IntTupleSet point to that new data.
25// - Modifying an IntTupleSet that doesn't share its data with any other
26// IntTupleSet will modify the data directly.
27// Therefore, you don't need to use const IntTupleSet& in methods. Just do:
28// void MyMethod(IntTupleSet tuple_set) { ... }
29//
30// This class is thread hostile as the copy and reference counter are
31// not protected by a mutex.
32
33#ifndef OR_TOOLS_UTIL_TUPLE_SET_H_
34#define OR_TOOLS_UTIL_TUPLE_SET_H_
35
36#include <algorithm>
37#include <cstdint>
38#include <vector>
39
40#include "absl/container/flat_hash_map.h"
41#include "absl/container/flat_hash_set.h"
42#include "ortools/base/hash.h"
44
45namespace operations_research {
46// ----- Main IntTupleSet class -----
48 public:
49 // Creates an empty tuple set with a fixed length for all tuples.
50 explicit IntTupleSet(int arity);
51 // Copy constructor (it actually does a lazy copy, see toplevel comment).
52 IntTupleSet(const IntTupleSet& set); // NOLINT
54
55 // Clears data.
56 void Clear();
57
58 // Inserts the tuple to the set. It does nothing if the tuple is
59 // already in the set. The size of the tuple must be equal to the
60 // arity of the set. It returns the index at which the tuple was
61 // inserted (-1 if it was already present).
62 int Insert(const std::vector<int>& tuple);
63 int Insert(const std::vector<int64_t>& tuple);
64 // Arity fixed version of Insert removing the need for a vector for the user.
65 int Insert2(int64_t v0, int64_t v1);
66 int Insert3(int64_t v0, int64_t v1, int64_t v2);
67 int Insert4(int64_t v0, int64_t v1, int64_t v2, int64_t v3);
68 // Inserts the tuples.
69 void InsertAll(const std::vector<std::vector<int64_t> >& tuples);
70 void InsertAll(const std::vector<std::vector<int> >& tuples);
71
72 // Checks if the tuple is in the set.
73 bool Contains(const std::vector<int>& tuple) const;
74 bool Contains(const std::vector<int64_t>& tuple) const;
75
76 // Returns the number of tuples.
77 int NumTuples() const;
78 // Get the given tuple's value at the given position. The indices
79 // of the tuples correspond to the order in which they were
80 // inserted.
81 int64_t Value(int tuple_index, int pos_in_tuple) const;
82 // Returns the arity of the set.
83 int Arity() const;
84 // Access the raw data, see IntTupleSet::Data::flat_tuples_.
85 const int64_t* RawData() const;
86 // Returns the number of different values in the given column.
87 int NumDifferentValuesInColumn(int col) const;
88 // Return a copy of the set, sorted by the "col"-th value of each
89 // tuples. The sort is stable.
91 // Returns a copy of the tuple set lexicographically sorted.
93
94 private:
95 // Class that holds the actual data of an IntTupleSet. It handles
96 // the reference counters, etc.
97 class Data {
98 public:
99 explicit Data(int arity);
100 Data(const Data& data);
101 ~Data();
102 void AddSharedOwner();
103 bool RemovedSharedOwner();
104 Data* CopyIfShared();
105 template <class T>
106 int Insert(const std::vector<T>& tuple);
107 template <class T>
108 bool Contains(const std::vector<T>& candidate) const;
109 template <class T>
110 int64_t Fingerprint(const std::vector<T>& tuple) const;
111 int NumTuples() const;
112 int64_t Value(int index, int pos) const;
113 int Arity() const;
114 const int64_t* RawData() const;
115 void Clear();
116
117 private:
118 const int arity_;
119 int num_owners_;
120 // Concatenation of all tuples ever added.
121 std::vector<int64_t> flat_tuples_;
122 // Maps a tuple's fingerprint to the list of tuples with this
123 // fingerprint, represented by their start index in the
124 // flat_tuples_ vector.
125 absl::flat_hash_map<int64_t, std::vector<int> > tuple_fprint_to_index_;
126 };
127
128 // Used to represent a light representation of a tuple.
129 struct IndexData {
130 int index;
131 IntTupleSet::Data* data;
132 IndexData(int i, IntTupleSet::Data* const d) : index(i), data(d) {}
133 static bool Compare(const IndexData& a, const IndexData& b);
134 };
135
136 struct IndexValue {
137 int index;
138 int64_t value;
139 IndexValue(int i, int64_t v) : index(i), value(v) {}
140 static bool Compare(const IndexValue& a, const IndexValue& b);
141 };
142
143 mutable Data* data_;
144};
145
146// ----- Data -----
147inline IntTupleSet::Data::Data(int arity) : arity_(arity), num_owners_(0) {}
148
149inline IntTupleSet::Data::Data(const Data& data)
150 : arity_(data.arity_),
151 num_owners_(0),
152 flat_tuples_(data.flat_tuples_),
153 tuple_fprint_to_index_(data.tuple_fprint_to_index_) {}
154
155inline IntTupleSet::Data::~Data() {}
156
157inline void IntTupleSet::Data::AddSharedOwner() { num_owners_++; }
158
159inline bool IntTupleSet::Data::RemovedSharedOwner() {
160 return (--num_owners_ == 0);
161}
162
163inline IntTupleSet::Data* IntTupleSet::Data::CopyIfShared() {
164 if (num_owners_ > 1) { // Copy on write.
165 Data* const new_data = new Data(*this);
166 RemovedSharedOwner();
167 new_data->AddSharedOwner();
168 return new_data;
169 }
170 return this;
171}
172
173template <class T>
174int IntTupleSet::Data::Insert(const std::vector<T>& tuple) {
175 DCHECK(arity_ == 0 || flat_tuples_.size() % arity_ == 0);
176 CHECK_EQ(arity_, tuple.size());
177 DCHECK_EQ(1, num_owners_);
178 if (!Contains(tuple)) {
179 const int index = NumTuples();
180 const int offset = flat_tuples_.size();
181 flat_tuples_.resize(offset + arity_);
182 // On mac os X, using this instead of push_back gives a 10x speedup!
183 for (int i = 0; i < arity_; ++i) {
184 flat_tuples_[offset + i] = tuple[i];
185 }
186 const int64_t fingerprint = Fingerprint(tuple);
187 tuple_fprint_to_index_[fingerprint].push_back(index);
188 return index;
189 } else {
190 return -1;
191 }
192}
193
194template <class T>
195bool IntTupleSet::Data::Contains(const std::vector<T>& candidate) const {
196 if (candidate.size() != arity_) {
197 return false;
198 }
199 const int64_t fingerprint = Fingerprint(candidate);
200 if (tuple_fprint_to_index_.contains(fingerprint)) {
201 const std::vector<int>& indices = tuple_fprint_to_index_.at(fingerprint);
202 for (int i = 0; i < indices.size(); ++i) {
203 const int tuple_index = indices[i];
204 for (int j = 0; j < arity_; ++j) {
205 if (candidate[j] != flat_tuples_[tuple_index * arity_ + j]) {
206 return false;
207 }
208 }
209 return true;
210 }
211 }
212 return false;
213}
214
215template <class T>
216int64_t IntTupleSet::Data::Fingerprint(const std::vector<T>& tuple) const {
217 switch (arity_) {
218 case 0:
219 return 0;
220 case 1:
221 return tuple[0];
222 case 2: {
223 uint64_t x = tuple[0];
224 uint64_t y = uint64_t{0xe08c1d668b756f82};
225 uint64_t z = tuple[1];
226 mix(x, y, z);
227 return z;
228 }
229 default: {
230 uint64_t x = tuple[0];
231 uint64_t y = uint64_t{0xe08c1d668b756f82};
232 for (int i = 1; i < tuple.size(); ++i) {
233 uint64_t z = tuple[i];
234 mix(x, y, z);
235 x = z;
236 }
237 return x;
238 }
239 }
240}
241
242inline int IntTupleSet::Data::NumTuples() const {
243 return tuple_fprint_to_index_.size();
244}
245
246inline int64_t IntTupleSet::Data::Value(int index, int pos) const {
247 DCHECK_GE(index, 0);
248 DCHECK_LT(index, flat_tuples_.size() / arity_);
249 DCHECK_GE(pos, 0);
250 DCHECK_LT(pos, arity_);
251 return flat_tuples_[index * arity_ + pos];
252}
253
254inline int IntTupleSet::Data::Arity() const { return arity_; }
255
256inline const int64_t* IntTupleSet::Data::RawData() const {
257 return flat_tuples_.data();
258}
259
260inline void IntTupleSet::Data::Clear() {
261 flat_tuples_.clear();
262 tuple_fprint_to_index_.clear();
263}
264
265inline IntTupleSet::IntTupleSet(int arity) : data_(new Data(arity)) {
266 CHECK_GE(arity, 0);
267 data_->AddSharedOwner();
268}
269
270inline IntTupleSet::IntTupleSet(const IntTupleSet& set) : data_(set.data_) {
271 data_->AddSharedOwner();
272}
273
275 CHECK(data_ != nullptr);
276 if (data_->RemovedSharedOwner()) {
277 delete data_;
278 }
279}
280
281inline void IntTupleSet::Clear() {
282 data_ = data_->CopyIfShared();
283 data_->Clear();
284}
285
286inline int IntTupleSet::Insert(const std::vector<int>& tuple) {
287 data_ = data_->CopyIfShared();
288 return data_->Insert(tuple);
289}
290
291inline int IntTupleSet::Insert(const std::vector<int64_t>& tuple) {
292 data_ = data_->CopyIfShared();
293 return data_->Insert(tuple);
294}
295
296inline int IntTupleSet::Insert2(int64_t v0, int64_t v1) {
297 std::vector<int64_t> tuple(2);
298 tuple[0] = v0;
299 tuple[1] = v1;
300 return Insert(tuple);
301}
302
303inline int IntTupleSet::Insert3(int64_t v0, int64_t v1, int64_t v2) {
304 std::vector<int64_t> tuple(3);
305 tuple[0] = v0;
306 tuple[1] = v1;
307 tuple[2] = v2;
308 return Insert(tuple);
309}
310
311inline int IntTupleSet::Insert4(int64_t v0, int64_t v1, int64_t v2,
312 int64_t v3) {
313 std::vector<int64_t> tuple(4);
314 tuple[0] = v0;
315 tuple[1] = v1;
316 tuple[2] = v2;
317 tuple[3] = v3;
318 return Insert(tuple);
319}
320
321inline bool IntTupleSet::Contains(const std::vector<int>& tuple) const {
322 return data_->Contains(tuple);
323}
324
325inline bool IntTupleSet::Contains(const std::vector<int64_t>& tuple) const {
326 return data_->Contains(tuple);
327}
328
330 const std::vector<std::vector<int> >& tuples) {
331 data_ = data_->CopyIfShared();
332 for (int i = 0; i < tuples.size(); ++i) {
333 Insert(tuples[i]);
334 }
335}
336
338 const std::vector<std::vector<int64_t> >& tuples) {
339 data_ = data_->CopyIfShared();
340 for (int i = 0; i < tuples.size(); ++i) {
341 Insert(tuples[i]);
342 }
343}
344
345inline int IntTupleSet::NumTuples() const { return data_->NumTuples(); }
346
347inline int64_t IntTupleSet::Value(int index, int pos) const {
348 return data_->Value(index, pos);
349}
350
351inline int IntTupleSet::Arity() const { return data_->Arity(); }
352
353inline const int64_t* IntTupleSet::RawData() const { return data_->RawData(); }
354
356 if (col < 0 || col >= data_->Arity()) {
357 return 0;
358 }
359 absl::flat_hash_set<int64_t> values;
360 for (int i = 0; i < data_->NumTuples(); ++i) {
361 values.insert(data_->Value(i, col));
362 }
363 return values.size();
364}
365
366inline bool IntTupleSet::IndexValue::Compare(const IndexValue& a,
367 const IndexValue& b) {
368 return a.value < b.value || (a.value == b.value && a.index < b.index);
369}
370
372 std::vector<IndexValue> keys;
373 keys.reserve(data_->NumTuples());
374 for (int index = 0; index < data_->NumTuples(); ++index) {
375 keys.push_back(IndexValue(index, data_->Value(index, col)));
376 }
377 std::sort(keys.begin(), keys.end(), IntTupleSet::IndexValue::Compare);
378 const int arity = data_->Arity();
379 IntTupleSet sorted(arity);
380 for (int i = 0; i < keys.size(); ++i) {
381 const int64_t* tuple_ptr = data_->RawData() + keys[i].index * arity;
382 sorted.Insert(std::vector<int64_t>(tuple_ptr, tuple_ptr + arity));
383 }
384 return sorted;
385}
386
387inline bool IntTupleSet::IndexData::Compare(const IndexData& a,
388 const IndexData& b) {
389 const IntTupleSet::Data* const data = a.data;
390 const int arity = data->Arity();
391 for (int i = 0; i < arity; ++i) {
392 const int64_t value1 = data->Value(a.index, i);
393 const int64_t value2 = data->Value(b.index, i);
394 if (value1 < value2) {
395 return true;
396 }
397 if (value1 > value2) {
398 return false;
399 }
400 }
401 return false;
402}
403
405 std::vector<IndexData> keys;
406 keys.reserve(data_->NumTuples());
407 for (int index = 0; index < data_->NumTuples(); ++index) {
408 keys.push_back(IndexData(index, data_));
409 }
410 std::sort(keys.begin(), keys.end(), IntTupleSet::IndexData::Compare);
411 const int arity = data_->Arity();
412 IntTupleSet sorted(arity);
413 for (int i = 0; i < keys.size(); ++i) {
414 std::vector<int64_t> tuple(arity);
415 const int64_t* tuple_ptr = data_->RawData() + keys[i].index * arity;
416 sorted.Insert(std::vector<int64_t>(tuple_ptr, tuple_ptr + arity));
417 }
418 return sorted;
419}
420} // namespace operations_research
421
422#endif // OR_TOOLS_UTIL_TUPLE_SET_H_
IntegerValue y
--— Main IntTupleSet class --—
Definition tuple_set.h:47
int Insert3(int64_t v0, int64_t v1, int64_t v2)
Definition tuple_set.h:303
int Insert4(int64_t v0, int64_t v1, int64_t v2, int64_t v3)
Definition tuple_set.h:311
IntTupleSet(int arity)
Creates an empty tuple set with a fixed length for all tuples.
Definition tuple_set.h:265
int64_t Value(int tuple_index, int pos_in_tuple) const
Definition tuple_set.h:347
int NumTuples() const
Returns the number of tuples.
Definition tuple_set.h:345
bool Contains(const std::vector< int > &tuple) const
Checks if the tuple is in the set.
Definition tuple_set.h:321
int Insert(const std::vector< int > &tuple)
Definition tuple_set.h:286
int Insert2(int64_t v0, int64_t v1)
Arity fixed version of Insert removing the need for a vector for the user.
Definition tuple_set.h:296
int NumDifferentValuesInColumn(int col) const
Returns the number of different values in the given column.
Definition tuple_set.h:355
IntTupleSet SortedLexicographically() const
Returns a copy of the tuple set lexicographically sorted.
Definition tuple_set.h:404
int Arity() const
Returns the arity of the set.
Definition tuple_set.h:351
void InsertAll(const std::vector< std::vector< int64_t > > &tuples)
Inserts the tuples.
Definition tuple_set.h:337
IntTupleSet SortedByColumn(int col) const
Definition tuple_set.h:371
const int64_t * RawData() const
Access the raw data, see IntTupleSet::Data::flat_tuples_.
Definition tuple_set.h:353
const int arity_
Definition table.cc:224
int64_t b
Definition table.cc:45
int64_t a
Definition table.cc:44
int64_t value
int index
ColIndex col
Definition markowitz.cc:187
In SWIG mode, we don't want anything besides these top-level includes.
static void mix(uint64_t &a, uint64_t &b, uint64_t &c)
64 bit version.
Definition hash.h:29
const Variable x
Definition qp_tests.cc:127