22#include "absl/flags/flag.h"
23#include "absl/log/check.h"
24#include "absl/strings/str_cat.h"
25#include "absl/strings/str_format.h"
26#include "google/protobuf/text_format.h"
29#include "ortools/glop/parameters.pb.h"
34#include "ortools/linear_solver/linear_solver.pb.h"
43#ifndef __PORTABLE_PLATFORM__
49 "Tells whether do dump the problem to a protobuf file.");
51 "Whether the proto dump file is compressed.");
53 "Whether the proto dump file is binary.");
55 "Number for the dump file, in the form name-000048.pb. "
56 "If < 0, the file is automatically numbered from the number of "
57 "calls to LPSolver::Solve().");
59 "Directory where dump files are written.");
61 "Base name for dump files. LinearProgram::name_ is used if "
62 "lp_dump_file_basename is empty. If LinearProgram::name_ is "
63 "empty, \"linear_program_dump_file\" is used.");
65 "Override any user parameters with the value of this flag. This is "
66 "interpreted as a GlopParameters proto in text format.");
79void DumpLinearProgramIfRequiredByFlags(
const LinearProgram& linear_program,
81 if (!absl::GetFlag(FLAGS_lp_dump_to_proto_file))
return;
82#ifdef __PORTABLE_PLATFORM__
83 LOG(WARNING) <<
"DumpLinearProgramIfRequiredByFlags(linear_program, num) "
84 "requested for linear_program.name()='"
85 << linear_program.name() <<
"', num=" << num
86 <<
" but is not implemented for this platform.";
88 std::string filename = absl::GetFlag(FLAGS_lp_dump_file_basename);
89 if (filename.empty()) {
90 if (linear_program.name().empty()) {
91 filename =
"linear_program_dump";
93 filename = linear_program.name();
96 const int file_num = absl::GetFlag(FLAGS_lp_dump_file_number) >= 0
97 ? absl::GetFlag(FLAGS_lp_dump_file_number)
99 absl::StrAppendFormat(&filename,
"-%06d.pb", file_num);
100 const std::string filespec =
101 absl::StrCat(absl::GetFlag(FLAGS_lp_dump_dir),
"/", filename);
104 const ProtoWriteFormat write_format = absl::GetFlag(FLAGS_lp_dump_binary_file)
108 absl::GetFlag(FLAGS_lp_dump_compressed_file)));
120std::string LPSolver::GlopVersion() {
124void LPSolver::SetParameters(
const GlopParameters&
parameters) {
126#ifndef __PORTABLE_PLATFORM__
127 if (!absl::GetFlag(FLAGS_glop_params).empty()) {
128 GlopParameters flag_params;
129 CHECK(google::protobuf::TextFormat::ParseFromString(
130 absl::GetFlag(FLAGS_glop_params), &flag_params));
131 parameters_.MergeFrom(flag_params);
136const GlopParameters& LPSolver::GetParameters()
const {
return parameters_; }
138GlopParameters* LPSolver::GetMutableParameters() {
return ¶meters_; }
144 TimeLimit::FromParameters(parameters_);
145 return SolveWithTimeLimit(lp,
time_limit.get());
151 LOG(DFATAL) <<
"SolveWithTimeLimit() called with a nullptr time_limit.";
152 return ProblemStatus::ABNORMAL;
155 num_revised_simplex_iterations_ = 0;
156 DumpLinearProgramIfRequiredByFlags(lp, num_solves_);
160 <<
"\n******************************************************************"
161 "\n* WARNING: Glop will be very slow because it will use DCHECKs *"
162 "\n* to verify the results and the precision of the solver. *"
163 "\n* You can gain at least an order of magnitude speedup by *"
164 "\n* compiling with optimizations enabled and by defining NDEBUG. *"
165 "\n******************************************************************";
168 logger_.EnableLogging(parameters_.log_search_progress());
169 logger_.SetLogToStdOut(parameters_.log_to_stdout());
170 if (!parameters_.log_search_progress() && VLOG_IS_ON(1)) {
171 logger_.EnableLogging(
true);
172 logger_.SetLogToStdOut(
false);
176 if (logger_.LoggingIsEnabled()) {
186 LOG(DFATAL) <<
"The columns of the given linear program should be ordered "
187 <<
"by row and contain no zero coefficients. Call CleanUp() "
188 <<
"on it before calling Solve().";
190 return ProblemStatus::INVALID_PROBLEM;
196 if (!lp.
IsValid(parameters_.max_valid_magnitude())) {
198 "The given linear program is invalid. It contains NaNs, "
199 "coefficients too large or invalid bounds specification.");
201 return ProblemStatus::INVALID_PROBLEM;
205 current_linear_program_.PopulateFromLinearProgram(lp);
215 current_linear_program_.RemoveNearZeroEntries(parameters_.drop_magnitude());
222 const bool postsolve_is_needed =
preprocessor.Run(¤t_linear_program_);
224 if (logger_.LoggingIsEnabled()) {
227 current_linear_program_.GetDimensionString());
229 current_linear_program_.GetObjectiveStatsString());
231 current_linear_program_.GetBoundsStatsString());
236 ProblemSolution
solution(current_linear_program_.num_constraints(),
237 current_linear_program_.num_variables());
248 constraints_dual_ray_.clear();
249 variable_bounds_dual_ray_.clear();
260 if (logger_.LoggingIsEnabled()) {
262 SOLVER_LOG(&logger_,
"objective: ", GetObjectiveValue());
263 SOLVER_LOG(&logger_,
"iterations: ", GetNumberOfSimplexIterations());
273void LPSolver::Clear() {
274 ResizeSolution(RowIndex(0), ColIndex(0));
275 revised_simplex_.reset(
nullptr);
278void LPSolver::SetInitialBasis(
288 case ConstraintStatus::FREE:
291 case ConstraintStatus::AT_LOWER_BOUND:
294 case ConstraintStatus::AT_UPPER_BOUND:
297 case ConstraintStatus::FIXED_VALUE:
300 case ConstraintStatus::BASIC:
305 if (revised_simplex_ ==
nullptr) {
306 revised_simplex_ = std::make_unique<RevisedSimplex>();
307 revised_simplex_->SetLogger(&logger_);
309 revised_simplex_->LoadStateForNextSolve(state);
310 if (parameters_.use_preprocessing()) {
311 LOG(WARNING) <<
"In GLOP, SetInitialBasis() was called but the parameter "
312 "use_preprocessing is true, this will likely not result in "
326Fractional AllowedError(Fractional tolerance, Fractional
value) {
327 return tolerance * std::max(1.0, std::abs(
value));
333ProblemStatus LPSolver::LoadAndVerifySolution(
const LinearProgram& lp,
336 SOLVER_LOG(&logger_,
"Final unscaled solution:");
338 if (!IsProblemSolutionConsistent(lp,
solution)) {
339 SOLVER_LOG(&logger_,
"Inconsistency detected in the solution.");
340 ResizeSolution(lp.num_constraints(), lp.num_variables());
341 return ProblemStatus::ABNORMAL;
345 primal_values_ =
solution.primal_values;
346 dual_values_ =
solution.dual_values;
347 variable_statuses_ =
solution.variable_statuses;
348 constraint_statuses_ =
solution.constraint_statuses;
354 ComputeReducedCosts(lp);
355 const Fractional primal_objective_value = ComputeObjective(lp);
356 const Fractional dual_objective_value = ComputeDualObjective(lp);
357 SOLVER_LOG(&logger_,
"Primal objective (before moving primal/dual values) = ",
359 "%.15E", ProblemObjectiveValue(lp, primal_objective_value)));
360 SOLVER_LOG(&logger_,
"Dual objective (before moving primal/dual values) = ",
361 absl::StrFormat(
"%.15E",
362 ProblemObjectiveValue(lp, dual_objective_value)));
365 if (
status == ProblemStatus::OPTIMAL &&
366 parameters_.provide_strong_optimal_guarantee()) {
367 MovePrimalValuesWithinBounds(lp);
368 MoveDualValuesWithinBounds(lp);
372 problem_objective_value_ = ProblemObjectiveValue(lp, ComputeObjective(lp));
373 SOLVER_LOG(&logger_,
"Primal objective (after moving primal/dual values) = ",
374 absl::StrFormat(
"%.15E", problem_objective_value_));
376 ComputeReducedCosts(lp);
377 ComputeConstraintActivities(lp);
387 bool rhs_perturbation_is_too_large =
false;
388 bool cost_perturbation_is_too_large =
false;
389 bool primal_infeasibility_is_too_large =
false;
390 bool dual_infeasibility_is_too_large =
false;
391 bool primal_residual_is_too_large =
false;
392 bool dual_residual_is_too_large =
false;
395 ComputeMaxRhsPerturbationToEnforceOptimality(lp,
396 &rhs_perturbation_is_too_large);
397 ComputeMaxCostPerturbationToEnforceOptimality(
398 lp, &cost_perturbation_is_too_large);
399 const double primal_infeasibility =
400 ComputePrimalValueInfeasibility(lp, &primal_infeasibility_is_too_large);
401 const double dual_infeasibility =
402 ComputeDualValueInfeasibility(lp, &dual_infeasibility_is_too_large);
403 const double primal_residual =
404 ComputeActivityInfeasibility(lp, &primal_residual_is_too_large);
405 const double dual_residual =
406 ComputeReducedCostInfeasibility(lp, &dual_residual_is_too_large);
411 max_absolute_primal_infeasibility_ =
412 std::max(primal_infeasibility, primal_residual);
413 max_absolute_dual_infeasibility_ =
414 std::max(dual_infeasibility, dual_residual);
415 SOLVER_LOG(&logger_,
"Max. primal infeasibility = ",
416 max_absolute_primal_infeasibility_);
418 "Max. dual infeasibility = ", max_absolute_dual_infeasibility_);
423 const double objective_error_ub = ComputeMaxExpectedObjectiveError(lp);
424 SOLVER_LOG(&logger_,
"Objective error <= ", objective_error_ub);
426 if (
status == ProblemStatus::OPTIMAL &&
427 parameters_.provide_strong_optimal_guarantee()) {
430 if (primal_infeasibility != 0.0 || dual_infeasibility != 0.0) {
431 LOG(ERROR) <<
"Primal/dual values have been moved to their bounds. "
432 <<
"Therefore the primal/dual infeasibilities should be "
433 <<
"exactly zero (but not the residuals). If this message "
434 <<
"appears, there is probably a bug in "
435 <<
"MovePrimalValuesWithinBounds() or in "
436 <<
"MoveDualValuesWithinBounds().";
438 if (rhs_perturbation_is_too_large) {
439 SOLVER_LOG(&logger_,
"The needed rhs perturbation is too large !!");
440 if (parameters_.change_status_to_imprecise()) {
441 status = ProblemStatus::IMPRECISE;
444 if (cost_perturbation_is_too_large) {
445 SOLVER_LOG(&logger_,
"The needed cost perturbation is too large !!");
446 if (parameters_.change_status_to_imprecise()) {
447 status = ProblemStatus::IMPRECISE;
455 if (
status == ProblemStatus::OPTIMAL) {
456 if (std::abs(primal_objective_value - dual_objective_value) >
457 objective_error_ub) {
459 "The objective gap of the final solution is too large.");
460 if (parameters_.change_status_to_imprecise()) {
461 status = ProblemStatus::IMPRECISE;
465 if ((
status == ProblemStatus::OPTIMAL ||
466 status == ProblemStatus::PRIMAL_FEASIBLE) &&
467 (primal_residual_is_too_large || primal_infeasibility_is_too_large)) {
469 "The primal infeasibility of the final solution is too large.");
470 if (parameters_.change_status_to_imprecise()) {
471 status = ProblemStatus::IMPRECISE;
474 if ((
status == ProblemStatus::OPTIMAL ||
475 status == ProblemStatus::DUAL_FEASIBLE) &&
476 (dual_residual_is_too_large || dual_infeasibility_is_too_large)) {
478 "The dual infeasibility of the final solution is too large.");
479 if (parameters_.change_status_to_imprecise()) {
480 status = ProblemStatus::IMPRECISE;
484 may_have_multiple_solutions_ =
485 (
status == ProblemStatus::OPTIMAL) ? IsOptimalSolutionOnFacet(lp) :
false;
489bool LPSolver::IsOptimalSolutionOnFacet(
const LinearProgram& lp) {
494 const double kReducedCostTolerance = 1e-9;
495 const double kBoundTolerance = 1e-7;
496 const ColIndex num_cols = lp.num_variables();
497 for (ColIndex
col(0);
col < num_cols; ++
col) {
498 if (variable_statuses_[
col] == VariableStatus::FIXED_VALUE)
continue;
503 kReducedCostTolerance) &&
509 const RowIndex num_rows = lp.num_constraints();
510 for (RowIndex
row(0);
row < num_rows; ++
row) {
511 if (constraint_statuses_[
row] == ConstraintStatus::FIXED_VALUE)
continue;
516 kReducedCostTolerance) &&
525Fractional LPSolver::GetObjectiveValue()
const {
526 return problem_objective_value_;
529Fractional LPSolver::GetMaximumPrimalInfeasibility()
const {
530 return max_absolute_primal_infeasibility_;
533Fractional LPSolver::GetMaximumDualInfeasibility()
const {
534 return max_absolute_dual_infeasibility_;
537bool LPSolver::MayHaveMultipleOptimalSolutions()
const {
538 return may_have_multiple_solutions_;
541int LPSolver::GetNumberOfSimplexIterations()
const {
542 return num_revised_simplex_iterations_;
545double LPSolver::DeterministicTime()
const {
546 return revised_simplex_ ==
nullptr ? 0.0
547 : revised_simplex_->DeterministicTime();
550void LPSolver::MovePrimalValuesWithinBounds(
const LinearProgram& lp) {
552 DCHECK_EQ(num_cols, primal_values_.size());
554 for (ColIndex
col(0);
col < num_cols; ++
col) {
564 SOLVER_LOG(&logger_,
"Max. primal values move = ", error);
567void LPSolver::MoveDualValuesWithinBounds(
const LinearProgram& lp) {
568 const RowIndex num_rows = lp.num_constraints();
569 DCHECK_EQ(num_rows, dual_values_.size());
570 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
571 Fractional error = 0.0;
572 for (RowIndex
row(0);
row < num_rows; ++
row) {
577 Fractional minimization_dual_value = optimization_sign * dual_values_[
row];
578 if (
lower_bound == -kInfinity && minimization_dual_value > 0.0) {
579 error = std::max(error, minimization_dual_value);
580 minimization_dual_value = 0.0;
582 if (
upper_bound == kInfinity && minimization_dual_value < 0.0) {
583 error = std::max(error, -minimization_dual_value);
584 minimization_dual_value = 0.0;
586 dual_values_[
row] = optimization_sign * minimization_dual_value;
588 SOLVER_LOG(&logger_,
"Max. dual values move = ", error);
591void LPSolver::ResizeSolution(RowIndex num_rows, ColIndex num_cols) {
592 primal_values_.resize(num_cols, 0.0);
593 reduced_costs_.resize(num_cols, 0.0);
594 variable_statuses_.resize(num_cols, VariableStatus::FREE);
596 dual_values_.resize(num_rows, 0.0);
597 constraint_activities_.resize(num_rows, 0.0);
598 constraint_statuses_.resize(num_rows, ConstraintStatus::FREE);
601void LPSolver::RunRevisedSimplexIfNeeded(ProblemSolution*
solution,
610 current_linear_program_.ClearTransposeMatrix();
611 if (
solution->status != ProblemStatus::INIT)
return;
612 if (revised_simplex_ ==
nullptr) {
613 revised_simplex_ = std::make_unique<RevisedSimplex>();
614 revised_simplex_->SetLogger(&logger_);
616 revised_simplex_->SetParameters(parameters_);
617 if (revised_simplex_->Solve(current_linear_program_,
time_limit).ok()) {
618 num_revised_simplex_iterations_ = revised_simplex_->GetNumberOfIterations();
619 solution->status = revised_simplex_->GetProblemStatus();
622 const ColIndex num_cols =
solution->primal_values.size();
623 DCHECK_LE(num_cols, revised_simplex_->GetProblemNumCols());
624 for (ColIndex
col(0);
col < num_cols; ++
col) {
625 solution->primal_values[
col] = revised_simplex_->GetVariableValue(
col);
627 revised_simplex_->GetVariableStatus(
col);
629 const RowIndex num_rows = revised_simplex_->GetProblemNumRows();
630 DCHECK_EQ(
solution->dual_values.size(), num_rows);
631 for (RowIndex
row(0);
row < num_rows; ++
row) {
634 revised_simplex_->GetConstraintStatus(
row);
636 if (!parameters_.use_preprocessing() && !parameters_.use_scaling()) {
637 if (
solution->status == ProblemStatus::PRIMAL_UNBOUNDED) {
638 primal_ray_ = revised_simplex_->GetPrimalRay();
640 primal_ray_.resize(num_cols);
641 }
else if (
solution->status == ProblemStatus::DUAL_UNBOUNDED) {
642 constraints_dual_ray_ = revised_simplex_->GetDualRay();
643 variable_bounds_dual_ray_ =
644 revised_simplex_->GetDualRayRowCombination();
646 variable_bounds_dual_ray_.resize(num_cols);
657 if (current_linear_program_.IsMaximizationProblem()) {
665 SOLVER_LOG(&logger_,
"Error during the revised simplex algorithm.");
666 solution->status = ProblemStatus::ABNORMAL;
672void LogVariableStatusError(ColIndex
col, Fractional
value,
673 VariableStatus
status, Fractional lb,
675 VLOG(1) <<
"Variable " <<
col <<
" status is "
677 <<
" and its bounds are [" << lb <<
", " << ub <<
"].";
680void LogConstraintStatusError(RowIndex
row, ConstraintStatus
status,
681 Fractional lb, Fractional ub) {
682 VLOG(1) <<
"Constraint " <<
row <<
" status is "
684 <<
", " << ub <<
"].";
689bool LPSolver::IsProblemSolutionConsistent(
690 const LinearProgram& lp,
const ProblemSolution&
solution)
const {
691 const RowIndex num_rows = lp.num_constraints();
692 const ColIndex num_cols = lp.num_variables();
693 if (
solution.variable_statuses.size() != num_cols)
return false;
694 if (
solution.constraint_statuses.size() != num_rows)
return false;
695 if (
solution.primal_values.size() != num_cols)
return false;
696 if (
solution.dual_values.size() != num_rows)
return false;
697 if (
solution.status != ProblemStatus::OPTIMAL &&
698 solution.status != ProblemStatus::PRIMAL_FEASIBLE &&
699 solution.status != ProblemStatus::DUAL_FEASIBLE) {
705 RowIndex num_basic_variables(0);
706 for (ColIndex
col(0);
col < num_cols; ++
col) {
712 case VariableStatus::BASIC:
715 ++num_basic_variables;
717 case VariableStatus::FIXED_VALUE:
732 case VariableStatus::AT_LOWER_BOUND:
733 if (
value != lb || lb == ub) {
738 case VariableStatus::AT_UPPER_BOUND:
746 case VariableStatus::FREE:
747 if (lb != -kInfinity || ub != kInfinity ||
value != 0.0) {
754 for (RowIndex
row(0);
row < num_rows; ++
row) {
764 case ConstraintStatus::BASIC:
765 if (dual_value != 0.0) {
766 VLOG(1) <<
"Constraint " <<
row <<
" is BASIC, but its dual value is "
767 << dual_value <<
" instead of 0.";
770 ++num_basic_variables;
772 case ConstraintStatus::FIXED_VALUE:
776 if (ub - lb > 1e-12) {
777 LogConstraintStatusError(
row,
status, lb, ub);
781 case ConstraintStatus::AT_LOWER_BOUND:
782 if (lb == -kInfinity) {
783 LogConstraintStatusError(
row,
status, lb, ub);
787 case ConstraintStatus::AT_UPPER_BOUND:
788 if (ub == kInfinity) {
789 LogConstraintStatusError(
row,
status, lb, ub);
793 case ConstraintStatus::FREE:
794 if (dual_value != 0.0) {
795 VLOG(1) <<
"Constraint " <<
row <<
" is FREE, but its dual value is "
796 << dual_value <<
" instead of 0.";
799 if (lb != -kInfinity || ub != kInfinity) {
800 LogConstraintStatusError(
row,
status, lb, ub);
809 if (num_basic_variables != num_rows) {
810 VLOG(1) <<
"Wrong number of basic variables: " << num_basic_variables;
820Fractional LPSolver::ComputeMaxCostPerturbationToEnforceOptimality(
821 const LinearProgram& lp,
bool* is_too_large) {
823 const ColIndex num_cols = lp.num_variables();
824 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
825 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
826 for (ColIndex
col(0);
col < num_cols; ++
col) {
830 const Fractional reduced_cost = optimization_sign * reduced_costs_[
col];
832 if (
status == VariableStatus::BASIC ||
status == VariableStatus::FREE ||
833 (
status == VariableStatus::AT_UPPER_BOUND && reduced_cost > 0.0) ||
834 (
status == VariableStatus::AT_LOWER_BOUND && reduced_cost < 0.0)) {
835 max_cost_correction =
836 std::max(max_cost_correction, std::abs(reduced_cost));
838 std::abs(reduced_cost) >
839 AllowedError(tolerance, lp.objective_coefficients()[
col]);
842 SOLVER_LOG(&logger_,
"Max. cost perturbation = ", max_cost_correction);
843 return max_cost_correction;
848Fractional LPSolver::ComputeMaxRhsPerturbationToEnforceOptimality(
849 const LinearProgram& lp,
bool* is_too_large) {
851 const RowIndex num_rows = lp.num_constraints();
852 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
853 for (RowIndex
row(0);
row < num_rows; ++
row) {
863 allowed_error = AllowedError(tolerance,
lower_bound);
864 }
else if (
status == ConstraintStatus::AT_UPPER_BOUND ||
867 allowed_error = AllowedError(tolerance,
upper_bound);
869 max_rhs_correction = std::max(max_rhs_correction, rhs_error);
870 *is_too_large |= rhs_error > allowed_error;
872 SOLVER_LOG(&logger_,
"Max. rhs perturbation = ", max_rhs_correction);
873 return max_rhs_correction;
876void LPSolver::ComputeConstraintActivities(
const LinearProgram& lp) {
877 const RowIndex num_rows = lp.num_constraints();
878 const ColIndex num_cols = lp.num_variables();
879 DCHECK_EQ(num_cols, primal_values_.size());
880 constraint_activities_.assign(num_rows, 0.0);
881 for (ColIndex
col(0);
col < num_cols; ++
col) {
882 lp.GetSparseColumn(
col).AddMultipleToDenseVector(primal_values_[
col],
883 &constraint_activities_);
887void LPSolver::ComputeReducedCosts(
const LinearProgram& lp) {
888 const RowIndex num_rows = lp.num_constraints();
889 const ColIndex num_cols = lp.num_variables();
890 DCHECK_EQ(num_rows, dual_values_.size());
891 reduced_costs_.resize(num_cols, 0.0);
892 for (ColIndex
col(0);
col < num_cols; ++
col) {
893 reduced_costs_[
col] = lp.objective_coefficients()[
col] -
898double LPSolver::ComputeObjective(
const LinearProgram& lp) {
899 const ColIndex num_cols = lp.num_variables();
900 DCHECK_EQ(num_cols, primal_values_.size());
902 for (ColIndex
col(0);
col < num_cols; ++
col) {
903 sum.Add(lp.objective_coefficients()[
col] * primal_values_[
col]);
924double LPSolver::ComputeDualObjective(
const LinearProgram& lp) {
928 const RowIndex num_rows = lp.num_constraints();
929 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
930 for (RowIndex
row(0);
row < num_rows; ++
row) {
935 const Fractional corrected_value = optimization_sign * dual_values_[
row];
936 if (corrected_value > 0.0 &&
lower_bound != -kInfinity) {
939 if (corrected_value < 0.0 &&
upper_bound != kInfinity) {
960 const ColIndex num_cols = lp.num_variables();
961 for (ColIndex
col(0);
col < num_cols; ++
col) {
967 const Fractional reduced_cost = optimization_sign * reduced_costs_[
col];
972 if (variable_statuses_[
col] == VariableStatus::AT_LOWER_BOUND &&
973 reduced_cost > 0.0) {
975 }
else if (variable_statuses_[
col] == VariableStatus::AT_UPPER_BOUND &&
976 reduced_cost < 0.0) {
978 }
else if (variable_statuses_[
col] == VariableStatus::FIXED_VALUE) {
982 dual_objective.Add(optimization_sign * correction);
984 return dual_objective.Value();
987double LPSolver::ComputeMaxExpectedObjectiveError(
const LinearProgram& lp) {
988 const ColIndex num_cols = lp.num_variables();
989 DCHECK_EQ(num_cols, primal_values_.size());
990 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
992 for (ColIndex
col(0);
col < num_cols; ++
col) {
996 primal_objective_error += std::abs(lp.objective_coefficients()[
col]) *
997 AllowedError(tolerance, primal_values_[
col]);
999 return primal_objective_error;
1002double LPSolver::ComputePrimalValueInfeasibility(
const LinearProgram& lp,
1003 bool* is_too_large) {
1004 double infeasibility = 0.0;
1005 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
1006 const ColIndex num_cols = lp.num_variables();
1007 for (ColIndex
col(0);
col < num_cols; ++
col) {
1014 infeasibility = std::max(infeasibility, error);
1015 *is_too_large |= error > AllowedError(tolerance,
upper_bound);
1020 infeasibility = std::max(infeasibility, error);
1021 *is_too_large |= error > AllowedError(tolerance,
upper_bound);
1025 infeasibility = std::max(infeasibility, error);
1026 *is_too_large |= error > AllowedError(tolerance,
lower_bound);
1029 return infeasibility;
1032double LPSolver::ComputeActivityInfeasibility(
const LinearProgram& lp,
1033 bool* is_too_large) {
1034 double infeasibility = 0.0;
1035 int num_problematic_rows(0);
1036 const RowIndex num_rows = lp.num_constraints();
1037 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
1038 for (RowIndex
row(0);
row < num_rows; ++
row) {
1047 VLOG(2) <<
"Row " <<
row.value() <<
" has activity " << activity
1048 <<
" which is different from " <<
upper_bound <<
" by "
1050 ++num_problematic_rows;
1052 infeasibility = std::max(infeasibility, std::abs(activity -
upper_bound));
1057 if (row_excess > AllowedError(tolerance,
upper_bound)) {
1058 VLOG(2) <<
"Row " <<
row.value() <<
" has activity " << activity
1059 <<
", exceeding its upper bound " <<
upper_bound <<
" by "
1061 ++num_problematic_rows;
1063 infeasibility = std::max(infeasibility, row_excess);
1067 if (row_deficit > AllowedError(tolerance,
lower_bound)) {
1068 VLOG(2) <<
"Row " <<
row.value() <<
" has activity " << activity
1069 <<
", below its lower bound " <<
lower_bound <<
" by "
1071 ++num_problematic_rows;
1073 infeasibility = std::max(infeasibility, row_deficit);
1076 if (num_problematic_rows > 0) {
1077 *is_too_large =
true;
1078 VLOG(1) <<
"Number of infeasible rows = " << num_problematic_rows;
1080 return infeasibility;
1083double LPSolver::ComputeDualValueInfeasibility(
const LinearProgram& lp,
1084 bool* is_too_large) {
1085 const Fractional allowed_error = parameters_.solution_feasibility_tolerance();
1086 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
1087 double infeasibility = 0.0;
1088 const RowIndex num_rows = lp.num_constraints();
1089 for (RowIndex
row(0);
row < num_rows; ++
row) {
1094 const Fractional minimization_dual_value = optimization_sign * dual_value;
1096 *is_too_large |= minimization_dual_value > allowed_error;
1097 infeasibility = std::max(infeasibility, minimization_dual_value);
1100 *is_too_large |= -minimization_dual_value > allowed_error;
1101 infeasibility = std::max(infeasibility, -minimization_dual_value);
1104 return infeasibility;
1107double LPSolver::ComputeReducedCostInfeasibility(
const LinearProgram& lp,
1108 bool* is_too_large) {
1109 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
1110 double infeasibility = 0.0;
1111 const ColIndex num_cols = lp.num_variables();
1112 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
1113 for (ColIndex
col(0);
col < num_cols; ++
col) {
1119 optimization_sign * reduced_cost;
1121 AllowedError(tolerance, lp.objective_coefficients()[
col]);
1123 *is_too_large |= minimization_reduced_cost > allowed_error;
1124 infeasibility = std::max(infeasibility, minimization_reduced_cost);
1127 *is_too_large |= -minimization_reduced_cost > allowed_error;
1128 infeasibility = std::max(infeasibility, -minimization_reduced_cost);
1131 return infeasibility;
FpNumber Value() const
Gets the value of the sum.
std::string GetDimensionString() const
A short string with the problem dimension.
bool IsValid(Fractional max_valid_magnitude=kInfinity) const
const DenseRow & variable_lower_bounds() const
const DenseRow & variable_upper_bounds() const
Fractional objective_scaling_factor() const
ColIndex num_variables() const
Returns the number of variables.
std::string GetObjectiveStatsString() const
A short line with some stats on the problem coefficients.
Fractional objective_offset() const
Returns the objective offset and scaling factor.
RowIndex num_constraints() const
Returns the number of constraints.
std::string GetBoundsStatsString() const
void push_back(const value_type &val)
CpModelProto proto
The output proto.
ABSL_FLAG(bool, lp_dump_to_proto_file, false, "Tells whether do dump the problem to a protobuf file.")
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
AccurateSum< Fractional > KahanSum
void LinearProgramToMPModelProto(const LinearProgram &input, MPModelProto *output)
Converts a LinearProgram to a MPModelProto.
std::string GetVariableStatusString(VariableStatus status)
Returns the string representation of the VariableStatus enum.
bool IsFinite(Fractional value)
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
Changes the sign of all the entries in the given vector.
std::string GetConstraintStatusString(ConstraintStatus status)
Returns the string representation of the ConstraintStatus enum.
ProblemStatus
Different statuses for a given problem.
std::string GetProblemStatusString(ProblemStatus problem_status)
Returns the string representation of the ProblemStatus enum.
In SWIG mode, we don't want anything besides these top-level includes.
std::string OrToolsVersionString()
bool AreWithinAbsoluteTolerance(FloatType x, FloatType y, FloatType absolute_tolerance)
std::string ProtobufShortDebugString(const P &message)
absl::Status WriteProtoToFile(absl::string_view filename, const google::protobuf::Message &proto, ProtoWriteFormat proto_write_format, bool gzipped, bool append_extension_to_file_name)
glop::MainLpPreprocessor preprocessor
VariableStatusRow statuses
#define SOLVER_LOG(logger,...)