/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* */
/* 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