/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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_REDUCTIONS_HPP_ #define _PAPILO_CORE_REDUCTIONS_HPP_ #include "papilo/misc/Vec.hpp" #include namespace papilo { struct ColReduction { enum { NONE = -1, OBJECTIVE = -2, LOWER_BOUND = -3, UPPER_BOUND = -4, FIXED = -5, LOCKED = -6, SUBSTITUTE = -8, BOUNDS_LOCKED = -9, REPLACE = -10, SUBSTITUTE_OBJ = -11, PARALLEL = -12, IMPL_INT = -13, FIXED_INFINITY = -14, }; }; struct RowReduction { enum { NONE = -1, RHS = -2, LHS = -3, REDUNDANT = -4, LOCKED = -5, RHS_INF = -7, LHS_INF = -8, SPARSIFY = -9, RHS_LESS_RESTRICTIVE = -10, LHS_LESS_RESTRICTIVE = -11, REASON_FOR_LESS_RESTRICTIVE_BOUND_CHANGE = -12, SAVE_ROW = -13 }; }; template struct Reduction { /// value stored in reduction. Meaning depends on the operation REAL newval; /// index of row or negative for column specific operations int row; /// index of column or negative for row specific operations int col; Reduction( REAL _newval, int _row, int _col ) : newval( _newval ), row( _row ), col( _col ) { } }; template class Reductions { public: void startTransaction() { assert( transactions.empty() || transactions.back().end >= 0 ); const int start = static_cast( reductions.size() ); transactions.emplace_back( start, -1 ); } void endTransaction() { assert( !transactions.empty() && transactions.back().end == -1 ); const int end = static_cast( reductions.size() ); assert( end != transactions.back().start ); transactions.back().end = end; } void add_reduction( int row, int col, REAL newval ) { reductions.emplace_back( newval, row, col ); } void changeMatrixEntry( int row, int col, REAL newval ) { assert( row >= 0 && col >= 0 ); reductions.emplace_back( newval, row, col ); } void changeRowLHS( int row, REAL newval ) { reductions.emplace_back( newval, row, RowReduction::LHS ); } void change_row_lhs_parallel( int row, REAL newval ) { reductions.emplace_back( newval, row, RowReduction::LHS_LESS_RESTRICTIVE ); } void changeRowRHS( int row, REAL newval ) { reductions.emplace_back( newval, row, RowReduction::RHS ); } void change_row_rhs_parallel( int row, REAL newval ) { reductions.emplace_back( newval, row, RowReduction::RHS_LESS_RESTRICTIVE ); } void bound_change_caused_by_row( int remaining_row, int deleted_row ) { reductions.emplace_back( remaining_row, deleted_row, RowReduction::REASON_FOR_LESS_RESTRICTIVE_BOUND_CHANGE ); } void changeRowLHSInf( int row ) { reductions.emplace_back( 0.0, row, RowReduction::LHS_INF ); } void changeRowRHSInf( int row ) { reductions.emplace_back( 0.0, row, RowReduction::RHS_INF ); } void markRowRedundant( int row ) { reductions.emplace_back( REAL{ 0.0 }, row, RowReduction::REDUNDANT ); } /// lock row, i.e. modifications that come before this transaction are /// conflicting but not modifications that come after this transaction void lockRow( int row ) { // locks are only valid inside a transaction assert( !transactions.empty() && transactions.back().end == -1 ); // locks must come first within a transaction assert( transactions.back().start + transactions.back().nlocks == static_cast( reductions.size() ) ); reductions.emplace_back( 0.0, row, RowReduction::LOCKED ); ++transactions.back().nlocks; } void changeColLB( int col, REAL new_val, int forcing_row = -1 ) { if(forcing_row > -1) reductions.emplace_back(0, forcing_row, RowReduction::SAVE_ROW); reductions.emplace_back( new_val, ColReduction::LOWER_BOUND, col ); } void changeColUB( int col, REAL new_val, int forcing_row = -1 ) { if( forcing_row > -1) reductions.emplace_back(0, forcing_row, RowReduction::SAVE_ROW); reductions.emplace_back( new_val, ColReduction::UPPER_BOUND, col ); } void fixCol( int col, REAL val, int forcing_row = -1 ) { if(forcing_row > -1) reductions.emplace_back(0, forcing_row, RowReduction::SAVE_ROW); reductions.emplace_back( val, ColReduction::FIXED, col ); } void fixColPositiveInfinity( int col, int columnLength, const int* rowIndices ) { for( int i = 0; i < columnLength; i++ ) markRowRedundant( rowIndices[i] ); reductions.emplace_back( 1, ColReduction::FIXED_INFINITY, col ); } void fixColNegativeInfinity( int col, int columnLength, const int* rowIndices ) { for( int i = 0; i < columnLength; i++ ) markRowRedundant( rowIndices[i] ); reductions.emplace_back( -1, ColReduction::FIXED_INFINITY, col ); } /// lock column, i.e. modifications that come before this transaction are /// conflicting but not modifications that come after this transaction void lockCol( int col ) { assert( !transactions.empty() && transactions.back().end == -1 ); assert( transactions.back().start + transactions.back().nlocks == static_cast( reductions.size() ) ); reductions.emplace_back( 0.0, ColReduction::LOCKED, col ); ++transactions.back().nlocks; } /// lock column lower and upper bounds void lockColBounds( int col ) { assert( !transactions.empty() && transactions.back().end == -1 ); assert( transactions.back().start + transactions.back().nlocks == static_cast( reductions.size() ) ); reductions.emplace_back( 0.0, ColReduction::BOUNDS_LOCKED, col ); ++transactions.back().nlocks; } /// signal that a column in free and can be substituted in the matrix void aggregateFreeCol( int col, int equalityRow ) { assert( col >= 0 && equalityRow >= 0 ); reductions.emplace_back( static_cast( equalityRow ), ColReduction::SUBSTITUTE, col ); } /// signal that a column in free and can be substituted in the matrix void substituteColInObjective( int col, int equalityRow ) { assert( col >= 0 && equalityRow >= 0 ); reductions.emplace_back( static_cast( equalityRow ), ColReduction::SUBSTITUTE_OBJ, col ); } // replace col1 = factor * col2 + offset void replaceCol( int col1, int col2, REAL factor, REAL offset ) { assert( col1 >= 0 && col2 >= 0 ); startTransaction(); reductions.emplace_back( factor, ColReduction::REPLACE, col1 ); reductions.emplace_back( offset, ColReduction::NONE, col2 ); endTransaction(); } /// parallel columns col1 and col2 must satisfies all conditions so /// that they can be substituted by a new variable y = col2 + factor * col1 /// where factor is computed by using the ratio between the two /// columns coefficients void mark_parallel_cols( int col1, int col2 ) { assert( col1 >= 0 && col2 >= 0 ); reductions.emplace_back( static_cast( col2 ), ColReduction::PARALLEL, col1 ); } void impliedInteger( int col ) { assert( col >= 0 ); reductions.emplace_back( 0, ColReduction::IMPL_INT, col ); } void sparsify( int eq, int numrows, const std::pair* sparsifiedrows ) { reductions.emplace_back( static_cast( numrows ), eq, RowReduction::SPARSIFY ); for( int i = 0; i != numrows; ++i ) reductions.emplace_back( sparsifiedrows[i].second, sparsifiedrows[i].first, RowReduction::NONE ); } unsigned int size() { return reductions.size(); } void clear() { reductions.clear(); transactions.clear(); } const Vec>& getReductions() const { return reductions; } struct Transaction { int start; int end; int nlocks; int naddcoeffs; Transaction( int start_, int end_ ) : start( start_ ), end( end_ ), nlocks( 0 ), naddcoeffs( 0 ) { } }; const Vec& getTransactions() const { return transactions; } private: Vec> reductions; Vec transactions; public: Reduction& getReduction( int i ) { return reductions[i]; } }; template class TransactionGuard { public: TransactionGuard( Reductions& _reductions ) : reductions( _reductions ) { _reductions.startTransaction(); } ~TransactionGuard() { reductions.endTransaction(); } private: Reductions& reductions; }; } // namespace papilo #endif