OR-Tools  9.1
id_map.h
Go to the documentation of this file.
1// Copyright 2010-2021 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// A faster version of flat_hash_map for Variable and LinearConstraint keys.
15#ifndef OR_TOOLS_MATH_OPT_CPP_ID_MAP_H_
16#define OR_TOOLS_MATH_OPT_CPP_ID_MAP_H_
17
18#include <algorithm>
19#include <initializer_list>
20#include <iterator>
21#include <utility>
22#include <vector>
23
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/types/span.h"
30#include "ortools/math_opt/cpp/arrow_operator_proxy.h" // IWYU pragma: export
32
33namespace operations_research {
34namespace math_opt {
35
36// Similar to a absl::flat_hash_map<K, V> for K as Variable or LinearConstraint.
37//
38// Important differences:
39// * The storage is more efficient, as we store the underlying ids directly.
40// * The consequence of that is that the keys are usually returned by value in
41// situations where the flat_hash_map would return references.
42// * You cannot mix variables/constraints from multiple models in these maps.
43// Doing so results in a CHECK failure.
44//
45// Implementation notes:
46// * Emptying the map (with clear() or erase()) resets the underlying model to
47// nullptr, enabling reusing the same instance with a different model.
48// * Operator= and swap() support operating with different models by
49// respectively replacing or swapping it.
50// * For details requirements on K, see key_types.h.
51//
52// See also IdSet for the equivalent class for sets.
53template <typename K, typename V>
54class IdMap {
55 public:
56 using IdType = typename K::IdType;
57 using StorageType = absl::flat_hash_map<IdType, V>;
58 using key_type = K;
59 using mapped_type = V;
60 using value_type = std::pair<const K, V>;
61 using size_type = typename StorageType::size_type;
62 using difference_type = typename StorageType::difference_type;
63 using reference = std::pair<const K, V&>;
64 using const_reference = std::pair<const K, const V&>;
65 using pointer = void;
66 using const_pointer = void;
67
68 class iterator {
69 public:
74 using iterator_category = std::forward_iterator_tag;
75
76 iterator() = default;
77
78 inline reference operator*() const;
80 inline iterator& operator++();
81 inline iterator operator++(int);
82
83 friend bool operator==(const iterator& lhs, const iterator& rhs) {
84 return lhs.storage_iterator_ == rhs.storage_iterator_;
85 }
86 friend bool operator!=(const iterator& lhs, const iterator& rhs) {
87 return lhs.storage_iterator_ != rhs.storage_iterator_;
88 }
89
90 private:
91 friend class IdMap;
92
93 inline iterator(const IdMap* map,
94 typename StorageType::iterator storage_iterator);
95
96 const IdMap* map_ = nullptr;
97 typename StorageType::iterator storage_iterator_;
98 };
99
101 public:
106 using iterator_category = std::forward_iterator_tag;
107
108 const_iterator() = default;
109 inline const_iterator(const iterator& non_const_iterator); // NOLINT
110
111 inline reference operator*() const;
113 inline const_iterator& operator++();
114 inline const_iterator operator++(int);
115
116 friend bool operator==(const const_iterator& lhs,
117 const const_iterator& rhs) {
118 return lhs.storage_iterator_ == rhs.storage_iterator_;
119 }
120 friend bool operator!=(const const_iterator& lhs,
121 const const_iterator& rhs) {
122 return lhs.storage_iterator_ != rhs.storage_iterator_;
123 }
124
125 private:
126 friend class IdMap;
127
128 inline const_iterator(
129 const IdMap* map,
130 typename StorageType::const_iterator storage_iterator);
131
132 const IdMap* map_ = nullptr;
133 typename StorageType::const_iterator storage_iterator_;
134 };
135
136 IdMap() = default;
137 template <typename InputIt>
138 inline IdMap(InputIt first, InputIt last);
139 inline IdMap(std::initializer_list<value_type> ilist);
140
141 // Typically for internal use only.
142 inline IdMap(IndexedModel* model, StorageType values);
143
144 inline const_iterator cbegin() const;
145 inline const_iterator begin() const;
146 inline iterator begin();
147
148 inline const_iterator cend() const;
149 inline const_iterator end() const;
150 inline iterator end();
151
152 bool empty() const { return map_.empty(); }
153 size_type size() const { return map_.size(); }
154 inline void clear();
155 void reserve(size_type count) { map_.reserve(count); }
156
157 inline std::pair<iterator, bool> insert(std::pair<K, V> k_v);
158 template <typename InputIt>
159 inline void insert(InputIt first, InputIt last);
160 inline void insert(std::initializer_list<value_type> ilist);
161
162 inline std::pair<iterator, bool> emplace(const K& k, V v);
163 template <typename... Args>
164 inline std::pair<iterator, bool> try_emplace(const K& k, Args&&... args);
165
166 // Returns the number of elements erased (zero or one).
167 inline int erase(const K& k);
168 // In STL erase(const_iterator) and erase(iterator) both return an
169 // iterator. But flat_hash_map instead has void return types. So here we also
170 // use void.
171 //
172 // In flat_hash_map, both erase(const_iterator) and erase(iterator) are
173 // defined since there is also the erase<K>(const K&) that exists and that
174 // would be used. Since we don't have this overload, we can rely on the
175 // automatic cast of the iterator in const_iterator.
176 inline void erase(const_iterator pos);
177 inline iterator erase(const_iterator first, const_iterator last);
178
179 inline void swap(IdMap& other);
180
181 inline const V& at(const K& k) const;
182 inline V& at(const K& k);
183 inline V& operator[](const K& k);
184 inline size_type count(const K& k) const;
185 inline bool contains(const K& k) const;
186 inline iterator find(const K& k);
187 inline const_iterator find(const K& k) const;
188 inline std::pair<iterator, iterator> equal_range(const K& k);
189 inline std::pair<const_iterator, const_iterator> equal_range(
190 const K& k) const;
191
192 // Updates the values in this map by adding the value of the corresponding
193 // keys in the other map. For keys only in the other map, insert their value.
194 //
195 // This function is only available when type V supports operator+=.
196 //
197 // This is equivalent to (but is more efficient than):
198 // for (const auto pair : other) {
199 // (*this)[pair.first] += pair.second;
200 // }
201 //
202 // This function CHECK that all the keys in the two maps have the same model.
203 inline void Add(const IdMap& other);
204
205 // Updates the values in this map by subtracting the value of the
206 // corresponding keys in the other map. For keys only in the other map, insert
207 // the opposite of their value.
208 //
209 // This function is only available when type V supports operator-=.
210 //
211 // This is equivalent to (but is more efficient than):
212 // for (const auto pair : other) {
213 // (*this)[pair.first] -= pair.second;
214 // }
215 //
216 // This function CHECK that all the keys in the two maps have the same model.
217 inline void Subtract(const IdMap& other);
218
219 inline std::vector<V> Values(absl::Span<const K> keys) const;
220 inline absl::flat_hash_map<K, V> Values(
221 const absl::flat_hash_set<K>& keys) const;
222
223 inline std::vector<K> SortedKeys() const;
224
225 // Returns the values in sorted KEY order.
226 inline std::vector<V> SortedValues() const;
227
228 const StorageType& raw_map() const { return map_; }
229 IndexedModel* model() const { return model_; }
230
231 friend bool operator==(const IdMap& lhs, const IdMap& rhs) {
232 return lhs.model_ == rhs.model_ && lhs.map_ == rhs.map_;
233 }
234 friend bool operator!=(const IdMap& lhs, const IdMap& rhs) {
235 return !(lhs == rhs);
236 }
237
238 private:
239 inline std::vector<IdType> SortedIds() const;
240 // CHECKs that model_ and k.model() matches when this map is not empty
241 // (i.e. its model_ is not null). When it is empty, simply check that
242 // k.model() is not null.
243 inline void CheckModel(const K& k) const;
244 // Sets model_ to k.model() if this map is empty (i.e. its model_ is
245 // null). Else CHECK that it has the same model. It also CHECK that k.model()
246 // is not null.
247 inline void CheckOrSetModel(const K& k);
248 // Sets model_ to other.model_ if this map is empty (i.e. its model_ is
249 // null). Else if the other map is not empty, CHECK that it has the same
250 // model.
251 inline void CheckOrSetModel(const IdMap& other);
252
253 // Invariant: model == nullptr if and only if map_.empty().
254 IndexedModel* model_ = nullptr;
255 StorageType map_;
256};
257
258// Calls a.swap(b).
259//
260// This function is used for making MapId "swappable".
261// Ref: https://en.cppreference.com/w/cpp/named_req/Swappable.
262template <typename K, typename V>
264 a.swap(b);
265}
266
268// Inline implementations
270
272// IdMap::iterator
274
275template <typename K, typename V>
277 return reference(K(map_->model_, storage_iterator_->first),
278 storage_iterator_->second);
279}
280
281template <typename K, typename V>
285}
286
287template <typename K, typename V>
289 ++storage_iterator_;
290 return *this;
291}
292
293template <typename K, typename V>
295 iterator ret = *this;
296 ++(*this);
297 return ret;
298}
299
300template <typename K, typename V>
302 typename StorageType::iterator storage_iterator)
303 : map_(map), storage_iterator_(std::move(storage_iterator)) {}
304
306// IdMap::const_iterator
308
309template <typename K, typename V>
311 : map_(non_const_iterator.map_),
312 storage_iterator_(non_const_iterator.storage_iterator_) {}
313
314template <typename K, typename V>
317 return reference(K(map_->model_, storage_iterator_->first),
318 storage_iterator_->second);
319}
320
321template <typename K, typename V>
325}
326
327template <typename K, typename V>
330 ++storage_iterator_;
331 return *this;
332}
333
334template <typename K, typename V>
336 int) {
337 const_iterator ret = *this;
338 ++(*this);
339 return ret;
340}
341
342template <typename K, typename V>
344 const IdMap* map, typename StorageType::const_iterator storage_iterator)
345 : map_(map), storage_iterator_(std::move(storage_iterator)) {}
346
348// IdMap
350
351template <typename K, typename V>
353 : model_(model), map_(std::move(values)) {
354 if (!map_.empty()) {
355 CHECK(model_ != nullptr);
356 }
357}
358
359template <typename K, typename V>
360template <typename InputIt>
361IdMap<K, V>::IdMap(InputIt first, InputIt last) {
362 insert(first, last);
363}
364
365template <typename K, typename V>
366IdMap<K, V>::IdMap(std::initializer_list<value_type> ilist) {
367 insert(ilist);
368}
369
370template <typename K, typename V>
372 return const_iterator(this, map_.cbegin());
373}
374
375template <typename K, typename V>
377 return cbegin();
378}
379
380template <typename K, typename V>
382 return iterator(this, map_.begin());
383}
384
385template <typename K, typename V>
387 return const_iterator(this, map_.cend());
388}
389
390template <typename K, typename V>
392 return cend();
393}
394
395template <typename K, typename V>
397 return iterator(this, map_.end());
398}
399
400template <typename K, typename V>
402 model_ = nullptr;
403 map_.clear();
404}
405
406template <typename K, typename V>
407std::pair<typename IdMap<K, V>::iterator, bool> IdMap<K, V>::insert(
408 std::pair<K, V> k_v) {
409 return emplace(k_v.first, std::move(k_v.second));
410}
411
412template <typename K, typename V>
413template <typename InputIt>
414void IdMap<K, V>::insert(const InputIt first, const InputIt last) {
415 for (InputIt it = first; it != last; ++it) {
416 insert(*it);
417 }
418}
419
420template <typename K, typename V>
421void IdMap<K, V>::insert(std::initializer_list<value_type> ilist) {
422 insert(ilist.begin(), ilist.end());
423}
424
425template <typename K, typename V>
426std::pair<typename IdMap<K, V>::iterator, bool> IdMap<K, V>::emplace(const K& k,
427 V v) {
428 CheckOrSetModel(k);
429 auto initial_ret = map_.emplace(k.typed_id(), std::move(v));
430 return std::make_pair(iterator(this, std::move(initial_ret.first)),
431 initial_ret.second);
432}
433
434template <typename K, typename V>
435template <typename... Args>
436std::pair<typename IdMap<K, V>::iterator, bool> IdMap<K, V>::try_emplace(
437 const K& k, Args&&... args) {
438 CheckOrSetModel(k);
439 auto initial_ret =
440 map_.try_emplace(k.typed_id(), std::forward<Args>(args)...);
441 return std::make_pair(iterator(this, std::move(initial_ret.first)),
442 initial_ret.second);
443}
444
445template <typename K, typename V>
446int IdMap<K, V>::erase(const K& k) {
447 CheckModel(k);
448 const int ret = map_.erase(k.typed_id());
449 if (map_.empty()) {
450 model_ = nullptr;
451 }
452 return ret;
453}
454
455template <typename K, typename V>
457 map_.erase(pos.storage_iterator_);
458 if (map_.empty()) {
459 model_ = nullptr;
460 }
461}
462
463template <typename K, typename V>
465 const const_iterator last) {
466 auto ret = map_.erase(first.storage_iterator_, last.storage_iterator_);
467 if (map_.empty()) {
468 model_ = nullptr;
469 }
470 return iterator(this, std::move(ret));
471}
472
473template <typename K, typename V>
475 using std::swap;
476 swap(model_, other.model_);
477 swap(map_, other.map_);
478}
479
480template <typename K, typename V>
481const V& IdMap<K, V>::at(const K& k) const {
482 CheckModel(k);
483 return map_.at(k.typed_id());
484}
485
486template <typename K, typename V>
487V& IdMap<K, V>::at(const K& k) {
488 CheckModel(k);
489 return map_.at(k.typed_id());
490}
491
492template <typename K, typename V>
494 CheckOrSetModel(k);
495 return map_[k.typed_id()];
496}
497
498template <typename K, typename V>
499typename IdMap<K, V>::size_type IdMap<K, V>::count(const K& k) const {
500 CheckModel(k);
501 return map_.count(k.typed_id());
502}
503
504template <typename K, typename V>
505bool IdMap<K, V>::contains(const K& k) const {
506 CheckModel(k);
507 return map_.contains(k.typed_id());
508}
509
510template <typename K, typename V>
512 CheckModel(k);
513 return iterator(this, map_.find(k.typed_id()));
514}
515
516template <typename K, typename V>
518 CheckModel(k);
519 return const_iterator(this, map_.find(k.typed_id()));
520}
521
522template <typename K, typename V>
523std::pair<typename IdMap<K, V>::iterator, typename IdMap<K, V>::iterator>
525 const auto it = find(k);
526 if (it == end()) {
527 return {it, it};
528 }
529 return {it, std::next(it)};
530}
531
532template <typename K, typename V>
533std::pair<typename IdMap<K, V>::const_iterator,
535IdMap<K, V>::equal_range(const K& k) const {
536 const auto it = find(k);
537 if (it == end()) {
538 return {it, it};
539 }
540 return {it, std::next(it)};
541}
542
543template <typename K, typename V>
544void IdMap<K, V>::Add(const IdMap& other) {
545 CheckOrSetModel(other);
546 for (const auto& pair : other.map_) {
547 map_[pair.first] += pair.second;
548 }
549}
550
551template <typename K, typename V>
552void IdMap<K, V>::Subtract(const IdMap& other) {
553 CheckOrSetModel(other);
554 for (const auto& pair : other.map_) {
555 map_[pair.first] -= pair.second;
556 }
557}
558
559template <typename K, typename V>
560std::vector<V> IdMap<K, V>::Values(const absl::Span<const K> keys) const {
561 std::vector<V> result;
562 result.reserve(keys.size());
563 for (const K key : keys) {
564 result.push_back(at(key));
565 }
566 return result;
567}
568
569template <typename K, typename V>
570absl::flat_hash_map<K, V> IdMap<K, V>::Values(
571 const absl::flat_hash_set<K>& keys) const {
572 absl::flat_hash_map<K, V> result;
573 for (const K key : keys) {
574 result[key] = at(key);
575 }
576 return result;
577}
578
579template <typename K, typename V>
580std::vector<K> IdMap<K, V>::SortedKeys() const {
581 std::vector<K> result;
582 result.reserve(map_.size());
583 for (const IdType id : SortedIds()) {
584 result.push_back(K(model_, id));
585 }
586 return result;
587}
588
589template <typename K, typename V>
590std::vector<V> IdMap<K, V>::SortedValues() const {
591 std::vector<V> result;
592 result.reserve(map_.size());
593 for (const IdType id : SortedIds()) {
594 result.push_back(map_.at(id));
595 }
596 return result;
597}
598
599template <typename K, typename V>
600std::vector<typename K::IdType> IdMap<K, V>::SortedIds() const {
601 std::vector<IdType> result;
602 result.reserve(map_.size());
603 for (const auto& [id, _] : map_) {
604 result.push_back(id);
605 }
606 std::sort(result.begin(), result.end());
607 return result;
608}
609
610template <typename K, typename V>
611void IdMap<K, V>::CheckModel(const K& k) const {
612 CHECK(k.model() != nullptr) << internal::kKeyHasNullIndexedModel;
613 CHECK(model_ == nullptr || model_ == k.model())
615}
616
617template <typename K, typename V>
618void IdMap<K, V>::CheckOrSetModel(const K& k) {
619 CHECK(k.model() != nullptr) << internal::kKeyHasNullIndexedModel;
620 if (model_ == nullptr) {
621 model_ = k.model();
622 } else {
624 }
625}
626
627template <typename K, typename V>
628void IdMap<K, V>::CheckOrSetModel(const IdMap& other) {
629 if (model_ == nullptr) {
630 model_ = other.model_;
631 } else if (other.model_ != nullptr) {
632 CHECK_EQ(model_, other.model_) << internal::kObjectsFromOtherIndexedModel;
633 } else {
634 // By construction when other is not empty, it has a non null `model_`.
635 DCHECK(other.empty());
636 }
637}
638
639} // namespace math_opt
640} // namespace operations_research
641
642#endif // OR_TOOLS_MATH_OPT_CPP_ID_MAP_H_
#define CHECK(condition)
Definition: base/logging.h:491
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define DCHECK(condition)
Definition: base/logging.h:885
friend bool operator!=(const const_iterator &lhs, const const_iterator &rhs)
Definition: id_map.h:120
internal::ArrowOperatorProxy< reference > operator->() const
Definition: id_map.h:323
friend bool operator==(const const_iterator &lhs, const const_iterator &rhs)
Definition: id_map.h:116
friend bool operator==(const iterator &lhs, const iterator &rhs)
Definition: id_map.h:83
IdMap::difference_type difference_type
Definition: id_map.h:73
std::forward_iterator_tag iterator_category
Definition: id_map.h:74
friend bool operator!=(const iterator &lhs, const iterator &rhs)
Definition: id_map.h:86
internal::ArrowOperatorProxy< reference > operator->() const
Definition: id_map.h:283
void Subtract(const IdMap &other)
Definition: id_map.h:552
std::pair< iterator, bool > try_emplace(const K &k, Args &&... args)
void reserve(size_type count)
Definition: id_map.h:155
const_iterator begin() const
Definition: id_map.h:376
std::pair< const K, V > value_type
Definition: id_map.h:60
std::vector< K > SortedKeys() const
Definition: id_map.h:580
std::vector< V > SortedValues() const
Definition: id_map.h:590
absl::flat_hash_map< IdType, V > StorageType
Definition: id_map.h:57
std::pair< iterator, bool > emplace(const K &k, V v)
Definition: id_map.h:426
std::pair< const K, const V & > const_reference
Definition: id_map.h:64
IndexedModel * model() const
Definition: id_map.h:229
std::vector< V > Values(absl::Span< const K > keys) const
Definition: id_map.h:560
friend bool operator==(const IdMap &lhs, const IdMap &rhs)
Definition: id_map.h:231
bool contains(const K &k) const
Definition: id_map.h:505
typename StorageType::size_type size_type
Definition: id_map.h:61
const_iterator end() const
Definition: id_map.h:391
std::pair< iterator, bool > insert(std::pair< K, V > k_v)
Definition: id_map.h:407
typename K::IdType IdType
Definition: id_map.h:56
const StorageType & raw_map() const
Definition: id_map.h:228
const_iterator cend() const
Definition: id_map.h:386
size_type count(const K &k) const
Definition: id_map.h:499
const V & at(const K &k) const
Definition: id_map.h:481
const_iterator cbegin() const
Definition: id_map.h:371
friend bool operator!=(const IdMap &lhs, const IdMap &rhs)
Definition: id_map.h:234
typename StorageType::difference_type difference_type
Definition: id_map.h:62
std::pair< iterator, iterator > equal_range(const K &k)
Definition: id_map.h:524
void Add(const IdMap &other)
Definition: id_map.h:544
iterator find(const K &k)
Definition: id_map.h:511
std::pair< const K, V & > reference
Definition: id_map.h:63
int64_t b
int64_t a
Block * next
constexpr absl::string_view kObjectsFromOtherIndexedModel
Definition: key_types.h:56
constexpr absl::string_view kKeyHasNullIndexedModel
Definition: key_types.h:51
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
Collection of objects used to extend the Constraint Solver library.
STL namespace.