Skip to content

Commit

Permalink
Merge pull request #136 from ampl/mipbackend
Browse files Browse the repository at this point in the history
Added MIP backend with computeIIS() and computeMIPGap()
  • Loading branch information
glebbelov authored May 5, 2021
2 parents 791d691 + ee0072b commit 50e3c0d
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 25 deletions.
3 changes: 3 additions & 0 deletions include/mp/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ enum {
namespace sol {
/** Solution status. */
enum Status {

NOT_CHECKED = -200,

/** Unknown status. */
UNKNOWN = -1,

Expand Down
29 changes: 23 additions & 6 deletions include/mp/convert/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ class BasicBackend :
MP_DISPATCH( WrapupSolve() );

MP_DISPATCH( ObtainSolutionStatus() );
MP_DISPATCH( CalculateDerivedResults() );
MP_DISPATCH( ReportSolution() );
if (MP_DISPATCH( timing() ))
MP_DISPATCH( PrintTimingInfo() );
Expand All @@ -226,6 +227,7 @@ class BasicBackend :
solve_status = MP_DISPATCH(
ConvertSolutionStatus(*MP_DISPATCH( interrupter() ), solve_code) );
}
void CalculateDerivedResults() { }

void ReportSolution() {
MP_DISPATCH( ReportSuffixes() );
Expand All @@ -249,9 +251,7 @@ class BasicBackend :
DeclareAndReportIntSuffix(suf_constatus, stt);
}

void ReportCustomSuffixes() {

}
void ReportCustomSuffixes() { }

void ReportPrimalDualValues() {
fmt::MemoryWriter writer;
Expand Down Expand Up @@ -285,8 +285,21 @@ class BasicBackend :
///
/////////////////////////////////////////////////////////////////////////////////

int solve_code=0;
/////////////////////////////// SOLUTION STATUS /////////////////////////////////
bool IsProblemInfOrUnb() const {
assert( IsSolStatusRetrieved() );
return sol::INFEASIBLE<=solve_code &&
sol::UNBOUNDED>=solve_code;
}

bool IsSolStatusRetrieved() const {
return sol::NOT_CHECKED!=solve_code;
}

int solve_code=sol::NOT_CHECKED;
std::string solve_status;

/////////////////////////////// STORING SOLUTON AND STATS ///////////////////////
double obj_value = std::numeric_limits<double>::quiet_NaN();
std::vector<double> solution, dual_solution;

Expand Down Expand Up @@ -328,9 +341,13 @@ class BasicBackend :
GetCQ().DeclareAndReportIntSuffix(suf, values);
}

void DeclareAndReportDblSuffix(const SuffixDef<double>& suf,
const std::vector<double>& values) {
GetCQ().DeclareAndReportDblSuffix(suf, values);
}


///////////////////////////// OPTIONS /////////////////////////////////
/// TODOs
/// - hide all Solver stuff behind an abstract interface
protected:

using Solver::AddOption;
Expand Down
6 changes: 6 additions & 0 deletions include/mp/convert/converter_flat_query.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ class FlatConverterQuery : public ConverterQuery {
GetOutputModel().DeclareAndReportIntSuffix(suf, values);
}


void DeclareAndReportDblSuffix(const SuffixDef<double>& suf,
const std::vector<double>& values) override {
GetOutputModel().DeclareAndReportDblSuffix(suf, values);
}

void HandleSolution(int status, fmt::CStringRef msg,
const double *x, const double * y, double obj) override {
GetCvt().HandleSolution(status, msg, x, y, obj);
Expand Down
3 changes: 3 additions & 0 deletions include/mp/convert/converter_query.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class ConverterQuery {
virtual void DeclareAndReportIntSuffix(const SuffixDef<int>& suf,
const std::vector<int>& values) = 0;

virtual void DeclareAndReportDblSuffix(const SuffixDef<double>& suf,
const std::vector<double>& values) = 0;

virtual void HandleSolution(int, fmt::CStringRef,
const double *, const double *, double) = 0;

Expand Down
127 changes: 127 additions & 0 deletions include/mp/convert/mip_backend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
Abstract MIP solver backend wrapper.
Copyright (C) 2020 AMPL Optimization Inc
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that the copyright notice and this permission notice and warranty
disclaimer appear in supporting documentation.
The author and AMPL Optimization Inc disclaim all warranties with
regard to this software, including all implied warranties of
merchantability and fitness. In no event shall the author be liable
for any special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether in an
action of contract, negligence or other tortious action, arising out
of or in connection with the use or performance of this software.
*/

#ifndef MIPBACKEND_H_
#define MIPBACKEND_H_

#include "mp/convert/backend.h"


namespace mp {

/// MIP backend wrapper.
/// The MIP wrapper provides common functionality relative to MIP solvers;
/// it implements the common suffixes and the logic shared across all MIP
/// solvers
template <class Impl>
class MIPBackend :
public BasicBackend<Impl>
{
using BaseBackend = BasicBackend<Impl>;

struct Options {
int exportIIS_;
int returnMipGap_;
};
Options mipStoredOptions_;

public:

void InitOptions() {
BaseBackend::InitOptions();
MP_DISPATCH( InitMIPOptions() );
}

void InitMIPOptions() {
this->AddStoredOption("iisfind",
"Whether to find and export the IIS. "
"Default = 0 (don't export).",
mipStoredOptions_.exportIIS_);
this->AddStoredOption("return_mipgap",
"Whether to return mipgap suffixes or include mipgap values\n\
(|objectve - best_bound|) in the solve_message: sum of\n\
1 = return relmipgap suffix (relative to |obj|);\n\
2 = return absmipgap suffix (absolute mipgap);\n\
4 = suppress mipgap values in solve_message.\n\
Default = 0. The suffixes are on the objective and problem.\n\
Returned suffix values are +Infinity if no integer-feasible\n\
solution has been found, in which case no mipgap values are\n\
reported in the solve_message.",
mipStoredOptions_.returnMipGap_);
}

void CalculateDerivedResults() {
BasicBackend<Impl>::CalculateDerivedResults();
if (MP_DISPATCH( IsProblemInfOrUnb() ) &&
mipStoredOptions_.exportIIS_)
MP_DISPATCH( ComputeIIS() );
if (mipStoredOptions_.returnMipGap_)
MP_DISPATCH( ComputeMipGap() );

}

// Override for standard MIP calculations
/**
* Compute the IIS and relevant values
**/
void ComputeIIS();
/**
* Get IIS values for constraints
**/
void ConsIIS(std::vector<int>& stt) { stt.clear(); }
/**
* Get IIS values for variables
**/
void VarsIIS(std::vector<int>& stt) { stt.clear(); }

void ComputeMipGap() {}
double MIPGap() { throw std::runtime_error("Not implemented"); }

const SuffixDef<int> sufIISCon = { "iis", suf::CON | suf::OUTPUT };
const SuffixDef<int> sufIISVar = { "iis", suf::VAR | suf::OUTPUT };

const SuffixDef<double> sufRelMipGapObj = { "relmipgap", suf::OBJ | suf::OUTPUT };
const SuffixDef<double> sufRelMipGapProb = { "relmipgap", suf::PROBLEM | suf::OUTPUT };

void ReportStandardSuffixes() {
BasicBackend<Impl>::ReportStandardSuffixes();
std::vector<int> stt;
std::vector<double> dbl;
if (mipStoredOptions_.exportIIS_)
{
MP_DISPATCH(ConsIIS(stt));
this->DeclareAndReportIntSuffix(sufIISCon, stt);
MP_DISPATCH(VarsIIS(stt));
this->DeclareAndReportIntSuffix(sufIISVar, stt);
}
if (mipStoredOptions_.returnMipGap_)
{
dbl.clear();
dbl.push_back(MP_DISPATCH( MIPGap() ));
this->DeclareAndReportDblSuffix(sufRelMipGapObj, dbl);
this->DeclareAndReportDblSuffix(sufRelMipGapProb, dbl);
}
}

};
} // namespace mp

#endif // MIPBACKEND_H_
2 changes: 1 addition & 1 deletion include/mp/convert/model_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class ModelAdapter : public Model {
}

template <class T>
void DeclareAndReportSuffix(const SuffixDef<int>& sufdef,
void DeclareAndReportSuffix(const SuffixDef<T>& sufdef,
const std::vector<T>& values) {
if (values.empty())
return;
Expand Down
63 changes: 48 additions & 15 deletions solvers/gurobidirect/gurobibackend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,17 @@ int GurobiBackend::NumberOfObjectives() const {
void GurobiBackend::PrimalSolution(std::vector<double> &x) {
int num_vars = NumberOfVariables();
x.resize(num_vars);
int error = GRBgetdblattrarray(model, GRB_DBL_ATTR_X, 0, num_vars, x.data());
int error = GRBgetdblattrarray(model, GRB_DBL_ATTR_X,
0, num_vars, x.data());
if (error)
x.clear();
}

void GurobiBackend::DualSolution(std::vector<double> &pi) {
int num_cons = NumberOfConstraints();
pi.resize(num_cons);
int error = GRBgetdblattrarray(model, GRB_DBL_ATTR_PI, 0, num_cons, pi.data());
int error = GRBgetdblattrarray(model, GRB_DBL_ATTR_PI,
0, num_cons, pi.data());
if (error)
pi.clear();
}
Expand All @@ -108,15 +110,17 @@ double GurobiBackend::ObjectiveValue() const {
void GurobiBackend::VarStatii(std::vector<int> &stt) {
int num_vars = NumberOfVariables();
stt.resize(num_vars);
int error = GRBgetintattrarray(model, GRB_INT_ATTR_VBASIS, 0, num_vars, stt.data());
int error = GRBgetintattrarray(model, GRB_INT_ATTR_VBASIS,
0, num_vars, stt.data());
if (error)
stt.clear();
}

void GurobiBackend::ConStatii(std::vector<int> &stt) {
int num_cons = NumberOfConstraints();
stt.resize(num_cons);
int error = GRBgetintattrarray(model, GRB_INT_ATTR_CBASIS, 0, num_cons, stt.data());
int error = GRBgetintattrarray(model, GRB_INT_ATTR_CBASIS,
0, num_cons, stt.data());
if (error)
stt.clear();
}
Expand Down Expand Up @@ -152,7 +156,7 @@ std::string GurobiBackend::ConvertSolutionStatus(
default:
// Fall through.
if (interrupter.Stop()) {
solve_code = 600;
solve_code = sol::INTERRUPTED;
return "interrupted";
}
int solcount;
Expand Down Expand Up @@ -196,10 +200,11 @@ void GurobiBackend::AddVariable(Variable var) {
void GurobiBackend::AddLinearObjective( const LinearObjective& lo ) {
if (1>=NumberOfObjectives()) {
GRB_CALL( GRBsetintattr(model, GRB_INT_ATTR_MODELSENSE,
obj::Type::MAX==lo.get_sense() ? GRB_MAXIMIZE : GRB_MINIMIZE) );
obj::Type::MAX==lo.get_sense() ? GRB_MAXIMIZE : GRB_MINIMIZE) );
GRB_CALL( GRBsetdblattrlist(model, GRB_DBL_ATTR_OBJ,
lo.get_num_terms(),
(int*)lo.get_vars().data(), (double*)lo.get_coefs().data()) );
(int*)lo.get_vars().data(),
(double*)lo.get_coefs().data()) );
} else {
throw std::runtime_error("Multiple objectives not supported");
// TODO
Expand All @@ -219,28 +224,28 @@ void GurobiBackend::AddQuadraticObjective(const QuadraticObjective &qo) {
}

}

void GurobiBackend::AddConstraint( const LinearConstraint& lc ) {
GRB_CALL( GRBaddrangeconstr(model, lc.nnz(),
(int*)lc.pvars(), (double*)lc.pcoefs(), lc.lb(), lc.ub(), NULL) );
(int*)lc.pvars(), (double*)lc.pcoefs(),
lc.lb(), lc.ub(), NULL) );
}

void GurobiBackend::AddConstraint( const QuadraticConstraint& qc ) {
const auto& qt = qc.GetQPTerms();
if (qc.lb()==qc.ub())
GRB_CALL( GRBaddqconstr(model, qc.nnz(), (int*)qc.pvars(), (double*)qc.pcoefs(),
qt.num_terms(), (int*)qt.vars1(), (int*)qt.vars2(), (double*)qt.coefs(),
GRB_EQUAL, qc.lb(), NULL) );
qt.num_terms(), (int*)qt.vars1(), (int*)qt.vars2(),
(double*)qt.coefs(), GRB_EQUAL, qc.lb(), NULL) );
else { // Let solver deal with lb>~ub etc.
if (qc.lb()>MinusInfinity()) {
GRB_CALL( GRBaddqconstr(model, qc.nnz(), (int*)qc.pvars(), (double*)qc.pcoefs(),
qt.num_terms(), (int*)qt.vars1(), (int*)qt.vars2(), (double*)qt.coefs(),
GRB_GREATER_EQUAL, qc.lb(), NULL) );
qt.num_terms(), (int*)qt.vars1(), (int*)qt.vars2(),
(double*)qt.coefs(), GRB_GREATER_EQUAL, qc.lb(), NULL) );
}
if (qc.ub()<Infinity()) {
GRB_CALL( GRBaddqconstr(model, qc.nnz(), (int*)qc.pvars(), (double*)qc.pcoefs(),
qt.num_terms(), (int*)qt.vars1(), (int*)qt.vars2(), (double*)qt.coefs(),
GRB_LESS_EQUAL, qc.ub(), NULL) );
qt.num_terms(), (int*)qt.vars1(), (int*)qt.vars2(),
(double*)qt.coefs(), GRB_LESS_EQUAL, qc.ub(), NULL) );
}
}
}
Expand Down Expand Up @@ -380,6 +385,7 @@ void GurobiBackend::InitOptions() {
"limit on solve time (in seconds; default: no limit).",
GRB_DBL_PAR_TIMELIMIT, 0.0, DBL_MAX);


}

void GurobiBackend::GetSolverOption(const char *key, int &value) const {
Expand Down Expand Up @@ -408,5 +414,32 @@ void GurobiBackend::SetSolverOption(const char *key, const std::string& value) {
GRB_CALL( GRBsetstrparam(GRBgetenv(model), key, value.c_str()) );
}

void GurobiBackend::ComputeIIS() {
GRB_CALL(GRBcomputeIIS(model));
}

void GurobiBackend::VarsIIS(std::vector<int>& stt) {
int num_vars = NumberOfVariables();
stt.resize(num_vars);
int error = GRBgetintattrarray(model, GRB_INT_ATTR_IIS_LB,
0, stt.size(), stt.data());
if (error)
stt.clear();
}

void GurobiBackend::ConsIIS(std::vector<int>& stt) {
int nl = GetGrbIntAttribute(GRB_INT_ATTR_NUMSOS) +
GetGrbIntAttribute(GRB_INT_ATTR_NUMQCONSTRS) +
GetGrbIntAttribute(GRB_INT_ATTR_NUMGENCONSTRS);
stt.resize((std::size_t)NumberOfConstraints() + nl);
int error = GRBgetintattrarray(model, GRB_INT_ATTR_IIS_CONSTR,
0, stt.size()-nl, stt.data()+nl);
if (error)
stt.clear();
}

double GurobiBackend::MIPGap() {
return GetGrbDblAttribute(GRB_DBL_ATTR_MIPGAP);
}

} // namespace mp
Loading

0 comments on commit 50e3c0d

Please sign in to comment.