Reference documentation for deal.II version 9.1.0-pre
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.md at
12 // the top level directory of deal.II.
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  : Subscriptor()
36 {
37  Assert(s.rows == 0 && s.cols == 0,
38  ExcMessage(
39  "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 {
53  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
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 {
66  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
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 {
87  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
88 
89  reinit(m, m, row_lengths, chunk_size);
90 }
91 
92 
93 
96 {
97  Assert(s.rows == 0 && s.cols == 0,
98  ExcMessage(
99  "This operator can only be called if the provided argument "
100  "is the sparsity pattern for an empty matrix. This operator can "
101  "not be used to copy a non-empty sparsity pattern."));
102 
103  Assert(rows == 0 && cols == 0,
104  ExcMessage("This operator can only be called if the current object is "
105  "empty."));
106 
107  // perform the checks in the underlying object as well
109 
110  return *this;
111 }
112 
113 
114 
115 void
117  const size_type n,
118  const size_type max_per_row,
119  const size_type chunk_size)
120 {
121  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
122 
123  // simply map this function to the other @p{reinit} function
124  const std::vector<size_type> row_lengths(m, max_per_row);
125  reinit(m, n, row_lengths, chunk_size);
126 }
127 
128 
129 
130 void
132  const size_type m,
133  const size_type n,
134  const VectorSlice<const std::vector<size_type>> &row_lengths,
135  const size_type chunk_size)
136 {
137  Assert(row_lengths.size() == m, ExcInvalidNumber(m));
138  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
139 
140  rows = m;
141  cols = n;
142 
143  this->chunk_size = chunk_size;
144 
145  // pass down to the necessary information to the underlying object. we need
146  // to calculate how many chunks we need: we need to round up (m/chunk_size)
147  // and (n/chunk_size). rounding up in integer arithmetic equals
148  // ((m+chunk_size-1)/chunk_size):
149  const size_type m_chunks = (m + chunk_size - 1) / chunk_size,
150  n_chunks = (n + chunk_size - 1) / chunk_size;
151 
152  // compute the maximum number of chunks in each row. the passed array
153  // denotes the number of entries in each row of the big matrix -- in the
154  // worst case, these are all in independent chunks, so we have to calculate
155  // it as follows (as an example: let chunk_size==2, row_lengths={2,2,...},
156  // and entries in row zero at columns {0,2} and for row one at {4,6} -->
157  // we'll need 4 chunks for the first chunk row!) :
158  std::vector<unsigned int> chunk_row_lengths(m_chunks, 0);
159  for (size_type i = 0; i < m; ++i)
160  chunk_row_lengths[i / chunk_size] += row_lengths[i];
161 
162  // for the case that the reduced sparsity pattern optimizes the diagonal but
163  // the actual sparsity pattern does not, need to take one more entry in the
164  // row to fit the user-required entry
165  if (m != n && m_chunks == n_chunks)
166  for (unsigned int i = 0; i < m_chunks; ++i)
167  ++chunk_row_lengths[i];
168 
169  sparsity_pattern.reinit(m_chunks, n_chunks, chunk_row_lengths);
170 }
171 
172 
173 
174 void
176 {
178 }
179 
180 
181 
182 template <typename SparsityPatternType>
183 void
184 ChunkSparsityPattern::copy_from(const SparsityPatternType &dsp,
185  const size_type chunk_size)
186 {
187  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
188  this->chunk_size = chunk_size;
189  rows = dsp.n_rows();
190  cols = dsp.n_cols();
191 
192  // simple case: just use the given sparsity pattern
193  if (chunk_size == 1)
194  {
196  return;
197  }
198 
199  // create a temporary compressed sparsity pattern that collects all entries
200  // from the input sparsity pattern and then initialize the underlying small
201  // sparsity pattern
202  const size_type m_chunks = (dsp.n_rows() + chunk_size - 1) / chunk_size,
203  n_chunks = (dsp.n_cols() + chunk_size - 1) / chunk_size;
204  DynamicSparsityPattern temporary_sp(m_chunks, n_chunks);
205 
206  for (size_type row = 0; row < dsp.n_rows(); ++row)
207  {
208  const size_type reduced_row = row / chunk_size;
209 
210  // TODO: This could be made more efficient if we cached the
211  // previous column and only called add() if the previous and the
212  // current column lead to different chunk columns
213  for (typename SparsityPatternType::iterator col_num = dsp.begin(row);
214  col_num != dsp.end(row);
215  ++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 template <typename number>
225 void
227  const size_type chunk_size)
228 {
229  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
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()) && (matrix(row, row) == 0))
243  ++entries_per_row[row];
244  }
245 
246  reinit(matrix.m(), matrix.n(), entries_per_row, chunk_size);
247 
248  // then actually fill it
249  for (size_type row = 0; row < matrix.m(); ++row)
250  for (size_type col = 0; col < matrix.n(); ++col)
251  if (matrix(row, col) != 0)
252  add(row, col);
253 
254  // finally compress
255  compress();
256 }
257 
258 
259 
260 void
262  const size_type n,
263  const std::vector<size_type> &row_lengths,
264  const size_type chunk_size)
265 {
266  Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
267 
268  reinit(m, n, make_slice(row_lengths), chunk_size);
269 }
270 
271 
272 
273 namespace internal
274 {
275  namespace
276  {
277  template <typename SparsityPatternType>
278  void
279  copy_sparsity(const SparsityPatternType &src, SparsityPattern &dst)
280  {
281  dst.copy_from(src);
282  }
283 
284  void
285  copy_sparsity(const SparsityPattern &src, SparsityPattern &dst)
286  {
287  dst = src;
288  }
289  } // namespace
290 } // namespace internal
291 
292 
293 
294 template <typename Sparsity>
295 void
297  const unsigned int n,
298  const Sparsity &sparsity_pattern_for_chunks,
299  const unsigned int chunk_size_in,
300  const bool)
301 {
302  Assert(m > (sparsity_pattern_for_chunks.n_rows() - 1) * chunk_size_in &&
303  m <= sparsity_pattern_for_chunks.n_rows() * chunk_size_in,
304  ExcMessage("Number of rows m is not compatible with chunk size "
305  "and number of rows in sparsity pattern for the chunks."));
306  Assert(n > (sparsity_pattern_for_chunks.n_cols() - 1) * chunk_size_in &&
307  n <= sparsity_pattern_for_chunks.n_cols() * chunk_size_in,
308  ExcMessage(
309  "Number of columns m is not compatible with chunk size "
310  "and number of columns in sparsity pattern for the chunks."));
311 
312  internal::copy_sparsity(sparsity_pattern_for_chunks, sparsity_pattern);
313  chunk_size = chunk_size_in;
314  rows = m;
315  cols = n;
316 }
317 
318 
319 
320 bool
322 {
323  return sparsity_pattern.empty();
324 }
325 
326 
327 
330 {
332 }
333 
334 
335 
336 void
338 {
339  Assert(i < rows, ExcInvalidIndex(i, rows));
340  Assert(j < cols, ExcInvalidIndex(j, cols));
341 
343 }
344 
345 
346 bool
348 {
349  Assert(i < rows, ExcIndexRange(i, 0, rows));
350  Assert(j < cols, ExcIndexRange(j, 0, cols));
351 
352  return sparsity_pattern.exists(i / chunk_size, j / chunk_size);
353 }
354 
355 
356 
357 void
359 {
360  // matrix must be square. note that the for some matrix sizes, the current
361  // sparsity pattern may not be square even if the underlying sparsity
362  // pattern is (e.g. a 10x11 matrix with chunk_size 4)
364 
366 }
367 
368 
369 
372 {
373  Assert(i < rows, ExcIndexRange(i, 0, rows));
374 
375  // find out if we did padding and if this row is affected by it
376  if (n_cols() % chunk_size == 0)
378  else
379  // if columns don't align, then just iterate over all chunks and see
380  // what this leads to
381  {
384  end =
386  unsigned int n = 0;
387  for (; p != end; ++p)
388  if (p->column() != sparsity_pattern.n_cols() - 1)
389  n += chunk_size;
390  else
391  n += (n_cols() % chunk_size);
392  return n;
393  }
394 }
395 
396 
397 
400 {
401  if ((n_rows() % chunk_size == 0) && (n_cols() % chunk_size == 0))
403  else
404  // some of the chunks reach beyond the extent of this matrix. this
405  // requires a somewhat more complicated computations, in particular if the
406  // columns don't align
407  {
408  if ((n_rows() % chunk_size != 0) && (n_cols() % chunk_size == 0))
409  {
410  // columns align with chunks, but not rows
411  size_type n =
413  n -= (sparsity_pattern.n_rows() * chunk_size - n_rows()) *
415  chunk_size;
416  return n;
417  }
418 
419  else
420  {
421  // if columns don't align, then just iterate over all chunks and see
422  // what this leads to. follow the advice in the documentation of the
423  // sparsity pattern iterators to do the loop over individual rows,
424  // rather than all elements
425  size_type n = 0;
426 
427  for (size_type row = 0; row < sparsity_pattern.n_rows(); ++row)
428  {
430  for (; p != sparsity_pattern.end(row); ++p)
431  if ((row != sparsity_pattern.n_rows() - 1) &&
432  (p->column() != sparsity_pattern.n_cols() - 1))
433  n += chunk_size * chunk_size;
434  else if ((row == sparsity_pattern.n_rows() - 1) &&
435  (p->column() != sparsity_pattern.n_cols() - 1))
436  // last chunk row, but not last chunk column. only a smaller
437  // number (n_rows % chunk_size) of rows actually exist
438  n += (n_rows() % chunk_size) * chunk_size;
439  else if ((row != sparsity_pattern.n_rows() - 1) &&
440  (p->column() == sparsity_pattern.n_cols() - 1))
441  // last chunk column, but not row
442  n += (n_cols() % chunk_size) * chunk_size;
443  else
444  // bottom right chunk
445  n += (n_cols() % chunk_size) * (n_rows() % chunk_size);
446  }
447 
448  return n;
449  }
450  }
451 }
452 
453 
454 
455 void
456 ChunkSparsityPattern::print(std::ostream &out) const
457 {
458  Assert((sparsity_pattern.rowstart != nullptr) &&
459  (sparsity_pattern.colnums != nullptr),
460  ExcEmptyObject());
461 
462  AssertThrow(out, ExcIO());
463 
464  for (size_type i = 0; i < sparsity_pattern.rows; ++i)
465  for (size_type d = 0; (d < chunk_size) && (i * chunk_size + d < n_rows());
466  ++d)
467  {
468  out << '[' << i * chunk_size + d;
469  for (size_type j = sparsity_pattern.rowstart[i];
470  j < sparsity_pattern.rowstart[i + 1];
471  ++j)
473  for (size_type e = 0;
474  ((e < chunk_size) &&
475  (sparsity_pattern.colnums[j] * chunk_size + e < n_cols()));
476  ++e)
477  out << ',' << sparsity_pattern.colnums[j] * chunk_size + e;
478  out << ']' << std::endl;
479  }
480 
481  AssertThrow(out, ExcIO());
482 }
483 
484 
485 
486 void
487 ChunkSparsityPattern::print_gnuplot(std::ostream &out) const
488 {
489  Assert((sparsity_pattern.rowstart != nullptr) &&
490  (sparsity_pattern.colnums != nullptr),
491  ExcEmptyObject());
492 
493  AssertThrow(out, ExcIO());
494 
495  // for each entry in the underlying sparsity pattern, repeat everything
496  // chunk_size x chunk_size times
497  for (size_type i = 0; i < sparsity_pattern.rows; ++i)
498  for (size_type j = sparsity_pattern.rowstart[i];
499  j < sparsity_pattern.rowstart[i + 1];
500  ++j)
502  for (size_type d = 0;
503  ((d < chunk_size) &&
504  (sparsity_pattern.colnums[j] * chunk_size + d < n_cols()));
505  ++d)
506  for (size_type e = 0;
507  (e < chunk_size) && (i * chunk_size + e < n_rows());
508  ++e)
509  // while matrix entries are usually written (i,j), with i vertical
510  // and j horizontal, gnuplot output is x-y, that is we have to
511  // exchange the order of output
512  out << sparsity_pattern.colnums[j] * chunk_size + d << " "
513  << -static_cast<signed int>(i * chunk_size + e) << std::endl;
514 
515  AssertThrow(out, ExcIO());
516 }
517 
518 
519 
522 {
523  // calculate the bandwidth from that of the underlying sparsity
524  // pattern. note that even if the bandwidth of that is zero, then the
525  // bandwidth of the chunky pattern is chunk_size-1, if it is 1 then the
526  // chunky pattern has chunk_size+(chunk_size-1), etc
527  //
528  // we'll cut it off at max(n(),m())
529  return std::min(sparsity_pattern.bandwidth() * chunk_size + (chunk_size - 1),
530  std::max(n_rows(), n_cols()));
531 }
532 
533 
534 
535 bool
537 {
538  if (chunk_size == 1)
540  else
541  return false;
542 }
543 
544 
545 
546 void
547 ChunkSparsityPattern::block_write(std::ostream &out) const
548 {
549  AssertThrow(out, ExcIO());
550 
551  // first the simple objects, bracketed in [...]
552  out << '[' << rows << ' ' << cols << ' ' << chunk_size << ' ' << "][";
553  // then the underlying sparsity pattern
555  out << ']';
556 
557  AssertThrow(out, ExcIO());
558 }
559 
560 
561 
562 void
564 {
565  AssertThrow(in, ExcIO());
566 
567  char c;
568 
569  // first read in simple data
570  in >> c;
571  AssertThrow(c == '[', ExcIO());
572  in >> rows >> cols >> chunk_size;
573 
574  in >> c;
575  AssertThrow(c == ']', ExcIO());
576  in >> c;
577  AssertThrow(c == '[', ExcIO());
578 
579  // then read the underlying sparsity pattern
581 
582  in >> c;
583  AssertThrow(c == ']', ExcIO());
584 }
585 
586 
587 
588 std::size_t
590 {
591  return (sizeof(*this) + sparsity_pattern.memory_consumption());
592 }
593 
594 
595 
596 // explicit instantiations
597 template void
598 ChunkSparsityPattern::copy_from<DynamicSparsityPattern>(
599  const DynamicSparsityPattern &,
600  const size_type);
601 template void
602 ChunkSparsityPattern::create_from<SparsityPattern>(const unsigned int,
603  const unsigned int,
604  const SparsityPattern &,
605  const unsigned int,
606  const bool);
607 template void
608 ChunkSparsityPattern::create_from<DynamicSparsityPattern>(
609  const unsigned int,
610  const unsigned int,
611  const DynamicSparsityPattern &,
612  const unsigned int,
613  const bool);
614 template void
615 ChunkSparsityPattern::copy_from<float>(const FullMatrix<float> &,
616  const size_type);
617 template void
618 ChunkSparsityPattern::copy_from<double>(const FullMatrix<double> &,
619  const size_type);
620 
621 DEAL_II_NAMESPACE_CLOSE
size_type n_rows() 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)
void block_write(std::ostream &out) const
static::ExceptionBase & ExcEmptyObject()
std::size_t memory_consumption() const
static::ExceptionBase & ExcInvalidNumber(int arg1)
void print_gnuplot(std::ostream &out) const
static::ExceptionBase & ExcInvalidIndex(int arg1, int arg2)
std::size_t memory_consumption() const
static::ExceptionBase & ExcIO()
static const size_type invalid_entry
types::global_dof_index size_type
void add(const size_type i, const size_type j)
bool exists(const size_type i, const size_type j) const
iterator begin() const
void block_read(std::istream &in)
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
void add(const size_type i, const size_type j)
static::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
SparsityPattern sparsity_pattern
iterator end() const
std::unique_ptr< size_type[]> colnums
bool stores_only_added_elements() const
static::ExceptionBase & ExcMessage(std::string arg1)
size_type n() const
size_type max_entries_per_row() const
size_type max_entries_per_row() const
size_type bandwidth() const
void add(const size_type i, const size_type j)
void block_write(std::ostream &out) const
#define Assert(cond, exc)
Definition: exceptions.h:1227
size_type n_nonzero_elements() const
size_type n_cols() const
unsigned int row_length(const size_type row) const
void print(std::ostream &out) const
bool stores_only_added_elements() const
size_type m() const
iterator end() const
void copy_from(const size_type n_rows, const size_type n_cols, const ForwardIterator begin, const ForwardIterator end)
size_type bandwidth() const
size_type row_length(const size_type row) const
ChunkSparsityPattern & operator=(const ChunkSparsityPattern &)
void reinit(const size_type m, const size_type n, const unsigned int max_per_row)
static::ExceptionBase & ExcNotQuadratic()
size_type n_cols() const
std::unique_ptr< std::size_t[]> rowstart
size_type n_rows() const
void block_read(std::istream &in)
bool exists(const size_type i, const size_type j) const
bool empty() 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)
std::size_t n_nonzero_elements() const
void reinit(const size_type m, const size_type n, const size_type max_per_row, const size_type chunk_size)