26#include "absl/base/attributes.h"
27#include "absl/memory/memory.h"
28#include "absl/strings/str_format.h"
64 CHECK(tree !=
nullptr);
65 CHECK(info !=
nullptr);
67 switch (glp_ios_reason(tree)) {
74 glp_ios_tree_size(tree,
nullptr,
nullptr, &glpk_info->
num_all_nodes_);
76 int node_id = glp_ios_best_node(tree);
91int MPSolverIndexToGlpkIndex(
int index) {
return index + 1; }
109 void Reset()
override;
123 double new_value,
double old_value)
override;
138 int64_t
nodes()
const override;
151 bool IsLP()
const override {
return !mip_; }
152 bool IsMIP()
const override {
return mip_; }
159 return absl::StrFormat(
"GLPK %s", glp_version());
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;
180 void ExtractOldConstraints();
181 void ExtractOneConstraint(
MPConstraint* constraint,
int* indices,
189 double ComputeScaledBasisL1Norm(
int num_rows,
int num_cols,
190 double* row_scaling_factor,
191 double* column_scaling_factor)
const;
196 double ComputeInverseScaledBasisL1Norm(
int num_rows,
int num_cols,
197 double* row_scaling_factor,
198 double* column_scaling_factor)
const;
207 std::unique_ptr<GLPKInformation> mip_callback_info_;
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_);
224 CHECK(lp_ !=
nullptr);
225 glp_delete_prob(lp_);
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);
243 glp_set_obj_dir(lp_, maximize ? GLP_MAX : GLP_MIN);
254 DCHECK(lp_ !=
nullptr);
256 const int glpk_var_index = MPSolverIndexToGlpkIndex(mpsolver_var_index);
260 glp_set_col_bnds(lp_, glpk_var_index, GLP_FX, lb, ub);
262 glp_set_col_bnds(lp_, glpk_var_index, GLP_DB, lb, ub);
265 glp_set_col_bnds(lp_, glpk_var_index, GLP_LO, lb, 0.0);
268 glp_set_col_bnds(lp_, glpk_var_index, GLP_UP, 0.0, ub);
270 glp_set_col_bnds(lp_, glpk_var_index, GLP_FR, 0.0, 0.0);
279 glp_set_col_kind(lp_, MPSolverIndexToGlpkIndex(mpsolver_var_index),
280 integer ? GLP_IV : GLP_CV);
288 double lb,
double ub) {
295 const int glpk_constraint_index =
296 MPSolverIndexToGlpkIndex(mpsolver_constraint_index);
297 DCHECK(lp_ !=
nullptr);
302 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_FX, lb, ub);
304 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_DB, lb, ub);
307 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_LO, lb, 0.0);
310 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_UP, 0.0, ub);
312 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_FR, 0.0, 0.0);
318 double new_value,
double old_value) {
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());
339 glp_set_mat_row(lp_, MPSolverIndexToGlpkIndex(constraint->
index()), 0,
358 for (
const auto& entry :
solver_->objective_->coefficients_) {
359 const int mpsolver_var_index = entry.first->index();
364 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(mpsolver_var_index), 0.0);
368 glp_set_obj_coef(lp_, 0, 0.0);
381 int total_num_vars =
solver_->variables_.size();
387 if (!
var->name().empty()) {
388 glp_set_col_name(lp_, MPSolverIndexToGlpkIndex(j),
var->name().c_str());
394 double tmp_obj_coef = 0.0;
395 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(j), tmp_obj_coef);
398 ExtractOldConstraints();
403void GLPKInterface::ExtractOldConstraints() {
404 const int max_constraint_size =
408 std::unique_ptr<int[]> indices(
new int[max_constraint_size + 1]);
409 std::unique_ptr<double[]> coefs(
new double[max_constraint_size + 1]);
414 const int size =
ct->coefficients_.size();
419 if (
ct->ContainsNewVariables()) {
420 ExtractOneConstraint(
ct, indices.get(), coefs.get());
428void GLPKInterface::ExtractOneConstraint(MPConstraint*
const constraint,
430 double*
const coefs) {
433 for (
const auto& entry : constraint->coefficients_) {
435 indices[k] = MPSolverIndexToGlpkIndex(entry.first->index());
436 coefs[k] = entry.second;
439 glp_set_mat_row(lp_, MPSolverIndexToGlpkIndex(constraint->index()), k - 1,
445 int total_num_rows =
solver_->constraints_.size();
453 if (
ct->name().empty()) {
454 glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i),
455 absl::StrFormat(
"ct_%i", i).c_str());
457 glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i),
ct->name().c_str());
461 num_coefs +=
ct->coefficients_.size();
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]);
476 for (
int i = 0; i <
solver_->constraints_.size(); ++i) {
478 for (
const auto& entry :
ct->coefficients_) {
480 constraint_indices[k] = MPSolverIndexToGlpkIndex(
ct->index());
481 variable_indices[k] = MPSolverIndexToGlpkIndex(entry.first->index());
482 coefs[k] = entry.second;
486 CHECK_EQ(num_coefs + 1, k);
487 glp_load_matrix(lp_, num_coefs, constraint_indices.get(),
488 variable_indices.get(), coefs.get());
491 int max_constraint_size =
solver_->ComputeMaxConstraintSize(
495 std::unique_ptr<int[]> indices(
new int[max_constraint_size + 1]);
496 std::unique_ptr<double[]> coefs(
new double[max_constraint_size + 1]);
498 ExtractOneConstraint(
solver_->constraints_[i], indices.get(),
508 for (
const auto& entry :
solver_->objective_->coefficients_) {
509 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(entry.first->index()),
529 glp_term_out(GLP_OFF);
531 glp_term_out(GLP_ON);
535 VLOG(1) << absl::StrFormat(
"Model built in %.3f seconds.", timer.
Get());
540 ConfigureGLPKParameters(param);
544 int solver_status = glp_simplex(lp_, &lp_param_);
548 if (solver_status == 0) {
549 solver_status = glp_intopt(lp_, &mip_param_);
556 if (solver_status == GLP_ETMLIM) {
563 VLOG(1) << absl::StrFormat(
"GLPK Status: %i (time spent: %.3f seconds).",
564 solver_status, timer.
Get());
575 for (
int i = 0; i <
solver_->variables_.size(); ++i) {
579 val = glp_mip_col_val(lp_, MPSolverIndexToGlpkIndex(i));
581 val = glp_get_col_prim(lp_, MPSolverIndexToGlpkIndex(i));
583 var->set_solution_value(val);
584 VLOG(3) <<
var->name() <<
": value =" << val;
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;
592 for (
int i = 0; i <
solver_->constraints_.size(); ++i) {
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;
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) {
616 }
else if (solver_status == GLP_ETMLIM) {
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) {
635 }
else if (tmp_status == GLP_UNBND) {
640 }
else if (solver_status == GLP_ETMLIM) {
653 int glpk_basis_status)
const {
654 switch (glpk_basis_status) {
666 LOG(FATAL) <<
"Unknown GLPK basis status";
674#if GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION < 49
676 return lpx_get_int_parm(lp_, LPX_K_ITCNT);
678#elif (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 53) || \
679 GLP_MAJOR_VERSION >= 5
681 return glp_get_it_cnt(lp_);
684 LOG(WARNING) <<
"Total number of iterations is not available";
691 return mip_callback_info_->num_all_nodes_;
693 LOG(DFATAL) <<
"Number of nodes only available for discrete problems";
701 const int glpk_basis_status =
703 return TransformGLPKBasisStatus(glpk_basis_status);
707 DCHECK_GE(variable_index, 0);
709 const int glpk_basis_status =
710 glp_get_col_stat(lp_, MPSolverIndexToGlpkIndex(variable_index));
711 return TransformGLPKBasisStatus(glpk_basis_status);
716 LOG(WARNING) <<
"Ignoring ABNORMAL status from GLPK: This status may or may"
717 <<
" not indicate that a solution exists.";
728 LOG(DFATAL) <<
"ComputeExactConditionNumber not implemented for"
729 <<
" GLPK_MIXED_INTEGER_PROGRAMMING";
736 const int num_rows = glp_get_num_rows(lp_);
737 const int num_cols = glp_get_num_cols(lp_);
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);
744 for (
int col = 1;
col <= num_cols; ++
col) {
745 column_scaling_factor[
col] = glp_get_sjj(lp_,
col);
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());
754double GLPKInterface::ComputeScaledBasisL1Norm(
755 int num_rows,
int num_cols,
double* row_scaling_factor,
756 double* column_scaling_factor)
const {
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);
763 if (glpk_basis_status == GLP_BS) {
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]]);
770 column_norm *= fabs(column_scaling_factor[
col]);
772 norm = std::max(norm, column_norm);
776 for (
int row = 1;
row <= num_rows; ++
row) {
777 const int glpk_basis_status = glp_get_row_stat(lp_,
row);
779 if (glpk_basis_status == GLP_BS) {
783 const double column_norm = fabs(row_scaling_factor[
row]);
785 norm = std::max(norm, column_norm);
791double GLPKInterface::ComputeInverseScaledBasisL1Norm(
792 int num_rows,
int num_cols,
double* row_scaling_factor,
793 double* column_scaling_factor)
const {
795 if (!glp_bf_exists(lp_)) {
796 const int factorize_status = glp_factorize(lp_);
797 switch (factorize_status) {
799 LOG(FATAL) <<
"Not able to factorize: error GLP_EBADB.";
804 <<
"Not able to factorize: "
805 <<
"the basis matrix is singular within the working precision.";
810 <<
"Not able to factorize: the basis matrix is ill-conditioned.";
817 std::unique_ptr<double[]> right_hand_side(
new double[num_rows + 1]);
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;
835 right_hand_side[k] = 1.0;
837 for (
int row = 1;
row <= num_rows; ++
row) {
838 right_hand_side[
row] /= row_scaling_factor[
row];
840 glp_ftran(lp_, right_hand_side.get());
844 for (
int row = 1;
row <= num_rows; ++
row) {
845 const int k = glp_get_bhead(lp_,
row);
848 right_hand_side[
row] *= row_scaling_factor[k];
851 right_hand_side[
row] /= column_scaling_factor[k - num_rows];
855 double column_norm = 0.0;
856 for (
int row = 1;
row <= num_rows; ++
row) {
857 column_norm += fabs(right_hand_side[
row]);
860 norm = std::max(norm, column_norm);
867void GLPKInterface::ConfigureGLPKParameters(
const MPSolverParameters& param) {
869 glp_init_iocp(&mip_param_);
878 mip_param_.cb_info = mip_callback_info_.get();
884 glp_init_smcp(&lp_param_);
892 glp_scale_prob(lp_, GLP_SF_AUTO);
895 glp_adv_basis(lp_, 0);
898 SetParameters(param);
901void GLPKInterface::SetParameters(
const MPSolverParameters& param) {
908void GLPKInterface::SetRelativeMipGap(
double value) {
910 mip_param_.mip_gap =
value;
912 LOG(WARNING) <<
"The relative MIP gap is only available "
913 <<
"for discrete problems.";
917void GLPKInterface::SetPrimalTolerance(
double value) {
918 lp_param_.tol_bnd =
value;
921void GLPKInterface::SetDualTolerance(
double value) { lp_param_.tol_dj =
value; }
923void GLPKInterface::SetPresolveMode(
int value) {
926 mip_param_.presolve = GLP_OFF;
927 lp_param_.presolve = GLP_OFF;
931 mip_param_.presolve = GLP_ON;
932 lp_param_.presolve = GLP_ON;
941void GLPKInterface::SetScalingMode(
int value) {
945void GLPKInterface::SetLpAlgorithm(
int value) {
949 lp_param_.meth = GLP_DUALP;
953 lp_param_.meth = GLP_PRIMAL;
void Start()
When Start() is called multiple times, only the most recent is used.
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 IsContinuous() const override
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 ¶m) 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)
bool CheckSolutionIsSynchronized() const
static constexpr int64_t kUnknownNumberOfIterations
void InvalidateSolutionSynchronization()
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 bool CheckSolutionExists() const
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 ¶m)
Sets MIP specific parameters in the underlying solver.
virtual double infinity()
bool quiet_
Boolean indicator for the verbosity of the solver output.
void SetCommonParameters(const MPSolverParameters ¶m)
Sets parameters common to LP and MIP in the underlying solver.
MPSolver::ResultStatus result_status_
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
@ PRESOLVE_OFF
Presolve is off.
@ PRESOLVE_ON
Presolve is on.
@ BARRIER
Barrier algorithm.
@ 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.
int64_t time_limit() const
@ FEASIBLE
feasible, or stopped by limit.
@ NOT_SOLVED
not been solved yet.
@ INFEASIBLE
proven infeasible.
@ UNBOUNDED
proven unbounded.
@ ABNORMAL
abnormal, i.e., error of some kind.
const MPObjective & Objective() const
The class for variables of a Mathematical Programming (MP) model.
In SWIG mode, we don't want anything besides these top-level includes.
MPSolverInterface * BuildGLPKInterface(bool mip, MPSolver *const solver)
void SetupGlpkEnvAutomaticDeletion()
void GLPKGatherInformationCallback(glp_tree *tree, void *info)
Function to be called in the GLPK callback.