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