/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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_CORE_PRESOLVE_METHOD_HPP_ #define _PAPILO_CORE_PRESOLVE_METHOD_HPP_ #include "papilo/core/PresolveOptions.hpp" #include "papilo/core/Reductions.hpp" #include "papilo/core/RowFlags.hpp" #include "papilo/core/VariableDomains.hpp" #include "papilo/io/Message.hpp" #include "papilo/misc/Num.hpp" #include "papilo/misc/Vec.hpp" #include "papilo/misc/fmt.hpp" #include "papilo/misc/Timer.hpp" #ifdef PAPILO_TBB #include "papilo/misc/tbb.hpp" #else #include #endif #include namespace papilo { // forward declaration of problem and reduction template class Presolve; template class Problem; template class ProblemUpdate; enum class PresolveStatus : int { kUnchanged = 0, kReduced = 1, kUnbndOrInfeas = 2, kUnbounded = 3, kInfeasible = 4, }; enum class PresolverTiming : int { kFast = 0, kMedium = 1, kExhaustive = 2, }; enum class PresolverType { kAllCols, kIntegralCols, kContinuousCols, kMixedCols, }; template class PresolveMethod { public: PresolveMethod() { ncalls = 0; nsuccessCall = 0; name = "unnamed"; type = PresolverType::kAllCols; timing = PresolverTiming::kExhaustive; delayed = false; execTime = 0.0; enabled = true; skip = 0; nconsecutiveUnsuccessCall = 0; } virtual ~PresolveMethod() = default; virtual void compress( const Vec& rowmap, const Vec& colmap ) { } virtual bool initialize( const Problem& problem, const PresolveOptions& presolveOptions ) { return false; } virtual void addPresolverParams( ParameterSet& paramSet ) { } void addParameters( ParameterSet& paramSet ) { paramSet.addParameter( fmt::format( "{}.enabled", this->name ).c_str(), fmt::format( "is presolver {} enabled", this->name ).c_str(), this->enabled ); addPresolverParams( paramSet ); } PresolveStatus run( const Problem& problem, const ProblemUpdate& problemUpdate, const Num& num, Reductions& reductions, const Timer& timer ) { if( !enabled || delayed ) return PresolveStatus::kUnchanged; if( skip != 0 ) { --skip; return PresolveStatus::kUnchanged; } if( problem.getNumIntegralCols() == 0 && ( type == PresolverType::kIntegralCols || type == PresolverType::kMixedCols ) ) return PresolveStatus::kUnchanged; if( problem.getNumContinuousCols() == 0 && ( type == PresolverType::kContinuousCols || type == PresolverType::kMixedCols ) ) return PresolveStatus::kUnchanged; ++ncalls; #ifdef PAPILO_TBB auto start = tbb::tick_count::now(); #else auto start = std::chrono::steady_clock::now(); #endif PresolveStatus result = execute( problem, problemUpdate, num, reductions, timer ); #ifdef PAPILO_TBB auto end = tbb::tick_count::now(); auto duration = end - start; execTime = execTime + duration.seconds(); #else auto end = std::chrono::steady_clock::now(); execTime = execTime + std::chrono::duration_cast( end- start ).count()/1000; #endif switch( result ) { case PresolveStatus::kUnbounded: case PresolveStatus::kUnbndOrInfeas: case PresolveStatus::kInfeasible: Message::debug( &problemUpdate, "[{}:{}] {} detected unboundedness or infeasibility\n", __FILE__, __LINE__, this->name ); break; case PresolveStatus::kReduced: ++nsuccessCall; nconsecutiveUnsuccessCall = 0; break; case PresolveStatus::kUnchanged: ++nconsecutiveUnsuccessCall; if( timing != PresolverTiming::kFast ) skip += nconsecutiveUnsuccessCall; break; } return result; } void printStats( const Message& message, std::pair stats ) { double success = ncalls == 0 ? 0.0 : ( double( nsuccessCall ) / double( ncalls ) ) * 100.0; double applied = stats.first == 0 ? 0.0 : ( double( stats.second ) / double( stats.first ) ) * 100.0; message.info( " {:>18} {:>12} {:>18.1f} {:>18} {:>18.1f} {:>18.3f}\n", name, ncalls, success, stats.first, applied, execTime ); } PresolverTiming getTiming() const { return this->timing; } bool isEnabled() const { return this->enabled; } bool isDelayed() const { return this->delayed; } const std::string& getName() const { return this->name; } unsigned int getNCalls() const { return ncalls; } void setDelayed( bool value ) { this->delayed = value; } void setEnabled( bool value ) { this->enabled = value; } protected: /// execute member function for a presolve method gets the constant problem /// and can communicate reductions via the given reductions object virtual PresolveStatus execute( const Problem& problem, const ProblemUpdate& problemUpdate, const Num& num, Reductions& reductions, const Timer& timer ) = 0; void setName( const std::string& value ) { this->name = value; } void setTiming( PresolverTiming value ) { this->timing = value; } void setType( PresolverType value ) { this->type = value; } static bool is_time_exceeded( const Timer& timer, double tlim) { return tlim != std::numeric_limits::max() && timer.getTime() >= tlim; } void skipRounds( unsigned int nrounds ) { this->skip += nrounds; } template void loop( int start, int end, LOOP&& loop_instruction ) { #ifdef PAPILO_TBB tbb::parallel_for( tbb::blocked_range( start, end ), [&]( const tbb::blocked_range& r ) { for( int i = r.begin(); i != r.end(); ++i ) loop_instruction( i ); } ); #else for( int i = 0; i < end; i++ ) { loop_instruction( i ); } #endif } private: std::string name; double execTime; bool enabled; bool delayed; PresolverTiming timing; PresolverType type; unsigned int ncalls; // number of times execute returns REDUCED unsigned int nsuccessCall; unsigned int nconsecutiveUnsuccessCall; unsigned int skip; }; } // namespace papilo #endif