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