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