/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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_COEFFICIENT_STRENGTHENING_HPP_ #define _PAPILO_PRESOLVERS_COEFFICIENT_STRENGTHENING_HPP_ #include "papilo/core/PresolveMethod.hpp" #include "papilo/core/Problem.hpp" #include "papilo/core/ProblemUpdate.hpp" namespace papilo { template class CoefficientStrengthening : public PresolveMethod { public: CoefficientStrengthening() : PresolveMethod() { this->setName( "coefftightening" ); this->setType( PresolverType::kIntegralCols ); this->setTiming( PresolverTiming::kFast ); } PresolveStatus execute( const Problem& problem, const ProblemUpdate& problemUpdate, const Num& num, Reductions& reductions, const Timer& timer ) override; private: PresolveStatus perform_coefficient_tightening( const Num& num, const VariableDomains& domains, const Vec>& activities, int changed_activity, const ConstraintMatrix& constMatrix, const Vec& lhs_values, const Vec& rhs_values, const Vec& rflags, const Vec& cflags, Reductions& reductions, Vec>& integerCoefficients ) const; }; #ifdef PAPILO_USE_EXTERN_TEMPLATES extern template class CoefficientStrengthening; extern template class CoefficientStrengthening; extern template class CoefficientStrengthening; #endif template PresolveStatus CoefficientStrengthening::execute( const Problem& problem, const ProblemUpdate& problemUpdate, const Num& num, Reductions& reductions, const Timer& timer ) { assert( problem.getNumIntegralCols() != 0 ); const auto& domains = problem.getVariableDomains(); const auto& cflags = domains.flags; const auto& activities = problem.getRowActivities(); const auto& changedActivities = problemUpdate.getChangedActivities(); const auto& constMatrix = problem.getConstraintMatrix(); const auto& lhs_values = constMatrix.getLeftHandSides(); const auto& rhs_values = constMatrix.getRightHandSides(); const auto& rflags = constMatrix.getRowFlags(); PresolveStatus result = PresolveStatus::kUnchanged; #ifndef PAPILO_TBB assert( problemUpdate.getPresolveOptions().runs_sequential() ); #endif if( problemUpdate.getPresolveOptions().runs_sequential() || !problemUpdate.getPresolveOptions().coefficient_strengthening_parallel ) { Vec> integerCoefficients; for( int i : changedActivities ) { if( perform_coefficient_tightening( num, domains, activities, i, constMatrix, lhs_values, rhs_values, rflags, cflags, reductions, integerCoefficients ) == PresolveStatus::kReduced ) result = PresolveStatus::kReduced; } } #ifdef PAPILO_TBB else { Vec> stored_reductions( changedActivities.size() ); tbb::parallel_for( tbb::blocked_range( 0, changedActivities.size() ), [&]( const tbb::blocked_range& r ) { Vec> integerCoefficients; for( int j = r.begin(); j < r.end(); ++j ) { if( perform_coefficient_tightening( num, domains, activities, changedActivities[j], constMatrix, lhs_values, rhs_values, rflags, cflags, stored_reductions[j], integerCoefficients ) == 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& 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 result; } template PresolveStatus CoefficientStrengthening::perform_coefficient_tightening( const Num& num, const VariableDomains& domains, const Vec>& activities, int changed_activity, const ConstraintMatrix& constMatrix, const Vec& lhs_values, const Vec& rhs_values, const Vec& rflags, const Vec& cflags, Reductions& reductions, Vec>& integerCoefficients ) const { auto rowCoefficients = constMatrix.getRowCoefficients( changed_activity ); const REAL* coefficients = rowCoefficients.getValues(); const int len = rowCoefficients.getLength(); const int* coefindices = rowCoefficients.getIndices(); auto& rowFlag = rflags[changed_activity]; if( ( !rowFlag.test( RowFlag::kLhsInf ) && !rowFlag.test( RowFlag::kRhsInf ) ) || len <= 1 ) return PresolveStatus::kUnchanged; REAL rhs; REAL maxact; int scale; // normalize constraint to a * x <= b constraint, remember if it // was scaled by -1 if( !rowFlag.test( RowFlag::kLhsInf ) ) { assert( rowFlag.test( RowFlag::kRhsInf ) ); if( activities[changed_activity].ninfmin == 0 ) maxact = -activities[changed_activity].min; else return PresolveStatus::kUnchanged; rhs = -lhs_values[changed_activity]; scale = -1; } else { assert( !rowFlag.test( RowFlag::kRhsInf ) ); assert( rowFlag.test( RowFlag::kLhsInf ) ); if( activities[changed_activity].ninfmax == 0 ) maxact = activities[changed_activity].max; else return PresolveStatus::kUnchanged; rhs = rhs_values[changed_activity]; scale = 1; } // remember the integer coefficients and the maximum absolute value // of an integer variable integerCoefficients.clear(); REAL newabscoef = maxact - rhs; if( num.isZero( newabscoef ) ) newabscoef = 0; else if( num.isFeasEq( newabscoef, ceil( newabscoef ) ) ) newabscoef = ceil( newabscoef ); for( int k = 0; k != len; ++k ) { if( !cflags[coefindices[k]].test( ColFlag::kIntegral, ColFlag::kImplInt ) || cflags[coefindices[k]].test( ColFlag::kFixed ) ) continue; if( num.isFeasGE( newabscoef, abs( coefficients[k] ) ) ) continue; integerCoefficients.emplace_back( coefficients[k] * scale, coefindices[k] ); } if( integerCoefficients.empty() ) return PresolveStatus::kUnchanged; assert( num.isFeasGT( maxact, rhs ) ); // adjust side and qualified coefficients for( std::pair& intCoef : integerCoefficients ) { assert( domains.flags[intCoef.second].test( ColFlag::kLbInf ) || domains.flags[intCoef.second].test( ColFlag::kUbInf ) || domains.lower_bounds[intCoef.second] != domains.upper_bounds[intCoef.second] ); assert( domains.flags[intCoef.second].test( ColFlag::kLbInf ) || domains.flags[intCoef.second].test( ColFlag::kUbInf ) || domains.upper_bounds[intCoef.second] - domains.lower_bounds[intCoef.second] >= 1.0 ); assert( newabscoef < abs( intCoef.first ) ); if( intCoef.first < REAL{ 0 } ) { assert( !domains.flags[intCoef.second].test( ColFlag::kLbInf ) ); rhs -= ( newabscoef + intCoef.first ) * domains.lower_bounds[intCoef.second]; intCoef.first = -newabscoef; } else { assert( !domains.flags[intCoef.second].test( ColFlag::kUbInf ) ); rhs += ( newabscoef - intCoef.first ) * domains.upper_bounds[intCoef.second]; intCoef.first = newabscoef; } } // add reduction to change side and coefficients TransactionGuard guard{ reductions }; reductions.lockRow( changed_activity ); if( scale == -1 ) { for( const std::pair& intCoef : integerCoefficients ) reductions.changeMatrixEntry( changed_activity, intCoef.second, -intCoef.first ); assert( rowFlag.test( RowFlag::kRhsInf ) ); assert( !rowFlag.test( RowFlag::kLhsInf ) ); if( lhs_values[changed_activity] != -rhs ) reductions.changeRowLHS( changed_activity, -rhs ); } else { assert( scale == 1 ); for( const std::pair& intCoef : integerCoefficients ) reductions.changeMatrixEntry( changed_activity, intCoef.second, intCoef.first ); assert( rowFlag.test( RowFlag::kLhsInf ) ); assert( !rowFlag.test( RowFlag::kRhsInf ) ); if( rhs_values[changed_activity] != rhs ) reductions.changeRowRHS( changed_activity, rhs ); } // stored_reductions[j] = reduction; return PresolveStatus::kReduced; } } // namespace papilo #endif