Reference documentation for deal.II version 9.0.0
block_linear_operator.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2010 - 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 at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii_block_linear_operator_h
17 #define dealii_block_linear_operator_h
18 
19 #include <deal.II/base/config.h>
20 #include <deal.II/base/exceptions.h>
21 
22 #include <deal.II/lac/linear_operator.h>
23 
24 
25 DEAL_II_NAMESPACE_OPEN
26 
27 // Forward declarations:
28 
29 namespace internal
30 {
31  namespace BlockLinearOperatorImplementation
32  {
33  template <typename PayloadBlockType = internal::LinearOperatorImplementation::EmptyPayload>
35  }
36 }
37 
38 template <typename Number> class BlockVector;
39 
40 template <typename Range = BlockVector<double>,
41  typename Domain = Range,
42  typename BlockPayload = internal::BlockLinearOperatorImplementation::EmptyBlockPayload<> >
44 
45 template <typename Range = BlockVector<double>,
46  typename Domain = Range,
47  typename BlockPayload = internal::BlockLinearOperatorImplementation::EmptyBlockPayload<>,
48  typename BlockMatrixType>
50 block_operator(const BlockMatrixType &matrix);
51 
52 template <size_t m, size_t n,
53  typename Range = BlockVector<double>,
54  typename Domain = Range,
58 
59 template <size_t m,
60  typename Range = BlockVector<double>,
61  typename Domain = Range,
65 
66 template <size_t m,
67  typename Range = BlockVector<double>,
68  typename Domain = Range,
72 
73 // This is a workaround for a bug in <=gcc-4.7 that does not like partial
74 // template default values in combination with local lambda expressions [1]
75 //
76 // [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53624
77 //
78 // Forward declare functions with partial template defaults:
79 
80 template <typename Range = BlockVector<double>,
81  typename Domain = Range,
82  typename BlockPayload = internal::BlockLinearOperatorImplementation::EmptyBlockPayload<>,
83  typename BlockMatrixType>
85 block_diagonal_operator(const BlockMatrixType &block_matrix);
86 
87 template <typename Range = BlockVector<double>,
88  typename Domain = Range,
89  typename BlockPayload = internal::BlockLinearOperatorImplementation::EmptyBlockPayload<> >
93 
94 template <typename Range = BlockVector<double>,
95  typename Domain = Range,
96  typename BlockPayload = internal::BlockLinearOperatorImplementation::EmptyBlockPayload<> >
100 
101 // end of workaround
102 
103 
104 
173 template <typename Range, typename Domain, typename BlockPayload>
174 class BlockLinearOperator : public LinearOperator<Range, Domain, typename BlockPayload::BlockType>
175 {
176 public:
177 
179 
187  BlockLinearOperator(const BlockPayload &payload)
188  : LinearOperator<Range, Domain, typename BlockPayload::BlockType>(typename BlockPayload::BlockType(payload,payload))
189  {
190  n_block_rows = []() -> unsigned int
191  {
192  Assert(false, ExcMessage("Uninitialized BlockLinearOperator<Range, Domain>::n_block_rows called"));
193  return 0;
194  };
195 
196  n_block_cols = []() -> unsigned int
197  {
198  Assert(false, ExcMessage("Uninitialized BlockLinearOperator<Range, Domain>::n_block_cols called"));
199  return 0;
200  };
201 
202  block = [](unsigned int, unsigned int) -> BlockType
203  {
204  Assert(false, ExcMessage("Uninitialized BlockLinearOperator<Range, Domain>::block called"));
205  return BlockType();
206  };
207  }
208 
213  default;
214 
220  template <typename Op>
221  BlockLinearOperator(const Op &op)
222  {
223  *this = block_operator<Range, Domain, BlockPayload, Op>(op);
224  }
225 
231  template <size_t m, size_t n>
232  BlockLinearOperator(const std::array<std::array<BlockType, n>, m> &ops)
233  {
234  *this = block_operator<m, n, Range, Domain, BlockPayload>(ops);
235  }
236 
242  template <size_t m>
243  BlockLinearOperator(const std::array<BlockType, m> &ops)
244  {
245  *this = block_diagonal_operator<m, Range, Domain, BlockPayload>(ops);
246  }
247 
253 
258  template <typename Op>
260  {
261  *this = block_operator<Range, Domain, BlockPayload, Op>(op);
262  return *this;
263  }
264 
270  template <size_t m, size_t n>
272  operator=(const std::array<std::array<BlockType, n>, m> &ops)
273  {
274  *this = block_operator<m, n, Range, Domain, BlockPayload>(ops);
275  return *this;
276  }
277 
283  template <size_t m>
285  operator=(const std::array<BlockType, m> &ops)
286  {
287  *this = block_diagonal_operator<m, Range, Domain, BlockPayload>(ops);
288  return *this;
289  }
290 
295  std::function<unsigned int()> n_block_rows;
296 
301  std::function<unsigned int()> n_block_cols;
302 
308  std::function<BlockType(unsigned int, unsigned int)> block;
309 };
310 
311 
312 
313 namespace internal
314 {
315  namespace BlockLinearOperatorImplementation
316  {
317  // Populate the LinearOperator interfaces with the help of the
318  // BlockLinearOperator functions
319  template <typename Range, typename Domain, typename BlockPayload>
320  inline void
321  populate_linear_operator_functions(
323  {
324  op.reinit_range_vector = [=](Range &v, bool omit_zeroing_entries)
325  {
326  const unsigned int m = op.n_block_rows();
327 
328  // Reinitialize the block vector to m blocks:
329  v.reinit(m);
330 
331  // And reinitialize every individual block with reinit_range_vectors:
332  for (unsigned int i = 0; i < m; ++i)
333  op.block(i, 0).reinit_range_vector(v.block(i), omit_zeroing_entries);
334 
335  v.collect_sizes();
336  };
337 
338  op.reinit_domain_vector = [=](Domain &v, bool omit_zeroing_entries)
339  {
340  const unsigned int n = op.n_block_cols();
341 
342  // Reinitialize the block vector to n blocks:
343  v.reinit(n);
344 
345  // And reinitialize every individual block with reinit_domain_vectors:
346  for (unsigned int i = 0; i < n; ++i)
347  op.block(0, i).reinit_domain_vector(v.block(i), omit_zeroing_entries);
348 
349  v.collect_sizes();
350  };
351 
352  op.vmult = [=](Range &v, const Domain &u)
353  {
354  const unsigned int m = op.n_block_rows();
355  const unsigned int n = op.n_block_cols();
356  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
357  Assert(u.n_blocks() == n, ExcDimensionMismatch(u.n_blocks(), n));
358 
359  // Bug: We need a mechanism similar to
360  // "apply_with_intermediate_storage" for LinearOperator to do the
361  // matrix vector multiplication correctly. Currently, if u and v
362  // are equal, the first vmult will garble up the ith block and
363  // subsequent multiplications are wrong.
365  ExcMessage("BlockLinearOperator::vmult currently requires that "
366  "source and destination vectors are different memory "
367  "locations"));
368 
369  for (unsigned int i = 0; i < m; ++i)
370  {
371  op.block(i, 0).vmult(v.block(i), u.block(0));
372  for (unsigned int j = 1; j < n; ++j)
373  op.block(i, j).vmult_add(v.block(i), u.block(j));
374  }
375  };
376 
377  op.vmult_add = [=](Range &v, const Domain &u)
378  {
379  const unsigned int m = op.n_block_rows();
380  const unsigned int n = op.n_block_cols();
381  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
382  Assert(u.n_blocks() == n, ExcDimensionMismatch(u.n_blocks(), n));
383 
384  // Bug: We need a mechanism similar to
385  // "apply_with_intermediate_storage" for LinearOperator to do the
386  // matrix vector multiplication correctly. Currently, if u and v
387  // are equal, the first vmult will garble up the ith block and
388  // subsequent multiplications are wrong.
390  ExcMessage("BlockLinearOperator::vmult_add currently requires that "
391  "source and destination vectors are different memory "
392  "locations"));
393 
394  for (unsigned int i = 0; i < m; ++i)
395  for (unsigned int j = 0; j < n; ++j)
396  op.block(i, j).vmult_add(v.block(i), u.block(j));
397  };
398 
399  op.Tvmult = [=](Domain &v, const Range &u)
400  {
401  const unsigned int n = op.n_block_cols();
402  const unsigned int m = op.n_block_rows();
403  Assert(v.n_blocks() == n, ExcDimensionMismatch(v.n_blocks(), n));
404  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
405 
406  // Bug: We need a mechanism similar to
407  // "apply_with_intermediate_storage" for LinearOperator to do the
408  // matrix vector multiplication correctly. Currently, if u and v
409  // are equal, the first vmult will garble up the ith block and
410  // subsequent multiplications are wrong.
412  ExcMessage("BlockLinearOperator::Tvmult currently requires that "
413  "source and destination vectors are different memory "
414  "locations"));
415 
416  for (unsigned int i = 0; i < n; ++i)
417  {
418  op.block(0, i).Tvmult(v.block(i), u.block(0));
419  for (unsigned int j = 1; j < m; ++j)
420  op.block(j, i).Tvmult_add(v.block(i), u.block(j));
421  }
422  };
423 
424  op.Tvmult_add = [=](Domain &v, const Range &u)
425  {
426  const unsigned int n = op.n_block_cols();
427  const unsigned int m = op.n_block_rows();
428  Assert(v.n_blocks() == n, ExcDimensionMismatch(v.n_blocks(), n));
429  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
430 
431  // Bug: We need a mechanism similar to
432  // "apply_with_intermediate_storage" for LinearOperator to do the
433  // matrix vector multiplication correctly. Currently, if u and v
434  // are equal, the first vmult will garble up the ith block and
435  // subsequent multiplications are wrong.
437  ExcMessage("BlockLinearOperator::Tvmult_add currently requires that "
438  "source and destination vectors are different memory "
439  "locations"));
440 
441  for (unsigned int i = 0; i < n; ++i)
442  for (unsigned int j = 0; j < m; ++j)
443  op.block(j, i).Tvmult_add(v.block(i), u.block(j));
444  };
445  }
446 
447 
462  template <typename PayloadBlockType>
463  class EmptyBlockPayload
464  {
465  public:
469  typedef PayloadBlockType BlockType;
470 
478  template <typename... Args>
479  EmptyBlockPayload (const Args &...)
480  { }
481  };
482 
483  } /*namespace BlockLinearOperator*/
484 } /*namespace internal*/
485 
486 
487 
492 
504 template <typename Range,
505  typename Domain,
506  typename BlockPayload,
507  typename BlockMatrixType>
509 block_operator(const BlockMatrixType &block_matrix)
510 {
512 
513  BlockLinearOperator<Range, Domain, BlockPayload> return_op (BlockPayload(block_matrix,block_matrix));
514 
515  return_op.n_block_rows = [&block_matrix]() -> unsigned int
516  {
517  return block_matrix.n_block_rows();
518  };
519 
520  return_op.n_block_cols = [&block_matrix]() -> unsigned int
521  {
522  return block_matrix.n_block_cols();
523  };
524 
525  return_op.block = [&block_matrix](unsigned int i, unsigned int j) -> BlockType
526  {
527 #ifdef DEBUG
528  const unsigned int m = block_matrix.n_block_rows();
529  const unsigned int n = block_matrix.n_block_cols();
530  Assert(i < m, ExcIndexRange (i, 0, m));
531  Assert(j < n, ExcIndexRange (j, 0, n));
532 #endif
533 
534  return BlockType(block_matrix.block(i, j));
535  };
536 
537  internal::BlockLinearOperatorImplementation::populate_linear_operator_functions(return_op);
538  return return_op;
539 }
540 
541 
542 
570 template <size_t m, size_t n, typename Range, typename Domain, typename BlockPayload>
573 {
574  static_assert(m > 0 && n > 0,
575  "a blocked LinearOperator must consist of at least one block");
576 
578 
579  BlockLinearOperator<Range, Domain, BlockPayload> return_op ((BlockPayload())); // TODO: Create block payload so that this can be initialized correctly
580 
581  return_op.n_block_rows = []() -> unsigned int
582  {
583  return m;
584  };
585 
586  return_op.n_block_cols = []() -> unsigned int
587  {
588  return n;
589  };
590 
591  return_op.block = [ops](unsigned int i, unsigned int j) -> BlockType
592  {
593  Assert(i < m, ExcIndexRange (i, 0, m));
594  Assert(j < n, ExcIndexRange (j, 0, n));
595 
596  return ops[i][j];
597  };
598 
599  internal::BlockLinearOperatorImplementation::populate_linear_operator_functions(return_op);
600  return return_op;
601 }
602 
603 
604 
620 template <typename Range,
621  typename Domain,
622  typename BlockPayload,
623  typename BlockMatrixType>
625 block_diagonal_operator(const BlockMatrixType &block_matrix)
626 {
628 
629  BlockLinearOperator<Range, Domain, BlockPayload> return_op (BlockPayload(block_matrix,block_matrix));
630 
631  return_op.n_block_rows = [&block_matrix]() -> unsigned int
632  {
633  return block_matrix.n_block_rows();
634  };
635 
636  return_op.n_block_cols = [&block_matrix]() -> unsigned int
637  {
638  return block_matrix.n_block_cols();
639  };
640 
641  return_op.block = [&block_matrix](unsigned int i, unsigned int j) -> BlockType
642  {
643 #ifdef DEBUG
644  const unsigned int m = block_matrix.n_block_rows();
645  const unsigned int n = block_matrix.n_block_cols();
646  Assert(m == n, ExcDimensionMismatch(m, n));
647  Assert(i < m, ExcIndexRange (i, 0, m));
648  Assert(j < n, ExcIndexRange (j, 0, n));
649 #endif
650  if (i == j)
651  return BlockType(block_matrix.block(i, j));
652  else
653  return null_operator(BlockType(block_matrix.block(i, j)));
654  };
655 
656  internal::BlockLinearOperatorImplementation::populate_linear_operator_functions(return_op);
657  return return_op;
658 }
659 
660 
661 
679 template <size_t m, typename Range, typename Domain, typename BlockPayload>
682 {
683  static_assert(m > 0,
684  "a blockdiagonal LinearOperator must consist of at least one block");
685 
687 
688  std::array<std::array<BlockType, m>, m> new_ops;
689 
690  // This is a bit tricky. We have to make sure that the off-diagonal
691  // elements of return_op.ops are populated correctly. They must be
692  // null_operators, but with correct reinit_domain_vector and
693  // reinit_range_vector functions.
694  for (unsigned int i = 0; i < m; ++i)
695  for (unsigned int j = 0; j < m; ++j)
696  if (i == j)
697  {
698  // diagonal elements are easy:
699  new_ops[i][j] = ops[i];
700  }
701  else
702  {
703  // create a null-operator...
704  new_ops[i][j] = null_operator(ops[i]);
705  // ... and fix up reinit_domain_vector:
706  new_ops[i][j].reinit_domain_vector = ops[j].reinit_domain_vector;
707  }
708 
709  return block_operator<m,m,Range,Domain>(new_ops);
710 }
711 
712 
713 
723 template <size_t m, typename Range, typename Domain, typename BlockPayload>
726 {
727  static_assert(m > 0,
728  "a blockdiagonal LinearOperator must consist of at least "
729  "one block");
730 
732  std::array<BlockType, m> new_ops;
733  new_ops.fill(op);
734 
735  return block_diagonal_operator(new_ops);
736 }
737 
738 
739 
741 
745 
781 template <typename Range, typename Domain, typename BlockPayload>
784  const BlockLinearOperator<Domain, Range, BlockPayload> &diagonal_inverse)
785 {
786  LinearOperator<Range, Range, typename BlockPayload::BlockType> return_op ((typename BlockPayload::BlockType(diagonal_inverse)));
787 
788  return_op.reinit_range_vector = diagonal_inverse.reinit_range_vector;
789  return_op.reinit_domain_vector = diagonal_inverse.reinit_domain_vector;
790 
791  return_op.vmult = [block_operator, diagonal_inverse](Range &v, const Range &u)
792  {
793  const unsigned int m = block_operator.n_block_rows();
794  Assert(block_operator.n_block_cols() == m,
795  ExcDimensionMismatch(block_operator.n_block_cols(), m));
796  Assert(diagonal_inverse.n_block_rows() == m,
797  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
798  Assert(diagonal_inverse.n_block_cols() == m,
799  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
800  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
801  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
802 
803  if (m == 0)
804  return;
805 
806  diagonal_inverse.block(0, 0).vmult(v.block(0), u.block(0));
807  for (unsigned int i = 1; i < m; ++i)
808  {
809  auto &dst = v.block(i);
810  dst = u.block(i);
811  dst *= -1.;
812  for (unsigned int j = 0; j < i; ++j)
813  block_operator.block(i, j).vmult_add(dst, v.block(j));
814  dst *= -1.;
815  diagonal_inverse.block(i, i).vmult(dst, dst); // uses intermediate storage
816  }
817  };
818 
819  return_op.vmult_add = [block_operator, diagonal_inverse](Range &v, const Range &u)
820  {
821  const unsigned int m = block_operator.n_block_rows();
822  Assert(block_operator.n_block_cols() == m,
823  ExcDimensionMismatch(block_operator.n_block_cols(), m));
824  Assert(diagonal_inverse.n_block_rows() == m,
825  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
826  Assert(diagonal_inverse.n_block_cols() == m,
827  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
828  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
829  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
830 
831  if (m == 0)
832  return;
833 
835  typename VectorMemory<typename Range::BlockType>::Pointer tmp (vector_memory);
836 
837  diagonal_inverse.block(0, 0).vmult_add(v.block(0), u.block(0));
838 
839  for (unsigned int i = 1; i < m; ++i)
840  {
841  diagonal_inverse.block(i, i).reinit_range_vector(*tmp, /*bool omit_zeroing_entries=*/ true);
842  *tmp = u.block(i);
843  *tmp *= -1.;
844  for (unsigned int j = 0; j < i; ++j)
845  block_operator.block(i, j).vmult_add(*tmp, v.block(j));
846  *tmp *= -1.;
847  diagonal_inverse.block(i, i).vmult_add(v.block(i),*tmp);
848  }
849  };
850 
851  return return_op;
852 }
853 
854 
855 
891 template <typename Range, typename Domain, typename BlockPayload>
894  const BlockLinearOperator<Domain, Range, BlockPayload> &diagonal_inverse)
895 {
896  LinearOperator<Range, Range, typename BlockPayload::BlockType> return_op ((typename BlockPayload::BlockType(diagonal_inverse)));
897 
898  return_op.reinit_range_vector = diagonal_inverse.reinit_range_vector;
899  return_op.reinit_domain_vector = diagonal_inverse.reinit_domain_vector;
900 
901  return_op.vmult = [block_operator, diagonal_inverse](Range &v, const Range &u)
902  {
903  const unsigned int m = block_operator.n_block_rows();
904  Assert(block_operator.n_block_cols() == m,
905  ExcDimensionMismatch(block_operator.n_block_cols(), m));
906  Assert(diagonal_inverse.n_block_rows() == m,
907  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
908  Assert(diagonal_inverse.n_block_cols() == m,
909  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
910  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
911  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
912 
913  if (m == 0)
914  return;
915 
916  diagonal_inverse.block(m-1, m-1).vmult(v.block(m-1),u.block(m-1));
917 
918  for (int i = m - 2; i >= 0; --i)
919  {
920  auto &dst = v.block(i);
921  dst = u.block(i);
922  dst *= -1.;
923  for (unsigned int j = i + 1; j < m; ++j)
924  block_operator.block(i, j).vmult_add(dst, v.block(j));
925  dst *= -1.;
926  diagonal_inverse.block(i, i).vmult(dst, dst); // uses intermediate storage
927  }
928  };
929 
930  return_op.vmult_add = [block_operator, diagonal_inverse](Range &v, const Range &u)
931  {
932  const unsigned int m = block_operator.n_block_rows();
933  Assert(block_operator.n_block_cols() == m,
934  ExcDimensionMismatch(block_operator.n_block_cols(), m));
935  Assert(diagonal_inverse.n_block_rows() == m,
936  ExcDimensionMismatch(diagonal_inverse.n_block_rows(), m));
937  Assert(diagonal_inverse.n_block_cols() == m,
938  ExcDimensionMismatch(diagonal_inverse.n_block_cols(), m));
939  Assert(v.n_blocks() == m, ExcDimensionMismatch(v.n_blocks(), m));
940  Assert(u.n_blocks() == m, ExcDimensionMismatch(u.n_blocks(), m));
942  typename VectorMemory<typename Range::BlockType>::Pointer tmp (vector_memory);
943 
944  if (m == 0)
945  return;
946 
947  diagonal_inverse.block(m-1, m-1).vmult_add(v.block(m-1),u.block(m-1));
948 
949  for (int i = m - 2; i >= 0; --i)
950  {
951  diagonal_inverse.block(i, i).reinit_range_vector(*tmp, /*bool omit_zeroing_entries=*/ true);
952  *tmp = u.block(i);
953  *tmp *= -1.;
954  for (unsigned int j = i + 1; j < m; ++j)
955  block_operator.block(i, j).vmult_add(*tmp,v.block(j));
956  *tmp *= -1.;
957  diagonal_inverse.block(i, i).vmult_add(v.block(i),*tmp);
958  }
959  };
960 
961  return return_op;
962 }
963 
965 
966 DEAL_II_NAMESPACE_CLOSE
967 
968 #endif
BlockLinearOperator(const std::array< BlockType, m > &ops)
LinearOperator< Domain, Range, typename BlockPayload::BlockType > block_forward_substitution(const BlockLinearOperator< Range, Domain, BlockPayload > &block_operator, const BlockLinearOperator< Domain, Range, BlockPayload > &diagonal_inverse)
BlockLinearOperator(const Op &op)
std::function< void(Range &v, bool omit_zeroing_entries)> reinit_range_vector
std::function< void(Domain &v, const Range &u)> Tvmult_add
std::function< void(Domain &v, const Range &u)> Tvmult
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const std::array< BlockType, m > &ops)
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const Op &op)
static ::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
LinearOperator< Range, Domain, Payload > null_operator(const LinearOperator< Range, Domain, Payload > &op)
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const std::array< std::array< BlockType, n >, m > &ops)
static ::ExceptionBase & ExcMessage(std::string arg1)
BlockLinearOperator< Range, Domain, BlockPayload > block_operator(const BlockMatrixType &block_matrix)
#define Assert(cond, exc)
Definition: exceptions.h:1142
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
BlockLinearOperator< Range, Domain, BlockPayload > & operator=(const BlockLinearOperator< Range, Domain, BlockPayload > &)=default
LinearOperator< Domain, Range, typename BlockPayload::BlockType > block_back_substitution(const BlockLinearOperator< Range, Domain, BlockPayload > &block_operator, const BlockLinearOperator< Domain, Range, BlockPayload > &diagonal_inverse)
std::function< void(Range &v, const Domain &u)> vmult
std::function< void(Domain &v, bool omit_zeroing_entries)> reinit_domain_vector
BlockLinearOperator(const std::array< std::array< BlockType, n >, m > &ops)
std::function< void(Range &v, const Domain &u)> vmult_add
std::function< BlockType(unsigned int, unsigned int)> block
std::function< unsigned int()> n_block_cols
BlockLinearOperator(const BlockPayload &payload)
BlockLinearOperator< Range, Domain, BlockPayload > block_diagonal_operator(const BlockMatrixType &block_matrix)
std::function< unsigned int()> n_block_rows
static bool equal(const T *p1, const T *p2)