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
chunk_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) 2008 - 2026 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
14#include <deal.II/base/config.h>
15
21
27
28#include <Kokkos_Macros.hpp>
29
30#include <algorithm>
31#include <cstddef>
32#include <istream>
33#include <memory>
34#include <string>
35#include <vector>
36
37
39
40
45
46
47
50 , chunk_size(s.chunk_size)
51 , sparsity_pattern(s.sparsity_pattern)
52{
53 Assert(s.rows == 0 && s.cols == 0,
55 "This constructor can only be called if the provided argument "
56 "is the sparsity pattern for an empty matrix. This constructor can "
57 "not be used to copy-construct a non-empty sparsity pattern."));
58
59 reinit(0, 0, 0, chunk_size);
60}
61
62
63
65 const size_type n,
66 const size_type max_per_row,
67 const size_type chunk_size)
68{
70
71 reinit(m, n, max_per_row, chunk_size);
72}
73
74
75
77 const size_type m,
78 const size_type n,
79 const std::vector<size_type> &row_lengths,
80 const size_type chunk_size)
81{
83
84 reinit(m, n, row_lengths, chunk_size);
85}
86
87
88
90 const size_type max_per_row,
91 const size_type chunk_size)
92{
93 reinit(n, n, max_per_row, chunk_size);
94}
95
96
97
99 const size_type m,
100 const std::vector<size_type> &row_lengths,
101 const size_type chunk_size)
102{
104
105 reinit(m, m, row_lengths, chunk_size);
106}
107
108
109
112{
113 Assert(s.rows == 0 && s.cols == 0,
115 "This operator can only be called if the provided argument "
116 "is the sparsity pattern for an empty matrix. This operator can "
117 "not be used to copy a non-empty sparsity pattern."));
118
119 Assert(rows == 0 && cols == 0,
120 ExcMessage("This operator can only be called if the current object is "
121 "empty."));
122
123 // perform the checks in the underlying object as well
125
126 return *this;
127}
128
129
130
131void
133 const size_type n,
134 const size_type max_per_row,
135 const size_type chunk_size)
136{
138
139 // simply map this function to the other @p{reinit} function
140 const std::vector<size_type> row_lengths(m, max_per_row);
141 reinit(m, n, row_lengths, chunk_size);
142}
143
144
145
146void
148 const size_type n,
149 const ArrayView<const size_type> &row_lengths,
150 const size_type chunk_size)
151{
152 Assert(row_lengths.size() == m, ExcInvalidNumber(m));
154
155 rows = m;
156 cols = n;
157
158 this->chunk_size = chunk_size;
159
160 // pass down to the necessary information to the underlying object. we need
161 // to calculate how many chunks we need: we need to round up (m/chunk_size)
162 // and (n/chunk_size). rounding up in integer arithmetic equals
163 // ((m+chunk_size-1)/chunk_size):
164 const size_type m_chunks = (m + chunk_size - 1) / chunk_size,
165 n_chunks = (n + chunk_size - 1) / chunk_size;
166
167 // compute the maximum number of chunks in each row. the passed array
168 // denotes the number of entries in each row of the big matrix -- in the
169 // worst case, these are all in independent chunks, so we have to calculate
170 // it as follows (as an example: let chunk_size==2, row_lengths={2,2,...},
171 // and entries in row zero at columns {0,2} and for row one at {4,6} -->
172 // we'll need 4 chunks for the first chunk row!) :
173 std::vector<unsigned int> chunk_row_lengths(m_chunks, 0);
174 for (size_type i = 0; i < m; ++i)
175 chunk_row_lengths[i / chunk_size] += row_lengths[i];
176
177 // for the case that the reduced sparsity pattern optimizes the diagonal but
178 // the actual sparsity pattern does not, need to take one more entry in the
179 // row to fit the user-required entry
180 if (m != n && m_chunks == n_chunks)
181 for (unsigned int i = 0; i < m_chunks; ++i)
182 ++chunk_row_lengths[i];
183
184 sparsity_pattern.reinit(m_chunks, n_chunks, chunk_row_lengths);
185}
186
187
188
189void
194
195
196
197template <typename SparsityPatternType>
198void
199ChunkSparsityPattern::copy_from(const SparsityPatternType &dsp,
200 const size_type chunk_size)
201{
203 this->chunk_size = chunk_size;
204 rows = dsp.n_rows();
205 cols = dsp.n_cols();
206
207 // simple case: just use the given sparsity pattern
208 if (chunk_size == 1)
209 {
211 return;
212 }
213
214 // create a temporary compressed sparsity pattern that collects all entries
215 // from the input sparsity pattern and then initialize the underlying small
216 // sparsity pattern
217 const size_type m_chunks = (dsp.n_rows() + chunk_size - 1) / chunk_size,
218 n_chunks = (dsp.n_cols() + chunk_size - 1) / chunk_size;
219 DynamicSparsityPattern temporary_sp(m_chunks, n_chunks);
220
221 for (size_type row = 0; row < dsp.n_rows(); ++row)
222 {
223 const size_type reduced_row = row / chunk_size;
224
225 // TODO: This could be made more efficient if we cached the
226 // previous column and only called add() if the previous and the
227 // current column lead to different chunk columns
228 for (typename SparsityPatternType::iterator col_num = dsp.begin(row);
229 col_num != dsp.end(row);
230 ++col_num)
231 temporary_sp.add(reduced_row, col_num->column() / chunk_size);
232 }
233
234 sparsity_pattern.copy_from(temporary_sp);
235}
236
237
238
239template <typename number>
240void
242 const size_type chunk_size)
243{
245
246 // count number of entries per row, then initialize the underlying sparsity
247 // pattern. remember to also allocate space for the diagonal entry (if that
248 // hasn't happened yet) if m==n since we always allocate that for diagonal
249 // matrices
250 std::vector<size_type> entries_per_row(matrix.m(), 0);
251 for (size_type row = 0; row < matrix.m(); ++row)
252 {
253 for (size_type col = 0; col < matrix.n(); ++col)
254 if (matrix(row, col) != 0)
255 ++entries_per_row[row];
256
257 if ((matrix.m() == matrix.n()) && (matrix(row, row) == 0))
258 ++entries_per_row[row];
259 }
260
261 reinit(matrix.m(), matrix.n(), entries_per_row, chunk_size);
262
263 // then actually fill it
264 for (size_type row = 0; row < matrix.m(); ++row)
265 for (size_type col = 0; col < matrix.n(); ++col)
266 if (matrix(row, col) != 0)
267 add(row, col);
268
269 // finally compress
270 compress();
271}
272
273
274
275void
277 const size_type n,
278 const std::vector<size_type> &row_lengths,
279 const size_type chunk_size)
280{
282
283 reinit(m, n, make_array_view(row_lengths), chunk_size);
284}
285
286
287
288namespace internal
289{
290 namespace
291 {
292 template <typename SparsityPatternType>
293 void
294 copy_sparsity(const SparsityPatternType &src, SparsityPattern &dst)
295 {
296 dst.copy_from(src);
297 }
298
299 void
300 copy_sparsity(const SparsityPattern &src, SparsityPattern &dst)
301 {
302 dst = src;
303 }
304 } // namespace
305} // namespace internal
306
307
308
309template <typename Sparsity>
310void
312 const size_type n,
313 const Sparsity &sparsity_pattern_for_chunks,
314 const size_type chunk_size_in,
315 const bool)
316{
317 Assert(m > (sparsity_pattern_for_chunks.n_rows() - 1) * chunk_size_in &&
318 m <= sparsity_pattern_for_chunks.n_rows() * chunk_size_in,
319 ExcMessage("Number of rows m is not compatible with chunk size "
320 "and number of rows in sparsity pattern for the chunks."));
321 Assert(n > (sparsity_pattern_for_chunks.n_cols() - 1) * chunk_size_in &&
322 n <= sparsity_pattern_for_chunks.n_cols() * chunk_size_in,
324 "Number of columns m is not compatible with chunk size "
325 "and number of columns in sparsity pattern for the chunks."));
326
327 internal::copy_sparsity(sparsity_pattern_for_chunks, sparsity_pattern);
328 chunk_size = chunk_size_in;
329 rows = m;
330 cols = n;
331}
332
333
334
335bool
337{
338 return sparsity_pattern.empty();
339}
340
341
342
348
349
350
351void
359
360
361bool
363{
366
368}
369
370
371
372void
374{
375 // matrix must be square. note that the for some matrix sizes, the current
376 // sparsity pattern may not be square even if the underlying sparsity
377 // pattern is (e.g. a 10x11 matrix with chunk_size 4)
379
381}
382
383
384
387{
389
390 // find out if we did padding and if this row is affected by it
391 if (n_cols() % chunk_size == 0)
393 else
394 // if columns don't align, then just iterate over all chunks and see
395 // what this leads to
396 {
399 end =
401 unsigned int n = 0;
402 for (; p != end; ++p)
403 if (p->column() != sparsity_pattern.n_cols() - 1)
404 n += chunk_size;
405 else
406 n += (n_cols() % chunk_size);
407 return n;
408 }
409}
410
411
412
415{
416 if ((n_rows() % chunk_size == 0) && (n_cols() % chunk_size == 0))
418 else
419 // some of the chunks reach beyond the extent of this matrix. this
420 // requires a somewhat more complicated computations, in particular if the
421 // columns don't align
422 {
423 if ((n_rows() % chunk_size != 0) && (n_cols() % chunk_size == 0))
424 {
425 // columns align with chunks, but not rows
426 size_type n =
431 return n;
432 }
433
434 else
435 {
436 // if columns don't align, then just iterate over all chunks and see
437 // what this leads to. follow the advice in the documentation of the
438 // sparsity pattern iterators to do the loop over individual rows,
439 // rather than all elements
440 size_type n = 0;
441
442 for (size_type row = 0; row < sparsity_pattern.n_rows(); ++row)
443 {
445 for (; p != sparsity_pattern.end(row); ++p)
446 if ((row != sparsity_pattern.n_rows() - 1) &&
447 (p->column() != sparsity_pattern.n_cols() - 1))
448 n += chunk_size * chunk_size;
449 else if ((row == sparsity_pattern.n_rows() - 1) &&
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 (p->column() == sparsity_pattern.n_cols() - 1))
456 // last chunk column, but not row
457 n += (n_cols() % chunk_size) * chunk_size;
458 else
459 // bottom right chunk
460 n += (n_cols() % chunk_size) * (n_rows() % chunk_size);
461 }
462
463 return n;
464 }
465 }
466}
467
468
469
470void
471ChunkSparsityPattern::print(std::ostream &out) const
472{
473 Assert((sparsity_pattern.rowstart != nullptr) &&
474 (sparsity_pattern.colnums != nullptr),
476
477 AssertThrow(out.fail() == false, ExcIO());
478
479 for (size_type i = 0; i < sparsity_pattern.rows; ++i)
480 for (size_type d = 0; (d < chunk_size) && (i * chunk_size + d < n_rows());
481 ++d)
482 {
483 out << '[' << i * chunk_size + d;
485 j < sparsity_pattern.rowstart[i + 1];
486 ++j)
488 for (size_type e = 0;
489 ((e < chunk_size) &&
491 ++e)
492 out << ',' << sparsity_pattern.colnums[j] * chunk_size + e;
493 out << ']' << std::endl;
494 }
495
496 AssertThrow(out.fail() == false, ExcIO());
497}
498
499
500
501void
503{
504 Assert((sparsity_pattern.rowstart != nullptr) &&
505 (sparsity_pattern.colnums != nullptr),
507
508 AssertThrow(out.fail() == false, 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];
515 ++j)
517 for (size_type d = 0;
518 ((d < chunk_size) &&
520 ++d)
521 for (size_type e = 0;
522 (e < chunk_size) && (i * chunk_size + e < n_rows());
523 ++e)
524 // while matrix entries are usually written (i,j), with i vertical
525 // and j horizontal, gnuplot output is x-y, that is we have to
526 // exchange the order of output
527 out << sparsity_pattern.colnums[j] * chunk_size + d << " "
528 << -static_cast<signed int>(i * chunk_size + e) << std::endl;
529
530 AssertThrow(out.fail() == false, 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())
545 std::max(n_rows(), n_cols()));
546}
547
548
549
550bool
552{
553 if (chunk_size == 1)
555 else
556 return false;
557}
558
559
560
561void
562ChunkSparsityPattern::block_write(std::ostream &out) const
563{
564 AssertThrow(out.fail() == false, ExcIO());
565
566 // first the simple objects, bracketed in [...]
567 out << '[' << rows << ' ' << cols << ' ' << chunk_size << ' ' << "][";
568 // then the underlying sparsity pattern
570 out << ']';
571
572 AssertThrow(out.fail() == false, ExcIO());
573}
574
575
576
577void
579{
580 AssertThrow(in.fail() == false, ExcIO());
581
582 char c;
583
584 // first read in simple data
585 in >> c;
586 AssertThrow(c == '[', ExcIO());
587 in >> rows >> cols >> chunk_size;
588
589 in >> c;
590 AssertThrow(c == ']', ExcIO());
591 in >> c;
592 AssertThrow(c == '[', ExcIO());
593
594 // then read the underlying sparsity pattern
596
597 in >> c;
598 AssertThrow(c == ']', ExcIO());
599}
600
601
602
603std::size_t
605{
606 return (sizeof(*this) + sparsity_pattern.memory_consumption());
607}
608
609
610
611#ifndef DOXYGEN
612// explicit instantiations
613template void
614ChunkSparsityPattern::copy_from<DynamicSparsityPattern>(
616 const size_type);
617template void
618ChunkSparsityPattern::create_from<SparsityPattern>(const size_type,
619 const size_type,
620 const SparsityPattern &,
621 const size_type,
622 const bool);
623template void
624ChunkSparsityPattern::create_from<DynamicSparsityPattern>(
625 const size_type,
626 const size_type,
628 const size_type,
629 const bool);
630template void
631ChunkSparsityPattern::copy_from<float>(const FullMatrix<float> &,
632 const size_type);
633template void
634ChunkSparsityPattern::copy_from<double>(const FullMatrix<double> &,
635 const size_type);
636#endif
637
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
std::size_t size() const
Definition array_view.h:737
void create_from(const size_type m, const size_type n, const Sparsity &sparsity_pattern_for_chunks, const size_type chunk_size, const bool optimize_diagonal=true)
void add(const size_type i, const size_type j)
void block_write(std::ostream &out) const
std::size_t memory_consumption() const
bool exists(const size_type i, const size_type j) const
iterator end() const
void reinit(const size_type m, const size_type n, const size_type max_per_row, const size_type chunk_size)
void print_gnuplot(std::ostream &out) const
void block_read(std::istream &in)
ChunkSparsityPattern & operator=(const ChunkSparsityPattern &)
size_type max_entries_per_row() const
size_type n_cols() const
size_type n_nonzero_elements() const
size_type row_length(const size_type row) const
void print(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)
size_type n_rows() const
void add(const size_type i, const size_type j)
size_type n_rows() const
size_type n_cols() 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)
bool stores_only_added_elements() const
size_type bandwidth() const
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
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
std::unique_ptr< std::size_t[]> rowstart
void add(const size_type i, const size_type j)
size_type max_entries_per_row() const
iterator end() const
unsigned int row_length(const size_type row) const
std::size_t memory_consumption() 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()
#define Assert(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcEmptyObject()
static ::ExceptionBase & ExcNotQuadratic()
static ::ExceptionBase & ExcInvalidIndex(size_type arg1, size_type arg2)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
static ::ExceptionBase & ExcInvalidNumber(size_type arg1)
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)