/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* */
/* 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_SOPLEX_INTERFACE_HPP_
#define _PAPILO_INTERFACES_SOPLEX_INTERFACE_HPP_
#include "papilo/misc/String.hpp"
#include "papilo/misc/Vec.hpp"
#include
#include
#include
#include "papilo/core/Problem.hpp"
#include "papilo/interfaces/SolverInterface.hpp"
#include "soplex.h"
namespace papilo
{
template
class SoplexInterface : public SolverInterface
{
private:
soplex::SoPlex spx;
public:
void
readSettings( const String& file ) override
{
if( !spx.loadSettingsFile( file.c_str() ) )
this->status = SolverStatus::kError;
}
soplex::SoPlex&
getSoPlex()
{
return spx;
}
void
setTimeLimit( double tlim ) override
{
using namespace soplex;
spx.setIntParam( SoPlex::TIMER, SoPlex::TIMER_WALLCLOCK );
spx.setRealParam( SoPlex::TIMELIMIT, Real( tlim ) );
}
void
setUp( const Problem& problem, const Vec& row_maps,
const Vec& col_maps ) override
{
using namespace soplex;
int ncols = problem.getNCols();
int nrows = problem.getNRows();
const VariableDomains& domains = problem.getVariableDomains();
const Objective& obj = problem.getObjective();
const auto& consMatrix = problem.getConstraintMatrix();
const auto& lhs_values = consMatrix.getLeftHandSides();
const auto& rhs_values = consMatrix.getRightHandSides();
const auto& rflags = problem.getRowFlags();
/* set the objective sense and offset */
spx.setIntParam( SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE );
if( obj.offset != 0 )
spx.setRealParam( SoPlex::OBJ_OFFSET, Real( obj.offset ) );
LPRowSet rows( nrows );
LPColSet cols( ncols );
DSVector vec( ncols );
for( int i = 0; i < nrows; ++i )
{
Real lhs = rflags[i].test( RowFlag::kLhsInf ) ? -infinity
: Real( lhs_values[i] );
Real rhs = rflags[i].test( RowFlag::kRhsInf ) ? infinity
: Real( rhs_values[i] );
rows.add( lhs, vec, rhs );
}
spx.addRowsReal( rows );
for( int i = 0; i < ncols; ++i )
{
assert( !domains.flags[i].test( ColFlag::kInactive ) );
Real lb = domains.flags[i].test( ColFlag::kLbInf )
? -infinity
: Real( domains.lower_bounds[i] );
Real ub = domains.flags[i].test( ColFlag::kUbInf )
? infinity
: Real( domains.upper_bounds[i] );
auto colvec = consMatrix.getColumnCoefficients( i );
int collen = colvec.getLength();
const int* colrows = colvec.getIndices();
const REAL* colvals = colvec.getValues();
vec.clear();
if( std::is_same::value )
{
vec.add( collen, colrows, (const Real*)colvals );
}
else
{
for( int j = 0; j != collen; ++j )
vec.add( colrows[j], Real( colvals[j] ) );
}
cols.add( Real( obj.coefficients[i] ), lb, vec, ub );
}
spx.addColsReal( cols );
}
void
setUp( const Problem& problem, const Vec& row_maps,
const Vec& col_maps, const Components& components,
const ComponentInfo& component ) override
{
using namespace soplex;
const VariableDomains& domains = problem.getVariableDomains();
const Objective& obj = problem.getObjective();
const auto& consMatrix = problem.getConstraintMatrix();
const auto& lhs_values = consMatrix.getLeftHandSides();
const auto& rhs_values = consMatrix.getRightHandSides();
const auto& rflags = problem.getRowFlags();
const int* rowset = components.getComponentsRows( component.componentid );
const int* colset = components.getComponentsCols( component.componentid );
int numrows = components.getComponentsNumRows( component.componentid );
int numcols = components.getComponentsNumCols( component.componentid );
/* set the objective sense and offset */
spx.setIntParam( SoPlex::OBJSENSE, SoPlex::OBJSENSE_MINIMIZE );
LPRowSet rows( numrows );
LPColSet cols( numcols );
DSVector vec( numcols );
for( int i = 0; i != numrows; ++i )
{
int row = rowset[i];
assert( components.getRowComponentIdx( row ) == i );
Real lhs = rflags[row].test( RowFlag::kLhsInf )
? -infinity
: Real( lhs_values[row] );
Real rhs = rflags[row].test( RowFlag::kRhsInf )
? infinity
: Real( rhs_values[row] );
rows.add( lhs, vec, rhs );
}
spx.addRowsReal( rows );
for( int i = 0; i != numcols; ++i )
{
int col = colset[i];
assert( components.getColComponentIdx( col ) == i );
assert( !domains.flags[col].test( ColFlag::kInactive ) );
Real lb = domains.flags[col].test( ColFlag::kLbInf )
? -infinity
: Real( domains.lower_bounds[col] );
Real ub = domains.flags[col].test( ColFlag::kUbInf )
? infinity
: Real( domains.upper_bounds[col] );
auto colvec = consMatrix.getColumnCoefficients( col );
int collen = colvec.getLength();
const int* colrows = colvec.getIndices();
const REAL* colvals = colvec.getValues();
vec.clear();
for( int j = 0; j != collen; ++j )
vec.add( components.getRowComponentIdx( colrows[j] ),
Real( colvals[j] ) );
cols.add( Real( obj.coefficients[col] ), lb, vec, ub );
}
spx.addColsReal( cols );
}
void
solve() override
{
using namespace soplex;
assert( this->status != SolverStatus::kError );
spx.setSettings( spx.settings() );
SPxSolver::Status stat = spx.optimize();
switch( stat )
{
default:
this->status = SolverStatus::kError;
return;
case SPxSolver::Status::INForUNBD:
this->status = SolverStatus::kUnbndOrInfeas;
return;
case SPxSolver::Status::INFEASIBLE:
this->status = SolverStatus::kInfeasible;
return;
case SPxSolver::Status::UNBOUNDED:
this->status = SolverStatus::kUnbounded;
return;
case SPxSolver::Status::ABORT_CYCLING:
this->status = SolverStatus::kInterrupted;
return;
case SPxSolver::Status::OPTIMAL_UNSCALED_VIOLATIONS:
case SPxSolver::Status::OPTIMAL:
this->status = SolverStatus::kOptimal;
}
}
void
setVerbosity( VerbosityLevel verbosity ) override
{
using namespace soplex;
switch( verbosity )
{
case VerbosityLevel::kQuiet:
case VerbosityLevel::kError:
spx.setIntParam( SoPlex::VERBOSITY, SoPlex::VERBOSITY_ERROR );
break;
case VerbosityLevel::kWarning:
spx.setIntParam( SoPlex::VERBOSITY, SoPlex::VERBOSITY_WARNING );
break;
case VerbosityLevel::kInfo:
spx.setIntParam( SoPlex::VERBOSITY, SoPlex::VERBOSITY_NORMAL );
break;
case VerbosityLevel::kDetailed:
spx.setIntParam( SoPlex::VERBOSITY, SoPlex::VERBOSITY_HIGH );
}
}
REAL
getDualBound() override
{
if( spx.hasPrimal() )
return spx.objValueReal();
else
return -soplex::infinity;
}
bool
getSolution( Solution& sol ) override
{
Vec buffer;
int numcols = spx.numColsReal();
buffer.resize( numcols );
if( !spx.getPrimalReal( buffer.data(), numcols ) )
return false;
sol.primal.resize( numcols );
for( int i = 0; i != numcols; ++i )
sol.primal[i] = REAL( buffer[i] );
if( sol.type == SolutionType::kPrimal )
return true;
if( !spx.getRedCostReal( buffer.data(), numcols ) )
return false;
sol.reducedCosts.resize( numcols );
for( int i = 0; i != numcols; ++i )
sol.reducedCosts[i] = REAL( buffer[i] );
int numrows = spx.numRowsReal();
buffer.resize( numrows );
if( !spx.getDualReal( buffer.data(), numrows ) )
return false;
sol.dual.resize( numrows );
for( int i = 0; i != numrows; ++i )
sol.dual[i] = REAL( buffer[i] );
sol.basisAvailabe = true;
sol.varBasisStatus.resize( numcols, VarBasisStatus::UNDEFINED );
for( int i = 0; i < numcols; ++i )
switch( spx.basisColStatus( i ) )
{
case soplex::SPxSolverBase::VarStatus::BASIC:
sol.varBasisStatus[i] = VarBasisStatus::BASIC;
break;
case soplex::SPxSolverBase::VarStatus::ON_LOWER:
sol.varBasisStatus[i] = VarBasisStatus::ON_LOWER;
break;
case soplex::SPxSolverBase::VarStatus::ON_UPPER:
sol.varBasisStatus[i] = VarBasisStatus::ON_UPPER;
break;
case soplex::SPxSolverBase::VarStatus::FIXED:
sol.varBasisStatus[i] = VarBasisStatus::FIXED;
break;
case soplex::SPxSolverBase::VarStatus::ZERO:
sol.varBasisStatus[i] = VarBasisStatus::ZERO;
break;
case soplex::SPxSolverBase::VarStatus::UNDEFINED:
sol.varBasisStatus[i] = VarBasisStatus::UNDEFINED;
break;
}
sol.rowBasisStatus.resize( numrows, VarBasisStatus::UNDEFINED );for( int i = 0; i < numrows; ++i )
switch( spx.basisRowStatus( i ) )
{
case soplex::SPxSolverBase::VarStatus::BASIC:
sol.rowBasisStatus[i] = VarBasisStatus::BASIC;
break;
case soplex::SPxSolverBase::VarStatus::ON_LOWER:
sol.rowBasisStatus[i] = VarBasisStatus::ON_LOWER;
break;
case soplex::SPxSolverBase::VarStatus::ON_UPPER:
sol.rowBasisStatus[i] = VarBasisStatus::ON_UPPER;
break;
case soplex::SPxSolverBase::VarStatus::FIXED:
sol.rowBasisStatus[i] = VarBasisStatus::FIXED;
break;
case soplex::SPxSolverBase::VarStatus::ZERO:
sol.rowBasisStatus[i] = VarBasisStatus::ZERO;
break;
case soplex::SPxSolverBase::VarStatus::UNDEFINED:
sol.rowBasisStatus[i] = VarBasisStatus::UNDEFINED;
break;
}
return true;
}
bool
getSolution( const Components& components, int component,
Solution& sol ) override
{
Vec buffer;
int numcols = spx.numColsReal();
assert( components.getComponentsNumCols( component ) ==
spx.numColsReal() );
buffer.resize( numcols );
if( !spx.getPrimalReal( buffer.data(), numcols ) )
return false;
const int* compcols = components.getComponentsCols( component );
for( int i = 0; i != numcols; ++i )
sol.primal[compcols[i]] = REAL( buffer[i] );
if( sol.type == SolutionType::kPrimal )
return true;
if( !spx.getRedCostReal( buffer.data(), numcols ) )
return false;
for( int i = 0; i != numcols; ++i )
sol.reducedCosts[compcols[i]] = REAL( buffer[i] );
int numrows = spx.numRowsReal();
buffer.resize( numrows );
const int* comprows = components.getComponentsRows( component );
if( !spx.getDualReal( buffer.data(), numrows ) )
return false;
for( int i = 0; i != numrows; ++i )
sol.dual[comprows[i]] = REAL( buffer[i] );
for( int i = 0; i < numcols; ++i )
switch( spx.basisColStatus( i ) )
{
case soplex::SPxSolverBase::VarStatus::BASIC:
sol.varBasisStatus[comprows[i]] = VarBasisStatus::BASIC;
break;
case soplex::SPxSolverBase::VarStatus::ON_LOWER:
sol.varBasisStatus[comprows[i]] = VarBasisStatus::ON_LOWER;
break;
case soplex::SPxSolverBase::VarStatus::ON_UPPER:
sol.varBasisStatus[comprows[i]] = VarBasisStatus::ON_UPPER;
break;
case soplex::SPxSolverBase::VarStatus::FIXED:
sol.varBasisStatus[comprows[i]] = VarBasisStatus::FIXED;
break;
case soplex::SPxSolverBase::VarStatus::ZERO:
sol.varBasisStatus[comprows[i]] = VarBasisStatus::ZERO;
break;
case soplex::SPxSolverBase::VarStatus::UNDEFINED:
sol.varBasisStatus[comprows[i]] = VarBasisStatus::UNDEFINED;
break;
}
for( int i = 0; i < numrows; ++i )
switch( spx.basisRowStatus( i ) )
{
case soplex::SPxSolverBase::VarStatus::BASIC:
sol.rowBasisStatus[comprows[i]] = VarBasisStatus::BASIC;
break;
case soplex::SPxSolverBase::VarStatus::ON_LOWER:
sol.rowBasisStatus[comprows[i]] = VarBasisStatus::ON_LOWER;
break;
case soplex::SPxSolverBase::VarStatus::ON_UPPER:
sol.rowBasisStatus[comprows[i]] = VarBasisStatus::ON_UPPER;
break;
case soplex::SPxSolverBase::VarStatus::FIXED:
sol.rowBasisStatus[comprows[i]] = VarBasisStatus::FIXED;
break;
case soplex::SPxSolverBase::VarStatus::ZERO:
sol.rowBasisStatus[comprows[i]] = VarBasisStatus::ZERO;
break;
case soplex::SPxSolverBase::VarStatus::UNDEFINED:
sol.rowBasisStatus[comprows[i]] = VarBasisStatus::UNDEFINED;
break;
}
return true;
}
void
addParameters( ParameterSet& paramSet ) override
{
using namespace soplex;
SoPlex::Settings& settings =
const_cast( spx.settings() );
for( int i = 0; i != SoPlex::BOOLPARAM_COUNT; ++i )
{
paramSet.addParameter( settings.boolParam.name[i].c_str(),
settings.boolParam.description[i].c_str(),
settings._boolParamValues[i] );
}
for( int i = 0; i != SoPlex::INTPARAM_COUNT; ++i )
{
paramSet.addParameter( settings.intParam.name[i].c_str(),
settings.intParam.description[i].c_str(),
settings._intParamValues[i],
settings.intParam.lower[i],
settings.intParam.upper[i] );
}
for( int i = 0; i != SoPlex::REALPARAM_COUNT; ++i )
{
paramSet.addParameter( settings.realParam.name[i].c_str(),
settings.realParam.description[i].c_str(),
settings._realParamValues[i],
settings.realParam.lower[i],
settings.realParam.upper[i] );
}
}
SolverType
getType() override
{
return SolverType::LP;
}
String
getName() override
{
return "SoPlex";
}
bool
is_dual_solution_available() override
{
return true;
}
void
printDetails() override
{
spx.printStatistics( std::cout );
}
};
template
class SoplexFactory : public SolverFactory
{
void ( *soplexsetup )( soplex::SoPlex& soplex, void* usrdata );
void* soplexsetup_usrdata;
SoplexFactory( void ( *soplexsetup_ )( soplex::SoPlex& soplex,
void* usrdata ),
void* soplexsetup_usrdata_ )
: soplexsetup( soplexsetup_ ), soplexsetup_usrdata( soplexsetup_usrdata_ )
{
}
public:
std::unique_ptr>
newSolver( VerbosityLevel verbosity ) const
{
auto soplex =
std::unique_ptr>( new SoplexInterface() );
// set verbosity already before the setup function call
soplex->setVerbosity( verbosity );
if( soplexsetup != nullptr )
soplexsetup(
static_cast*>( soplex.get() )->getSoPlex(),
soplexsetup_usrdata );
// set verbosity again in case the setup function altered it
soplex->setVerbosity( verbosity );
return std::move( soplex );
}
virtual void
add_parameters( ParameterSet& parameter ) const
{
}
static std::unique_ptr>
create( void ( *soplexsetup )( soplex::SoPlex& soplex,
void* usrdata ) = nullptr,
void* soplexsetup_usrdata = nullptr )
{
return std::unique_ptr>(
new SoplexFactory( soplexsetup, soplexsetup_usrdata ) );
}
};
} // namespace papilo
#endif