/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* */
/* 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_MISC_NUMERICALSTATISTICS_HPP_
#define _PAPILO_MISC_NUMERICALSTATISTICS_HPP_
#include "papilo/core/ConstraintMatrix.hpp"
#include "papilo/core/Problem.hpp"
#include "papilo/misc/fmt.hpp"
#include
namespace papilo
{
template
struct Num_stats
{
REAL matrixMin;
REAL matrixMax;
REAL objMin;
REAL objMax;
REAL boundsMin;
REAL boundsMax;
REAL rhsMin;
REAL rhsMax;
REAL dynamism;
REAL rowDynamism;
REAL colDynamism;
};
template
class NumericalStatistics
{
public:
NumericalStatistics( const Problem& p )
: stats( Num_stats() ), prob( p )
{
// Set all values in Num_stats
const ConstraintMatrix& cm = prob.getConstraintMatrix();
const VariableDomains& vd = prob.getVariableDomains();
const Vec& rf = cm.getRowFlags();
const Vec& lhs = cm.getLeftHandSides();
const Vec& rhs = cm.getRightHandSides();
int nrows = cm.getNRows();
int ncols = cm.getNCols();
stats.matrixMin = 0.0;
stats.matrixMax = 0.0;
stats.rowDynamism = 0.0;
stats.rhsMin = 0.0;
stats.rhsMax = 0.0;
bool rhsMinSet = false;
// Row dynamism, matrixMin/Max, RHS
for( int r = 0; r < nrows; ++r )
{
// matrixMin/Max
const SparseVectorView& row = cm.getRowCoefficients( r );std::pair minmax = row.getMinMaxAbsValue();
stats.matrixMax = std::max( minmax.second, stats.matrixMax );
if( r == 0 )
stats.matrixMin = stats.matrixMax;
else
stats.matrixMin = std::min( minmax.first, stats.matrixMin );
// Row dynamism
if(minmax.first != 0)
{
REAL dyn = minmax.second / minmax.first;
stats.rowDynamism = std::max( dyn, stats.rowDynamism );
}
// RHS min/max
// Handle case where RHS/LHS is inf
if( !rhsMinSet )
{
rhsMinSet = true;
if( !rf[r].test( RowFlag::kLhsInf ) &&
!rf[r].test( RowFlag::kRhsInf ) && lhs[r] != 0 && rhs[r] != 0 )
stats.rhsMin = std::min( abs( lhs[r] ), abs( rhs[r] ) );
else if( !rf[r].test( RowFlag::kLhsInf ) && lhs[r] != 0 )
stats.rhsMin = abs( lhs[r] );
else if( !rf[r].test( RowFlag::kRhsInf ) && rhs[r] != 0 )
stats.rhsMin = abs( rhs[r] );
else
rhsMinSet = false;
}
else
{
if( !rf[r].test( RowFlag::kLhsInf ) &&
!rf[r].test( RowFlag::kRhsInf ) && lhs[r] != 0 && rhs[r] != 0 )
stats.rhsMin =
std::min( stats.rhsMin,
REAL( std::min( abs( lhs[r] ), abs( rhs[r] ) ) ) );
else if( !rf[r].test( RowFlag::kLhsInf ) && lhs[r] != 0 )
stats.rhsMin = std::min( stats.rhsMin, REAL( abs( lhs[r] ) ) );
else if( !rf[r].test( RowFlag::kRhsInf ) && rhs[r] != 0 )
stats.rhsMin = std::min( stats.rhsMin, REAL( abs( rhs[r] ) ) );
}
if( !rf[r].test( RowFlag::kLhsInf ) &&
!rf[r].test( RowFlag::kRhsInf ) )
stats.rhsMax =
std::max( stats.rhsMax,
REAL( std::max( abs( lhs[r] ), abs( rhs[r] ) ) ) );
else if( !rf[r].test( RowFlag::kLhsInf ) )
stats.rhsMax = std::max( stats.rhsMax, REAL( abs( lhs[r] ) ) );
else if( !rf[r].test( RowFlag::kRhsInf ) )
stats.rhsMax = std::max( stats.rhsMax, REAL( abs( rhs[r] ) ) );
}
stats.colDynamism = 0.0;
stats.boundsMin = 0.0;
stats.boundsMax = 0.0;
bool boundsMinSet = false;
// Column dynamism, Variable Bounds
for( int c = 0; c < ncols; ++c )
{
// Column dynamism
const SparseVectorView& col = cm.getColumnCoefficients( c );
std::pair minmax = col.getMinMaxAbsValue();
REAL dyn = minmax.first == 0 ? (REAL) 0 : minmax.second / minmax.first;
stats.colDynamism = std::max( dyn, stats.colDynamism );
// Bounds
// Handle case where first variables are unbounded
if( !boundsMinSet )
{
boundsMinSet = true;
if( !vd.flags[c].test( ColFlag::kLbInf ) &&
!vd.flags[c].test( ColFlag::kUbInf ) &&
vd.lower_bounds[c] != 0 && vd.upper_bounds[c] != 0 )
stats.boundsMin = std::min( abs( vd.lower_bounds[c] ),
abs( vd.upper_bounds[c] ) );
else if( !vd.flags[c].test( ColFlag::kLbInf ) &&
vd.lower_bounds[c] != 0 )
stats.boundsMin = abs( vd.lower_bounds[c] );
else if( !vd.flags[c].test( ColFlag::kUbInf ) &&
vd.upper_bounds[c] != 0 )
stats.boundsMin = abs( vd.upper_bounds[c] );
else
boundsMinSet = false;
}
else
{
if( !vd.flags[c].test( ColFlag::kLbInf ) &&
!vd.flags[c].test( ColFlag::kUbInf ) &&
vd.lower_bounds[c] != 0 && vd.upper_bounds[c] != 0 )
stats.boundsMin =
std::min( stats.boundsMin,
REAL( std::min( abs( vd.lower_bounds[c] ),
abs( vd.upper_bounds[c] ) ) ) );
else if( !vd.flags[c].test( ColFlag::kLbInf ) &&
vd.lower_bounds[c] != 0 )
stats.boundsMin = std::min( stats.boundsMin,
REAL( abs( vd.lower_bounds[c] ) ) );
else if( !vd.flags[c].test( ColFlag::kUbInf ) &&
vd.upper_bounds[c] != 0 )
stats.boundsMin = std::min( stats.boundsMin,
REAL( abs( vd.upper_bounds[c] ) ) );
}
if( !vd.flags[c].test( ColFlag::kLbInf ) &&
!vd.flags[c].test( ColFlag::kUbInf ) )
stats.boundsMax =
std::max( stats.boundsMax,
REAL( std::max( abs( vd.lower_bounds[c] ),
abs( vd.upper_bounds[c] ) ) ) );
else if( !vd.flags[c].test( ColFlag::kLbInf ) )
stats.boundsMax =
std::max( stats.boundsMax, REAL( abs( vd.lower_bounds[c] ) ) );
else if( !vd.flags[c].test( ColFlag::kUbInf ) )
stats.boundsMax =
std::max( stats.boundsMax, REAL( abs( vd.upper_bounds[c] ) ) );
}
if(stats.matrixMin != 0)
stats.dynamism = stats.matrixMax / stats.matrixMin;
// Objective
const Objective& obj = prob.getObjective();
stats.objMax = 0.0;
stats.objMin = 0.0;
bool objMinSet = false;
for( int i = 0; i < (int) obj.coefficients.size(); ++i )
{
if( obj.coefficients[i] != 0 )
{
stats.objMax =
std::max( stats.objMax, REAL( abs( obj.coefficients[i] ) ) );
if( !objMinSet )
{
stats.objMin = abs( obj.coefficients[i] );
objMinSet = true;
}
else
stats.objMin =
std::min( stats.objMin, REAL( abs( obj.coefficients[i] ) ) );
}
}
}
void
printStatistics()
{
fmt::print( "Numerical Statistics:\n Matrix range [{:.0e},{:.0e}]\n "
"Objective range [{:.0e},{:.0e}]\n Bounds range "
"[{:.0e},{:.0e}]\n RHS range [{:.0e},{:.0e}]\n "
"Dynamism Variables: {:.0e}\n Dynamism Rows : {:.0e}\n",
double( stats.matrixMin ), double( stats.matrixMax ),
double( stats.objMin ), double( stats.objMax ),
double( stats.boundsMin ), double( stats.boundsMax ),
double( stats.rhsMin ), double( stats.rhsMax ),
double( stats.colDynamism ), double( stats.rowDynamism ) );
}
const Num_stats&
getNum_stats()
{
return stats;
}
private:
Num_stats stats;
const Problem& prob;
};
} // namespace papilo
#endif