Reference documentation for deal.II version 9.1.0-pre
dynamic_sparsity_pattern.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2017 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #include <deal.II/base/memory_consumption.h>
17 
18 #include <deal.II/lac/dynamic_sparsity_pattern.h>
19 #include <deal.II/lac/sparsity_pattern.h>
20 
21 #include <algorithm>
22 #include <cmath>
23 #include <functional>
24 #include <numeric>
25 
26 DEAL_II_NAMESPACE_OPEN
27 
28 
29 
30 template <typename ForwardIterator>
31 void
33  ForwardIterator end,
34  const bool indices_are_sorted)
35 {
36  const int n_elements = end - begin;
37  if (n_elements <= 0)
38  return;
39 
40  const size_type stop_size = entries.size() + n_elements;
41 
42  if (indices_are_sorted == true && n_elements > 3)
43  {
44  // in debug mode, check whether the
45  // indices really are sorted.
46 #ifdef DEBUG
47  {
48  ForwardIterator test = begin, test1 = begin;
49  ++test1;
50  for (; test1 != end; ++test, ++test1)
51  Assert(*test1 > *test, ExcInternalError());
52  }
53 #endif
54 
55  if (entries.size() == 0 || entries.back() < *begin)
56  {
57  entries.insert(entries.end(), begin, end);
58  return;
59  }
60 
61  // find a possible insertion point for
62  // the first entry. check whether the
63  // first entry is a duplicate before
64  // actually doing something.
65  ForwardIterator my_it = begin;
66  size_type col = *my_it;
67  std::vector<size_type>::iterator it =
68  Utilities::lower_bound(entries.begin(), entries.end(), col);
69  while (*it == col)
70  {
71  ++my_it;
72  if (my_it == end)
73  break;
74  col = *my_it;
75  // check the very next entry in the
76  // current array
77  ++it;
78  if (it == entries.end())
79  break;
80  if (*it > col)
81  break;
82  if (*it == col)
83  continue;
84  // ok, it wasn't the very next one, do a
85  // binary search to find the insert point
86  it = Utilities::lower_bound(it, entries.end(), col);
87  if (it == entries.end())
88  break;
89  }
90  // all input entries were duplicates.
91  if (my_it == end)
92  return;
93 
94  // resize vector by just inserting the
95  // list
96  const size_type pos1 = it - entries.begin();
97  Assert(pos1 <= entries.size(), ExcInternalError());
98  entries.insert(it, my_it, end);
99  it = entries.begin() + pos1;
100  Assert(entries.size() >= (size_type)(it - entries.begin()),
101  ExcInternalError());
102 
103  // now merge the two lists.
104  std::vector<size_type>::iterator it2 = it + (end - my_it);
105 
106  // as long as there are indices both in
107  // the end of the entries list and in the
108  // input list
109  while (my_it != end && it2 != entries.end())
110  {
111  if (*my_it < *it2)
112  *it++ = *my_it++;
113  else if (*my_it == *it2)
114  {
115  *it++ = *it2++;
116  ++my_it;
117  }
118  else
119  *it++ = *it2++;
120  }
121  // in case there are indices left in the
122  // input list
123  while (my_it != end)
124  *it++ = *my_it++;
125 
126  // in case there are indices left in the
127  // end of entries
128  while (it2 != entries.end())
129  *it++ = *it2++;
130 
131  // resize and return
132  const size_type new_size = it - entries.begin();
133  Assert(new_size <= stop_size, ExcInternalError());
134  entries.resize(new_size);
135  return;
136  }
137 
138  // unsorted case or case with too few
139  // elements
140  ForwardIterator my_it = begin;
141 
142  // If necessary, increase the size of the
143  // array.
144  if (stop_size > entries.capacity())
145  entries.reserve(stop_size);
146 
147  size_type col = *my_it;
148  std::vector<size_type>::iterator it, it2;
149  // insert the first element as for one
150  // entry only first check the last
151  // element (or if line is still empty)
152  if ((entries.size() == 0) || (entries.back() < col))
153  {
154  entries.push_back(col);
155  it = entries.end() - 1;
156  }
157  else
158  {
159  // do a binary search to find the place
160  // where to insert:
161  it2 = Utilities::lower_bound(entries.begin(), entries.end(), col);
162 
163  // If this entry is a duplicate, continue
164  // immediately Insert at the right place
165  // in the vector. Vector grows
166  // automatically to fit elements. Always
167  // doubles its size.
168  if (*it2 != col)
169  it = entries.insert(it2, col);
170  else
171  it = it2;
172  }
173 
174  ++my_it;
175  // Now try to be smart and insert with
176  // bias in the direction we are
177  // walking. This has the advantage that
178  // for sorted lists, we always search in
179  // the right direction, what should
180  // decrease the work needed in here.
181  for (; my_it != end; ++my_it)
182  {
183  col = *my_it;
184  // need a special insertion command when
185  // we're at the end of the list
186  if (col > entries.back())
187  {
188  entries.push_back(col);
189  it = entries.end() - 1;
190  }
191  // search to the right (preferred search
192  // direction)
193  else if (col > *it)
194  {
195  it2 = Utilities::lower_bound(it++, entries.end(), col);
196  if (*it2 != col)
197  it = entries.insert(it2, col);
198  }
199  // search to the left
200  else if (col < *it)
201  {
202  it2 = Utilities::lower_bound(entries.begin(), it, col);
203  if (*it2 != col)
204  it = entries.insert(it2, col);
205  }
206  // if we're neither larger nor smaller,
207  // then this was a duplicate and we can
208  // just continue.
209  }
210 }
211 
212 
215 {
216  return entries.capacity() * sizeof(size_type) + sizeof(Line);
217 }
218 
219 
221  : have_entries(false)
222  , rows(0)
223  , cols(0)
224  , rowset(0)
225 {}
226 
227 
228 
230  : Subscriptor()
231  , have_entries(false)
232  , rows(0)
233  , cols(0)
234  , rowset(0)
235 {
236  (void)s;
237  Assert(s.rows == 0 && s.cols == 0,
238  ExcMessage(
239  "This constructor can only be called if the provided argument "
240  "is the sparsity pattern for an empty matrix. This constructor can "
241  "not be used to copy-construct a non-empty sparsity pattern."));
242 }
243 
244 
245 
247  const size_type n,
248  const IndexSet &rowset_)
249  : have_entries(false)
250  , rows(0)
251  , cols(0)
252  , rowset(0)
253 {
254  reinit(m, n, rowset_);
255 }
256 
257 
259  : have_entries(false)
260  , rows(0)
261  , cols(0)
262  , rowset(0)
263 {
264  reinit(rowset_.size(), rowset_.size(), rowset_);
265 }
266 
267 
269  : have_entries(false)
270  , rows(0)
271  , cols(0)
272  , rowset(0)
273 {
274  reinit(n, n);
275 }
276 
277 
278 
281 {
282  (void)s;
283  Assert(s.rows == 0 && s.cols == 0,
284  ExcMessage(
285  "This operator can only be called if the provided argument "
286  "is the sparsity pattern for an empty matrix. This operator can "
287  "not be used to copy a non-empty sparsity pattern."));
288 
289  Assert(rows == 0 && cols == 0,
290  ExcMessage("This operator can only be called if the current object is"
291  "empty."));
292 
293  return *this;
294 }
295 
296 
297 
298 void
300  const size_type n,
301  const IndexSet &rowset_)
302 {
303  have_entries = false;
304  rows = m;
305  cols = n;
306  rowset = rowset_;
307 
308  Assert(rowset.size() == 0 || rowset.size() == m,
309  ExcMessage(
310  "The IndexSet argument to this function needs to either "
311  "be empty (indicating the complete set of rows), or have size "
312  "equal to the desired number of rows as specified by the "
313  "first argument to this function. (Of course, the number "
314  "of indices in this IndexSet may be less than the number "
315  "of rows, but the *size* of the IndexSet must be equal.)"));
316 
317  std::vector<Line> new_lines(rowset.size() == 0 ? rows : rowset.n_elements());
318  lines.swap(new_lines);
319 }
320 
321 
322 
323 void
325 {}
326 
327 
328 
329 bool
331 {
332  return ((rows == 0) && (cols == 0));
333 }
334 
335 
336 
339 {
340  if (!have_entries)
341  return 0;
342 
343  size_type m = 0;
344  for (size_type i = 0; i < lines.size(); ++i)
345  {
346  m = std::max(m, static_cast<size_type>(lines[i].entries.size()));
347  }
348 
349  return m;
350 }
351 
352 
353 
354 bool
356 {
357  Assert(i < rows, ExcIndexRange(i, 0, rows));
358  Assert(j < cols, ExcIndexRange(j, 0, cols));
360 
361  if (!have_entries)
362  return false;
363 
364  const size_type rowindex =
365  rowset.size() == 0 ? i : rowset.index_within_set(i);
366 
367  return std::binary_search(lines[rowindex].entries.begin(),
368  lines[rowindex].entries.end(),
369  j);
370 }
371 
372 
373 
374 void
376 {
378 
379  // loop over all elements presently
380  // in the sparsity pattern and add
381  // the transpose element. note:
382  //
383  // 1. that the sparsity pattern
384  // changes which we work on, but
385  // not the present row
386  //
387  // 2. that the @p{add} function can
388  // be called on elements that
389  // already exist without any harm
390  for (size_type row = 0; row < lines.size(); ++row)
391  {
392  const size_type rowindex =
393  rowset.size() == 0 ? row : rowset.nth_index_in_set(row);
394 
395  for (std::vector<size_type>::const_iterator j =
396  lines[row].entries.begin();
397  j != lines[row].entries.end();
398  ++j)
399  // add the transpose entry if
400  // this is not the diagonal
401  if (rowindex != *j)
402  add(*j, rowindex);
403  }
404 }
405 
406 
407 
408 template <typename SparsityPatternTypeLeft, typename SparsityPatternTypeRight>
409 void
411  const SparsityPatternTypeLeft & left,
412  const SparsityPatternTypeRight &right)
413 {
414  Assert(left.n_cols() == right.n_rows(),
415  ExcDimensionMismatch(left.n_cols(), right.n_rows()));
416 
417  this->reinit(left.n_rows(), right.n_cols());
418 
419  typename SparsityPatternTypeLeft::iterator it_left = left.begin(),
420  end_left = left.end();
421  for (; it_left != end_left; ++it_left)
422  {
423  const unsigned int j = it_left->column();
424 
425  // We are sitting on entry (i,j) of the left sparsity pattern. We then
426  // need to add all entries (i,k) to the final sparsity pattern where (j,k)
427  // exists in the right sparsity pattern -- i.e., we need to iterate over
428  // row j.
429  typename SparsityPatternTypeRight::iterator it_right = right.begin(j),
430  end_right = right.end(j);
431  for (; it_right != end_right; ++it_right)
432  this->add(it_left->row(), it_right->column());
433  }
434 }
435 
436 
437 
438 void
439 DynamicSparsityPattern::print(std::ostream &out) const
440 {
441  for (size_type row = 0; row < lines.size(); ++row)
442  {
443  out << '[' << (rowset.size() == 0 ? row : rowset.nth_index_in_set(row));
444 
445  for (std::vector<size_type>::const_iterator j =
446  lines[row].entries.begin();
447  j != lines[row].entries.end();
448  ++j)
449  out << ',' << *j;
450 
451  out << ']' << std::endl;
452  }
453 
454  AssertThrow(out, ExcIO());
455 }
456 
457 
458 
459 void
461 {
462  for (size_type row = 0; row < lines.size(); ++row)
463  {
464  const size_type rowindex =
465  rowset.size() == 0 ? row : rowset.nth_index_in_set(row);
466 
467  for (std::vector<size_type>::const_iterator j =
468  lines[row].entries.begin();
469  j != lines[row].entries.end();
470  ++j)
471  // while matrix entries are usually
472  // written (i,j), with i vertical and
473  // j horizontal, gnuplot output is
474  // x-y, that is we have to exchange
475  // the order of output
476  out << *j << " " << -static_cast<signed int>(rowindex) << std::endl;
477  }
478 
479 
480  AssertThrow(out, ExcIO());
481 }
482 
483 
484 
487 {
488  size_type b = 0;
489  for (size_type row = 0; row < lines.size(); ++row)
490  {
491  const size_type rowindex =
492  rowset.size() == 0 ? row : rowset.nth_index_in_set(row);
493 
494  for (std::vector<size_type>::const_iterator j =
495  lines[row].entries.begin();
496  j != lines[row].entries.end();
497  ++j)
498  if (static_cast<size_type>(std::abs(static_cast<int>(rowindex - *j))) >
499  b)
500  b = std::abs(static_cast<signed int>(rowindex - *j));
501  }
502 
503  return b;
504 }
505 
506 
507 
510 {
511  if (!have_entries)
512  return 0;
513 
514  size_type n = 0;
515  for (size_type i = 0; i < lines.size(); ++i)
516  {
517  n += lines[i].entries.size();
518  }
519 
520  return n;
521 }
522 
523 
526 {
527  size_type mem = sizeof(DynamicSparsityPattern) +
529  sizeof(rowset);
530 
531  for (size_type i = 0; i < lines.size(); ++i)
533 
534  return mem;
535 }
536 
537 
538 // explicit instantiations
539 template void
541 template void
543  const size_type *,
544  const bool);
545 #ifndef DEAL_II_VECTOR_ITERATOR_IS_POINTER
546 template void
547 DynamicSparsityPattern::Line::add_entries(std::vector<size_type>::iterator,
548  std::vector<size_type>::iterator,
549  const bool);
550 #endif
551 
552 template void
554  const DynamicSparsityPattern &);
555 template void
557  const SparsityPattern &);
558 template void
560  const DynamicSparsityPattern &);
561 template void
563  const SparsityPattern &);
564 
565 DEAL_II_NAMESPACE_CLOSE
Iterator lower_bound(Iterator first, Iterator last, const T &val)
Definition: utilities.h:939
bool exists(const size_type i, const size_type j) const
static::ExceptionBase & ExcIO()
void add(const size_type i, const size_type j)
void print(std::ostream &out) const
size_type n_elements() const
Definition: index_set.h:1743
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
size_type size() const
Definition: index_set.h:1611
static::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
static::ExceptionBase & ExcMessage(std::string arg1)
size_type memory_consumption() const
#define Assert(cond, exc)
Definition: exceptions.h:1227
void add_entries(ForwardIterator begin, ForwardIterator end, const bool indices_are_sorted)
void print_gnuplot(std::ostream &out) const
void reinit(const size_type m, const size_type n, const IndexSet &rowset=IndexSet())
static::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
size_type max_entries_per_row() const
void compute_mmult_pattern(const SparsityPatternTypeLeft &left, const SparsityPatternTypeRight &right)
static::ExceptionBase & ExcNotQuadratic()
types::global_dof_index size_type
DynamicSparsityPattern & operator=(const DynamicSparsityPattern &)
size_type index_within_set(const size_type global_index) const
Definition: index_set.h:1834
bool is_element(const size_type index) const
Definition: index_set.h:1676
size_type n_nonzero_elements() const
size_type nth_index_in_set(const unsigned int local_index) const
Definition: index_set.h:1793
std::enable_if< std::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)
static::ExceptionBase & ExcInternalError()