/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* */
/* 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_PRESOLVERS_IMPL_INT_DETECTION_HPP_
#define _PAPILO_PRESOLVERS_IMPL_INT_DETECTION_HPP_
#include "papilo/core/PresolveMethod.hpp"
#include "papilo/core/Problem.hpp"
#include "papilo/core/ProblemUpdate.hpp"
#include "papilo/misc/Num.hpp"
#include "papilo/misc/fmt.hpp"
namespace papilo
{
template
class ImplIntDetection : public PresolveMethod
{
public:
ImplIntDetection() : PresolveMethod()
{
this->setName( "implint" );
this->setTiming( PresolverTiming::kExhaustive );
this->setType( PresolverType::kMixedCols );
}
PresolveStatus
execute( const Problem& problem,
const ProblemUpdate& problemUpdate, const Num& num,
Reductions& reductions, const Timer& timer) override;
private:
PresolveStatus
perform_implied_integer_task(
const ProblemUpdate& problemUpdate, const Num& num,
Reductions& reductions, const Vec& cflags,
const ConstraintMatrix& consmatrix, const Vec& lhs_values,
const Vec& rhs_values, const Vec& lower_bounds,
const Vec& upper_bounds, const Vec& rflags,
int col ) const;
};
#ifdef PAPILO_USE_EXTERN_TEMPLATES
extern template class ImplIntDetection;
extern template class ImplIntDetection;
extern template class ImplIntDetection;
#endif
template
PresolveStatus
ImplIntDetection::execute( const Problem& problem,
const ProblemUpdate& problemUpdate,
const Num& num,
Reductions& reductions, const Timer& timer )
{
const auto& domains = problem.getVariableDomains();
const auto& lower_bounds = domains.lower_bounds;
const auto& upper_bounds = domains.upper_bounds;
const auto& cflags = domains.flags;
const auto& consmatrix = problem.getConstraintMatrix();
const auto& lhs_values = consmatrix.getLeftHandSides();
const auto& rhs_values = consmatrix.getRightHandSides();
const auto& rflags = consmatrix.getRowFlags();
const auto& ncols = consmatrix.getNCols();
PresolveStatus result = PresolveStatus::kUnchanged;
#ifndef PAPILO_TBB
assert( problemUpdate.getPresolveOptions().runs_sequential() );
#endif
if( problemUpdate.getPresolveOptions().runs_sequential() ||
!problemUpdate.getPresolveOptions().implied_integer_parallel )
{
for( int col = 0; col < ncols; ++col )
{
if( perform_implied_integer_task(
problemUpdate, num, reductions, cflags, consmatrix, lhs_values,
rhs_values, lower_bounds, upper_bounds, rflags,
col ) == PresolveStatus::kReduced )
result = PresolveStatus::kReduced;
}
}
#ifdef PAPILO_TBB
else
{
Vec> stored_reductions( ncols );
tbb::parallel_for( tbb::blocked_range( 0, ncols ),
[&]( const tbb::blocked_range& r ) {
for( int col = r.begin(); col < r.end(); ++col )
{
if( perform_implied_integer_task(
problemUpdate, num,
stored_reductions[col], cflags,
consmatrix, lhs_values, rhs_values,
lower_bounds, upper_bounds, rflags,
col ) == PresolveStatus::kReduced )
result = PresolveStatus::kReduced;
}
} );
if( result == PresolveStatus::kUnchanged )
return PresolveStatus::kUnchanged;
for( int i = 0; i < (int) stored_reductions.size(); ++i )
{
Reductions reds = stored_reductions[i];
if( reds.size() > 0 )
{
for( const auto& reduction : reds.getReductions() )
{
reductions.add_reduction( reduction.row, reduction.col,
reduction.newval );
}
}
}
}
#endif
return result;
}
template
PresolveStatus
ImplIntDetection::perform_implied_integer_task(
const ProblemUpdate& problemUpdate, const Num& num,
Reductions& reductions, const Vec& cflags,
const ConstraintMatrix& consmatrix, const Vec& lhs_values,
const Vec& rhs_values, const Vec& lower_bounds,
const Vec& upper_bounds, const Vec& rflags, int col ) const
{
PresolveStatus result = PresolveStatus::kUnchanged;
if( cflags[col].test( ColFlag::kIntegral, ColFlag::kImplInt,
ColFlag::kInactive ) )
return PresolveStatus::kUnchanged;
bool testinequalities = problemUpdate.getPresolveOptions().dualreds == 2;
bool impliedint = false;
auto colvec = consmatrix.getColumnCoefficients( col );
int collen = colvec.getLength();
const int* colrows = colvec.getIndices();
const REAL* colvals = colvec.getValues();
for( int i = 0; i != collen; ++i )
{
int row = colrows[i];
if( rflags[row].test( RowFlag::kRedundant ) ||
!rflags[row].test( RowFlag::kEquation ) )
continue;
testinequalities = false;
REAL scale = 1 / colvals[i];
if( !num.isIntegral( scale * rhs_values[row] ) )
continue;
auto rowvec = consmatrix.getRowCoefficients( row );
int rowlen = rowvec.getLength();
const int* rowcols = rowvec.getIndices();
const REAL* rowvals = rowvec.getValues();
impliedint = true;
for( int j = 0; j != rowlen; ++j )
{
int rowcol = rowcols[j];
if( rowcol == col )
continue;
if( !cflags[rowcol].test( ColFlag::kIntegral, ColFlag::kImplInt ) ||
!num.isIntegral( scale * rowvals[j] ) )
{
impliedint = false;
break;
}
}
if( impliedint )
break;
}
if( impliedint )
{
// add reduction
reductions.impliedInteger( col );
return PresolveStatus::kReduced;
}
if( !testinequalities )
return PresolveStatus::kUnchanged;
if( !cflags[col].test( ColFlag::kLbInf ) &&
!num.isIntegral( lower_bounds[col] ) )
return PresolveStatus::kUnchanged;
if( !cflags[col].test( ColFlag::kUbInf ) &&
!num.isIntegral( upper_bounds[col] ) )
return PresolveStatus::kUnchanged;
impliedint = true;
for( int i = 0; i != collen; ++i )
{
int row = colrows[i];
if( rflags[row].test( RowFlag::kRedundant ) )
continue;
REAL scale = 1 / colvals[i];
if( !rflags[row].test( RowFlag::kRhsInf ) &&
!num.isIntegral( scale * rhs_values[row] ) )
{
impliedint = false;
break;
}
if( !rflags[row].test( RowFlag::kLhsInf ) &&
!num.isIntegral( scale * lhs_values[row] ) )
{
impliedint = false;
break;
}
auto rowvec = consmatrix.getRowCoefficients( row );
int rowlen = rowvec.getLength();
const int* rowcols = rowvec.getIndices();
const REAL* rowvals = rowvec.getValues();
for( int j = 0; j != rowlen; ++j )
{
int rowcol = rowcols[j];
if( rowcol == col )
continue;
if( !cflags[rowcol].test( ColFlag::kIntegral, ColFlag::kImplInt ) ||
!num.isIntegral( scale * rowvals[j] ) )
{
impliedint = false;
break;
}
}
if( !impliedint )
break;
}
if( impliedint )
{
// add reduction
reductions.impliedInteger( col );
result = PresolveStatus::kReduced;
}
return result;
}
} // namespace papilo
#endif