/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* */
/* This file is part of the program and library */
/* PaPILO --- Parallel Presolve for Integer and Linear Optimization */
/* */
/* Copyright (C) 2020-2022 Konrad-Zuse-Zentrum */
/* fuer Informationstechnik Berlin */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU Lesser General Public License as published */
/* by the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Lesser General Public License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with this program. If not, see . */
/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef _PAPILO_INTERFACES_GUROBI_INTERFACE_HPP_
#define _PAPILO_INTERFACES_GUROBI_INTERFACE_HPP_
#include "papilo/misc/Vec.hpp"
#include
#include
#include "gurobi_c++.h"
#include "papilo/core/Problem.hpp"
#include "papilo/core/Solution.hpp"
#include "papilo/interfaces/SolverInterface.hpp"
namespace papilo
{
template
class GurobiInterface : public SolverInterface
{
private:
int grb_status;
GRBEnv* env;
Solution sol;
int
doSetUp( const Problem& problem, const Vec& origRowMap,
const Vec& origColMap )
{
if( !std::is_same::value )
{
fmt::print( "Please use double precision when solving with "
"Gurobi." );
return -1;
}
const Vec& varNames = problem.getVariableNames();
const Vec& rowNames = problem.getConstraintNames();
const VariableDomains& domains = problem.getVariableDomains();
const Vec& obj = problem.getObjective().coefficients;
const Vec& rhs = problem.getConstraintMatrix().getRightHandSides();
const Vec& lhs = problem.getConstraintMatrix().getLeftHandSides();
const auto consMatrix = problem.getConstraintMatrix();
env = new GRBEnv();
GRBModel model = GRBModel( *env );
model.set( GRB_StringAttr_ModelName, problem.getName() );
model.set( GRB_IntAttr_ModelSense, GRB_MINIMIZE );
int ncols = problem.getNCols();
GRBVar* vars = new GRBVar[ncols];
for( int i = 0; i < ncols; ++i )
{
assert( !domains.flags[i].test( ColFlag::kInactive ) );
double lb = domains.flags[i].test( ColFlag::kLbInf )
? -GRB_INFINITY
: domains.lower_bounds[i];
double ub = domains.flags[i].test( ColFlag::kUbInf )
? GRB_INFINITY
: domains.upper_bounds[i];
char type;
if( domains.flags[i].test( ColFlag::kIntegral ) )
{
if( lb == 0 && ub == 1 )
type = GRB_BINARY;
else
type = GRB_INTEGER;
}
else if( domains.flags[i].test( ColFlag::kImplInt ) )
type = GRB_INTEGER;
else
type = GRB_CONTINUOUS;
vars[i] = model.addVar( lb, ub, double( obj[i] ), type,
varNames[origColMap[i]].c_str() );
model.update();
}
for( int i = 0; i < problem.getNRows(); ++i )
{
auto row_coeff = problem.getConstraintMatrix().getRowCoefficients( i );
GRBLinExpr grbLinExpr = 0;
for( int j = 0; j < row_coeff.getLength(); ++j )
grbLinExpr += vars[row_coeff.getIndices()[j]] *
double( row_coeff.getValues()[j] );
if( consMatrix.getRowFlags()[i].test( RowFlag::kEquation ) )
{
model.addConstr( grbLinExpr, GRB_EQUAL, double( rhs[i] ),
rowNames[origRowMap[i]] );
continue;
}
if( !consMatrix.getRowFlags()[i].test( RowFlag::kLhsInf ) )
model.addConstr( grbLinExpr, GRB_GREATER_EQUAL, double( lhs[i] ),
rowNames[origRowMap[i]] + "_lhs" );
if( !consMatrix.getRowFlags()[i].test( RowFlag::kRhsInf ) )
model.addConstr( grbLinExpr, GRB_LESS_EQUAL, double( rhs[i] ),
rowNames[origRowMap[i]] + "_rhs" );
model.update();
}
model.update();
model.write( "test.mps" );
model.optimize();
grb_status = model.get( GRB_IntAttr_Status );
fmt::print( "{}\n", model.get( GRB_DoubleAttr_ObjVal ) );
Vec primal{};
try
{
for( int i = 0; i < ncols; i++ )
primal.push_back( REAL( vars[i].get( GRB_DoubleAttr_X ) ) );
sol = Solution( primal );
}
catch( GRBException& ex )
{
return 1;
}
return 0;
}
int
doSetUp( const Problem& problem, const Vec& origRowMap,
const Vec& origColMap, const Components& components,
const ComponentInfo& component )
{
if( !std::is_same::value )
{
fmt::print( "Please use double precision when solving with "
"Gurobi." );
return -1;
}
int ncols = components.getComponentsNumCols( component.componentid );
int nrows = components.getComponentsNumRows( component.componentid );
const int* colset = components.getComponentsCols( component.componentid );
const int* rowset = components.getComponentsRows( component.componentid );
const Vec& varNames = problem.getVariableNames();
const Vec& rowNames = problem.getConstraintNames();
const VariableDomains& domains = problem.getVariableDomains();
const Vec& obj = problem.getObjective().coefficients;
const Vec& rhs = problem.getConstraintMatrix().getRightHandSides();
const Vec& lhs = problem.getConstraintMatrix().getLeftHandSides();
const auto consMatrix = problem.getConstraintMatrix();
env = new GRBEnv();
GRBModel model = GRBModel( *env );
model.set( GRB_StringAttr_ModelName, problem.getName() );
model.set( GRB_IntAttr_ModelSense, GRB_MINIMIZE );
GRBVar* vars = new GRBVar[ncols];
for( int i = 0; i < ncols; ++i )
{
int col = colset[i];
assert( !domains.flags[col].test( ColFlag::kInactive ) );
double lb = domains.flags[col].test( ColFlag::kLbInf )
? -GRB_INFINITY
: domains.lower_bounds[col];
double ub = domains.flags[col].test( ColFlag::kUbInf )
? GRB_INFINITY
: domains.upper_bounds[col];
char type;
if( domains.flags[col].test( ColFlag::kIntegral ) )
{
if( lb == 0 && ub == 1 )
type = GRB_BINARY;
else
type = GRB_INTEGER;
}
else if( domains.flags[col].test( ColFlag::kImplInt ) )
type = GRB_INTEGER;
else
type = GRB_CONTINUOUS;
vars[col] = model.addVar( lb, ub, double( obj[col] ), type,
varNames[origColMap[col]].c_str() );
model.update();
}
for( int i = 0; i < nrows; ++i )
{
int row = rowset[i];
auto row_coeff =
problem.getConstraintMatrix().getRowCoefficients( row );
GRBLinExpr grbLinExpr = 0;
for( int j = 0; j < row_coeff.getLength(); ++j )
grbLinExpr += vars[row_coeff.getIndices()[j]] *
double( row_coeff.getValues()[j] );
if( consMatrix.getRowFlags()[row].test( RowFlag::kEquation ) )
{
model.addConstr( grbLinExpr, GRB_EQUAL, double( rhs[row] ),
rowNames[origRowMap[row]] );
continue;
}
if( !consMatrix.getRowFlags()[row].test( RowFlag::kLhsInf ) )
model.addConstr( grbLinExpr, GRB_GREATER_EQUAL, double( lhs[row] ),
rowNames[origRowMap[row]] + "_lhs" );
if( !consMatrix.getRowFlags()[row].test( RowFlag::kRhsInf ) )
model.addConstr( grbLinExpr, GRB_LESS_EQUAL, double( rhs[row] ),
rowNames[origRowMap[row]] + "_rhs" );
model.update();
}
model.update();
model.write( "test.mps" );
model.optimize();
grb_status = model.get( GRB_IntAttr_Status );
fmt::print( "{}\n", model.get( GRB_DoubleAttr_ObjVal ) );
Vec primal{};
try
{
for( int i = 0; i < ncols; i++ )
primal.push_back( REAL( vars[i].get( GRB_DoubleAttr_X ) ) );
sol = Solution( primal );
}
catch( GRBException& ex )
{
return 1;
}
return 0;
}
public:
GurobiInterface() {}
void
setUp( const Problem& prob, const Vec& row_maps,
const Vec& col_maps, const Components& components,
const ComponentInfo& component ) override
{
if( doSetUp( prob, row_maps, col_maps, components, component ) != 0 )
this->status = SolverStatus::kError;
}
void
setNodeLimit( int num ) override
{
}
void
setGapLimit( const REAL& gaplim ) override
{
}
void
setSoftTimeLimit( double tlim ) override
{
}
void
setTimeLimit( double tlim ) override
{
}
void
setVerbosity( VerbosityLevel verbosity ) override
{
}
void
setUp( const Problem& prob, const Vec& row_maps,
const Vec& col_maps ) override
{
if( doSetUp( prob, row_maps, col_maps ) != 0 )
this->status = SolverStatus::kError;
}
void
solve() override
{
assert( this->status != SolverStatus::kError );
switch( grb_status )
{
case GRB_INTERRUPTED:
case GRB_ITERATION_LIMIT:
case GRB_NODE_LIMIT:
case GRB_TIME_LIMIT:
this->status = SolverStatus::kInterrupted;
break;
case GRB_UNBOUNDED:
this->status = SolverStatus::kUnbndOrInfeas;
return;
case GRB_INFEASIBLE:
this->status = SolverStatus::kInfeasible;
return;
case GRB_INF_OR_UNBD:
this->status = SolverStatus::kUnbounded;
return;
case GRB_OPTIMAL:
this->status = SolverStatus::kOptimal;
}
}
REAL
getDualBound() override
{
// TODO:
return 0;
}
bool
getSolution( Solution& solbuffer ) override
{
solbuffer = sol;
return true;
}
bool
getSolution( const Components& components, int component,
Solution& solbuffer ) override
{
const int* colset = components.getComponentsCols( component );
assert( components.getComponentsNumCols( component ) ==
sol.primal.size() );
for( std::size_t i = 0; i < sol.primal.size(); ++i )
solbuffer.primal[colset[i]] = sol.primal[i];
return true;
}
SolverType
getType() override
{
return SolverType::MIP;
}
String
getName() override
{
return "gurobi";
}
void
printDetails() override
{
}
bool
is_dual_solution_available() override
{
return false;
}
void
addParameters( ParameterSet& paramSet ) override
{
}
~GurobiInterface() = default;
};
template
class GurobiFactory : public SolverFactory
{
GurobiFactory() {}
public:
virtual std::unique_ptr>
newSolver( VerbosityLevel verbosity ) const
{
auto gurobi =
std::unique_ptr>( new GurobiInterface() );
return std::move( gurobi );
}
virtual void
add_parameters( ParameterSet& parameter ) const
{
}
static std::unique_ptr>
create()
{
return std::unique_ptr>( new GurobiFactory() );
}
};
} // namespace papilo
#endif