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