OR-Tools  9.2
glpk_interface.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
14//
15
16#if defined(USE_GLPK)
17
18#include <cmath>
19#include <cstddef>
20#include <cstdint>
21#include <limits>
22#include <memory>
23#include <string>
24#include <utility>
25#include <vector>
26
27#include "absl/base/attributes.h"
28#include "absl/memory/memory.h"
29#include "absl/strings/str_format.h"
31#include "ortools/base/hash.h"
34#include "ortools/base/timer.h"
35//#include "ortools/glpk/glpk_env_deleter.h"
37
38extern "C" {
39#include "glpk.h"
40}
41
42namespace operations_research {
43// Class to store information gathered in the callback
44class GLPKInformation {
45 public:
46 explicit GLPKInformation(bool maximize) : num_all_nodes_(0) {
47 ResetBestObjectiveBound(maximize);
48 }
49 void Reset(bool maximize) {
50 num_all_nodes_ = 0;
51 ResetBestObjectiveBound(maximize);
52 }
53 void ResetBestObjectiveBound(bool maximize) {
54 if (maximize) {
55 best_objective_bound_ = std::numeric_limits<double>::infinity();
56 } else {
57 best_objective_bound_ = -std::numeric_limits<double>::infinity();
58 }
59 }
60 int num_all_nodes_;
61 double best_objective_bound_;
62};
63
64// Function to be called in the GLPK callback
65void GLPKGatherInformationCallback(glp_tree* tree, void* info) {
66 CHECK(tree != nullptr);
67 CHECK(info != nullptr);
68 GLPKInformation* glpk_info = reinterpret_cast<GLPKInformation*>(info);
69 switch (glp_ios_reason(tree)) {
70 // The best bound and the number of nodes change only when GLPK
71 // branches, generates cuts or finds an integer solution.
72 case GLP_ISELECT:
73 case GLP_IROWGEN:
74 case GLP_IBINGO: {
75 // Get total number of nodes
76 glp_ios_tree_size(tree, nullptr, nullptr, &glpk_info->num_all_nodes_);
77 // Get best bound
78 int node_id = glp_ios_best_node(tree);
79 if (node_id > 0) {
80 glpk_info->best_objective_bound_ = glp_ios_node_bound(tree, node_id);
81 }
82 break;
83 }
84 default:
85 break;
86 }
87}
88
89// ----- GLPK Solver -----
90
91namespace {
92// GLPK indexes its variables and constraints starting at 1.
93int MPSolverIndexToGlpkIndex(int index) { return index + 1; }
94} // namespace
95
96class GLPKInterface : public MPSolverInterface {
97 public:
98 // Constructor that takes a name for the underlying glpk solver.
99 GLPKInterface(MPSolver* const solver, bool mip);
100 ~GLPKInterface() override;
101
102 // Sets the optimization direction (min/max).
103 void SetOptimizationDirection(bool maximize) override;
104
105 // ----- Solve -----
106 // Solve the problem using the parameter values specified.
107 MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
108
109 // ----- Model modifications and extraction -----
110 // Resets extracted model
111 void Reset() override;
112
113 // Modify bounds.
114 void SetVariableBounds(int mpsolver_var_index, double lb, double ub) override;
115 void SetVariableInteger(int mpsolver_var_index, bool integer) override;
116 void SetConstraintBounds(int mpsolver_constraint_index, double lb,
117 double ub) override;
118
119 // Add Constraint incrementally.
120 void AddRowConstraint(MPConstraint* const ct) override;
121 // Add variable incrementally.
122 void AddVariable(MPVariable* const var) override;
123 // Change a coefficient in a constraint.
124 void SetCoefficient(MPConstraint* const constraint,
125 const MPVariable* const variable, double new_value,
126 double old_value) override;
127 // Clear a constraint from all its terms.
128 void ClearConstraint(MPConstraint* const constraint) override;
129 // Change a coefficient in the linear objective
130 void SetObjectiveCoefficient(const MPVariable* const variable,
131 double coefficient) override;
132 // Change the constant term in the linear objective.
133 void SetObjectiveOffset(double value) override;
134 // Clear the objective from all its terms.
135 void ClearObjective() override;
136
137 // ------ Query statistics on the solution and the solve ------
138 // Number of simplex iterations
139 int64_t iterations() const override;
140 // Number of branch-and-bound nodes. Only available for discrete problems.
141 int64_t nodes() const override;
142
143 // Returns the basis status of a row.
144 MPSolver::BasisStatus row_status(int constraint_index) const override;
145 // Returns the basis status of a column.
146 MPSolver::BasisStatus column_status(int variable_index) const override;
147
148 // Checks whether a feasible solution exists.
149 bool CheckSolutionExists() const override;
150
151 // ----- Misc -----
152 // Query problem type.
153 bool IsContinuous() const override { return IsLP(); }
154 bool IsLP() const override { return !mip_; }
155 bool IsMIP() const override { return mip_; }
156
157 void ExtractNewVariables() override;
158 void ExtractNewConstraints() override;
159 void ExtractObjective() override;
160
161 std::string SolverVersion() const override {
162 return absl::StrFormat("GLPK %s", glp_version());
163 }
164
165 void* underlying_solver() override { return reinterpret_cast<void*>(lp_); }
166
167 double ComputeExactConditionNumber() const override;
168
169 private:
170 // Configure the solver's parameters.
171 void ConfigureGLPKParameters(const MPSolverParameters& param);
172
173 // Set all parameters in the underlying solver.
174 void SetParameters(const MPSolverParameters& param) override;
175 // Set each parameter in the underlying solver.
176 void SetRelativeMipGap(double value) override;
177 void SetPrimalTolerance(double value) override;
178 void SetDualTolerance(double value) override;
179 void SetPresolveMode(int value) override;
180 void SetScalingMode(int value) override;
181 void SetLpAlgorithm(int value) override;
182
183 void ExtractOldConstraints();
184 void ExtractOneConstraint(MPConstraint* const constraint, int* const indices,
185 double* const coefs);
186 // Transforms basis status from GLPK integer code to MPSolver::BasisStatus.
187 MPSolver::BasisStatus TransformGLPKBasisStatus(int glpk_basis_status) const;
188
189 // Computes the L1-norm of the current scaled basis.
190 // The L1-norm |A| is defined as max_j sum_i |a_ij|
191 // This method is available only for continuous problems.
192 double ComputeScaledBasisL1Norm(int num_rows, int num_cols,
193 double* row_scaling_factor,
194 double* column_scaling_factor) const;
195
196 // Computes the L1-norm of the inverse of the current scaled
197 // basis.
198 // This method is available only for continuous problems.
199 double ComputeInverseScaledBasisL1Norm(int num_rows, int num_cols,
200 double* row_scaling_factor,
201 double* column_scaling_factor) const;
202
203 glp_prob* lp_;
204 bool mip_;
205
206 // Parameters
207 glp_smcp lp_param_;
208 glp_iocp mip_param_;
209 // For the callback
210 std::unique_ptr<GLPKInformation> mip_callback_info_;
211};
212
213// Creates a LP/MIP instance with the specified name and minimization objective.
214GLPKInterface::GLPKInterface(MPSolver* const solver, bool mip)
215 : MPSolverInterface(solver), lp_(nullptr), mip_(mip) {
216 // Make sure glp_free_env() is called at the exit of the current thread.
217 // SetupGlpkEnvAutomaticDeletion();
218
219 lp_ = glp_create_prob();
220 glp_set_prob_name(lp_, solver_->name_.c_str());
221 glp_set_obj_dir(lp_, GLP_MIN);
222 mip_callback_info_ = absl::make_unique<GLPKInformation>(maximize_);
223}
224
225// Frees the LP memory allocations.
226GLPKInterface::~GLPKInterface() {
227 CHECK(lp_ != nullptr);
228 glp_delete_prob(lp_);
229 lp_ = nullptr;
230}
231
232void GLPKInterface::Reset() {
233 CHECK(lp_ != nullptr);
234 glp_delete_prob(lp_);
235 lp_ = glp_create_prob();
236 glp_set_prob_name(lp_, solver_->name_.c_str());
237 glp_set_obj_dir(lp_, maximize_ ? GLP_MAX : GLP_MIN);
238 ResetExtractionInformation();
239}
240
241// ------ Model modifications and extraction -----
242
243// Not cached
244void GLPKInterface::SetOptimizationDirection(bool maximize) {
245 InvalidateSolutionSynchronization();
246 glp_set_obj_dir(lp_, maximize ? GLP_MAX : GLP_MIN);
247}
248
249void GLPKInterface::SetVariableBounds(int mpsolver_var_index, double lb,
250 double ub) {
251 InvalidateSolutionSynchronization();
252 if (!variable_is_extracted(mpsolver_var_index)) {
253 sync_status_ = MUST_RELOAD;
254 return;
255 }
256 // Not cached if the variable has been extracted.
257 DCHECK(lp_ != nullptr);
258 const double infinity = solver_->infinity();
259 const int glpk_var_index = MPSolverIndexToGlpkIndex(mpsolver_var_index);
260 if (lb != -infinity) {
261 if (ub != infinity) {
262 if (lb == ub) {
263 glp_set_col_bnds(lp_, glpk_var_index, GLP_FX, lb, ub);
264 } else {
265 glp_set_col_bnds(lp_, glpk_var_index, GLP_DB, lb, ub);
266 }
267 } else {
268 glp_set_col_bnds(lp_, glpk_var_index, GLP_LO, lb, 0.0);
269 }
270 } else if (ub != infinity) {
271 glp_set_col_bnds(lp_, glpk_var_index, GLP_UP, 0.0, ub);
272 } else {
273 glp_set_col_bnds(lp_, glpk_var_index, GLP_FR, 0.0, 0.0);
274 }
275}
276
277void GLPKInterface::SetVariableInteger(int mpsolver_var_index, bool integer) {
278 InvalidateSolutionSynchronization();
279 if (mip_) {
280 if (variable_is_extracted(mpsolver_var_index)) {
281 // Not cached if the variable has been extracted.
282 glp_set_col_kind(lp_, MPSolverIndexToGlpkIndex(mpsolver_var_index),
283 integer ? GLP_IV : GLP_CV);
284 } else {
285 sync_status_ = MUST_RELOAD;
286 }
287 }
288}
289
290void GLPKInterface::SetConstraintBounds(int mpsolver_constraint_index,
291 double lb, double ub) {
292 InvalidateSolutionSynchronization();
293 if (!constraint_is_extracted(mpsolver_constraint_index)) {
294 sync_status_ = MUST_RELOAD;
295 return;
296 }
297 // Not cached if the row has been extracted
298 const int glpk_constraint_index =
299 MPSolverIndexToGlpkIndex(mpsolver_constraint_index);
300 DCHECK(lp_ != nullptr);
301 const double infinity = solver_->infinity();
302 if (lb != -infinity) {
303 if (ub != infinity) {
304 if (lb == ub) {
305 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_FX, lb, ub);
306 } else {
307 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_DB, lb, ub);
308 }
309 } else {
310 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_LO, lb, 0.0);
311 }
312 } else if (ub != infinity) {
313 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_UP, 0.0, ub);
314 } else {
315 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_FR, 0.0, 0.0);
316 }
317}
318
319void GLPKInterface::SetCoefficient(MPConstraint* const constraint,
320 const MPVariable* const variable,
321 double new_value, double old_value) {
322 InvalidateSolutionSynchronization();
323 // GLPK does not allow to modify one coefficient at a time, so we
324 // extract the whole constraint again, if it has been extracted
325 // already and if it does not contain new variables. Otherwise, we
326 // cache the modification.
327 if (constraint_is_extracted(constraint->index()) &&
328 (sync_status_ == MODEL_SYNCHRONIZED ||
329 !constraint->ContainsNewVariables())) {
330 const int size = constraint->coefficients_.size();
331 std::unique_ptr<int[]> indices(new int[size + 1]);
332 std::unique_ptr<double[]> coefs(new double[size + 1]);
333 ExtractOneConstraint(constraint, indices.get(), coefs.get());
334 }
335}
336
337// Not cached
338void GLPKInterface::ClearConstraint(MPConstraint* const constraint) {
339 InvalidateSolutionSynchronization();
340 // Constraint may have not been extracted yet.
341 if (constraint_is_extracted(constraint->index())) {
342 glp_set_mat_row(lp_, MPSolverIndexToGlpkIndex(constraint->index()), 0,
343 nullptr, nullptr);
344 }
345}
346
347// Cached
348void GLPKInterface::SetObjectiveCoefficient(const MPVariable* const variable,
349 double coefficient) {
350 sync_status_ = MUST_RELOAD;
351}
352
353// Cached
354void GLPKInterface::SetObjectiveOffset(double value) {
355 sync_status_ = MUST_RELOAD;
356}
357
358// Clear objective of all its terms (linear)
359void GLPKInterface::ClearObjective() {
360 InvalidateSolutionSynchronization();
361 for (const auto& entry : solver_->objective_->coefficients_) {
362 const int mpsolver_var_index = entry.first->index();
363 // Variable may have not been extracted yet.
364 if (!variable_is_extracted(mpsolver_var_index)) {
365 DCHECK_NE(MODEL_SYNCHRONIZED, sync_status_);
366 } else {
367 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(mpsolver_var_index), 0.0);
368 }
369 }
370 // Constant term.
371 glp_set_obj_coef(lp_, 0, 0.0);
372}
373
374void GLPKInterface::AddRowConstraint(MPConstraint* const ct) {
375 sync_status_ = MUST_RELOAD;
376}
377
378void GLPKInterface::AddVariable(MPVariable* const var) {
379 sync_status_ = MUST_RELOAD;
380}
381
382// Define new variables and add them to existing constraints.
383void GLPKInterface::ExtractNewVariables() {
384 int total_num_vars = solver_->variables_.size();
385 if (total_num_vars > last_variable_index_) {
386 glp_add_cols(lp_, total_num_vars - last_variable_index_);
387 for (int j = last_variable_index_; j < solver_->variables_.size(); ++j) {
388 MPVariable* const var = solver_->variables_[j];
389 set_variable_as_extracted(j, true);
390 if (!var->name().empty()) {
391 glp_set_col_name(lp_, MPSolverIndexToGlpkIndex(j), var->name().c_str());
392 }
393 SetVariableBounds(/*mpsolver_var_index=*/j, var->lb(), var->ub());
394 SetVariableInteger(/*mpsolver_var_index=*/j, var->integer());
395
396 // The true objective coefficient will be set later in ExtractObjective.
397 double tmp_obj_coef = 0.0;
398 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(j), tmp_obj_coef);
399 }
400 // Add new variables to the existing constraints.
401 ExtractOldConstraints();
402 }
403}
404
405// Extract again existing constraints if they contain new variables.
406void GLPKInterface::ExtractOldConstraints() {
407 const int max_constraint_size =
408 solver_->ComputeMaxConstraintSize(0, last_constraint_index_);
409 // The first entry in the following arrays is dummy, to be
410 // consistent with glpk API.
411 std::unique_ptr<int[]> indices(new int[max_constraint_size + 1]);
412 std::unique_ptr<double[]> coefs(new double[max_constraint_size + 1]);
413
414 for (int i = 0; i < last_constraint_index_; ++i) {
415 MPConstraint* const ct = solver_->constraints_[i];
416 DCHECK(constraint_is_extracted(i));
417 const int size = ct->coefficients_.size();
418 if (size == 0) {
419 continue;
420 }
421 // Update the constraint's coefficients if it contains new variables.
422 if (ct->ContainsNewVariables()) {
423 ExtractOneConstraint(ct, indices.get(), coefs.get());
424 }
425 }
426}
427
428// Extract one constraint. Arrays indices and coefs must be
429// preallocated to have enough space to contain the constraint's
430// coefficients.
431void GLPKInterface::ExtractOneConstraint(MPConstraint* const constraint,
432 int* const indices,
433 double* const coefs) {
434 // GLPK convention is to start indexing at 1.
435 int k = 1;
436 for (const auto& entry : constraint->coefficients_) {
437 DCHECK(variable_is_extracted(entry.first->index()));
438 indices[k] = MPSolverIndexToGlpkIndex(entry.first->index());
439 coefs[k] = entry.second;
440 ++k;
441 }
442 glp_set_mat_row(lp_, MPSolverIndexToGlpkIndex(constraint->index()), k - 1,
443 indices, coefs);
444}
445
446// Define new constraints on old and new variables.
447void GLPKInterface::ExtractNewConstraints() {
448 int total_num_rows = solver_->constraints_.size();
449 if (last_constraint_index_ < total_num_rows) {
450 // Define new constraints
451 glp_add_rows(lp_, total_num_rows - last_constraint_index_);
452 int num_coefs = 0;
453 for (int i = last_constraint_index_; i < total_num_rows; ++i) {
454 MPConstraint* ct = solver_->constraints_[i];
455 set_constraint_as_extracted(i, true);
456 if (ct->name().empty()) {
457 glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i),
458 absl::StrFormat("ct_%i", i).c_str());
459 } else {
460 glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i), ct->name().c_str());
461 }
462 // All constraints are set to be of the type <= limit_ .
463 SetConstraintBounds(/*mpsolver_constraint_index=*/i, ct->lb(), ct->ub());
464 num_coefs += ct->coefficients_.size();
465 }
466
467 // Fill new constraints with coefficients
468 if (last_variable_index_ == 0 && last_constraint_index_ == 0) {
469 // Faster extraction when nothing has been extracted yet: build
470 // and load whole matrix at once instead of constructing rows
471 // separately.
472
473 // The first entry in the following arrays is dummy, to be
474 // consistent with glpk API.
475 std::unique_ptr<int[]> variable_indices(new int[num_coefs + 1]);
476 std::unique_ptr<int[]> constraint_indices(new int[num_coefs + 1]);
477 std::unique_ptr<double[]> coefs(new double[num_coefs + 1]);
478 int k = 1;
479 for (int i = 0; i < solver_->constraints_.size(); ++i) {
480 MPConstraint* ct = solver_->constraints_[i];
481 for (const auto& entry : ct->coefficients_) {
482 DCHECK(variable_is_extracted(entry.first->index()));
483 constraint_indices[k] = MPSolverIndexToGlpkIndex(ct->index());
484 variable_indices[k] = MPSolverIndexToGlpkIndex(entry.first->index());
485 coefs[k] = entry.second;
486 ++k;
487 }
488 }
489 CHECK_EQ(num_coefs + 1, k);
490 glp_load_matrix(lp_, num_coefs, constraint_indices.get(),
491 variable_indices.get(), coefs.get());
492 } else {
493 // Build each new row separately.
494 int max_constraint_size = solver_->ComputeMaxConstraintSize(
495 last_constraint_index_, total_num_rows);
496 // The first entry in the following arrays is dummy, to be
497 // consistent with glpk API.
498 std::unique_ptr<int[]> indices(new int[max_constraint_size + 1]);
499 std::unique_ptr<double[]> coefs(new double[max_constraint_size + 1]);
500 for (int i = last_constraint_index_; i < total_num_rows; i++) {
501 ExtractOneConstraint(solver_->constraints_[i], indices.get(),
502 coefs.get());
503 }
504 }
505 }
506}
507
508void GLPKInterface::ExtractObjective() {
509 // Linear objective: set objective coefficients for all variables
510 // (some might have been modified).
511 for (const auto& entry : solver_->objective_->coefficients_) {
512 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(entry.first->index()),
513 entry.second);
514 }
515 // Constant term.
516 glp_set_obj_coef(lp_, 0, solver_->Objective().offset());
517}
518
519// Solve the problem using the parameter values specified.
520MPSolver::ResultStatus GLPKInterface::Solve(const MPSolverParameters& param) {
521 WallTimer timer;
522 timer.Start();
523
524 // Note that GLPK provides incrementality for LP but not for MIP.
525 if (param.GetIntegerParam(MPSolverParameters::INCREMENTALITY) ==
526 MPSolverParameters::INCREMENTALITY_OFF) {
527 Reset();
528 }
529
530 // Set log level.
531 if (quiet_) {
532 glp_term_out(GLP_OFF);
533 } else {
534 glp_term_out(GLP_ON);
535 }
536
537 ExtractModel();
538 VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get());
539
540 // Configure parameters at every solve, even when the model has not
541 // been changed, in case some of the parameters such as the time
542 // limit have been changed since the last solve.
543 ConfigureGLPKParameters(param);
544
545 // Solve
546 timer.Restart();
547 int solver_status = glp_simplex(lp_, &lp_param_);
548 if (mip_) {
549 // glp_intopt requires to solve the root LP separately.
550 // If the root LP was solved successfully, solve the MIP.
551 if (solver_status == 0) {
552 solver_status = glp_intopt(lp_, &mip_param_);
553 } else {
554 // Something abnormal occurred during the root LP solve. It is
555 // highly unlikely that an integer feasible solution is
556 // available at this point, so we don't put any effort in trying
557 // to recover it.
558 result_status_ = MPSolver::ABNORMAL;
559 if (solver_status == GLP_ETMLIM) {
560 result_status_ = MPSolver::NOT_SOLVED;
561 }
562 sync_status_ = SOLUTION_SYNCHRONIZED;
563 return result_status_;
564 }
565 }
566 VLOG(1) << absl::StrFormat("GLPK Status: %i (time spent: %.3f seconds).",
567 solver_status, timer.Get());
568
569 // Get the results.
570 if (mip_) {
571 objective_value_ = glp_mip_obj_val(lp_);
572 best_objective_bound_ = mip_callback_info_->best_objective_bound_;
573 } else {
574 objective_value_ = glp_get_obj_val(lp_);
575 }
576 VLOG(1) << "objective=" << objective_value_
577 << ", bound=" << best_objective_bound_;
578 for (int i = 0; i < solver_->variables_.size(); ++i) {
579 MPVariable* const var = solver_->variables_[i];
580 double val;
581 if (mip_) {
582 val = glp_mip_col_val(lp_, MPSolverIndexToGlpkIndex(i));
583 } else {
584 val = glp_get_col_prim(lp_, MPSolverIndexToGlpkIndex(i));
585 }
586 var->set_solution_value(val);
587 VLOG(3) << var->name() << ": value =" << val;
588 if (!mip_) {
589 double reduced_cost;
590 reduced_cost = glp_get_col_dual(lp_, MPSolverIndexToGlpkIndex(i));
591 var->set_reduced_cost(reduced_cost);
592 VLOG(4) << var->name() << ": reduced cost = " << reduced_cost;
593 }
594 }
595 for (int i = 0; i < solver_->constraints_.size(); ++i) {
596 MPConstraint* const ct = solver_->constraints_[i];
597 if (!mip_) {
598 const double dual_value =
599 glp_get_row_dual(lp_, MPSolverIndexToGlpkIndex(i));
600 ct->set_dual_value(dual_value);
601 VLOG(4) << "row " << MPSolverIndexToGlpkIndex(i)
602 << ": dual value = " << dual_value;
603 }
604 }
605
606 // Check the status: optimal, infeasible, etc.
607 if (mip_) {
608 int tmp_status = glp_mip_status(lp_);
609 VLOG(1) << "GLPK result status: " << tmp_status;
610 if (tmp_status == GLP_OPT) {
611 result_status_ = MPSolver::OPTIMAL;
612 } else if (tmp_status == GLP_FEAS) {
613 result_status_ = MPSolver::FEASIBLE;
614 } else if (tmp_status == GLP_NOFEAS) {
615 // For infeasible problems, GLPK actually seems to return
616 // GLP_UNDEF. So this is never (?) reached. Return infeasible
617 // in case GLPK returns a correct status in future versions.
618 result_status_ = MPSolver::INFEASIBLE;
619 } else if (solver_status == GLP_ETMLIM) {
620 result_status_ = MPSolver::NOT_SOLVED;
621 } else {
622 result_status_ = MPSolver::ABNORMAL;
623 // GLPK does not have a status code for unbounded MIP models, so
624 // we return an abnormal status instead.
625 }
626 } else {
627 int tmp_status = glp_get_status(lp_);
628 VLOG(1) << "GLPK result status: " << tmp_status;
629 if (tmp_status == GLP_OPT) {
630 result_status_ = MPSolver::OPTIMAL;
631 } else if (tmp_status == GLP_FEAS) {
632 result_status_ = MPSolver::FEASIBLE;
633 } else if (tmp_status == GLP_NOFEAS || tmp_status == GLP_INFEAS) {
634 // For infeasible problems, GLPK actually seems to return
635 // GLP_UNDEF. So this is never (?) reached. Return infeasible
636 // in case GLPK returns a correct status in future versions.
637 result_status_ = MPSolver::INFEASIBLE;
638 } else if (tmp_status == GLP_UNBND) {
639 // For unbounded problems, GLPK actually seems to return
640 // GLP_UNDEF. So this is never (?) reached. Return unbounded
641 // in case GLPK returns a correct status in future versions.
642 result_status_ = MPSolver::UNBOUNDED;
643 } else if (solver_status == GLP_ETMLIM) {
644 result_status_ = MPSolver::NOT_SOLVED;
645 } else {
646 result_status_ = MPSolver::ABNORMAL;
647 }
648 }
649
650 sync_status_ = SOLUTION_SYNCHRONIZED;
651
652 return result_status_;
653}
654
655MPSolver::BasisStatus GLPKInterface::TransformGLPKBasisStatus(
656 int glpk_basis_status) const {
657 switch (glpk_basis_status) {
658 case GLP_BS:
659 return MPSolver::BASIC;
660 case GLP_NL:
661 return MPSolver::AT_LOWER_BOUND;
662 case GLP_NU:
663 return MPSolver::AT_UPPER_BOUND;
664 case GLP_NF:
665 return MPSolver::FREE;
666 case GLP_NS:
667 return MPSolver::FIXED_VALUE;
668 default:
669 LOG(FATAL) << "Unknown GLPK basis status";
670 return MPSolver::FREE;
671 }
672}
673
674// ------ Query statistics on the solution and the solve ------
675
676int64_t GLPKInterface::iterations() const {
677#if GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION < 49
678 if (!mip_ && CheckSolutionIsSynchronized()) {
679 return lpx_get_int_parm(lp_, LPX_K_ITCNT);
680 }
681#elif (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 53) || \
682 GLP_MAJOR_VERSION >= 5
683 if (!mip_ && CheckSolutionIsSynchronized()) {
684 return glp_get_it_cnt(lp_);
685 }
686#endif
687 LOG(WARNING) << "Total number of iterations is not available";
688 return kUnknownNumberOfIterations;
689}
690
691int64_t GLPKInterface::nodes() const {
692 if (mip_) {
693 if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfNodes;
694 return mip_callback_info_->num_all_nodes_;
695 } else {
696 LOG(DFATAL) << "Number of nodes only available for discrete problems";
697 return kUnknownNumberOfNodes;
698 }
699}
700
701MPSolver::BasisStatus GLPKInterface::row_status(int constraint_index) const {
702 DCHECK_GE(constraint_index, 0);
703 DCHECK_LT(constraint_index, last_constraint_index_);
704 const int glpk_basis_status =
705 glp_get_row_stat(lp_, MPSolverIndexToGlpkIndex(constraint_index));
706 return TransformGLPKBasisStatus(glpk_basis_status);
707}
708
709MPSolver::BasisStatus GLPKInterface::column_status(int variable_index) const {
710 DCHECK_GE(variable_index, 0);
711 DCHECK_LT(variable_index, last_variable_index_);
712 const int glpk_basis_status =
713 glp_get_col_stat(lp_, MPSolverIndexToGlpkIndex(variable_index));
714 return TransformGLPKBasisStatus(glpk_basis_status);
715}
716
717bool GLPKInterface::CheckSolutionExists() const {
718 if (result_status_ == MPSolver::ABNORMAL) {
719 LOG(WARNING) << "Ignoring ABNORMAL status from GLPK: This status may or may"
720 << " not indicate that a solution exists.";
721 return true;
722 } else {
723 // Call default implementation
724 return MPSolverInterface::CheckSolutionExists();
725 }
726}
727
728double GLPKInterface::ComputeExactConditionNumber() const {
729 if (!IsContinuous()) {
730 // TODO(user): support MIP.
731 LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
732 << " GLPK_MIXED_INTEGER_PROGRAMMING";
733 return 0.0;
734 }
735 if (!CheckSolutionIsSynchronized()) return 0.0;
736 // Simplex is the only LP algorithm supported in the wrapper for
737 // GLPK, so when a solution exists, a basis exists.
738 CheckSolutionExists();
739 const int num_rows = glp_get_num_rows(lp_);
740 const int num_cols = glp_get_num_cols(lp_);
741 // GLPK indexes everything starting from 1 instead of 0.
742 std::unique_ptr<double[]> row_scaling_factor(new double[num_rows + 1]);
743 std::unique_ptr<double[]> column_scaling_factor(new double[num_cols + 1]);
744 for (int row = 1; row <= num_rows; ++row) {
745 row_scaling_factor[row] = glp_get_rii(lp_, row);
746 }
747 for (int col = 1; col <= num_cols; ++col) {
748 column_scaling_factor[col] = glp_get_sjj(lp_, col);
749 }
750 return ComputeInverseScaledBasisL1Norm(num_rows, num_cols,
751 row_scaling_factor.get(),
752 column_scaling_factor.get()) *
753 ComputeScaledBasisL1Norm(num_rows, num_cols, row_scaling_factor.get(),
754 column_scaling_factor.get());
755}
756
757double GLPKInterface::ComputeScaledBasisL1Norm(
758 int num_rows, int num_cols, double* row_scaling_factor,
759 double* column_scaling_factor) const {
760 double norm = 0.0;
761 std::unique_ptr<double[]> values(new double[num_rows + 1]);
762 std::unique_ptr<int[]> indices(new int[num_rows + 1]);
763 for (int col = 1; col <= num_cols; ++col) {
764 const int glpk_basis_status = glp_get_col_stat(lp_, col);
765 // Take into account only basic columns.
766 if (glpk_basis_status == GLP_BS) {
767 // Compute L1-norm of column 'col': sum_row |a_row,col|.
768 const int num_nz = glp_get_mat_col(lp_, col, indices.get(), values.get());
769 double column_norm = 0.0;
770 for (int k = 1; k <= num_nz; k++) {
771 column_norm += fabs(values[k] * row_scaling_factor[indices[k]]);
772 }
773 column_norm *= fabs(column_scaling_factor[col]);
774 // Compute max_col column_norm
775 norm = std::max(norm, column_norm);
776 }
777 }
778 // Slack variables.
779 for (int row = 1; row <= num_rows; ++row) {
780 const int glpk_basis_status = glp_get_row_stat(lp_, row);
781 // Take into account only basic slack variables.
782 if (glpk_basis_status == GLP_BS) {
783 // Only one non-zero coefficient: +/- 1.0 in the corresponding
784 // row. The row has a scaling coefficient but the slack variable
785 // is never scaled on top of that.
786 const double column_norm = fabs(row_scaling_factor[row]);
787 // Compute max_col column_norm
788 norm = std::max(norm, column_norm);
789 }
790 }
791 return norm;
792}
793
794double GLPKInterface::ComputeInverseScaledBasisL1Norm(
795 int num_rows, int num_cols, double* row_scaling_factor,
796 double* column_scaling_factor) const {
797 // Compute the LU factorization if it doesn't exist yet.
798 if (!glp_bf_exists(lp_)) {
799 const int factorize_status = glp_factorize(lp_);
800 switch (factorize_status) {
801 case GLP_EBADB: {
802 LOG(FATAL) << "Not able to factorize: error GLP_EBADB.";
803 break;
804 }
805 case GLP_ESING: {
806 LOG(WARNING)
807 << "Not able to factorize: "
808 << "the basis matrix is singular within the working precision.";
809 return MPSolver::infinity();
810 }
811 case GLP_ECOND: {
812 LOG(WARNING)
813 << "Not able to factorize: the basis matrix is ill-conditioned.";
814 return MPSolver::infinity();
815 }
816 default:
817 break;
818 }
819 }
820 std::unique_ptr<double[]> right_hand_side(new double[num_rows + 1]);
821 double norm = 0.0;
822 // Iteratively solve B x = e_k, where e_k is the kth unit vector.
823 // The result of this computation is the kth column of B^-1.
824 // glp_ftran works on original matrix. Scale input and result to
825 // obtain the norm of the kth column in the inverse scaled
826 // matrix. See glp_ftran documentation in glpapi12.c for how the
827 // scaling is done: inv(B'') = inv(SB) * inv(B) * inv(R) where:
828 // o B'' is the scaled basis
829 // o B is the original basis
830 // o R is the diagonal row scaling matrix
831 // o SB consists of the basic columns of the augmented column
832 // scaling matrix (auxiliary variables then structural variables):
833 // S~ = diag(inv(R) | S).
834 for (int k = 1; k <= num_rows; ++k) {
835 for (int row = 1; row <= num_rows; ++row) {
836 right_hand_side[row] = 0.0;
837 }
838 right_hand_side[k] = 1.0;
839 // Multiply input by inv(R).
840 for (int row = 1; row <= num_rows; ++row) {
841 right_hand_side[row] /= row_scaling_factor[row];
842 }
843 glp_ftran(lp_, right_hand_side.get());
844 // glp_ftran stores the result in the same vector where the right
845 // hand side was provided.
846 // Multiply result by inv(SB).
847 for (int row = 1; row <= num_rows; ++row) {
848 const int k = glp_get_bhead(lp_, row);
849 if (k <= num_rows) {
850 // Auxiliary variable.
851 right_hand_side[row] *= row_scaling_factor[k];
852 } else {
853 // Structural variable.
854 right_hand_side[row] /= column_scaling_factor[k - num_rows];
855 }
856 }
857 // Compute sum_row |vector_row|.
858 double column_norm = 0.0;
859 for (int row = 1; row <= num_rows; ++row) {
860 column_norm += fabs(right_hand_side[row]);
861 }
862 // Compute max_col column_norm
863 norm = std::max(norm, column_norm);
864 }
865 return norm;
866}
867
868// ------ Parameters ------
869
870void GLPKInterface::ConfigureGLPKParameters(const MPSolverParameters& param) {
871 if (mip_) {
872 glp_init_iocp(&mip_param_);
873 // Time limit
874 if (solver_->time_limit()) {
875 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
876 mip_param_.tm_lim = solver_->time_limit();
877 }
878 // Initialize structures related to the callback.
879 mip_param_.cb_func = GLPKGatherInformationCallback;
880 mip_callback_info_->Reset(maximize_);
881 mip_param_.cb_info = mip_callback_info_.get();
882 // TODO(user): switch some cuts on? All cuts are off by default!?
883 }
884
885 // Configure LP parameters in all cases since they will be used to
886 // solve the root LP in the MIP case.
887 glp_init_smcp(&lp_param_);
888 // Time limit
889 if (solver_->time_limit()) {
890 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
891 lp_param_.tm_lim = solver_->time_limit();
892 }
893
894 // Should give a numerically better representation of the problem.
895 glp_scale_prob(lp_, GLP_SF_AUTO);
896
897 // Use advanced initial basis (options: standard / advanced / Bixby's).
898 glp_adv_basis(lp_, 0);
899
900 // Set parameters specified by the user.
901 SetParameters(param);
902}
903
904void GLPKInterface::SetParameters(const MPSolverParameters& param) {
905 SetCommonParameters(param);
906 if (mip_) {
907 SetMIPParameters(param);
908 }
909}
910
911void GLPKInterface::SetRelativeMipGap(double value) {
912 if (mip_) {
913 mip_param_.mip_gap = value;
914 } else {
915 LOG(WARNING) << "The relative MIP gap is only available "
916 << "for discrete problems.";
917 }
918}
919
920void GLPKInterface::SetPrimalTolerance(double value) {
921 lp_param_.tol_bnd = value;
922}
923
924void GLPKInterface::SetDualTolerance(double value) { lp_param_.tol_dj = value; }
925
926void GLPKInterface::SetPresolveMode(int value) {
927 switch (value) {
928 case MPSolverParameters::PRESOLVE_OFF: {
929 mip_param_.presolve = GLP_OFF;
930 lp_param_.presolve = GLP_OFF;
931 break;
932 }
933 case MPSolverParameters::PRESOLVE_ON: {
934 mip_param_.presolve = GLP_ON;
935 lp_param_.presolve = GLP_ON;
936 break;
937 }
938 default: {
939 SetIntegerParamToUnsupportedValue(MPSolverParameters::PRESOLVE, value);
940 }
941 }
942}
943
944void GLPKInterface::SetScalingMode(int value) {
945 SetUnsupportedIntegerParam(MPSolverParameters::SCALING);
946}
947
948void GLPKInterface::SetLpAlgorithm(int value) {
949 switch (value) {
950 case MPSolverParameters::DUAL: {
951 // Use dual, and if it fails, switch to primal.
952 lp_param_.meth = GLP_DUALP;
953 break;
954 }
955 case MPSolverParameters::PRIMAL: {
956 lp_param_.meth = GLP_PRIMAL;
957 break;
958 }
959 case MPSolverParameters::BARRIER:
960 default: {
961 SetIntegerParamToUnsupportedValue(MPSolverParameters::LP_ALGORITHM,
962 value);
963 }
964 }
965}
966
967MPSolverInterface* BuildGLPKInterface(bool mip, MPSolver* const solver) {
968 return new GLPKInterface(solver, mip);
969}
970
971} // namespace operations_research
972#endif // #if defined(USE_GLPK)
int64_t max
Definition: alldiff_cst.cc:140
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:891
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:894
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:893
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:889
#define VLOG(verboselevel)
Definition: base/logging.h:983
void Start()
Definition: timer.h:31
void Restart()
Definition: timer.h:35
double Get() const
Definition: timer.h:45
ResultStatus
The status of solving the problem.
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
const int WARNING
Definition: log_severity.h:31
const int FATAL
Definition: log_severity.h:32
ColIndex col
Definition: markowitz.cc:183
RowIndex row
Definition: markowitz.cc:182
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Definition: solve.cc:155
Collection of objects used to extend the Constraint Solver library.
int index
Definition: pack.cc:509
int64_t coefficient
int nodes
const bool maximize_
Definition: search.cc:2559