Select Git revision
test_WhileProcessor.cpp
TinyMatrix.hpp 13.36 KiB
#ifndef TINY_MATRIX_HPP
#define TINY_MATRIX_HPP
#include <algebra/TinyVector.hpp>
#include <utils/InvalidData.hpp>
#include <utils/NaNHelper.hpp>
#include <utils/PugsAssert.hpp>
#include <utils/PugsMacros.hpp>
#include <utils/Types.hpp>
#include <iostream>
template <size_t M, size_t N = M, typename T = double>
class [[nodiscard]] TinyMatrix
{
public:
inline static constexpr size_t Dimension = M * N;
inline static constexpr size_t NumberOfRows = M;
inline static constexpr size_t NumberOfColumns = N;
using data_type = T;
private:
static_assert((M > 0), "TinyMatrix number of rows must be strictly positive");
static_assert((N > 0), "TinyMatrix number of columns must be strictly positive");
T m_values[M * N];
PUGS_FORCEINLINE
constexpr size_t
_index(size_t i, size_t j) const noexcept // LCOV_EXCL_LINE (due to forced inline)
{
return i * N + j;
}
template <typename... Args>
PUGS_FORCEINLINE constexpr void
_unpackVariadicInput(const T& t, Args&&... args) noexcept
{
m_values[M * N - 1 - sizeof...(args)] = t;
if constexpr (sizeof...(args) > 0) {
this->_unpackVariadicInput(std::forward<Args>(args)...);
}
}
template <typename... Args>
PUGS_FORCEINLINE constexpr void
_unpackVariadicInput(const TinyVector<M, T>& t, Args&&... args) noexcept
{
for (size_t j = 0; j < M; ++j) {
m_values[_index(j, N - 1 - sizeof...(args))] = t[j];
}
if constexpr (sizeof...(args) > 0) {
this->_unpackVariadicInput(std::forward<Args>(args)...);
}
}
public:
PUGS_INLINE
constexpr bool
isSquare() const noexcept
{
return M == N;
}
PUGS_INLINE
constexpr friend TinyMatrix<N, M, T>
transpose(const TinyMatrix& A)
{
TinyMatrix<N, M, T> tA;
for (size_t i = 0; i < M; ++i) {
for (size_t j = 0; j < N; ++j) {
tA(j, i) = A(i, j);
}
}
return tA;
}
PUGS_INLINE
constexpr size_t
dimension() const
{
return M * N;
}
PUGS_INLINE
constexpr size_t
numberOfValues() const
{
return this->dimension();
}
PUGS_INLINE
constexpr size_t
numberOfRows() const
{
return M;
}
PUGS_INLINE
constexpr size_t
numberOfColumns() const
{
return N;
}
PUGS_INLINE
constexpr TinyMatrix
operator-() const
{
TinyMatrix opposite;
for (size_t i = 0; i < M * N; ++i) {
opposite.m_values[i] = -m_values[i];
}
return opposite;
}
PUGS_INLINE
constexpr friend TinyMatrix
operator*(const T& t, const TinyMatrix& A)
{
TinyMatrix B = A;
return B *= t;
}
PUGS_INLINE
constexpr friend TinyMatrix
operator*(const T& t, TinyMatrix&& A)
{
return std::move(A *= t);
}
PUGS_INLINE
constexpr TinyMatrix&
operator*=(const T& t)
{
for (size_t i = 0; i < M * N; ++i) {
m_values[i] *= t;
}
return *this;
}
template <size_t P>
PUGS_INLINE constexpr TinyMatrix<M, P, T>
operator*(const TinyMatrix<N, P, T>& B) const
{
const TinyMatrix& A = *this;
TinyMatrix<M, P, T> AB;
for (size_t i = 0; i < M; ++i) {
for (size_t j = 0; j < P; ++j) {
T sum = A(i, 0) * B(0, j);
for (size_t k = 1; k < N; ++k) {
sum += A(i, k) * B(k, j);
}
AB(i, j) = sum;
}
}
return AB;
}
PUGS_INLINE
constexpr TinyVector<M, T>
operator*(const TinyVector<N, T>& x) const
{
const TinyMatrix& A = *this;
TinyVector<M, T> Ax;
for (size_t i = 0; i < M; ++i) {
T sum = A(i, 0) * x[0];
for (size_t j = 1; j < N; ++j) {
sum += A(i, j) * x[j];
}
Ax[i] = sum;
}
return Ax;
}
PUGS_INLINE
constexpr friend std::ostream&
operator<<(std::ostream& os, const TinyMatrix& A)
{
os << '[';
for (size_t i = 0; i < M; ++i) {
if (i > 0) {
os << ',';
}
os << '[' << NaNHelper(A(i, 0));
for (size_t j = 1; j < N; ++j) {
os << ',' << NaNHelper(A(i, j));
}
os << ']';
}
os << ']';
return os;
}
PUGS_INLINE
constexpr bool
operator==(const TinyMatrix& A) const
{
for (size_t i = 0; i < M * N; ++i) {
if (m_values[i] != A.m_values[i])
return false;
}
return true;
}
PUGS_INLINE
constexpr bool
operator!=(const TinyMatrix& A) const
{
return not this->operator==(A);
}
PUGS_INLINE
constexpr TinyMatrix
operator+(const TinyMatrix& A) const
{
TinyMatrix sum;
for (size_t i = 0; i < M * N; ++i) {
sum.m_values[i] = m_values[i] + A.m_values[i];
}
return sum;
}
PUGS_INLINE
constexpr TinyMatrix
operator+(TinyMatrix&& A) const
{
A += *this;
return std::move(A);
}
PUGS_INLINE
constexpr TinyMatrix
operator-(const TinyMatrix& A) const
{
TinyMatrix difference;
for (size_t i = 0; i < M * N; ++i) {
difference.m_values[i] = m_values[i] - A.m_values[i];
}
return difference;
}
PUGS_INLINE
constexpr TinyMatrix
operator-(TinyMatrix&& A) const
{
for (size_t i = 0; i < M * N; ++i) {
A.m_values[i] = m_values[i] - A.m_values[i];
}
return std::move(A);
}
PUGS_INLINE
constexpr TinyMatrix&
operator+=(const TinyMatrix& A)
{
for (size_t i = 0; i < M * N; ++i) {
m_values[i] += A.m_values[i];
}
return *this;
}
PUGS_INLINE
constexpr TinyMatrix&
operator-=(const TinyMatrix& A)
{
for (size_t i = 0; i < M * N; ++i) {
m_values[i] -= A.m_values[i];
}
return *this;
}
PUGS_INLINE
constexpr T&
operator()(size_t i, size_t j) noexcept(NO_ASSERT)
{
Assert((i < M) and (j < N));
return m_values[_index(i, j)];
}
PUGS_INLINE
constexpr const T&
operator()(size_t i, size_t j) const noexcept(NO_ASSERT)
{
Assert((i < M) and (j < N));
return m_values[_index(i, j)];
}
PUGS_INLINE
constexpr TinyMatrix&
operator=(ZeroType) noexcept
{
static_assert(std::is_arithmetic<T>(), "Cannot assign 'zero' value for non-arithmetic types");
for (size_t i = 0; i < M * N; ++i) {
m_values[i] = 0;
}
return *this;
}
PUGS_INLINE
constexpr TinyMatrix&
operator=(IdentityType) noexcept
{
static_assert(std::is_arithmetic<T>(), "Cannot assign 'identity' value for non-arithmetic types");
for (size_t i = 0; i < M; ++i) {
for (size_t j = 0; j < N; ++j) {
m_values[_index(i, j)] = (i == j) ? 1 : 0;
}
}
return *this;
}
PUGS_INLINE
constexpr TinyMatrix& operator=(const TinyMatrix& A) noexcept = default;
PUGS_INLINE
constexpr TinyMatrix& operator=(TinyMatrix&& A) noexcept = default;
template <typename... Args>
PUGS_INLINE explicit constexpr TinyMatrix(const T& t, Args&&... args) noexcept
{
static_assert(sizeof...(args) == M * N - 1, "wrong number of parameters");
this->_unpackVariadicInput(t, std::forward<Args>(args)...);
}
template <typename... Args>
PUGS_INLINE explicit constexpr TinyMatrix(const TinyVector<M, T>& t, Args&&... args) noexcept
{
static_assert(sizeof...(args) == N - 1, "wrong number of parameters");
this->_unpackVariadicInput(t, std::forward<Args>(args)...);
}
// One does not use the '=default' constructor to avoid (unexpected)
// performances issues
PUGS_INLINE
constexpr TinyMatrix() noexcept
{
#ifndef NDEBUG
for (size_t i = 0; i < M * N; ++i) {
m_values[i] = invalid_data_v<T>;
}
#endif // NDEBUG
}
PUGS_INLINE
constexpr TinyMatrix(ZeroType) noexcept
{
static_assert(std::is_arithmetic<T>(), "Cannot construct from 'zero' value "
"for non-arithmetic types");
for (size_t i = 0; i < M * N; ++i) {
m_values[i] = 0;
}
}
PUGS_INLINE
constexpr TinyMatrix(IdentityType) noexcept
{
static_assert(std::is_arithmetic<T>(), "Cannot construct from 'identity' "
"value for non-arithmetic types");
for (size_t i = 0; i < M; ++i) {
for (size_t j = 0; j < N; ++j) {
m_values[_index(i, j)] = (i == j) ? 1 : 0;
}
}
}
PUGS_INLINE
constexpr TinyMatrix(const TinyMatrix&) noexcept = default;
PUGS_INLINE
TinyMatrix(TinyMatrix&& A) noexcept = default;
PUGS_INLINE
~TinyMatrix() = default;
};
template <size_t M, size_t N, typename T>
PUGS_INLINE constexpr TinyMatrix<M, N, T>
tensorProduct(const TinyVector<M, T>& x, const TinyVector<N, T>& y)
{
TinyMatrix<M, N, T> A;
for (size_t i = 0; i < M; ++i) {
for (size_t j = 0; j < N; ++j) {
A(i, j) = x[i] * y[j];
}
}
return A;
}
template <size_t N, typename T>
PUGS_INLINE constexpr T
det(const TinyMatrix<N, N, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "determinant is not defined for non-arithmetic types");
static_assert(std::is_floating_point<T>::value, "determinant for arbitrary dimension N is defined for floating "
"point types only");
TinyMatrix B = A;
TinyVector<N, size_t> index;
for (size_t i = 0; i < N; ++i) {
index[i] = i;
}
T determinent = 1;
for (size_t i = 0; i < N; ++i) {
for (size_t j = i; j < N; ++j) {
size_t l = j;
const size_t J = index[j];
for (size_t k = j + 1; k < N; ++k) {
if (std::abs(B(index[k], i)) > std::abs(B(J, i))) {
l = k;
}
}
if (l != j) {
std::swap(index[l], index[j]);
determinent *= -1;
}
}
const size_t I = index[i];
const T Bii = B(I, i);
if (Bii != 0) {
const T inv_Bii = 1. / B(I, i);
for (size_t k = i + 1; k < N; ++k) {
const size_t K = index[k];
const T factor = B(K, i) * inv_Bii;
for (size_t l = i + 1; l < N; ++l) {
B(K, l) -= factor * B(I, l);
}
}
} else {
return 0;
}
}
for (size_t i = 0; i < N; ++i) {
determinent *= B(index[i], i);
}
return determinent;
}
template <typename T>
PUGS_INLINE constexpr T
det(const TinyMatrix<1, 1, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types");
return A(0, 0);
}
template <typename T>
PUGS_INLINE constexpr T
det(const TinyMatrix<2, 2, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types");
return A(0, 0) * A(1, 1) - A(1, 0) * A(0, 1);
}
template <typename T>
PUGS_INLINE constexpr T
det(const TinyMatrix<3, 3, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "determinent is not defined for non-arithmetic types");
return A(0, 0) * (A(1, 1) * A(2, 2) - A(2, 1) * A(1, 2)) - A(1, 0) * (A(0, 1) * A(2, 2) - A(2, 1) * A(0, 2)) +
A(2, 0) * (A(0, 1) * A(1, 2) - A(1, 1) * A(0, 2));
}
template <size_t M, size_t N, typename T>
PUGS_INLINE constexpr TinyMatrix<M - 1, N - 1, T>
getMinor(const TinyMatrix<M, N, T>& A, size_t I, size_t J)
{
static_assert(M >= 2 and N >= 2, "minor calculation requires at least 2x2 matrices");
Assert((I < M) and (J < N));
TinyMatrix<M - 1, N - 1, T> m;
for (size_t i = 0; i < I; ++i) {
for (size_t j = 0; j < J; ++j) {
m(i, j) = A(i, j);
}
for (size_t j = J + 1; j < N; ++j) {
m(i, j - 1) = A(i, j);
}
}
for (size_t i = I + 1; i < M; ++i) {
for (size_t j = 0; j < J; ++j) {
m(i - 1, j) = A(i, j);
}
for (size_t j = J + 1; j < N; ++j) {
m(i - 1, j - 1) = A(i, j);
}
}
return m;
}
template <size_t N, typename T>
PUGS_INLINE T
trace(const TinyMatrix<N, N, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "trace is not defined for non-arithmetic types");
T t = A(0, 0);
for (size_t i = 1; i < N; ++i) {
t += A(i, i);
}
return t;
}
template <size_t N, typename T>
PUGS_INLINE constexpr TinyMatrix<N, N, T> inverse(const TinyMatrix<N, N, T>& A);
template <typename T>
PUGS_INLINE constexpr TinyMatrix<1, 1, T>
inverse(const TinyMatrix<1, 1, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types");
static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only");
TinyMatrix<1, 1, T> A_1(1. / A(0, 0));
return A_1;
}
template <size_t N, typename T>
PUGS_INLINE constexpr T
cofactor(const TinyMatrix<N, N, T>& A, size_t i, size_t j)
{
static_assert(std::is_arithmetic<T>::value, "cofactor is not defined for non-arithmetic types");
const T sign = ((i + j) % 2) ? -1 : 1;
return sign * det(getMinor(A, i, j));
}
template <typename T>
PUGS_INLINE constexpr TinyMatrix<2, 2, T>
inverse(const TinyMatrix<2, 2, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types");
static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only");
const T determinent = det(A);
const T inv_determinent = 1. / determinent;
TinyMatrix<2, 2, T> A_cofactors_T(A(1, 1), -A(0, 1), -A(1, 0), A(0, 0));
return A_cofactors_T *= inv_determinent;
}
template <typename T>
PUGS_INLINE constexpr TinyMatrix<3, 3, T>
inverse(const TinyMatrix<3, 3, T>& A)
{
static_assert(std::is_arithmetic<T>::value, "inverse is not defined for non-arithmetic types");
static_assert(std::is_floating_point<T>::value, "inverse is defined for floating point types only");
const T determinent = det(A);
TinyMatrix<3, 3, T> A_cofactors_T(cofactor(A, 0, 0), cofactor(A, 1, 0), cofactor(A, 2, 0), cofactor(A, 0, 1),
cofactor(A, 1, 1), cofactor(A, 2, 1), cofactor(A, 0, 2), cofactor(A, 1, 2),
cofactor(A, 2, 2));
return A_cofactors_T *= 1. / determinent;
}
#endif // TINYMATRIX_HPP