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