Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added MIP backend #136

Merged
merged 5 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 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 @@ -328,6 +328,12 @@ 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
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
126 changes: 126 additions & 0 deletions include/mp/convert/mip_backend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
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 (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
61 changes: 47 additions & 14 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 @@ -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
23 changes: 20 additions & 3 deletions solvers/gurobidirect/gurobibackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ extern "C" {

#include <string>

#include "mp/convert/backend.h"
#include "mp/convert/mip_backend.h"
#include "mp/convert/std_constr.h"

namespace mp {

class GurobiBackend : public BasicBackend<GurobiBackend>
class GurobiBackend : public MIPBackend<GurobiBackend>
{
using BaseBackend = BasicBackend<GurobiBackend>;
using BaseBackend = MIPBackend<GurobiBackend>;

//////////////////// [[ The public interface ]] //////////////////////
public:
Expand Down Expand Up @@ -122,6 +122,12 @@ class GurobiBackend : public BasicBackend<GurobiBackend>
void VarStatii(std::vector<int>& stt);
void ConStatii(std::vector<int>& stt);


void VarsIIS(std::vector<int>& stt);
void ConsIIS(std::vector<int>& stt);

double MIPGap();

/// Solution attributes
double NodeCount() const;
double Niterations() const;
Expand All @@ -132,6 +138,8 @@ class GurobiBackend : public BasicBackend<GurobiBackend>
private:
GRBenv *env = NULL;
GRBmodel *model = NULL;

int optimstatus; // Stores gurobi optimization status after SolveAndReportIntermediateResults

public:
void OpenSolver();
Expand All @@ -149,9 +157,13 @@ class GurobiBackend : public BasicBackend<GurobiBackend>
/// for direct access
struct Options {
std::string exportFile_;
int exportIIS_;
};

Options storedOptions_;



public:
/// These methods access Gurobi options. Used by AddSolverOption()
void GetSolverOption(const char* key, int& value) const;
Expand All @@ -161,6 +173,11 @@ class GurobiBackend : public BasicBackend<GurobiBackend>
void GetSolverOption(const char* key, std::string& value) const;
void SetSolverOption(const char* key, const std::string& value);


// Calculate MIP backend related quantities
void ComputeIIS();
void ComputeMIPGap() {}

};

} // namespace mp
Expand Down