Files
ortools-clone/ortools/lp_data/scattered_vector.h
2025-02-05 18:11:16 +01:00

212 lines
7.4 KiB
C++

// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OR_TOOLS_LP_DATA_SCATTERED_VECTOR_H_
#define OR_TOOLS_LP_DATA_SCATTERED_VECTOR_H_
#include <cstddef>
#include <vector>
#include "absl/log/check.h"
#include "ortools/lp_data/lp_types.h"
#include "ortools/util/bitset.h"
namespace operations_research {
namespace glop {
// A class representing an entry of a scattered vector. The i-th nonzero
// element of the vector is assumed to be located at indices[i] and its value is
// coefficients[indices[i]], i.e., coefficients is a dense array.
template <typename IndexType>
class ScatteredVectorEntry {
public:
using Index = IndexType;
Index index() const { return index_[i_.value()]; }
Fractional coefficient() const {
return coefficient_[index_[i_.value()].value()];
}
protected:
ScatteredVectorEntry(const Index* indices, const Fractional* coefficients,
EntryIndex i)
: i_(i), index_(indices), coefficient_(coefficients) {}
EntryIndex i_;
const Index* index_;
const Fractional* coefficient_;
};
// A simple struct that contains a DenseVector and its non-zero indices.
// TODO(user): This should be changed from struct to class.
template <typename Index,
typename Iterator = VectorIterator<ScatteredVectorEntry<Index>>>
struct ScatteredVector {
StrictITIVector<Index, Fractional> values;
// This can be left empty in which case we just have the dense representation
// above. Otherwise, it should always be a superset of the actual non-zeros.
bool non_zeros_are_sorted = false;
std::vector<Index> non_zeros;
// Temporary vector used in some sparse computation on the ScatteredVector.
// True indicates a possible non-zero value. Note that its state is not always
// consistent.
Bitset64<Index> is_non_zero;
// In many cases there is a choice between treating the ScatteredVector as
// dense or as sparse. By default, dense algorithms are used when the
// proportion of non-zero entries is greater than
// kDefaultRatioForUsingDenseIteration.
//
// TODO(user): The constant should depend on what algorithm is used. Clearing
// a dense vector is a lot more efficient than doing more complex stuff. Clean
// this up by extracting all the currently used constants in one place with
// meaningful names.
constexpr static const double kDefaultRatioForUsingDenseIteration = 0.8;
Fractional operator[](Index index) const { return values[index]; }
Fractional& operator[](Index index) { return values[index]; }
// The iterator syntax for (auto entry : v) where v is a ScatteredVector only
// works when non_zeros is populated (i.e., when the vector is treated as
// sparse).
Iterator begin() const {
DCHECK(!non_zeros.empty() || IsAllZero(values));
return Iterator(this->non_zeros.data(), this->values.data(), EntryIndex(0));
}
Iterator end() const {
return Iterator(this->non_zeros.data(), this->values.data(),
EntryIndex(non_zeros.size()));
}
// Add the given value to the vector at position index. This interface
// encapsulates usage of the "is_non_zero" array, which should not be
// explicitly referenced outside of this struct.
void Add(Index index, Fractional value) {
values[index] += value;
if (!is_non_zero[index] && value != 0.0) {
is_non_zero.Set(index);
non_zeros.push_back(index);
non_zeros_are_sorted = false;
}
}
// Sorting the non-zeros is not always needed, but it allows us to have
// exactly the same behavior while using a sparse iteration or a dense one. So
// we always do it after a Solve().
void SortNonZerosIfNeeded() {
if (!non_zeros_are_sorted) {
std::sort(non_zeros.begin(), non_zeros.end());
non_zeros_are_sorted = true;
}
}
// Returns true if it is more advantageous to use a dense iteration rather
// than using the non-zeros positions.
bool ShouldUseDenseIteration(
double ratio_for_using_dense_representation) const {
if (non_zeros.empty()) return true;
return static_cast<double>(non_zeros.size()) >
ratio_for_using_dense_representation *
static_cast<double>(values.size().value());
}
bool ShouldUseDenseIteration() const {
return ShouldUseDenseIteration(kDefaultRatioForUsingDenseIteration);
}
// Efficiently clears the is_non_zero vector.
void ClearSparseMask() {
if (ShouldUseDenseIteration()) {
is_non_zero.ClearAndResize(values.size());
} else {
is_non_zero.Resize(values.size());
for (const Index index : non_zeros) {
is_non_zero.ClearBucket(index);
}
DCHECK(is_non_zero.IsAllFalse());
}
}
// Update the is_non_zero vector to be consistent with the non_zeros vector.
void RepopulateSparseMask() {
ClearSparseMask();
for (const Index index : non_zeros) {
// is_non_zero[index] = true;
is_non_zero.Set(index);
}
}
// If the proportion of non-zero entries is too large, clears the vector of
// non-zeros.
void ClearNonZerosIfTooDense(double ratio_for_using_dense_representation) {
if (ShouldUseDenseIteration(ratio_for_using_dense_representation)) {
ClearSparseMask();
non_zeros.clear();
}
}
void ClearNonZerosIfTooDense() {
ClearNonZerosIfTooDense(kDefaultRatioForUsingDenseIteration);
}
// Returns an overestimate of the number of non-zeros. This is actually
// exact for sparse vector, or the full size otherwise.
size_t NumNonZerosEstimate() const {
return non_zeros.empty() ? values.size().value() : non_zeros.size();
}
};
// Specializations used in the code.
class ScatteredColumnEntry : public ScatteredVectorEntry<RowIndex> {
public:
// Returns the row of the current entry.
RowIndex row() const { return index(); }
protected:
ScatteredColumnEntry(const RowIndex* indices, const Fractional* coefficients,
EntryIndex i)
: ScatteredVectorEntry<RowIndex>(indices, coefficients, i) {}
};
class ScatteredRowEntry : public ScatteredVectorEntry<ColIndex> {
public:
// Returns the column of the current entry.
ColIndex column() const { return index(); }
protected:
ScatteredRowEntry(const ColIndex* indices, const Fractional* coefficients,
EntryIndex i)
: ScatteredVectorEntry<ColIndex>(indices, coefficients, i) {}
};
using ScatteredColumnIterator = VectorIterator<ScatteredColumnEntry>;
using ScatteredRowIterator = VectorIterator<ScatteredRowEntry>;
struct ScatteredColumn
: public ScatteredVector<RowIndex, ScatteredColumnIterator> {};
struct ScatteredRow : public ScatteredVector<ColIndex, ScatteredRowIterator> {};
inline const ScatteredRow& TransposedView(const ScatteredColumn& c) {
return reinterpret_cast<const ScatteredRow&>(c);
}
inline const ScatteredColumn& TransposedView(const ScatteredRow& r) {
return reinterpret_cast<const ScatteredColumn&>(r);
}
} // namespace glop
} // namespace operations_research
#endif // OR_TOOLS_LP_DATA_SCATTERED_VECTOR_H_