Reference documentation for deal.II version 8.4.2
linear_operator.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2014 - 2016 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__linear_operator_h
17 #define dealii__linear_operator_h
18 
19 #include <deal.II/base/config.h>
20 #include <deal.II/base/exceptions.h>
21 #include <deal.II/lac/vector_memory.h>
22 
23 #ifdef DEAL_II_WITH_CXX11
24 
25 #include <array>
26 #include <functional>
27 #include <type_traits>
28 
29 DEAL_II_NAMESPACE_OPEN
30 
31 // Forward declarations:
32 
33 template <typename Number> class Vector;
34 
35 template <typename Range = Vector<double>, typename Domain = Range>
37 
38 template <typename Range = Vector<double>,
39  typename Domain = Range,
40  typename OperatorExemplar,
41  typename Matrix>
42 LinearOperator<Range, Domain> linear_operator (const OperatorExemplar &,
43  const Matrix &);
44 
45 template <typename Range = Vector<double>,
46  typename Domain = Range,
47  typename Matrix>
49 
50 template <typename Range = Vector<double>,
51  typename Domain = Range>
54 
55 
115 template <typename Range, typename Domain> class LinearOperator
116 {
117 public:
118 
125  :
126  is_null_operator(false)
127  {
128 
129  vmult = [](Range &, const Domain &)
130  {
131  Assert(false, ExcMessage("Uninitialized LinearOperator<Range, "
132  "Domain>::vmult called"));
133  };
134 
135  vmult_add = [](Range &, const Domain &)
136  {
137  Assert(false, ExcMessage("Uninitialized LinearOperator<Range, "
138  "Domain>::vmult_add called"));
139  };
140 
141  Tvmult = [](Domain &, const Range &)
142  {
143  Assert(false, ExcMessage("Uninitialized LinearOperator<Range, "
144  "Domain>::Tvmult called"));
145  };
146 
147  Tvmult_add = [](Domain &, const Range &)
148  {
149  Assert(false, ExcMessage("Uninitialized LinearOperator<Range, "
150  "Domain>::Tvmult_add called"));
151  };
152 
153  reinit_range_vector = [](Range &, bool)
154  {
155  Assert(false, ExcMessage("Uninitialized LinearOperator<Range, "
156  "Domain>::reinit_range_vector method called"));
157  };
158 
159  reinit_domain_vector = [](Domain &, bool)
160  {
161  Assert(false, ExcMessage("Uninitialized LinearOperator<Range, "
162  "Domain>::reinit_domain_vector method called"));
163  };
164  }
165 
169  LinearOperator (const LinearOperator<Range, Domain> &) = default;
170 
176  template<typename Op,
177  typename = typename std::enable_if<!std::is_base_of<LinearOperator<Range, Domain>, Op>::value>::type>
178  LinearOperator (const Op &op)
179  {
180  *this = linear_operator<Range, Domain, Op>(op);
181  }
182 
187  operator=(const LinearOperator<Range, Domain> &) = default;
188 
193  template <typename Op,
194  typename = typename std::enable_if<!std::is_base_of<LinearOperator<Range, Domain>, Op>::value>::type>
196  {
197  *this = linear_operator<Range, Domain, Op>(op);
198  return *this;
199  }
200 
205  std::function<void(Range &v, const Domain &u)> vmult;
206 
211  std::function<void(Range &v, const Domain &u)> vmult_add;
212 
217  std::function<void(Domain &v, const Range &u)> Tvmult;
218 
223  std::function<void(Domain &v, const Range &u)> Tvmult_add;
224 
232  std::function<void(Range &v, bool omit_zeroing_entries)> reinit_range_vector;
233 
241  std::function<void(Domain &v, bool omit_zeroing_entries)> reinit_domain_vector;
242 
247 
254  {
255  *this = *this + second_op;
256  return *this;
257  }
258 
265  {
266  *this = *this - second_op;
267  return *this;
268  }
269 
276  {
277  *this = *this * second_op;
278  return *this;
279  }
280 
286  operator*=(typename Domain::value_type number)
287  {
288  *this = *this * number;
289  return *this;
290  }
291 
298 
300 };
301 
302 
307 
317 template <typename Range, typename Domain>
320  const LinearOperator<Range, Domain> &second_op)
321 {
322  if (first_op.is_null_operator)
323  {
324  return second_op;
325  }
326  else if (second_op.is_null_operator)
327  {
328  return first_op;
329  }
330  else
331  {
333 
334  return_op.reinit_range_vector = first_op.reinit_range_vector;
335  return_op.reinit_domain_vector = first_op.reinit_domain_vector;
336 
337  // ensure to have valid computation objects by catching first_op and
338  // second_op by value
339 
340  return_op.vmult = [first_op, second_op](Range &v, const Domain &u)
341  {
342  first_op.vmult(v, u);
343  second_op.vmult_add(v, u);
344  };
345 
346  return_op.vmult_add = [first_op, second_op](Range &v, const Domain &u)
347  {
348  first_op.vmult_add(v, u);
349  second_op.vmult_add(v, u);
350  };
351 
352  return_op.Tvmult = [first_op, second_op](Domain &v, const Range &u)
353  {
354  second_op.Tvmult(v, u);
355  first_op.Tvmult_add(v, u);
356  };
357 
358  return_op.Tvmult_add = [first_op, second_op](Domain &v, const Range &u)
359  {
360  second_op.Tvmult_add(v, u);
361  first_op.Tvmult_add(v, u);
362  };
363 
364  return return_op;
365  }
366 }
367 
368 
378 template <typename Range, typename Domain>
381  const LinearOperator<Range, Domain> &second_op)
382 {
383  if (first_op.is_null_operator)
384  {
385  return -1 * second_op;
386  }
387  else if (second_op.is_null_operator)
388  {
389  return first_op;
390  }
391  else
392  {
393  // implement with addition and scalar multiplication
394  return first_op + (-1. * second_op);
395  }
396 }
397 
398 
416 template <typename Range, typename Domain>
418 operator*(typename Range::value_type number,
420 {
421  static_assert(
422  std::is_convertible<typename Range::value_type, typename Domain::value_type>::value,
423  "Range and Domain must have implicitly convertible 'value_type's");
424 
425  if (op.is_null_operator)
426  {
427  return op;
428  }
429  else if (number == 0.)
430  {
431  return null_operator(op);
432  }
433  else
434  {
435  LinearOperator<Range, Domain> return_op = op;
436 
437  // ensure to have valid computation objects by catching number and op by
438  // value
439 
440  return_op.vmult = [number, op](Range &v, const Domain &u)
441  {
442  op.vmult(v,u);
443  v *= number;
444  };
445 
446  return_op.vmult_add = [number, op](Range &v, const Domain &u)
447  {
448  v /= number;
449  op.vmult_add(v,u);
450  v *= number;
451  };
452 
453  return_op.Tvmult = [number, op](Domain &v, const Range &u)
454  {
455  op.Tvmult(v,u);
456  v *= number;
457  };
458 
459  return_op.Tvmult_add = [number, op](Domain &v, const Range &u)
460  {
461  v /= number;
462  op.Tvmult_add(v,u);
463  v *= number;
464  };
465 
466  return return_op;
467  }
468 }
469 
470 
486 template <typename Range, typename Domain>
489  typename Domain::value_type number)
490 {
491  static_assert(
492  std::is_convertible<typename Range::value_type, typename Domain::value_type>::value,
493  "Range and Domain must have implicitly convertible 'value_type's");
494 
495  return number * op;
496 }
497 
499 
500 
505 
515 template <typename Range, typename Intermediate, typename Domain>
518  const LinearOperator<Intermediate, Domain> &second_op)
519 {
520  if (first_op.is_null_operator || second_op.is_null_operator)
521  {
523  return_op.reinit_domain_vector = second_op.reinit_domain_vector;
524  return_op.reinit_range_vector = first_op.reinit_range_vector;
525  return null_operator(return_op);
526  }
527  else
528  {
530 
531  return_op.reinit_domain_vector = second_op.reinit_domain_vector;
532  return_op.reinit_range_vector = first_op.reinit_range_vector;
533 
534  // ensure to have valid computation objects by catching first_op and
535  // second_op by value
536 
537  return_op.vmult = [first_op, second_op](Range &v, const Domain &u)
538  {
539  static GrowingVectorMemory<Intermediate> vector_memory;
540 
541  Intermediate *i = vector_memory.alloc();
542  second_op.reinit_range_vector(*i, /*bool omit_zeroing_entries =*/ true);
543  second_op.vmult(*i, u);
544  first_op.vmult(v, *i);
545  vector_memory.free(i);
546  };
547 
548  return_op.vmult_add = [first_op, second_op](Range &v, const Domain &u)
549  {
550  static GrowingVectorMemory<Intermediate> vector_memory;
551 
552  Intermediate *i = vector_memory.alloc();
553  second_op.reinit_range_vector(*i, /*bool omit_zeroing_entries =*/ true);
554  second_op.vmult(*i, u);
555  first_op.vmult_add(v, *i);
556  vector_memory.free(i);
557  };
558 
559  return_op.Tvmult = [first_op, second_op](Domain &v, const Range &u)
560  {
561  static GrowingVectorMemory<Intermediate> vector_memory;
562 
563  Intermediate *i = vector_memory.alloc();
564  first_op.reinit_domain_vector(*i, /*bool omit_zeroing_entries =*/ true);
565  first_op.Tvmult(*i, u);
566  second_op.Tvmult(v, *i);
567  vector_memory.free(i);
568  };
569 
570  return_op.Tvmult_add = [first_op, second_op](Domain &v, const Range &u)
571  {
572  static GrowingVectorMemory<Intermediate> vector_memory;
573 
574  Intermediate *i = vector_memory.alloc();
575  first_op.reinit_domain_vector(*i, /*bool omit_zeroing_entries =*/ true);
576  first_op.Tvmult(*i, u);
577  second_op.Tvmult_add(v, *i);
578  vector_memory.free(i);
579  };
580 
581  return return_op;
582  }
583 }
584 
592 template <typename Range, typename Domain>
595 {
597 
600 
601  return_op.vmult = op.Tvmult;
602  return_op.vmult_add = op.Tvmult_add;
603  return_op.Tvmult = op.vmult;
604  return_op.Tvmult_add = op.vmult_add;
605 
606  return return_op;
607 }
608 
627 template <typename Solver, typename Preconditioner>
630  Solver &solver,
631  const Preconditioner &preconditioner)
632 {
633  typedef typename Solver::vector_type Vector;
634 
636 
639 
640  return_op.vmult = [op, &solver, &preconditioner](Vector &v, const Vector &u)
641  {
642  op.reinit_range_vector(v, /*bool omit_zeroing_entries =*/ false);
643  solver.solve(op, v, u, preconditioner);
644  };
645 
646  return_op.vmult_add =
647  [op, &solver, &preconditioner](Vector &v, const Vector &u)
648  {
650 
651  Vector *v2 = vector_memory.alloc();
652  op.reinit_range_vector(*v2, /*bool omit_zeroing_entries =*/ false);
653  solver.solve(op, *v2, u, preconditioner);
654  v += *v2;
655  vector_memory.free(v2);
656  };
657 
658  return_op.Tvmult = [op, &solver, &preconditioner]( Vector &v, const
659  Vector &u)
660  {
661  op.reinit_range_vector(v, /*bool omit_zeroing_entries =*/ false);
662  solver.solve(transpose_operator(op), v, u, preconditioner);
663  };
664 
665  return_op.Tvmult_add =
666  [op, &solver, &preconditioner](Vector &v, const Vector &u)
667  {
669 
670  Vector *v2 = vector_memory.alloc();
671  op.reinit_range_vector(*v2, /*bool omit_zeroing_entries =*/ false);
672  solver.solve(transpose_operator(op), *v2, u, preconditioner);
673  v += *v2;
674  vector_memory.free(v2);
675  };
676 
677  return return_op;
678 }
679 
681 
682 
687 
699 template <typename Range>
701 identity_operator(const std::function<void(Range &, bool)> &reinit_vector)
702 {
704 
705  return_op.reinit_range_vector = reinit_vector;
706  return_op.reinit_domain_vector = reinit_vector;
707 
708  return_op.vmult = [](Range &v, const Range &u)
709  {
710  v = u;
711  };
712 
713  return_op.vmult_add = [](Range &v, const Range &u)
714  {
715  v += u;
716  };
717 
718  return_op.Tvmult = [](Range &v, const Range &u)
719  {
720  v = u;
721  };
722 
723  return_op.Tvmult_add = [](Range &v, const Range &u)
724  {
725  v += u;
726  };
727 
728  return return_op;
729 }
730 
731 
741 template <typename Range, typename Domain>
744 {
745  auto return_op = op;
746 
747  return_op.is_null_operator = true;
748 
749  return_op.vmult = [](Range &v, const Domain &)
750  {
751  v = 0.;
752  };
753 
754  return_op.vmult_add = [](Range &, const Domain &)
755  {};
756 
757  return_op.Tvmult = [](Domain &v, const Range &)
758  {
759  v = 0.;
760  };
761 
762  return_op.Tvmult_add = [](Domain &, const Range &)
763  {};
764 
765  return return_op;
766 }
767 
768 
769 namespace internal
770 {
771  namespace LinearOperator
772  {
784  template<typename Vector>
785  class ReinitHelper
786  {
787  public:
799  template <typename Matrix>
800  static
801  void reinit_range_vector (const Matrix &matrix, Vector &v, bool omit_zeroing_entries)
802  {
803  v.reinit(matrix.m(), omit_zeroing_entries);
804  }
805 
817  template <typename Matrix>
818  static
819  void reinit_domain_vector (const Matrix &matrix, Vector &v, bool omit_zeroing_entries)
820  {
821  v.reinit(matrix.n(), omit_zeroing_entries);
822  }
823  };
824  } /* namespace LinearOperator */
825 } /* namespace internal */
826 
827 
828 namespace
829 {
830  // A trait class that determines whether type T provides public
831  // (templated or non-templated) vmult_add and Tvmult_add member functions
832  template <typename Range, typename Domain, typename T>
833  class has_vmult_add
834  {
835  template <typename C>
836  static std::false_type test(...);
837 
838  template <typename C>
839  static std::true_type test(decltype(&C::vmult_add),
840  decltype(&C::Tvmult_add));
841 
842  // Work around a bug with icc (up to version 15) that fails during type
843  // deduction in an SFINAE scenario
844 #ifndef DEAL_II_ICC_SFINAE_BUG
845 
846  template <typename C>
847  static std::true_type test(decltype(&C::template vmult_add<Range>),
848  decltype(&C::template Tvmult_add<Range>));
849 
850  template <typename C>
851  static std::true_type test(decltype(&C::template vmult_add<Range, Domain>),
852  decltype(&C::template Tvmult_add<Domain, Range>));
853 #endif
854 
855  public:
856  // type is std::true_type if Matrix provides vmult_add and Tvmult_add,
857  // otherwise it is std::false_type
858 
859  typedef decltype(test<T>(0, 0)) type;
860  };
861 
862 
863  // A helper function to apply a given vmult, or Tvmult to a vector with
864  // intermediate storage
865  template <typename Function, typename Range, typename Domain>
866  void apply_with_intermediate_storage(Function function, Range &v,
867  const Domain &u, bool add)
868  {
869  static GrowingVectorMemory<Range> vector_memory;
870 
871  Range *i = vector_memory.alloc();
872  i->reinit(v, /*bool omit_zeroing_entries =*/true);
873 
874  function(*i, u);
875 
876  if (add)
877  v += *i;
878  else
879  v = *i;
880 
881  vector_memory.free(i);
882  }
883 
884 
885  // A helper class to add a reduced matrix interface to a LinearOperator
886  // (typically provided by Preconditioner classes)
887  template <typename Range, typename Domain>
888  class MatrixInterfaceWithoutVmultAdd
889  {
890  public:
891  template <typename Matrix>
892  void operator()(LinearOperator<Range, Domain> &op, const Matrix &matrix)
893  {
894  op.vmult = [&matrix](Range &v, const Domain &u)
895  {
896  if (PointerComparison::equal(&v, &u))
897  {
898  // If v and u are the same memory location use intermediate storage
899  apply_with_intermediate_storage([&matrix](Range &b, const Domain &a)
900  {
901  matrix.vmult(b, a);
902  },
903  v, u, /*bool add =*/ false);
904  }
905  else
906  {
907  matrix.vmult(v,u);
908  }
909  };
910 
911  op.vmult_add = [&matrix](Range &v, const Domain &u)
912  {
913  // use intermediate storage to implement vmult_add with vmult
914  apply_with_intermediate_storage([&matrix](Range &b, const Domain &a)
915  {
916  matrix.vmult(b, a);
917  },
918  v, u, /*bool add =*/ true);
919  };
920 
921  op.Tvmult = [&matrix](Domain &v, const Range &u)
922  {
923  if (PointerComparison::equal(&v, &u))
924  {
925  // If v and u are the same memory location use intermediate storage
926  apply_with_intermediate_storage([&matrix](Domain &b, const Range &a)
927  {
928  matrix.Tvmult(b, a);
929  },
930  v, u, /*bool add =*/ false);
931  }
932  else
933  {
934  matrix.Tvmult(v,u);
935  }
936 
937  };
938 
939  op.Tvmult_add = [&matrix](Domain &v, const Range &u)
940  {
941  // use intermediate storage to implement Tvmult_add with Tvmult
942  apply_with_intermediate_storage([&matrix](Domain &b, const Range &a)
943  {
944  matrix.Tvmult(b, a);
945  },
946  v, u, /*bool add =*/ true);
947  };
948  }
949  };
950 
951 
952  // A helper class to add the full matrix interface to a LinearOperator
953  template <typename Range, typename Domain>
954  class MatrixInterfaceWithVmultAdd
955  {
956  public:
957  template <typename Matrix>
958  void operator()(LinearOperator<Range, Domain> &op, const Matrix &matrix)
959  {
960  // As above ...
961 
962  MatrixInterfaceWithoutVmultAdd<Range, Domain>().operator()(op, matrix);
963 
964  // ... but add native vmult_add and Tvmult_add variants:
965 
966  op.vmult_add = [&matrix](Range &v, const Domain &u)
967  {
968  if (PointerComparison::equal(&v, &u))
969  {
970  apply_with_intermediate_storage([&matrix](Range &b, const Domain &a)
971  {
972  matrix.vmult(b, a);
973  },
974  v, u, /*bool add =*/ false);
975  }
976  else
977  {
978  matrix.vmult_add(v,u);
979  }
980  };
981 
982  op.Tvmult_add = [&matrix](Domain &v, const Range &u)
983  {
984  if (PointerComparison::equal(&v, &u))
985  {
986  apply_with_intermediate_storage([&matrix](Domain &b, const Range &a)
987  {
988  matrix.Tvmult(b, a);
989  },
990  v, u, /*bool add =*/ true);
991  }
992  else
993  {
994  matrix.Tvmult_add(v,u);
995  }
996  };
997  }
998  };
999 
1000 } /* namespace */
1001 
1002 
1060 template <typename Range, typename Domain, typename Matrix>
1062 {
1063  // implement with the more generic variant below...
1064  return linear_operator<Range, Domain, Matrix, Matrix>(matrix, matrix);
1065 }
1066 
1067 
1083 template <typename Range,
1084  typename Domain,
1085  typename OperatorExemplar,
1086  typename Matrix>
1088 linear_operator(const OperatorExemplar &operator_exemplar, const Matrix &matrix)
1089 {
1091 
1092  // Always store a reference to matrix and operator_exemplar in the lambda
1093  // functions. This ensures that a modification of the matrix after the
1094  // creation of a LinearOperator wrapper is respected - further a matrix
1095  // or an operator_exemplar cannot usually be copied...
1096 
1097  return_op.reinit_range_vector = [&operator_exemplar](Range &v, bool omit_zeroing_entries)
1098  {
1099  internal::LinearOperator::ReinitHelper<Range>::reinit_range_vector(operator_exemplar, v, omit_zeroing_entries);
1100  };
1101 
1102  return_op.reinit_domain_vector = [&operator_exemplar](Domain &v, bool omit_zeroing_entries)
1103  {
1104  internal::LinearOperator::ReinitHelper<Domain>::reinit_domain_vector(operator_exemplar, v, omit_zeroing_entries);
1105  };
1106 
1107  typename std::conditional<
1108  has_vmult_add<Range, Domain, Matrix>::type::value,
1109  MatrixInterfaceWithVmultAdd<Range, Domain>,
1110  MatrixInterfaceWithoutVmultAdd<Range, Domain>>::type().
1111  operator()(return_op, matrix);
1112 
1113  return return_op;
1114 }
1115 
1117 
1118 DEAL_II_NAMESPACE_CLOSE
1119 
1120 #endif // DEAL_II_WITH_CXX11
1121 #endif
LinearOperator< Range, Domain > operator+(const LinearOperator< Range, Domain > &first_op, const LinearOperator< Range, Domain > &second_op)
LinearOperator< Range, Domain > operator-(const LinearOperator< Range, Domain > &first_op, const LinearOperator< Range, Domain > &second_op)
LinearOperator< Range, Domain > linear_operator(const Matrix &matrix)
::ExceptionBase & ExcMessage(std::string arg1)
LinearOperator< Range, Domain > & operator-=(const LinearOperator< Range, Domain > &second_op)
LinearOperator(const Op &op)
std::function< void(Range &v, bool omit_zeroing_entries)> reinit_range_vector
static void reinit_domain_vector(const Matrix &matrix, Vector &v, bool omit_zeroing_entries)
LinearOperator< Range, Domain > & operator+=(const LinearOperator< Range, Domain > &second_op)
std::function< void(Domain &v, const Range &u)> Tvmult_add
LinearOperator< Range, Domain > null_operator(const LinearOperator< Range, Domain > &op)
virtual VectorType * alloc()
#define Assert(cond, exc)
Definition: exceptions.h:294
static bool equal(const T *p1, const T *p2)
LinearOperator< Range, Domain > operator*(typename Range::value_type number, const LinearOperator< Range, Domain > &op)
LinearOperator< Range, Domain > operator*(const LinearOperator< Range, Intermediate > &first_op, const LinearOperator< Intermediate, Domain > &second_op)
virtual void free(const VectorType *const)
LinearOperator< Range, Domain > & operator=(const LinearOperator< Range, Domain > &)=default
LinearOperator< typename Solver::vector_type, typename Solver::vector_type > inverse_operator(const LinearOperator< typename Solver::vector_type, typename Solver::vector_type > &op, Solver &solver, const Preconditioner &preconditioner)
std::function< void(Domain &v, bool omit_zeroing_entries)> reinit_domain_vector
LinearOperator< Range, Range > identity_operator(const std::function< void(Range &, bool)> &reinit_vector)
static void reinit_range_vector(const Matrix &matrix, Vector &v, bool omit_zeroing_entries)
LinearOperator< Range, Domain > operator*=(typename Domain::value_type number)
Definition: solver.h:325
VectorType vector_type
Definition: solver.h:331
LinearOperator< Range, Domain > & operator*=(const LinearOperator< Domain, Domain > &second_op)
LinearOperator< Range, Domain > operator*(const LinearOperator< Range, Domain > &op, typename Domain::value_type number)
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
std::function< void(Range &v, const Domain &u)> vmult
std::function< void(Range &v, const Domain &u)> vmult_add
LinearOperator< Domain, Range > transpose_operator(const LinearOperator< Range, Domain > &op)
std::function< void(Domain &v, const Range &u)> Tvmult
LinearOperator< Range, Domain > linear_operator(const OperatorExemplar &operator_exemplar, const Matrix &matrix)
LinearOperator< Range, Domain > & operator=(const Op &op)