SparseLibrary  Version 1.6.0
BlockHilbert.hpp
1 /*
2  * Copyright (c) 2007-2014, A. N. Yzelman, Utrecht University 2007-2011;
3  * KU Leuven 2011-2014.
4  * R. H. Bisseling, Utrecht University 2007-2014.
5  *
6  * This file is part of the Sparse Library.
7  *
8  * This library was developed under supervision of Prof. dr. Rob H. Bisseling at
9  * Utrecht University, from 2007 until 2011. From 2011-2014, development continued
10  * at KU Leuven, where Prof. dr. Dirk Roose contributed significantly to the ideas
11  * behind the newer parts of the library code.
12  *
13  * The Sparse Library is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by the
15  * Free Software Foundation, either version 3 of the License, or (at your
16  * option) any later version.
17  *
18  * The Sparse Library is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  * for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with the Sparse Library. If not, see <http://www.gnu.org/licenses/>.
25  */
26 
27 
28 /*
29  * File created by:
30  * A. N. Yzelman, Dept. of Mathematics, Utrecht University, 2010.
31  */
32 
33 
34 #include <vector>
35 #include "SparseMatrix.hpp"
36 #include "HilbertTriplet.hpp"
37 #include "HilbertTripletCompare.hpp"
38 #include "Triplet.hpp"
39 #include "HBICRS.hpp"
40 
41 #ifdef _DEBUG
42  #include <iostream>
43 #endif
44 
45 #ifndef _H_BLOCKHILBERT
46 #define _H_BLOCKHILBERT
47 
51 template< typename T >
52 class BlockHilbert: public SparseMatrix< T, LI > {
53 
54  private:
55 
56  protected:
57 
59  char bisection;
60 
62  static const ULI BLOCK_M = 2048;
63 
65  static const ULI BLOCK_N = 2048;
66 
68  static const ULI MAX_BLOCKS = 1024*128;
69 
71  static const ULI MAX_DEPTH = 64; // NOTE: This CANNOT be larger than 64, unless unsigned long ints are larger than 64 bits!
72 
74  static const signed char DS = 2; // ICRS for use on the submatrices
75 
77  ULI minexp;
78 
80  std::vector< HilbertTriplet< std::vector< Triplet < T > > > > hilbert;
81 
84 
95  char bisect( std::vector< HilbertTriplet< std::vector< Triplet< T > > > > &build,
96  std::vector< Triplet< T > > &input,
97  const unsigned long int blocki, const unsigned long int blockj,
98  const unsigned char depth ) {
99  //find min/max row, column
100  typename std::vector< Triplet< T > >::iterator it = input.begin();
101  unsigned long int row, col; // number of rows/cols
102  unsigned long int rl = it->i(); // initial min/max row/cols
103  unsigned long int rh = it->i();
104  unsigned long int cl = it->j();
105  unsigned long int ch = it->j();
106  ++it;
107  for( ; it != input.end(); ++it ) {
108  if( rl > it->i() ) rl = it->i();
109  if( rh < it->i() ) rh = it->i();
110  if( cl > it->j() ) cl = it->j();
111  if( ch < it->j() ) ch = it->j();
112  }
113 
114  assert( rl <= rh );
115  assert( cl <= ch );
116 
117  row = rh - rl + 1;
118  col = ch - cl + 1;
119 
120  assert( row <= (unsigned long int)(this->nor) );
121  assert( col <= (unsigned long int)(this->noc) );
122 
123  //remember bounds
124  const unsigned long int row_lo = rl;
125  const unsigned long int col_lo = cl;
126  const unsigned long int row_hi = rh;
127  const unsigned long int col_hi = ch;
128 
129  //build cumulative row/col arrays, count empty rows/cols
130  unsigned long int re = 0;
131  unsigned long int ce = 0;
132  unsigned long int *rows = new unsigned long int[ row ];
133  unsigned long int *cols = new unsigned long int[ col ];
134  for( unsigned long int i=0; i<row; i++ ) rows[ i ] = 0;
135  for( unsigned long int j=0; j<col; j++ ) cols[ j ] = 0;
136  it = input.begin();
137  for( ; it != input.end(); ++it ) {
138  rows[ it->i()-rl ]++;
139  cols[ it->j()-cl ]++;
140  }
141  for( unsigned long int i=1; i<row; i++ ) {
142  if( rows[i] == 0 ) re++;
143  rows[ i ] += rows[i-1];
144  }
145  for( unsigned long int j=1; j<col; j++ ) {
146  if( cols[j] == 0 ) ce++;
147  cols[ j ] += cols[j-1];
148  }
149 
150  //check if not eligable to proceed, taking into account empty subrows, subcolumns
151  if( row+col <= BLOCK_M + BLOCK_N + re + ce ) { //FIXME: empty count should take into account w (how many doubles in a cache line?)
152  //if( row+col <= BLOCK_M + BLOCK_N ) {
153  //already small enough block
154  //so simply store current one again and exit
155  build.push_back( HilbertTriplet< std::vector< Triplet< T > > >( blocki, blockj, input ) );
156  delete [] rows; /*FIXME inefficient memory handling, but this is initialisation only */
157  delete [] cols;
158  return 2;
159  }
160 
161  //set target
162  const unsigned long int half = input.size() / 2;
163  //prepare bisection
164  rl = cl = 0; //reset to *local* column/row indices
165  rh = row;
166  ch = col;
167  unsigned long int rs, cs;
168  unsigned long int rm, cm;
169  //do bisection search
170  while( true ) {
171  rm = (rl+rh)/2;
172  cm = (cl+ch)/2;
173  if( rows[ rm ] < half ) { rs = (rm - rl); rl = rm; }
174  else if( rows[ rm ] > half ) { rs = (rh - rm); rh = rm; }
175  else rs = 0;
176  if( cols[ cm ] < half ) { cs = (cm - cl); cl = cm; }
177  else if( cols[ cm ] > half ) { cs = (ch - cm); ch = cm; }
178  else cs = 0;
179  if( rs == 0 && cs == 0 ) break;
180  }
181  //note rh, ch, rl, ch, were used for search and now may not be the
182  //values you may expect
183 
184  //prevent empty split if it all possible
185  if( rm - row_lo == 0 && row_hi - rm > 1 ) rm++;
186  if( row_hi - rm == 0 && rm - row_lo > 1 ) rm--;
187  if( cm - col_lo == 0 && col_hi - cm > 1 ) cm++;
188  if( col_hi - cm == 0 && cm - col_lo > 1 ) cm--;
189 
190  //choose a split direction
191  char dir = 2; //0 for row, 1 for col
192  if( rows[ rm ] > half ) rs = rows[ rm ] - half;
193  else rs = half - rows[ rm ];
194  if( cols[ cm ] > half ) cs = cols[ cm ] - half;
195  else cs = half - cols[ cm ];
196  if ( rs > cs ) dir = 1;
197  else if( cs > rs ) dir = 0;
198  else dir = rand() % 2; //tie break
199 
200  //don't split if one of the two parts will be empty
201  if( dir ) {
202  if( col_hi - cm == 0 || cm - col_lo == 0 ) {
203  build.push_back( HilbertTriplet< std::vector< Triplet< T > > >( blocki, blockj, input ) );
204  delete [] rows; /*FIXME inefficient memory handling, but this is initialisation only */
205  delete [] cols;
206  return 2;
207  }
208  } else
209  if( row_hi - rm == 0 || rm - row_lo == 0 ) {
210  build.push_back( HilbertTriplet< std::vector< Triplet< T > > >( blocki, blockj, input ) );
211  delete [] rows; /*FIXME inefficient memory handling, but this is initialisation only */
212  delete [] cols;
213  return 2;
214  }
215 
216  //split
217  std::vector< Triplet< T > > t1, t2;
218  it = input.begin();
219  for( ; it != input.end(); ++it ) {
220  if( dir ) { //column direction
221  //remember to offset indices to go from local to global
222  if( it->j() > cm+col_lo ) t2.push_back( *it );
223  else t1.push_back( *it );
224  } else {
225  if( it->i() > rm+row_lo ) t2.push_back( *it );
226  else t1.push_back( *it );
227  }
228  }
229 
230 // std::cout << "\t storing block 1 of size " << (!dir?rm+1:row) << " by " << (dir?cm+1:col) << ", " << rows[rm] << " nnz" << std::endl;
231 // std::cout << "\t storing block 2 of size " << (!dir?row_hi-rm-row_lo+1:row) << " by " << (dir?col_hi-cm-col_lo+1:col) << ", " << (input.size()-rows[rm]) << " nnz" << std::endl;
232 
233  //add to return vector
234  HilbertTriplet< std::vector< Triplet< T > > > p1( blocki, blockj, t1 );
235  const unsigned long int new_i = dir ? blocki | (1<<(MAX_DEPTH-depth-1)) : blocki;
236  const unsigned long int new_j =!dir ? blockj | (1<<(MAX_DEPTH-depth-1)) : blockj;
237  HilbertTriplet< std::vector< Triplet< T > > > p2( new_i, new_j, t2 );
238  if( t1.size() > 0 )
239  build.push_back( p1 );
240  if( t2.size() > 0 )
241  build.push_back( p2 );
242 
243  //free memory
244  delete [] rows;
245  delete [] cols;
246 
247  //exit flag
248  if( t1.size() == 0 || t2.size() == 0 ) return 0;
249  return 1;
250  }
251 
258  void bisect( std::vector< HilbertTriplet< std::vector< Triplet< T > > > > &build ) {
259  char flag = 1;
260  std::vector< char > go;
261  go.reserve( build.size() );
262  for( unsigned long int i=0; i<build.size(); ++i ) go.push_back( 1 );
263  unsigned char depth = 0;
264  while( flag && build.size() < MAX_BLOCKS && depth < MAX_DEPTH ) {
265  std::vector< HilbertTriplet< std::vector< Triplet< T > > > > replace;
266  std::vector< char > replace_go;
267  replace_go.reserve( 2*build.size() );
268  assert( build.size() == go.size() );
269  for( unsigned long int i=0; i<build.size(); i++ )
270  if( go[ i ] ) {
271  char ret = bisect( replace, build[i].value, build[i].i(), build[i].j(), depth );
272  if( ret == 1 ) {
273  replace_go.push_back( 1 );
274  replace_go.push_back( 1 );
275  } else
276  replace_go.push_back( 0 );
277  } else {
278  //just copy
279  replace.push_back( build[i] );
280  replace_go.push_back( 0 );
281  }
282  //check for stop condition
283  if( replace.size() == build.size() ) flag = 0; //no new blocks created; done
284  //replace
285  build = replace;
286  go = replace_go;
287 // std::cout << "\t --------------------" << std::endl;
288  depth++;
289  }
290  }
291 
298  std::vector< HilbertTriplet< std::vector< Triplet< T > > > > buildBisectionBlocks( std::vector< Triplet< T > > &input ) {
299 #ifndef NDEBUG
300  const unsigned long int nnz = input.size();
301 #endif
302 
303  //build output structure
304  std::vector< HilbertTriplet< std::vector< Triplet< T > > > > ret;
305  //put all nonzeroes in a giant block
306  ret.push_back( HilbertTriplet< std::vector< Triplet< T > > >( 0, 0, input ) );
307  //recursively bisect that block
308  bisect( ret );
309 
310  std::cout << "\t Bisection created " << ret.size() << " submatrices," << std::endl;
311 
312  //check total number of nonzeroes
313  unsigned long int sum = 0;
314  typename std::vector< HilbertTriplet< std::vector< Triplet< T > > > >::iterator it = ret.begin();
315  for( ; it != ret.end(); ++it ) {
316  sum += it->value.size();
317  }
318 
319  std::cout << "\t storing a total of " << sum << " nonzeroes." << std::endl;
320 
321  assert( nnz == sum );
322 
323  return ret;
324  }
325 
327  std::vector< HilbertTriplet< std::vector< Triplet< T > > > > buildBlocks( std::vector< Triplet< T > > &input ) {
328  const unsigned long int blockm = this->nor % BLOCK_M > 0 ? this->nor / BLOCK_M + 1 : this->nor / BLOCK_M;
329  const unsigned long int blockn = this->noc % BLOCK_N > 0 ? this->nor / BLOCK_N + 1 : this->nor / BLOCK_N;
330  const unsigned long int blocks = blockm * blockn;
331  std::cout << "\tMaking " << blockm << " by " << blockn << " submatrices" << std::endl;
332  std::cout << "\tyielding a total of " << blocks << " blocks. " << std::endl;
333  std::vector< HilbertTriplet< std::vector< Triplet< T > > > > ret;
334  for( unsigned long int i=0; i<blocks; i++ )
335  ret.push_back( HilbertTriplet< std::vector< Triplet< T > > >( i / blockn, i % blockn, std::vector< Triplet< T > >() ) );
336  typename std::vector< Triplet< T > >::iterator it = input.begin();
337  for( ; it != input.end(); ++it ) {
338  const unsigned long int blocki = it->i() / BLOCK_M;
339  const unsigned long int blockj = it->j() / BLOCK_N;
340  ret[ blockn * blocki + blockj ].value.push_back( *it );
341  }
342  return ret;
343  }
344 
346  bool cmp( HilbertTriplet< std::vector< Triplet< T > > > &left, HilbertTriplet< std::vector< Triplet< T > > > &right ) {
347 
348  if( left.i() == right.i() && left.j() == right.j() ) return true;
349 
350  const std::vector< unsigned long int > h_one = left.getHilbert();
351  const std::vector< unsigned long int > h_two = right.getHilbert();
352 
353  unsigned long int max = h_one.size();
354  bool max_is_one = true;
355  if ( h_two.size() < max ) { max = h_two.size(); max_is_one = false; }
356  for( unsigned long int i=0; i<max; i++ )
357  if( h_one[ i ] != h_two[ i ] )
358  return h_one[ i ] < h_two[ i ];
359 #ifdef _DEBUG
360  std::cout << "expand, ";
361 #endif
362  if ( max_is_one )
363  left.morePrecision( this->nor, this->noc );
364  else
365  right.morePrecision( this->nor, this->noc );
366 
367  return cmp( left, right );
368  }
369 
379  unsigned long int find( HilbertTriplet< std::vector< Triplet< T > > > &x, ULI &left, ULI &right, ULI &minexp ) {
380 #ifdef _DEBUG
381  std::cout << "left: " << left << ", right: " << right << std::endl;
382 #endif
383  if( hilbert[left].getHilbert().size() > minexp ) minexp = hilbert[left].getHilbert().size();
384  if( hilbert[right].getHilbert().size() > minexp ) minexp = hilbert[right].getHilbert().size();
385 
386  if ( left == right ) return left;
387  if ( left+1 == right ) return right;
388  if ( cmp( x, hilbert[ left ] ) ) return left;
389  if ( cmp( hilbert[ right ], x ) ) return right+1;
390 
391  ULI mid = static_cast< unsigned long int >( ( left + right ) / 2 );
392 #ifdef _DEBUG
393  std::cout << "mid: " << mid << std::endl;
394 #endif
395  if ( cmp( x, hilbert[ mid ] ) )
396  return find( x, left, mid, minexp );
397  else
398  return find( x, mid, right, minexp );
399  }
400 
401  public:
402 
404  virtual ~BlockHilbert() {
405  if( ds != NULL )
406  delete ds;
407  }
408 
411  bisection = 0;
412  }
413 
421  BlockHilbert( std::string file, T zero = 0, char bisect = 0 ) {
422  bisection = bisect;
423  this->loadFromFile( file, zero );
424  }
425 
434  BlockHilbert( std::vector< Triplet< T > >& input, LI m, LI n, T zero ) {
435  bisection = 0;
436  load( input, m, n, zero );
437  }
438 
439 
449  BlockHilbert( std::vector< Triplet< T > >& input, LI m, LI n, char bisect, T zero ) {
450  bisection = bisect;
451  load( input, m, n, zero );
452  }
453 
455  virtual void load( std::vector< Triplet< T > >& input, const LI m, const LI n, const T zero ) {
456  ds = NULL;
457  this->zero_element = 0;
458  this->nor = m;
459  this->noc = n;
460  this->nnz = input.size();
461 
462  std::cout << std::endl << "Loading into *experimental* block hilbert structure." << std::endl;
463 
464  if( bisection ) {
465  std::cout << "\tUsing bisection to create blocks" << std::endl;
466  hilbert = buildBisectionBlocks( input );
467  } else {
468  std::cout << "\tUsing fixed block sizes..." << std::endl;
469  hilbert = buildBlocks( input );
470  }
471 
472  std::cout << "\tGetting Hilbert coordinates" << std::endl;
473  typename std::vector< HilbertTriplet< std::vector< Triplet< T > > > >::iterator it = hilbert.begin();
474  for( ; it!=hilbert.end(); ++it )
475  it->calculateHilbertCoordinate();
476  std::cout << "\tUsing std::sort to get a Hilbert ordering on the sparse blocks" << std::endl;
478  std::sort( hilbert.begin(), hilbert.end(), compare );
479 
480 #ifdef _DEBUG
481  for( ULI i=0; i<this->nnz; i++ ) {
482  for( ULI j=0; j<hilbert[i].getHilbert().size(); j++ )
483  std::cout << hilbert[i].getHilbert()[j] << ",";
484  std::cout << std::endl;
485  }
486 #endif
487  //load into HBICRS
488  std::cout << "\tLoading into HBICRS..." << std::endl;
489  std::vector< std::vector< Triplet< T > > > hierarchy;
490  it = hilbert.begin();
491  for( ; it!=hilbert.end(); ++it ) hierarchy.push_back( it->value );
492  signed char *hierarchy_datatype = new signed char[ hierarchy.size() ];
493  for( unsigned long int i=0; i<hierarchy.size(); i++ ) hierarchy_datatype[i] = DS; //ICRS default
494  ds = new HBICRS< T >( hierarchy, hierarchy_datatype, this->nor, this->noc, 0 );
495  delete [] hierarchy_datatype;
496 
497  std::cout << "BlockHilbert structure ready!" << std::endl;
498  }
499 
501  virtual void getFirstIndexPair( LI &row, LI &col ) {
502  ds->getFirstIndexPair( row, col );
503  }
504 
512  virtual void zxa( const T* x, T* z ) {
513  ds->zxa( x, z );
514  }
515 
523  virtual void zax( const T* x, T* z ) {
524  ds->zax( x, z );
525  }
526 
527  virtual size_t bytesUsed() {
528  return ds->bytesUsed();
529  }
530 
531 };
532 
533 #endif
534 
virtual ~BlockHilbert()
Base deconstructor.
Definition: BlockHilbert.hpp:404
LI nnz
Number of non-zeros.
Definition: SparseMatrix.hpp:58
BlockHilbert(std::string file, T zero=0, char bisect=0)
Base constructor.
Definition: BlockHilbert.hpp:421
static const ULI BLOCK_M
Minimum number of rows in a block.
Definition: BlockHilbert.hpp:62
Hilbert-coordinate-aware triplet.
Definition: HilbertTriplet.hpp:46
BlockHilbert()
Base constructor.
Definition: BlockHilbert.hpp:410
static const signed char DS
Which data structure to use for each block.
Definition: BlockHilbert.hpp:74
virtual unsigned long int m()
Queries the number of rows this matrix contains.
Definition: SparseMatrix.hpp:107
char bisection
Whether we use fixed block grid or a bisection-based grid.
Definition: BlockHilbert.hpp:59
char bisect(std::vector< HilbertTriplet< std::vector< Triplet< T > > > > &build, std::vector< Triplet< T > > &input, const unsigned long int blocki, const unsigned long int blockj, const unsigned char depth)
Recursive bisection, leaf case.
Definition: BlockHilbert.hpp:95
Class-comparator of HilbertTriplet.
Definition: HilbertTripletCompare.hpp:41
virtual void zax(const _t_value *__restrict__ x_p, _t_value *__restrict__ y_p)
Calculates y=Ax, but does not allocate y itself.
Definition: HBICRS.hpp:379
virtual size_t bytesUsed()
Function to query the amount of storage required by this sparse matrix.
Definition: BlockHilbert.hpp:527
The Block Hilbert triplet scheme.
Definition: BlockHilbert.hpp:52
virtual void zxa(const T *x, T *z)
Calculates z=xA.
Definition: BlockHilbert.hpp:512
std::vector< HilbertTriplet< std::vector< Triplet< T > > > > hilbert
Vector storing the block indices and their Hilbert coordinates.
Definition: BlockHilbert.hpp:80
std::vector< HilbertTriplet< std::vector< Triplet< T > > > > buildBlocks(std::vector< Triplet< T > > &input)
Builds block array by ordering them in fixed-size sparse submatrices.
Definition: BlockHilbert.hpp:327
ULI minexp
Minimum number of expansions.
Definition: BlockHilbert.hpp:77
bool cmp(HilbertTriplet< std::vector< Triplet< T > > > &left, HilbertTriplet< std::vector< Triplet< T > > > &right)
HilbertCoordinate comparison function.
Definition: BlockHilbert.hpp:346
void loadFromFile(const std::string file, const T zero=0)
Function which loads a matrix from a matrix market file.
Definition: SparseMatrix.hpp:89
virtual void zax(const T *x, T *z)
Calculates z=Ax.
Definition: BlockHilbert.hpp:523
Interface common to all sparse matrix storage schemes.
Definition: SparseMatrix.hpp:46
LI noc
Number of columns.
Definition: SparseMatrix.hpp:55
BlockHilbert(std::vector< Triplet< T > > &input, LI m, LI n, char bisect, T zero)
Base constructor.
Definition: BlockHilbert.hpp:449
static const ULI MAX_DEPTH
Maximum depth of bisection.
Definition: BlockHilbert.hpp:71
unsigned long int find(HilbertTriplet< std::vector< Triplet< T > > > &x, ULI &left, ULI &right, ULI &minexp)
Binary search for finding a given triplet in a given range.
Definition: BlockHilbert.hpp:379
virtual void getFirstIndexPair(LI &row, LI &col)
Returns the first nonzero index, per reference.
Definition: BlockHilbert.hpp:501
HBICRS< T > * ds
The actual data structure.
Definition: BlockHilbert.hpp:83
static const ULI BLOCK_N
Minimum number of columns in a block.
Definition: BlockHilbert.hpp:65
LI nor
Number of rows.
Definition: SparseMatrix.hpp:52
virtual void zxa(const _t_value *__restrict__ x_p, _t_value *__restrict__ y_p)
Calculates y=xA, but does not allocate y itself.
Definition: HBICRS.hpp:320
T zero_element
The element considered to be zero.
Definition: SparseMatrix.hpp:63
void bisect(std::vector< HilbertTriplet< std::vector< Triplet< T > > > > &build)
Automatically performs recursive bisection on the input nonzeroes to achieve sparse blocking...
Definition: BlockHilbert.hpp:258
static const ULI MAX_BLOCKS
Maximum number of blocks.
Definition: BlockHilbert.hpp:68
virtual unsigned long int n()
Queries the number of columns this matrix contains.
Definition: SparseMatrix.hpp:115
A single triplet value.
Definition: Triplet.hpp:52
std::vector< HilbertTriplet< std::vector< Triplet< T > > > > buildBisectionBlocks(std::vector< Triplet< T > > &input)
Builds block array by bisection.
Definition: BlockHilbert.hpp:298
virtual void load(std::vector< Triplet< T > > &input, const LI m, const LI n, const T zero)
Definition: BlockHilbert.hpp:455
BlockHilbert(std::vector< Triplet< T > > &input, LI m, LI n, T zero)
Base constructor.
Definition: BlockHilbert.hpp:434
virtual size_t bytesUsed()
Function to query the amount of storage required by this sparse matrix.
Definition: HBICRS.hpp:434
virtual void getFirstIndexPair(LI &i, LI &j)
Definition: HBICRS.hpp:310