Reference documentation for deal.II version 9.0.0
chunk_sparsity_pattern.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2017 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 
17 #include <deal.II/lac/chunk_sparsity_pattern.h>
18 #include <deal.II/lac/dynamic_sparsity_pattern.h>
19 #include <deal.II/lac/full_matrix.h>
20 
21 
22 DEAL_II_NAMESPACE_OPEN
23 
24 
26 {
27  reinit (0,0,0,1);
28 }
29 
30 
31 
33  :
34  Subscriptor(),
35  chunk_size (s.chunk_size),
36  sparsity_pattern(s.sparsity_pattern)
37 {
38  Assert (s.rows==0 && s.cols==0,
39  ExcMessage("This constructor can only be called if the provided argument "
40  "is the sparsity pattern for an empty matrix. This constructor can "
41  "not be used to copy-construct a non-empty sparsity pattern."));
42 
43  reinit (0,0,0,chunk_size);
44 }
45 
46 
47 
49  const size_type n,
50  const size_type max_per_row,
51  const size_type chunk_size)
52 {
54 
55  reinit (m,n,max_per_row, chunk_size);
56 }
57 
58 
59 
61  const size_type m,
62  const size_type n,
63  const std::vector<size_type> &row_lengths,
64  const size_type chunk_size)
65 {
67 
68  reinit (m, n, row_lengths, chunk_size);
69 }
70 
71 
72 
74  const size_type max_per_row,
75  const size_type chunk_size)
76 {
77  reinit (n, n, max_per_row, chunk_size);
78 }
79 
80 
81 
83  const size_type m,
84  const std::vector<size_type > &row_lengths,
85  const size_type chunk_size)
86 {
88 
89  reinit (m, m, row_lengths, chunk_size);
90 }
91 
92 
93 
96 {
97  Assert (s.rows==0 && s.cols==0,
98  ExcMessage("This operator can only be called if the provided argument "
99  "is the sparsity pattern for an empty matrix. This operator can "
100  "not be used to copy a non-empty sparsity pattern."));
101 
102  Assert (rows==0 && cols==0,
103  ExcMessage("This operator can only be called if the current object is "
104  "empty."));
105 
106  // perform the checks in the underlying object as well
108 
109  return *this;
110 }
111 
112 
113 
114 void
116  const size_type n,
117  const size_type max_per_row,
118  const size_type chunk_size)
119 {
121 
122  // simply map this function to the other @p{reinit} function
123  const std::vector<size_type> row_lengths (m, max_per_row);
124  reinit (m, n, row_lengths, chunk_size);
125 }
126 
127 
128 
129 void
131  const size_type m,
132  const size_type n,
133  const VectorSlice<const std::vector<size_type> > &row_lengths,
134  const size_type chunk_size)
135 {
136  Assert (row_lengths.size() == m, ExcInvalidNumber (m));
138 
139  rows = m;
140  cols = n;
141 
142  this->chunk_size = chunk_size;
143 
144  // pass down to the necessary information to the underlying object. we need
145  // to calculate how many chunks we need: we need to round up (m/chunk_size)
146  // and (n/chunk_size). rounding up in integer arithmetic equals
147  // ((m+chunk_size-1)/chunk_size):
148  const size_type m_chunks = (m+chunk_size-1) / chunk_size,
149  n_chunks = (n+chunk_size-1) / chunk_size;
150 
151  // compute the maximum number of chunks in each row. the passed array
152  // denotes the number of entries in each row of the big matrix -- in the
153  // worst case, these are all in independent chunks, so we have to calculate
154  // it as follows (as an example: let chunk_size==2, row_lengths={2,2,...},
155  // and entries in row zero at columns {0,2} and for row one at {4,6} -->
156  // we'll need 4 chunks for the first chunk row!) :
157  std::vector<unsigned int> chunk_row_lengths (m_chunks, 0);
158  for (size_type i=0; i<m; ++i)
159  chunk_row_lengths[i/chunk_size] += row_lengths[i];
160 
161  // for the case that the reduced sparsity pattern optimizes the diagonal but
162  // the actual sparsity pattern does not, need to take one more entry in the
163  // row to fit the user-required entry
164  if (m != n && m_chunks == n_chunks)
165  for (unsigned int i=0; i<m_chunks; ++i)
166  ++chunk_row_lengths[i];
167 
168  sparsity_pattern.reinit (m_chunks,
169  n_chunks,
170  chunk_row_lengths);
171 }
172 
173 
174 
175 void
177 {
179 }
180 
181 
182 
183 template <typename SparsityPatternType>
184 void
185 ChunkSparsityPattern::copy_from (const SparsityPatternType &dsp,
186  const size_type chunk_size)
187 {
189  this->chunk_size = chunk_size;
190  rows = dsp.n_rows();
191  cols = dsp.n_cols();
192 
193  // simple case: just use the given sparsity pattern
194  if (chunk_size == 1)
195  {
197  return;
198  }
199 
200  // create a temporary compressed sparsity pattern that collects all entries
201  // from the input sparsity pattern and then initialize the underlying small
202  // sparsity pattern
203  const size_type m_chunks = (dsp.n_rows()+chunk_size-1) / chunk_size,
204  n_chunks = (dsp.n_cols()+chunk_size-1) / chunk_size;
205  DynamicSparsityPattern temporary_sp(m_chunks, n_chunks);
206 
207  for (size_type row = 0; row<dsp.n_rows(); ++row)
208  {
209  const size_type reduced_row = row/chunk_size;
210 
211  // TODO: This could be made more efficient if we cached the
212  // previous column and only called add() if the previous and the
213  // current column lead to different chunk columns
214  for (typename SparsityPatternType::iterator col_num = dsp.begin(row);
215  col_num != dsp.end(row); ++col_num)
216  temporary_sp.add (reduced_row, col_num->column()/chunk_size);
217  }
218 
219  sparsity_pattern.copy_from (temporary_sp);
220 }
221 
222 
223 
224 
225 template <typename number>
227  const size_type chunk_size)
228 {
230 
231  // count number of entries per row, then initialize the underlying sparsity
232  // pattern. remember to also allocate space for the diagonal entry (if that
233  // hasn't happened yet) if m==n since we always allocate that for diagonal
234  // matrices
235  std::vector<size_type> entries_per_row (matrix.m(), 0);
236  for (size_type row=0; row<matrix.m(); ++row)
237  {
238  for (size_type col=0; col<matrix.n(); ++col)
239  if (matrix(row,col) != 0)
240  ++entries_per_row[row];
241 
242  if ((matrix.m() == matrix.n())
243  &&
244  (matrix(row,row) == 0))
245  ++entries_per_row[row];
246  }
247 
248  reinit (matrix.m(), matrix.n(),
249  entries_per_row,
250  chunk_size);
251 
252  // then actually fill it
253  for (size_type row=0; row<matrix.m(); ++row)
254  for (size_type col=0; col<matrix.n(); ++col)
255  if (matrix(row,col) != 0)
256  add (row,col);
257 
258  // finally compress
259  compress ();
260 }
261 
262 
263 
264 void
266  const size_type m,
267  const size_type n,
268  const std::vector<size_type> &row_lengths,
269  const size_type chunk_size)
270 {
272 
273  reinit(m, n, make_slice(row_lengths), chunk_size);
274 }
275 
276 
277 
278 namespace internal
279 {
280  namespace
281  {
282  template <typename SparsityPatternType>
283  void copy_sparsity (const SparsityPatternType &src,
284  SparsityPattern &dst)
285  {
286  dst.copy_from(src);
287  }
288 
289  void copy_sparsity (const SparsityPattern &src,
290  SparsityPattern &dst)
291  {
292  dst = src;
293  }
294  }
295 }
296 
297 
298 
299 template <typename Sparsity>
300 void
302 (const unsigned int m,
303  const unsigned int n,
304  const Sparsity &sparsity_pattern_for_chunks,
305  const unsigned int chunk_size_in,
306  const bool)
307 {
308  Assert (m > (sparsity_pattern_for_chunks.n_rows()-1) * chunk_size_in &&
309  m <= sparsity_pattern_for_chunks.n_rows() * chunk_size_in,
310  ExcMessage("Number of rows m is not compatible with chunk size "
311  "and number of rows in sparsity pattern for the chunks."));
312  Assert (n > (sparsity_pattern_for_chunks.n_cols()-1) * chunk_size_in &&
313  n <= sparsity_pattern_for_chunks.n_cols() * chunk_size_in,
314  ExcMessage("Number of columns m is not compatible with chunk size "
315  "and number of columns in sparsity pattern for the chunks."));
316 
317  internal::copy_sparsity(sparsity_pattern_for_chunks, sparsity_pattern);
318  chunk_size = chunk_size_in;
319  rows = m;
320  cols = n;
321 }
322 
323 
324 
325 bool
327 {
328  return sparsity_pattern.empty();
329 }
330 
331 
332 
335 {
337 }
338 
339 
340 
341 void
343  const size_type j)
344 {
347 
349 }
350 
351 
352 bool
354  const size_type j) const
355 {
356  Assert (i<rows, ExcIndexRange(i,0,rows));
357  Assert (j<cols, ExcIndexRange(j,0,cols));
358 
360  j/chunk_size);
361 }
362 
363 
364 
365 void
367 {
368  // matrix must be square. note that the for some matrix sizes, the current
369  // sparsity pattern may not be square even if the underlying sparsity
370  // pattern is (e.g. a 10x11 matrix with chunk_size 4)
372 
374 }
375 
376 
377 
380 {
381  Assert (i<rows, ExcIndexRange(i,0,rows));
382 
383  // find out if we did padding and if this row is affected by it
384  if (n_cols() % chunk_size == 0)
386  else
387  // if columns don't align, then just iterate over all chunks and see
388  // what this leads to
389  {
392  unsigned int n = 0;
393  for ( ; p != end; ++p)
394  if (p->column() != sparsity_pattern.n_cols() - 1)
395  n += chunk_size;
396  else
397  n += (n_cols() % chunk_size);
398  return n;
399  }
400 }
401 
402 
403 
406 {
407  if ((n_rows() % chunk_size == 0)
408  &&
409  (n_cols() % chunk_size == 0))
411  chunk_size *
412  chunk_size);
413  else
414  // some of the chunks reach beyond the extent of this matrix. this
415  // requires a somewhat more complicated computations, in particular if the
416  // columns don't align
417  {
418  if ((n_rows() % chunk_size != 0)
419  &&
420  (n_cols() % chunk_size == 0))
421  {
422  // columns align with chunks, but not rows
424  chunk_size *
425  chunk_size;
426  n -= (sparsity_pattern.n_rows() * chunk_size - n_rows()) *
428  chunk_size;
429  return n;
430  }
431 
432  else
433  {
434  // if columns don't align, then just iterate over all chunks and see
435  // what this leads to. follow the advice in the documentation of the
436  // sparsity pattern iterators to do the loop over individual rows,
437  // rather than all elements
438  size_type n = 0;
439 
440  for (size_type row = 0; row < sparsity_pattern.n_rows(); ++row)
441  {
443  for (; p!=sparsity_pattern.end(row); ++p)
444  if ((row != sparsity_pattern.n_rows() - 1)
445  &&
446  (p->column() != sparsity_pattern.n_cols() - 1))
447  n += chunk_size * chunk_size;
448  else if ((row == sparsity_pattern.n_rows() - 1)
449  &&
450  (p->column() != sparsity_pattern.n_cols() - 1))
451  // last chunk row, but not last chunk column. only a smaller
452  // number (n_rows % chunk_size) of rows actually exist
453  n += (n_rows() % chunk_size) * chunk_size;
454  else if ((row != sparsity_pattern.n_rows() - 1)
455  &&
456  (p->column() == sparsity_pattern.n_cols() - 1))
457  // last chunk column, but not row
458  n += (n_cols() % chunk_size) * chunk_size;
459  else
460  // bottom right chunk
461  n += (n_cols() % chunk_size) *
462  (n_rows() % chunk_size);
463  }
464 
465  return n;
466  }
467  }
468 }
469 
470 
471 
472 void
473 ChunkSparsityPattern::print (std::ostream &out) const
474 {
475  Assert ((sparsity_pattern.rowstart!=nullptr) && (sparsity_pattern.colnums!=nullptr),
476  ExcEmptyObject());
477 
478  AssertThrow (out, ExcIO());
479 
480  for (size_type i=0; i<sparsity_pattern.rows; ++i)
481  for (size_type d=0;
482  (d<chunk_size) && (i*chunk_size + d < n_rows());
483  ++d)
484  {
485  out << '[' << i *chunk_size+d;
487  j<sparsity_pattern.rowstart[i+1]; ++j)
489  for (size_type e=0;
490  ((e<chunk_size) &&
492  ++e)
493  out << ',' << sparsity_pattern.colnums[j]*chunk_size+e;
494  out << ']' << std::endl;
495  }
496 
497  AssertThrow (out, ExcIO());
498 }
499 
500 
501 
502 void
503 ChunkSparsityPattern::print_gnuplot (std::ostream &out) const
504 {
505  Assert ((sparsity_pattern.rowstart!=nullptr) &&
506  (sparsity_pattern.colnums!=nullptr), ExcEmptyObject());
507 
508  AssertThrow (out, ExcIO());
509 
510  // for each entry in the underlying sparsity pattern, repeat everything
511  // chunk_size x chunk_size times
512  for (size_type i=0; i<sparsity_pattern.rows; ++i)
514  j<sparsity_pattern.rowstart[i+1]; ++j)
516  for (size_type d=0;
517  ((d<chunk_size) &&
519  ++d)
520  for (size_type e=0;
521  (e<chunk_size) && (i*chunk_size + e < n_rows());
522  ++e)
523  // while matrix entries are usually written (i,j), with i vertical
524  // and j horizontal, gnuplot output is x-y, that is we have to
525  // exchange the order of output
526  out << sparsity_pattern.colnums[j]*chunk_size+d << " "
527  << -static_cast<signed int>(i*chunk_size+e)
528  << std::endl;
529 
530  AssertThrow (out, ExcIO());
531 }
532 
533 
534 
537 {
538  // calculate the bandwidth from that of the underlying sparsity
539  // pattern. note that even if the bandwidth of that is zero, then the
540  // bandwidth of the chunky pattern is chunk_size-1, if it is 1 then the
541  // chunky pattern has chunk_size+(chunk_size-1), etc
542  //
543  // we'll cut it off at max(n(),m())
544  return std::min (sparsity_pattern.bandwidth()*chunk_size
545  + (chunk_size-1),
546  std::max(n_rows(), n_cols()));
547 }
548 
549 
550 
551 bool
553 {
554  if (chunk_size == 1)
556  else
557  return false;
558 }
559 
560 
561 
562 void
563 ChunkSparsityPattern::block_write (std::ostream &out) const
564 {
565  AssertThrow (out, ExcIO());
566 
567  // first the simple objects, bracketed in [...]
568  out << '['
569  << rows << ' '
570  << cols << ' '
571  << chunk_size << ' '
572  << "][";
573  // then the underlying sparsity pattern
575  out << ']';
576 
577  AssertThrow (out, ExcIO());
578 }
579 
580 
581 
582 void
584 {
585  AssertThrow (in, ExcIO());
586 
587  char c;
588 
589  // first read in simple data
590  in >> c;
591  AssertThrow (c == '[', ExcIO());
592  in >> rows
593  >> cols
594  >> chunk_size;
595 
596  in >> c;
597  AssertThrow (c == ']', ExcIO());
598  in >> c;
599  AssertThrow (c == '[', ExcIO());
600 
601  // then read the underlying sparsity pattern
603 
604  in >> c;
605  AssertThrow (c == ']', ExcIO());
606 }
607 
608 
609 
610 std::size_t
612 {
613  return (sizeof(*this) +
615 }
616 
617 
618 
619 // explicit instantiations
620 template
621 void ChunkSparsityPattern::copy_from<DynamicSparsityPattern> (const DynamicSparsityPattern &,
622  const size_type);
623 template
624 void ChunkSparsityPattern::create_from<SparsityPattern>
625 (const unsigned int,
626  const unsigned int,
627  const SparsityPattern &,
628  const unsigned int,
629  const bool);
630 template
631 void ChunkSparsityPattern::create_from<DynamicSparsityPattern>
632 (const unsigned int,
633  const unsigned int,
634  const DynamicSparsityPattern &,
635  const unsigned int,
636  const bool);
637 template
638 void ChunkSparsityPattern::copy_from<float> (const FullMatrix<float> &,
639  const size_type);
640 template
641 void ChunkSparsityPattern::copy_from<double> (const FullMatrix<double> &,
642  const size_type);
643 
644 DEAL_II_NAMESPACE_CLOSE
void block_write(std::ostream &out) const
void block_write(std::ostream &out) const
void copy_from(const size_type n_rows, const size_type n_cols, const ForwardIterator begin, const ForwardIterator end, const size_type chunk_size)
std::size_t memory_consumption() const
static ::ExceptionBase & ExcEmptyObject()
std::size_t n_nonzero_elements() const
static ::ExceptionBase & ExcInvalidNumber(int arg1)
static ::ExceptionBase & ExcInvalidIndex(int arg1, int arg2)
static ::ExceptionBase & ExcIO()
static const size_type invalid_entry
void add(const size_type i, const size_type j)
iterator end() const
std::size_t memory_consumption() const
void block_read(std::istream &in)
iterator end() const
bool exists(const size_type i, const size_type j) const
bool exists(const size_type i, const size_type j) const
bool empty() const
#define AssertThrow(cond, exc)
Definition: exceptions.h:1221
void add(const size_type i, const size_type j)
static ::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
size_type n_cols() const
SparsityPattern sparsity_pattern
size_type n_rows() const
size_type bandwidth() const
std::unique_ptr< size_type[]> colnums
size_type n_cols() const
static ::ExceptionBase & ExcMessage(std::string arg1)
void add(const size_type i, const size_type j)
#define Assert(cond, exc)
Definition: exceptions.h:1142
size_type row_length(const size_type row) const
size_type n_rows() const
size_type max_entries_per_row() const
size_type n_nonzero_elements() const
void copy_from(const size_type n_rows, const size_type n_cols, const ForwardIterator begin, const ForwardIterator end)
ChunkSparsityPattern & operator=(const ChunkSparsityPattern &)
void reinit(const size_type m, const size_type n, const unsigned int max_per_row)
types::global_dof_index size_type
static ::ExceptionBase & ExcNotQuadratic()
bool stores_only_added_elements() const
std::unique_ptr< std::size_t[]> rowstart
void block_read(std::istream &in)
iterator begin() const
void print(std::ostream &out) const
void create_from(const unsigned int m, const unsigned int n, const Sparsity &sparsity_pattern_for_chunks, const unsigned int chunk_size, const bool optimize_diagonal=true)
void print_gnuplot(std::ostream &out) const
void reinit(const size_type m, const size_type n, const size_type max_per_row, const size_type chunk_size)
unsigned int row_length(const size_type row) const
size_type max_entries_per_row() const