/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* 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_MATRIX_BUFFER_HPP_ #define _PAPILO_CORE_MATRIX_BUFFER_HPP_ #include "papilo/core/SparseStorage.hpp" #include "papilo/misc/Vec.hpp" #include #include namespace papilo { // for doubles, entry is 32bytes which makes two entries per cache line // the entries are added to a vector and linked to two trees. One // allows traversal for row major and one for column major storage order template struct MatrixEntry { REAL val; int row; int col; struct TreeHook { int left; int right; }; TreeHook row_major; TreeHook col_major; MatrixEntry() {} MatrixEntry( int _row, int _col, const REAL& _val ) : val( _val ), row( _row ), col( _col ) { row_major.left = 0; row_major.right = 0; col_major.left = 0; col_major.right = 0; } }; template struct GetNodeProperty; /// data structure for sparse matrix entries that allows efficient row /// major and column major traversal template struct MatrixBuffer { template int splay( int n, int t ) { using Node = GetNodeProperty; /* Simple top down splay, not requiring i to be in the tree t. */ /* What it does is described above. */ assert( t != 0 ); int l = 0; int r = 0; int y; for( ;; ) { if( Node::lesser( entries[n], entries[t] ) ) { int t_left = Node::left( entries[t] ); if( t_left == 0 ) break; if( Node::lesser( entries[n], entries[t_left] ) ) { // rotate right y = t_left; Node::left( entries[t] ) = Node::right( entries[y] ); Node::right( entries[y] ) = t; t = y; if( Node::left( entries[t] ) == 0 ) break; } // link right Node::left( entries[r] ) = t; r = t; t = Node::left( entries[t] ); } else { int t_right = Node::right( entries[t] ); if( t_right == 0 ) break; if( Node::lesser( entries[t_right], entries[n] ) ) { // rotate left y = t_right; Node::right( entries[t] ) = Node::left( entries[y] ); Node::left( entries[y] ) = t; t = y; if( Node::right( entries[t] ) == 0 ) break; } // link left Node::right( entries[l] ) = t; l = t; t = Node::right( entries[t] ); } } // assemble Node::right( entries[l] ) = Node::left( entries[t] ); Node::left( entries[r] ) = Node::right( entries[t] ); Node::left( entries[t] ) = Node::right( entries[0] ); Node::right( entries[t] ) = Node::left( entries[0] ); Node::left( entries[0] ) = 0; Node::right( entries[0] ) = 0; return t; } template void splay_insert( int n, int t ) { using Node = GetNodeProperty; t = splay( n, t ); // make n the new root if( Node::lesser( entries[n], entries[t] ) ) { Node::left( entries[n] ) = Node::left( entries[t] ); Node::right( entries[n] ) = t; Node::left( entries[t] ) = 0; } else { Node::right( entries[n] ) = Node::right( entries[t] ); Node::left( entries[n] ) = t; Node::right( entries[t] ) = 0; } } /// link node n into the tree for the given storage order template void link( int n ) { using Node = GetNodeProperty; // currnode is the pointer of the parent node // where n will be inserted, starting with the root pointer int* currnode = &Node::root( this ); // get priority of n uint32_t prio = Node::priority( entries[n] ); // after this loop n will be the subtree that needs // to be linked to *currnode. That is either a // leaf node or a treap where n is the root while( *currnode != 0 ) { uint32_t p = Node::priority( entries[*currnode] ); if( p < prio ) { // if we found a node with a smaller priority we do // a top down splay to make n the root of this sub tree // and stop insertion here. int t = splay( n, *currnode ); // make n the new root if( Node::lesser( entries[n], entries[t] ) ) { Node::left( entries[n] ) = Node::left( entries[t] ); Node::right( entries[n] ) = t; Node::left( entries[t] ) = 0; } else { Node::right( entries[n] ) = Node::right( entries[t] ); Node::left( entries[n] ) = t; Node::right( entries[t] ) = 0; } break; } // go further down the tree if( Node::lesser( entries[n], entries[*currnode] ) ) currnode = &Node::left( entries[*currnode] ); else currnode = &Node::right( entries[*currnode] ); } // update the parents pointer to the the tree now rooted at n // (could be the root pointer) *currnode = n; } bool empty() const { return entries.size() == 1; } void clear() { entries.resize( 1 ); col_major_root = 0; row_major_root = 0; } void addEntry( int row, int col, const REAL& val ) { int n = entries.size(); entries.emplace_back( row, col, val ); this->template link( n ); this->template link( n ); } template MatrixEntry* findEntry( int row, int col ) { using Node = GetNodeProperty; MatrixEntry entry( row, col, REAL{ 0 } ); int k = Node::root( this ); while( k != 0 ) { if( Node::lesser( entry, entries[k] ) ) k = Node::left( entries[k] ); else if( Node::lesser( entries[k], entry ) ) k = Node::right( entries[k] ); else { assert( entries[k].row == row ); assert( entries[k].col == col ); return &entries[k]; } } return nullptr; } void addEntrySafe( int row, int col, const REAL& val ) { MatrixEntry* entry = findEntry( row, col ); if( entry != NULL ) entry->val += val; else addEntry( row, col, val ); } template const MatrixEntry* begin( SmallVec& stack ) const { using Node = GetNodeProperty; stack.clear(); stack.push_back( 0 ); int k = Node::root( this ); while( k != 0 ) { stack.push_back( k ); k = Node::left( entries[k] ); } return &entries[stack.back()]; } template const MatrixEntry* beginStart( SmallVec& stack, int row, int col ) const { using Node = GetNodeProperty; stack.clear(); stack.push_back( 0 ); int k = Node::root( this ); assert( row == -1 || col == -1 ); MatrixEntry dummy( row, col, REAL{ 0 } ); while( k != 0 ) { if( Node::lesser( dummy, entries[k] ) ) { stack.push_back( k ); k = Node::left( entries[k] ); } else { k = Node::right( entries[k] ); } } return &entries[stack.back()]; } template const MatrixEntry* next( SmallVec& stack ) const { using Node = GetNodeProperty; int k = stack.back(); stack.pop_back(); k = Node::right( entries[k] ); while( k != 0 ) { stack.push_back( k ); k = Node::left( entries[k] ); } return &entries[stack.back()]; } const MatrixEntry* end() const { return &entries[0]; } void reserve( int nnz ) { entries.reserve( nnz + 1 ); } void startBadge() { badge_start = entries.size(); } void addBadgeEntry( int row, int col, const REAL& val ) { assert( badge_start >= 0 && badge_start <= (int)entries.size() ); entries.emplace_back( row, col, val ); } void discardBadge() { assert( badge_start >= 0 && badge_start <= entries.size() ); entries.resize( badge_start ); badge_start = -1; } void finishBadge() { assert( badge_start >= 0 && badge_start <= (int)entries.size() ); for( int i = badge_start; i != (int)entries.size(); ++i ) { this->template link( i ); this->template link( i ); } badge_start = -1; } int getNnz() const { return entries.size() - 1; } SparseStorage buildCSR( int nrows, int ncols, double spareRatio = SparseStorage::DEFAULT_SPARE_RATIO, int mininterrowspace = SparseStorage::DEFAULT_MIN_INTER_ROW_SPACE ) { int nnz = getNnz(); SparseStorage csrStorage( nrows, ncols, nnz, spareRatio, mininterrowspace ); SmallVec stack; REAL* values = csrStorage.getValues(); int* columns = csrStorage.getColumns(); IndexRange* rowranges = csrStorage.getRowRanges(); const MatrixEntry* it = this->begin( stack ); const MatrixEntry* end = this->end(); int k = 0; for( int i = 0; i != nrows; ++i ) { rowranges[i].start = k; while( it != end && it->row == i ) { values[k] = it->val; columns[k] = it->col; ++k; it = this->next( stack ); } rowranges[i].end = k; if( k != rowranges[i].start ) { int rowsize = k - rowranges[i].start; k += csrStorage.computeRowAlloc( rowsize ) - rowsize; } } rowranges[nrows].start = csrStorage.getNAlloc(); rowranges[nrows].end = csrStorage.getNAlloc(); return csrStorage; } SparseStorage buildCSC( int nrows, int ncols, double spareRatio = SparseStorage::DEFAULT_SPARE_RATIO, int minintercolspace = SparseStorage::DEFAULT_MIN_INTER_ROW_SPACE ) { int nnz = getNnz(); SparseStorage cscStorage( ncols, nrows, nnz, spareRatio, minintercolspace ); SmallVec stack; REAL* values = cscStorage.getValues(); int* rows = cscStorage.getColumns(); IndexRange* colranges = cscStorage.getRowRanges(); const MatrixEntry* it = this->begin( stack ); const MatrixEntry* end = this->end(); int k = 0; for( int i = 0; i != ncols; ++i ) { colranges[i].start = k; while( it != end && it->col == i ) { values[k] = it->val; rows[k] = it->row; ++k; it = this->next( stack ); } colranges[i].end = k; if( k != colranges[i].start ) { int colsize = k - colranges[i].start; k += cscStorage.computeRowAlloc( colsize ) - colsize; } } colranges[ncols].start = cscStorage.getNAlloc(); colranges[ncols].end = cscStorage.getNAlloc(); return cscStorage; } MatrixBuffer() { // root is the dummy NULL node row_major_root = 0; col_major_root = 0; // insert dummy NULL node at position 0 entries.emplace_back( -1, -1, 0 ); } int badge_start = -1; int row_major_root; int col_major_root; Vec> entries; }; template <> struct GetNodeProperty { template static uint32_t priority( const MatrixEntry& e ) { uint64_t x = uint64_t( uint32_t( e.row ) ) << 32 | uint32_t( e.col ); return uint32_t( ( x * UINT64_C( 0x9e3779b97f4a7c15 ) ) >> 32 ); } template static bool lesser( const MatrixEntry& a, const MatrixEntry& b ) { return a.row < b.row || ( a.row == b.row && a.col < b.col ); } template static int& left( MatrixEntry& e ) { return e.row_major.left; } template static int& right( MatrixEntry& e ) { return e.row_major.right; } template static int& root( MatrixBuffer* thisptr ) { return thisptr->row_major_root; } template static const int& left( const MatrixEntry& e ) { return e.row_major.left; } template static const int& right( const MatrixEntry& e ) { return e.row_major.right; } template static const int& root( const MatrixBuffer* thisptr ) { return thisptr->row_major_root; } }; template <> struct GetNodeProperty { template static uint32_t priority( const MatrixEntry& e ) { uint64_t x = uint64_t( uint32_t( e.col ) ) << 32 | uint32_t( e.row ); return uint32_t( ( x * UINT64_C( 0x9e3779b97f4a7c15 ) ) >> 32 ); } template static bool lesser( const MatrixEntry& a, const MatrixEntry& b ) { return a.col < b.col || ( a.col == b.col && a.row < b.row ); } template static int& left( MatrixEntry& e ) { return e.col_major.left; } template static int& right( MatrixEntry& e ) { return e.col_major.right; } template static int& root( MatrixBuffer* thisptr ) { return thisptr->col_major_root; } template static const int& left( const MatrixEntry& e ) { return e.col_major.left; } template static const int& right( const MatrixEntry& e ) { return e.col_major.right; } template static const int& root( const MatrixBuffer* thisptr ) { return thisptr->col_major_root; } }; } // namespace papilo #endif