OR-Tools  9.6
lp_data/sparse_vector.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 // Classes to represent sparse vectors.
15 //
16 // The following are very good references for terminology, data structures,
17 // and algorithms:
18 //
19 // I.S. Duff, A.M. Erisman and J.K. Reid, "Direct Methods for Sparse Matrices",
20 // Clarendon, Oxford, UK, 1987, ISBN 0-19-853421-3,
21 // http://www.amazon.com/dp/0198534213.
22 //
23 //
24 // T.A. Davis, "Direct methods for Sparse Linear Systems", SIAM, Philadelphia,
25 // 2006, ISBN-13: 978-0-898716-13, http://www.amazon.com/dp/0898716136.
26 //
27 //
28 // Both books also contain a wealth of references.
29 
30 #ifndef OR_TOOLS_LP_DATA_SPARSE_VECTOR_H_
31 #define OR_TOOLS_LP_DATA_SPARSE_VECTOR_H_
32 
33 #include <algorithm>
34 #include <cstring>
35 #include <memory>
36 #include <string>
37 #include <utility>
38 
39 #include "absl/strings/str_format.h"
41 #include "ortools/base/logging.h" // for CHECK*
46 
47 namespace operations_research {
48 namespace glop {
49 
50 template <typename IndexType>
51 class SparseVectorEntry;
52 
53 // --------------------------------------------------------
54 // SparseVector
55 // --------------------------------------------------------
56 // This class allows to store a vector taking advantage of its sparsity.
57 // Space complexity is in O(num_entries).
58 // In the current implementation, entries are stored in a first-in order (order
59 // of SetCoefficient() calls) when they are added; then the "cleaning" process
60 // sorts them by index (and duplicates are removed: the last entry takes
61 // precedence).
62 // Many methods assume that the entries are sorted by index and without
63 // duplicates, and DCHECK() that.
64 //
65 // Default copy construction is fully supported.
66 //
67 // This class uses strong integer types (i.e. no implicit cast to/from other
68 // integer types) for both:
69 // - the index of entries (eg. SparseVector<RowIndex> is a SparseColumn,
70 // see ./sparse_column.h).
71 // - the *internal* indices of entries in the internal storage, which is an
72 // entirely different type: EntryType.
73 // This class can be extended with a custom iterator/entry type for the
74 // iterator-based API. This can be used to extend the interface with additional
75 // methods for the entries returned by the iterators; for an example of such
76 // extension, see SparseColumnEntry in sparse_column.h. The custom entries and
77 // iterators should be derived from SparseVectorEntry and SparseVectorIterator,
78 // or at least provide the same public and protected interface.
79 //
80 // TODO(user): un-expose this type to client; by getting rid of the
81 // index-based APIs and leveraging iterator-based APIs; if possible.
82 template <typename IndexType,
83  typename IteratorType = VectorIterator<SparseVectorEntry<IndexType>>>
84 class SparseVector {
85  public:
86  typedef IndexType Index;
87 
90 
91  using Iterator = IteratorType;
92  using Entry = typename Iterator::Entry;
93 
95 
96  // NOTE(user): STL uses the expensive copy constructor when relocating
97  // elements of a vector, unless the move constructor exists *and* it is marked
98  // as noexcept. However, the noexcept annotation is banned by the style guide,
99  // and the only way to get it is by using the default move constructor and
100  // assignment operator generated by the compiler.
101  SparseVector(const SparseVector& other);
102 #if !defined(_MSC_VER)
103  SparseVector(SparseVector&& other) = default;
104 #endif
105 
107 #if !defined(_MSC_VER)
108  SparseVector& operator=(SparseVector&& other) = default;
109 #endif
110 
111  // Read-only API for a given SparseVector entry. The typical way for a
112  // client to use this is to use the natural range iteration defined by the
113  // Iterator class below:
114  // SparseVector<int> v;
115  // ...
116  // for (const SparseVector<int>::Entry e : v) {
117  // LOG(INFO) << "Index: " << e.index() << ", Coeff: " << e.coefficient();
118  // }
119  //
120  // Note that this can only be used when the vector has no duplicates.
121  //
122  // Note(user): using either "const SparseVector<int>::Entry&" or
123  // "const SparseVector<int>::Entry" yields the exact same performance on the
124  // netlib, thus we recommend to use the latter version, for consistency.
125  Iterator begin() const;
126  Iterator end() const;
127 
128  // Clears the vector, i.e. removes all entries.
129  void Clear();
130 
131  // Clears the vector and releases the memory it uses.
133 
134  // Reserve the underlying storage for the given number of entries.
135  void Reserve(EntryIndex new_capacity);
136 
137  // Returns true if the vector is empty.
138  bool IsEmpty() const;
139 
140  // Cleans the vector, i.e. removes zero-values entries, removes duplicates
141  // entries and sorts remaining entries in increasing index order.
142  // Runs in O(num_entries * log(num_entries)).
143  void CleanUp();
144 
145  // Returns true if the entries of this SparseVector are in strictly increasing
146  // index order and if the vector contains no duplicates nor zero coefficients.
147  // Runs in O(num_entries). It is not const because it modifies
148  // possibly_contains_duplicates_.
149  bool IsCleanedUp() const;
150 
151  // Swaps the content of this sparse vector with the one passed as argument.
152  // Works in O(1).
153  void Swap(SparseVector* other);
154 
155  // Populates the current vector from sparse_vector.
156  // Runs in O(num_entries).
157  void PopulateFromSparseVector(const SparseVector& sparse_vector);
158 
159  // Populates the current vector from dense_vector.
160  // Runs in O(num_indices_in_dense_vector).
161  void PopulateFromDenseVector(const DenseVector& dense_vector);
162 
163  // Appends all entries from sparse_vector to the current vector; the indices
164  // of the appended entries are increased by offset. If the current vector
165  // already has a value at an index changed by this method, this value is
166  // overwritten with the value from sparse_vector.
167  // Note that while offset may be negative itself, the indices of all entries
168  // after applying the offset must be non-negative.
169  void AppendEntriesWithOffset(const SparseVector& sparse_vector, Index offset);
170 
171  // Returns true when the vector contains no duplicates. Runs in
172  // O(max_index + num_entries), max_index being the largest index in entry.
173  // This method allocates (and deletes) a Boolean array of size max_index.
174  // Note that we use a mutable Boolean to make subsequent call runs in O(1).
175  bool CheckNoDuplicates() const;
176 
177  // Same as CheckNoDuplicates() except it uses a reusable boolean vector
178  // to make the code more efficient. Runs in O(num_entries).
179  // Note that boolean_vector should be initialized to false before calling this
180  // method; It will remain equal to false after calls to CheckNoDuplicates().
181  // Note that we use a mutable Boolean to make subsequent call runs in O(1).
182  bool CheckNoDuplicates(StrictITIVector<Index, bool>* boolean_vector) const;
183 
184  // Defines the coefficient at index, i.e. vector[index] = value;
186 
187  // Removes an entry from the vector if present. The order of the other entries
188  // is preserved. Runs in O(num_entries).
190 
191  // Sets to 0.0 (i.e. remove) all entries whose fabs() is lower or equal to
192  // the given threshold.
194 
195  // Same as RemoveNearZeroEntries, but the entry magnitude of each row is
196  // multiplied by weights[row] before being compared with threshold.
198  const DenseVector& weights);
199 
200  // Moves the entry with given Index to the first position in the vector. If
201  // the entry is not present, nothing happens.
203 
204  // Moves the entry with given Index to the last position in the vector. If
205  // the entry is not present, nothing happens.
207 
208  // Multiplies all entries by factor.
209  // i.e. entry.coefficient *= factor.
211 
212  // Multiplies all entries by its corresponding factor,
213  // i.e. entry.coefficient *= factors[entry.index].
214  void ComponentWiseMultiply(const DenseVector& factors);
215 
216  // Divides all entries by factor.
217  // i.e. entry.coefficient /= factor.
219 
220  // Divides all entries by its corresponding factor,
221  // i.e. entry.coefficient /= factors[entry.index].
222  void ComponentWiseDivide(const DenseVector& factors);
223 
224  // Populates a dense vector from the sparse vector.
225  // Runs in O(num_indices) as the dense vector values have to be reset to 0.0.
226  void CopyToDenseVector(Index num_indices, DenseVector* dense_vector) const;
227 
228  // Populates a dense vector from the permuted sparse vector.
229  // Runs in O(num_indices) as the dense vector values have to be reset to 0.0.
231  Index num_indices,
232  DenseVector* dense_vector) const;
233 
234  // Performs the operation dense_vector += multiplier * this.
235  // This is known as multiply-accumulate or (fused) multiply-add.
237  DenseVector* dense_vector) const;
238 
239  // WARNING: BOTH vectors (the current and the destination) MUST be "clean",
240  // i.e. sorted and without duplicates.
241  // Performs the operation accumulator_vector += multiplier * this, removing
242  // a given index which must be in both vectors, and pruning new entries whose
243  // absolute value are under the given drop_tolerance.
245  Fractional multiplier, Index removed_common_index,
246  Fractional drop_tolerance, SparseVector* accumulator_vector) const;
247 
248  // Same as AddMultipleToSparseVectorAndDeleteCommonIndex() but instead of
249  // deleting the common index, leave it unchanged.
251  Fractional multiplier, Index removed_common_index,
252  Fractional drop_tolerance, SparseVector* accumulator_vector) const;
253 
254  // Applies the index permutation to all entries: index = index_perm[index];
255  void ApplyIndexPermutation(const IndexPermutation& index_perm);
256 
257  // Same as ApplyIndexPermutation but deletes the index if index_perm[index]
258  // is negative.
260 
261  // Removes the entries for which index_perm[index] is non-negative and appends
262  // them to output. Note that the index of the entries are NOT permuted.
263  void MoveTaggedEntriesTo(const IndexPermutation& index_perm,
264  SparseVector* output);
265 
266  // Returns the coefficient at position index.
267  // Call with care: runs in O(number-of-entries) as entries may not be sorted.
269 
270  // Note this method can only be used when the vector has no duplicates.
271  EntryIndex num_entries() const {
272  DCHECK(CheckNoDuplicates());
273  return EntryIndex(num_entries_);
274  }
275 
276  // Returns the first entry's index and coefficient; note that 'first' doesn't
277  // mean 'entry with the smallest index'.
278  // Runs in O(1).
279  // Note this method can only be used when the vector has no duplicates.
281  DCHECK(CheckNoDuplicates());
282  return GetIndex(EntryIndex(0));
283  }
285  DCHECK(CheckNoDuplicates());
286  return GetCoefficient(EntryIndex(0));
287  }
288 
289  // Like GetFirst*, but for the last entry.
290  Index GetLastIndex() const {
291  DCHECK(CheckNoDuplicates());
292  return GetIndex(num_entries() - 1);
293  }
295  DCHECK(CheckNoDuplicates());
296  return GetCoefficient(num_entries() - 1);
297  }
298 
299  // Allows to loop over the entry indices like this:
300  // for (const EntryIndex i : sparse_vector.AllEntryIndices()) { ... }
301  // TODO(user): consider removing this, in favor of the natural range
302  // iteration.
304  return ::util::IntegerRange<EntryIndex>(EntryIndex(0), num_entries_);
305  }
306 
307  // Returns true if this vector is exactly equal to the given one, i.e. all its
308  // index indices and coefficients appear in the same order and are equal.
309  bool IsEqualTo(const SparseVector& other) const;
310 
311  // An exhaustive, pretty-printed listing of the entries, in their
312  // internal order. a.DebugString() == b.DebugString() iff a.IsEqualTo(b).
313  std::string DebugString() const;
314 
315  protected:
316  // Adds a new entry to the sparse vector, growing the internal buffer if
317  // needed. It does not set may_contain_duplicates_ to true.
319  DCHECK_GE(index, 0);
320  // Grow the internal storage if there is no space left for the new entry. We
321  // increase the size to max(4, 1.5*current capacity).
322  if (num_entries_ == capacity_) {
323  // Reserve(capacity_ == 0 ? EntryIndex(4)
324  // : EntryIndex(2 * capacity_.value()));
325  Reserve(capacity_ == 0 ? EntryIndex(4)
326  : EntryIndex(2 * capacity_.value()));
327  DCHECK_LT(num_entries_, capacity_);
328  }
329  const EntryIndex new_entry_index = num_entries_;
330  ++num_entries_;
331  MutableIndex(new_entry_index) = index;
332  MutableCoefficient(new_entry_index) = value;
333  }
334 
335  // Resizes the sparse vector to a smaller size, without re-allocating the
336  // internal storage.
337  void ResizeDown(EntryIndex new_size) {
338  DCHECK_GE(new_size, 0);
339  DCHECK_LE(new_size, num_entries_);
340  num_entries_ = new_size;
341  }
342 
343  // Read-only access to the indices and coefficients of the entries of the
344  // sparse vector.
345  Index GetIndex(EntryIndex i) const {
346  DCHECK_GE(i, 0);
347  DCHECK_LT(i, num_entries_);
348  return index_[i.value()];
349  }
350  Fractional GetCoefficient(EntryIndex i) const {
351  DCHECK_GE(i, 0);
352  DCHECK_LT(i, num_entries_);
353  return coefficient_[i.value()];
354  }
355 
356  // Mutable access to the indices and coefficients of the entries of the sparse
357  // vector.
358  Index& MutableIndex(EntryIndex i) {
359  DCHECK_GE(i, 0);
360  DCHECK_LT(i, num_entries_);
361  return index_[i.value()];
362  }
364  DCHECK_GE(i, 0);
365  DCHECK_LT(i, num_entries_);
366  return coefficient_[i.value()];
367  }
368 
369  // The internal storage of the sparse vector. Both the indices and the
370  // coefficients are stored in the same buffer; the first
371  // sizeof(Index)*capacity_ bytes are used for storing the indices, the
372  // following sizeof(Fractional)*capacity_ bytes contain the values. This
373  // representation ensures that for small vectors, both the indices and the
374  // coefficients are in the same page/cache line.
375  // We use a single buffer for both arrays. The amount of data copied during
376  // relocations is the same in both cases, and it is much smaller than the cost
377  // of an additional allocation - especially when the vectors are small.
378  // Moreover, using two separate vectors/buffers would mean that even small
379  // vectors would be spread across at least two different cache lines.
380  std::unique_ptr<char[]> buffer_;
381  EntryIndex num_entries_;
382  EntryIndex capacity_;
383 
384  // Pointers to the first elements of the index and coefficient arrays.
387 
388  // This is here to speed up the CheckNoDuplicates() methods and is mutable
389  // so we can perform checks on const argument.
391 
392  private:
393  // Actual implementation of AddMultipleToSparseVectorAndDeleteCommonIndex()
394  // and AddMultipleToSparseVectorAndIgnoreCommonIndex() which is shared.
395  void AddMultipleToSparseVectorInternal(
396  bool delete_common_index, Fractional multiplier, Index common_index,
397  Fractional drop_tolerance, SparseVector* accumulator_vector) const;
398 };
399 
400 // --------------------------------------------------------
401 // SparseVectorEntry
402 // --------------------------------------------------------
403 
404 // A reference-like class that points to a certain element of a sparse data
405 // structure that stores its elements in two parallel arrays. The main purpose
406 // of the entry class is to support implementation of iterator objects over the
407 // sparse data structure.
408 // Note that the entry object does not own the data, and it is valid only as
409 // long as the underlying sparse data structure; it may also be invalidated if
410 // the underlying sparse data structure is modified.
411 template <typename IndexType>
413  public:
414  using Index = IndexType;
415 
416  Index index() const { return index_[i_.value()]; }
417  Fractional coefficient() const { return coefficient_[i_.value()]; }
418 
419  protected:
420  // Creates the sparse vector entry from the given base pointers and the index.
421  // We accept the low-level data structures rather than a SparseVector
422  // reference to make it possible to use the SparseVectorEntry and
423  // SparseVectorIterator classes also for other data structures using the same
424  // internal data representation.
425  // Note that the constructor is intentionally made protected, so that the
426  // entry can be created only as a part of the construction of an iterator over
427  // a sparse data structure.
429  EntryIndex i)
430  : i_(i), index_(indices), coefficient_(coefficients) {}
431 
432  // The index of the sparse vector entry represented by this object.
433  EntryIndex i_;
434  // The index and coefficient arrays of the sparse vector.
435  // NOTE(user): Keeping directly the index and the base pointers gives the
436  // best performance with a tiny margin of the options:
437  // 1. keep the base pointers and an index of the current entry,
438  // 2. keep pointers to the current index and the current coefficient and
439  // increment both when moving the iterator.
440  // 3. keep a pointer to the sparse vector object and the index of the current
441  // entry.
442  const Index* index_;
444 };
445 
446 template <typename IndexType, typename IteratorType>
448  return Iterator(this->index_, this->coefficient_, EntryIndex(0));
449 }
450 
451 template <typename IndexType, typename IteratorType>
453  return Iterator(this->index_, this->coefficient_, num_entries_);
454 }
455 
456 // --------------------------------------------------------
457 // SparseVector implementation
458 // --------------------------------------------------------
459 template <typename IndexType, typename IteratorType>
461  : num_entries_(0),
462  capacity_(0),
463  index_(nullptr),
464  coefficient_(nullptr),
465  may_contain_duplicates_(false) {}
466 
467 template <typename IndexType, typename IteratorType>
469  PopulateFromSparseVector(other);
470 }
471 
472 template <typename IndexType, typename IteratorType>
475  PopulateFromSparseVector(other);
476  return *this;
477 }
478 
479 template <typename IndexType, typename IteratorType>
481  num_entries_ = EntryIndex(0);
482  may_contain_duplicates_ = false;
483 }
484 
485 template <typename IndexType, typename IteratorType>
487  capacity_ = EntryIndex(0);
488  num_entries_ = EntryIndex(0);
489  index_ = nullptr;
490  coefficient_ = nullptr;
491  buffer_.reset();
492  may_contain_duplicates_ = false;
493 }
494 
495 template <typename IndexType, typename IteratorType>
496 void SparseVector<IndexType, IteratorType>::Reserve(EntryIndex new_capacity) {
497  if (new_capacity <= capacity_) return;
498  // Round up the capacity to a multiple of four. This way, the start of the
499  // coefficient array will be aligned to 16-bytes, provided that the buffer
500  // used for storing the data is aligned in that way.
501  if (new_capacity.value() & 3) {
502  new_capacity += EntryIndex(4 - (new_capacity.value() & 3));
503  }
504 
505  const size_t index_buffer_size = new_capacity.value() * sizeof(Index);
506  const size_t value_buffer_size = new_capacity.value() * sizeof(Fractional);
507  const size_t new_buffer_size = index_buffer_size + value_buffer_size;
508  std::unique_ptr<char[]> new_buffer(new char[new_buffer_size]);
509  IndexType* const new_index = reinterpret_cast<Index*>(new_buffer.get());
510  Fractional* const new_coefficient =
511  reinterpret_cast<Fractional*>(new_index + new_capacity.value());
512 
513  // Avoid copying the data if the vector is empty.
514  if (num_entries_ > 0) {
515  // NOTE(user): We use memmove instead of std::copy, because the latter
516  // leads to naive copying code when used with strong ints (a loop that
517  // copies a single 32-bit value in each iteration), and as of 06/2016,
518  // memmove is 3-4x faster on Haswell.
519  std::memmove(new_index, index_, sizeof(IndexType) * num_entries_.value());
520  std::memmove(new_coefficient, coefficient_,
521  sizeof(Fractional) * num_entries_.value());
522  }
523  std::swap(buffer_, new_buffer);
524  index_ = new_index;
525  coefficient_ = new_coefficient;
526  capacity_ = new_capacity;
527 }
528 
529 template <typename IndexType, typename IteratorType>
531  return num_entries_ == EntryIndex(0);
532 }
533 
534 template <typename IndexType, typename IteratorType>
536  std::swap(buffer_, other->buffer_);
537  std::swap(num_entries_, other->num_entries_);
538  std::swap(capacity_, other->capacity_);
539  std::swap(may_contain_duplicates_, other->may_contain_duplicates_);
540  std::swap(index_, other->index_);
541  std::swap(coefficient_, other->coefficient_);
542 }
543 
544 template <typename IndexType, typename IteratorType>
546  // TODO(user): Implement in-place sorting of the entries and cleanup. The
547  // current version converts the data to an array-of-pairs representation that
548  // can be sorted easily with std::stable_sort, and the converts the sorted
549  // data back to the struct-of-arrays implementation.
550  // The current version is ~20% slower than the in-place sort on the
551  // array-of-struct representation. It is not visible on GLOP benchmarks, but
552  // it increases peak memory usage by ~8%.
553  // Implementing in-place search will require either implementing a custom
554  // sorting code, or custom iterators that abstract away the internal
555  // representation.
556  std::vector<std::pair<Index, Fractional>> entries;
557  entries.reserve(num_entries_.value());
558  for (EntryIndex i(0); i < num_entries_; ++i) {
559  entries.emplace_back(GetIndex(i), GetCoefficient(i));
560  }
561  std::stable_sort(
562  entries.begin(), entries.end(),
563  [](const std::pair<Index, Fractional>& a,
564  const std::pair<Index, Fractional>& b) { return a.first < b.first; });
565 
566  EntryIndex new_size(0);
567  for (int i = 0; i < num_entries_; ++i) {
568  const std::pair<Index, Fractional> entry = entries[i];
569  if (entry.second == 0.0) continue;
570  if (i + 1 == num_entries_ || entry.first != entries[i + 1].first) {
571  MutableIndex(new_size) = entry.first;
572  MutableCoefficient(new_size) = entry.second;
573  ++new_size;
574  }
575  }
576  ResizeDown(new_size);
577  may_contain_duplicates_ = false;
578 }
579 
580 template <typename IndexType, typename IteratorType>
582  Index previous_index(-1);
583  for (const EntryIndex i : AllEntryIndices()) {
584  const Index index = GetIndex(i);
585  if (index <= previous_index || GetCoefficient(i) == 0.0) return false;
586  previous_index = index;
587  }
588  may_contain_duplicates_ = false;
589  return true;
590 }
591 
592 template <typename IndexType, typename IteratorType>
594  const SparseVector& sparse_vector) {
595  // Clear the sparse vector before reserving the new capacity. If we didn't do
596  // this, Reserve would have to copy the current contents of the vector if it
597  // allocated a new buffer. This would be wasteful, since we overwrite it in
598  // the next step anyway.
599  Clear();
600  Reserve(sparse_vector.capacity_);
601  // If there are no entries, then sparse_vector.index_ or .coefficient_
602  // may be nullptr or invalid, and accessing them in memmove is UB,
603  // even if the moved size is zero.
604  if (sparse_vector.num_entries_ > 0) {
605  // NOTE(user): Using a single memmove would be slightly faster, but it
606  // would not work correctly if this already had a greater capacity than
607  // sparse_vector, because the coefficient_ pointer would be positioned
608  // incorrectly.
609  std::memmove(index_, sparse_vector.index_,
610  sizeof(Index) * sparse_vector.num_entries_.value());
611  std::memmove(coefficient_, sparse_vector.coefficient_,
612  sizeof(Fractional) * sparse_vector.num_entries_.value());
613  }
614  num_entries_ = sparse_vector.num_entries_;
615  may_contain_duplicates_ = sparse_vector.may_contain_duplicates_;
616 }
617 
618 template <typename IndexType, typename IteratorType>
620  const DenseVector& dense_vector) {
621  Clear();
622  const Index num_indices(dense_vector.size());
623  for (Index index(0); index < num_indices; ++index) {
624  if (dense_vector[index] != 0.0) {
625  SetCoefficient(index, dense_vector[index]);
626  }
627  }
628  may_contain_duplicates_ = false;
629 }
630 
631 template <typename IndexType, typename IteratorType>
633  const SparseVector& sparse_vector, Index offset) {
634  for (const EntryIndex i : sparse_vector.AllEntryIndices()) {
635  const Index new_index = offset + sparse_vector.GetIndex(i);
636  DCHECK_GE(new_index, 0);
637  AddEntry(new_index, sparse_vector.GetCoefficient(i));
638  }
639  may_contain_duplicates_ = true;
640 }
641 
642 template <typename IndexType, typename IteratorType>
644  StrictITIVector<IndexType, bool>* boolean_vector) const {
645  RETURN_VALUE_IF_NULL(boolean_vector, false);
646  // Note(user): Using num_entries() or any function that call
647  // CheckNoDuplicates() again will cause an infinite loop!
648  if (!may_contain_duplicates_ || num_entries_ <= 1) return true;
649 
650  // Update size if needed.
651  const Index max_index =
652  *std::max_element(index_, index_ + num_entries_.value());
653  if (boolean_vector->size() <= max_index) {
654  boolean_vector->resize(max_index + 1, false);
655  }
656 
657  may_contain_duplicates_ = false;
658  for (const EntryIndex i : AllEntryIndices()) {
659  const Index index = GetIndex(i);
660  if ((*boolean_vector)[index]) {
661  may_contain_duplicates_ = true;
662  break;
663  }
664  (*boolean_vector)[index] = true;
665  }
666 
667  // Reset boolean_vector to false.
668  for (const EntryIndex i : AllEntryIndices()) {
669  (*boolean_vector)[GetIndex(i)] = false;
670  }
671  return !may_contain_duplicates_;
672 }
673 
674 template <typename IndexType, typename IteratorType>
676  // Using num_entries() or any function in that will call CheckNoDuplicates()
677  // again will cause an infinite loop!
678  if (!may_contain_duplicates_ || num_entries_ <= 1) return true;
679  StrictITIVector<Index, bool> boolean_vector;
680  return CheckNoDuplicates(&boolean_vector);
681 }
682 
683 // Do not filter out zero values, as a zero value can be added to reset a
684 // previous value. Zero values and duplicates will be removed by CleanUp.
685 template <typename IndexType, typename IteratorType>
687  Fractional value) {
688  AddEntry(index, value);
689  may_contain_duplicates_ = true;
690 }
691 
692 template <typename IndexType, typename IteratorType>
694  DCHECK(CheckNoDuplicates());
695  EntryIndex i(0);
696  const EntryIndex end(num_entries());
697  while (i < end && GetIndex(i) != index) {
698  ++i;
699  }
700  if (i == end) return;
701  const int num_moved_entries = (num_entries_ - i).value() - 1;
702  std::memmove(index_ + i.value(), index_ + i.value() + 1,
703  sizeof(Index) * num_moved_entries);
704  std::memmove(coefficient_ + i.value(), coefficient_ + i.value() + 1,
705  sizeof(Fractional) * num_moved_entries);
706  --num_entries_;
707 }
708 
709 template <typename IndexType, typename IteratorType>
711  Fractional threshold) {
712  DCHECK(CheckNoDuplicates());
713  EntryIndex new_index(0);
714  for (const EntryIndex i : AllEntryIndices()) {
715  const Fractional magnitude = fabs(GetCoefficient(i));
716  if (magnitude > threshold) {
717  MutableIndex(new_index) = GetIndex(i);
718  MutableCoefficient(new_index) = GetCoefficient(i);
719  ++new_index;
720  }
721  }
722  ResizeDown(new_index);
723 }
724 
725 template <typename IndexType, typename IteratorType>
727  Fractional threshold, const DenseVector& weights) {
728  DCHECK(CheckNoDuplicates());
729  EntryIndex new_index(0);
730  for (const EntryIndex i : AllEntryIndices()) {
731  if (fabs(GetCoefficient(i)) * weights[GetIndex(i)] > threshold) {
732  MutableIndex(new_index) = GetIndex(i);
733  MutableCoefficient(new_index) = GetCoefficient(i);
734  ++new_index;
735  }
736  }
737  ResizeDown(new_index);
738 }
739 
740 template <typename IndexType, typename IteratorType>
742  Index index) {
743  DCHECK(CheckNoDuplicates());
744  for (const EntryIndex i : AllEntryIndices()) {
745  if (GetIndex(i) == index) {
746  std::swap(MutableIndex(EntryIndex(0)), MutableIndex(i));
747  std::swap(MutableCoefficient(EntryIndex(0)), MutableCoefficient(i));
748  return;
749  }
750  }
751 }
752 
753 template <typename IndexType, typename IteratorType>
755  Index index) {
756  DCHECK(CheckNoDuplicates());
757  const EntryIndex last_entry = num_entries() - 1;
758  for (const EntryIndex i : AllEntryIndices()) {
759  if (GetIndex(i) == index) {
760  std::swap(MutableIndex(last_entry), MutableIndex(i));
761  std::swap(MutableCoefficient(last_entry), MutableCoefficient(i));
762  return;
763  }
764  }
765 }
766 
767 template <typename IndexType, typename IteratorType>
769  Fractional factor) {
770  for (const EntryIndex i : AllEntryIndices()) {
771  MutableCoefficient(i) *= factor;
772  }
773 }
774 
775 template <typename IndexType, typename IteratorType>
777  const DenseVector& factors) {
778  for (const EntryIndex i : AllEntryIndices()) {
779  MutableCoefficient(i) *= factors[GetIndex(i)];
780  }
781 }
782 
783 template <typename IndexType, typename IteratorType>
785  Fractional factor) {
786  for (const EntryIndex i : AllEntryIndices()) {
787  MutableCoefficient(i) /= factor;
788  }
789 }
790 
791 template <typename IndexType, typename IteratorType>
793  const DenseVector& factors) {
794  for (const EntryIndex i : AllEntryIndices()) {
795  MutableCoefficient(i) /= factors[GetIndex(i)];
796  }
797 }
798 
799 template <typename IndexType, typename IteratorType>
801  Index num_indices, DenseVector* dense_vector) const {
802  RETURN_IF_NULL(dense_vector);
803  dense_vector->AssignToZero(num_indices);
804  for (const EntryIndex i : AllEntryIndices()) {
805  (*dense_vector)[GetIndex(i)] = GetCoefficient(i);
806  }
807 }
808 
809 template <typename IndexType, typename IteratorType>
811  const IndexPermutation& index_perm, Index num_indices,
812  DenseVector* dense_vector) const {
813  RETURN_IF_NULL(dense_vector);
814  dense_vector->AssignToZero(num_indices);
815  for (const EntryIndex i : AllEntryIndices()) {
816  (*dense_vector)[index_perm[GetIndex(i)]] = GetCoefficient(i);
817  }
818 }
819 
820 template <typename IndexType, typename IteratorType>
822  Fractional multiplier, DenseVector* dense_vector) const {
823  RETURN_IF_NULL(dense_vector);
824  if (multiplier == 0.0) return;
825  for (const EntryIndex i : AllEntryIndices()) {
826  (*dense_vector)[GetIndex(i)] += multiplier * GetCoefficient(i);
827  }
828 }
829 
830 template <typename IndexType, typename IteratorType>
833  Fractional multiplier, Index removed_common_index,
834  Fractional drop_tolerance, SparseVector* accumulator_vector) const {
835  AddMultipleToSparseVectorInternal(true, multiplier, removed_common_index,
836  drop_tolerance, accumulator_vector);
837 }
838 
839 template <typename IndexType, typename IteratorType>
842  Fractional multiplier, Index removed_common_index,
843  Fractional drop_tolerance, SparseVector* accumulator_vector) const {
844  AddMultipleToSparseVectorInternal(false, multiplier, removed_common_index,
845  drop_tolerance, accumulator_vector);
846 }
847 
848 template <typename IndexType, typename IteratorType>
850  bool delete_common_index, Fractional multiplier, Index common_index,
851  Fractional drop_tolerance, SparseVector* accumulator_vector) const {
852  // DCHECK that the input is correct.
853  DCHECK(IsCleanedUp());
854  DCHECK(accumulator_vector->IsCleanedUp());
855  DCHECK(CheckNoDuplicates());
856  DCHECK(accumulator_vector->CheckNoDuplicates());
857  DCHECK_NE(0.0, LookUpCoefficient(common_index));
858  DCHECK_NE(0.0, accumulator_vector->LookUpCoefficient(common_index));
859 
860  // Implementation notes: we create a temporary SparseVector "c" to hold the
861  // result. We call "a" the first vector (i.e. the current object, which will
862  // be multiplied by "multiplier"), and "b" the second vector (which will be
863  // swapped with "c" at the end to hold the result).
864  // We incrementally build c as: a * multiplier + b.
865  const SparseVector& a = *this;
866  const SparseVector& b = *accumulator_vector;
867  SparseVector c;
868  EntryIndex ia(0); // Index in the vector "a"
869  EntryIndex ib(0); // ... and "b"
870  EntryIndex ic(0); // ... and "c"
871  const EntryIndex size_a = a.num_entries();
872  const EntryIndex size_b = b.num_entries();
873  const int size_adjustment = delete_common_index ? -2 : 0;
874  const EntryIndex new_size_upper_bound = size_a + size_b + size_adjustment;
875  c.Reserve(new_size_upper_bound);
876  c.num_entries_ = new_size_upper_bound;
877  while ((ia < size_a) && (ib < size_b)) {
878  const Index index_a = a.GetIndex(ia);
879  const Index index_b = b.GetIndex(ib);
880  // Benchmarks done by fdid@ in 2012 showed that it was faster to put the
881  // "if" clauses in that specific order.
882  if (index_a == index_b) {
883  if (index_a != common_index) {
884  const Fractional a_coeff_mul = multiplier * a.GetCoefficient(ia);
885  const Fractional b_coeff = b.GetCoefficient(ib);
886  const Fractional sum = a_coeff_mul + b_coeff;
887 
888  // We do not want to leave near-zero entries.
889  // TODO(user): expose the tolerance used here.
890  if (std::abs(sum) > drop_tolerance) {
891  c.MutableIndex(ic) = index_a;
892  c.MutableCoefficient(ic) = sum;
893  ++ic;
894  }
895  } else if (!delete_common_index) {
896  c.MutableIndex(ic) = b.GetIndex(ib);
897  c.MutableCoefficient(ic) = b.GetCoefficient(ib);
898  ++ic;
899  }
900  ++ia;
901  ++ib;
902  } else if (index_a < index_b) {
903  c.MutableIndex(ic) = index_a;
904  c.MutableCoefficient(ic) = multiplier * a.GetCoefficient(ia);
905  ++ia;
906  ++ic;
907  } else { // index_b < index_a
908  c.MutableIndex(ic) = b.GetIndex(ib);
909  c.MutableCoefficient(ic) = b.GetCoefficient(ib);
910  ++ib;
911  ++ic;
912  }
913  }
914  while (ia < size_a) {
915  c.MutableIndex(ic) = a.GetIndex(ia);
916  c.MutableCoefficient(ic) = multiplier * a.GetCoefficient(ia);
917  ++ia;
918  ++ic;
919  }
920  while (ib < size_b) {
921  c.MutableIndex(ic) = b.GetIndex(ib);
922  c.MutableCoefficient(ic) = b.GetCoefficient(ib);
923  ++ib;
924  ++ic;
925  }
926  c.ResizeDown(ic);
927  c.may_contain_duplicates_ = false;
928  c.Swap(accumulator_vector);
929 }
930 
931 template <typename IndexType, typename IteratorType>
933  const IndexPermutation& index_perm) {
934  for (const EntryIndex i : AllEntryIndices()) {
935  MutableIndex(i) = index_perm[GetIndex(i)];
936  }
937 }
938 
939 template <typename IndexType, typename IteratorType>
941  const IndexPermutation& index_perm) {
942  EntryIndex new_index(0);
943  for (const EntryIndex i : AllEntryIndices()) {
944  const Index index = GetIndex(i);
945  if (index_perm[index] >= 0) {
946  MutableIndex(new_index) = index_perm[index];
947  MutableCoefficient(new_index) = GetCoefficient(i);
948  ++new_index;
949  }
950  }
951  ResizeDown(new_index);
952 }
953 
954 template <typename IndexType, typename IteratorType>
956  const IndexPermutation& index_perm, SparseVector* output) {
957  // Note that this function is called many times, so performance does matter
958  // and it is why we optimized the "nothing to do" case.
959  const EntryIndex end(num_entries_);
960  EntryIndex i(0);
961  while (true) {
962  if (i >= end) return; // "nothing to do" case.
963  if (index_perm[GetIndex(i)] >= 0) break;
964  ++i;
965  }
966  output->AddEntry(GetIndex(i), GetCoefficient(i));
967  for (EntryIndex j(i + 1); j < end; ++j) {
968  if (index_perm[GetIndex(j)] < 0) {
969  MutableIndex(i) = GetIndex(j);
970  MutableCoefficient(i) = GetCoefficient(j);
971  ++i;
972  } else {
973  output->AddEntry(GetIndex(j), GetCoefficient(j));
974  }
975  }
976  ResizeDown(i);
977 
978  // TODO(user): In the way we use this function, we know that will not
979  // happen, but it is better to be careful so we can check that properly in
980  // debug mode.
981  output->may_contain_duplicates_ = true;
982 }
983 
984 template <typename IndexType, typename IteratorType>
986  Index index) const {
987  Fractional value(0.0);
988  for (const EntryIndex i : AllEntryIndices()) {
989  if (GetIndex(i) == index) {
990  // Keep in mind the vector may contains several entries with the same
991  // index. In such a case the last one is returned.
992  // TODO(user): investigate whether an optimized version of
993  // LookUpCoefficient for "clean" columns yields speed-ups.
994  value = GetCoefficient(i);
995  }
996  }
997  return value;
998 }
999 
1000 template <typename IndexType, typename IteratorType>
1002  const SparseVector& other) const {
1003  // We do not take into account the mutable value may_contain_duplicates_.
1004  if (num_entries() != other.num_entries()) return false;
1005  for (const EntryIndex i : AllEntryIndices()) {
1006  if (GetIndex(i) != other.GetIndex(i)) return false;
1007  if (GetCoefficient(i) != other.GetCoefficient(i)) return false;
1008  }
1009  return true;
1010 }
1011 
1012 template <typename IndexType, typename IteratorType>
1014  std::string s;
1015  for (const EntryIndex i : AllEntryIndices()) {
1016  if (i != 0) s += ", ";
1017  absl::StrAppendFormat(&s, "[%d]=%g", GetIndex(i).value(),
1018  GetCoefficient(i));
1019  }
1020  return s;
1021 }
1022 
1023 } // namespace glop
1024 } // namespace operations_research
1025 
1026 #endif // OR_TOOLS_LP_DATA_SPARSE_VECTOR_H_
EntryIndex i_
Index index() const
IndexType Index
Fractional coefficient() const
const Fractional * coefficient_
SparseVectorEntry(const Index *indices, const Fractional *coefficients, EntryIndex i)
const Index * index_
void ComponentWiseMultiply(const DenseVector &factors)
void PopulateFromDenseVector(const DenseVector &dense_vector)
void MoveTaggedEntriesTo(const IndexPermutation &index_perm, SparseVector *output)
void ApplyIndexPermutation(const IndexPermutation &index_perm)
void CopyToDenseVector(Index num_indices, DenseVector *dense_vector) const
Fractional LookUpCoefficient(Index index) const
SparseVector & operator=(const SparseVector &other)
::util::IntegerRange< EntryIndex > AllEntryIndices() const
void AddMultipleToDenseVector(Fractional multiplier, DenseVector *dense_vector) const
void RemoveNearZeroEntriesWithWeights(Fractional threshold, const DenseVector &weights)
void PermutedCopyToDenseVector(const IndexPermutation &index_perm, Index num_indices, DenseVector *dense_vector) const
void AddMultipleToSparseVectorAndDeleteCommonIndex(Fractional multiplier, Index removed_common_index, Fractional drop_tolerance, SparseVector *accumulator_vector) const
SparseVector(SparseVector &&other)=default
void ComponentWiseDivide(const DenseVector &factors)
void ApplyPartialIndexPermutation(const IndexPermutation &index_perm)
SparseVector & operator=(SparseVector &&other)=default
void AddMultipleToSparseVectorAndIgnoreCommonIndex(Fractional multiplier, Index removed_common_index, Fractional drop_tolerance, SparseVector *accumulator_vector) const
void RemoveNearZeroEntries(Fractional threshold)
bool CheckNoDuplicates(StrictITIVector< Index, bool > *boolean_vector) const
void AddEntry(Index index, Fractional value)
bool IsEqualTo(const SparseVector &other) const
void AppendEntriesWithOffset(const SparseVector &sparse_vector, Index offset)
Fractional GetCoefficient(EntryIndex i) const
void SetCoefficient(Index index, Fractional value)
void PopulateFromSparseVector(const SparseVector &sparse_vector)
StrictITIVector< Index, Fractional > DenseVector
int64_t b
int64_t a
int64_t value
absl::Span< const double > coefficients
int index
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:269
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
Collection of objects used to extend the Constraint Solver library.
EntryIndex num_entries
#define RETURN_IF_NULL(x)
Definition: return_macros.h:20
#define RETURN_VALUE_IF_NULL(x, v)
Definition: return_macros.h:26
std::optional< int64_t > end