FastMatrixMathHelper.h
Go to the documentation of this file.
1 /**
2  * @file FastMatrixMathHelper.h
3  * @brief Classes with hard-coded (hence "fast") matrix math
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date March 31st, 2015
6  *
7  * Currently includes:
8  * - determinant (2x2, 3x3, 4x4)
9  * - inversion (2x2, 3x3, 4x4)
10  *
11  */
12 
13 #ifndef FASTMATRIXMATHHELPER_H
14 #define FASTMATRIXMATHHELPER_H 1
15 
16 // C/C++ standard libraries
17 #include <cmath> // std::sqrt()
18 #include <tuple>
19 #include <array>
20 #include <iterator> // std::begin(), std::end()
21 #include <algorithm> // std::for_each()
22 #include <stdexcept> // std::range_error
23 
24 
25 #include "lardataalg/Utilities/StatCollector.h" // lar::util::identity
26 
27 namespace lar {
28  namespace util {
29 
30  namespace details {
31 
32  /// Base class with common definitions for "fast" matrix operations.
33  template <typename T, unsigned int DIM>
35  using Data_t = T;
36 
37  static constexpr unsigned int Dim = DIM; ///< matrix dimensions
38 
39  using Matrix_t = std::array<Data_t, Dim*Dim>;
40  using Vector_t = std::array<Data_t, Dim>;
41 
42  /// Returns the product of a square matrix times a column vector
44  (Matrix_t const& mat, Vector_t const& vec);
45 
46 
47  static constexpr Data_t sqr(Data_t v) { return v*v; }
48  }; // struct FastMatrixOperationsBase<>
49 
50 
51  /**
52  * @brief Provides "fast" matrix operations.
53  * @tparam T data type for the elements of the matrix
54  * @tparam DIM the dimension of the (square) matrix
55  *
56  * Actually this class does nothing: specialize it!
57  *
58  * Once the specialization is in place, this class offers:
59  *
60  * constexpr unsigned int Dim = 2;
61  * std::array<float, Dim*Dim> matrix;
62  *
63  * float det = FastMatrixOperations<float, Dim>::Determinant();
64  *
65  * std::array<float, Dim*Dim> inverse;
66  *
67  * // generic inversion
68  * inverse = FastMatrixOperations<float, Dim>::InvertMatrix(matrix);
69  *
70  * // faster inversion if we already have the determinant
71  * inverse = FastMatrixOperations<float, Dim>::InvertMatrix
72  * (matrix, det);
73  *
74  * // faster inversion if we know the matrix is symmetric
75  * inverse = FastMatrixOperations<float, Dim>::InvertSymmetricMatrix
76  * (matrix);
77  *
78  * // even faster inversion if we also know the determinant already
79  * inverse = FastMatrixOperations<float, Dim>::InvertSymmetricMatrix
80  * (matrix, det);
81  *
82  * Note that the inversion functions do not have a defined policy for
83  * non-invertible matrices. If you need to check (and you usually do),
84  * compute the determinant first, and invert only if `std::isnormal(det)`.
85  */
86  template <typename T, unsigned int DIM>
89  using Data_t = typename Base_t::Data_t;
90  using Matrix_t = typename Base_t::Matrix_t;
91 
92  /// Computes the determinant of a matrix
93  static Data_t Determinant(Matrix_t const& mat);
94 
95  /// Computes the determinant of a matrix, using the provided determinant
96  static Matrix_t InvertMatrix(Matrix_t const& mat, Data_t det);
97 
98  /// Computes the determinant of a symmatric matrix,
99  /// using the provided determinant
100  static Matrix_t InvertSymmetricMatrix(Matrix_t const& mat, Data_t det);
101 
102  /// Computes the determinant of a matrix
103  static Matrix_t InvertMatrix(Matrix_t const& mat)
104  { return InvertMatrix(mat, Determinant(mat)); }
105 
106  /// Computes the determinant of a matrix
108  { return InvertSymmetricMatrix(mat, Determinant(mat)); }
109 
110  }; // struct FastMatrixOperations<>
111 
112  } // namespace details
113  } // namespace util
114 } // namespace lar
115 
116 
117 
118 //******************************************************************************
119 //*** template implementation
120 //***
121 namespace lar {
122  namespace util {
123 
124  namespace details {
125 
126  template <unsigned int NCols>
127  constexpr size_t MatrixIndex(unsigned int row, unsigned int col)
128  { return row * NCols + col; }
129 
130  template <unsigned int N>
132  static constexpr size_t index(unsigned int row, unsigned int col)
133  { return MatrixIndex<N>(row, col); }
134  }; // struct DeterminantHelperBase<>
135 
136  template <typename T, unsigned int N,
137  unsigned int... RnC // all row indices, then all column indices
138  >
141 
142  static T compute(T const* data);
143  }; // struct DeterminantHelper<>
144 
145 
146  /// Determinant of a 1x1 submatrix
147  template <typename T, unsigned int N,
148  unsigned int R, unsigned int C
149  >
150  struct DeterminantHelper<T, N, R, C>: public DeterminantHelperBase<N> {
152  static_assert(R < N, "invalid row index specified");
153  static_assert(C < N, "invalid column index specified");
154 
155  static T compute(T const* data) { return data[index(R, C)]; }
156  }; // struct DeterminantHelper<>
157 
158 
159  /// Determinant of a 2x2 submatrix
160  template <typename T, unsigned int N,
161  unsigned int R1, unsigned int R2,
162  unsigned int C1, unsigned int C2
163  >
164  struct DeterminantHelper<T, N, R1, R2, C1, C2>:
165  public DeterminantHelperBase<N>
166  {
168  static_assert(R1 < N, "invalid row index specified");
169  static_assert(R2 < N, "invalid row index specified");
170  static_assert(C1 < N, "invalid column index specified");
171  static_assert(C2 < N, "invalid column index specified");
172 
173  static constexpr T sqr(T v) { return v*v; }
174 
175  static T compute(T const* data)
176  {
177  return data[index(R1, C1)] * data[index(R2, C2)]
178  - data[index(R1, C2)] * data[index(R2, C1)];
179  /* // also
180  data[index(R1, C1)] * UpHelper<R2, C2>::compute(data)
181  - data[index(R1, C2)] * UpHelper<R2, C1>::compute(data)
182  */
183  }
184  }; // struct DeterminantHelper<>
185 
186 
187  /// Determinant of a 3x3 submatrix
188  template <typename T, unsigned int N,
189  unsigned int R1, unsigned int R2, unsigned int R3,
190  unsigned int C1, unsigned int C2, unsigned int C3
191  >
192  struct DeterminantHelper<T, N, R1, R2, R3, C1, C2, C3>:
193  public DeterminantHelperBase<N>
194  {
196  static_assert(R1 < N, "invalid row index specified");
197  static_assert(R2 < N, "invalid row index specified");
198  static_assert(R3 < N, "invalid row index specified");
199  static_assert(C1 < N, "invalid column index specified");
200  static_assert(C2 < N, "invalid column index specified");
201  static_assert(C3 < N, "invalid column index specified");
202  template <
203  unsigned int sR1, unsigned int sR2,
204  unsigned int sC1, unsigned int sC2
205  >
207  static T compute(T const* data)
208  {
209  return
210  data[index(R1, C1)] * UpHelper<R2, R3, C2, C3>::compute(data)
211  - data[index(R1, C2)] * UpHelper<R2, R3, C1, C3>::compute(data)
212  + data[index(R1, C3)] * UpHelper<R2, R3, C1, C2>::compute(data)
213  ;
214  }
215  }; // struct DeterminantHelper<>
216 
217 
218  /// Determinant of a 4x4 submatrix
219  template <typename T, unsigned int N,
220  unsigned int R1, unsigned int R2, unsigned int R3, unsigned int R4,
221  unsigned int C1, unsigned int C2, unsigned int C3, unsigned int C4
222  >
223  struct DeterminantHelper<T, N, R1, R2, R3, R4, C1, C2, C3, C4>:
224  public DeterminantHelperBase<N>
225  {
227  static_assert(R1 < N, "invalid row index specified");
228  static_assert(R2 < N, "invalid row index specified");
229  static_assert(R3 < N, "invalid row index specified");
230  static_assert(R4 < N, "invalid row index specified");
231  static_assert(C1 < N, "invalid column index specified");
232  static_assert(C2 < N, "invalid column index specified");
233  static_assert(C3 < N, "invalid column index specified");
234  static_assert(C4 < N, "invalid column index specified");
235  template <
236  unsigned int sR1, unsigned int sR2, unsigned int sR3,
237  unsigned int sC1, unsigned int sC2, unsigned int sC3
238  >
240  static T compute(T const* data)
241  {
242  return
244  - data[index(R1, C2)] * UpHelper<R2, R3, R4, C1, C3, C4>::compute(data)
245  + data[index(R1, C3)] * UpHelper<R2, R3, R4, C1, C2, C4>::compute(data)
247  ;
248  }
249  }; // struct DeterminantHelper<>
250 
251 
252  /// Routines for 2x2 matrices
253  template <typename T>
255  public FastMatrixOperationsBase<T, 2>
256  {
258  static constexpr unsigned int Dim = Base_t::Dim;
259  using Data_t = typename Base_t::Data_t;
260  using Matrix_t = typename Base_t::Matrix_t;
261 
262  /// Computes the determinant of a matrix
263  static Data_t Determinant(Matrix_t const& mat)
264  { return DeterminantHelper<T, Dim, 0, 1, 0, 1>::compute(mat.data()); }
265 
266  /// Computes the determinant of a matrix, using the provided determinant
267  static Matrix_t InvertMatrix(Matrix_t const& mat, Data_t det);
268 
269  /// Computes the determinant of a symmatric matrix,
270  /// using the provided determinant
271  static Matrix_t InvertSymmetricMatrix(Matrix_t const& mat, Data_t det);
272 
273  /// Computes the determinant of a matrix
274  static Matrix_t InvertMatrix(Matrix_t const& mat)
275  { return InvertMatrix(mat, Determinant(mat)); }
276 
277  /// Computes the determinant of a matrix
279  { return InvertSymmetricMatrix(mat, Determinant(mat)); }
280 
281  }; // struct FastMatrixOperations<T, 2>
282 
283 
284  /// Routines for 3x3 matrices
285  template <typename T>
287  public FastMatrixOperationsBase<T, 3>
288  {
290  static constexpr unsigned int Dim = Base_t::Dim;
291  using Data_t = typename Base_t::Data_t;
292  using Matrix_t = typename Base_t::Matrix_t;
293 
294  /// Computes the determinant of a matrix
295  static Data_t Determinant(Matrix_t const& mat)
296  {
297  return
299  }
300 
301  /// Computes the determinant of a matrix, using the provided determinant
302  static Matrix_t InvertMatrix(Matrix_t const& mat, Data_t det);
303 
304  /// Computes the determinant of a symmatric matrix,
305  /// using the provided determinant
306  static Matrix_t InvertSymmetricMatrix(Matrix_t const& mat, Data_t det);
307 
308  /// Computes the determinant of a matrix
309  static Matrix_t InvertMatrix(Matrix_t const& mat)
310  { return InvertMatrix(mat, Determinant(mat)); }
311 
312  /// Computes the determinant of a matrix
314  { return InvertSymmetricMatrix(mat, Determinant(mat)); }
315 
316  }; // struct FastMatrixOperations<T, 3>
317 
318 
319  /// Routines for 4x4 matrices
320  template <typename T>
322  public FastMatrixOperationsBase<T, 4>
323  {
325  static constexpr unsigned int Dim = Base_t::Dim;
326  using Data_t = typename Base_t::Data_t;
327  using Matrix_t = typename Base_t::Matrix_t;
328 
329  /// Computes the determinant of a matrix
330  static Data_t Determinant(Matrix_t const& mat)
331  {
333  (mat.data());
334  }
335 
336  /// Computes the determinant of a matrix, using the provided determinant
337  static Matrix_t InvertMatrix(Matrix_t const& mat, Data_t det);
338 
339  /// Computes the determinant of a symmatric matrix,
340  /// using the provided determinant
341  static Matrix_t InvertSymmetricMatrix(Matrix_t const& mat, Data_t det);
342 
343  /// Computes the determinant of a matrix
344  static Matrix_t InvertMatrix(Matrix_t const& mat)
345  { return InvertMatrix(mat, Determinant(mat)); }
346 
347  /// Computes the determinant of a matrix
349  { return InvertSymmetricMatrix(mat, Determinant(mat)); }
350 
351  }; // struct FastMatrixOperations<T, 3>
352 
353 
354 
355  } // namespace details
356  } // namespace util
357 } // namespace lar
358 
359 
360 template <typename T, unsigned int DIM>
362  (Matrix_t const& mat, Vector_t const& vec) -> Vector_t
363 {
364  // not really fast, but there is probably not much to fasten...
365  Vector_t res;
366  Data_t const* mat_row = mat.data();
367  for (size_t r = 0; r < Dim; ++r) {
368  Data_t elem = Data_t(0);
369  for (size_t c = 0; c < Dim; ++c)
370  elem += *(mat_row++) * vec[c];
371  res[r] = elem;
372  } // for
373  return res;
374 } // FastMatrixOperationsBase<>::MatrixVectorProduct()
375 
376 
377 template <typename T>
379  (Matrix_t const& mat, Data_t det) -> Matrix_t
380 {
381  Matrix_t Inverse;
382  Inverse[0 * Dim + 0] = mat[1 * Dim + 1] / det;
383  Inverse[0 * Dim + 1] = - mat[0 * Dim + 1] / det;
384  Inverse[1 * Dim + 0] = - mat[1 * Dim + 0] / det;
385  Inverse[1 * Dim + 1] = mat[0 * Dim + 0] / det;
386  return Inverse;
387 } // FastMatrixOperations<T, 2>::InvertMatrix()
388 
389 
390 template <typename T>
392  (Matrix_t const& mat, Data_t det) -> Matrix_t
393 {
394  Matrix_t Inverse;
395  Inverse[0 * Dim + 0] = mat[1 * Dim + 1] / det;
396  Inverse[0 * Dim + 1] =
397  Inverse[1 * Dim + 0] = - mat[1 * Dim + 0] / det;
398  Inverse[1 * Dim + 1] = mat[0 * Dim + 0] / det;
399  return Inverse;
400 } // FastMatrixOperations<T, 2>::InvertMatrix()
401 
402 
403 template <typename T>
405  (Matrix_t const& mat, Data_t det) -> Matrix_t
406 {
407  Data_t const* data = mat.data();
408  Matrix_t Inverse;
409  //
410  // Basically using Cramer's rule;
411  // each element [r,c] gets assigned the determinant of the submatrix
412  // after removing c from the rows and r from the columns
413  // (effectively assigning the transpose of the minor matrix)
414  // with the usual sign -1^(r+c)
415  //
416  //
417  Inverse[0 * Dim + 0] = DeterminantHelper<T, 3, 1, 2, 1, 2>::compute(data)/det;
418  Inverse[0 * Dim + 1] = -DeterminantHelper<T, 3, 0, 2, 1, 2>::compute(data)/det;
419  Inverse[0 * Dim + 2] = DeterminantHelper<T, 3, 0, 1, 1, 2>::compute(data)/det;
420  Inverse[1 * Dim + 0] = -DeterminantHelper<T, 3, 1, 2, 0, 2>::compute(data)/det;
421  Inverse[1 * Dim + 1] = DeterminantHelper<T, 3, 0, 2, 0, 2>::compute(data)/det;
422  Inverse[1 * Dim + 2] = -DeterminantHelper<T, 3, 0, 1, 0, 2>::compute(data)/det;
423  Inverse[2 * Dim + 0] = DeterminantHelper<T, 3, 1, 2, 0, 1>::compute(data)/det;
424  Inverse[2 * Dim + 1] = -DeterminantHelper<T, 3, 0, 2, 0, 1>::compute(data)/det;
425  Inverse[2 * Dim + 2] = DeterminantHelper<T, 3, 0, 1, 0, 1>::compute(data)/det;
426  return Inverse;
427 } // FastMatrixOperations<T, 3>::InvertMatrix()
428 
429 
430 template <typename T>
432  (Matrix_t const& mat, Data_t det) -> Matrix_t
433 {
434  //
435  // Same algorithm as InvertMatrix(), but use the fact that the result is
436  // also symmetric
437  //
438  Data_t const* data = mat.data();
439  Matrix_t Inverse;
440  Inverse[0 * Dim + 0] = DeterminantHelper<T, 3, 1, 2, 1, 2>::compute(data)/det;
441  Inverse[0 * Dim + 1] =
442  Inverse[1 * Dim + 0] = -DeterminantHelper<T, 3, 1, 2, 0, 2>::compute(data)/det;
443  Inverse[0 * Dim + 2] =
444  Inverse[2 * Dim + 0] = DeterminantHelper<T, 3, 1, 2, 0, 1>::compute(data)/det;
445  Inverse[1 * Dim + 1] = DeterminantHelper<T, 3, 0, 2, 0, 2>::compute(data)/det;
446  Inverse[1 * Dim + 2] =
447  Inverse[2 * Dim + 1] = -DeterminantHelper<T, 3, 0, 2, 0, 1>::compute(data)/det;
448  Inverse[2 * Dim + 2] = DeterminantHelper<T, 3, 0, 1, 0, 1>::compute(data)/det;
449  return Inverse;
450 } // FastMatrixOperations<T, 3>::InvertSymmetricMatrix()
451 
452 
453 template <typename T>
455  (Matrix_t const& mat, Data_t det) -> Matrix_t
456 {
457  //
458  // Basically using Cramer's rule;
459  // each element [r,c] gets assigned the determinant of the submatrix
460  // after removing c from the rows and r from the columns
461  // (effectively assigning the transpose of the minor matrix)
462  // with the usual sign -1^(r+c)
463  //
464  //
465  Data_t const* data = mat.data();
466  Matrix_t Inverse;
467  Inverse[0 * Dim + 0] = DeterminantHelper<T, 4, 1, 2, 3, 1, 2, 3>::compute(data)/det;
468  Inverse[0 * Dim + 1] = -DeterminantHelper<T, 4, 0, 2, 3, 1, 2, 3>::compute(data)/det;
469  Inverse[0 * Dim + 2] = DeterminantHelper<T, 4, 0, 1, 3, 1, 2, 3>::compute(data)/det;
470  Inverse[0 * Dim + 3] = -DeterminantHelper<T, 4, 0, 1, 2, 1, 2, 3>::compute(data)/det;
471  Inverse[1 * Dim + 0] = -DeterminantHelper<T, 4, 1, 2, 3, 0, 2, 3>::compute(data)/det;
472  Inverse[1 * Dim + 1] = DeterminantHelper<T, 4, 0, 2, 3, 0, 2, 3>::compute(data)/det;
473  Inverse[1 * Dim + 2] = -DeterminantHelper<T, 4, 0, 1, 3, 0, 2, 3>::compute(data)/det;
474  Inverse[1 * Dim + 3] = DeterminantHelper<T, 4, 0, 1, 2, 0, 2, 3>::compute(data)/det;
475  Inverse[2 * Dim + 0] = DeterminantHelper<T, 4, 1, 2, 3, 0, 1, 3>::compute(data)/det;
476  Inverse[2 * Dim + 1] = -DeterminantHelper<T, 4, 0, 2, 3, 0, 1, 3>::compute(data)/det;
477  Inverse[2 * Dim + 2] = DeterminantHelper<T, 4, 0, 1, 3, 0, 1, 3>::compute(data)/det;
478  Inverse[2 * Dim + 3] = -DeterminantHelper<T, 4, 0, 1, 2, 0, 1, 3>::compute(data)/det;
479  Inverse[3 * Dim + 0] = -DeterminantHelper<T, 4, 1, 2, 3, 0, 1, 2>::compute(data)/det;
480  Inverse[3 * Dim + 1] = DeterminantHelper<T, 4, 0, 2, 3, 0, 1, 2>::compute(data)/det;
481  Inverse[3 * Dim + 2] = -DeterminantHelper<T, 4, 0, 1, 3, 0, 1, 2>::compute(data)/det;
482  Inverse[3 * Dim + 3] = DeterminantHelper<T, 4, 0, 1, 2, 0, 1, 2>::compute(data)/det;
483  return Inverse;
484 } // FastMatrixOperations<T, 4>::InvertMatrix()
485 
486 
487 template <typename T>
489  (Matrix_t const& mat, Data_t det) -> Matrix_t
490 {
491  //
492  // Same algorithm as InvertMatrix(), but use the fact that the result is
493  // also symmetric
494  //
495  Data_t const* data = mat.data();
496  Matrix_t Inverse;
497  Inverse[0 * Dim + 0] = DeterminantHelper<T, 4, 1, 2, 3, 1, 2, 3>::compute(data)/det;
498  Inverse[0 * Dim + 1] =
499  Inverse[1 * Dim + 0] = -DeterminantHelper<T, 4, 1, 2, 3, 0, 2, 3>::compute(data)/det;
500  Inverse[0 * Dim + 2] =
501  Inverse[2 * Dim + 0] = DeterminantHelper<T, 4, 1, 2, 3, 0, 1, 3>::compute(data)/det;
502  Inverse[0 * Dim + 3] =
503  Inverse[3 * Dim + 0] = -DeterminantHelper<T, 4, 1, 2, 3, 0, 1, 2>::compute(data)/det;
504  Inverse[1 * Dim + 1] = DeterminantHelper<T, 4, 0, 2, 3, 0, 2, 3>::compute(data)/det;
505  Inverse[1 * Dim + 2] =
506  Inverse[2 * Dim + 1] = -DeterminantHelper<T, 4, 0, 2, 3, 3, 0, 1>::compute(data)/det;
507  Inverse[1 * Dim + 3] =
508  Inverse[3 * Dim + 1] = DeterminantHelper<T, 4, 0, 2, 3, 0, 1, 2>::compute(data)/det;
509  Inverse[2 * Dim + 2] = DeterminantHelper<T, 4, 0, 1, 3, 0, 1, 3>::compute(data)/det;
510  Inverse[2 * Dim + 3] =
511  Inverse[3 * Dim + 2] = -DeterminantHelper<T, 4, 0, 1, 3, 0, 1, 2>::compute(data)/det;
512  Inverse[3 * Dim + 3] = DeterminantHelper<T, 4, 0, 1, 2, 0, 1, 2>::compute(data)/det;
513  return Inverse;
514 } // FastMatrixOperations<T, 4>::InvertSymmetricMatrix()
515 
516 
517 #endif // FASTMATRIXMATHHELPER_H
static Data_t Determinant(Matrix_t const &mat)
Computes the determinant of a matrix.
Namespace for general, non-LArSoft-specific utilities.
static Matrix_t InvertSymmetricMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
class C3 in group 2
Definition: group.cpp:35
static Matrix_t InvertMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
static constexpr size_t index(unsigned int row, unsigned int col)
static Matrix_t InvertMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
Classes gathering simple statistics.
static Matrix_t InvertMatrix(Matrix_t const &mat, Data_t det)
Computes the determinant of a matrix, using the provided determinant.
static Matrix_t InvertSymmetricMatrix(Matrix_t const &mat, Data_t det)
Base class with common definitions for "fast" matrix operations.
Provides "fast" matrix operations.
static T compute(T const *data)
static Matrix_t InvertSymmetricMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
static Matrix_t InvertMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
static Matrix_t InvertSymmetricMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
static constexpr unsigned int Dim
matrix dimensions
static Data_t Determinant(Matrix_t const &mat)
Computes the determinant of a matrix.
static Vector_t MatrixVectorProduct(Matrix_t const &mat, Vector_t const &vec)
Returns the product of a square matrix times a column vector.
static Matrix_t InvertMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
constexpr size_t MatrixIndex(unsigned int row, unsigned int col)
static Matrix_t InvertSymmetricMatrix(Matrix_t const &mat)
Computes the determinant of a matrix.
class C2 in group 1
Definition: group.cpp:10
LArSoft-specific namespace.
static Data_t Determinant(Matrix_t const &mat)
Computes the determinant of a matrix.
class C4 in group 2
Definition: group.cpp:40
class C1 in group 1
Definition: group.cpp:7