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