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