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"
32 #include "ortools/base/logging.h"
33 #include "ortools/base/timer.h"
34 #include "ortools/glpk/glpk_env_deleter.h"
36 
37 extern "C" {
38 #include "glpk.h"
39 }
40 
41 namespace operations_research {
42 // Class to store information gathered in the callback
43 class 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
64 void 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 
90 namespace {
91 // GLPK indexes its variables and constraints starting at 1.
92 int MPSolverIndexToGlpkIndex(int index) { return index + 1; }
93 } // namespace
94 
95 class 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.
213 GLPKInterface::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.
225 GLPKInterface::~GLPKInterface() {
226  CHECK(lp_ != nullptr);
227  glp_delete_prob(lp_);
228  lp_ = nullptr;
229 }
230 
231 void 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
243 void GLPKInterface::SetOptimizationDirection(bool maximize) {
244  InvalidateSolutionSynchronization();
245  glp_set_obj_dir(lp_, maximize ? GLP_MAX : GLP_MIN);
246 }
247 
248 void 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 
276 void 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 
289 void 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 
318 void 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
337 void 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
347 void GLPKInterface::SetObjectiveCoefficient(const MPVariable* const variable,
348  double coefficient) {
349  sync_status_ = MUST_RELOAD;
350 }
351 
352 // Cached
353 void GLPKInterface::SetObjectiveOffset(double value) {
354  sync_status_ = MUST_RELOAD;
355 }
356 
357 // Clear objective of all its terms (linear)
358 void 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 
373 void GLPKInterface::AddRowConstraint(MPConstraint* const ct) {
374  sync_status_ = MUST_RELOAD;
375 }
376 
377 void GLPKInterface::AddVariable(MPVariable* const var) {
378  sync_status_ = MUST_RELOAD;
379 }
380 
381 // Define new variables and add them to existing constraints.
382 void 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.
405 void 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.
430 void 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.
446 void 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 
507 void 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.
519 MPSolver::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 
654 MPSolver::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 
675 int64_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 
690 int64_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 
700 MPSolver::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 
708 MPSolver::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 
716 bool 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 
727 double 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 
756 double 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 
793 double 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 
869 void 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 
903 void GLPKInterface::SetParameters(const MPSolverParameters& param) {
904  SetCommonParameters(param);
905  if (mip_) {
906  SetMIPParameters(param);
907  }
908 }
909 
910 void 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 
919 void GLPKInterface::SetPrimalTolerance(double value) {
920  lp_param_.tol_bnd = value;
921 }
922 
923 void GLPKInterface::SetDualTolerance(double value) { lp_param_.tol_dj = value; }
924 
925 void 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 
943 void GLPKInterface::SetScalingMode(int value) {
944  SetUnsupportedIntegerParam(MPSolverParameters::SCALING);
945 }
946 
947 void 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 
966 MPSolverInterface* BuildGLPKInterface(bool mip, MPSolver* const solver) {
967  return new GLPKInterface(solver, mip);
968 }
969 
970 } // namespace operations_research
971 #endif // #if defined(USE_GLPK)
ResultStatus
The status of solving the problem.
#define CHECK(condition)
Definition: base/logging.h:491
double Get() const
Definition: timer.h:45
const int FATAL
Definition: log_severity.h:32
#define VLOG(verboselevel)
Definition: base/logging.h:979
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
#define LOG(severity)
Definition: base/logging.h:416
ColIndex col
Definition: markowitz.cc:183
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
RowIndex row
Definition: markowitz.cc:182
int64_t coefficient
void Start()
Definition: timer.h:31
int64_t max
Definition: alldiff_cst.cc:140
const int WARNING
Definition: log_severity.h:31
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
void Restart()
Definition: timer.h:35
int index
Definition: pack.cc:509
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define DCHECK(condition)
Definition: base/logging.h:885
Collection of objects used to extend the Constraint Solver library.
IntVar * var
Definition: expr_array.cc:1874
const bool maximize_
Definition: search.cc:2508
int nodes
int64_t value
const Constraint * ct
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889