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