/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* */
/* 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_IO_MPS_WRITER_
#define _PAPILO_IO_MPS_WRITER_
#include "papilo/Config.hpp"
#include "papilo/core/Problem.hpp"
#include "papilo/misc/Vec.hpp"
#include "papilo/misc/fmt.hpp"
#include
#include
#include
#include
#include
#ifdef PAPILO_USE_BOOST_IOSTREAMS_WITH_BZIP2
#include
#endif
#ifdef PAPILO_USE_BOOST_IOSTREAMS_WITH_ZLIB
#include
#endif
namespace papilo
{
/// Writer to write problem structures into an mps file
template
struct MpsWriter
{
static void
writeProb( const std::string& filename, const Problem& prob,
const Vec& row_mapping, const Vec& col_mapping )
{
const ConstraintMatrix& consmatrix = prob.getConstraintMatrix();
const Vec& consnames = prob.getConstraintNames();
const Vec& varnames = prob.getVariableNames();
const Vec& lhs = consmatrix.getLeftHandSides();
const Vec& rhs = consmatrix.getRightHandSides();
const Objective& obj = prob.getObjective();
const Vec& col_flags = prob.getColFlags();
const Vec& row_flags = prob.getRowFlags();
std::ofstream file( filename, std::ofstream::out );
boost::iostreams::filtering_ostream out;
#ifdef PAPILO_USE_BOOST_IOSTREAMS_WITH_ZLIB
if( boost::algorithm::ends_with( filename, ".gz" ) )
out.push( boost::iostreams::gzip_compressor() );
#endif
#ifdef PAPILO_USE_BOOST_IOSTREAMS_WITH_BZIP2
if( boost::algorithm::ends_with( filename, ".bz2" ) )
out.push( boost::iostreams::bzip2_compressor() );
#endif
out.push( file );
fmt::print( out, "*ROWS: {}\n", consmatrix.getNRows() );
fmt::print( out, "*COLUMNS: {}\n", consmatrix.getNCols() );
fmt::print( out, "*INTEGER: {}\n", prob.getNumIntegralCols() );
fmt::print( out, "*NONZERO: {}\n*\n*\n", consmatrix.getNnz() );
fmt::print( out, "NAME {}\n", prob.getName() );
fmt::print( out, "ROWS\n" );
fmt::print( out, " N OBJ\n" );
bool hasRangedRow = false;
for( int i = 0; i < consmatrix.getNRows(); ++i )
{
assert( !consmatrix.isRowRedundant( i ) );
char type;
if( row_flags[i].test( RowFlag::kLhsInf ) &&
row_flags[i].test( RowFlag::kRhsInf ) )
type = 'N';
else if( row_flags[i].test( RowFlag::kRhsInf ) )
type = 'G';
else if( row_flags[i].test( RowFlag::kLhsInf ) )
type = 'L';
else
{
if( !row_flags[i].test( RowFlag::kEquation ) )
hasRangedRow = true;
type = 'E';
}
fmt::print( out, " {} {}\n", type, consnames[row_mapping[i]] );
}
fmt::print( out, "COLUMNS\n" );
int hasintegral = prob.getNumIntegralCols() != 0;
for( int integral = 0; integral <= hasintegral; ++integral )
{
if( integral )
fmt::print( out,
" MARK0000 'MARKER' 'INTORG'\n" );
for( int i = 0; i < consmatrix.getNCols(); ++i )
{
if( col_flags[i].test( ColFlag::kInactive ) )
continue;
if( ( !col_flags[i].test( ColFlag::kIntegral ) && integral ) ||
( col_flags[i].test( ColFlag::kIntegral ) && !integral ) )
continue;
assert(
!col_flags[i].test( ColFlag::kFixed, ColFlag::kSubstituted ) );
if( obj.coefficients[i] != 0.0 )
{
fmt::print( out, " {: <9} OBJ {:.15}\n",
varnames[col_mapping[i]],
double( obj.coefficients[i] ) );
}
SparseVectorView column =
consmatrix.getColumnCoefficients( i );
const int* rowinds = column.getIndices();
const REAL* colvals = column.getValues();
int len = column.getLength();
for( int j = 0; j < len; ++j )
{
int r = rowinds[j];
// discard redundant rows when writing problem
if( consmatrix.isRowRedundant( r ) )
continue;
// normal row
fmt::print( out, " {: <9} {: <9} {:.15}\n",
varnames[col_mapping[i]], consnames[row_mapping[r]],
double( colvals[j] ) );
}
}
if( integral )
fmt::print( out,
" MARK0000 'MARKER' 'INTEND'\n" );
}
const Vec& lower_bounds = prob.getLowerBounds();
const Vec& upper_bounds = prob.getUpperBounds();
fmt::print( out, "RHS\n" );
if( obj.offset != 0 )
{
if( obj.offset != REAL{ 0.0 } )
fmt::print( out, " B {: <9} {:.15}\n", "OBJ",
double( REAL( -obj.offset ) ) );
}
for( int i = 0; i < consmatrix.getNRows(); ++i )
{
// discard redundant rows when writing problem
if( consmatrix.isRowRedundant( i ) )
continue;
if( row_flags[i].test( RowFlag::kLhsInf ) &&
row_flags[i].test( RowFlag::kRhsInf ) )
continue;
if( row_flags[i].test( RowFlag::kLhsInf ) )
{
if( rhs[i] != REAL{ 0.0 } )
fmt::print( out, " B {: <9} {:.15}\n",
consnames[row_mapping[i]], double( rhs[i] ) );
}
else
{
if( lhs[i] != REAL{ 0.0 } )
fmt::print( out, " B {: <9} {:.15}\n",
consnames[row_mapping[i]], double( lhs[i] ) );
}
}
if( hasRangedRow )
{
fmt::print( out, "RANGES\n" );
for( int i = 0; i < consmatrix.getNRows(); ++i )
{
if( row_flags[i].test( RowFlag::kLhsInf, RowFlag::kRhsInf,
RowFlag::kEquation, RowFlag::kRedundant ) )
continue;
double rangeval = double( REAL( rhs[i] - lhs[i] ) );
if( rangeval != 0 )
{
fmt::print( out, " B {: <9} {:.15}\n",
consnames[row_mapping[i]], rangeval );
}
}
}
fmt::print( out, "BOUNDS\n" );
for( int i = 0; i < consmatrix.getNCols(); ++i )
{
if( col_flags[i].test( ColFlag::kInactive ) )
continue;
if( !col_flags[i].test( ColFlag::kLbInf ) &&
!col_flags[i].test( ColFlag::kUbInf ) &&
lower_bounds[i] == upper_bounds[i] )
{
fmt::print( out, " FX BND {: <9} {:.15}\n",
varnames[col_mapping[i]], double( lower_bounds[i] ) );
}
else
{
if( col_flags[i].test( ColFlag::kLbInf ) || lower_bounds[i] != 0.0 )
{
if( col_flags[i].test( ColFlag::kLbInf ) )
fmt::print( out, " MI BND {}\n",
varnames[col_mapping[i]] );
else
fmt::print( out, " LO BND {: <9} {:.15}\n",
varnames[col_mapping[i]],
double( lower_bounds[i] ) );
}
if( !col_flags[i].test( ColFlag::kUbInf ) )
fmt::print( out, " UP BND {: <9} {:.15}\n",
varnames[col_mapping[i]],
double( upper_bounds[i] ) );
else
fmt::print( out, " PL BND {: <9}\n",
varnames[col_mapping[i]] );
}
}
fmt::print( out, "ENDATA\n" );
}
};
} // namespace papilo
#endif