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