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