/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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_SIMPLE_PROBING_HPP_ #define _PAPILO_PRESOLVERS_SIMPLE_PROBING_HPP_ #include "papilo/core/PresolveMethod.hpp" #include "papilo/core/Problem.hpp" #include "papilo/core/ProblemUpdate.hpp" #include "papilo/io/Message.hpp" namespace papilo { template class SimpleProbing : public PresolveMethod { public: SimpleProbing() : PresolveMethod() { this->setName( "simpleprobing" ); this->setType( PresolverType::kIntegralCols ); this->setTiming( PresolverTiming::kMedium ); } PresolveStatus execute( const Problem& problem, const ProblemUpdate& problemUpdate, const Num& num, Reductions& reductions, const Timer& timer ) override; void calculateReductionsForSimpleProbing( const Num& num, Reductions& reductions, const VariableDomains& domains, const Vec>& activities, const REAL* rowvals, const int* rowcols, int rowlen, int bincol, REAL binary_coeff ); PresolveStatus perform_simple_probing_step( const Num& num, Reductions& reductions, const VariableDomains& domains, const Vec& cflags, const Vec>& activities, const ConstraintMatrix& constMatrix, const Vec& rhs_values, const Vec& rowsize, const Vec& rflags, int i ); }; #ifdef PAPILO_USE_EXTERN_TEMPLATES extern template class SimpleProbing; extern template class SimpleProbing; extern template class SimpleProbing; #endif template PresolveStatus SimpleProbing::execute( const Problem& problem, const ProblemUpdate& problemUpdate, const Num& num, Reductions& reductions, const Timer& timer ) { assert( problem.getNumIntegralCols() != 0 ); PresolveStatus status = PresolveStatus::kUnchanged; const auto& domains = problem.getVariableDomains(); const auto& cflags = domains.flags; const auto& activities = problem.getRowActivities(); const auto& rowsize = problem.getRowSizes(); const auto& constMatrix = problem.getConstraintMatrix(); const auto& rhs_values = constMatrix.getRightHandSides(); const auto& rflags = constMatrix.getRowFlags(); int nrows = problem.getNRows(); #ifndef PAPILO_TBB assert( problemUpdate.getPresolveOptions().runs_sequential() ); #endif if( problemUpdate.getPresolveOptions().runs_sequential() || !problemUpdate.getPresolveOptions().simple_probing_parallel ) { for( int i = 0; i < nrows; ++i ) { if( perform_simple_probing_step( num, reductions, domains, cflags, activities, constMatrix, rhs_values, rowsize, rflags, i ) == PresolveStatus::kReduced ) status = PresolveStatus::kReduced; } } #ifdef PAPILO_TBB else { Vec> stored_reductions( nrows ); tbb::parallel_for( tbb::blocked_range( 0, nrows ), [&]( const tbb::blocked_range& r ) { for( int j = r.begin(); j < r.end(); ++j ) { PresolveStatus s = perform_simple_probing_step( num, stored_reductions[j], domains, cflags, activities, constMatrix, rhs_values, rowsize, rflags, j ); assert( s == PresolveStatus::kUnchanged || s == PresolveStatus::kReduced ); if( s == PresolveStatus::kReduced ) status = s; } } ); if( status == 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& transaction : reds.getTransactions() ) { int start = transaction.start; int end = transaction.end; TransactionGuard guard{ reductions }; for( int c = start; c < end; c++ ) { Reduction& reduction = reds.getReduction( c ); reductions.add_reduction( reduction.row, reduction.col, reduction.newval ); } } } } } #endif return status; } template PresolveStatus SimpleProbing::perform_simple_probing_step( const Num& num, Reductions& reductions, const VariableDomains& domains, const Vec& cflags, const Vec>& activities, const ConstraintMatrix& constMatrix, const Vec& rhs_values, const Vec& rowsize, const Vec& rflags, int i ) { PresolveStatus status = PresolveStatus::kUnchanged; if( !rflags[i].test( RowFlag::kEquation ) || rowsize[i] <= 2 || activities[i].ninfmin != 0 || activities[i].ninfmax != 0 || !num.isEq( activities[i].min + activities[i].max, 2 * rhs_values[i] ) ) return PresolveStatus::kUnchanged; assert( rflags[i].test( RowFlag::kEquation ) ); assert( activities[i].ninfmin == 0 && activities[i].ninfmax == 0 ); assert( num.isEq( activities[i].min + activities[i].max, 2 * rhs_values[i] ) ); auto rowvec = constMatrix.getRowCoefficients( i ); const REAL* rowvals = rowvec.getValues(); const int* rowcols = rowvec.getIndices(); const int rowlen = rowvec.getLength(); REAL bincoef = activities[i].max - rhs_values[i]; for( int k = 0; k != rowlen; ++k ) { int col = rowcols[k]; assert( !cflags[col].test( ColFlag::kUnbounded ) ); if( !cflags[col].test( ColFlag::kIntegral ) || domains.lower_bounds[col] != 0 || domains.upper_bounds[col] != 1 || !num.isEq( abs( rowvals[k] ), bincoef ) ) continue; assert( num.isEq( abs( bincoef ), activities[i].max - rhs_values[i] ) ); assert( domains.lower_bounds[col] == 0 ); assert( domains.upper_bounds[col] == 1 ); assert( cflags[col].test( ColFlag::kIntegral ) ); Message::debug( this, "probing on simple equation detected {} substitutions\n", rowlen - 1 ); calculateReductionsForSimpleProbing( num, reductions, domains, activities, rowvals, rowcols, rowlen, col, rowvals[k] ); status = PresolveStatus::kReduced; } return status; } template void SimpleProbing::calculateReductionsForSimpleProbing( const Num& num, Reductions& reductions, const VariableDomains& domains, const Vec>& activities, const REAL* rowvals, const int* rowcols, int rowlen, int bincol, REAL binary_coeff ) { for( int k = 0; k != rowlen; ++k ) { int col = rowcols[k]; if( col == bincol ) continue; REAL factor; REAL offset; if( ( rowvals[k] > 0 && binary_coeff > 0 ) || ( rowvals[k] < 0 && binary_coeff < 0 ) ) { factor = domains.lower_bounds[col] - domains.upper_bounds[col]; offset = domains.upper_bounds[col]; } else { factor = domains.upper_bounds[col] - domains.lower_bounds[col]; offset = domains.lower_bounds[col]; } reductions.replaceCol( col, bincol, factor, offset ); } } } // namespace papilo #endif