Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/v0.3.0/v0.9.0/cpp
DOLFINx  0.3.0
DOLFINx C++ interface
math.h
1 // Copyright (C) 2021 Igor Baratta
2 //
3 // This file is part of DOLFINx (https://www.fenicsproject.org)
4 //
5 // SPDX-License-Identifier: LGPL-3.0-or-later
6 
7 #pragma once
8 
9 #include <cmath>
10 
11 namespace dolfinx::math
12 {
13 
16 template <typename T>
17 inline T difference_of_products(T a, T b, T c, T d) noexcept
18 {
19  T w = b * c;
20  T err = std::fma(-b, c, w);
21  T diff = std::fma(a, d, -w);
22  return (diff + err);
23 }
24 
27 template <typename Matrix>
28 auto det(const Matrix& A)
29 {
30  using value_type = typename Matrix::value_type;
31  assert(A.shape(0) == A.shape(1));
32  assert(A.dimension() == 2);
33 
34  const int nrows = A.shape(0);
35  switch (nrows)
36  {
37  case 1:
38  return A(0, 0);
39  case 2:
40  return difference_of_products(A(0, 0), A(0, 1), A(1, 0), A(1, 1));
41  case 3:
42  {
43  // Leibniz formula combined with Kahan’s method for accurate
44  // computation of 3 x 3 determinants
45  value_type w0 = difference_of_products(A(1, 1), A(1, 2), A(2, 1), A(2, 2));
46  value_type w1 = difference_of_products(A(1, 0), A(1, 2), A(2, 0), A(2, 2));
47  value_type w2 = difference_of_products(A(1, 0), A(1, 1), A(2, 0), A(2, 1));
48  value_type w3 = difference_of_products(A(0, 0), A(0, 1), w1, w0);
49  value_type w4 = std::fma(A(0, 2), w2, w3);
50  return w4;
51  }
52  default:
53  throw std::runtime_error("math::det is not implemented for "
54  + std::to_string(A.shape(0)) + "x"
55  + std::to_string(A.shape(1)) + " matrices.");
56  }
57 }
58 
62 template <typename U, typename V>
63 void inv(const U& A, V& B)
64 {
65  using value_type = typename U::value_type;
66  const std::size_t nrows = A.shape(0);
67  switch (nrows)
68  {
69  case 1:
70  B(0, 0) = 1 / A(0, 0);
71  break;
72  case 2:
73  {
74  value_type idet = 1. / det(A);
75  B(0, 0) = idet * A(1, 1);
76  B(0, 1) = -idet * A(0, 1);
77  B(1, 0) = -idet * A(1, 0);
78  B(1, 1) = idet * A(0, 0);
79  break;
80  }
81  case 3:
82  {
83  value_type w0 = difference_of_products(A(1, 1), A(1, 2), A(2, 1), A(2, 2));
84  value_type w1 = difference_of_products(A(1, 0), A(1, 2), A(2, 0), A(2, 2));
85  value_type w2 = difference_of_products(A(1, 0), A(1, 1), A(2, 0), A(2, 1));
86  value_type w3 = difference_of_products(A(0, 0), A(0, 1), w1, w0);
87  value_type det = std::fma(A(0, 2), w2, w3);
88  assert(det != 0.);
89  value_type idet = 1 / det;
90 
91  B(0, 0) = w0 * idet;
92  B(1, 0) = -w1 * idet;
93  B(2, 0) = w2 * idet;
94  B(0, 1) = difference_of_products(A(0, 2), A(0, 1), A(2, 2), A(2, 1)) * idet;
95  B(0, 2) = difference_of_products(A(0, 1), A(0, 2), A(1, 1), A(1, 2)) * idet;
96  B(1, 1) = difference_of_products(A(0, 0), A(0, 2), A(2, 0), A(2, 2)) * idet;
97  B(1, 2) = difference_of_products(A(1, 0), A(0, 0), A(1, 2), A(0, 2)) * idet;
98  B(2, 1) = difference_of_products(A(2, 0), A(0, 0), A(2, 1), A(0, 1)) * idet;
99  B(2, 2) = difference_of_products(A(0, 0), A(1, 0), A(0, 1), A(1, 1)) * idet;
100  break;
101  }
102  default:
103  throw std::runtime_error("math::inv is not implemented for "
104  + std::to_string(A.shape(0)) + "x"
105  + std::to_string(A.shape(1)) + " matrices.");
106  }
107 }
108 
109 } // namespace dolfinx::math