Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
element_storage.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 ORTOOLS_MATH_OPT_ELEMENTAL_ELEMENT_STORAGE_H_
15#define ORTOOLS_MATH_OPT_ELEMENTAL_ELEMENT_STORAGE_H_
16
17#include <algorithm>
18#include <cstdint>
19#include <string>
20#include <utility>
21#include <variant>
22#include <vector>
23
24#include "absl/container/flat_hash_map.h"
25#include "absl/status/statusor.h"
26#include "absl/strings/string_view.h"
28
30
31namespace detail {
32
33// A dense element storage, for use when no elements have been erased.
34// Same API as `ElementStorage`, but no deletion.
35// TODO(b/369972336): We should stay in dense mode if we have a small percentage
36// of deletions.
38 public:
39 int64_t Add(const absl::string_view name) {
40 const int64_t id = elements_.size();
41 elements_.emplace_back(name);
42 return id;
43 }
44
45 bool exists(const int64_t id) const {
46 return 0 <= id && id < elements_.size();
47 }
48
49 absl::StatusOr<absl::string_view> GetName(const int64_t id) const {
50 if (exists(id)) {
51 return elements_[id];
52 }
53 return util::InvalidArgumentErrorBuilder() << "no element with id " << id;
54 }
55
56 int64_t next_id() const { return size(); }
57
58 std::vector<int64_t> AllIds() const;
59
60 int64_t size() const { return elements_.size(); }
61
62 private:
64 std::vector<std::string> elements_;
65};
66
67// A sparse element storage, which supports deletion.
69 public:
71
72 int64_t Add(const absl::string_view name) {
73 const int64_t id = next_id_;
74 elements_.try_emplace(id, name);
75 ++next_id_;
76 return id;
77 }
78
79 bool Erase(int64_t id) { return elements_.erase(id) > 0; }
80
81 bool exists(int64_t id) const { return elements_.contains(id); }
82
83 absl::StatusOr<absl::string_view> GetName(int64_t id) const {
84 if (const auto it = elements_.find(id); it != elements_.end()) {
85 return it->second;
86 }
87 return util::InvalidArgumentErrorBuilder() << "no element with id " << id;
88 }
89
90 int64_t next_id() const { return next_id_; }
91
92 std::vector<int64_t> AllIds() const;
93
94 int64_t size() const { return elements_.size(); }
95
96 void ensure_next_id_at_least(int64_t id) {
97 next_id_ = std::max(next_id_, id);
98 }
99
100 private:
101 absl::flat_hash_map<int64_t, std::string> elements_;
102 int64_t next_id_ = 0;
103};
104
105} // namespace detail
106
108 // Functions with deduced return must be defined before they are used.
109 private:
110 // std::visit is very slow, see yaqs/5253596299885805568.
111 //
112 // This function is static, taking Self as template argument, to avoid
113 // having const and non-const versions. Post C++ 23, prefer:
114 // https://en.cppreference.com/w/cpp/language/member_functions#Explicit_object_member_functions
115 template <typename Self, typename Fn>
116 static auto Visit(Self& self, Fn fn) {
117 if (std::holds_alternative<detail::DenseElementStorage>(self.impl_)) {
118 return fn(std::get<detail::DenseElementStorage>(self.impl_));
119 } else {
120 return fn(std::get<detail::SparseElementStorage>(self.impl_));
121 }
122 }
123
124 public:
125 // We start with a dense storage, which is more efficient, and switch to a
126 // sparse storage when an element is erased.
127 ElementStorage() : impl_(detail::DenseElementStorage()) {}
128
129 // Creates a new element and returns its id.
130 int64_t Add(const absl::string_view name) {
131 return Visit(*this, [name](auto& impl) { return impl.Add(name); });
132 }
133
134 // Deletes an element by id, returning true on success and false if no element
135 // was deleted (it was already deleted or the id was not from any existing
136 // element).
137 bool Erase(const int64_t id) { return AsSparse().Erase(id); }
138
139 // Returns true an element with this id was created and not yet erased.
140 bool Exists(const int64_t id) const {
141 return Visit(*this, [id](auto& impl) { return impl.exists(id); });
142 }
143
144 // Returns the name of this element, or CHECK fails if no element with this id
145 // exists.
146 absl::StatusOr<absl::string_view> GetName(const int64_t id) const {
147 return Visit(*this, [id](auto& impl) { return impl.GetName(id); });
148 }
149
150 // Returns the id that will be used for the next element added.
151 //
152 // NOTE: when no elements have been erased, this equals size().
153 int64_t next_id() const {
154 return Visit(*this, [](auto& impl) { return impl.next_id(); });
155 }
156
157 // Returns all ids of all elements in the model in an unsorted,
158 // non-deterministic order.
159 std::vector<int64_t> AllIds() const;
160
161 // Returns the number of elements added and not erased.
162 int64_t size() const {
163 return Visit(*this, [](auto& impl) { return impl.size(); });
164 }
165
166 // Increases next_id() to `id` if it is currently less than `id`.
167 //
168 // Useful for reading a model back from proto, most users should not need to
169 // call this directly.
170 void EnsureNextIdAtLeast(const int64_t id) {
171 if (id > next_id()) {
172 AsSparse().ensure_next_id_at_least(id);
173 }
174 }
175
176 private:
177 detail::SparseElementStorage& AsSparse() {
178 if (auto* sparse = std::get_if<detail::SparseElementStorage>(&impl_)) {
179 return *sparse;
180 }
181 impl_ = detail::SparseElementStorage(
182 std::move(std::get<detail::DenseElementStorage>(impl_)));
183 return std::get<detail::SparseElementStorage>(impl_);
184 }
185
186 std::variant<detail::DenseElementStorage, detail::SparseElementStorage> impl_;
187};
188
189} // namespace operations_research::math_opt
190
191#endif // ORTOOLS_MATH_OPT_ELEMENTAL_ELEMENT_STORAGE_H_
absl::StatusOr< absl::string_view > GetName(const int64_t id) const
int64_t Add(const absl::string_view name)
absl::StatusOr< absl::string_view > GetName(const int64_t id) const
absl::StatusOr< absl::string_view > GetName(int64_t id) const
StatusBuilder InvalidArgumentErrorBuilder()