Reference documentation for deal.II version 9.1.0-pre
sparsity_pattern.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2000 - 2018 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/base/std_cxx14/memory.h>
18 #include <deal.II/base/utilities.h>
19 #include <deal.II/base/vector_slice.h>
20 
21 #include <deal.II/lac/dynamic_sparsity_pattern.h>
22 #include <deal.II/lac/full_matrix.h>
23 #include <deal.II/lac/sparsity_pattern.h>
24 #include <deal.II/lac/sparsity_tools.h>
25 
26 #include <algorithm>
27 #include <cmath>
28 #include <functional>
29 #include <iomanip>
30 #include <iostream>
31 #include <numeric>
32 
33 DEAL_II_NAMESPACE_OPEN
34 
35 #ifdef DEAL_II_MSVC
36 __declspec(selectany) // Weak extern binding due to multiple link error
37 #endif
39 
40 
41 
43  : max_dim(0)
44  , max_vec_len(0)
45  , rowstart(nullptr)
46  , colnums(nullptr)
47  , compressed(false)
48  , store_diagonal_first_in_row(false)
49 {
50  reinit(0, 0, 0);
51 }
52 
53 
54 
56  : Subscriptor()
57  , max_dim(0)
58  , max_vec_len(0)
59  , rowstart(nullptr)
60  , colnums(nullptr)
61  , compressed(false)
63 {
64  (void)s;
65  Assert(s.empty(),
66  ExcMessage(
67  "This constructor can only be called if the provided argument "
68  "is the sparsity pattern for an empty matrix. This constructor can "
69  "not be used to copy-construct a non-empty sparsity pattern."));
70 
71  reinit(0, 0, 0);
72 }
73 
74 
75 
77  const size_type n,
78  const unsigned int max_per_row)
79  : max_dim(0)
80  , max_vec_len(0)
81  , rowstart(nullptr)
82  , colnums(nullptr)
83  , compressed(false)
85 {
86  reinit(m, n, max_per_row);
87 }
88 
89 
90 
92  const size_type n,
93  const std::vector<unsigned int> &row_lengths)
94  : max_dim(0)
95  , max_vec_len(0)
96  , rowstart(nullptr)
97  , colnums(nullptr)
99 {
100  reinit(m, n, row_lengths);
101 }
102 
103 
104 
106  const unsigned int max_per_row)
107  : max_dim(0)
108  , max_vec_len(0)
109  , rowstart(nullptr)
110  , colnums(nullptr)
111 {
112  reinit(m, m, max_per_row);
113 }
114 
115 
116 
118  const std::vector<unsigned int> &row_lengths)
119  : max_dim(0)
120  , max_vec_len(0)
121  , rowstart(nullptr)
122  , colnums(nullptr)
123 {
124  reinit(m, m, row_lengths);
125 }
126 
127 
128 
130  const unsigned int max_per_row,
131  const size_type extra_off_diagonals)
132  : max_dim(0)
133  , max_vec_len(0)
134  , rowstart(nullptr)
135  , colnums(nullptr)
136 {
137  Assert(original.rows == original.cols, ExcNotQuadratic());
138  Assert(original.is_compressed(), ExcNotCompressed());
139 
140  reinit(original.rows, original.cols, max_per_row);
141 
142  // now copy the entries from the other object
143  for (size_type row = 0; row < original.rows; ++row)
144  {
145  // copy the elements of this row of the other object
146  //
147  // note that the first object actually is the main-diagonal element,
148  // which we need not copy
149  //
150  // we do the copying in two steps: first we note that the elements in
151  // @p{original} are sorted, so we may first copy all the elements up to
152  // the first side-diagonal one which is to be filled in. then we insert
153  // the side-diagonals, finally copy the rest from that element onwards
154  // which is not a side-diagonal any more.
155  const size_type *const original_row_start =
156  &original.colnums[original.rowstart[row]] + 1;
157  // the following requires that @p{original} be compressed since
158  // otherwise there might be invalid_entry's
159  const size_type *const original_row_end =
160  &original.colnums[original.rowstart[row + 1]];
161 
162  // find pointers before and after extra off-diagonals. if at top or
163  // bottom of matrix, then set these pointers such that no copying is
164  // necessary (see the @p{copy} commands)
165  const size_type *const original_last_before_side_diagonals =
166  (row > extra_off_diagonals ?
167  Utilities::lower_bound(original_row_start,
168  original_row_end,
169  row - extra_off_diagonals) :
170  original_row_start);
171 
172  const size_type *const original_first_after_side_diagonals =
173  (row < rows - extra_off_diagonals - 1 ?
174  std::upper_bound(original_row_start,
175  original_row_end,
176  row + extra_off_diagonals) :
177  original_row_end);
178 
179  // find first free slot. the first slot in each row is the diagonal
180  // element
181  size_type *next_free_slot = &colnums[rowstart[row]] + 1;
182 
183  // copy elements before side-diagonals
184  next_free_slot = std::copy(original_row_start,
185  original_last_before_side_diagonals,
186  next_free_slot);
187 
188  // insert left and right side-diagonals
189  for (size_type i = 1; i <= std::min(row, extra_off_diagonals);
190  ++i, ++next_free_slot)
191  *next_free_slot = row - i;
192  for (size_type i = 1; i <= std::min(extra_off_diagonals, rows - row - 1);
193  ++i, ++next_free_slot)
194  *next_free_slot = row + i;
195 
196  // copy rest
197  next_free_slot = std::copy(original_first_after_side_diagonals,
198  original_row_end,
199  next_free_slot);
200 
201  // this error may happen if the sum of previous elements per row and
202  // those of the new diagonals exceeds the maximum number of elements per
203  // row given to this constructor
204  Assert(next_free_slot <= &colnums[rowstart[row + 1]],
205  ExcNotEnoughSpace(0, rowstart[row + 1] - rowstart[row]));
206  };
207 }
208 
209 
210 
213 {
214  (void)s;
215  Assert(s.empty(),
216  ExcMessage(
217  "This operator can only be called if the provided argument "
218  "is the sparsity pattern for an empty matrix. This operator can "
219  "not be used to copy a non-empty sparsity pattern."));
220 
221  Assert(this->empty(),
222  ExcMessage("This operator can only be called if the current object is "
223  "empty."));
224 
225  return *this;
226 }
227 
228 
229 
230 void
232  const size_type n,
233  const unsigned int max_per_row)
234 {
235  // simply map this function to the other @p{reinit} function
236  const std::vector<unsigned int> row_lengths(m, max_per_row);
237  reinit(m, n, row_lengths);
238 }
239 
240 
241 
242 void
244  const size_type m,
245  const size_type n,
246  const VectorSlice<const std::vector<unsigned int>> &row_lengths)
247 {
248  AssertDimension(row_lengths.size(), m);
249 
250  rows = m;
251  cols = n;
252 
253  // delete empty matrices
254  if ((m == 0) || (n == 0))
255  {
256  rowstart.reset();
257  colnums.reset();
258 
259  max_vec_len = max_dim = rows = cols = 0;
260  // if dimension is zero: ignore max_per_row
261  max_row_length = 0;
262  compressed = false;
263 
264  return;
265  }
266 
267  // first, if the matrix is quadratic, we will have to make sure that each
268  // row has at least one entry for the diagonal element. make this more
269  // obvious by having a variable which we can query
270  store_diagonal_first_in_row = (m == n);
271 
272  // find out how many entries we need in the @p{colnums} array. if this
273  // number is larger than @p{max_vec_len}, then we will need to reallocate
274  // memory
275  //
276  // note that the number of elements per row is bounded by the number of
277  // columns
278  //
279  std::size_t vec_len = 0;
280  for (size_type i = 0; i < m; ++i)
281  vec_len += std::min(static_cast<size_type>(store_diagonal_first_in_row ?
282  std::max(row_lengths[i], 1U) :
283  row_lengths[i]),
284  n);
285 
286  // sometimes, no entries are requested in the matrix (this most often
287  // happens when blocks in a block matrix are simply zero). in that case,
288  // allocate exactly one element, to have a valid pointer to some memory
289  if (vec_len == 0)
290  {
291  vec_len = 1;
292  max_vec_len = vec_len;
293  colnums = std_cxx14::make_unique<size_type[]>(max_vec_len);
294  }
295 
297  (row_lengths.size() == 0 ?
298  0 :
299  std::min(static_cast<size_type>(
300  *std::max_element(row_lengths.begin(), row_lengths.end())),
301  n));
302 
303  if (store_diagonal_first_in_row && (max_row_length == 0) && (m != 0))
304  max_row_length = 1;
305 
306  // allocate memory for the rowstart values, if necessary. even though we
307  // re-set the pointers again immediately after deleting their old content,
308  // set them to zero in between because the allocation might fail, in which
309  // case we get an exception and the destructor of this object will be called
310  // -- where we look at the non-nullness of the (now invalid) pointer again
311  // and try to delete the memory a second time.
312  if (rows > max_dim)
313  {
314  max_dim = rows;
315  rowstart = std_cxx14::make_unique<std::size_t[]>(max_dim + 1);
316  }
317 
318  // allocate memory for the column numbers if necessary
319  if (vec_len > max_vec_len)
320  {
321  max_vec_len = vec_len;
322  colnums = std_cxx14::make_unique<size_type[]>(max_vec_len);
323  }
324 
325  // set the rowstart array
326  rowstart[0] = 0;
327  for (size_type i = 1; i <= rows; ++i)
328  rowstart[i] =
329  rowstart[i - 1] +
331  std::max(std::min(static_cast<size_type>(row_lengths[i - 1]), n),
332  static_cast<size_type>(1U)) :
333  std::min(static_cast<size_type>(row_lengths[i - 1]), n));
334  Assert((rowstart[rows] == vec_len) ||
335  ((vec_len == 1) && (rowstart[rows] == 0)),
336  ExcInternalError());
337 
338  // preset the column numbers by a value indicating it is not in use
339  std::fill_n(colnums.get(), vec_len, invalid_entry);
340 
341  // if diagonal elements are special: let the first entry in each row be the
342  // diagonal value
344  for (size_type i = 0; i < rows; i++)
345  colnums[rowstart[i]] = i;
346 
347  compressed = false;
348 }
349 
350 
351 
352 void
354 {
355  // nothing to do if the object corresponds to an empty matrix
356  if ((rowstart == nullptr) && (colnums == nullptr))
357  {
358  compressed = true;
359  return;
360  }
361 
362  // do nothing if already compressed
363  if (compressed)
364  return;
365 
366  size_type next_free_entry = 0, next_row_start = 0, row_length = 0;
367 
368  // first find out how many non-zero elements there are, in order to allocate
369  // the right amount of memory
370  const std::size_t nonzero_elements =
371  std::count_if(&colnums[rowstart[0]],
372  &colnums[rowstart[rows]],
373  std::bind(std::not_equal_to<size_type>(),
374  std::placeholders::_1,
375  invalid_entry));
376  // now allocate the respective memory
377  std::unique_ptr<size_type[]> new_colnums(new size_type[nonzero_elements]);
378 
379 
380  // reserve temporary storage to store the entries of one row
381  std::vector<size_type> tmp_entries(max_row_length);
382 
383  // Traverse all rows
384  for (size_type line = 0; line < rows; ++line)
385  {
386  // copy used entries, break if first unused entry is reached
387  row_length = 0;
388  for (size_type j = rowstart[line]; j < rowstart[line + 1];
389  ++j, ++row_length)
390  if (colnums[j] != invalid_entry)
391  tmp_entries[row_length] = colnums[j];
392  else
393  break;
394  // now @p{rowstart} is the number of entries in this line
395 
396  // Sort only beginning at the second entry, if optimized storage of
397  // diagonal entries is on.
398 
399  // if this line is empty or has only one entry, don't sort
400  if (row_length > 1)
401  std::sort((store_diagonal_first_in_row) ? tmp_entries.begin() + 1 :
402  tmp_entries.begin(),
403  tmp_entries.begin() + row_length);
404 
405  // insert column numbers into the new field
406  for (size_type j = 0; j < row_length; ++j)
407  new_colnums[next_free_entry++] = tmp_entries[j];
408 
409  // note new start of this and the next row
410  rowstart[line] = next_row_start;
411  next_row_start = next_free_entry;
412 
413  // some internal checks: either the matrix is not quadratic, or if it
414  // is, then the first element of this row must be the diagonal element
415  // (i.e. with column index==line number)
416  // this test only makes sense if we have written to the index
417  // rowstart_line in new_colnums which is the case if row_length is not 0,
418  // so check this first
420  (row_length != 0 && new_colnums[rowstart[line]] == line),
421  ExcInternalError());
422  // assert that the first entry does not show up in the remaining ones
423  // and that the remaining ones are unique among themselves (this handles
424  // both cases, quadratic and rectangular matrices)
425  //
426  // the only exception here is if the row contains no entries at all
427  Assert((rowstart[line] == next_row_start) ||
428  (std::find(&new_colnums[rowstart[line] + 1],
429  &new_colnums[next_row_start],
430  new_colnums[rowstart[line]]) ==
431  &new_colnums[next_row_start]),
432  ExcInternalError());
433  Assert((rowstart[line] == next_row_start) ||
434  (std::adjacent_find(&new_colnums[rowstart[line] + 1],
435  &new_colnums[next_row_start]) ==
436  &new_colnums[next_row_start]),
437  ExcInternalError());
438  };
439 
440  // assert that we have used all allocated space, no more and no less
441  Assert(next_free_entry == nonzero_elements, ExcInternalError());
442 
443  // set iterator-past-the-end
444  rowstart[rows] = next_row_start;
445 
446  // set colnums to the newly allocated array and delete previous content
447  // in the process
448  colnums = std::move(new_colnums);
449 
450  // store the size
451  max_vec_len = nonzero_elements;
452 
453  compressed = true;
454 }
455 
456 
457 
458 void
460 {
461  // first determine row lengths for each row. if the matrix is quadratic,
462  // then we might have to add an additional entry for the diagonal, if that
463  // is not yet present. as we have to call compress anyway later on, don't
464  // bother to check whether that diagonal entry is in a certain row or not
465  const bool do_diag_optimize = (sp.n_rows() == sp.n_cols());
466  std::vector<unsigned int> row_lengths(sp.n_rows());
467  for (size_type i = 0; i < sp.n_rows(); ++i)
468  {
469  row_lengths[i] = sp.row_length(i);
470  if (do_diag_optimize && !sp.exists(i, i))
471  ++row_lengths[i];
472  }
473  reinit(sp.n_rows(), sp.n_cols(), row_lengths);
474 
475  // now enter all the elements into the matrix, if there are any. note that
476  // if the matrix is quadratic, then we already have the diagonal element
477  // preallocated
478  if (n_rows() != 0 && n_cols() != 0)
479  for (size_type row = 0; row < sp.n_rows(); ++row)
480  {
481  size_type *cols = &colnums[rowstart[row]] + (do_diag_optimize ? 1 : 0);
482  typename SparsityPattern::iterator col_num = sp.begin(row),
483  end_row = sp.end(row);
484 
485  for (; col_num != end_row; ++col_num)
486  {
487  const size_type col = col_num->column();
488  if ((col != row) || !do_diag_optimize)
489  *cols++ = col;
490  }
491  }
492 
493  // do not need to compress the sparsity pattern since we already have
494  // allocated the right amount of data, and the SparsityPattern data is
495  // sorted, too.
496  compressed = true;
497 }
498 
499 
500 
501 // Use a special implementation for DynamicSparsityPattern where we can use
502 // the column_number method to gain faster access to the
503 // entries. DynamicSparsityPattern::iterator can show quadratic complexity in
504 // case many rows are empty and the begin() method needs to jump to the next
505 // free row. Otherwise, the code is exactly the same as above.
506 void
508 {
509  const bool do_diag_optimize = (dsp.n_rows() == dsp.n_cols());
510  std::vector<unsigned int> row_lengths(dsp.n_rows());
511  for (size_type i = 0; i < dsp.n_rows(); ++i)
512  {
513  row_lengths[i] = dsp.row_length(i);
514  if (do_diag_optimize && !dsp.exists(i, i))
515  ++row_lengths[i];
516  }
517  reinit(dsp.n_rows(), dsp.n_cols(), row_lengths);
518 
519  if (n_rows() != 0 && n_cols() != 0)
520  for (size_type row = 0; row < dsp.n_rows(); ++row)
521  {
522  size_type *cols = &colnums[rowstart[row]] + (do_diag_optimize ? 1 : 0);
523  const unsigned int row_length = dsp.row_length(row);
524  for (unsigned int index = 0; index < row_length; ++index)
525  {
526  const size_type col = dsp.column_number(row, index);
527  if ((col != row) || !do_diag_optimize)
528  *cols++ = col;
529  }
530  }
531 
532  // do not need to compress the sparsity pattern since we already have
533  // allocated the right amount of data, and the SparsityPatternType data is
534  // sorted, too.
535  compressed = true;
536 }
537 
538 
539 
540 template <typename number>
541 void
543 {
544  // first init with the number of entries per row. if this matrix is square
545  // then we also have to allocate memory for the diagonal entry, unless we
546  // have already counted it
547  const bool matrix_is_square = (matrix.m() == matrix.n());
548 
549  std::vector<unsigned int> entries_per_row(matrix.m(), 0);
550  for (size_type row = 0; row < matrix.m(); ++row)
551  {
552  for (size_type col = 0; col < matrix.n(); ++col)
553  if (matrix(row, col) != 0)
554  ++entries_per_row[row];
555  if (matrix_is_square && (matrix(row, row) == 0))
556  ++entries_per_row[row];
557  }
558 
559  reinit(matrix.m(), matrix.n(), entries_per_row);
560 
561  // now set entries. if we enter entries row by row, then we'll get
562  // quadratic complexity in the number of entries per row. this is
563  // not usually a problem (we don't usually create dense matrices),
564  // but there are cases where it matters -- so we may as well be
565  // gentler and hand over a whole row of entries at a time
566  std::vector<size_type> column_indices;
567  column_indices.reserve(
568  entries_per_row.size() > 0 ?
569  *std::max_element(entries_per_row.begin(), entries_per_row.end()) :
570  0);
571  for (size_type row = 0; row < matrix.m(); ++row)
572  {
573  column_indices.resize(entries_per_row[row]);
574 
575  size_type current_index = 0;
576  for (size_type col = 0; col < matrix.n(); ++col)
577  if (matrix(row, col) != 0)
578  {
579  column_indices[current_index] = col;
580  ++current_index;
581  }
582  else
583  // the (row,col) entry is zero; check if we need to add it
584  // anyway because it's the diagonal entry of a square
585  // matrix
586  if (matrix_is_square && (col == row))
587  {
588  column_indices[current_index] = row;
589  ++current_index;
590  }
591 
592  // check that we really added the correct number of indices
593  Assert(current_index == entries_per_row[row], ExcInternalError());
594 
595  // now bulk add all of these entries
596  add_entries(row, column_indices.begin(), column_indices.end(), true);
597  }
598 
599  // finally compress
600  compress();
601 }
602 
603 
604 void
606  const size_type n,
607  const std::vector<unsigned int> &row_lengths)
608 {
609  reinit(m, n, make_slice(row_lengths));
610 }
611 
612 
613 
614 bool
616 {
617  // let's try to be on the safe side of life by using multiple possibilities
618  // in the check for emptiness... (sorry for this kludge -- emptying matrices
619  // and freeing memory was not present in the original implementation and I
620  // don't know at how many places I missed something in adding it, so I try
621  // to be cautious. wb)
622  if ((rowstart == nullptr) || (rows == 0) || (cols == 0))
623  {
624  Assert(rowstart == nullptr, ExcInternalError());
625  Assert(rows == 0, ExcInternalError());
626  Assert(cols == 0, ExcInternalError());
627  Assert(colnums == nullptr, ExcInternalError());
629 
630  return true;
631  };
632  return false;
633 }
634 
635 
636 
639 {
640  // if compress() has not yet been called, we can get the maximum number of
641  // elements per row using the stored value
642  if (!compressed)
643  return max_row_length;
644 
645  // if compress() was called, we use a better algorithm which gives us a
646  // sharp bound
647  size_type m = 0;
648  for (size_type i = 1; i <= rows; ++i)
649  m = std::max(m, static_cast<size_type>(rowstart[i] - rowstart[i - 1]));
650 
651  return m;
652 }
653 
654 
655 
658 {
659  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
660  Assert(i < rows, ExcIndexRange(i, 0, rows));
661  Assert(j < cols, ExcIndexRange(j, 0, cols));
663 
664  // let's see whether there is something in this line
665  if (rowstart[i] == rowstart[i + 1])
666  return invalid_entry;
667 
668  // If special storage of diagonals was requested, we can get the diagonal
669  // element faster by this query.
670  if (store_diagonal_first_in_row && (i == j))
671  return rowstart[i];
672 
673  // all other entries are sorted, so we can use a binary search algorithm
674  //
675  // note that the entries are only sorted upon compression, so this would
676  // fail for non-compressed sparsity patterns; however, that is why the
677  // Assertion is at the top of this function, so it may not be called for
678  // noncompressed structures.
679  const size_type *sorted_region_start =
681  &colnums[rowstart[i]]);
682  const size_type *const p =
683  Utilities::lower_bound<const size_type *>(sorted_region_start,
684  &colnums[rowstart[i + 1]],
685  j);
686  if ((p != &colnums[rowstart[i + 1]]) && (*p == j))
687  return (p - colnums.get());
688  else
689  return invalid_entry;
690 }
691 
692 
693 
694 void
696 {
697  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
698  Assert(i < rows, ExcIndexRange(i, 0, rows));
699  Assert(j < cols, ExcIndexRange(j, 0, cols));
701 
702  for (std::size_t k = rowstart[i]; k < rowstart[i + 1]; k++)
703  {
704  // entry already exists
705  if (colnums[k] == j)
706  return;
707  // empty entry found, put new entry here
708  if (colnums[k] == invalid_entry)
709  {
710  colnums[k] = j;
711  return;
712  };
713  };
714 
715  // if we came thus far, something went wrong: there was not enough space in
716  // this line
717  Assert(false, ExcNotEnoughSpace(i, rowstart[i + 1] - rowstart[i]));
718 }
719 
720 
721 
722 template <typename ForwardIterator>
723 void
725  ForwardIterator begin,
726  ForwardIterator end,
727  const bool indices_are_sorted)
728 {
729  if (indices_are_sorted == true)
730  {
731  if (begin != end)
732  {
733  ForwardIterator it = begin;
734  bool has_larger_entries = false;
735  // skip diagonal
736  std::size_t k = rowstart[row] + store_diagonal_first_in_row;
737  for (; k < rowstart[row + 1]; k++)
738  if (colnums[k] == invalid_entry)
739  break;
740  else if (colnums[k] >= *it)
741  {
742  has_larger_entries = true;
743  break;
744  }
745  if (has_larger_entries == false)
746  for (; it != end; ++it)
747  {
748  if (store_diagonal_first_in_row && *it == row)
749  continue;
750  Assert(k <= rowstart[row + 1],
751  ExcNotEnoughSpace(row,
752  rowstart[row + 1] - rowstart[row]));
753  colnums[k++] = *it;
754  }
755  else
756  // cannot just append the new range at the end, forward to the
757  // other function
758  for (ForwardIterator p = begin; p != end; ++p)
759  add(row, *p);
760  }
761  }
762  else
763  {
764  // forward to the other function.
765  for (ForwardIterator it = begin; it != end; ++it)
766  add(row, *it);
767  }
768 }
769 
770 
771 bool
773 {
774  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
775  Assert(i < rows, ExcIndexRange(i, 0, rows));
776  Assert(j < cols, ExcIndexRange(j, 0, cols));
777 
778  for (size_type k = rowstart[i]; k < rowstart[i + 1]; k++)
779  {
780  // entry already exists
781  if (colnums[k] == j)
782  return true;
783  }
784  return false;
785 }
786 
787 
788 
791 {
792  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
793  Assert(i < rows, ExcIndexRange(i, 0, rows));
794  Assert(j < cols, ExcIndexRange(j, 0, cols));
795 
796  for (size_type k = rowstart[i]; k < rowstart[i + 1]; k++)
797  {
798  // entry exists
799  if (colnums[k] == j)
800  return k - rowstart[i];
801  }
803 }
804 
805 
806 
807 std::pair<SparsityPattern::size_type, SparsityPattern::size_type>
808 SparsityPattern::matrix_position(const std::size_t global_index) const
809 {
810  Assert(compressed == true, ExcNotCompressed());
811  Assert(global_index < n_nonzero_elements(),
812  ExcIndexRange(global_index, 0, n_nonzero_elements()));
813 
814  // first find the row in which the entry is located. for this note that the
815  // rowstart array indexes the global indices at which each row starts. since
816  // it is sorted, and since there is an element for the one-past-last row, we
817  // can simply use a bisection search on it
818  const size_type row =
819  (std::upper_bound(rowstart.get(), rowstart.get() + rows, global_index) -
820  rowstart.get() - 1);
821 
822  // now, the column index is simple since that is what the colnums array
823  // stores:
824  const size_type col = colnums[global_index];
825 
826  // so return the respective pair
827  return std::make_pair(row, col);
828 }
829 
830 
831 
832 void
834 {
835  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
837  // Note that we only require a quadratic matrix here, no special treatment
838  // of diagonals
840 
841  // loop over all elements presently in the sparsity pattern and add the
842  // transpose element. note:
843  //
844  // 1. that the sparsity pattern changes which we work on, but not the
845  // present row
846  //
847  // 2. that the @p{add} function can be called on elements that already exist
848  // without any harm
849  for (size_type row = 0; row < rows; ++row)
850  for (size_type k = rowstart[row]; k < rowstart[row + 1]; k++)
851  {
852  // check whether we are at the end of the entries of this row. if so,
853  // go to next row
854  if (colnums[k] == invalid_entry)
855  break;
856 
857  // otherwise add the transpose entry if this is not the diagonal (that
858  // would not harm, only take time to check up)
859  if (colnums[k] != row)
860  add(colnums[k], row);
861  };
862 }
863 
864 
865 
866 void
867 SparsityPattern::print(std::ostream &out) const
868 {
869  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
870 
871  AssertThrow(out, ExcIO());
872 
873  for (size_type i = 0; i < rows; ++i)
874  {
875  out << '[' << i;
876  for (size_type j = rowstart[i]; j < rowstart[i + 1]; ++j)
877  if (colnums[j] != invalid_entry)
878  out << ',' << colnums[j];
879  out << ']' << std::endl;
880  }
881 
882  AssertThrow(out, ExcIO());
883 }
884 
885 
886 
887 void
888 SparsityPattern::print_gnuplot(std::ostream &out) const
889 {
890  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
891 
892  AssertThrow(out, ExcIO());
893 
894  for (size_type i = 0; i < rows; ++i)
895  for (size_type j = rowstart[i]; j < rowstart[i + 1]; ++j)
896  if (colnums[j] != invalid_entry)
897  // while matrix entries are usually written (i,j), with i vertical and
898  // j horizontal, gnuplot output is x-y, that is we have to exchange
899  // the order of output
900  out << colnums[j] << " " << -static_cast<signed int>(i) << std::endl;
901 
902  AssertThrow(out, ExcIO());
903 }
904 
905 void
906 SparsityPattern::print_svg(std::ostream &out) const
907 {
908  unsigned int m = this->n_rows();
909  unsigned int n = this->n_cols();
910  out
911  << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "
912  << n + 2 << " " << m + 2
913  << " \">\n"
914  "<style type=\"text/css\" >\n"
915  " <![CDATA[\n"
916  " rect.pixel {\n"
917  " fill: #ff0000;\n"
918  " }\n"
919  " ]]>\n"
920  " </style>\n\n"
921  " <rect width=\""
922  << n + 2 << "\" height=\"" << m + 2
923  << "\" fill=\"rgb(128, 128, 128)\"/>\n"
924  " <rect x=\"1\" y=\"1\" width=\""
925  << n + 0.1 << "\" height=\"" << m + 0.1
926  << "\" fill=\"rgb(255, 255, 255)\"/>\n\n";
927 
928  SparsityPattern::iterator it = this->begin(), end = this->end();
929  for (; it != end; ++it)
930  {
931  out << " <rect class=\"pixel\" x=\"" << it->column() + 1 << "\" y=\""
932  << it->row() + 1 << "\" width=\".9\" height=\".9\"/>\n";
933  }
934  out << "</svg>" << std::endl;
935 }
936 
937 
938 
941 {
942  Assert((rowstart != nullptr) && (colnums != nullptr), ExcEmptyObject());
943  size_type b = 0;
944  for (size_type i = 0; i < rows; ++i)
945  for (size_type j = rowstart[i]; j < rowstart[i + 1]; ++j)
946  if (colnums[j] != invalid_entry)
947  {
948  if (static_cast<size_type>(
949  std::abs(static_cast<int>(i - colnums[j]))) > b)
950  b = std::abs(static_cast<signed int>(i - colnums[j]));
951  }
952  else
953  // leave if at the end of the entries of this line
954  break;
955  return b;
956 }
957 
958 
959 void
960 SparsityPattern::block_write(std::ostream &out) const
961 {
962  AssertThrow(out, ExcIO());
963 
964  // first the simple objects, bracketed in [...]
965  out << '[' << max_dim << ' ' << rows << ' ' << cols << ' ' << max_vec_len
966  << ' ' << max_row_length << ' ' << compressed << ' '
967  << store_diagonal_first_in_row << "][";
968  // then write out real data
969  out.write(reinterpret_cast<const char *>(rowstart.get()),
970  reinterpret_cast<const char *>(rowstart.get() + max_dim + 1) -
971  reinterpret_cast<const char *>(rowstart.get()));
972  out << "][";
973  out.write(reinterpret_cast<const char *>(colnums.get()),
974  reinterpret_cast<const char *>(colnums.get() + max_vec_len) -
975  reinterpret_cast<const char *>(colnums.get()));
976  out << ']';
977 
978  AssertThrow(out, ExcIO());
979 }
980 
981 
982 
983 void
985 {
986  AssertThrow(in, ExcIO());
987 
988  char c;
989 
990  // first read in simple data
991  in >> c;
992  AssertThrow(c == '[', ExcIO());
993  in >> max_dim >> rows >> cols >> max_vec_len >> max_row_length >>
995 
996  in >> c;
997  AssertThrow(c == ']', ExcIO());
998  in >> c;
999  AssertThrow(c == '[', ExcIO());
1000 
1001  // reallocate space
1002  rowstart = std_cxx14::make_unique<std::size_t[]>(max_dim + 1);
1003  colnums = std_cxx14::make_unique<size_type[]>(max_vec_len);
1004 
1005  // then read data
1006  in.read(reinterpret_cast<char *>(rowstart.get()),
1007  reinterpret_cast<char *>(rowstart.get() + max_dim + 1) -
1008  reinterpret_cast<char *>(rowstart.get()));
1009  in >> c;
1010  AssertThrow(c == ']', ExcIO());
1011  in >> c;
1012  AssertThrow(c == '[', ExcIO());
1013  in.read(reinterpret_cast<char *>(colnums.get()),
1014  reinterpret_cast<char *>(colnums.get() + max_vec_len) -
1015  reinterpret_cast<char *>(colnums.get()));
1016  in >> c;
1017  AssertThrow(c == ']', ExcIO());
1018 }
1019 
1020 
1021 
1022 std::size_t
1024 {
1025  return (max_dim * sizeof(size_type) + sizeof(*this) +
1026  max_vec_len * sizeof(size_type));
1027 }
1028 
1029 
1030 
1031 // explicit instantiations
1032 template void
1033 SparsityPattern::copy_from<float>(const FullMatrix<float> &);
1034 template void
1035 SparsityPattern::copy_from<double>(const FullMatrix<double> &);
1036 
1037 template void
1038 SparsityPattern::add_entries<const SparsityPattern::size_type *>(
1039  const size_type,
1040  const size_type *,
1041  const size_type *,
1042  const bool);
1043 #ifndef DEAL_II_VECTOR_ITERATOR_IS_POINTER
1044 template void
1046  std::vector<SparsityPattern::size_type>::const_iterator>(
1047  const size_type,
1048  std::vector<size_type>::const_iterator,
1049  std::vector<size_type>::const_iterator,
1050  const bool);
1051 #endif
1052 template void
1053 SparsityPattern::add_entries<std::vector<SparsityPattern::size_type>::iterator>(
1054  const size_type,
1055  std::vector<size_type>::iterator,
1056  std::vector<size_type>::iterator,
1057  const bool);
1058 
1059 DEAL_II_NAMESPACE_CLOSE
size_type row_length(const size_type row) const
Iterator lower_bound(Iterator first, Iterator last, const T &val)
Definition: utilities.h:939
const types::global_dof_index invalid_size_type
Definition: types.h:182
void add_entries(const size_type row, ForwardIterator begin, ForwardIterator end, const bool indices_are_sorted=false)
static::ExceptionBase & ExcMatrixIsCompressed()
bool exists(const size_type i, const size_type j) const
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1366
void print_svg(std::ostream &out) const
std::size_t memory_consumption() const
static::ExceptionBase & ExcIO()
static const size_type invalid_entry
std::pair< size_type, size_type > matrix_position(const std::size_t global_index) const
void print_gnuplot(std::ostream &out) const
size_type row_position(const size_type i, const size_type j) const
iterator begin() const
size_type operator()(const size_type i, const size_type j) const
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
static::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
std::size_t max_vec_len
std::unique_ptr< size_type[]> colnums
static::ExceptionBase & ExcNotCompressed()
static::ExceptionBase & ExcMessage(std::string arg1)
size_type n() 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_cols() const
unsigned int row_length(const size_type row) const
SparsityPattern & operator=(const SparsityPattern &)
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)
types::global_dof_index size_type
void reinit(const size_type m, const size_type n, const unsigned int max_per_row)
unsigned int max_row_length
size_type column_number(const size_type row, const size_type index) const
static::ExceptionBase & ExcNotQuadratic()
static::ExceptionBase & ExcNotEnoughSpace(int arg1, int arg2)
std::unique_ptr< std::size_t[]> rowstart
size_type n_rows() const
void print(std::ostream &out) const
static::ExceptionBase & ExcEmptyObject()
void block_read(std::istream &in)
bool exists(const size_type i, const size_type j) const
bool empty() const
bool is_compressed() const
std::size_t n_nonzero_elements() const
static::ExceptionBase & ExcInternalError()