Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
lp_utils.h
Go to the documentation of this file.
1// Copyright 2010-2024 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// Basic utility functions on Fractional or row/column of Fractional.
15
16#ifndef OR_TOOLS_LP_DATA_LP_UTILS_H_
17#define OR_TOOLS_LP_DATA_LP_UTILS_H_
18
19#include <cmath>
20#include <cstdlib>
21
22#include "absl/log/check.h"
28
29namespace operations_research {
30namespace glop {
31
32// TODO(user): For some Fractional types, it may not gain much (or even nothing
33// if we are in infinite precision) to use this sum. A solution is to templatize
34// this class and specialize it to a normal sum for the Fractional type we want
35// so in this case the PreciseXXX() functions below will become equivalent to
36// their normal version.
38
39// Returns the square of a Fractional.
40// Useful to shorten the code when f is an expression or a long name.
41inline Fractional Square(Fractional f) { return f * f; }
42
43// Returns distance from a given fractional number to the closest integer. It
44// means that the result is always contained in range of [0.0, 0.5].
46 return std::abs(f - std::round(f));
47}
48
49// Returns the scalar product between u and v.
50// The precise versions use KahanSum and are about two times slower.
51template <class DenseRowOrColumn1, class DenseRowOrColumn2>
52Fractional ScalarProduct(const DenseRowOrColumn1& u,
53 const DenseRowOrColumn2& v) {
54 DCHECK_EQ(u.size().value(), v.size().value());
55 Fractional sum(0.0);
56 typename DenseRowOrColumn1::IndexType i(0);
57 typename DenseRowOrColumn2::IndexType j(0);
58 const size_t num_blocks = u.size().value() / 4;
59 for (size_t block = 0; block < num_blocks; ++block) {
60 // Computing the sum of 4 elements at once may allow the compiler to
61 // generate more efficient code, e.g. using SIMD and checking the loop
62 // condition much less frequently.
63 //
64 // This produces different results from the case where each multiplication
65 // is added to sum separately. An extreme example of this can be derived
66 // using the fact that 1e11 + 2e-6 == 1e11, but 1e11 + 8e-6 > 1e11.
67 //
68 // While the results are different, they aren't necessarily better or worse.
69 // Typically, sum will be of larger magnitude than any individual
70 // multiplication, so one might expect, in practice, this method to yield
71 // more accurate results. However, if accuracy is vital, use the precise
72 // version.
73 sum += (u[i++] * v[j++]) + (u[i++] * v[j++]) + (u[i++] * v[j++]) +
74 (u[i++] * v[j++]);
75 }
76 while (i < u.size()) {
77 sum += u[i++] * v[j++];
78 }
79 return sum;
80}
81
82// Note: This version is heavily used in the pricing.
83// TODO(user): Optimize this more (SSE or unroll with two sums). Another
84// option is to skip the u[col] that are 0.0 rather than fetching the coeff
85// and doing a Fractional multiplication.
86template <class DenseRowOrColumn>
87Fractional ScalarProduct(const DenseRowOrColumn& u, const SparseColumn& v) {
88 Fractional sum(0.0);
89 for (const SparseColumn::Entry e : v) {
90 sum += u[typename DenseRowOrColumn::IndexType(e.row().value())] *
91 e.coefficient();
92 }
93 return sum;
94}
95
96template <class DenseRowOrColumn>
97Fractional ScalarProduct(const DenseRowOrColumn& u, const ScatteredColumn& v) {
98 DCHECK_EQ(u.size().value(), v.values.size().value());
100 return ScalarProduct(u, v.values);
101 }
102 Fractional sum = 0.0;
103 for (const auto e : v) {
104 sum += (u[typename DenseRowOrColumn::IndexType(e.row().value())] *
105 e.coefficient());
106 }
107 return sum;
108}
109
110template <class DenseRowOrColumn, class DenseRowOrColumn2>
111Fractional PreciseScalarProduct(const DenseRowOrColumn& u,
112 const DenseRowOrColumn2& v) {
113 DCHECK_EQ(u.size().value(), v.size().value());
114 KahanSum sum;
115 for (typename DenseRowOrColumn::IndexType i(0); i < u.size(); ++i) {
116 sum.Add(u[i] * v[typename DenseRowOrColumn2::IndexType(i.value())]);
117 }
118 return sum.Value();
119}
120
121template <class DenseRowOrColumn>
122Fractional PreciseScalarProduct(const DenseRowOrColumn& u,
123 const SparseColumn& v) {
124 KahanSum sum;
125 for (const SparseColumn::Entry e : v) {
126 sum.Add(u[typename DenseRowOrColumn::IndexType(e.row().value())] *
127 e.coefficient());
128 }
129 return sum.Value();
130}
131
132// Computes a scalar product for entries with index not greater than max_index.
133template <class DenseRowOrColumn>
134Fractional PartialScalarProduct(const DenseRowOrColumn& u,
135 const SparseColumn& v, int max_index) {
136 Fractional sum(0.0);
137 for (const SparseColumn::Entry e : v) {
138 if (e.row().value() >= max_index) {
139 return sum;
140 }
141 sum += u[typename DenseRowOrColumn::IndexType(e.row().value())] *
142 e.coefficient();
143 }
144 return sum;
145}
146
147// Returns the norm^2 (sum of the square of the entries) of the given column.
148// The precise version uses KahanSum and are about two times slower.
150Fractional SquaredNorm(absl::Span<const Fractional> data);
151Fractional SquaredNormAndResetToZero(absl::Span<Fractional> data);
153Fractional SquaredNorm(const ColumnView& v);
154Fractional SquaredNorm(const ScatteredColumn& v);
157Fractional PreciseSquaredNorm(const ScatteredColumn& v);
158
159// Returns the maximum of the |coefficients| of 'v'.
162Fractional InfinityNorm(const ColumnView& v);
163
164// Returns the fraction of non-zero entries of the given row.
165//
166// TODO(user): Take a Scattered row/col instead. This is only used to report
167// stats, but we should still have a sparse version to do it faster.
168double Density(const DenseRow& row);
169
170// Sets to 0.0 all entries of the given row whose fabs() is lower than the given
171// threshold.
174
175// Transposition functions implemented below with a cast so it should actually
176// have no complexity cost.
177const DenseRow& Transpose(const DenseColumn& col);
178const DenseColumn& Transpose(const DenseRow& row);
179
180// Returns the maximum of the |coefficients| of the given column restricted
181// to the rows_to_consider. Also returns the first RowIndex 'row' that attains
182// this maximum. If the maximum is 0.0, then row_index is left untouched.
184 const DenseBooleanColumn& rows_to_consider,
185 RowIndex* row_index);
186
187// Sets to false the entry b[row] if column[row] is non null.
188// Note that if 'b' was true only on the non-zero position of column, this can
189// be used as a fast way to clear 'b'.
190void SetSupportToFalse(const ColumnView& column, DenseBooleanColumn* b);
191
192// Returns true iff for all 'row' we have '|column[row]| <= radius[row]'.
193bool IsDominated(const ColumnView& column, const DenseColumn& radius);
194
195// This cast based implementation should be safe, as long as DenseRow and
196// DenseColumn are implemented by the same underlying type.
197// We still do some DCHECK to be sure it works as expected in addition to the
198// unit tests.
199inline const DenseRow& Transpose(const DenseColumn& col) {
200 const DenseRow& row = reinterpret_cast<const DenseRow&>(col);
201 DCHECK_EQ(col.size(), ColToRowIndex(row.size()));
202 DCHECK(col.empty() || (&(col[RowIndex(0)]) == &(row[ColIndex(0)])));
203 return row;
204}
205
206// Similar comment as the other Transpose() implementation above.
207inline const DenseColumn& Transpose(const DenseRow& row) {
208 const DenseColumn& col = reinterpret_cast<const DenseColumn&>(row);
209 DCHECK_EQ(col.size(), ColToRowIndex(row.size()));
210 DCHECK(col.empty() || (&(col[RowIndex(0)]) == &(row[ColIndex(0)])));
211 return col;
212}
213
214// Computes the positions of the non-zeros of a dense vector.
215template <typename IndexType>
217 std::vector<IndexType>* non_zeros) {
218 non_zeros->clear();
219 const IndexType end = input.size();
220 for (IndexType index(0); index < end; ++index) {
221 if (input[index] != 0.0) {
222 non_zeros->push_back(index);
223 }
224 }
225}
226
227// Returns true if the given Fractional container is all zeros.
228template <typename Container>
229inline bool IsAllZero(const Container& input) {
230 for (Fractional value : input) {
231 if (value != 0.0) return false;
232 }
233 return true;
234}
235
236// Returns true if the given vector of bool is all false.
237template <typename BoolVector>
238bool IsAllFalse(const BoolVector& v) {
239 return std::all_of(v.begin(), v.end(), [](bool value) { return !value; });
240}
241
242// Permutes the given dense vector. It uses for this an all zero scratchpad.
243template <typename IndexType, typename PermutationIndexType>
245 const Permutation<PermutationIndexType>& permutation,
248 DCHECK(IsAllZero(*zero_scratchpad));
249 const IndexType size = input_output->size();
250 zero_scratchpad->swap(*input_output);
251 input_output->resize(size, 0.0);
252 for (IndexType index(0); index < size; ++index) {
253 const Fractional value = (*zero_scratchpad)[index];
254 if (value != 0.0) {
255 const IndexType permuted_index(
256 permutation[PermutationIndexType(index.value())].value());
257 (*input_output)[permuted_index] = value;
258 }
259 }
260 zero_scratchpad->assign(size, 0.0);
261}
262
263// Same as PermuteAndComputeNonZeros() except that we assume that the given
264// non-zeros are the initial non-zeros positions of output.
265template <typename IndexType>
267 const Permutation<IndexType>& permutation,
270 std::vector<IndexType>* non_zeros) {
271 DCHECK(IsAllZero(*zero_scratchpad));
272 zero_scratchpad->swap(*output);
273 output->resize(zero_scratchpad->size(), 0.0);
274 for (IndexType& index_ref : *non_zeros) {
275 const Fractional value = (*zero_scratchpad)[index_ref];
276 (*zero_scratchpad)[index_ref] = 0.0;
277 const IndexType permuted_index(permutation[index_ref]);
278 (*output)[permuted_index] = value;
279 index_ref = permuted_index;
280 }
281}
282
283// Sets a dense vector for which the non zeros are known to be non_zeros.
284template <typename IndexType, typename ScatteredRowOrCol>
286 ScatteredRowOrCol* v) {
287 // Only use the sparse version if there is less than 5% non-zeros positions
288 // compared to the wanted size. Note that in most cases the vector will
289 // already be of the correct size.
290 const double kSparseThreshold = 0.05;
291 if (!v->non_zeros.empty() &&
292 v->non_zeros.size() < kSparseThreshold * size.value()) {
293 for (const IndexType index : v->non_zeros) {
294 DCHECK_LT(index, v->values.size());
295 (*v)[index] = 0.0;
296 }
297 v->values.resize(size, 0.0);
298 DCHECK(IsAllZero(v->values));
299 } else {
300 v->values.AssignToZero(size);
301 }
302 v->non_zeros.clear();
303}
304
305// Changes the sign of all the entries in the given vector.
306template <typename IndexType>
308 const IndexType end = data->size();
309 for (IndexType i(0); i < end; ++i) {
310 (*data)[i] = -(*data)[i];
311 }
312}
313
314// Given N Fractional elements, this class maintains their sum and can
315// provide, for each element X, the sum of all elements except X.
316// The subtelty is that it works well with infinities: for example, if there is
317// exactly one infinite element X, then SumWithout(X) will be finite.
318//
319// Two flavors of this class are provided: SumWithPositiveInfiniteAndOneMissing
320// supports calling Add() with normal numbers and positive infinities (and will
321// DCHECK() that), and SumWithNegativeInfiniteAndOneMissing does the same with
322// negative infinities.
323//
324// The numerical accuracy suffers however. If X is 1e100 and SumWithout(X)
325// should be 1e-100, then the value actually returned by SumWithout(X) is likely
326// to be wrong.
327template <bool supported_infinity_is_positive>
329 public:
330 SumWithOneMissing() : num_infinities_(0), sum_() {}
331
333 DCHECK(!std::isnan(x));
334
335 if (!IsFinite(x)) {
336 DCHECK_EQ(x, Infinity());
337 ++num_infinities_;
338 return;
339 }
340
341 // If we overflow, then there is not much we can do. This is needed
342 // because KahanSum seems to give nan if we try to add stuff to an
343 // infinite sum.
344 if (!IsFinite(sum_.Value())) return;
345
346 sum_.Add(x);
347 }
348
350 DCHECK_GE(num_infinities_, 1);
351 --num_infinities_;
352 }
353
354 Fractional Sum() const {
355 if (num_infinities_ > 0) return Infinity();
356 return sum_.Value();
357 }
358
360 if (IsFinite(x)) {
361 if (num_infinities_ > 0) return Infinity();
362 return sum_.Value() - x;
363 }
364 DCHECK_EQ(Infinity(), x);
365 if (num_infinities_ > 1) return Infinity();
366 return sum_.Value();
367 }
368
369 // When the term we substract has a big magnitude, the SumWithout() can be
370 // quite imprecise. On can use these version to have more defensive bounds.
372 if (!IsFinite(c)) return SumWithout(c);
373 return SumWithout(c) - std::abs(c) * 1e-12;
374 }
375
377 if (!IsFinite(c)) return SumWithout(c);
378 return SumWithout(c) + std::abs(c) * 1e-12;
379 }
380
381 private:
382 Fractional Infinity() const {
383 return supported_infinity_is_positive ? kInfinity : -kInfinity;
384 }
385
386 // Count how many times Add() was called with an infinite value.
387 int num_infinities_;
388 KahanSum sum_; // stripped of all the infinite values.
389};
392
393} // namespace glop
394} // namespace operations_research
395
396#endif // OR_TOOLS_LP_DATA_LP_UTILS_H_
IntegerValue size
void Add(const FpNumber &value)
Adds an FpNumber to the sum.
FpNumber Value() const
Gets the value of the sum.
void assign(IntType size, const T &v)
Definition lp_types.h:319
Fractional SumWithout(Fractional x) const
Definition lp_utils.h:359
Fractional SumWithoutUb(Fractional c) const
Definition lp_utils.h:376
Fractional SumWithoutLb(Fractional c) const
Definition lp_utils.h:371
void swap(StrongVector &x) noexcept
int64_t b
Definition table.cc:45
int64_t value
int index
ColIndex col
Definition markowitz.cc:187
RowIndex row
Definition markowitz.cc:186
double Density(const DenseRow &row)
Definition lp_utils.cc:176
constexpr double kInfinity
Infinity for type Fractional.
Definition lp_types.h:89
void PermuteWithScratchpad(const Permutation< PermutationIndexType > &permutation, StrictITIVector< IndexType, Fractional > *zero_scratchpad, StrictITIVector< IndexType, Fractional > *input_output)
Permutes the given dense vector. It uses for this an all zero scratchpad.
Definition lp_utils.h:244
void RemoveNearZeroEntries(Fractional threshold, DenseRow *row)
Definition lp_utils.cc:185
Fractional Square(Fractional f)
Definition lp_utils.h:41
StrictITIVector< RowIndex, bool > DenseBooleanColumn
Column of booleans.
Definition lp_types.h:385
bool IsAllZero(const Container &input)
Returns true if the given Fractional container is all zeros.
Definition lp_utils.h:229
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
Definition lp_utils.h:52
void ComputeNonZeros(const StrictITIVector< IndexType, Fractional > &input, std::vector< IndexType > *non_zeros)
Computes the positions of the non-zeros of a dense vector.
Definition lp_utils.h:216
AccurateSum< Fractional > KahanSum
Definition lp_utils.h:37
Fractional PreciseScalarProduct(const DenseRowOrColumn &u, const DenseRowOrColumn2 &v)
Definition lp_utils.h:111
SumWithOneMissing< true > SumWithPositiveInfiniteAndOneMissing
Definition lp_utils.h:390
const DenseRow & Transpose(const DenseColumn &col)
Similar comment as the other Transpose() implementation above.
Definition lp_utils.h:199
bool IsAllFalse(const BoolVector &v)
Returns true if the given vector of bool is all false.
Definition lp_utils.h:238
void PermuteWithKnownNonZeros(const Permutation< IndexType > &permutation, StrictITIVector< IndexType, Fractional > *zero_scratchpad, StrictITIVector< IndexType, Fractional > *output, std::vector< IndexType > *non_zeros)
Definition lp_utils.h:266
bool IsFinite(Fractional value)
Definition lp_types.h:96
void ClearAndResizeVectorWithNonZeros(IndexType size, ScatteredRowOrCol *v)
Sets a dense vector for which the non zeros are known to be non_zeros.
Definition lp_utils.h:285
Fractional PreciseSquaredNorm(const SparseColumn &v)
Definition lp_utils.cc:44
Fractional RestrictedInfinityNorm(const ColumnView &column, const DenseBooleanColumn &rows_to_consider, RowIndex *row_index)
Definition lp_utils.cc:203
Fractional InfinityNorm(const DenseColumn &v)
Returns the maximum of the coefficients of 'v'.
Definition lp_utils.cc:151
void SetSupportToFalse(const ColumnView &column, DenseBooleanColumn *b)
Definition lp_utils.cc:216
RowIndex ColToRowIndex(ColIndex col)
Get the RowIndex corresponding to the row # col.
Definition lp_types.h:57
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
Changes the sign of all the entries in the given vector.
Definition lp_utils.h:307
static Fractional Fractionality(Fractional f)
Definition lp_utils.h:45
Fractional SquaredNorm(const SparseColumn &v)
Definition lp_utils.cc:36
Fractional PartialScalarProduct(const DenseRowOrColumn &u, const SparseColumn &v, int max_index)
Computes a scalar product for entries with index not greater than max_index.
Definition lp_utils.h:134
StrictITIVector< RowIndex, Fractional > DenseColumn
Column-vector types. Column-vector types are indexed by a row index.
Definition lp_types.h:382
StrictITIVector< ColIndex, Fractional > DenseRow
Row-vector types. Row-vector types are indexed by a column index.
Definition lp_types.h:353
bool IsDominated(const ColumnView &column, const DenseColumn &radius)
Returns true iff for all 'row' we have '|column[row]| <= radius[row]'.
Definition lp_utils.cc:224
SumWithOneMissing< false > SumWithNegativeInfiniteAndOneMissing
Definition lp_utils.h:391
Fractional SquaredNormAndResetToZero(absl::Span< Fractional > data)
Definition lp_utils.cc:103
In SWIG mode, we don't want anything besides these top-level includes.
int column
static int input(yyscan_t yyscanner)
const Variable x
Definition qp_tests.cc:127
std::optional< int64_t > end
StrictITIVector< Index, Fractional > values
bool ShouldUseDenseIteration(double ratio_for_using_dense_representation) const