23#include "absl/base/attributes.h"
24#include "absl/strings/str_format.h"
58 CHECK(tree !=
nullptr);
59 CHECK(info !=
nullptr);
61 switch (glp_ios_reason(tree)) {
68 glp_ios_tree_size(tree,
nullptr,
nullptr, &glpk_info->
num_all_nodes_);
70 int node_id = glp_ios_best_node(tree);
85int MPSolverIndexToGlpkIndex(
int index) {
return index + 1; }
103 void Reset()
override;
117 double new_value,
double old_value)
override;
122 double coefficient)
override;
132 int64_t
nodes()
const override;
145 bool IsLP()
const override {
return !mip_; }
146 bool IsMIP()
const override {
return mip_; }
153 return absl::StrFormat(
"GLPK %s", glp_version());
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;
174 void ExtractOldConstraints();
175 void ExtractOneConstraint(
MPConstraint* constraint,
int* indices,
183 double ComputeScaledBasisL1Norm(
int num_rows,
int num_cols,
184 double* row_scaling_factor,
185 double* column_scaling_factor)
const;
190 double ComputeInverseScaledBasisL1Norm(
int num_rows,
int num_cols,
191 double* row_scaling_factor,
192 double* column_scaling_factor)
const;
201 std::unique_ptr<GLPKInformation> mip_callback_info_;
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_);
218 CHECK(lp_ !=
nullptr);
219 glp_delete_prob(lp_);
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);
237 glp_set_obj_dir(lp_, maximize ? GLP_MAX : GLP_MIN);
248 DCHECK(lp_ !=
nullptr);
250 const int glpk_var_index = MPSolverIndexToGlpkIndex(mpsolver_var_index);
254 glp_set_col_bnds(lp_, glpk_var_index, GLP_FX, lb, ub);
256 glp_set_col_bnds(lp_, glpk_var_index, GLP_DB, lb, ub);
259 glp_set_col_bnds(lp_, glpk_var_index, GLP_LO, lb, 0.0);
262 glp_set_col_bnds(lp_, glpk_var_index, GLP_UP, 0.0, ub);
264 glp_set_col_bnds(lp_, glpk_var_index, GLP_FR, 0.0, 0.0);
273 glp_set_col_kind(lp_, MPSolverIndexToGlpkIndex(mpsolver_var_index),
274 integer ? GLP_IV : GLP_CV);
282 double lb,
double ub) {
289 const int glpk_constraint_index =
290 MPSolverIndexToGlpkIndex(mpsolver_constraint_index);
291 DCHECK(lp_ !=
nullptr);
296 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_FX, lb, ub);
298 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_DB, lb, ub);
301 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_LO, lb, 0.0);
304 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_UP, 0.0, ub);
306 glp_set_row_bnds(lp_, glpk_constraint_index, GLP_FR, 0.0, 0.0);
312 double new_value,
double old_value) {
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());
333 glp_set_mat_row(lp_, MPSolverIndexToGlpkIndex(constraint->
index()), 0,
340 double coefficient) {
352 for (
const auto& entry :
solver_->objective_->coefficients_) {
353 const int mpsolver_var_index = entry.first->index();
358 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(mpsolver_var_index), 0.0);
362 glp_set_obj_coef(lp_, 0, 0.0);
375 int total_num_vars =
solver_->variables_.size();
381 if (!var->
name().empty()) {
382 glp_set_col_name(lp_, MPSolverIndexToGlpkIndex(j), var->
name().c_str());
388 double tmp_obj_coef = 0.0;
389 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(j), tmp_obj_coef);
392 ExtractOldConstraints();
397void GLPKInterface::ExtractOldConstraints() {
398 const int max_constraint_size =
402 std::unique_ptr<int[]> indices(
new int[max_constraint_size + 1]);
403 std::unique_ptr<double[]> coefs(
new double[max_constraint_size + 1]);
408 const int size = ct->coefficients_.size();
413 if (ct->ContainsNewVariables()) {
414 ExtractOneConstraint(ct, indices.get(), coefs.get());
422void GLPKInterface::ExtractOneConstraint(
MPConstraint*
const constraint,
424 double*
const coefs) {
427 for (
const auto& entry : constraint->coefficients_) {
429 indices[k] = MPSolverIndexToGlpkIndex(entry.first->index());
430 coefs[k] = entry.second;
433 glp_set_mat_row(lp_, MPSolverIndexToGlpkIndex(constraint->index()), k - 1,
439 int total_num_rows =
solver_->constraints_.size();
447 if (ct->
name().empty()) {
448 glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i),
449 absl::StrFormat(
"ct_%i", i).c_str());
451 glp_set_row_name(lp_, MPSolverIndexToGlpkIndex(i), ct->
name().c_str());
455 num_coefs += ct->coefficients_.size();
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]);
470 for (
int i = 0; i <
solver_->constraints_.size(); ++i) {
472 for (
const auto& entry : ct->coefficients_) {
474 constraint_indices[k] = MPSolverIndexToGlpkIndex(ct->
index());
475 variable_indices[k] = MPSolverIndexToGlpkIndex(entry.first->index());
476 coefs[k] = entry.second;
480 CHECK_EQ(num_coefs + 1, k);
481 glp_load_matrix(lp_, num_coefs, constraint_indices.get(),
482 variable_indices.get(), coefs.get());
485 int max_constraint_size =
solver_->ComputeMaxConstraintSize(
489 std::unique_ptr<int[]> indices(
new int[max_constraint_size + 1]);
490 std::unique_ptr<double[]> coefs(
new double[max_constraint_size + 1]);
492 ExtractOneConstraint(
solver_->constraints_[i], indices.get(),
502 for (
const auto& entry :
solver_->objective_->coefficients_) {
503 glp_set_obj_coef(lp_, MPSolverIndexToGlpkIndex(entry.first->index()),
507 glp_set_obj_coef(lp_, 0,
solver_->Objective().offset());
523 glp_term_out(GLP_OFF);
525 glp_term_out(GLP_ON);
529 VLOG(1) << absl::StrFormat(
"Model built in %.3f seconds.", timer.
Get());
534 ConfigureGLPKParameters(param);
538 int solver_status = glp_simplex(lp_, &lp_param_);
542 if (solver_status == 0) {
543 solver_status = glp_intopt(lp_, &mip_param_);
550 if (solver_status == GLP_ETMLIM) {
557 VLOG(1) << absl::StrFormat(
"GLPK Status: %i (time spent: %.3f seconds).",
558 solver_status, timer.
Get());
569 for (
int i = 0; i <
solver_->variables_.size(); ++i) {
573 val = glp_mip_col_val(lp_, MPSolverIndexToGlpkIndex(i));
575 val = glp_get_col_prim(lp_, MPSolverIndexToGlpkIndex(i));
578 VLOG(3) << var->
name() <<
": value =" << val;
581 reduced_cost = glp_get_col_dual(lp_, MPSolverIndexToGlpkIndex(i));
583 VLOG(4) << var->
name() <<
": reduced cost = " << reduced_cost;
586 for (
int i = 0; i <
solver_->constraints_.size(); ++i) {
589 const double dual_value =
590 glp_get_row_dual(lp_, MPSolverIndexToGlpkIndex(i));
592 VLOG(4) <<
"row " << MPSolverIndexToGlpkIndex(i)
593 <<
": dual value = " << dual_value;
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) {
610 }
else if (solver_status == GLP_ETMLIM) {
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) {
629 }
else if (tmp_status == GLP_UNBND) {
634 }
else if (solver_status == GLP_ETMLIM) {
647 int glpk_basis_status)
const {
648 switch (glpk_basis_status) {
660 LOG(FATAL) <<
"Unknown GLPK basis status";
668#if GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION < 49
670 return lpx_get_int_parm(lp_, LPX_K_ITCNT);
672#elif (GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 53) || \
673 GLP_MAJOR_VERSION >= 5
675 return glp_get_it_cnt(lp_);
678 LOG(WARNING) <<
"Total number of iterations is not available";
685 return mip_callback_info_->num_all_nodes_;
687 LOG(DFATAL) <<
"Number of nodes only available for discrete problems";
693 DCHECK_GE(constraint_index, 0);
695 const int glpk_basis_status =
696 glp_get_row_stat(lp_, MPSolverIndexToGlpkIndex(constraint_index));
697 return TransformGLPKBasisStatus(glpk_basis_status);
701 DCHECK_GE(variable_index, 0);
703 const int glpk_basis_status =
704 glp_get_col_stat(lp_, MPSolverIndexToGlpkIndex(variable_index));
705 return TransformGLPKBasisStatus(glpk_basis_status);
710 LOG(WARNING) <<
"Ignoring ABNORMAL status from GLPK: This status may or may"
711 <<
" not indicate that a solution exists.";
722 LOG(DFATAL) <<
"ComputeExactConditionNumber not implemented for"
723 <<
" GLPK_MIXED_INTEGER_PROGRAMMING";
730 const int num_rows = glp_get_num_rows(lp_);
731 const int num_cols = glp_get_num_cols(lp_);
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);
738 for (
int col = 1; col <= num_cols; ++col) {
739 column_scaling_factor[col] = glp_get_sjj(lp_, col);
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());
748double GLPKInterface::ComputeScaledBasisL1Norm(
749 int num_rows,
int num_cols,
double* row_scaling_factor,
750 double* column_scaling_factor)
const {
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);
757 if (glpk_basis_status == GLP_BS) {
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]]);
764 column_norm *= fabs(column_scaling_factor[col]);
766 norm = std::max(norm, column_norm);
770 for (
int row = 1; row <= num_rows; ++row) {
771 const int glpk_basis_status = glp_get_row_stat(lp_, row);
773 if (glpk_basis_status == GLP_BS) {
777 const double column_norm = fabs(row_scaling_factor[row]);
779 norm = std::max(norm, column_norm);
785double GLPKInterface::ComputeInverseScaledBasisL1Norm(
786 int num_rows,
int num_cols,
double* row_scaling_factor,
787 double* column_scaling_factor)
const {
789 if (!glp_bf_exists(lp_)) {
790 const int factorize_status = glp_factorize(lp_);
791 switch (factorize_status) {
793 LOG(FATAL) <<
"Not able to factorize: error GLP_EBADB.";
798 <<
"Not able to factorize: "
799 <<
"the basis matrix is singular within the working precision.";
804 <<
"Not able to factorize: the basis matrix is ill-conditioned.";
811 std::unique_ptr<double[]> right_hand_side(
new double[num_rows + 1]);
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;
829 right_hand_side[k] = 1.0;
831 for (
int row = 1; row <= num_rows; ++row) {
832 right_hand_side[row] /= row_scaling_factor[row];
834 glp_ftran(lp_, right_hand_side.get());
838 for (
int row = 1; row <= num_rows; ++row) {
839 const int k = glp_get_bhead(lp_, row);
842 right_hand_side[row] *= row_scaling_factor[k];
845 right_hand_side[row] /= column_scaling_factor[k - num_rows];
849 double column_norm = 0.0;
850 for (
int row = 1; row <= num_rows; ++row) {
851 column_norm += fabs(right_hand_side[row]);
854 norm = std::max(norm, column_norm);
863 glp_init_iocp(&mip_param_);
866 VLOG(1) <<
"Setting time limit = " <<
solver_->time_limit() <<
" ms.";
867 mip_param_.tm_lim =
solver_->time_limit();
872 mip_param_.cb_info = mip_callback_info_.get();
878 glp_init_smcp(&lp_param_);
881 VLOG(1) <<
"Setting time limit = " <<
solver_->time_limit() <<
" ms.";
882 lp_param_.tm_lim =
solver_->time_limit();
886 glp_scale_prob(lp_, GLP_SF_AUTO);
889 glp_adv_basis(lp_, 0);
892 SetParameters(param);
902void GLPKInterface::SetRelativeMipGap(
double value) {
904 mip_param_.mip_gap = value;
906 LOG(WARNING) <<
"The relative MIP gap is only available "
907 <<
"for discrete problems.";
911void GLPKInterface::SetPrimalTolerance(
double value) {
912 lp_param_.tol_bnd = value;
915void GLPKInterface::SetDualTolerance(
double value) { lp_param_.tol_dj = value; }
917void GLPKInterface::SetPresolveMode(
int value) {
920 mip_param_.presolve = GLP_OFF;
921 lp_param_.presolve = GLP_OFF;
925 mip_param_.presolve = GLP_ON;
926 lp_param_.presolve = GLP_ON;
935void GLPKInterface::SetScalingMode(
int value) {
939void GLPKInterface::SetLpAlgorithm(
int value) {
943 lp_param_.meth = GLP_DUALP;
947 lp_param_.meth = GLP_PRIMAL;
961const void*
const kRegisterGLPKLP ABSL_ATTRIBUTE_UNUSED = [] {
969const void*
const kRegisterGLPKMIP ABSL_ATTRIBUTE_UNUSED = [] {
int64_t nodes() const override
~GLPKInterface() override
bool IsLP() const override
double ComputeExactConditionNumber() const override
void ClearObjective() override
void ClearConstraint(MPConstraint *constraint) override
void ExtractNewConstraints() override
void SetVariableBounds(int mpsolver_var_index, double lb, double ub) override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
bool IsContinuous() const override
bool CheckSolutionExists() const override
void AddRowConstraint(MPConstraint *ct) override
void * underlying_solver() override
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
MPSolver::ResultStatus Solve(const MPSolverParameters ¶m) override
void AddVariable(MPVariable *var) override
GLPKInterface(MPSolver *solver, bool mip)
bool IsMIP() const override
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 ExtractObjective() override
void ExtractNewVariables() 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)
bool CheckSolutionIsSynchronized() const
static constexpr int64_t kUnknownNumberOfIterations
friend class MPConstraint
void InvalidateSolutionSynchronization()
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
int last_constraint_index_
virtual bool CheckSolutionExists() const
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
double best_objective_bound_
void SetMIPParameters(const MPSolverParameters ¶m)
MPSolverInterface(MPSolver *solver)
virtual double infinity()
void SetCommonParameters(const MPSolverParameters ¶m)
MPSolver::ResultStatus result_status_
SynchronizationStatus sync_status_
@ 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.
@ 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.
@ GLPK_MIXED_INTEGER_PROGRAMMING
@ GLPK_LINEAR_PROGRAMMING
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)
void SetupGlpkEnvAutomaticDeletion()
void GLPKGatherInformationCallback(glp_tree *tree, void *info)