1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2020 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 
ChunkSparsityPattern()25 ChunkSparsityPattern::ChunkSparsityPattern()
26 {
27   reinit(0, 0, 0, 1);
28 }
29 
30 
31 
ChunkSparsityPattern(const ChunkSparsityPattern & s)32 ChunkSparsityPattern::ChunkSparsityPattern(const ChunkSparsityPattern &s)
33   : Subscriptor()
34   , chunk_size(s.chunk_size)
35   , sparsity_pattern(s.sparsity_pattern)
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 
ChunkSparsityPattern(const size_type m,const size_type n,const size_type max_per_row,const size_type chunk_size)48 ChunkSparsityPattern::ChunkSparsityPattern(const size_type m,
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 
ChunkSparsityPattern(const size_type m,const size_type n,const std::vector<size_type> & row_lengths,const size_type chunk_size)60 ChunkSparsityPattern::ChunkSparsityPattern(
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 
ChunkSparsityPattern(const size_type n,const size_type max_per_row,const size_type chunk_size)73 ChunkSparsityPattern::ChunkSparsityPattern(const size_type n,
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 
ChunkSparsityPattern(const size_type m,const std::vector<size_type> & row_lengths,const size_type chunk_size)82 ChunkSparsityPattern::ChunkSparsityPattern(
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 
94 ChunkSparsityPattern &
operator =(const ChunkSparsityPattern & s)95 ChunkSparsityPattern::operator=(const ChunkSparsityPattern &s)
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
108   sparsity_pattern = s.sparsity_pattern;
109 
110   return *this;
111 }
112 
113 
114 
115 void
reinit(const size_type m,const size_type n,const size_type max_per_row,const size_type chunk_size)116 ChunkSparsityPattern::reinit(const size_type m,
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
reinit(const size_type m,const size_type n,const ArrayView<const size_type> & row_lengths,const size_type chunk_size)131 ChunkSparsityPattern::reinit(const size_type                   m,
132                              const size_type                   n,
133                              const ArrayView<const size_type> &row_lengths,
134                              const size_type                   chunk_size)
135 {
136   Assert(row_lengths.size() == m, ExcInvalidNumber(m));
137   Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
138 
139   rows = m;
140   cols = n;
141 
142   this->chunk_size = chunk_size;
143 
144   // pass down to the necessary information to the underlying object. we need
145   // to calculate how many chunks we need: we need to round up (m/chunk_size)
146   // and (n/chunk_size). rounding up in integer arithmetic equals
147   // ((m+chunk_size-1)/chunk_size):
148   const size_type m_chunks = (m + chunk_size - 1) / chunk_size,
149                   n_chunks = (n + chunk_size - 1) / chunk_size;
150 
151   // compute the maximum number of chunks in each row. the passed array
152   // denotes the number of entries in each row of the big matrix -- in the
153   // worst case, these are all in independent chunks, so we have to calculate
154   // it as follows (as an example: let chunk_size==2, row_lengths={2,2,...},
155   // and entries in row zero at columns {0,2} and for row one at {4,6} -->
156   // we'll need 4 chunks for the first chunk row!) :
157   std::vector<unsigned int> chunk_row_lengths(m_chunks, 0);
158   for (size_type i = 0; i < m; ++i)
159     chunk_row_lengths[i / chunk_size] += row_lengths[i];
160 
161   // for the case that the reduced sparsity pattern optimizes the diagonal but
162   // the actual sparsity pattern does not, need to take one more entry in the
163   // row to fit the user-required entry
164   if (m != n && m_chunks == n_chunks)
165     for (unsigned int i = 0; i < m_chunks; ++i)
166       ++chunk_row_lengths[i];
167 
168   sparsity_pattern.reinit(m_chunks, n_chunks, chunk_row_lengths);
169 }
170 
171 
172 
173 void
compress()174 ChunkSparsityPattern::compress()
175 {
176   sparsity_pattern.compress();
177 }
178 
179 
180 
181 template <typename SparsityPatternType>
182 void
copy_from(const SparsityPatternType & dsp,const size_type chunk_size)183 ChunkSparsityPattern::copy_from(const SparsityPatternType &dsp,
184                                 const size_type            chunk_size)
185 {
186   Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
187   this->chunk_size = chunk_size;
188   rows             = dsp.n_rows();
189   cols             = dsp.n_cols();
190 
191   // simple case: just use the given sparsity pattern
192   if (chunk_size == 1)
193     {
194       sparsity_pattern.copy_from(dsp);
195       return;
196     }
197 
198   // create a temporary compressed sparsity pattern that collects all entries
199   // from the input sparsity pattern and then initialize the underlying small
200   // sparsity pattern
201   const size_type m_chunks = (dsp.n_rows() + chunk_size - 1) / chunk_size,
202                   n_chunks = (dsp.n_cols() + chunk_size - 1) / chunk_size;
203   DynamicSparsityPattern temporary_sp(m_chunks, n_chunks);
204 
205   for (size_type row = 0; row < dsp.n_rows(); ++row)
206     {
207       const size_type reduced_row = row / chunk_size;
208 
209       // TODO: This could be made more efficient if we cached the
210       // previous column and only called add() if the previous and the
211       // current column lead to different chunk columns
212       for (typename SparsityPatternType::iterator col_num = dsp.begin(row);
213            col_num != dsp.end(row);
214            ++col_num)
215         temporary_sp.add(reduced_row, col_num->column() / chunk_size);
216     }
217 
218   sparsity_pattern.copy_from(temporary_sp);
219 }
220 
221 
222 
223 template <typename number>
224 void
copy_from(const FullMatrix<number> & matrix,const size_type chunk_size)225 ChunkSparsityPattern::copy_from(const FullMatrix<number> &matrix,
226                                 const size_type           chunk_size)
227 {
228   Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
229 
230   // count number of entries per row, then initialize the underlying sparsity
231   // pattern. remember to also allocate space for the diagonal entry (if that
232   // hasn't happened yet) if m==n since we always allocate that for diagonal
233   // matrices
234   std::vector<size_type> entries_per_row(matrix.m(), 0);
235   for (size_type row = 0; row < matrix.m(); ++row)
236     {
237       for (size_type col = 0; col < matrix.n(); ++col)
238         if (matrix(row, col) != 0)
239           ++entries_per_row[row];
240 
241       if ((matrix.m() == matrix.n()) && (matrix(row, row) == 0))
242         ++entries_per_row[row];
243     }
244 
245   reinit(matrix.m(), matrix.n(), entries_per_row, chunk_size);
246 
247   // then actually fill it
248   for (size_type row = 0; row < matrix.m(); ++row)
249     for (size_type col = 0; col < matrix.n(); ++col)
250       if (matrix(row, col) != 0)
251         add(row, col);
252 
253   // finally compress
254   compress();
255 }
256 
257 
258 
259 void
reinit(const size_type m,const size_type n,const std::vector<size_type> & row_lengths,const size_type chunk_size)260 ChunkSparsityPattern::reinit(const size_type               m,
261                              const size_type               n,
262                              const std::vector<size_type> &row_lengths,
263                              const size_type               chunk_size)
264 {
265   Assert(chunk_size > 0, ExcInvalidNumber(chunk_size));
266 
267   reinit(m, n, make_array_view(row_lengths), chunk_size);
268 }
269 
270 
271 
272 namespace internal
273 {
274   namespace
275   {
276     template <typename SparsityPatternType>
277     void
copy_sparsity(const SparsityPatternType & src,SparsityPattern & dst)278     copy_sparsity(const SparsityPatternType &src, SparsityPattern &dst)
279     {
280       dst.copy_from(src);
281     }
282 
283     void
copy_sparsity(const SparsityPattern & src,SparsityPattern & dst)284     copy_sparsity(const SparsityPattern &src, SparsityPattern &dst)
285     {
286       dst = src;
287     }
288   } // namespace
289 } // namespace internal
290 
291 
292 
293 template <typename Sparsity>
294 void
create_from(const size_type m,const size_type n,const Sparsity & sparsity_pattern_for_chunks,const size_type chunk_size_in,const bool)295 ChunkSparsityPattern::create_from(const size_type m,
296                                   const size_type n,
297                                   const Sparsity &sparsity_pattern_for_chunks,
298                                   const size_type chunk_size_in,
299                                   const bool)
300 {
301   Assert(m > (sparsity_pattern_for_chunks.n_rows() - 1) * chunk_size_in &&
302            m <= sparsity_pattern_for_chunks.n_rows() * chunk_size_in,
303          ExcMessage("Number of rows m is not compatible with chunk size "
304                     "and number of rows in sparsity pattern for the chunks."));
305   Assert(n > (sparsity_pattern_for_chunks.n_cols() - 1) * chunk_size_in &&
306            n <= sparsity_pattern_for_chunks.n_cols() * chunk_size_in,
307          ExcMessage(
308            "Number of columns m is not compatible with chunk size "
309            "and number of columns in sparsity pattern for the chunks."));
310 
311   internal::copy_sparsity(sparsity_pattern_for_chunks, sparsity_pattern);
312   chunk_size = chunk_size_in;
313   rows       = m;
314   cols       = n;
315 }
316 
317 
318 
319 bool
empty() const320 ChunkSparsityPattern::empty() const
321 {
322   return sparsity_pattern.empty();
323 }
324 
325 
326 
327 ChunkSparsityPattern::size_type
max_entries_per_row() const328 ChunkSparsityPattern::max_entries_per_row() const
329 {
330   return sparsity_pattern.max_entries_per_row() * chunk_size;
331 }
332 
333 
334 
335 void
add(const size_type i,const size_type j)336 ChunkSparsityPattern::add(const size_type i, const size_type j)
337 {
338   Assert(i < rows, ExcInvalidIndex(i, rows));
339   Assert(j < cols, ExcInvalidIndex(j, cols));
340 
341   sparsity_pattern.add(i / chunk_size, j / chunk_size);
342 }
343 
344 
345 bool
exists(const size_type i,const size_type j) const346 ChunkSparsityPattern::exists(const size_type i, const size_type j) const
347 {
348   AssertIndexRange(i, rows);
349   AssertIndexRange(j, cols);
350 
351   return sparsity_pattern.exists(i / chunk_size, j / chunk_size);
352 }
353 
354 
355 
356 void
symmetrize()357 ChunkSparsityPattern::symmetrize()
358 {
359   // matrix must be square. note that the for some matrix sizes, the current
360   // sparsity pattern may not be square even if the underlying sparsity
361   // pattern is (e.g. a 10x11 matrix with chunk_size 4)
362   Assert(rows == cols, ExcNotQuadratic());
363 
364   sparsity_pattern.symmetrize();
365 }
366 
367 
368 
369 ChunkSparsityPattern::size_type
row_length(const size_type i) const370 ChunkSparsityPattern::row_length(const size_type i) const
371 {
372   AssertIndexRange(i, rows);
373 
374   // find out if we did padding and if this row is affected by it
375   if (n_cols() % chunk_size == 0)
376     return sparsity_pattern.row_length(i / chunk_size) * chunk_size;
377   else
378     // if columns don't align, then just iterate over all chunks and see
379     // what this leads to
380     {
381       SparsityPattern::const_iterator p =
382                                         sparsity_pattern.begin(i / chunk_size),
383                                       end =
384                                         sparsity_pattern.end(i / chunk_size);
385       unsigned int n = 0;
386       for (; p != end; ++p)
387         if (p->column() != sparsity_pattern.n_cols() - 1)
388           n += chunk_size;
389         else
390           n += (n_cols() % chunk_size);
391       return n;
392     }
393 }
394 
395 
396 
397 ChunkSparsityPattern::size_type
n_nonzero_elements() const398 ChunkSparsityPattern::n_nonzero_elements() const
399 {
400   if ((n_rows() % chunk_size == 0) && (n_cols() % chunk_size == 0))
401     return (sparsity_pattern.n_nonzero_elements() * chunk_size * chunk_size);
402   else
403     // some of the chunks reach beyond the extent of this matrix. this
404     // requires a somewhat more complicated computations, in particular if the
405     // columns don't align
406     {
407       if ((n_rows() % chunk_size != 0) && (n_cols() % chunk_size == 0))
408         {
409           // columns align with chunks, but not rows
410           size_type n =
411             sparsity_pattern.n_nonzero_elements() * chunk_size * chunk_size;
412           n -= (sparsity_pattern.n_rows() * chunk_size - n_rows()) *
413                sparsity_pattern.row_length(sparsity_pattern.n_rows() - 1) *
414                chunk_size;
415           return n;
416         }
417 
418       else
419         {
420           // if columns don't align, then just iterate over all chunks and see
421           // what this leads to. follow the advice in the documentation of the
422           // sparsity pattern iterators to do the loop over individual rows,
423           // rather than all elements
424           size_type n = 0;
425 
426           for (size_type row = 0; row < sparsity_pattern.n_rows(); ++row)
427             {
428               SparsityPattern::const_iterator p = sparsity_pattern.begin(row);
429               for (; p != sparsity_pattern.end(row); ++p)
430                 if ((row != sparsity_pattern.n_rows() - 1) &&
431                     (p->column() != sparsity_pattern.n_cols() - 1))
432                   n += chunk_size * chunk_size;
433                 else if ((row == sparsity_pattern.n_rows() - 1) &&
434                          (p->column() != sparsity_pattern.n_cols() - 1))
435                   // last chunk row, but not last chunk column. only a smaller
436                   // number (n_rows % chunk_size) of rows actually exist
437                   n += (n_rows() % chunk_size) * chunk_size;
438                 else if ((row != sparsity_pattern.n_rows() - 1) &&
439                          (p->column() == sparsity_pattern.n_cols() - 1))
440                   // last chunk column, but not row
441                   n += (n_cols() % chunk_size) * chunk_size;
442                 else
443                   // bottom right chunk
444                   n += (n_cols() % chunk_size) * (n_rows() % chunk_size);
445             }
446 
447           return n;
448         }
449     }
450 }
451 
452 
453 
454 void
print(std::ostream & out) const455 ChunkSparsityPattern::print(std::ostream &out) const
456 {
457   Assert((sparsity_pattern.rowstart != nullptr) &&
458            (sparsity_pattern.colnums != nullptr),
459          ExcEmptyObject());
460 
461   AssertThrow(out, ExcIO());
462 
463   for (size_type i = 0; i < sparsity_pattern.rows; ++i)
464     for (size_type d = 0; (d < chunk_size) && (i * chunk_size + d < n_rows());
465          ++d)
466       {
467         out << '[' << i * chunk_size + d;
468         for (size_type j = sparsity_pattern.rowstart[i];
469              j < sparsity_pattern.rowstart[i + 1];
470              ++j)
471           if (sparsity_pattern.colnums[j] != sparsity_pattern.invalid_entry)
472             for (size_type e = 0;
473                  ((e < chunk_size) &&
474                   (sparsity_pattern.colnums[j] * chunk_size + e < n_cols()));
475                  ++e)
476               out << ',' << sparsity_pattern.colnums[j] * chunk_size + e;
477         out << ']' << std::endl;
478       }
479 
480   AssertThrow(out, ExcIO());
481 }
482 
483 
484 
485 void
print_gnuplot(std::ostream & out) const486 ChunkSparsityPattern::print_gnuplot(std::ostream &out) const
487 {
488   Assert((sparsity_pattern.rowstart != nullptr) &&
489            (sparsity_pattern.colnums != nullptr),
490          ExcEmptyObject());
491 
492   AssertThrow(out, ExcIO());
493 
494   // for each entry in the underlying sparsity pattern, repeat everything
495   // chunk_size x chunk_size times
496   for (size_type i = 0; i < sparsity_pattern.rows; ++i)
497     for (size_type j = sparsity_pattern.rowstart[i];
498          j < sparsity_pattern.rowstart[i + 1];
499          ++j)
500       if (sparsity_pattern.colnums[j] != sparsity_pattern.invalid_entry)
501         for (size_type d = 0;
502              ((d < chunk_size) &&
503               (sparsity_pattern.colnums[j] * chunk_size + d < n_cols()));
504              ++d)
505           for (size_type e = 0;
506                (e < chunk_size) && (i * chunk_size + e < n_rows());
507                ++e)
508             // while matrix entries are usually written (i,j), with i vertical
509             // and j horizontal, gnuplot output is x-y, that is we have to
510             // exchange the order of output
511             out << sparsity_pattern.colnums[j] * chunk_size + d << " "
512                 << -static_cast<signed int>(i * chunk_size + e) << std::endl;
513 
514   AssertThrow(out, ExcIO());
515 }
516 
517 
518 
519 ChunkSparsityPattern::size_type
bandwidth() const520 ChunkSparsityPattern::bandwidth() const
521 {
522   // calculate the bandwidth from that of the underlying sparsity
523   // pattern. note that even if the bandwidth of that is zero, then the
524   // bandwidth of the chunky pattern is chunk_size-1, if it is 1 then the
525   // chunky pattern has chunk_size+(chunk_size-1), etc
526   //
527   // we'll cut it off at max(n(),m())
528   return std::min(sparsity_pattern.bandwidth() * chunk_size + (chunk_size - 1),
529                   std::max(n_rows(), n_cols()));
530 }
531 
532 
533 
534 bool
stores_only_added_elements() const535 ChunkSparsityPattern::stores_only_added_elements() const
536 {
537   if (chunk_size == 1)
538     return sparsity_pattern.stores_only_added_elements();
539   else
540     return false;
541 }
542 
543 
544 
545 void
block_write(std::ostream & out) const546 ChunkSparsityPattern::block_write(std::ostream &out) const
547 {
548   AssertThrow(out, ExcIO());
549 
550   // first the simple objects, bracketed in [...]
551   out << '[' << rows << ' ' << cols << ' ' << chunk_size << ' ' << "][";
552   // then the underlying sparsity pattern
553   sparsity_pattern.block_write(out);
554   out << ']';
555 
556   AssertThrow(out, ExcIO());
557 }
558 
559 
560 
561 void
block_read(std::istream & in)562 ChunkSparsityPattern::block_read(std::istream &in)
563 {
564   AssertThrow(in, ExcIO());
565 
566   char c;
567 
568   // first read in simple data
569   in >> c;
570   AssertThrow(c == '[', ExcIO());
571   in >> rows >> cols >> chunk_size;
572 
573   in >> c;
574   AssertThrow(c == ']', ExcIO());
575   in >> c;
576   AssertThrow(c == '[', ExcIO());
577 
578   // then read the underlying sparsity pattern
579   sparsity_pattern.block_read(in);
580 
581   in >> c;
582   AssertThrow(c == ']', ExcIO());
583 }
584 
585 
586 
587 std::size_t
memory_consumption() const588 ChunkSparsityPattern::memory_consumption() const
589 {
590   return (sizeof(*this) + sparsity_pattern.memory_consumption());
591 }
592 
593 
594 
595 #ifndef DOXYGEN
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 size_type,
603                                                    const size_type,
604                                                    const SparsityPattern &,
605                                                    const size_type,
606                                                    const bool);
607 template void
608 ChunkSparsityPattern::create_from<DynamicSparsityPattern>(
609   const size_type,
610   const size_type,
611   const DynamicSparsityPattern &,
612   const size_type,
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 #endif
621 
622 DEAL_II_NAMESPACE_CLOSE
623