OR-Tools  9.6
lp_data.cc
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 
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <cstdint>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/str_format.h"
26 #include "ortools/base/logging.h"
32 
33 namespace operations_research {
34 namespace glop {
35 
36 namespace {
37 
38 // This should be the same as DCHECK(AreBoundsValid()), but the DCHECK() are
39 // split to give more meaningful information to the user in case of failure.
40 void DebugCheckBoundsValid(Fractional lower_bound, Fractional upper_bound) {
41  DCHECK(!std::isnan(lower_bound));
42  DCHECK(!std::isnan(upper_bound));
43  DCHECK(!(lower_bound == kInfinity && upper_bound == kInfinity));
44  DCHECK(!(lower_bound == -kInfinity && upper_bound == -kInfinity));
45  DCHECK_LE(lower_bound, upper_bound);
47 }
48 
49 // Returns true if the bounds are the ones of a free or boxed row. Note that
50 // a fixed row is not counted as boxed.
51 bool AreBoundsFreeOrBoxed(Fractional lower_bound, Fractional upper_bound) {
52  if (lower_bound == -kInfinity && upper_bound == kInfinity) return true;
55  return true;
56  }
57  return false;
58 }
59 
60 template <class I, class T>
61 double Average(const absl::StrongVector<I, T>& v) {
62  const size_t size = v.size();
63  DCHECK_LT(0, size);
64  double sum = 0.0;
65  double n = 0.0; // n is used in a calculation involving doubles.
66  for (I i(0); i < size; ++i) {
67  if (v[i] == 0.0) continue;
68  ++n;
69  sum += static_cast<double>(v[i].value());
70  }
71  return n == 0.0 ? 0.0 : sum / n;
72 }
73 
74 template <class I, class T>
75 double StandardDeviation(const absl::StrongVector<I, T>& v) {
76  const size_t size = v.size();
77  double n = 0.0; // n is used in a calculation involving doubles.
78  double sigma_square = 0.0;
79  double sigma = 0.0;
80  for (I i(0); i < size; ++i) {
81  double sample = static_cast<double>(v[i].value());
82  if (sample == 0.0) continue;
83  sigma_square += sample * sample;
84  sigma += sample;
85  ++n;
86  }
87  return n == 0.0 ? 0.0 : sqrt((sigma_square - sigma * sigma / n) / n);
88 }
89 
90 // Returns 0 when the vector is empty.
91 template <class I, class T>
92 T GetMaxElement(const absl::StrongVector<I, T>& v) {
93  const size_t size = v.size();
94  if (size == 0) {
95  return T(0);
96  }
97 
98  T max_index = v[I(0)];
99  for (I i(1); i < size; ++i) {
100  if (max_index < v[i]) {
101  max_index = v[i];
102  }
103  }
104  return max_index;
105 }
106 
107 } // anonymous namespace
108 
109 // --------------------------------------------------------
110 // LinearProgram
111 // --------------------------------------------------------
113  : matrix_(),
114  transpose_matrix_(),
115  constraint_lower_bounds_(),
116  constraint_upper_bounds_(),
117  constraint_names_(),
118  objective_coefficients_(),
119  variable_lower_bounds_(),
120  variable_upper_bounds_(),
121  variable_names_(),
122  variable_types_(),
123  integer_variables_list_(),
124  variable_table_(),
125  constraint_table_(),
126  objective_offset_(0.0),
127  objective_scaling_factor_(1.0),
128  maximize_(false),
129  columns_are_known_to_be_clean_(true),
130  transpose_matrix_is_consistent_(true),
131  integer_variables_list_is_consistent_(true),
132  name_(),
133  first_slack_variable_(kInvalidCol) {}
134 
136  matrix_.Clear();
137  transpose_matrix_.Clear();
138 
139  constraint_lower_bounds_.clear();
140  constraint_upper_bounds_.clear();
141  constraint_names_.clear();
142 
143  objective_coefficients_.clear();
144  variable_lower_bounds_.clear();
145  variable_upper_bounds_.clear();
146  variable_types_.clear();
147  integer_variables_list_.clear();
148  variable_names_.clear();
149 
150  constraint_table_.clear();
151  variable_table_.clear();
152 
153  maximize_ = false;
154  objective_offset_ = 0.0;
155  objective_scaling_factor_ = 1.0;
156  columns_are_known_to_be_clean_ = true;
157  transpose_matrix_is_consistent_ = true;
158  integer_variables_list_is_consistent_ = true;
159  name_.clear();
160  first_slack_variable_ = kInvalidCol;
161 }
162 
164  DCHECK_EQ(kInvalidCol, first_slack_variable_)
165  << "New variables can't be added to programs that already have slack "
166  "variables. Consider calling LinearProgram::DeleteSlackVariables() "
167  "before adding new variables to the problem.";
168  objective_coefficients_.push_back(0.0);
169  variable_lower_bounds_.push_back(0);
170  variable_upper_bounds_.push_back(kInfinity);
171  variable_types_.push_back(VariableType::CONTINUOUS);
172  variable_names_.push_back("");
173  transpose_matrix_is_consistent_ = false;
174  return matrix_.AppendEmptyColumn();
175 }
176 
177 ColIndex LinearProgram::CreateNewSlackVariable(bool is_integer_slack_variable,
180  const std::string& name) {
181  objective_coefficients_.push_back(0.0);
182  variable_lower_bounds_.push_back(lower_bound);
183  variable_upper_bounds_.push_back(upper_bound);
184  variable_types_.push_back(is_integer_slack_variable
187  variable_names_.push_back(name);
188  transpose_matrix_is_consistent_ = false;
189  return matrix_.AppendEmptyColumn();
190 }
191 
193  DCHECK_EQ(kInvalidCol, first_slack_variable_)
194  << "New constraints can't be added to programs that already have slack "
195  "variables. Consider calling LinearProgram::DeleteSlackVariables() "
196  "before adding new variables to the problem.";
197  const RowIndex row(constraint_names_.size());
198  matrix_.SetNumRows(row + 1);
199  constraint_lower_bounds_.push_back(Fractional(0.0));
200  constraint_upper_bounds_.push_back(Fractional(0.0));
201  constraint_names_.push_back("");
202  transpose_matrix_is_consistent_ = false;
203  return row;
204 }
205 
206 ColIndex LinearProgram::FindOrCreateVariable(const std::string& variable_id) {
207  const absl::flat_hash_map<std::string, ColIndex>::iterator it =
208  variable_table_.find(variable_id);
209  if (it != variable_table_.end()) {
210  return it->second;
211  } else {
212  const ColIndex col = CreateNewVariable();
213  variable_names_[col] = variable_id;
214  variable_table_[variable_id] = col;
215  return col;
216  }
217 }
218 
220  const std::string& constraint_id) {
221  const absl::flat_hash_map<std::string, RowIndex>::iterator it =
222  constraint_table_.find(constraint_id);
223  if (it != constraint_table_.end()) {
224  return it->second;
225  } else {
226  const RowIndex row = CreateNewConstraint();
227  constraint_names_[row] = constraint_id;
228  constraint_table_[constraint_id] = row;
229  return row;
230  }
231 }
232 
233 void LinearProgram::SetVariableName(ColIndex col, absl::string_view name) {
234  variable_names_[col] = std::string(name);
235 }
236 
238  const bool var_was_integer = IsVariableInteger(col);
239  variable_types_[col] = type;
240  const bool var_is_integer = IsVariableInteger(col);
241  if (var_is_integer != var_was_integer) {
242  integer_variables_list_is_consistent_ = false;
243  }
244 }
245 
246 void LinearProgram::SetConstraintName(RowIndex row, absl::string_view name) {
247  constraint_names_[row] = std::string(name);
248 }
249 
252  if (dcheck_bounds_) DebugCheckBoundsValid(lower_bound, upper_bound);
253  const bool var_was_binary = IsVariableBinary(col);
254  variable_lower_bounds_[col] = lower_bound;
255  variable_upper_bounds_[col] = upper_bound;
256  const bool var_is_binary = IsVariableBinary(col);
257  if (var_is_binary != var_was_binary) {
258  integer_variables_list_is_consistent_ = false;
259  }
260 }
261 
262 void LinearProgram::UpdateAllIntegerVariableLists() const {
263  if (integer_variables_list_is_consistent_) return;
264  integer_variables_list_.clear();
265  binary_variables_list_.clear();
266  non_binary_variables_list_.clear();
267  const ColIndex num_cols = num_variables();
268  for (ColIndex col(0); col < num_cols; ++col) {
269  if (IsVariableInteger(col)) {
270  integer_variables_list_.push_back(col);
271  if (IsVariableBinary(col)) {
272  binary_variables_list_.push_back(col);
273  } else {
274  non_binary_variables_list_.push_back(col);
275  }
276  }
277  }
278  integer_variables_list_is_consistent_ = true;
279 }
280 
281 const std::vector<ColIndex>& LinearProgram::IntegerVariablesList() const {
282  UpdateAllIntegerVariableLists();
283  return integer_variables_list_;
284 }
285 
286 const std::vector<ColIndex>& LinearProgram::BinaryVariablesList() const {
287  UpdateAllIntegerVariableLists();
288  return binary_variables_list_;
289 }
290 
291 const std::vector<ColIndex>& LinearProgram::NonBinaryVariablesList() const {
292  UpdateAllIntegerVariableLists();
293  return non_binary_variables_list_;
294 }
295 
296 bool LinearProgram::IsVariableInteger(ColIndex col) const {
297  return variable_types_[col] == VariableType::INTEGER ||
298  variable_types_[col] == VariableType::IMPLIED_INTEGER;
299 }
300 
301 bool LinearProgram::IsVariableBinary(ColIndex col) const {
302  // TODO(user): bounds of binary variables (and of integer ones) should
303  // be integer. Add a preprocessor for that.
304  return IsVariableInteger(col) && (variable_lower_bounds_[col] < kEpsilon) &&
305  (variable_lower_bounds_[col] > Fractional(-1)) &&
306  (variable_upper_bounds_[col] > Fractional(1) - kEpsilon) &&
307  (variable_upper_bounds_[col] < 2);
308 }
309 
312  if (dcheck_bounds_) DebugCheckBoundsValid(lower_bound, upper_bound);
313  ResizeRowsIfNeeded(row);
314  constraint_lower_bounds_[row] = lower_bound;
315  constraint_upper_bounds_[row] = upper_bound;
316 }
317 
318 void LinearProgram::SetCoefficient(RowIndex row, ColIndex col,
319  Fractional value) {
320  DCHECK(IsFinite(value));
321  ResizeRowsIfNeeded(row);
322  columns_are_known_to_be_clean_ = false;
323  transpose_matrix_is_consistent_ = false;
325 }
326 
328  DCHECK(IsFinite(value));
329  objective_coefficients_[col] = value;
330 }
331 
333  DCHECK(IsFinite(objective_offset));
334  objective_offset_ = objective_offset;
335 }
336 
338  Fractional objective_scaling_factor) {
340  DCHECK_NE(0.0, objective_scaling_factor);
341  objective_scaling_factor_ = objective_scaling_factor;
342 }
343 
345  maximize_ = maximize;
346 }
347 
349  if (columns_are_known_to_be_clean_) return;
350  matrix_.CleanUp();
351  columns_are_known_to_be_clean_ = true;
352  transpose_matrix_is_consistent_ = false;
353 }
354 
356  if (columns_are_known_to_be_clean_) return true;
357  columns_are_known_to_be_clean_ = matrix_.IsCleanedUp();
358  return columns_are_known_to_be_clean_;
359 }
360 
361 std::string LinearProgram::GetVariableName(ColIndex col) const {
362  return col >= variable_names_.size() || variable_names_[col].empty()
363  ? absl::StrFormat("c%d", col.value())
364  : variable_names_[col];
365 }
366 
367 std::string LinearProgram::GetConstraintName(RowIndex row) const {
368  return row >= constraint_names_.size() || constraint_names_[row].empty()
369  ? absl::StrFormat("r%d", row.value())
370  : constraint_names_[row];
371 }
372 
374  return variable_types_[col];
375 }
376 
378  if (!transpose_matrix_is_consistent_) {
379  transpose_matrix_.PopulateFromTranspose(matrix_);
380  transpose_matrix_is_consistent_ = true;
381  }
382  DCHECK_EQ(transpose_matrix_.num_rows().value(), matrix_.num_cols().value());
383  DCHECK_EQ(transpose_matrix_.num_cols().value(), matrix_.num_rows().value());
384  return transpose_matrix_;
385 }
386 
388  if (!transpose_matrix_is_consistent_) {
389  transpose_matrix_.PopulateFromTranspose(matrix_);
390  }
391  // This enables a client to start modifying the matrix and then abort and not
392  // call UseTransposeMatrixAsReference(). Then, the other client of
393  // GetTransposeSparseMatrix() will still see the correct matrix.
394  transpose_matrix_is_consistent_ = false;
395  return &transpose_matrix_;
396 }
397 
399  DCHECK_EQ(transpose_matrix_.num_rows().value(), matrix_.num_cols().value());
400  DCHECK_EQ(transpose_matrix_.num_cols().value(), matrix_.num_rows().value());
401  matrix_.PopulateFromTranspose(transpose_matrix_);
402  transpose_matrix_is_consistent_ = true;
403 }
404 
406  transpose_matrix_.Clear();
407  transpose_matrix_is_consistent_ = false;
408 }
409 
411  return matrix_.column(col);
412 }
413 
415  columns_are_known_to_be_clean_ = false;
416  transpose_matrix_is_consistent_ = false;
417  return matrix_.mutable_column(col);
418 }
419 
421  ColIndex col) const {
422  return maximize_ ? -objective_coefficients()[col]
424 }
425 
427  Fractional min_magnitude = 0.0;
428  Fractional max_magnitude = 0.0;
429  matrix_.ComputeMinAndMaxMagnitudes(&min_magnitude, &max_magnitude);
430  return absl::StrFormat(
431  "%d rows, %d columns, %d entries with magnitude in [%e, %e]",
433  // static_cast<int64_t> is needed because the Android port uses int32_t.
434  static_cast<int64_t>(num_entries().value()), min_magnitude,
435  max_magnitude);
436 }
437 
438 namespace {
439 
440 template <typename FractionalValues>
441 void UpdateStats(const FractionalValues& values, int64_t* num_non_zeros,
442  Fractional* min_value, Fractional* max_value) {
443  for (const Fractional v : values) {
444  if (v == 0 || v == kInfinity || v == -kInfinity) continue;
445  *min_value = std::min(*min_value, v);
446  *max_value = std::max(*max_value, v);
447  ++(*num_non_zeros);
448  }
449 }
450 
451 } // namespace
452 
454  int64_t num_non_zeros = 0;
455  Fractional min_value = +kInfinity;
456  Fractional max_value = -kInfinity;
457  UpdateStats(objective_coefficients_, &num_non_zeros, &min_value, &max_value);
458  if (num_non_zeros == 0) {
459  return "No objective term. This is a pure feasibility problem.";
460  } else {
461  return absl::StrFormat("%d non-zeros, range [%e, %e]", num_non_zeros,
462  min_value, max_value);
463  }
464 }
465 
467  int64_t num_non_zeros = 0;
468  Fractional min_value = +kInfinity;
469  Fractional max_value = -kInfinity;
470  UpdateStats(variable_lower_bounds_, &num_non_zeros, &min_value, &max_value);
471  UpdateStats(variable_upper_bounds_, &num_non_zeros, &min_value, &max_value);
472  UpdateStats(constraint_lower_bounds_, &num_non_zeros, &min_value, &max_value);
473  UpdateStats(constraint_upper_bounds_, &num_non_zeros, &min_value, &max_value);
474  if (num_non_zeros == 0) {
475  return "All variables/constraints bounds are zero or +/- infinity.";
476  } else {
477  return absl::StrFormat("%d non-zeros, range [%e, %e]", num_non_zeros,
478  min_value, max_value);
479  }
480 }
481 
483  const DenseRow& solution, Fractional absolute_tolerance) const {
484  DCHECK_EQ(solution.size(), num_variables());
485  if (solution.size() != num_variables()) return false;
486  const ColIndex num_cols = num_variables();
487  for (ColIndex col = ColIndex(0); col < num_cols; ++col) {
488  if (!IsFinite(solution[col])) return false;
489  const Fractional lb_error = variable_lower_bounds()[col] - solution[col];
490  const Fractional ub_error = solution[col] - variable_upper_bounds()[col];
491  if (lb_error > absolute_tolerance || ub_error > absolute_tolerance) {
492  return false;
493  }
494  }
495  return true;
496 }
497 
499  Fractional absolute_tolerance) const {
500  if (!SolutionIsWithinVariableBounds(solution, absolute_tolerance)) {
501  return false;
502  }
503  const SparseMatrix& transpose = GetTransposeSparseMatrix();
504  const RowIndex num_rows = num_constraints();
505  for (RowIndex row = RowIndex(0); row < num_rows; ++row) {
506  const Fractional sum =
507  ScalarProduct(solution, transpose.column(RowToColIndex(row)));
508  if (!IsFinite(sum)) return false;
509  const Fractional lb_error = constraint_lower_bounds()[row] - sum;
510  const Fractional ub_error = sum - constraint_upper_bounds()[row];
511  if (lb_error > absolute_tolerance || ub_error > absolute_tolerance) {
512  return false;
513  }
514  }
515  return true;
516 }
517 
519  Fractional absolute_tolerance) const {
520  DCHECK_EQ(solution.size(), num_variables());
521  if (solution.size() != num_variables()) return false;
522  for (ColIndex col : IntegerVariablesList()) {
523  if (!IsFinite(solution[col])) return false;
524  const Fractional fractionality = fabs(solution[col] - round(solution[col]));
525  if (fractionality > absolute_tolerance) return false;
526  }
527  return true;
528 }
529 
531  Fractional absolute_tolerance) const {
532  return SolutionIsLPFeasible(solution, absolute_tolerance) &&
533  SolutionIsInteger(solution, absolute_tolerance);
534 }
535 
537  CHECK(solution != nullptr);
538  const ColIndex num_cols = GetFirstSlackVariable();
539  const SparseMatrix& transpose = GetTransposeSparseMatrix();
540  const RowIndex num_rows = num_constraints();
541  CHECK_EQ(solution->size(), num_variables());
542  for (RowIndex row = RowIndex(0); row < num_rows; ++row) {
543  const Fractional sum = PartialScalarProduct(
544  *solution, transpose.column(RowToColIndex(row)), num_cols.value());
545  const ColIndex slack_variable = GetSlackVariable(row);
546  CHECK_NE(slack_variable, kInvalidCol);
547  (*solution)[slack_variable] = -sum;
548  }
549 }
550 
552  Fractional value) const {
554 }
555 
557  Fractional value) const {
559 }
560 
561 std::string LinearProgram::Dump() const {
562  // Objective line.
563  std::string output = maximize_ ? "max:" : "min:";
564  if (objective_offset_ != 0.0) {
565  output += Stringify(objective_offset_);
566  }
567  const ColIndex num_cols = num_variables();
568  for (ColIndex col(0); col < num_cols; ++col) {
569  const Fractional coeff = objective_coefficients()[col];
570  if (coeff != 0.0) {
571  output += StringifyMonomial(coeff, GetVariableName(col), false);
572  }
573  }
574  output.append(";\n");
575 
576  // Constraints.
577  const RowIndex num_rows = num_constraints();
578  for (RowIndex row(0); row < num_rows; ++row) {
581  output += GetConstraintName(row);
582  output += ":";
583  if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
584  output += " ";
585  output += Stringify(lower_bound);
586  output += " <=";
587  }
588  for (ColIndex col(0); col < num_cols; ++col) {
589  const Fractional coeff = matrix_.LookUpValue(row, col);
590  output += StringifyMonomial(coeff, GetVariableName(col), false);
591  }
592  if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
593  output += " <= ";
594  output += Stringify(upper_bound);
595  } else if (lower_bound == upper_bound) {
596  output += " = ";
597  output += Stringify(upper_bound);
598  } else if (lower_bound != -kInfinity) {
599  output += " >= ";
600  output += Stringify(lower_bound);
601  } else if (lower_bound != kInfinity) {
602  output += " <= ";
603  output += Stringify(upper_bound);
604  }
605  output += ";\n";
606  }
607 
608  // Variables.
609  for (ColIndex col(0); col < num_cols; ++col) {
612  if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
613  output += Stringify(lower_bound);
614  output += " <= ";
615  }
616  output += GetVariableName(col);
617  if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
618  output += " <= ";
619  output += Stringify(upper_bound);
620  } else if (lower_bound == upper_bound) {
621  output += " = ";
622  output += Stringify(upper_bound);
623  } else if (lower_bound != -kInfinity) {
624  output += " >= ";
625  output += Stringify(lower_bound);
626  } else if (lower_bound != kInfinity) {
627  output += " <= ";
628  output += Stringify(upper_bound);
629  }
630  output += ";\n";
631  }
632 
633  // Integer variables.
634  // TODO(user): if needed provide similar output for binary variables.
635  const std::vector<ColIndex>& integer_variables = IntegerVariablesList();
636  if (!integer_variables.empty()) {
637  output += "int";
638  for (ColIndex col : integer_variables) {
639  output += " ";
640  output += GetVariableName(col);
641  }
642  output += ";\n";
643  }
644 
645  return output;
646 }
647 
648 std::string LinearProgram::DumpSolution(const DenseRow& variable_values) const {
649  DCHECK_EQ(variable_values.size(), num_variables());
650  std::string output;
651  for (ColIndex col(0); col < variable_values.size(); ++col) {
652  if (!output.empty()) absl::StrAppend(&output, ", ");
653  absl::StrAppend(&output, GetVariableName(col), " = ",
654  (variable_values[col]));
655  }
656  return output;
657 }
658 
659 std::string LinearProgram::GetProblemStats() const {
660  return ProblemStatFormatter(
661  "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
662  "%d,%d,%d,%d");
663 }
664 
666  return ProblemStatFormatter(
667  "Number of rows : %d\n"
668  "Number of variables in file : %d\n"
669  "Number of entries (non-zeros) : %d\n"
670  "Number of entries in the objective : %d\n"
671  "Number of entries in the right-hand side : %d\n"
672  "Number of <= constraints : %d\n"
673  "Number of >= constraints : %d\n"
674  "Number of = constraints : %d\n"
675  "Number of range constraints : %d\n"
676  "Number of non-negative variables : %d\n"
677  "Number of boxed variables : %d\n"
678  "Number of free variables : %d\n"
679  "Number of fixed variables : %d\n"
680  "Number of other variables : %d\n"
681  "Number of integer variables : %d\n"
682  "Number of binary variables : %d\n"
683  "Number of non-binary integer variables : %d\n"
684  "Number of continuous variables : %d\n");
685 }
686 
687 std::string LinearProgram::GetNonZeroStats() const {
688  return NonZeroStatFormatter("%.2f%%,%d,%.2f,%.2f,%d,%.2f,%.2f");
689 }
690 
692  return NonZeroStatFormatter(
693  "Fill rate : %.2f%%\n"
694  "Entries in row (Max / average / std. dev.) : %d / %.2f / %.2f\n"
695  "Entries in column (Max / average / std. dev.): %d / %.2f / %.2f\n");
696 }
697 
699  bool detect_integer_constraints) {
700  // Clean up the matrix. We're going to add entries, but we'll only be adding
701  // them to new columns, and only one entry per column, which does not
702  // invalidate the "cleanness" of the matrix.
703  CleanUp();
704 
705  // Detect which constraints produce an integer slack variable. A constraint
706  // has an integer slack variable, if it contains only integer variables with
707  // integer coefficients. We do not check the bounds of the constraints,
708  // because in such case, they will be tightened to integer values by the
709  // preprocessors.
710  //
711  // We don't use the transpose, because it might not be valid and it would be
712  // inefficient to update it and invalidate it again at the end of this
713  // preprocessor.
714  DenseBooleanColumn has_integer_slack_variable(num_constraints(),
715  detect_integer_constraints);
716  if (detect_integer_constraints) {
717  for (ColIndex col(0); col < num_variables(); ++col) {
718  const SparseColumn& column = matrix_.column(col);
719  const bool is_integer_variable = IsVariableInteger(col);
720  for (const SparseColumn::Entry& entry : column) {
721  const RowIndex row = entry.row();
722  has_integer_slack_variable[row] =
723  has_integer_slack_variable[row] && is_integer_variable &&
724  round(entry.coefficient()) == entry.coefficient();
725  }
726  }
727  }
728 
729  // Extend the matrix of the problem with an identity matrix.
730  const ColIndex original_num_variables = num_variables();
731  for (RowIndex row(0); row < num_constraints(); ++row) {
732  ColIndex slack_variable_index = GetSlackVariable(row);
733  if (slack_variable_index != kInvalidCol &&
734  slack_variable_index < original_num_variables) {
735  // Slack variable is already present in this constraint.
736  continue;
737  }
738  const ColIndex slack_col = CreateNewSlackVariable(
739  has_integer_slack_variable[row], -constraint_upper_bounds_[row],
740  -constraint_lower_bounds_[row], absl::StrCat("s", row.value()));
741  SetCoefficient(row, slack_col, 1.0);
742  SetConstraintBounds(row, 0.0, 0.0);
743  }
744 
745  columns_are_known_to_be_clean_ = true;
746  transpose_matrix_is_consistent_ = false;
747  if (first_slack_variable_ == kInvalidCol) {
748  first_slack_variable_ = original_num_variables;
749  }
750 }
751 
753  return first_slack_variable_;
754 }
755 
756 ColIndex LinearProgram::GetSlackVariable(RowIndex row) const {
757  DCHECK_GE(row, RowIndex(0));
758  DCHECK_LT(row, num_constraints());
759  if (first_slack_variable_ == kInvalidCol) {
760  return kInvalidCol;
761  }
762  return first_slack_variable_ + RowToColIndex(row);
763 }
764 
766  RowToColMapping* duplicated_rows) {
767  const ColIndex dual_num_variables = dual.num_variables();
768  const RowIndex dual_num_constraints = dual.num_constraints();
769  Clear();
770 
771  // We always take the dual in its minimization form thanks to the
772  // GetObjectiveCoefficientForMinimizationVersion() below, so this will always
773  // be a maximization problem.
775 
776  // Taking the dual does not change the offset nor the objective scaling
777  // factor.
780 
781  // Create the dual variables y, with bounds depending on the type
782  // of constraints in the primal.
783  for (RowIndex dual_row(0); dual_row < dual_num_constraints; ++dual_row) {
785  const ColIndex col = RowToColIndex(dual_row);
786  const Fractional lower_bound = dual.constraint_lower_bounds()[dual_row];
787  const Fractional upper_bound = dual.constraint_upper_bounds()[dual_row];
788  if (lower_bound == upper_bound) {
791  } else if (upper_bound != kInfinity) {
792  // Note that for a ranged constraint, the loop will be continued here.
793  // This is wanted because we want to deal with the lower_bound afterwards.
796  } else if (lower_bound != -kInfinity) {
799  } else {
800  // This code does not support free rows in linear_program.
801  LOG(DFATAL) << "PopulateFromDual() was called with a program "
802  << "containing free constraints.";
803  }
804  }
805  // Create the dual slack variables v.
806  for (ColIndex dual_col(0); dual_col < dual_num_variables; ++dual_col) {
807  const Fractional lower_bound = dual.variable_lower_bounds()[dual_col];
808  if (lower_bound != -kInfinity) {
809  const ColIndex col = CreateNewVariable();
812  const RowIndex row = ColToRowIndex(dual_col);
814  }
815  }
816  // Create the dual surplus variables w.
817  for (ColIndex dual_col(0); dual_col < dual_num_variables; ++dual_col) {
818  const Fractional upper_bound = dual.variable_upper_bounds()[dual_col];
819  if (upper_bound != kInfinity) {
820  const ColIndex col = CreateNewVariable();
823  const RowIndex row = ColToRowIndex(dual_col);
825  }
826  }
827  // Store the transpose of the matrix.
828  for (ColIndex dual_col(0); dual_col < dual_num_variables; ++dual_col) {
829  const RowIndex row = ColToRowIndex(dual_col);
830  const Fractional row_bound =
832  SetConstraintBounds(row, row_bound, row_bound);
833  for (const SparseColumn::Entry e : dual.GetSparseColumn(dual_col)) {
834  const RowIndex dual_row = e.row();
835  const ColIndex col = RowToColIndex(dual_row);
836  SetCoefficient(row, col, e.coefficient());
837  }
838  }
839 
840  // Take care of ranged constraints.
841  duplicated_rows->assign(dual_num_constraints, kInvalidCol);
842  for (RowIndex dual_row(0); dual_row < dual_num_constraints; ++dual_row) {
843  const Fractional lower_bound = dual.constraint_lower_bounds()[dual_row];
844  const Fractional upper_bound = dual.constraint_upper_bounds()[dual_row];
845  if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
846  DCHECK(upper_bound != kInfinity || lower_bound != -kInfinity);
847 
848  // upper_bound was done in a loop above, now do the lower_bound.
849  const ColIndex col = CreateNewVariable();
853  matrix_.column(RowToColIndex(dual_row)));
854  (*duplicated_rows)[dual_row] = col;
855  }
856  }
857 
858  // We know that the columns are ordered by rows.
859  columns_are_known_to_be_clean_ = true;
860  transpose_matrix_is_consistent_ = false;
861 }
862 
864  const LinearProgram& linear_program) {
865  matrix_.PopulateFromSparseMatrix(linear_program.matrix_);
866  if (linear_program.transpose_matrix_is_consistent_) {
867  transpose_matrix_is_consistent_ = true;
868  transpose_matrix_.PopulateFromSparseMatrix(
869  linear_program.transpose_matrix_);
870  } else {
871  transpose_matrix_is_consistent_ = false;
872  transpose_matrix_.Clear();
873  }
874 
875  constraint_lower_bounds_ = linear_program.constraint_lower_bounds_;
876  constraint_upper_bounds_ = linear_program.constraint_upper_bounds_;
877  constraint_names_ = linear_program.constraint_names_;
878  constraint_table_.clear();
879 
880  PopulateNameObjectiveAndVariablesFromLinearProgram(linear_program);
881  first_slack_variable_ = linear_program.first_slack_variable_;
882 }
883 
885  const LinearProgram& lp, const RowPermutation& row_permutation,
886  const ColumnPermutation& col_permutation) {
887  DCHECK(lp.IsCleanedUp());
888  DCHECK_EQ(row_permutation.size(), lp.num_constraints());
889  DCHECK_EQ(col_permutation.size(), lp.num_variables());
890  DCHECK_EQ(lp.GetFirstSlackVariable(), kInvalidCol);
891  Clear();
892 
893  // Populate matrix coefficients.
894  ColumnPermutation inverse_col_permutation;
895  inverse_col_permutation.PopulateFromInverse(col_permutation);
896  matrix_.PopulateFromPermutedMatrix(lp.matrix_, row_permutation,
897  inverse_col_permutation);
899 
900  // Populate constraints.
901  ApplyPermutation(row_permutation, lp.constraint_lower_bounds(),
902  &constraint_lower_bounds_);
903  ApplyPermutation(row_permutation, lp.constraint_upper_bounds(),
904  &constraint_upper_bounds_);
905 
906  // Populate variables.
907  ApplyPermutation(col_permutation, lp.objective_coefficients(),
908  &objective_coefficients_);
909  ApplyPermutation(col_permutation, lp.variable_lower_bounds(),
910  &variable_lower_bounds_);
911  ApplyPermutation(col_permutation, lp.variable_upper_bounds(),
912  &variable_upper_bounds_);
913  ApplyPermutation(col_permutation, lp.variable_types(), &variable_types_);
914  integer_variables_list_is_consistent_ = false;
915 
916  // There is no vector based accessor to names, because they may be created
917  // on the fly.
918  constraint_names_.resize(lp.num_constraints());
919  for (RowIndex old_row(0); old_row < lp.num_constraints(); ++old_row) {
920  const RowIndex new_row = row_permutation[old_row];
921  constraint_names_[new_row] = lp.constraint_names_[old_row];
922  }
923  variable_names_.resize(lp.num_variables());
924  for (ColIndex old_col(0); old_col < lp.num_variables(); ++old_col) {
925  const ColIndex new_col = col_permutation[old_col];
926  variable_names_[new_col] = lp.variable_names_[old_col];
927  }
928 
929  // Populate singular fields.
930  maximize_ = lp.maximize_;
931  objective_offset_ = lp.objective_offset_;
932  objective_scaling_factor_ = lp.objective_scaling_factor_;
933  name_ = lp.name_;
934 }
935 
937  const LinearProgram& linear_program) {
938  matrix_.PopulateFromZero(RowIndex(0), linear_program.num_variables());
939  first_slack_variable_ = kInvalidCol;
940  transpose_matrix_is_consistent_ = false;
941  transpose_matrix_.Clear();
942 
943  constraint_lower_bounds_.clear();
944  constraint_upper_bounds_.clear();
945  constraint_names_.clear();
946  constraint_table_.clear();
947 
948  PopulateNameObjectiveAndVariablesFromLinearProgram(linear_program);
949 }
950 
951 void LinearProgram::PopulateNameObjectiveAndVariablesFromLinearProgram(
952  const LinearProgram& linear_program) {
953  objective_coefficients_ = linear_program.objective_coefficients_;
954  variable_lower_bounds_ = linear_program.variable_lower_bounds_;
955  variable_upper_bounds_ = linear_program.variable_upper_bounds_;
956  variable_names_ = linear_program.variable_names_;
957  variable_types_ = linear_program.variable_types_;
958  integer_variables_list_is_consistent_ =
959  linear_program.integer_variables_list_is_consistent_;
960  integer_variables_list_ = linear_program.integer_variables_list_;
961  binary_variables_list_ = linear_program.binary_variables_list_;
962  non_binary_variables_list_ = linear_program.non_binary_variables_list_;
963  variable_table_.clear();
964 
965  maximize_ = linear_program.maximize_;
966  objective_offset_ = linear_program.objective_offset_;
967  objective_scaling_factor_ = linear_program.objective_scaling_factor_;
968  columns_are_known_to_be_clean_ =
969  linear_program.columns_are_known_to_be_clean_;
970  name_ = linear_program.name_;
971 }
972 
974  const SparseMatrix& coefficients, const DenseColumn& left_hand_sides,
975  const DenseColumn& right_hand_sides,
977  const RowIndex num_new_constraints = coefficients.num_rows();
978  DCHECK_EQ(num_variables(), coefficients.num_cols());
979  DCHECK_EQ(num_new_constraints, left_hand_sides.size());
980  DCHECK_EQ(num_new_constraints, right_hand_sides.size());
981  DCHECK_EQ(num_new_constraints, names.size());
982 
984  transpose_matrix_is_consistent_ = false;
985  transpose_matrix_.Clear();
986  columns_are_known_to_be_clean_ = false;
987 
988  // Copy constraint bounds and names from linear_program.
989  constraint_lower_bounds_.insert(constraint_lower_bounds_.end(),
990  left_hand_sides.begin(),
991  left_hand_sides.end());
992  constraint_upper_bounds_.insert(constraint_upper_bounds_.end(),
993  right_hand_sides.begin(),
994  right_hand_sides.end());
995  constraint_names_.insert(constraint_names_.end(), names.begin(), names.end());
996 }
997 
999  const SparseMatrix& coefficients, const DenseColumn& left_hand_sides,
1000  const DenseColumn& right_hand_sides,
1002  bool detect_integer_constraints_for_slack) {
1003  AddConstraints(coefficients, left_hand_sides, right_hand_sides, names);
1004  AddSlackVariablesWhereNecessary(detect_integer_constraints_for_slack);
1005 }
1006 
1010  const ColIndex num_vars = num_variables();
1011  DCHECK_EQ(variable_lower_bounds.size(), num_vars);
1012  DCHECK_EQ(variable_upper_bounds.size(), num_vars);
1013 
1014  DenseRow new_lower_bounds(num_vars, 0);
1015  DenseRow new_upper_bounds(num_vars, 0);
1016  for (ColIndex i(0); i < num_vars; ++i) {
1017  const Fractional new_lower_bound =
1018  std::max(variable_lower_bounds[i], variable_lower_bounds_[i]);
1019  const Fractional new_upper_bound =
1020  std::min(variable_upper_bounds[i], variable_upper_bounds_[i]);
1021  if (new_lower_bound > new_upper_bound) {
1022  return false;
1023  }
1024  new_lower_bounds[i] = new_lower_bound;
1025  new_upper_bounds[i] = new_upper_bound;
1026  }
1027  variable_lower_bounds_.swap(new_lower_bounds);
1028  variable_upper_bounds_.swap(new_upper_bounds);
1029  return true;
1030 }
1031 
1032 void LinearProgram::Swap(LinearProgram* linear_program) {
1033  matrix_.Swap(&linear_program->matrix_);
1034  transpose_matrix_.Swap(&linear_program->transpose_matrix_);
1035 
1036  constraint_lower_bounds_.swap(linear_program->constraint_lower_bounds_);
1037  constraint_upper_bounds_.swap(linear_program->constraint_upper_bounds_);
1038  constraint_names_.swap(linear_program->constraint_names_);
1039 
1040  objective_coefficients_.swap(linear_program->objective_coefficients_);
1041  variable_lower_bounds_.swap(linear_program->variable_lower_bounds_);
1042  variable_upper_bounds_.swap(linear_program->variable_upper_bounds_);
1043  variable_names_.swap(linear_program->variable_names_);
1044  variable_types_.swap(linear_program->variable_types_);
1045  integer_variables_list_.swap(linear_program->integer_variables_list_);
1046  binary_variables_list_.swap(linear_program->binary_variables_list_);
1047  non_binary_variables_list_.swap(linear_program->non_binary_variables_list_);
1048 
1049  variable_table_.swap(linear_program->variable_table_);
1050  constraint_table_.swap(linear_program->constraint_table_);
1051 
1052  std::swap(maximize_, linear_program->maximize_);
1053  std::swap(objective_offset_, linear_program->objective_offset_);
1054  std::swap(objective_scaling_factor_,
1055  linear_program->objective_scaling_factor_);
1056  std::swap(columns_are_known_to_be_clean_,
1057  linear_program->columns_are_known_to_be_clean_);
1058  std::swap(transpose_matrix_is_consistent_,
1059  linear_program->transpose_matrix_is_consistent_);
1060  std::swap(integer_variables_list_is_consistent_,
1061  linear_program->integer_variables_list_is_consistent_);
1062  name_.swap(linear_program->name_);
1063  std::swap(first_slack_variable_, linear_program->first_slack_variable_);
1064 }
1065 
1066 void LinearProgram::DeleteColumns(const DenseBooleanRow& columns_to_delete) {
1067  if (columns_to_delete.empty()) return;
1068  integer_variables_list_is_consistent_ = false;
1069  const ColIndex num_cols = num_variables();
1070  ColumnPermutation permutation(num_cols);
1071  ColIndex new_index(0);
1072  for (ColIndex col(0); col < num_cols; ++col) {
1073  permutation[col] = new_index;
1074  if (col >= columns_to_delete.size() || !columns_to_delete[col]) {
1075  objective_coefficients_[new_index] = objective_coefficients_[col];
1076  variable_lower_bounds_[new_index] = variable_lower_bounds_[col];
1077  variable_upper_bounds_[new_index] = variable_upper_bounds_[col];
1078  variable_names_[new_index] = variable_names_[col];
1079  variable_types_[new_index] = variable_types_[col];
1080  ++new_index;
1081  } else {
1082  permutation[col] = kInvalidCol;
1083  }
1084  }
1085 
1086  matrix_.DeleteColumns(columns_to_delete);
1087  objective_coefficients_.resize(new_index, 0.0);
1088  variable_lower_bounds_.resize(new_index, 0.0);
1089  variable_upper_bounds_.resize(new_index, 0.0);
1090  variable_types_.resize(new_index, VariableType::CONTINUOUS);
1091  variable_names_.resize(new_index, "");
1092 
1093  // Remove the id of the deleted columns and adjust the index of the other.
1094  absl::flat_hash_map<std::string, ColIndex>::iterator it =
1095  variable_table_.begin();
1096  while (it != variable_table_.end()) {
1097  const ColIndex col = it->second;
1098  if (col >= columns_to_delete.size() || !columns_to_delete[col]) {
1099  it->second = permutation[col];
1100  ++it;
1101  } else {
1102  // This safely deletes the entry and moves the iterator one step ahead.
1103  variable_table_.erase(it++);
1104  }
1105  }
1106 
1107  // Eventually update transpose_matrix_.
1108  if (transpose_matrix_is_consistent_) {
1109  transpose_matrix_.DeleteRows(
1110  ColToRowIndex(new_index),
1111  reinterpret_cast<const RowPermutation&>(permutation));
1112  }
1113 }
1114 
1116  DCHECK_NE(first_slack_variable_, kInvalidCol);
1117  DenseBooleanRow slack_variables(matrix_.num_cols(), false);
1118  // Restore the bounds on the constraints corresponding to the slack variables.
1119  for (ColIndex slack_variable = first_slack_variable_;
1120  slack_variable < matrix_.num_cols(); ++slack_variable) {
1121  const SparseColumn& column = matrix_.column(slack_variable);
1122  // Slack variables appear only in the constraints for which they were
1123  // created. We can find this constraint by looking at the (only) entry in
1124  // the columnm of the slack variable.
1125  DCHECK_EQ(column.num_entries(), 1);
1126  const RowIndex row = column.EntryRow(EntryIndex(0));
1127  DCHECK_EQ(constraint_lower_bounds_[row], 0.0);
1128  DCHECK_EQ(constraint_upper_bounds_[row], 0.0);
1129  SetConstraintBounds(row, -variable_upper_bounds_[slack_variable],
1130  -variable_lower_bounds_[slack_variable]);
1131  slack_variables[slack_variable] = true;
1132  }
1133 
1134  DeleteColumns(slack_variables);
1135  first_slack_variable_ = kInvalidCol;
1136 }
1137 
1138 namespace {
1139 
1140 // Note that we ignore zeros and infinities because they do not matter from a
1141 // scaling perspective where this function is used.
1142 template <typename FractionalRange>
1143 void UpdateMinAndMaxMagnitude(const FractionalRange& range,
1144  Fractional* min_magnitude,
1145  Fractional* max_magnitude) {
1146  for (const Fractional value : range) {
1147  const Fractional magnitude = std::abs(value);
1148  if (magnitude == 0 || magnitude == kInfinity) continue;
1149  *min_magnitude = std::min(*min_magnitude, magnitude);
1150  *max_magnitude = std::max(*max_magnitude, magnitude);
1151  }
1152 }
1153 
1154 Fractional GetMedianScalingFactor(const DenseRow& range) {
1155  std::vector<Fractional> median;
1156  for (const Fractional value : range) {
1157  if (value == 0.0) continue;
1158  median.push_back(std::abs(value));
1159  }
1160  if (median.empty()) return 1.0;
1161  std::sort(median.begin(), median.end());
1162  return median[median.size() / 2];
1163 }
1164 
1165 Fractional GetMeanScalingFactor(const DenseRow& range) {
1166  Fractional mean = 0.0;
1167  int num_non_zeros = 0;
1168  for (const Fractional value : range) {
1169  if (value == 0.0) continue;
1170  ++num_non_zeros;
1171  mean += std::abs(value);
1172  }
1173  if (num_non_zeros == 0.0) return 1.0;
1174  return mean / static_cast<Fractional>(num_non_zeros);
1175 }
1176 
1177 Fractional ComputeDivisorSoThatRangeContainsOne(Fractional min_magnitude,
1178  Fractional max_magnitude) {
1179  if (min_magnitude > 1.0 && min_magnitude < kInfinity) {
1180  return min_magnitude;
1181  } else if (max_magnitude > 0.0 && max_magnitude < 1.0) {
1182  return max_magnitude;
1183  }
1184  return 1.0;
1185 }
1186 
1187 } // namespace
1188 
1190  GlopParameters::CostScalingAlgorithm method) {
1191  Fractional min_magnitude = kInfinity;
1192  Fractional max_magnitude = 0.0;
1193  UpdateMinAndMaxMagnitude(objective_coefficients(), &min_magnitude,
1194  &max_magnitude);
1195  Fractional cost_scaling_factor = 1.0;
1196  switch (method) {
1197  case GlopParameters::NO_COST_SCALING:
1198  break;
1199  case GlopParameters::CONTAIN_ONE_COST_SCALING:
1200  cost_scaling_factor =
1201  ComputeDivisorSoThatRangeContainsOne(min_magnitude, max_magnitude);
1202  break;
1203  case GlopParameters::MEAN_COST_SCALING:
1204  cost_scaling_factor = GetMeanScalingFactor(objective_coefficients());
1205  break;
1206  case GlopParameters::MEDIAN_COST_SCALING:
1207  cost_scaling_factor = GetMedianScalingFactor(objective_coefficients());
1208  break;
1209  }
1210  if (cost_scaling_factor != 1.0) {
1211  for (ColIndex col(0); col < num_variables(); ++col) {
1212  if (objective_coefficients()[col] == 0.0) continue;
1214  col, objective_coefficients()[col] / cost_scaling_factor);
1215  }
1216  SetObjectiveScalingFactor(objective_scaling_factor() * cost_scaling_factor);
1217  SetObjectiveOffset(objective_offset() / cost_scaling_factor);
1218  }
1219  VLOG(1) << "Objective magnitude range is [" << min_magnitude << ", "
1220  << max_magnitude << "] (dividing by " << cost_scaling_factor << ").";
1221  return cost_scaling_factor;
1222 }
1223 
1225  Fractional min_magnitude = kInfinity;
1226  Fractional max_magnitude = 0.0;
1227  UpdateMinAndMaxMagnitude(variable_lower_bounds(), &min_magnitude,
1228  &max_magnitude);
1229  UpdateMinAndMaxMagnitude(variable_upper_bounds(), &min_magnitude,
1230  &max_magnitude);
1231  UpdateMinAndMaxMagnitude(constraint_lower_bounds(), &min_magnitude,
1232  &max_magnitude);
1233  UpdateMinAndMaxMagnitude(constraint_upper_bounds(), &min_magnitude,
1234  &max_magnitude);
1235  const Fractional bound_scaling_factor =
1236  ComputeDivisorSoThatRangeContainsOne(min_magnitude, max_magnitude);
1237  if (bound_scaling_factor != 1.0) {
1239  bound_scaling_factor);
1240  SetObjectiveOffset(objective_offset() / bound_scaling_factor);
1241  for (ColIndex col(0); col < num_variables(); ++col) {
1243  variable_lower_bounds()[col] / bound_scaling_factor,
1244  variable_upper_bounds()[col] / bound_scaling_factor);
1245  }
1246  for (RowIndex row(0); row < num_constraints(); ++row) {
1248  row, constraint_lower_bounds()[row] / bound_scaling_factor,
1249  constraint_upper_bounds()[row] / bound_scaling_factor);
1250  }
1251  }
1252 
1253  VLOG(1) << "Bounds magnitude range is [" << min_magnitude << ", "
1254  << max_magnitude << "] (dividing bounds by " << bound_scaling_factor
1255  << ").";
1256  return bound_scaling_factor;
1257 }
1258 
1259 void LinearProgram::DeleteRows(const DenseBooleanColumn& rows_to_delete) {
1260  if (rows_to_delete.empty()) return;
1261 
1262  // Deal with row-indexed data and construct the row mapping that will need to
1263  // be applied to every column entry.
1264  const RowIndex num_rows = num_constraints();
1265  RowPermutation permutation(num_rows);
1266  RowIndex new_index(0);
1267  for (RowIndex row(0); row < num_rows; ++row) {
1268  if (row >= rows_to_delete.size() || !rows_to_delete[row]) {
1269  constraint_lower_bounds_[new_index] = constraint_lower_bounds_[row];
1270  constraint_upper_bounds_[new_index] = constraint_upper_bounds_[row];
1271  constraint_names_[new_index].swap(constraint_names_[row]);
1272  permutation[row] = new_index;
1273  ++new_index;
1274  } else {
1275  permutation[row] = kInvalidRow;
1276  }
1277  }
1278  constraint_lower_bounds_.resize(new_index, 0.0);
1279  constraint_upper_bounds_.resize(new_index, 0.0);
1280  constraint_names_.resize(new_index, "");
1281 
1282  // Remove the rows from the matrix.
1283  matrix_.DeleteRows(new_index, permutation);
1284 
1285  // Remove the id of the deleted rows and adjust the index of the other.
1286  absl::flat_hash_map<std::string, RowIndex>::iterator it =
1287  constraint_table_.begin();
1288  while (it != constraint_table_.end()) {
1289  const RowIndex row = it->second;
1290  if (permutation[row] != kInvalidRow) {
1291  it->second = permutation[row];
1292  ++it;
1293  } else {
1294  // This safely deletes the entry and moves the iterator one step ahead.
1295  constraint_table_.erase(it++);
1296  }
1297  }
1298 
1299  // Eventually update transpose_matrix_.
1300  if (transpose_matrix_is_consistent_) {
1301  transpose_matrix_.DeleteColumns(
1302  reinterpret_cast<const DenseBooleanRow&>(rows_to_delete));
1303  }
1304 }
1305 
1306 bool LinearProgram::IsValid(Fractional max_valid_magnitude) const {
1307  if (!IsFinite(objective_offset_)) return false;
1308  if (std::abs(objective_offset_) > max_valid_magnitude) return false;
1309 
1310  if (!IsFinite(objective_scaling_factor_)) return false;
1311  if (objective_scaling_factor_ == 0.0) return false;
1312  if (std::abs(objective_scaling_factor_) > max_valid_magnitude) return false;
1313 
1314  const ColIndex num_cols = num_variables();
1315  for (ColIndex col(0); col < num_cols; ++col) {
1316  const Fractional lb = variable_lower_bounds()[col];
1317  const Fractional ub = variable_upper_bounds()[col];
1318  if (!AreBoundsValid(lb, ub)) return false;
1319  if (IsFinite(lb) && std::abs(lb) > max_valid_magnitude) return false;
1320  if (IsFinite(ub) && std::abs(ub) > max_valid_magnitude) return false;
1321 
1322  if (!IsFinite(objective_coefficients()[col])) {
1323  return false;
1324  }
1325  if (std::abs(objective_coefficients()[col]) > max_valid_magnitude) {
1326  return false;
1327  }
1328 
1329  for (const SparseColumn::Entry e : GetSparseColumn(col)) {
1330  if (!IsFinite(e.coefficient())) return false;
1331  if (std::abs(e.coefficient()) > max_valid_magnitude) return false;
1332  }
1333  }
1334  if (constraint_upper_bounds_.size() != constraint_lower_bounds_.size()) {
1335  return false;
1336  }
1337  for (RowIndex row(0); row < constraint_lower_bounds_.size(); ++row) {
1338  const Fractional lb = constraint_lower_bounds()[row];
1339  const Fractional ub = constraint_upper_bounds()[row];
1340  if (!AreBoundsValid(lb, ub)) return false;
1341  if (IsFinite(lb) && std::abs(lb) > max_valid_magnitude) return false;
1342  if (IsFinite(ub) && std::abs(ub) > max_valid_magnitude) return false;
1343  }
1344  return true;
1345 }
1346 
1347 std::string LinearProgram::ProblemStatFormatter(
1348  const absl::string_view format) const {
1349  int num_objective_non_zeros = 0;
1350  int num_non_negative_variables = 0;
1351  int num_boxed_variables = 0;
1352  int num_free_variables = 0;
1353  int num_fixed_variables = 0;
1354  int num_other_variables = 0;
1355  const ColIndex num_cols = num_variables();
1356  for (ColIndex col(0); col < num_cols; ++col) {
1357  if (objective_coefficients()[col] != 0.0) {
1358  ++num_objective_non_zeros;
1359  }
1360 
1363  const bool lower_bounded = (lower_bound != -kInfinity);
1364  const bool upper_bounded = (upper_bound != kInfinity);
1365 
1366  if (!lower_bounded && !upper_bounded) {
1367  ++num_free_variables;
1368  } else if (lower_bound == 0.0 && !upper_bounded) {
1369  ++num_non_negative_variables;
1370  } else if (!upper_bounded || !lower_bounded) {
1371  ++num_other_variables;
1372  } else if (lower_bound == upper_bound) {
1373  ++num_fixed_variables;
1374  } else {
1375  ++num_boxed_variables;
1376  }
1377  }
1378 
1379  int num_range_constraints = 0;
1380  int num_less_than_constraints = 0;
1381  int num_greater_than_constraints = 0;
1382  int num_equal_constraints = 0;
1383  int num_rhs_non_zeros = 0;
1384  const RowIndex num_rows = num_constraints();
1385  for (RowIndex row(0); row < num_rows; ++row) {
1388  if (AreBoundsFreeOrBoxed(lower_bound, upper_bound)) {
1389  // TODO(user): we currently count a free row as a range constraint.
1390  // Add a new category?
1391  ++num_range_constraints;
1392  continue;
1393  }
1394  if (lower_bound == upper_bound) {
1395  ++num_equal_constraints;
1396  if (lower_bound != 0) {
1397  ++num_rhs_non_zeros;
1398  }
1399  continue;
1400  }
1401  if (lower_bound == -kInfinity) {
1402  ++num_less_than_constraints;
1403  if (upper_bound != 0) {
1404  ++num_rhs_non_zeros;
1405  }
1406  continue;
1407  }
1408  if (upper_bound == kInfinity) {
1409  ++num_greater_than_constraints;
1410  if (lower_bound != 0) {
1411  ++num_rhs_non_zeros;
1412  }
1413  continue;
1414  }
1415  LOG(DFATAL) << "There is a bug since all possible cases for the row bounds "
1416  "should have been accounted for. row="
1417  << row;
1418  }
1419 
1420  const int num_integer_variables = IntegerVariablesList().size();
1421  const int num_binary_variables = BinaryVariablesList().size();
1422  const int num_non_binary_variables = NonBinaryVariablesList().size();
1423  const int num_continuous_variables =
1424  ColToIntIndex(num_variables()) - num_integer_variables;
1425  auto format_runtime =
1426  absl::ParsedFormat<'d', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'd',
1427  'd', 'd', 'd', 'd', 'd', 'd', 'd'>::New(format);
1428  CHECK(format_runtime);
1429  return absl::StrFormat(
1430  *format_runtime, RowToIntIndex(num_constraints()),
1431  ColToIntIndex(num_variables()), matrix_.num_entries().value(),
1432  num_objective_non_zeros, num_rhs_non_zeros, num_less_than_constraints,
1433  num_greater_than_constraints, num_equal_constraints,
1434  num_range_constraints, num_non_negative_variables, num_boxed_variables,
1435  num_free_variables, num_fixed_variables, num_other_variables,
1436  num_integer_variables, num_binary_variables, num_non_binary_variables,
1437  num_continuous_variables);
1438 }
1439 
1440 std::string LinearProgram::NonZeroStatFormatter(
1441  const absl::string_view format) const {
1442  StrictITIVector<RowIndex, EntryIndex> num_entries_in_row(num_constraints(),
1443  EntryIndex(0));
1444  StrictITIVector<ColIndex, EntryIndex> num_entries_in_column(num_variables(),
1445  EntryIndex(0));
1446  EntryIndex num_entries(0);
1447  const ColIndex num_cols = num_variables();
1448  for (ColIndex col(0); col < num_cols; ++col) {
1449  const SparseColumn& sparse_column = GetSparseColumn(col);
1450  num_entries += sparse_column.num_entries();
1451  num_entries_in_column[col] = sparse_column.num_entries();
1452  for (const SparseColumn::Entry e : sparse_column) {
1453  ++num_entries_in_row[e.row()];
1454  }
1455  }
1456 
1457  // To avoid division by 0 if there are no columns or no rows, we set
1458  // height and width to be at least one.
1459  const int64_t height = std::max(RowToIntIndex(num_constraints()), 1);
1460  const int64_t width = std::max(ColToIntIndex(num_variables()), 1);
1461  const double fill_rate = 100.0 * static_cast<double>(num_entries.value()) /
1462  static_cast<double>(height * width);
1463 
1464  auto format_runtime =
1465  absl::ParsedFormat<'f', 'd', 'f', 'f', 'd', 'f', 'f'>::New(format);
1466  return absl::StrFormat(
1467  *format_runtime, fill_rate, GetMaxElement(num_entries_in_row).value(),
1468  Average(num_entries_in_row), StandardDeviation(num_entries_in_row),
1469  GetMaxElement(num_entries_in_column).value(),
1470  Average(num_entries_in_column), StandardDeviation(num_entries_in_column));
1471 }
1472 
1473 void LinearProgram::ResizeRowsIfNeeded(RowIndex row) {
1474  DCHECK_GE(row, 0);
1475  if (row >= num_constraints()) {
1476  transpose_matrix_is_consistent_ = false;
1477  matrix_.SetNumRows(row + 1);
1478  constraint_lower_bounds_.resize(row + 1, Fractional(0.0));
1479  constraint_upper_bounds_.resize(row + 1, Fractional(0.0));
1480  constraint_names_.resize(row + 1, "");
1481  }
1482 }
1483 
1485  for (RowIndex constraint(0); constraint < num_constraints(); ++constraint) {
1486  if (constraint_lower_bounds_[constraint] != 0.0 ||
1487  constraint_upper_bounds_[constraint] != 0.0) {
1488  return false;
1489  }
1490  }
1491  const ColIndex num_slack_variables =
1493  return num_constraints().value() == num_slack_variables.value() &&
1495 }
1496 
1498  Fractional tolerance) const {
1499  for (const ColIndex col : IntegerVariablesList()) {
1500  if ((IsFinite(variable_lower_bounds_[col]) &&
1501  !IsIntegerWithinTolerance(variable_lower_bounds_[col], tolerance)) ||
1502  (IsFinite(variable_upper_bounds_[col]) &&
1503  !IsIntegerWithinTolerance(variable_upper_bounds_[col], tolerance))) {
1504  VLOG(1) << "Bounds of variable " << col.value() << " are non-integer ("
1505  << variable_lower_bounds_[col] << ", "
1506  << variable_upper_bounds_[col] << ").";
1507  return false;
1508  }
1509  }
1510  return true;
1511 }
1512 
1514  Fractional tolerance) const {
1515  // Using transpose for this is faster (complexity = O(number of non zeros in
1516  // matrix)) than directly iterating through entries (complexity = O(number of
1517  // constraints * number of variables)).
1518  const SparseMatrix& transpose = GetTransposeSparseMatrix();
1519  for (RowIndex row = RowIndex(0); row < num_constraints(); ++row) {
1520  bool integer_constraint = true;
1521  for (const SparseColumn::Entry var : transpose.column(RowToColIndex(row))) {
1522  if (!IsVariableInteger(RowToColIndex(var.row()))) {
1523  integer_constraint = false;
1524  break;
1525  }
1526 
1527  // To match what the IntegerBoundsPreprocessor is doing, we require all
1528  // coefficient to be EXACTLY integer here.
1529  if (std::round(var.coefficient()) != var.coefficient()) {
1530  integer_constraint = false;
1531  break;
1532  }
1533  }
1534  if (integer_constraint) {
1535  if ((IsFinite(constraint_lower_bounds_[row]) &&
1536  !IsIntegerWithinTolerance(constraint_lower_bounds_[row],
1537  tolerance)) ||
1538  (IsFinite(constraint_upper_bounds_[row]) &&
1539  !IsIntegerWithinTolerance(constraint_upper_bounds_[row],
1540  tolerance))) {
1541  VLOG(1) << "Bounds of constraint " << row.value()
1542  << " are non-integer (" << constraint_lower_bounds_[row] << ", "
1543  << constraint_upper_bounds_[row] << ").";
1544  return false;
1545  }
1546  }
1547  }
1548  return true;
1549 }
1550 
1551 // --------------------------------------------------------
1552 // ProblemSolution
1553 // --------------------------------------------------------
1554 std::string ProblemSolution::DebugString() const {
1555  std::string s = "Problem status: " + GetProblemStatusString(status);
1556  for (ColIndex col(0); col < primal_values.size(); ++col) {
1557  absl::StrAppendFormat(&s, "\n Var #%d: %s %g", col.value(),
1559  primal_values[col]);
1560  }
1561  s += "\n------------------------------";
1562  for (RowIndex row(0); row < dual_values.size(); ++row) {
1563  absl::StrAppendFormat(&s, "\n Constraint #%d: %s %g", row.value(),
1565  dual_values[row]);
1566  }
1567  return s;
1568 }
1569 
1570 } // namespace glop
1571 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
iterator insert(const_iterator pos, const value_type &x)
size_type size() const
bool empty() const
void push_back(const value_type &x)
void swap(StrongVector &x)
SparseMatrix * GetMutableTransposeSparseMatrix()
Definition: lp_data.cc:387
std::string GetObjectiveStatsString() const
Definition: lp_data.cc:453
void SetObjectiveScalingFactor(Fractional objective_scaling_factor)
Definition: lp_data.cc:337
void PopulateFromPermutedLinearProgram(const LinearProgram &lp, const RowPermutation &row_permutation, const ColumnPermutation &col_permutation)
Definition: lp_data.cc:884
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:250
std::string GetVariableName(ColIndex col) const
Definition: lp_data.cc:361
void SetConstraintName(RowIndex row, absl::string_view name)
Definition: lp_data.cc:246
const SparseMatrix & GetTransposeSparseMatrix() const
Definition: lp_data.cc:377
bool SolutionIsWithinVariableBounds(const DenseRow &solution, Fractional absolute_tolerance) const
Definition: lp_data.cc:482
bool BoundsOfIntegerConstraintsAreInteger(Fractional tolerance) const
Definition: lp_data.cc:1513
void SetObjectiveOffset(Fractional objective_offset)
Definition: lp_data.cc:332
void PopulateFromLinearProgram(const LinearProgram &linear_program)
Definition: lp_data.cc:863
std::string GetPrettyProblemStats() const
Definition: lp_data.cc:665
bool SolutionIsMIPFeasible(const DenseRow &solution, Fractional absolute_tolerance) const
Definition: lp_data.cc:530
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition: lp_data.cc:318
bool BoundsOfIntegerVariablesAreInteger(Fractional tolerance) const
Definition: lp_data.cc:1497
void SetVariableName(ColIndex col, absl::string_view name)
Definition: lp_data.cc:233
std::string DumpSolution(const DenseRow &variable_values) const
Definition: lp_data.cc:648
ColIndex GetSlackVariable(RowIndex row) const
Definition: lp_data.cc:756
const DenseRow & variable_lower_bounds() const
Definition: lp_data.h:230
ColIndex FindOrCreateVariable(const std::string &variable_id)
Definition: lp_data.cc:206
const DenseColumn & constraint_lower_bounds() const
Definition: lp_data.h:216
std::string GetBoundsStatsString() const
Definition: lp_data.cc:466
Fractional ScaleObjective(GlopParameters::CostScalingAlgorithm method)
Definition: lp_data.cc:1189
bool IsValid(Fractional max_valid_magnitude=kInfinity) const
Definition: lp_data.cc:1306
const std::vector< ColIndex > & BinaryVariablesList() const
Definition: lp_data.cc:286
const DenseRow & objective_coefficients() const
Definition: lp_data.h:224
Fractional RemoveObjectiveScalingAndOffset(Fractional value) const
Definition: lp_data.cc:556
const std::vector< ColIndex > & IntegerVariablesList() const
Definition: lp_data.cc:281
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
Definition: lp_data.cc:420
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:310
ColIndex CreateNewSlackVariable(bool is_integer_slack_variable, Fractional lower_bound, Fractional upper_bound, const std::string &name)
Definition: lp_data.cc:177
VariableType GetVariableType(ColIndex col) const
Definition: lp_data.cc:373
RowIndex FindOrCreateConstraint(const std::string &constraint_id)
Definition: lp_data.cc:219
void Swap(LinearProgram *linear_program)
Definition: lp_data.cc:1032
void AddConstraints(const SparseMatrix &coefficients, const DenseColumn &left_hand_sides, const DenseColumn &right_hand_sides, const StrictITIVector< RowIndex, std::string > &names)
Definition: lp_data.cc:973
std::string GetPrettyNonZeroStats() const
Definition: lp_data.cc:691
void SetVariableType(ColIndex col, VariableType type)
Definition: lp_data.cc:237
const std::vector< ColIndex > & NonBinaryVariablesList() const
Definition: lp_data.cc:291
bool SolutionIsInteger(const DenseRow &solution, Fractional absolute_tolerance) const
Definition: lp_data.cc:518
SparseColumn * GetMutableSparseColumn(ColIndex col)
Definition: lp_data.cc:414
std::string GetConstraintName(RowIndex row) const
Definition: lp_data.cc:367
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
Definition: lp_data.cc:698
const DenseColumn & constraint_upper_bounds() const
Definition: lp_data.h:219
void ComputeSlackVariableValues(DenseRow *solution) const
Definition: lp_data.cc:536
bool SolutionIsLPFeasible(const DenseRow &solution, Fractional absolute_tolerance) const
Definition: lp_data.cc:498
bool IsVariableInteger(ColIndex col) const
Definition: lp_data.cc:296
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:327
bool IsVariableBinary(ColIndex col) const
Definition: lp_data.cc:301
Fractional ApplyObjectiveScalingAndOffset(Fractional value) const
Definition: lp_data.cc:551
void DeleteRows(const DenseBooleanColumn &rows_to_delete)
Definition: lp_data.cc:1259
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
Definition: lp_data.cc:1066
const DenseRow & variable_upper_bounds() const
Definition: lp_data.h:233
bool UpdateVariableBoundsToIntersection(const DenseRow &variable_lower_bounds, const DenseRow &variable_upper_bounds)
Definition: lp_data.cc:1007
void PopulateFromDual(const LinearProgram &dual, RowToColMapping *duplicated_rows)
Definition: lp_data.cc:765
const std::string & name() const
Definition: lp_data.h:76
void PopulateFromLinearProgramVariables(const LinearProgram &linear_program)
Definition: lp_data.cc:936
std::string GetDimensionString() const
Definition: lp_data.cc:426
Fractional objective_scaling_factor() const
Definition: lp_data.h:262
void SetMaximizationProblem(bool maximize)
Definition: lp_data.cc:344
void AddConstraintsWithSlackVariables(const SparseMatrix &coefficients, const DenseColumn &left_hand_sides, const DenseColumn &right_hand_sides, const StrictITIVector< RowIndex, std::string > &names, bool detect_integer_constraints_for_slack)
Definition: lp_data.cc:998
const StrictITIVector< ColIndex, VariableType > variable_types() const
Definition: lp_data.h:238
const SparseColumn & GetSparseColumn(ColIndex col) const
Definition: lp_data.cc:410
void PopulateFromInverse(const Permutation &inverse)
SparseColumn * mutable_column(ColIndex col)
Definition: sparse.h:184
void PopulateFromPermutedMatrix(const Matrix &a, const RowPermutation &row_perm, const ColumnPermutation &inverse_col_perm)
Definition: sparse.cc:217
void PopulateFromTranspose(const Matrix &input)
Definition: sparse.cc:186
void SetNumRows(RowIndex num_rows)
Definition: sparse.cc:148
Fractional LookUpValue(RowIndex row, ColIndex col) const
Definition: sparse.cc:328
void Swap(SparseMatrix *matrix)
Definition: sparse.cc:163
void ComputeMinAndMaxMagnitudes(Fractional *min_magnitude, Fractional *max_magnitude) const
Definition: sparse.cc:374
void DeleteRows(RowIndex num_rows, const RowPermutation &permutation)
Definition: sparse.cc:294
bool AppendRowsFromSparseMatrix(const SparseMatrix &matrix)
Definition: sparse.cc:307
void DeleteColumns(const DenseBooleanRow &columns_to_delete)
Definition: sparse.cc:281
void PopulateFromSparseMatrix(const SparseMatrix &matrix)
Definition: sparse.cc:211
const SparseColumn & column(ColIndex col) const
Definition: sparse.h:183
void PopulateFromZero(RowIndex num_rows, ColIndex num_cols)
Definition: sparse.cc:169
void SetCoefficient(Index index, Fractional value)
void PopulateFromSparseVector(const SparseVector &sparse_vector)
void assign(IntType size, const T &v)
Definition: lp_types.h:312
int64_t height
const std::string name
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Span< const double > coefficients
ColIndex col
Definition: markowitz.cc:186
RowIndex row
Definition: markowitz.cc:185
constexpr ColIndex kInvalidCol(-1)
std::string StringifyMonomial(const Fractional a, const std::string &x, bool fraction)
bool IsRightMostSquareMatrixIdentity(const SparseMatrix &matrix)
bool AreBoundsValid(Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.h:697
constexpr double kEpsilon
Definition: lp_types.h:91
std::string Stringify(const Fractional x, bool fraction)
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
StrictITIVector< ColIndex, Fractional > DenseRow
Definition: lp_types.h:341
std::string GetProblemStatusString(ProblemStatus problem_status)
Definition: lp_types.cc:21
Index ColToIntIndex(ColIndex col)
Definition: lp_types.h:59
constexpr double kInfinity
Definition: lp_types.h:88
std::string GetConstraintStatusString(ConstraintStatus status)
Definition: lp_types.cc:92
ColIndex RowToColIndex(RowIndex row)
Definition: lp_types.h:53
bool IsFinite(Fractional value)
Definition: lp_types.h:95
constexpr RowIndex kInvalidRow(-1)
RowIndex ColToRowIndex(ColIndex col)
Definition: lp_types.h:56
void ApplyPermutation(const Permutation< IndexType > &perm, const ITIVectorType &b, ITIVectorType *result)
Fractional PartialScalarProduct(const DenseRowOrColumn &u, const SparseColumn &v, int max_index)
std::string GetVariableStatusString(VariableStatus status)
Definition: lp_types.cc:73
Index RowToIntIndex(RowIndex row)
Definition: lp_types.h:62
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:269
Collection of objects used to extend the Constraint Solver library.
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
Definition: fp_utils.h:165
int column
Definition: parse_proto.cc:32
IntVar * upper_bound
Definition: routing.cc:1087
IntVar * lower_bound
Definition: routing.cc:1086
const bool maximize_
Definition: search.cc:2592
const std::optional< Range > & range
Definition: statistics.cc:36
const int width
Definition: statistics.cc:37
ConstraintStatusColumn constraint_statuses
Definition: lp_data.h:690
VectorXd variable_lower_bounds
VectorXd variable_upper_bounds
#define VLOG(verboselevel)
Definition: vlog.h:39