Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
base_types.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_SET_COVER_BASE_TYPES_H_
15#define OR_TOOLS_SET_COVER_BASE_TYPES_H_
16
17#include <cstddef>
18#include <cstdint>
19#include <type_traits>
20#include <vector>
21
22#include "absl/log/check.h"
23#include "absl/time/time.h"
24#include "absl/types/span.h"
27#include "ortools/base/timer.h"
28
29namespace operations_research {
30
31// Basic non-strict type for cost. The speed penalty for using double is ~2%.
32using Cost = double;
33
34// Base non-strict integer type for counting elements and subsets.
35// Using ints makes it possible to represent problems with more than 2 billion
36// (2e9) elements and subsets. If need arises one day, BaseInt can be split
37// into SubsetBaseInt and ElementBaseInt.
38// Quick testing has shown a slowdown of about 20-25% when using int64_t.
39using BaseInt = int32_t;
40
41// We make heavy use of strong typing to avoid obvious mistakes.
42// Subset index.
44
45// Element index.
47
48// Position in a vector. The vector may either represent a column, i.e. a
49// subset with all its elements, or a row, i,e. the list of subsets which
50// contain a given element.
53
54using SubsetRange = util_intops::StrongIntRange<SubsetIndex>;
55using ElementRange = util_intops::StrongIntRange<ElementIndex>;
56using ColumnEntryRange = util_intops::StrongIntRange<ColumnEntryIndex>;
57
60
63
66
67// Views of the sparse vectors.
70
73
74// Maps from element to subset. Useful to compress the sparse row view.
77
78template <typename EntryIndex, typename Index>
80
81// A compressed vector of strong indices (e.g. SubsetIndex, ElementIndex), with
82// EntryIndex indicating the position in the vector (e.g. RowEntryIndex or
83// ColumnEntryIndex).
84// The vector is compressed in a variable-length encoding, where each element
85// is encoded as a delta from the previous element.
86// The vector is stored in a byte stream, which is addressed byte-by-byte, which
87// is necessary to store variable-length integers.
88// The variable-length encoding is little-endian base128 (LEB128) as used by
89// protobufs. Since we only use non-negative integers, there is no need to
90// encode the sign bit (so non-zigzag encoding or similar techniques).
91//
92// TODO(user):
93// There is a lot of room for optimization in this class.
94// - Use a bit-twiddling approach to encode and decode integers.
95// - Use another simpler variable encoding (base on vu128) using a single 64-bit
96// word and limited to storing 8 bytes with 7 bits per byte. A 64-bit word would
97// contain 8*7 = 56 bits. This is enough for an index in an array with
98// 2^56 = 7.2e16 elements, and more that the address space of current (2025)
99// machines.
100// - Access memory by 64-bit words instead of bytes.
101// - Make Codecs a template parameter of CompressedStrongVector (?)
102template <typename EntryIndex, typename Index>
104 public:
107 using value_type = Index;
108
109 explicit CompressedStrongVector() : memorized_value_(0), byte_stream_() {}
110
111 // Initializes the compressed vector from a strong vector.
112 // TODO(user): try to guess the size of the compressed vector and pre-allocate
113 // it. Experience shows it consumes between 1 and 2 bytes per Index on
114 // average.
117 : memorized_value_(0), byte_stream_() {
118 for (const Index x : strong_vector) {
119 push_back(x);
120 }
121 }
122
123 explicit CompressedStrongVector(absl::Span<const Index> vec)
124 : memorized_value_(0), byte_stream_() {
125 for (const Index x : vec) {
126 push_back(x);
127 }
128 }
129
130 // Appends an x to the vector in a compressed form.
131 void push_back(Index x) { EncodeInteger(x.value()); }
132
133 // Returns a reference to the underlying byte stream.
134 const std::vector<uint8_t>& byte_stream() const { return byte_stream_; }
135
136 // Returns the number of bytes needed to store the vector.
137 size_t size_in_bytes() const { return byte_stream_.size(); }
138
139 bool empty() const { return byte_stream_.empty(); }
140
141 // Reserves space for n bytes.
142 void Reserve(size_t n) { byte_stream_.reserve(n); }
143
144 // For range-for loops.
145 iterator begin() { return iterator(*this); }
146 iterator end() { return iterator(*this, true); }
147
148 // For const range-for loops.
149 const_iterator begin() const { return const_iterator(*this); }
150 const_iterator end() const { return const_iterator(*this, true); }
151
152 private:
153 void EncodeInteger(BaseInt x) {
154 BaseInt delta = x - memorized_value_; // Delta from previous value.
155 DCHECK_GE(delta, 0);
156
157 // Push the delta as a variable-length integer.
158 while (delta >= 128) {
159 // Keep the lower 7 bits of the delta and set the higher bit to 1 to mark
160 // the continuation of the variable-length encoding.
161 byte_stream_.push_back(static_cast<uint8_t>(delta | 0x80));
162 // Shift the delta by 7 bits to get the next value.
163 delta >>= 7;
164 }
165 // The final byte is less than 128, so its higher bit is zero, which marks
166 // the end of the variable-length encoding.
167 byte_stream_.push_back(static_cast<uint8_t>(delta));
168
169 // Do not forget to remember the last value.
170 memorized_value_ = x + kMinDelta;
171 }
172 // The last value memorized in the vector. It is defined as the last value
173 // appended to the vector plus a kMinDelta. It starts at zero.
174 BaseInt memorized_value_;
175
176 // The minimum difference between two consecutive elements in the vector.
177 static constexpr int64_t kMinDelta = 1;
178
179 // The byte stream.
180 std::vector<uint8_t> byte_stream_;
181};
182
183// Iterator for a compressed strong vector. There is no tool for decompressing
184// a compressed strong vector, so this iterator is the only way to access the
185// elements, always in order.
186// The iterator is not thread-safe.
187template <typename EntryIndex, typename Index>
189 public:
191 const CompressedStrongVector<EntryIndex, Index>& compressed_vector)
192 : compressed_vector_(compressed_vector),
193 memorized_value_(0),
194 pos_(0),
195 last_pos_(0) {}
196
198 const CompressedStrongVector<EntryIndex, Index>& compressed_vector,
199 bool at_end)
200 : compressed_vector_(compressed_vector),
201 memorized_value_(0),
202 pos_(0),
203 last_pos_(at_end ? compressed_vector.size_in_bytes() : 0) {}
204
205 bool at_end() const {
206 DCHECK_LE(last_pos_, compressed_vector_.size_in_bytes());
207 return last_pos_ == compressed_vector_.size_in_bytes();
208 }
209
210 Index operator*() { return DecodeInteger(); }
211
213 DCHECK_EQ(&compressed_vector_, &(other.compressed_vector_));
214 return last_pos_ == other.last_pos_;
215 }
216
218 return !(*this == other);
219 }
220
222 last_pos_ = pos_;
223 return *this;
224 }
225
226 private:
227 Index DecodeInteger() {
228 // This can be made much faster by using a bit-twiddling approach.
229 const std::vector<uint8_t>& encoded = compressed_vector_.byte_stream();
230 BaseInt delta = 0;
231 int shift = 0;
232 for (pos_ = last_pos_, shift = 0;
233 encoded[pos_] >= 128 && pos_ < compressed_vector_.size_in_bytes();
234 shift += 7, ++pos_) {
235 delta |= static_cast<BaseInt>(encoded[pos_] & 0x7F) << shift;
236 }
237 delta |= static_cast<BaseInt>(encoded[pos_]) << shift;
238 ++pos_;
239 memorized_value_ += Index(delta + kMinDelta);
240 return memorized_value_ - Index(kMinDelta);
241 }
242
243 // The compressed vector.
244 const CompressedStrongVector<EntryIndex, Index>& compressed_vector_;
245
246 // The last value memorized in the decoder. It is defined as the last value
247 // appended to the vector plus a kMinDelta. It starts at zero.
248 Index memorized_value_;
249
250 // The current position in the byte stream.
251 int64_t pos_;
252
253 // The last position in the byte stream.
254 int64_t last_pos_;
255
256 static constexpr int64_t kMinDelta = 1;
257};
258
261
266
271
272template <typename Index>
274
275// A range of indices, that can be iterated over. Useful if used in a range-for
276// loop or as an IterableContainer.
277template <typename Index>
278class IndexRange {
279 public:
282 using value_type = Index;
283
284 IndexRange(Index start, Index end) : start_(start), end_(end) {}
285
286 explicit IndexRange(Index end) : start_(Index(0)), end_(end) {}
287
288 Index get_start() const { return start_; }
289 Index get_end() const { return end_; }
290
291 // For range-for loops.
292 iterator begin() { return iterator(*this); }
293 iterator end() { return iterator(*this, true); }
294
295 // For const range-for loops.
296 const_iterator begin() const { return const_iterator(*this); }
297 const_iterator end() const { return const_iterator(*this, true); }
298
299 private:
300 Index start_;
301 Index end_;
302};
303
304// Additional deduction guide
305template <class Index>
306IndexRange(Index a, Index b) -> IndexRange<Index>;
307
308// The iterator for an IndexRange.
309template <typename Index>
311 public:
313 : range_(range), current_(range.get_start()) {}
314
316 : range_(range), current_(at_end ? range.get_end() : range.get_start()) {}
317
318 bool at_end() const { return current_ == range_.get_end(); }
319
320 Index operator*() { return current_; }
321
322 bool operator==(const IndexRangeIterator& other) const {
323 DCHECK_EQ(range_.get_start(), other.range_.get_start());
324 DCHECK_EQ(range_.get_end(), other.range_.get_end());
325 return current_ == other.current_;
326 }
327
328 bool operator!=(const IndexRangeIterator& other) const {
329 return !(*this == other);
330 }
331
333 ++current_;
334 return *this;
335 }
336
337 private:
338 IndexRange<Index> range_;
339 Index current_;
340};
341
342// A container that can be iterated over, but does not own the data.
343// The container can be either const or non-const.
344// The container can be either a std::vector, a absl::Span, an IndexRange, a
345// StrongVector or a CompressedStrongVector.
346// We use the Curiously-Recurring Template Pattern (CRTP) to avoid having to
347// specify the type of the container in the derived class, and to not lose
348// runtime performance because of virtual functions.
349template <typename T, typename Derived>
351 public:
352 using value_type = typename std::decay_t<T>::value_type;
353 using iterator_type = decltype(std::begin(std::declval<T>()));
354
355 auto begin() { return static_cast<Derived*>(this)->begin_impl(); }
356 auto end() { return static_cast<Derived*>(this)->end_impl(); }
357 auto begin() const { return static_cast<const Derived*>(this)->begin_impl(); }
358 auto end() const { return static_cast<const Derived*>(this)->end_impl(); }
359};
360
361template <typename T>
363 : public IterableContainerBase<T, IterableContainer<T>> {
364 public:
365 explicit IterableContainer(const T& data_source) : data_(data_source) {}
366
367 auto begin_impl() { return data_.begin(); }
368 auto end_impl() { return data_.end(); }
369 auto begin_impl() const { return data_.begin(); }
370 auto end_impl() const { return data_.end(); }
371
372 private:
373 T data_;
374};
375
376// Additional deduction guide.
377template <typename T>
379
380// Simple stopwatch class that enables recording the time spent in various
381// functions in the code.
382// It uses RAII to automatically record the time spent in the constructor and
383// destructor, independently of the path taken by the code.
385 public:
386 explicit StopWatch(absl::Duration* duration) : duration_(duration), timer_() {
387 timer_.Start();
388 }
390 timer_.Stop();
391 *duration_ = timer_.GetDuration();
392 }
393 // Returns the elapsed time in seconds at a given moment. To be use to
394 // implement time limits in the derived classes.
395 double elapsed_time_in_seconds() const { return timer_.Get(); }
396
397 absl::Duration GetElapsedDuration() const { return timer_.GetDuration(); }
398
399 private:
400 absl::Duration* duration_;
401 WallTimer timer_;
402};
403
404} // namespace operations_research
405
406#endif // OR_TOOLS_SET_COVER_BASE_TYPES_H_
bool operator!=(const CompressedStrongVectorIterator &other) const
Definition base_types.h:217
CompressedStrongVectorIterator(const CompressedStrongVector< EntryIndex, Index > &compressed_vector)
Definition base_types.h:190
bool operator==(const CompressedStrongVectorIterator &other) const
Definition base_types.h:212
CompressedStrongVectorIterator & operator++()
Definition base_types.h:221
CompressedStrongVectorIterator(const CompressedStrongVector< EntryIndex, Index > &compressed_vector, bool at_end)
Definition base_types.h:197
void push_back(Index x)
Appends an x to the vector in a compressed form.
Definition base_types.h:131
size_t size_in_bytes() const
Returns the number of bytes needed to store the vector.
Definition base_types.h:137
CompressedStrongVectorIterator< EntryIndex, Index > iterator
Definition base_types.h:105
CompressedStrongVector(const util_intops::StrongVector< EntryIndex, Index > &strong_vector)
Definition base_types.h:115
void Reserve(size_t n)
Reserves space for n bytes.
Definition base_types.h:142
CompressedStrongVectorIterator< EntryIndex, Index > const_iterator
Definition base_types.h:106
const_iterator begin() const
For const range-for loops.
Definition base_types.h:149
iterator begin()
For range-for loops.
Definition base_types.h:145
CompressedStrongVector(absl::Span< const Index > vec)
Definition base_types.h:123
const std::vector< uint8_t > & byte_stream() const
Returns a reference to the underlying byte stream.
Definition base_types.h:134
The iterator for an IndexRange.
Definition base_types.h:310
IndexRangeIterator(const IndexRange< Index > &range, bool at_end)
Definition base_types.h:315
bool operator!=(const IndexRangeIterator &other) const
Definition base_types.h:328
bool operator==(const IndexRangeIterator &other) const
Definition base_types.h:322
IndexRangeIterator(const IndexRange< Index > &range)
Definition base_types.h:312
typename std::decay_t< T >::value_type value_type
Definition base_types.h:352
decltype(std::begin(std::declval< T >())) iterator_type
Definition base_types.h:353
IterableContainer(const T &data_source)
Definition base_types.h:365
double elapsed_time_in_seconds() const
Definition base_types.h:395
absl::Duration GetElapsedDuration() const
Definition base_types.h:397
StopWatch(absl::Duration *duration)
Definition base_types.h:386
In SWIG mode, we don't want anything besides these top-level includes.
IterableContainer(const T &data_source) -> IterableContainer< T >
Additional deduction guide.
CompressedStrongVectorIterator< ColumnEntryIndex, ElementIndex > CompressedColumnIterator
Definition base_types.h:267
util_intops::StrongVector< ElementIndex, CompressedRow > CompressedRowView
Definition base_types.h:264
util_intops::StrongVector< RowEntryIndex, SubsetIndex > SparseRow
Definition base_types.h:62
util_intops::StrongVector< SubsetIndex, Cost > SubsetCostVector
Definition base_types.h:58
CompressedStrongVector< RowEntryIndex, SubsetIndex > CompressedRow
Definition base_types.h:260
util_intops::StrongVector< SubsetIndex, BaseInt > SubsetToIntVector
Definition base_types.h:65
util_intops::StrongVector< ElementIndex, SubsetIndex > ElementToSubsetVector
Maps from element to subset. Useful to compress the sparse row view.
Definition base_types.h:75
util_intops::StrongIntRange< SubsetIndex > SubsetRange
Definition base_types.h:54
util_intops::StrongVector< ElementIndex, Cost > ElementCostVector
Definition base_types.h:59
CompressedStrongVector< ColumnEntryIndex, ElementIndex > CompressedColumn
Definition base_types.h:259
util_intops::StrongIntRange< ColumnEntryIndex > ColumnEntryRange
Definition base_types.h:56
IndexRange(Index a, Index b) -> IndexRange< Index >
Additional deduction guide.
util_intops::StrongVector< ElementIndex, BaseInt > ElementToIntVector
Definition base_types.h:64
util_intops::StrongVector< SubsetIndex, bool > SubsetBoolVector
Definition base_types.h:71
util_intops::StrongVector< SubsetIndex, SparseColumn > SparseColumnView
Views of the sparse vectors.
Definition base_types.h:68
util_intops::StrongVector< ElementIndex, bool > ElementBoolVector
Definition base_types.h:72
CompressedStrongVectorIterator< RowEntryIndex, SubsetIndex > CompressedRowIterator
Definition base_types.h:269
util_intops::StrongVector< SubsetIndex, CompressedColumn > CompressedColumnView
Definition base_types.h:262
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
Definition base_types.h:32
util_intops::StrongIntRange< ElementIndex > ElementRange
Definition base_types.h:55
util_intops::StrongVector< ColumnEntryIndex, ElementIndex > SparseColumn
Definition base_types.h:61
util_intops::StrongVector< ElementIndex, SparseRow > SparseRowView
Definition base_types.h:69
#define DEFINE_STRONG_INT_TYPE(type_name, value_type)
IndexRangeIterator< Index > const_iterator
Definition base_types.h:281
IndexRangeIterator< Index > iterator
Definition base_types.h:280
IndexRange(Index start, Index end)
Definition base_types.h:284
iterator begin()
For range-for loops.
Definition base_types.h:292
const_iterator begin() const
For const range-for loops.
Definition base_types.h:296
const_iterator end() const
Definition base_types.h:297