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