OR-Tools  9.3
lp_solver.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <cmath>
17#include <stack>
18#include <vector>
19
20#include "absl/memory/memory.h"
21#include "absl/strings/match.h"
22#include "absl/strings/str_format.h"
25#include "ortools/base/timer.h"
27#include "ortools/glop/status.h"
32
33// TODO(user): abstract this in some way to the port directory.
34#ifndef __PORTABLE_PLATFORM__
36#endif
37
38ABSL_FLAG(bool, lp_dump_to_proto_file, false,
39 "Tells whether do dump the problem to a protobuf file.");
40ABSL_FLAG(bool, lp_dump_compressed_file, true,
41 "Whether the proto dump file is compressed.");
42ABSL_FLAG(bool, lp_dump_binary_file, false,
43 "Whether the proto dump file is binary.");
44ABSL_FLAG(int, lp_dump_file_number, -1,
45 "Number for the dump file, in the form name-000048.pb. "
46 "If < 0, the file is automatically numbered from the number of "
47 "calls to LPSolver::Solve().");
48ABSL_FLAG(std::string, lp_dump_dir, "/tmp",
49 "Directory where dump files are written.");
50ABSL_FLAG(std::string, lp_dump_file_basename, "",
51 "Base name for dump files. LinearProgram::name_ is used if "
52 "lp_dump_file_basename is empty. If LinearProgram::name_ is "
53 "empty, \"linear_program_dump_file\" is used.");
54ABSL_FLAG(std::string, glop_params, "",
55 "Override any user parameters with the value of this flag. This is "
56 "interpreted as a GlopParameters proto in text format.");
57
58namespace operations_research {
59namespace glop {
60namespace {
61
62// Writes a LinearProgram to a file if FLAGS_lp_dump_to_proto_file is true. The
63// integer num is appended to the base name of the file. When this function is
64// called from LPSolver::Solve(), num is usually the number of times Solve() was
65// called. For a LinearProgram whose name is "LinPro", and num = 48, the default
66// output file will be /tmp/LinPro-000048.pb.gz.
67//
68// Warning: is a no-op on portable platforms (android, ios, etc).
69void DumpLinearProgramIfRequiredByFlags(const LinearProgram& linear_program,
70 int num) {
71 if (!absl::GetFlag(FLAGS_lp_dump_to_proto_file)) return;
72#ifdef __PORTABLE_PLATFORM__
73 LOG(WARNING) << "DumpLinearProgramIfRequiredByFlags(linear_program, num) "
74 "requested for linear_program.name()='"
75 << linear_program.name() << "', num=" << num
76 << " but is not implemented for this platform.";
77#else
78 std::string filename = absl::GetFlag(FLAGS_lp_dump_file_basename);
79 if (filename.empty()) {
80 if (linear_program.name().empty()) {
81 filename = "linear_program_dump";
82 } else {
83 filename = linear_program.name();
84 }
85 }
86 const int file_num = absl::GetFlag(FLAGS_lp_dump_file_number) >= 0
87 ? absl::GetFlag(FLAGS_lp_dump_file_number)
88 : num;
89 absl::StrAppendFormat(&filename, "-%06d.pb", file_num);
90 const std::string filespec =
91 absl::StrCat(absl::GetFlag(FLAGS_lp_dump_dir), "/", filename);
92 MPModelProto proto;
93 LinearProgramToMPModelProto(linear_program, &proto);
94 const ProtoWriteFormat write_format = absl::GetFlag(FLAGS_lp_dump_binary_file)
97 if (!WriteProtoToFile(filespec, proto, write_format,
98 absl::GetFlag(FLAGS_lp_dump_compressed_file))) {
99 LOG(DFATAL) << "Could not write " << filespec;
100 }
101#endif
102}
103
104} // anonymous namespace
105
106// --------------------------------------------------------
107// LPSolver
108// --------------------------------------------------------
109
110LPSolver::LPSolver() : num_solves_(0) {}
111
112void LPSolver::SetParameters(const GlopParameters& parameters) {
113 parameters_ = parameters;
114#ifndef __PORTABLE_PLATFORM__
115 if (!absl::GetFlag(FLAGS_glop_params).empty()) {
116 GlopParameters flag_params;
117 CHECK(google::protobuf::TextFormat::ParseFromString(
118 absl::GetFlag(FLAGS_glop_params), &flag_params));
119 parameters_.MergeFrom(flag_params);
120 }
121#endif
122}
123
124const GlopParameters& LPSolver::GetParameters() const { return parameters_; }
125
126GlopParameters* LPSolver::GetMutableParameters() { return &parameters_; }
127
129
131 std::unique_ptr<TimeLimit> time_limit =
132 TimeLimit::FromParameters(parameters_);
133 return SolveWithTimeLimit(lp, time_limit.get());
134}
135
138 if (time_limit == nullptr) {
139 LOG(DFATAL) << "SolveWithTimeLimit() called with a nullptr time_limit.";
141 }
142 ++num_solves_;
143 num_revised_simplex_iterations_ = 0;
144 DumpLinearProgramIfRequiredByFlags(lp, num_solves_);
145
146 // Display a warning if running in non-opt, unless we're inside a unit test.
148 << "\n******************************************************************"
149 "\n* WARNING: Glop will be very slow because it will use DCHECKs *"
150 "\n* to verify the results and the precision of the solver. *"
151 "\n* You can gain at least an order of magnitude speedup by *"
152 "\n* compiling with optimizations enabled and by defining NDEBUG. *"
153 "\n******************************************************************";
154
155 // Setup the logger.
156 logger_.EnableLogging(parameters_.log_search_progress());
157 logger_.SetLogToStdOut(parameters_.log_to_stdout());
158 if (!parameters_.log_search_progress() && VLOG_IS_ON(1)) {
159 logger_.EnableLogging(true);
160 logger_.SetLogToStdOut(false);
161 }
162
163 // Log some initial info about the input model.
164 if (logger_.LoggingIsEnabled()) {
165 SOLVER_LOG(&logger_, "");
166 SOLVER_LOG(&logger_, "Initial problem: ", lp.GetDimensionString());
167 SOLVER_LOG(&logger_, "Objective stats: ", lp.GetObjectiveStatsString());
168 SOLVER_LOG(&logger_, "Bounds stats: ", lp.GetBoundsStatsString());
169 }
170
171 // Check some preconditions.
172 if (!lp.IsCleanedUp()) {
173 LOG(DFATAL) << "The columns of the given linear program should be ordered "
174 << "by row and contain no zero coefficients. Call CleanUp() "
175 << "on it before calling Solve().";
176 ResizeSolution(lp.num_constraints(), lp.num_variables());
178 }
179
180 // TODO(user): Unfortunately we are not really helpful with the error message
181 // here. We could do a better job. However most client should talk to glop via
182 // an input protocol buffer which should have better validation messages.
183 if (!lp.IsValid(parameters_.max_valid_magnitude())) {
184 SOLVER_LOG(&logger_,
185 "The given linear program is invalid. It contains NaNs, "
186 "coefficients too large or invalid bounds specification.");
187 ResizeSolution(lp.num_constraints(), lp.num_variables());
189 }
190
191 // Make an internal copy of the problem for the preprocessing.
192 current_linear_program_.PopulateFromLinearProgram(lp);
193
194 // Preprocess.
195 MainLpPreprocessor preprocessor(&parameters_);
196 preprocessor.SetLogger(&logger_);
197 preprocessor.SetTimeLimit(time_limit);
198
199 const bool postsolve_is_needed = preprocessor.Run(&current_linear_program_);
200
201 if (logger_.LoggingIsEnabled()) {
202 SOLVER_LOG(&logger_, "");
203 SOLVER_LOG(&logger_, "Presolved problem: ",
204 current_linear_program_.GetDimensionString());
205 SOLVER_LOG(&logger_, "Objective stats: ",
206 current_linear_program_.GetObjectiveStatsString());
207 SOLVER_LOG(&logger_, "Bounds stats: ",
208 current_linear_program_.GetBoundsStatsString());
209 }
210
211 // At this point, we need to initialize a ProblemSolution with the correct
212 // size and status.
213 ProblemSolution solution(current_linear_program_.num_constraints(),
214 current_linear_program_.num_variables());
215 solution.status = preprocessor.status();
216
217 // Do not launch the solver if the time limit was already reached. This might
218 // mean that the pre-processors were not all run, and current_linear_program_
219 // might not be in a completely safe state.
220 if (!time_limit->LimitReached()) {
221 RunRevisedSimplexIfNeeded(&solution, time_limit);
222 }
223 if (postsolve_is_needed) preprocessor.DestructiveRecoverSolution(&solution);
224 const ProblemStatus status = LoadAndVerifySolution(lp, solution);
225 // LOG some statistics that can be parsed by our benchmark script.
226 if (logger_.LoggingIsEnabled()) {
227 SOLVER_LOG(&logger_, "status: ", GetProblemStatusString(status));
228 SOLVER_LOG(&logger_, "objective: ", GetObjectiveValue());
229 SOLVER_LOG(&logger_, "iterations: ", GetNumberOfSimplexIterations());
230 SOLVER_LOG(&logger_, "time: ", time_limit->GetElapsedTime());
231 SOLVER_LOG(&logger_, "deterministic_time: ",
232 time_limit->GetElapsedDeterministicTime());
233 SOLVER_LOG(&logger_, "");
234 }
235
236 return status;
237}
238
240 ResizeSolution(RowIndex(0), ColIndex(0));
241 revised_simplex_.reset(nullptr);
242}
243
245 const VariableStatusRow& variable_statuses,
246 const ConstraintStatusColumn& constraint_statuses) {
247 // Create the associated basis state.
248 BasisState state;
251 // Note the change of upper/lower bound between the status of a constraint
252 // and the status of its associated slack variable.
253 switch (status) {
256 break;
259 break;
262 break;
265 break;
268 break;
269 }
270 }
271 if (revised_simplex_ == nullptr) {
272 revised_simplex_ = absl::make_unique<RevisedSimplex>();
273 revised_simplex_->SetLogger(&logger_);
274 }
275 revised_simplex_->LoadStateForNextSolve(state);
276 if (parameters_.use_preprocessing()) {
277 LOG(WARNING) << "In GLOP, SetInitialBasis() was called but the parameter "
278 "use_preprocessing is true, this will likely not result in "
279 "what you want.";
280 }
281}
282
283namespace {
284// Computes the "real" problem objective from the one without offset nor
285// scaling.
286Fractional ProblemObjectiveValue(const LinearProgram& lp, Fractional value) {
287 return lp.objective_scaling_factor() * (value + lp.objective_offset());
288}
289
290// Returns the allowed error magnitude for something that should evaluate to
291// value under the given tolerance.
292Fractional AllowedError(Fractional tolerance, Fractional value) {
293 return tolerance * std::max(1.0, std::abs(value));
294}
295} // namespace
296
297// TODO(user): Try to also check the precision of an INFEASIBLE or UNBOUNDED
298// return status.
300 const ProblemSolution& solution) {
301 SOLVER_LOG(&logger_, "");
302 SOLVER_LOG(&logger_, "Final unscaled solution:");
303
304 if (!IsProblemSolutionConsistent(lp, solution)) {
305 SOLVER_LOG(&logger_, "Inconsistency detected in the solution.");
306 ResizeSolution(lp.num_constraints(), lp.num_variables());
308 }
309
310 // Load the solution.
311 primal_values_ = solution.primal_values;
312 dual_values_ = solution.dual_values;
313 variable_statuses_ = solution.variable_statuses;
314 constraint_statuses_ = solution.constraint_statuses;
315
316 ProblemStatus status = solution.status;
317
318 // Objective before eventually moving the primal/dual values inside their
319 // bounds.
320 ComputeReducedCosts(lp);
321 const Fractional primal_objective_value = ComputeObjective(lp);
322 const Fractional dual_objective_value = ComputeDualObjective(lp);
323 SOLVER_LOG(&logger_, "Primal objective (before moving primal/dual values) = ",
324 absl::StrFormat(
325 "%.15E", ProblemObjectiveValue(lp, primal_objective_value)));
326 SOLVER_LOG(&logger_, "Dual objective (before moving primal/dual values) = ",
327 absl::StrFormat("%.15E",
328 ProblemObjectiveValue(lp, dual_objective_value)));
329
330 // Eventually move the primal/dual values inside their bounds.
332 parameters_.provide_strong_optimal_guarantee()) {
333 MovePrimalValuesWithinBounds(lp);
334 MoveDualValuesWithinBounds(lp);
335 }
336
337 // The reported objective to the user.
338 problem_objective_value_ = ProblemObjectiveValue(lp, ComputeObjective(lp));
339 SOLVER_LOG(&logger_, "Primal objective (after moving primal/dual values) = ",
340 absl::StrFormat("%.15E", problem_objective_value_));
341
342 ComputeReducedCosts(lp);
343 ComputeConstraintActivities(lp);
344
345 // These will be set to true if the associated "infeasibility" is too large.
346 //
347 // The tolerance used is the parameter solution_feasibility_tolerance. To be
348 // somewhat independent of the original problem scaling, the thresholds used
349 // depend of the quantity involved and of its coordinates:
350 // - tolerance * max(1.0, abs(cost[col])) when a reduced cost is infeasible.
351 // - tolerance * max(1.0, abs(bound)) when a bound is crossed.
352 // - tolerance for an infeasible dual value (because the limit is always 0.0).
353 bool rhs_perturbation_is_too_large = false;
354 bool cost_perturbation_is_too_large = false;
355 bool primal_infeasibility_is_too_large = false;
356 bool dual_infeasibility_is_too_large = false;
357 bool primal_residual_is_too_large = false;
358 bool dual_residual_is_too_large = false;
359
360 // Computes all the infeasiblities and update the Booleans above.
361 ComputeMaxRhsPerturbationToEnforceOptimality(lp,
362 &rhs_perturbation_is_too_large);
363 ComputeMaxCostPerturbationToEnforceOptimality(
364 lp, &cost_perturbation_is_too_large);
365 const double primal_infeasibility =
366 ComputePrimalValueInfeasibility(lp, &primal_infeasibility_is_too_large);
367 const double dual_infeasibility =
368 ComputeDualValueInfeasibility(lp, &dual_infeasibility_is_too_large);
369 const double primal_residual =
370 ComputeActivityInfeasibility(lp, &primal_residual_is_too_large);
371 const double dual_residual =
372 ComputeReducedCostInfeasibility(lp, &dual_residual_is_too_large);
373
374 // TODO(user): the name is not really consistent since in practice those are
375 // the "residual" since the primal/dual infeasibility are zero when
376 // parameters_.provide_strong_optimal_guarantee() is true.
377 max_absolute_primal_infeasibility_ =
378 std::max(primal_infeasibility, primal_residual);
379 max_absolute_dual_infeasibility_ =
380 std::max(dual_infeasibility, dual_residual);
381 SOLVER_LOG(&logger_, "Max. primal infeasibility = ",
382 max_absolute_primal_infeasibility_);
383 SOLVER_LOG(&logger_,
384 "Max. dual infeasibility = ", max_absolute_dual_infeasibility_);
385
386 // Now that all the relevant quantities are computed, we check the precision
387 // and optimality of the result. See Chvatal pp. 61-62. If any of the tests
388 // fail, we return the IMPRECISE status.
389 const double objective_error_ub = ComputeMaxExpectedObjectiveError(lp);
390 SOLVER_LOG(&logger_, "Objective error <= ", objective_error_ub);
391
393 parameters_.provide_strong_optimal_guarantee()) {
394 // If the primal/dual values were moved to the bounds, then the primal/dual
395 // infeasibilities should be exactly zero (but not the residuals).
396 if (primal_infeasibility != 0.0 || dual_infeasibility != 0.0) {
397 LOG(ERROR) << "Primal/dual values have been moved to their bounds. "
398 << "Therefore the primal/dual infeasibilities should be "
399 << "exactly zero (but not the residuals). If this message "
400 << "appears, there is probably a bug in "
401 << "MovePrimalValuesWithinBounds() or in "
402 << "MoveDualValuesWithinBounds().";
403 }
404 if (rhs_perturbation_is_too_large) {
405 SOLVER_LOG(&logger_, "The needed rhs perturbation is too large !!");
406 if (parameters_.change_status_to_imprecise()) {
408 }
409 }
410 if (cost_perturbation_is_too_large) {
411 SOLVER_LOG(&logger_, "The needed cost perturbation is too large !!");
412 if (parameters_.change_status_to_imprecise()) {
414 }
415 }
416 }
417
418 // Note that we compare the values without offset nor scaling. We also need to
419 // compare them before we move the primal/dual values, otherwise we lose some
420 // precision since the values are modified independently of each other.
422 if (std::abs(primal_objective_value - dual_objective_value) >
423 objective_error_ub) {
424 SOLVER_LOG(&logger_,
425 "The objective gap of the final solution is too large.");
426 if (parameters_.change_status_to_imprecise()) {
428 }
429 }
430 }
433 (primal_residual_is_too_large || primal_infeasibility_is_too_large)) {
434 SOLVER_LOG(&logger_,
435 "The primal infeasibility of the final solution is too large.");
436 if (parameters_.change_status_to_imprecise()) {
438 }
439 }
442 (dual_residual_is_too_large || dual_infeasibility_is_too_large)) {
443 SOLVER_LOG(&logger_,
444 "The dual infeasibility of the final solution is too large.");
445 if (parameters_.change_status_to_imprecise()) {
447 }
448 }
449
450 may_have_multiple_solutions_ =
451 (status == ProblemStatus::OPTIMAL) ? IsOptimalSolutionOnFacet(lp) : false;
452 return status;
453}
454
455bool LPSolver::IsOptimalSolutionOnFacet(const LinearProgram& lp) {
456 // Note(user): We use the following same two tolerances for the dual and
457 // primal values.
458 // TODO(user): investigate whether to use the tolerances defined in
459 // parameters.proto.
460 const double kReducedCostTolerance = 1e-9;
461 const double kBoundTolerance = 1e-7;
462 const ColIndex num_cols = lp.num_variables();
463 for (ColIndex col(0); col < num_cols; ++col) {
464 if (variable_statuses_[col] == VariableStatus::FIXED_VALUE) continue;
467 const Fractional value = primal_values_[col];
468 if (AreWithinAbsoluteTolerance(reduced_costs_[col], 0.0,
469 kReducedCostTolerance) &&
470 (AreWithinAbsoluteTolerance(value, lower_bound, kBoundTolerance) ||
471 AreWithinAbsoluteTolerance(value, upper_bound, kBoundTolerance))) {
472 return true;
473 }
474 }
475 const RowIndex num_rows = lp.num_constraints();
476 for (RowIndex row(0); row < num_rows; ++row) {
477 if (constraint_statuses_[row] == ConstraintStatus::FIXED_VALUE) continue;
480 const Fractional activity = constraint_activities_[row];
481 if (AreWithinAbsoluteTolerance(dual_values_[row], 0.0,
482 kReducedCostTolerance) &&
483 (AreWithinAbsoluteTolerance(activity, lower_bound, kBoundTolerance) ||
484 AreWithinAbsoluteTolerance(activity, upper_bound, kBoundTolerance))) {
485 return true;
486 }
487 }
488 return false;
489}
490
492 return problem_objective_value_;
493}
494
496 return max_absolute_primal_infeasibility_;
497}
498
500 return max_absolute_dual_infeasibility_;
501}
502
504 return may_have_multiple_solutions_;
505}
506
508 return num_revised_simplex_iterations_;
509}
510
512 return revised_simplex_ == nullptr ? 0.0
513 : revised_simplex_->DeterministicTime();
514}
515
516void LPSolver::MovePrimalValuesWithinBounds(const LinearProgram& lp) {
517 const ColIndex num_cols = lp.num_variables();
518 DCHECK_EQ(num_cols, primal_values_.size());
519 Fractional error = 0.0;
520 for (ColIndex col(0); col < num_cols; ++col) {
524
525 error = std::max(error, primal_values_[col] - upper_bound);
526 error = std::max(error, lower_bound - primal_values_[col]);
527 primal_values_[col] = std::min(primal_values_[col], upper_bound);
528 primal_values_[col] = std::max(primal_values_[col], lower_bound);
529 }
530 SOLVER_LOG(&logger_, "Max. primal values move = ", error);
531}
532
533void LPSolver::MoveDualValuesWithinBounds(const LinearProgram& lp) {
534 const RowIndex num_rows = lp.num_constraints();
535 DCHECK_EQ(num_rows, dual_values_.size());
536 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
537 Fractional error = 0.0;
538 for (RowIndex row(0); row < num_rows; ++row) {
539 const Fractional lower_bound = lp.constraint_lower_bounds()[row];
540 const Fractional upper_bound = lp.constraint_upper_bounds()[row];
541
542 // For a minimization problem, we want a lower bound.
543 Fractional minimization_dual_value = optimization_sign * dual_values_[row];
544 if (lower_bound == -kInfinity && minimization_dual_value > 0.0) {
545 error = std::max(error, minimization_dual_value);
546 minimization_dual_value = 0.0;
547 }
548 if (upper_bound == kInfinity && minimization_dual_value < 0.0) {
549 error = std::max(error, -minimization_dual_value);
550 minimization_dual_value = 0.0;
551 }
552 dual_values_[row] = optimization_sign * minimization_dual_value;
553 }
554 SOLVER_LOG(&logger_, "Max. dual values move = ", error);
555}
556
557void LPSolver::ResizeSolution(RowIndex num_rows, ColIndex num_cols) {
558 primal_values_.resize(num_cols, 0.0);
559 reduced_costs_.resize(num_cols, 0.0);
560 variable_statuses_.resize(num_cols, VariableStatus::FREE);
561
562 dual_values_.resize(num_rows, 0.0);
563 constraint_activities_.resize(num_rows, 0.0);
564 constraint_statuses_.resize(num_rows, ConstraintStatus::FREE);
565}
566
567void LPSolver::RunRevisedSimplexIfNeeded(ProblemSolution* solution,
568 TimeLimit* time_limit) {
569 // Note that the transpose matrix is no longer needed at this point.
570 // This helps reduce the peak memory usage of the solver.
571 //
572 // TODO(user): actually, once the linear_program is loaded into the internal
573 // glop memory, there is no point keeping it around. Add a more complex
574 // Load/Solve API to RevisedSimplex so we can completely reclaim its memory
575 // right away.
576 current_linear_program_.ClearTransposeMatrix();
577 if (solution->status != ProblemStatus::INIT) return;
578 if (revised_simplex_ == nullptr) {
579 revised_simplex_ = absl::make_unique<RevisedSimplex>();
580 revised_simplex_->SetLogger(&logger_);
581 }
582 revised_simplex_->SetParameters(parameters_);
583 if (revised_simplex_->Solve(current_linear_program_, time_limit).ok()) {
584 num_revised_simplex_iterations_ = revised_simplex_->GetNumberOfIterations();
585 solution->status = revised_simplex_->GetProblemStatus();
586
587 // Make sure we do not copy the slacks added by revised_simplex_.
588 const ColIndex num_cols = solution->primal_values.size();
589 DCHECK_LE(num_cols, revised_simplex_->GetProblemNumCols());
590 for (ColIndex col(0); col < num_cols; ++col) {
591 solution->primal_values[col] = revised_simplex_->GetVariableValue(col);
592 solution->variable_statuses[col] =
593 revised_simplex_->GetVariableStatus(col);
594 }
595 const RowIndex num_rows = revised_simplex_->GetProblemNumRows();
596 DCHECK_EQ(solution->dual_values.size(), num_rows);
597 for (RowIndex row(0); row < num_rows; ++row) {
598 solution->dual_values[row] = revised_simplex_->GetDualValue(row);
599 solution->constraint_statuses[row] =
600 revised_simplex_->GetConstraintStatus(row);
601 }
602 if (!parameters_.use_preprocessing() && !parameters_.use_scaling()) {
603 if (solution->status == ProblemStatus::PRIMAL_UNBOUNDED) {
604 primal_ray_ = revised_simplex_->GetPrimalRay();
605 // Make sure we do not copy the slacks added by revised_simplex_.
606 primal_ray_.resize(num_cols);
607 } else if (solution->status == ProblemStatus::DUAL_UNBOUNDED) {
608 constraints_dual_ray_ = revised_simplex_->GetDualRay();
609 variable_bounds_dual_ray_ =
610 revised_simplex_->GetDualRayRowCombination();
611 // Make sure we do not copy the slacks added by revised_simplex_.
612 variable_bounds_dual_ray_.resize(num_cols);
613 // Revised simplex's GetDualRay is always such that GetDualRay.rhs < 0,
614 // which is a cost improving direction for the dual if the primal is a
615 // maximization problem (i.e. when the dual is a minimization problem).
616 // Hence, we change the sign of constraints_dual_ray_ for min problems.
617 //
618 // Revised simplex's GetDualRayRowCombination = A^T GetDualRay and
619 // we must have variable_bounds_dual_ray_ = - A^T constraints_dual_ray_.
620 // Then we need to change the sign of variable_bounds_dual_ray_, but for
621 // min problems this change is implicit because of the sign change of
622 // constraints_dual_ray_ described above.
623 if (current_linear_program_.IsMaximizationProblem()) {
624 ChangeSign(&variable_bounds_dual_ray_);
625 } else {
626 ChangeSign(&constraints_dual_ray_);
627 }
628 }
629 }
630 } else {
631 SOLVER_LOG(&logger_, "Error during the revised simplex algorithm.");
632 solution->status = ProblemStatus::ABNORMAL;
633 }
634}
635
636namespace {
637
638void LogVariableStatusError(ColIndex col, Fractional value,
640 Fractional ub) {
641 VLOG(1) << "Variable " << col << " status is "
642 << GetVariableStatusString(status) << " but its value is " << value
643 << " and its bounds are [" << lb << ", " << ub << "].";
644}
645
646void LogConstraintStatusError(RowIndex row, ConstraintStatus status,
647 Fractional lb, Fractional ub) {
648 VLOG(1) << "Constraint " << row << " status is "
649 << GetConstraintStatusString(status) << " but its bounds are [" << lb
650 << ", " << ub << "].";
651}
652
653} // namespace
654
655bool LPSolver::IsProblemSolutionConsistent(
656 const LinearProgram& lp, const ProblemSolution& solution) const {
657 const RowIndex num_rows = lp.num_constraints();
658 const ColIndex num_cols = lp.num_variables();
659 if (solution.variable_statuses.size() != num_cols) return false;
660 if (solution.constraint_statuses.size() != num_rows) return false;
661 if (solution.primal_values.size() != num_cols) return false;
662 if (solution.dual_values.size() != num_rows) return false;
663 if (solution.status != ProblemStatus::OPTIMAL &&
664 solution.status != ProblemStatus::PRIMAL_FEASIBLE &&
665 solution.status != ProblemStatus::DUAL_FEASIBLE) {
666 return true;
667 }
668
669 // This checks that the variable statuses verify the properties described
670 // in the VariableStatus declaration.
671 RowIndex num_basic_variables(0);
672 for (ColIndex col(0); col < num_cols; ++col) {
673 const Fractional value = solution.primal_values[col];
674 const Fractional lb = lp.variable_lower_bounds()[col];
675 const Fractional ub = lp.variable_upper_bounds()[col];
676 const VariableStatus status = solution.variable_statuses[col];
677 switch (solution.variable_statuses[col]) {
679 // TODO(user): Check that the reduced cost of this column is epsilon
680 // close to zero.
681 ++num_basic_variables;
682 break;
684 // TODO(user): Because of scaling, it is possible that a FIXED_VALUE
685 // status (only reserved for the exact lb == ub case) is now set for a
686 // variable where (ub == lb + epsilon). So we do not check here that the
687 // two bounds are exactly equal. The best is probably to remove the
688 // FIXED status from the API completely and report one of AT_LOWER_BOUND
689 // or AT_UPPER_BOUND instead. This also allows to indicate if at
690 // optimality, the objective is limited because of this variable lower
691 // bound or its upper bound. Note that there are other TODOs in the
692 // codebase about removing this FIXED_VALUE status.
693 if (value != ub && value != lb) {
694 LogVariableStatusError(col, value, status, lb, ub);
695 return false;
696 }
697 break;
699 if (value != lb || lb == ub) {
700 LogVariableStatusError(col, value, status, lb, ub);
701 return false;
702 }
703 break;
705 // TODO(user): revert to an exact comparison once the bug causing this
706 // to fail has been fixed.
707 if (!AreWithinAbsoluteTolerance(value, ub, 1e-7) || lb == ub) {
708 LogVariableStatusError(col, value, status, lb, ub);
709 return false;
710 }
711 break;
713 if (lb != -kInfinity || ub != kInfinity || value != 0.0) {
714 LogVariableStatusError(col, value, status, lb, ub);
715 return false;
716 }
717 break;
718 }
719 }
720 for (RowIndex row(0); row < num_rows; ++row) {
721 const Fractional dual_value = solution.dual_values[row];
722 const Fractional lb = lp.constraint_lower_bounds()[row];
723 const Fractional ub = lp.constraint_upper_bounds()[row];
724 const ConstraintStatus status = solution.constraint_statuses[row];
725
726 // The activity value is not checked since it is imprecise.
727 // TODO(user): Check that the activity is epsilon close to the expected
728 // value.
729 switch (status) {
731 if (dual_value != 0.0) {
732 VLOG(1) << "Constraint " << row << " is BASIC, but its dual value is "
733 << dual_value << " instead of 0.";
734 return false;
735 }
736 ++num_basic_variables;
737 break;
739 // Exactly the same remark as for the VariableStatus::FIXED_VALUE case
740 // above. Because of precision error, this can happen when the
741 // difference between the two bounds is small and not just exactly zero.
742 if (ub - lb > 1e-12) {
743 LogConstraintStatusError(row, status, lb, ub);
744 return false;
745 }
746 break;
748 if (lb == -kInfinity) {
749 LogConstraintStatusError(row, status, lb, ub);
750 return false;
751 }
752 break;
754 if (ub == kInfinity) {
755 LogConstraintStatusError(row, status, lb, ub);
756 return false;
757 }
758 break;
760 if (dual_value != 0.0) {
761 VLOG(1) << "Constraint " << row << " is FREE, but its dual value is "
762 << dual_value << " instead of 0.";
763 return false;
764 }
765 if (lb != -kInfinity || ub != kInfinity) {
766 LogConstraintStatusError(row, status, lb, ub);
767 return false;
768 }
769 break;
770 }
771 }
772
773 // TODO(user): We could check in debug mode (because it will be costly) that
774 // the basis is actually factorizable.
775 if (num_basic_variables != num_rows) {
776 VLOG(1) << "Wrong number of basic variables: " << num_basic_variables;
777 return false;
778 }
779 return true;
780}
781
782// This computes by how much the objective must be perturbed to enforce the
783// following complementary slackness conditions:
784// - Reduced cost is exactly zero for FREE and BASIC variables.
785// - Reduced cost is of the correct sign for variables at their bounds.
786Fractional LPSolver::ComputeMaxCostPerturbationToEnforceOptimality(
787 const LinearProgram& lp, bool* is_too_large) {
788 Fractional max_cost_correction = 0.0;
789 const ColIndex num_cols = lp.num_variables();
790 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
791 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
792 for (ColIndex col(0); col < num_cols; ++col) {
793 // We correct the reduced cost, so we have a minimization problem and
794 // thus the dual objective value will be a lower bound of the primal
795 // objective.
796 const Fractional reduced_cost = optimization_sign * reduced_costs_[col];
797 const VariableStatus status = variable_statuses_[col];
799 (status == VariableStatus::AT_UPPER_BOUND && reduced_cost > 0.0) ||
800 (status == VariableStatus::AT_LOWER_BOUND && reduced_cost < 0.0)) {
801 max_cost_correction =
802 std::max(max_cost_correction, std::abs(reduced_cost));
803 *is_too_large |=
804 std::abs(reduced_cost) >
805 AllowedError(tolerance, lp.objective_coefficients()[col]);
806 }
807 }
808 SOLVER_LOG(&logger_, "Max. cost perturbation = ", max_cost_correction);
809 return max_cost_correction;
810}
811
812// This computes by how much the rhs must be perturbed to enforce the fact that
813// the constraint activities exactly reflect their status.
814Fractional LPSolver::ComputeMaxRhsPerturbationToEnforceOptimality(
815 const LinearProgram& lp, bool* is_too_large) {
816 Fractional max_rhs_correction = 0.0;
817 const RowIndex num_rows = lp.num_constraints();
818 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
819 for (RowIndex row(0); row < num_rows; ++row) {
820 const Fractional lower_bound = lp.constraint_lower_bounds()[row];
821 const Fractional upper_bound = lp.constraint_upper_bounds()[row];
822 const Fractional activity = constraint_activities_[row];
823 const ConstraintStatus status = constraint_statuses_[row];
824
825 Fractional rhs_error = 0.0;
826 Fractional allowed_error = 0.0;
828 rhs_error = std::abs(activity - lower_bound);
829 allowed_error = AllowedError(tolerance, lower_bound);
831 activity > upper_bound) {
832 rhs_error = std::abs(activity - upper_bound);
833 allowed_error = AllowedError(tolerance, upper_bound);
834 }
835 max_rhs_correction = std::max(max_rhs_correction, rhs_error);
836 *is_too_large |= rhs_error > allowed_error;
837 }
838 SOLVER_LOG(&logger_, "Max. rhs perturbation = ", max_rhs_correction);
839 return max_rhs_correction;
840}
841
842void LPSolver::ComputeConstraintActivities(const LinearProgram& lp) {
843 const RowIndex num_rows = lp.num_constraints();
844 const ColIndex num_cols = lp.num_variables();
845 DCHECK_EQ(num_cols, primal_values_.size());
846 constraint_activities_.assign(num_rows, 0.0);
847 for (ColIndex col(0); col < num_cols; ++col) {
848 lp.GetSparseColumn(col).AddMultipleToDenseVector(primal_values_[col],
849 &constraint_activities_);
850 }
851}
852
853void LPSolver::ComputeReducedCosts(const LinearProgram& lp) {
854 const RowIndex num_rows = lp.num_constraints();
855 const ColIndex num_cols = lp.num_variables();
856 DCHECK_EQ(num_rows, dual_values_.size());
857 reduced_costs_.resize(num_cols, 0.0);
858 for (ColIndex col(0); col < num_cols; ++col) {
859 reduced_costs_[col] = lp.objective_coefficients()[col] -
860 ScalarProduct(dual_values_, lp.GetSparseColumn(col));
861 }
862}
863
864double LPSolver::ComputeObjective(const LinearProgram& lp) {
865 const ColIndex num_cols = lp.num_variables();
866 DCHECK_EQ(num_cols, primal_values_.size());
867 KahanSum sum;
868 for (ColIndex col(0); col < num_cols; ++col) {
869 sum.Add(lp.objective_coefficients()[col] * primal_values_[col]);
870 }
871 return sum.Value();
872}
873
874// By the duality theorem, the dual "objective" is a bound on the primal
875// objective obtained by taking the linear combinaison of the constraints
876// given by dual_values_.
877//
878// As it is written now, this has no real precise meaning since we ignore
879// infeasible reduced costs. This is almost the same as computing the objective
880// to the perturbed problem, but then we don't use the pertubed rhs. It is just
881// here as an extra "consistency" check.
882//
883// Note(user): We could actually compute an EXACT lower bound for the cost of
884// the non-cost perturbed problem. The idea comes from "Safe bounds in linear
885// and mixed-integer linear programming", Arnold Neumaier , Oleg Shcherbina,
886// Math Prog, 2003. Note that this requires having some variable bounds that may
887// not be in the original problem so that the current dual solution is always
888// feasible. It also involves changing the rounding mode to obtain exact
889// confidence intervals on the reduced costs.
890double LPSolver::ComputeDualObjective(const LinearProgram& lp) {
891 KahanSum dual_objective;
892
893 // Compute the part coming from the row constraints.
894 const RowIndex num_rows = lp.num_constraints();
895 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
896 for (RowIndex row(0); row < num_rows; ++row) {
897 const Fractional lower_bound = lp.constraint_lower_bounds()[row];
898 const Fractional upper_bound = lp.constraint_upper_bounds()[row];
899
900 // We correct the optimization_sign so we have to compute a lower bound.
901 const Fractional corrected_value = optimization_sign * dual_values_[row];
902 if (corrected_value > 0.0 && lower_bound != -kInfinity) {
903 dual_objective.Add(dual_values_[row] * lower_bound);
904 }
905 if (corrected_value < 0.0 && upper_bound != kInfinity) {
906 dual_objective.Add(dual_values_[row] * upper_bound);
907 }
908 }
909
910 // For a given column associated to a variable x, we want to find a lower
911 // bound for c.x (where c is the objective coefficient for this column). If we
912 // write a.x the linear combination of the constraints at this column we have:
913 // (c + a - c) * x = a * x, and so
914 // c * x = a * x + (c - a) * x
915 // Now, if we suppose for example that the reduced cost 'c - a' is positive
916 // and that x is lower-bounded by 'lb' then the best bound we can get is
917 // c * x >= a * x + (c - a) * lb.
918 //
919 // Note: when summing over all variables, the left side is the primal
920 // objective and the right side is a lower bound to the objective. In
921 // particular, a necessary and sufficient condition for both objectives to be
922 // the same is that all the single variable inequalities above be equalities.
923 // This is possible only if c == a or if x is at its bound (modulo the
924 // optimization_sign of the reduced cost), or both (this is one side of the
925 // complementary slackness conditions, see Chvatal p. 62).
926 const ColIndex num_cols = lp.num_variables();
927 for (ColIndex col(0); col < num_cols; ++col) {
928 const Fractional lower_bound = lp.variable_lower_bounds()[col];
929 const Fractional upper_bound = lp.variable_upper_bounds()[col];
930
931 // Correct the reduced cost, so as to have a minimization problem and
932 // thus a dual objective that is a lower bound of the primal objective.
933 const Fractional reduced_cost = optimization_sign * reduced_costs_[col];
934
935 // We do not do any correction if the reduced cost is 'infeasible', which is
936 // the same as computing the objective of the perturbed problem.
937 Fractional correction = 0.0;
938 if (variable_statuses_[col] == VariableStatus::AT_LOWER_BOUND &&
939 reduced_cost > 0.0) {
940 correction = reduced_cost * lower_bound;
941 } else if (variable_statuses_[col] == VariableStatus::AT_UPPER_BOUND &&
942 reduced_cost < 0.0) {
943 correction = reduced_cost * upper_bound;
944 } else if (variable_statuses_[col] == VariableStatus::FIXED_VALUE) {
945 correction = reduced_cost * upper_bound;
946 }
947 // Now apply the correction in the right direction!
948 dual_objective.Add(optimization_sign * correction);
949 }
950 return dual_objective.Value();
951}
952
953double LPSolver::ComputeMaxExpectedObjectiveError(const LinearProgram& lp) {
954 const ColIndex num_cols = lp.num_variables();
955 DCHECK_EQ(num_cols, primal_values_.size());
956 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
957 Fractional primal_objective_error = 0.0;
958 for (ColIndex col(0); col < num_cols; ++col) {
959 // TODO(user): Be more precise since the non-BASIC variables are exactly at
960 // their bounds, so for them the error bound is just the term magnitude
961 // times std::numeric_limits<double>::epsilon() with KahanSum.
962 primal_objective_error += std::abs(lp.objective_coefficients()[col]) *
963 AllowedError(tolerance, primal_values_[col]);
964 }
965 return primal_objective_error;
966}
967
968double LPSolver::ComputePrimalValueInfeasibility(const LinearProgram& lp,
969 bool* is_too_large) {
970 double infeasibility = 0.0;
971 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
972 const ColIndex num_cols = lp.num_variables();
973 for (ColIndex col(0); col < num_cols; ++col) {
974 const Fractional lower_bound = lp.variable_lower_bounds()[col];
975 const Fractional upper_bound = lp.variable_upper_bounds()[col];
976 DCHECK(IsFinite(primal_values_[col]));
977
978 if (lower_bound == upper_bound) {
979 const Fractional error = std::abs(primal_values_[col] - upper_bound);
980 infeasibility = std::max(infeasibility, error);
981 *is_too_large |= error > AllowedError(tolerance, upper_bound);
982 continue;
983 }
984 if (primal_values_[col] > upper_bound) {
985 const Fractional error = primal_values_[col] - upper_bound;
986 infeasibility = std::max(infeasibility, error);
987 *is_too_large |= error > AllowedError(tolerance, upper_bound);
988 }
989 if (primal_values_[col] < lower_bound) {
990 const Fractional error = lower_bound - primal_values_[col];
991 infeasibility = std::max(infeasibility, error);
992 *is_too_large |= error > AllowedError(tolerance, lower_bound);
993 }
994 }
995 return infeasibility;
996}
997
998double LPSolver::ComputeActivityInfeasibility(const LinearProgram& lp,
999 bool* is_too_large) {
1000 double infeasibility = 0.0;
1001 int num_problematic_rows(0);
1002 const RowIndex num_rows = lp.num_constraints();
1003 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
1004 for (RowIndex row(0); row < num_rows; ++row) {
1005 const Fractional activity = constraint_activities_[row];
1006 const Fractional lower_bound = lp.constraint_lower_bounds()[row];
1007 const Fractional upper_bound = lp.constraint_upper_bounds()[row];
1008 DCHECK(IsFinite(activity));
1009
1010 if (lower_bound == upper_bound) {
1011 if (std::abs(activity - upper_bound) >
1012 AllowedError(tolerance, upper_bound)) {
1013 VLOG(2) << "Row " << row.value() << " has activity " << activity
1014 << " which is different from " << upper_bound << " by "
1015 << activity - upper_bound;
1016 ++num_problematic_rows;
1017 }
1018 infeasibility = std::max(infeasibility, std::abs(activity - upper_bound));
1019 continue;
1020 }
1021 if (activity > upper_bound) {
1022 const Fractional row_excess = activity - upper_bound;
1023 if (row_excess > AllowedError(tolerance, upper_bound)) {
1024 VLOG(2) << "Row " << row.value() << " has activity " << activity
1025 << ", exceeding its upper bound " << upper_bound << " by "
1026 << row_excess;
1027 ++num_problematic_rows;
1028 }
1029 infeasibility = std::max(infeasibility, row_excess);
1030 }
1031 if (activity < lower_bound) {
1032 const Fractional row_deficit = lower_bound - activity;
1033 if (row_deficit > AllowedError(tolerance, lower_bound)) {
1034 VLOG(2) << "Row " << row.value() << " has activity " << activity
1035 << ", below its lower bound " << lower_bound << " by "
1036 << row_deficit;
1037 ++num_problematic_rows;
1038 }
1039 infeasibility = std::max(infeasibility, row_deficit);
1040 }
1041 }
1042 if (num_problematic_rows > 0) {
1043 *is_too_large = true;
1044 VLOG(1) << "Number of infeasible rows = " << num_problematic_rows;
1045 }
1046 return infeasibility;
1047}
1048
1049double LPSolver::ComputeDualValueInfeasibility(const LinearProgram& lp,
1050 bool* is_too_large) {
1051 const Fractional allowed_error = parameters_.solution_feasibility_tolerance();
1052 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
1053 double infeasibility = 0.0;
1054 const RowIndex num_rows = lp.num_constraints();
1055 for (RowIndex row(0); row < num_rows; ++row) {
1056 const Fractional dual_value = dual_values_[row];
1057 const Fractional lower_bound = lp.constraint_lower_bounds()[row];
1058 const Fractional upper_bound = lp.constraint_upper_bounds()[row];
1059 DCHECK(IsFinite(dual_value));
1060 const Fractional minimization_dual_value = optimization_sign * dual_value;
1061 if (lower_bound == -kInfinity) {
1062 *is_too_large |= minimization_dual_value > allowed_error;
1063 infeasibility = std::max(infeasibility, minimization_dual_value);
1064 }
1065 if (upper_bound == kInfinity) {
1066 *is_too_large |= -minimization_dual_value > allowed_error;
1067 infeasibility = std::max(infeasibility, -minimization_dual_value);
1068 }
1069 }
1070 return infeasibility;
1071}
1072
1073double LPSolver::ComputeReducedCostInfeasibility(const LinearProgram& lp,
1074 bool* is_too_large) {
1075 const Fractional optimization_sign = lp.IsMaximizationProblem() ? -1.0 : 1.0;
1076 double infeasibility = 0.0;
1077 const ColIndex num_cols = lp.num_variables();
1078 const Fractional tolerance = parameters_.solution_feasibility_tolerance();
1079 for (ColIndex col(0); col < num_cols; ++col) {
1080 const Fractional reduced_cost = reduced_costs_[col];
1081 const Fractional lower_bound = lp.variable_lower_bounds()[col];
1082 const Fractional upper_bound = lp.variable_upper_bounds()[col];
1083 DCHECK(IsFinite(reduced_cost));
1084 const Fractional minimization_reduced_cost =
1085 optimization_sign * reduced_cost;
1086 const Fractional allowed_error =
1087 AllowedError(tolerance, lp.objective_coefficients()[col]);
1088 if (lower_bound == -kInfinity) {
1089 *is_too_large |= minimization_reduced_cost > allowed_error;
1090 infeasibility = std::max(infeasibility, minimization_reduced_cost);
1091 }
1092 if (upper_bound == kInfinity) {
1093 *is_too_large |= -minimization_reduced_cost > allowed_error;
1094 infeasibility = std::max(infeasibility, -minimization_reduced_cost);
1095 }
1096 }
1097 return infeasibility;
1098}
1099
1100} // namespace glop
1101} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define DLOG(severity)
Definition: base/logging.h:881
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
#define VLOG(verboselevel)
Definition: base/logging.h:984
void push_back(const value_type &x)
void Add(const FpNumber &value)
Definition: accurate_sum.h:29
void SetLogToStdOut(bool enable)
Definition: util/logging.h:45
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Creates a time limit object initialized from an object that provides methods max_time_in_seconds() an...
Definition: time_limit.h:160
const GlopParameters & GetParameters() const
Definition: lp_solver.cc:124
void SetInitialBasis(const VariableStatusRow &variable_statuses, const ConstraintStatusColumn &constraint_statuses)
Definition: lp_solver.cc:244
bool MayHaveMultipleOptimalSolutions() const
Definition: lp_solver.cc:503
const VariableStatusRow & variable_statuses() const
Definition: lp_solver.h:103
GlopParameters * GetMutableParameters()
Definition: lp_solver.cc:126
Fractional GetMaximumDualInfeasibility() const
Definition: lp_solver.cc:499
const ConstraintStatusColumn & constraint_statuses() const
Definition: lp_solver.h:117
Fractional GetMaximumPrimalInfeasibility() const
Definition: lp_solver.cc:495
Fractional GetObjectiveValue() const
Definition: lp_solver.cc:491
ProblemStatus LoadAndVerifySolution(const LinearProgram &lp, const ProblemSolution &solution)
Definition: lp_solver.cc:299
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
Definition: lp_solver.cc:130
ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram &lp, TimeLimit *time_limit)
Definition: lp_solver.cc:136
void SetParameters(const GlopParameters &parameters)
Definition: lp_solver.cc:112
std::string GetObjectiveStatsString() const
Definition: lp_data.cc:452
void PopulateFromLinearProgram(const LinearProgram &linear_program)
Definition: lp_data.cc:862
std::string GetBoundsStatsString() const
Definition: lp_data.cc:465
bool IsValid(Fractional max_valid_magnitude=kInfinity) const
Definition: lp_data.cc:1305
const DenseColumn & constraint_lower_bounds() const
Definition: lp_data.h:215
const DenseRow & variable_upper_bounds() const
Definition: lp_data.h:232
const DenseColumn & constraint_upper_bounds() const
Definition: lp_data.h:218
const DenseRow & variable_lower_bounds() const
Definition: lp_data.h:229
std::string GetDimensionString() const
Definition: lp_data.cc:425
Fractional objective_scaling_factor() const
Definition: lp_data.h:261
void assign(IntType size, const T &v)
Definition: lp_types.h:278
SatParameters parameters
CpModelProto proto
ModelSharedTimeLimit * time_limit
int64_t value
absl::Status status
Definition: g_gurobi.cc:35
double upper_bound
double lower_bound
const int WARNING
Definition: log_severity.h:31
const int ERROR
Definition: log_severity.h:32
ABSL_FLAG(bool, lp_dump_to_proto_file, false, "Tells whether do dump the problem to a protobuf file.")
ColIndex col
Definition: markowitz.cc:183
RowIndex row
Definition: markowitz.cc:182
AccurateSum< Fractional > KahanSum
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
std::string GetProblemStatusString(ProblemStatus problem_status)
Definition: lp_types.cc:19
std::string GetConstraintStatusString(ConstraintStatus status)
Definition: lp_types.cc:90
void LinearProgramToMPModelProto(const LinearProgram &input, MPModelProto *output)
Definition: proto_utils.cc:20
bool IsFinite(Fractional value)
Definition: lp_types.h:91
void ChangeSign(StrictITIVector< IndexType, Fractional > *data)
std::string GetVariableStatusString(VariableStatus status)
Definition: lp_types.cc:71
const double kInfinity
Definition: lp_types.h:84
Collection of objects used to extend the Constraint Solver library.
bool WriteProtoToFile(absl::string_view filename, const google::protobuf::Message &proto, ProtoWriteFormat proto_write_format, bool gzipped, bool append_extension_to_file_name)
Definition: file_util.cc:106
bool AreWithinAbsoluteTolerance(FloatType x, FloatType y, FloatType absolute_tolerance)
Definition: fp_utils.h:145
glop::MainLpPreprocessor preprocessor
ConstraintStatusColumn constraint_statuses
Definition: lp_data.h:689
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:44