Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
attr_key.h
Go to the documentation of this file.
1// Copyright 2010-2025 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#ifndef OR_TOOLS_MATH_OPT_ELEMENTAL_ATTR_KEY_H_
15#define OR_TOOLS_MATH_OPT_ELEMENTAL_ATTR_KEY_H_
16
17#include <array>
18#include <cstddef>
19#include <cstdint>
20#include <ostream>
21#include <type_traits>
22#include <utility>
23
24#include "absl/container/flat_hash_map.h"
25#include "absl/container/flat_hash_set.h"
26#include "absl/log/check.h"
27#include "absl/strings/str_cat.h"
28#include "absl/strings/str_join.h"
29#include "absl/types/span.h"
33
35
36// An attribute key for an attribute keyed on `n` elements.
37// `AttrKey` is a value type.
38template <int n, typename Symmetry = NoSymmetry>
39class AttrKey {
40 public:
41 using value_type = int64_t;
42 using SymmetryT = Symmetry;
43
44 // Default constructor: values are uninitialized.
45 constexpr AttrKey() {} // NOLINT: uninitialized on purpose.
46
47 template <typename... Ints,
48 typename = std::enable_if_t<(sizeof...(Ints) == n &&
49 (std::is_integral_v<Ints> && ...))>>
50 explicit constexpr AttrKey(const Ints... ids) {
51 auto push_back = [this, i = 0](auto e) mutable { element_ids_[i++] = e; };
52 (push_back(ids), ...);
53 Symmetry::Enforce(element_ids_);
54 }
55
56 template <ElementType... element_types,
57 typename = std::enable_if_t<(sizeof...(element_types) == n)>>
58 explicit constexpr AttrKey(const ElementId<element_types>... ids)
59 : AttrKey(ids.value()...) {}
60
61 constexpr AttrKey(std::array<value_type, n> ids) // NOLINT: pybind11.
62 : element_ids_(ids) {
63 Symmetry::Enforce(element_ids_);
64 }
65
66 // Canonicalizes a non-canonical key, i.e., enforces the symmetries
68 return AttrKey(key.element_ids_);
69 }
70
71 // Creates a key from a range of `n` elements.
72 static absl::StatusOr<AttrKey> FromRange(absl::Span<const int64_t> range) {
73 if (range.size() != n) {
74 return ::util::InvalidArgumentErrorBuilder()
75 << "cannot build AttrKey<" << n << "> from a range of size "
76 << range.size();
77 }
78 AttrKey result;
79 std::copy(range.begin(), range.end(), result.element_ids_.begin());
80 Symmetry::Enforce(result.element_ids_);
81 return result;
82 }
83
84 constexpr AttrKey(const AttrKey&) = default;
85 constexpr AttrKey& operator=(const AttrKey&) = default;
86
87 static constexpr int size() { return n; }
88
89 // Element access.
90 constexpr value_type operator[](const int dim) const {
91 DCHECK_LT(dim, n);
92 DCHECK_GE(dim, 0);
93 return element_ids_[dim];
94 }
95 constexpr value_type& operator[](const int dim) {
96 DCHECK_LT(dim, n);
97 DCHECK_GE(dim, 0);
98 return element_ids_[dim];
99 }
100
101 // Element iteration.
102 constexpr const value_type* begin() const { return element_ids_.data(); }
103 constexpr const value_type* end() const {
104 return element_ids_.data() + element_ids_.size();
105 }
106
107 // `AttrKey` is comparable (ordering is lexicographic) and hashable.
108 //
109 // TODO(b/365998156): post C++ 20, replace by spaceship operator (with all
110 // comparison operators below). Do NOT use the default generated operator (see
111 // below).
112 friend constexpr bool operator==(const AttrKey& l, const AttrKey& r) {
113 // This is much faster than using the default generated `operator==`.
114 for (int i = 0; i < n; ++i) {
115 if (l.element_ids_[i] != r.element_ids_[i]) {
116 return false;
117 }
118 }
119 return true;
120 }
121
122 friend constexpr bool operator<(const AttrKey& l, const AttrKey& r) {
123 // This is much faster than using the default generated `operator<`.
124 for (int i = 0; i < n; ++i) {
125 if (l.element_ids_[i] < r.element_ids_[i]) {
126 return true;
127 }
128 if (l.element_ids_[i] > r.element_ids_[i]) {
129 return false;
130 }
131 }
132 return false;
133 }
134
135 friend constexpr bool operator<=(const AttrKey& l, const AttrKey& r) {
136 // This is much faster than using the default generated `operator<`.
137 for (int i = 0; i < n; ++i) {
138 if (l.element_ids_[i] < r.element_ids_[i]) {
139 return true;
140 }
141 if (l.element_ids_[i] > r.element_ids_[i]) {
142 return false;
143 }
144 }
145 return true;
146 }
147
148 friend constexpr bool operator>(const AttrKey& l, const AttrKey& r) {
149 return r < l;
150 }
151
152 friend constexpr bool operator>=(const AttrKey& l, const AttrKey& r) {
153 return r <= l;
154 }
155
156 template <typename H>
157 friend H AbslHashValue(H h, const AttrKey& a) {
158 return H::combine_contiguous(std::move(h), a.element_ids_.data(), n);
159 }
160
161 // `AttrKey` is printable for logging and tests.
162 template <typename Sink>
163 friend void AbslStringify(Sink& sink, const AttrKey& key) {
164 sink.Append(absl::StrCat(
165 "AttrKey(", absl::StrJoin(absl::MakeSpan(key.element_ids_), ", "),
166 ")"));
167 }
168
169 // Removes the element at dimension `dim` from the key and returns a key with
170 // only remaining dimensions.
171 template <int dim>
173 static_assert(dim >= 0);
174 static_assert(dim < n);
175 AttrKey<n - 1, NoSymmetry> result;
176 for (int i = 0; i < dim; ++i) {
177 result.element_ids_[i] = element_ids_[i];
178 }
179 for (int i = dim + 1; i < n; ++i) {
180 result.element_ids_[i - 1] = element_ids_[i];
181 }
182 return result;
183 }
184
185 // Adds element `elem` at dimension `dim` and returns the result.
186 // The result must respect `NewSymmetry` (we `DCHECK` this).
187 template <int dim, typename NewSymmetry>
189 static_assert(dim >= 0);
190 static_assert(dim < n + 1);
192 for (int i = 0; i < dim; ++i) {
193 result.element_ids_[i] = element_ids_[i];
194 }
195 result.element_ids_[dim] = elem;
196 for (int i = dim + 1; i < n + 1; ++i) {
197 result.element_ids_[i] = element_ids_[i - 1];
198 }
199 DCHECK(NewSymmetry::Validate(result.element_ids_))
200 << result << " does not have `" << NewSymmetry::GetName()
201 << "` symmetry";
202 return result;
203 }
204
205 private:
206 template <int other_n, typename OtherSymmetry>
207 friend class AttrKey;
208 std::array<value_type, n> element_ids_;
209};
210
211// CTAD for `AttrKey(1,2)`.
212template <typename... Ints>
213AttrKey(Ints... dims) -> AttrKey<sizeof...(Ints), NoSymmetry>;
214
215// Traits to detect whether `T` is an `AttrKey`.
216template <typename T>
217struct is_attr_key : public std::false_type {};
218
219template <int n, typename Symmetry>
220struct is_attr_key<AttrKey<n, Symmetry>> : public std::true_type {};
221
222template <typename T>
223static constexpr inline bool is_attr_key_v = is_attr_key<T>::value;
224
225// Required for open-source `StatusBuilder` support.
226template <int n, typename Symmetry>
227std::ostream& operator<<(std::ostream& ostr, const AttrKey<n, Symmetry>& key) {
228 ostr << absl::StrCat(key);
229 return ostr;
230}
231
232namespace detail {
233// A set of zero or one `AttrKey<0, Symmetry>, V`. This is used to make
234// implementations of `AttrDiff` and `AttrStorage` uniform.
235// `V` must by default constructible, trivially destructible and copyable
236// (we'll fail to compile otherwise).
237// After c++26, optional is a sequence container, so this can pretty much become
238// `std::optional<AttrKey<0, Symmetry>>` + `find()`.
239template <typename Symmetry, typename V>
241 public:
242 using value_type = V;
244
245 template <typename ValueT>
247 public:
248 IteratorImpl() = default;
249 // `iterator` converts to `const_iterator`.
250 IteratorImpl(const IteratorImpl<std::remove_cv_t<ValueT>>& other) // NOLINT
251 : value_(other.value_) {}
252
253 // Dereference.
254 ValueT& operator*() const {
255 DCHECK_NE(value_, nullptr);
256 return *value_;
257 }
258 ValueT* operator->() const {
259 DCHECK_NE(value_, nullptr);
260 return value_;
261 }
262
263 // Increment.
265 DCHECK_NE(value_, nullptr);
266 value_ = nullptr;
267 return *this;
268 }
269
270 // Equality.
271 friend bool operator==(const IteratorImpl& l, const IteratorImpl& r) {
272 return l.value_ == r.value_;
273 }
274 friend bool operator!=(const IteratorImpl& l, const IteratorImpl& r) {
275 return !(l == r);
276 }
277
278 private:
279 friend class AttrKey0RawSet;
280 explicit IteratorImpl(ValueT& value) : value_(&value) {}
281
282 ValueT* value_ = nullptr;
283 };
284
287
288 AttrKey0RawSet() = default;
289
290 bool empty() const { return !engaged_; }
291 size_t size() const { return engaged_ ? 1 : 0; }
292
294 return engaged_ ? const_iterator(value_) : const_iterator();
295 }
296 const_iterator end() const { return const_iterator(); }
297 iterator begin() { return engaged_ ? iterator(value_) : iterator(); }
298 iterator end() { return iterator(); }
299
300 bool contains(Key) const { return engaged_; }
301 const_iterator find(Key) const { return begin(); }
302 iterator find(Key) { return begin(); }
303
304 void clear() { engaged_ = false; }
305 size_t erase(Key) {
306 if (engaged_) {
307 engaged_ = false;
308 return 1;
309 }
310 return 0;
311 }
312 size_t erase(const_iterator) { return erase(Key()); }
313
314 template <typename... Args>
315 std::pair<iterator, bool> try_emplace(Key, Args&&... args) {
316 if (engaged_) {
317 return std::make_pair(iterator(value_), false);
318 }
319 value_ = value_type(Key(), std::forward<Args>(args)...);
320 engaged_ = true;
321 return std::make_pair(iterator(value_), true);
322 }
323
324 std::pair<iterator, bool> insert(const value_type& v) {
325 if (engaged_) {
326 return std::make_pair(iterator(value_), false);
327 }
328 value_ = v;
329 engaged_ = true;
330 return std::make_pair(iterator(value_), true);
331 }
332
333 private:
334 // The following greatly simplifies the implementation because we don't have
335 // to worry about side effects of the dtor (see e.g. `clear()`).
336 static_assert(std::is_trivially_destructible_v<value_type>);
337
338 bool engaged_ = false;
339 value_type value_;
340};
341
342} // namespace detail
343
344// A hash set of `AttrKeyT`, where `AttrKeyT` is an `AttrKey<n, Symmetry>`.
345template <typename AttrKeyT,
346 typename = std::enable_if_t<is_attr_key_v<AttrKeyT>>>
347using AttrKeyHashSet = std::conditional_t<
348 (AttrKeyT::size() > 0), absl::flat_hash_set<AttrKeyT>,
350
351// A hash map of `AttrKeyT` to `V`, where `AttrKeyT` is an
352// `AttrKey<n, Symmetry>`.
353template <typename AttrKeyT, typename V,
354 typename = std::enable_if_t<is_attr_key_v<AttrKeyT>>>
356 std::conditional_t<(AttrKeyT::size() > 0), absl::flat_hash_map<AttrKeyT, V>,
357 detail::AttrKey0RawSet<typename AttrKeyT::SymmetryT,
358 std::pair<AttrKeyT, V>>>;
359
360} // namespace operations_research::math_opt
361
362#endif // OR_TOOLS_MATH_OPT_ELEMENTAL_ATTR_KEY_H_
friend void AbslStringify(Sink &sink, const AttrKey &key)
AttrKey is printable for logging and tests.
Definition attr_key.h:163
AttrKey< n - 1, NoSymmetry > RemoveElement() const
Definition attr_key.h:172
constexpr AttrKey(const Ints... ids)
Definition attr_key.h:50
constexpr const value_type * begin() const
Element iteration.
Definition attr_key.h:102
constexpr const value_type * end() const
Definition attr_key.h:103
friend constexpr bool operator<=(const AttrKey &l, const AttrKey &r)
Definition attr_key.h:135
friend constexpr bool operator<(const AttrKey &l, const AttrKey &r)
Definition attr_key.h:122
static constexpr AttrKey Canonicalize(AttrKey< n, NoSymmetry > key)
Canonicalizes a non-canonical key, i.e., enforces the symmetries.
Definition attr_key.h:67
static absl::StatusOr< AttrKey > FromRange(absl::Span< const int64_t > range)
Creates a key from a range of n elements.
Definition attr_key.h:72
constexpr AttrKey(const AttrKey &)=default
friend constexpr bool operator==(const AttrKey &l, const AttrKey &r)
Definition attr_key.h:112
constexpr value_type & operator[](const int dim)
Definition attr_key.h:95
constexpr AttrKey(std::array< value_type, n > ids)
Definition attr_key.h:61
constexpr AttrKey()
Default constructor: values are uninitialized.
Definition attr_key.h:45
friend H AbslHashValue(H h, const AttrKey &a)
Definition attr_key.h:157
AttrKey< n+1, NewSymmetry > AddElement(const value_type elem) const
Definition attr_key.h:188
constexpr value_type operator[](const int dim) const
Element access.
Definition attr_key.h:90
constexpr AttrKey(const ElementId< element_types >... ids)
Definition attr_key.h:58
friend constexpr bool operator>(const AttrKey &l, const AttrKey &r)
Definition attr_key.h:148
constexpr AttrKey & operator=(const AttrKey &)=default
friend constexpr bool operator>=(const AttrKey &l, const AttrKey &r)
Definition attr_key.h:152
static constexpr int size()
Definition attr_key.h:87
A strongly typed element id. Value type.
Definition elements.h:64
int64_t value() const
Returns the raw id value.
Definition elements.h:77
friend bool operator!=(const IteratorImpl &l, const IteratorImpl &r)
Definition attr_key.h:274
IteratorImpl(const IteratorImpl< std::remove_cv_t< ValueT > > &other)
iterator converts to const_iterator.
Definition attr_key.h:250
friend bool operator==(const IteratorImpl &l, const IteratorImpl &r)
Equality.
Definition attr_key.h:271
IteratorImpl< const value_type > const_iterator
Definition attr_key.h:286
std::pair< iterator, bool > try_emplace(Key, Args &&... args)
Definition attr_key.h:315
std::pair< iterator, bool > insert(const value_type &v)
Definition attr_key.h:324
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
AttrKey(Ints... dims) -> AttrKey< sizeof...(Ints), NoSymmetry >
CTAD for AttrKey(1,2).
std::conditional_t<(AttrKeyT::size() > 0), absl::flat_hash_set< AttrKeyT >, detail::AttrKey0RawSet< typename AttrKeyT::SymmetryT, AttrKeyT > > AttrKeyHashSet
A hash set of AttrKeyT, where AttrKeyT is an AttrKey<n, Symmetry>.
Definition attr_key.h:347
std::ostream & operator<<(std::ostream &ostr, const SecondOrderConeConstraint &constraint)
static constexpr bool is_attr_key_v
Definition attr_key.h:223
std::conditional_t<(AttrKeyT::size() > 0), absl::flat_hash_map< AttrKeyT, V >, detail::AttrKey0RawSet< typename AttrKeyT::SymmetryT, std::pair< AttrKeyT, V > > > AttrKeyHashMap
Definition attr_key.h:355
A tag for no symmetry between two elements.
Definition symmetry.h:29
Traits to detect whether T is an AttrKey.
Definition attr_key.h:217