11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
18PROMEKI_NAMESPACE_BEGIN
43template <
typename T,
size_t W,
size_t H>
class Matrix {
46 using RowDataType = Array<T, W>;
49 using DataType = Array<RowDataType, H>;
55 static Matrix<T, W, H> identity() {
56 Matrix<T, W, H> result;
57 for (
size_t i = 0; i < H; ++i) {
58 for (
size_t j = 0; j < W; ++j) {
59 result[i][j] = (i == j) ?
static_cast<T
>(1) :
static_cast<T
>(0);
72 static Matrix<T, W, H> rotationMatrix(T radians,
size_t dim, Error *err =
nullptr) {
73 static_assert(W == H,
"Matrix can only be calculated for square matrices");
74 static_assert(W >= 2,
"Matrix size must be at least 2x2 for rotation matrix generation.");
76 if (err !=
nullptr) *err = Error::InvalidDimension;
77 return Matrix<T, W, H>();
80 Matrix<T, W, H> result;
81 for (
size_t i = 0; i < W; ++i) result[i][i] = static_cast<T>(1);
82 result[dim][dim] = std::cos(radians);
83 result[dim][dim + 1] = -std::sin(radians);
84 result[dim + 1][dim] = std::sin(radians);
85 result[dim + 1][dim + 1] = std::cos(radians);
96 Matrix(
const DataType &data) : d(data) {}
103 RowDataType &operator[](
size_t index) {
return d[index]; }
110 const RowDataType &operator[](
size_t index)
const {
return d[index]; }
113 DataType &data() {
return d; }
116 const DataType &data()
const {
return d; }
119 size_t width()
const {
return W; }
122 size_t height()
const {
return H; }
125 bool isSquare()
const {
return W == H; }
131 Matrix<T, H, W> transpose()
const {
132 Matrix<T, H, W> result;
133 for (
size_t i = 0; i < H; ++i) {
134 for (
size_t j = 0; j < W; ++j) {
135 result[j][i] = d[i][j];
146 Matrix<T, W, H> operator+(
const Matrix<T, W, H> &other)
const {
147 Matrix<T, W, H> result;
148 for (
size_t i = 0; i < H; ++i) {
149 for (
size_t j = 0; j < W; ++j) {
150 result[i][j] = d[i][j] + other[i][j];
161 Matrix<T, W, H> operator-(
const Matrix<T, W, H> &other)
const {
162 Matrix<T, W, H> result;
163 for (
size_t i = 0; i < H; ++i) {
164 for (
size_t j = 0; j < W; ++j) {
165 result[i][j] = d[i][j] - other[i][j];
176 Matrix<T, W, H> operator*(
const T &scalar)
const {
177 Matrix<T, W, H> result;
178 for (
size_t i = 0; i < H; ++i) {
179 for (
size_t j = 0; j < W; ++j) {
180 result[i][j] = d[i][j] * scalar;
192 template <
size_t K> Matrix<T, W, K> operator*(
const Matrix<T, H, K> &other)
const {
193 Matrix<T, W, K> result;
194 for (
size_t i = 0; i < W; ++i) {
195 for (
size_t j = 0; j < K; ++j) {
197 for (
size_t k = 0; k < H; ++k) {
198 sum += d[i][k] * other[k][j];
211 Matrix<T, W, H> operator/(
const T &scalar)
const {
212 Matrix<T, W, H> result;
213 for (
size_t i = 0; i < H; ++i) {
214 for (
size_t j = 0; j < W; ++j) {
215 result[i][j] = d[i][j] / scalar;
226 void LUdecomposition(Matrix<T, W, H> &L, Matrix<T, W, H> &U)
const {
227 for (
size_t i = 0; i < H; ++i) {
228 for (
size_t j = 0; j < W; ++j) {
232 L[j][i] = (*this)[j][i];
233 for (
size_t k = 0; k < i; ++k) {
234 L[j][i] -= L[j][k] * U[k][i];
243 U[i][j] = (*this)[i][j] / L[i][i];
244 for (
size_t k = 0; k < i; ++k) {
245 U[i][j] -= ((L[i][k] * U[k][j])) / L[i][i];
260 T determinant()
const {
261 static_assert(W == H,
"Determinant can only be calculated for square matrices");
263 if constexpr (W == 2) {
264 return d[0][0] * d[1][1] - d[0][1] * d[1][0];
265 }
else if constexpr (W == 3) {
266 return d[0][0] * (d[1][1] * d[2][2] - d[1][2] * d[2][1]) -
267 d[0][1] * (d[1][0] * d[2][2] - d[1][2] * d[2][0]) +
268 d[0][2] * (d[1][0] * d[2][1] - d[1][1] * d[2][0]);
270 Matrix<T, W, H> L = Matrix<T, W, H>::identity();
271 Matrix<T, W, H> U = Matrix<T, W, H>::identity();
273 LUdecomposition(L, U);
276 for (
size_t i = 0; i < H; ++i) {
277 det *= L[i][i] * U[i][i];
292 Matrix<T, W, H> inverse(Error *err =
nullptr)
const {
293 static_assert(W == H,
"Inverse can only be calculated for square matrices");
295 T det = determinant();
297 if (err !=
nullptr) *err = Error::SingularMatrix;
298 return Matrix<T, W, H>();
301 Matrix<T, W, H> result;
302 if constexpr (W == 2) {
303 result[0][0] = d[1][1] / det;
304 result[0][1] = -d[0][1] / det;
305 result[1][0] = -d[1][0] / det;
306 result[1][1] = d[0][0] / det;
307 }
else if constexpr (W == 3) {
308 result[0][0] = (d[1][1] * d[2][2] - d[1][2] * d[2][1]) / det;
309 result[0][1] = (d[0][2] * d[2][1] - d[0][1] * d[2][2]) / det;
310 result[0][2] = (d[0][1] * d[1][2] - d[0][2] * d[1][1]) / det;
311 result[1][0] = (d[1][2] * d[2][0] - d[1][0] * d[2][2]) / det;
312 result[1][1] = (d[0][0] * d[2][2] - d[0][2] * d[2][0]) / det;
313 result[1][2] = (d[0][2] * d[1][0] - d[0][0] * d[1][2]) / det;
314 result[2][0] = (d[1][0] * d[2][1] - d[1][1] * d[2][0]) / det;
315 result[2][1] = (d[0][1] * d[2][0] - d[0][0] * d[2][1]) / det;
316 result[2][2] = (d[0][0] * d[1][1] - d[0][1] * d[1][0]) / det;
319 result = Matrix<T, W, H>::identity();
320 Matrix<T, W, H> temp = *
this;
322 for (
size_t i = 0; i < W; ++i) {
325 for (
size_t j = i + 1; j < H; ++j) {
326 if (std::abs(temp[j][i]) > std::abs(temp[pivot][i])) {
333 temp[i].swap(temp[pivot]);
334 result[i].swap(result[pivot]);
338 T pivotValue = temp[i][i];
339 if (pivotValue == 0) {
340 if (err !=
nullptr) *err = Error::SingularMatrix;
341 return Matrix<T, W, H>();
344 for (
size_t j = 0; j < W; ++j) {
345 temp[i][j] /= pivotValue;
346 result[i][j] /= pivotValue;
350 for (
size_t j = 0; j < H; ++j) {
352 T factor = temp[j][i];
353 for (
size_t k = 0; k < W; ++k) {
354 temp[j][k] -= factor * temp[i][k];
355 result[j][k] -= factor * result[i][k];
373 template <
size_t L> T dot(
const Matrix<T, L, 1> &other)
const {
374 static_assert(W == 1 || H == 1,
"Both matrices must have a single row or a single column");
375 static_assert(L == H || L == W,
"The matrices must have the same number of elements");
377 size_t size = (W == 1) ? H : W;
380 if constexpr (W == 1 && other.width() == 1) {
381 for (
size_t i = 0; i < size; ++i) {
382 result += (*this)[i][0] * other[i][0];
384 }
else if constexpr (H == 1 && other.height() == 1) {
385 for (
size_t i = 0; i < size; ++i) {
386 result += (*this)[0][i] * other[0][i];
398 template <
typename Func> Matrix<T, W, H> apply(Func &&func)
const {
399 Matrix<T, W, H> result;
400 for (
size_t i = 0; i < H; ++i) {
401 for (
size_t j = 0; j < W; ++j) {
402 result[i][j] = func((*
this)[i][j]);
412 T frobeniusNorm()
const {
414 for (
size_t i = 0; i < H; ++i) {
415 for (
size_t j = 0; j < W; ++j) {
416 sum += (*this)[i][j] * (*this)[i][j];
419 return std::sqrt(sum);
427 static_assert(W == H,
"Trace can only be calculated for square matrices");
429 for (
size_t i = 0; i < H; ++i) {
430 sum += (*this)[i][i];
441 for (
size_t i = 0; i < H; ++i) {
442 for (
size_t j = 0; j < W; ++j) {
443 total += (*this)[i][j];
454 Matrix<T, W, H> hadamardProduct(
const Matrix<T, W, H> &other)
const {
455 Matrix<T, W, H> result;
456 for (
size_t i = 0; i < H; ++i) {
457 for (
size_t j = 0; j < W; ++j) {
458 result[i][j] = (*this)[i][j] * other[i][j];
473 template <
size_t L> Matrix<T, H, L> outer_product(
const Matrix<T, L, 1> &other)
const {
474 static_assert(W == 1,
"The first matrix must have a single column");
475 Matrix<T, H, L> result;
476 for (
size_t i = 0; i < H; ++i) {
477 for (
size_t j = 0; j < L; ++j) {
478 result[i][j] = (*this)[i][0] * other[j][0];
488 Matrix<T, 1, H> rowSum()
const {
489 Matrix<T, 1, H> result;
490 for (
size_t i = 0; i < H; ++i) {
492 for (
size_t j = 0; j < W; ++j) {
493 total += (*this)[i][j];
495 result[0][i] = total;
504 Matrix<T, W, 1> colSum()
const {
505 Matrix<T, W, 1> result;
506 for (
size_t j = 0; j < W; ++j) {
508 for (
size_t i = 0; i < H; ++i) {
509 total += (*this)[i][j];
511 result[j][0] = total;
520 Matrix<T, W, 1> diagonal()
const {
521 static_assert(W == H,
"Diagonal can only be calculated for square matrices");
523 Matrix<T, W, 1> result;
524 for (
size_t i = 0; i < W; ++i) {
525 result[i][0] = (*this)[i][i];
534 Matrix<double, 1, H> rowMean()
const {
535 Matrix<double, 1, H> result;
536 for (
size_t i = 0; i < H; ++i) {
538 for (
size_t j = 0; j < W; ++j) {
539 total += (*this)[i][j];
541 result[0][i] = total / W;
550 Matrix<double, W, 1> colMean()
const {
551 Matrix<double, W, 1> result;
552 for (
size_t j = 0; j < W; ++j) {
554 for (
size_t i = 0; i < H; ++i) {
555 total += (*this)[i][j];
557 result[j][0] = total / H;
566 Matrix<T, H, W> rotateCW()
const {
567 Matrix<T, H, W> result;
568 for (
size_t i = 0; i < H; ++i) {
569 for (
size_t j = 0; j < W; ++j) {
570 result[i][j] = (*this)[H - j - 1][i];
580 Matrix<T, H, W> rotateCCW()
const {
581 Matrix<T, H, W> result;
582 for (
size_t i = 0; i < H; ++i) {
583 for (
size_t j = 0; j < W; ++j) {
584 result[i][j] = (*this)[j][W - i - 1];