/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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_COMPONENTS_HPP_ #define _PAPILO_CORE_COMPONENTS_HPP_ #include "papilo/core/Problem.hpp" #include "papilo/misc/Hash.hpp" #include "papilo/misc/Vec.hpp" #include "papilo/external/pdqsort/pdqsort.h" #include namespace papilo { struct ComponentInfo { int componentid; int nintegral; int ncontinuous; int nnonz; bool operator<( const ComponentInfo& other ) const { return std::make_tuple( nintegral, nnonz, ncontinuous, componentid ) < std::make_tuple( other.nintegral, other.nnonz, other.ncontinuous, other.componentid ); } }; class Components { private: Vec col2comp; Vec row2comp; Vec compcols; Vec comprows; Vec compcolstart; Vec comprowstart; Vec compInfo; public: const int* getComponentsRows( int c ) const { return &comprows[comprowstart[c]]; } int getComponentsNumRows( int c ) const { return comprowstart[c + 1] - comprowstart[c]; } int getRowComponentIdx( int row ) const { return row2comp[row]; } const int* getComponentsCols( int c ) const { return &compcols[compcolstart[c]]; } int getComponentsNumCols( int c ) const { return compcolstart[c + 1] - compcolstart[c]; } int getColComponentIdx( int col ) const { return col2comp[col]; } const Vec& getComponentInfo() const { return compInfo; } template int detectComponents( const Problem& problem ) { const int ncols = problem.getNCols(); std::unique_ptr rank{ new int[ncols] }; std::unique_ptr parent{ new int[ncols] }; boost::disjoint_sets djsets( rank.get(), parent.get() ); for( int i = 0; i != ncols; ++i ) djsets.make_set( i ); const ConstraintMatrix& consMatrix = problem.getConstraintMatrix(); const IndexRange* ranges; int nrows; std::tie( ranges, nrows ) = consMatrix.getRangeInfo(); const int* colinds = consMatrix.getColumns(); for( int r = 0; r != nrows; ++r ) { if( ranges[r].end - ranges[r].start <= 1 ) continue; int firstcol = colinds[ranges[r].start]; for( int i = ranges[r].start + 1; i != ranges[r].end; ++i ) djsets.link( firstcol, colinds[i] ); } HashMap componentmap; for( int i = 0; i != ncols; ++i ) { int nextid = static_cast( componentmap.size() ); componentmap.insert( { djsets.find_set( i ), nextid } ); } int numcomponents = static_cast( componentmap.size() ); if( numcomponents > 1 ) { col2comp.resize( ncols ); compcols.resize( ncols ); for( int i = 0; i != ncols; ++i ) { col2comp[i] = componentmap[djsets.find_set( i )]; compcols[i] = i; } row2comp.resize( nrows ); comprows.resize( nrows ); for( int i = 0; i != nrows; ++i ) { assert( problem.getConstraintMatrix() .getRowCoefficients( i ) .getLength() > 0 ); int col = problem.getConstraintMatrix() .getRowCoefficients( i ) .getIndices()[0]; row2comp[i] = col2comp[col]; comprows[i] = i; } pdqsort( compcols.begin(), compcols.end(), [&]( int col1, int col2 ) { return col2comp[col1] < col2comp[col2]; } ); compcolstart.resize( numcomponents + 1 ); int k = 0; // first component starts at 0 compcolstart[0] = 0; // fill out starts for second to last components, also reuse the // col2comp vector to map the columns of a component to indices // starting at 0 without gaps for( int i = 1; i != numcomponents; ++i ) { while( k != ncols && col2comp[compcols[k]] == i - 1 ) { col2comp[compcols[k]] = k - compcolstart[i - 1]; ++k; } compcolstart[i] = k; } while( k != ncols ) { assert( col2comp[compcols[k]] == numcomponents - 1 ); col2comp[compcols[k]] = k - compcolstart[numcomponents - 1]; ++k; } // last component ends at ncols compcolstart[numcomponents] = ncols; pdqsort( comprows.begin(), comprows.end(), [&]( int row1, int row2 ) { return row2comp[row1] < row2comp[row2]; } ); comprowstart.resize( numcomponents + 1 ); k = 0; // first component starts at 0 comprowstart[0] = 0; // fill out starts for second to last components, also reuse the // row2comp vector to map the rows of a component to indices starting // at 0 without gaps for( int i = 1; i != numcomponents; ++i ) { while( k != nrows && row2comp[comprows[k]] == i - 1 ) { row2comp[comprows[k]] = k - comprowstart[i - 1]; ++k; } comprowstart[i] = k; } while( k != nrows ) { assert( row2comp[comprows[k]] == numcomponents - 1 ); row2comp[comprows[k]] = k - comprowstart[numcomponents - 1]; ++k; } // last component ends at nrows comprowstart[numcomponents] = nrows; // compute size informaton of components compInfo.resize( numcomponents ); const auto& colsizes = problem.getColSizes(); const auto& cflags = problem.getColFlags(); for( int i = 0; i != numcomponents; ++i ) { for( int j = compcolstart[i]; j != compcolstart[i + 1]; ++j ) { if( cflags[compcols[j]].test( ColFlag::kIntegral ) ) ++compInfo[i].nintegral; else ++compInfo[i].ncontinuous; compInfo[i].nnonz += colsizes[compcols[j]]; compInfo[i].componentid = i; } } pdqsort( compInfo.begin(), compInfo.end() ); } return numcomponents; } }; } // namespace papilo #endif