/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* */
/* 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_HIGHS_INTERFACE_HPP_
#define _PAPILO_INTERFACES_HIGHS_INTERFACE_HPP_
#include "papilo/misc/String.hpp"
#include "papilo/misc/Vec.hpp"
#include
#include
#include
#include "Highs.h"
#include "papilo/core/Problem.hpp"
#include "papilo/interfaces/SolverInterface.hpp"
namespace papilo
{
template
class HighsInterface : public SolverInterface
{
private:
Highs solver;
HighsOptions opts;
static constexpr double inf = std::numeric_limits::infinity();
public:
HighsInterface() {}
void
setTimeLimit( double tlim ) override
{
opts.time_limit = tlim;
}
void
setUp( const Problem& problem, const Vec& row_maps,
const Vec& col_maps ) override
{
int ncols = problem.getNCols();
int nrows = problem.getNRows();
const Vec& varNames = problem.getVariableNames();
const Vec& consNames = problem.getConstraintNames();
const VariableDomains& domains = problem.getVariableDomains();
const Objective& obj = problem.getObjective();
const ConstraintMatrix& consMatrix = problem.getConstraintMatrix();
const auto& lhs_values = consMatrix.getLeftHandSides();
const auto& rhs_values = consMatrix.getRightHandSides();
const auto& rflags = problem.getRowFlags();
HighsLp model;
model.sense_ = ObjSense::kMinimize;
model.offset_ = double( obj.offset );
model.num_row_ = nrows;
model.num_col_ = ncols;
model.col_cost_.resize( ncols );
model.col_lower_.resize( ncols );
model.col_upper_.resize( ncols );
model.integrality_.resize( ncols );
model.row_lower_.resize( nrows );
model.row_upper_.resize( nrows );
for( int i = 0; i != nrows; ++i )
{
model.row_lower_[i] = rflags[i].test( RowFlag::kLhsInf )
? -inf
: double( lhs_values[i] );
model.row_upper_[i] =
rflags[i].test( RowFlag::kRhsInf ) ? inf : double( rhs_values[i] );
}
model.a_index_.resize( consMatrix.getNnz() );
model.a_value_.resize( consMatrix.getNnz() );
model.a_start_.resize( ncols + 1 );
model.a_start_[ncols] = consMatrix.getNnz();
int start = 0;
for( int i = 0; i < ncols; ++i )
{
assert( !domains.flags[i].test( ColFlag::kInactive ) );
model.col_lower_[i] = domains.flags[i].test( ColFlag::kLbInf )
? -inf
: double( domains.lower_bounds[i] );
model.col_upper_[i] = domains.flags[i].test( ColFlag::kUbInf )
? inf
: double( domains.upper_bounds[i] );
model.col_cost_[i] = double( obj.coefficients[i] );
model.integrality_[i] =
domains.flags[i].test( ColFlag::kImplInt )
? HighsVarType::kImplicitInteger
: ( domains.flags[i].test( ColFlag::kIntegral )
? HighsVarType::kInteger
: HighsVarType::kContinuous );
auto colvec = consMatrix.getColumnCoefficients( i );
int collen = colvec.getLength();
const int* colrows = colvec.getIndices();
const REAL* colvals = colvec.getValues();
model.a_start_[i] = start;
for( int k = 0; k != collen; ++k )
{
model.a_value_[start + k] = double( colvals[k] );
model.a_index_[start + k] = colrows[k];
}
start += collen;
}
solver.passModel( std::move( model ) );
}
void
setUp( const Problem& problem, const Vec& row_maps,
const Vec& col_maps, const Components& components,
const ComponentInfo& component ) override
{
const Vec& varNames = problem.getVariableNames();
const Vec& consNames = problem.getConstraintNames();
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 );
HighsLp model;
/* set the objective sense and offset */
model.sense_ = ObjSense::kMinimize;
model.offset_ = 0;
model.num_row_ = numrows;
model.num_col_ = numcols;
model.col_cost_.resize( numcols );
model.col_lower_.resize( numcols );
model.col_upper_.resize( numcols );
model.integrality_.resize( numcols );
model.row_lower_.resize( numrows );
model.row_upper_.resize( numrows );
for( int i = 0; i != numrows; ++i )
{
int row = rowset[i];
assert( components.getRowComponentIdx( row ) == i );
model.row_lower_[i] = rflags[row].test( RowFlag::kLhsInf )
? -inf
: double( lhs_values[row] );
model.row_upper_[i] = rflags[row].test( RowFlag::kRhsInf )
? inf
: double( rhs_values[row] );
}
model.a_index_.resize( component.nnonz );
model.a_value_.resize( component.nnonz );
model.a_start_.resize( numcols + 1 );
model.a_start_[numcols] = component.nnonz;
int start = 0;
for( int i = 0; i != numcols; ++i )
{
int col = colset[i];
assert( components.getColComponentIdx( col ) == i );
assert( !domains.flags[col].test( ColFlag::kInactive ) );
model.col_lower_[i] = domains.flags[col].test( ColFlag::kLbInf )
? -inf
: double( domains.lower_bounds[col] );
model.col_upper_[i] = domains.flags[col].test( ColFlag::kUbInf )
? inf
: double( domains.upper_bounds[col] );
model.col_cost_[i] = double( obj.coefficients[col] );
model.integrality_[i] =
domains.flags[col].test( ColFlag::kImplInt )
? HighsVarType::kImplicitInteger
: ( domains.flags[col].test( ColFlag::kIntegral )
? HighsVarType::kInteger
: HighsVarType::kContinuous );
auto colvec = consMatrix.getColumnCoefficients( col );
int collen = colvec.getLength();
const int* colrows = colvec.getIndices();
const REAL* colvals = colvec.getValues();
model.a_start_[i] = start;
for( int k = 0; k != collen; ++k )
{
model.a_value_[start + k] = double( colvals[k] );
model.a_index_[start + k] =
components.getRowComponentIdx( colrows[k] );
}
start += collen;
}
solver.passModel( std::move( model ) );
}
void
solve() override
{
solver.passHighsOptions( opts );
if( solver.run() == HighsStatus::kError )
{
this->status = SolverStatus::kError;
return;
}
HighsModelStatus stat = solver.getModelStatus();
switch( stat )
{
default:
this->status = SolverStatus::kError;
return;
case HighsModelStatus::kInfeasible:
this->status = SolverStatus::kInfeasible;
return;
case HighsModelStatus::kUnbounded:
this->status = SolverStatus::kUnbounded;
return;
case HighsModelStatus::kTimeLimit:
case HighsModelStatus::kIterationLimit:
this->status = SolverStatus::kInterrupted;
return;
case HighsModelStatus::kOptimal:
this->status = SolverStatus::kOptimal;
}
}
void
setVerbosity( VerbosityLevel verbosity ) override
{
switch( verbosity )
{
case VerbosityLevel::kQuiet:
opts.output_flag = false;
solver.setHighsOptionValue( "output_flag", false );
break;
case VerbosityLevel::kError:
case VerbosityLevel::kWarning:
case VerbosityLevel::kInfo:
case VerbosityLevel::kDetailed:
opts.output_flag = true;
solver.setHighsOptionValue( "output_flag", true );
}
}
REAL
getDualBound() override
{
// TODO:
// if( this->status == SolverStatus::kOptimal )
// return -inf;
return solver.getHighsInfo().mip_dual_bound;
}
bool
getSolution( Solution& sol ) override
{
const HighsSolution& highsSol = solver.getSolution();
int numcols = solver.getNumCols();
int numrows = solver.getNumRows();
if( highsSol.col_value.size() != numcols )
return false;
// get primal values
sol.primal.resize( numcols );
for( int i = 0; i < numcols; ++i )
sol.primal[i] = highsSol.col_value[i];
// return if no dual requested
if( sol.type == SolutionType::kPrimal )
return true;
if( highsSol.col_dual.size() != numcols ||
highsSol.row_dual.size() != numrows )
{
sol.type = SolutionType::kPrimal;
return true;
}
// get reduced costs
sol.reducedCosts.resize( numcols );
for( int i = 0; i < numcols; ++i )
sol.reducedCosts[i] = REAL( highsSol.col_dual[i] );
// get row duals
sol.dual.resize( numrows );
for( int i = 0; i < numrows; ++i )
sol.dual[i] = - REAL( highsSol.row_dual[i] );
sol.basisAvailabe = false;
sol.varBasisStatus.resize( numcols, VarBasisStatus::UNDEFINED );
sol.rowBasisStatus.resize( numrows, VarBasisStatus::UNDEFINED );
return true;
}
bool
getSolution( const Components& components, int component,
Solution& sol ) override
{
if( this->status != SolverStatus::kOptimal )
return false;
int numcols = solver.getNumCols();
int numrows = solver.getNumRows();
const HighsSolution& highsSol = solver.getSolution();
if( highsSol.col_value.size() != numcols )
return false;
assert( components.getComponentsNumCols( component ) == numcols );
const int* compcols = components.getComponentsCols( component );
for( int i = 0; i != numcols; ++i )
sol.primal[compcols[i]] = REAL( highsSol.col_value[i] );
if( sol.type == SolutionType::kPrimal )
return true;
if( highsSol.col_dual.size() != numcols ||
highsSol.row_dual.size() != numrows )
{
sol.type = SolutionType::kPrimal;
return true;
}
for( int i = 0; i != numcols; ++i )
sol.reducedCosts[compcols[i]] = REAL( highsSol.col_dual[i] );
const int* comprows = components.getComponentsRows( component );
for( int i = 0; i != numrows; ++i )
sol.dual[comprows[i]] = REAL( highsSol.row_dual[i] );
return true;
}
void
addParameters( ParameterSet& paramSet ) override
{
}
bool
is_dual_solution_available() override
{
return false;
}
SolverType
getType() override
{
return SolverType::MIP;
}
String
getName() override
{
return "HiGHS";
}
void
printDetails() override
{
}
};
template
class HighsFactory : public SolverFactory
{
HighsFactory() = default;
public:
std::unique_ptr>
newSolver( VerbosityLevel verbosity ) const override
{
auto highs =
std::unique_ptr>( new HighsInterface() );
highs->setVerbosity( verbosity );
return std::move( highs );
}
virtual void
add_parameters( ParameterSet& parameter ) const
{
}
static std::unique_ptr>
create()
{
return std::unique_ptr>( new HighsFactory() );
}
};
} // namespace papilo
#endif