Linear Algebra  arduino
Accessible implementations of linear algebra algorithms
Matrix.cpp
Go to the documentation of this file.
1 #ifndef ARDUINO
2 #include <linalg/Matrix.hpp>
3 #else
5 #endif
6 
7 //#region Constructors ---------------------------------------------------------
8 
9 Matrix::Matrix(storage_t &&storage, size_t rows, size_t cols)
10  : rows_(rows), //
11  cols_(cols), //
12  storage(std::move(storage)) {}
13 
14 Matrix::Matrix(const storage_t &storage, size_t rows, size_t cols)
15  : rows_(rows), //
16  cols_(cols), //
17  storage(storage) {}
18 
19 Matrix::Matrix(size_t rows, size_t cols)
20  : rows_(rows), //
21  cols_(cols), //
22  storage(rows * cols) {}
23 
24 Matrix::Matrix(Matrix &&other) { *this = std::move(other); }
25 
26 Matrix::Matrix(std::initializer_list<std::initializer_list<double>> init) {
27  *this = init;
28 }
29 
30 //#endregion // ----------------------------------------------------------------
31 
32 //#region Assignment -----------------------------------------------------------
33 
35  // By explicitly defining move assignment, we can be sure that the object
36  // that's being moved from has a consistent state.
37  this->storage = std::move(other.storage);
38  this->rows_ = other.rows_;
39  this->cols_ = other.cols_;
40  other.clear_and_deallocate();
41  return *this;
42 }
43 
44 Matrix &
45 Matrix::operator=(std::initializer_list<std::initializer_list<double>> init) {
46  // First determine the size of the initializer list matrix:
47  this->rows_ = init.size();
48  assert(rows() > 0);
49  this->cols_ = init.begin()->size();
50  assert(cols() > 0);
51 
52  // Ensure that each row has the same number of columns:
53  [[maybe_unused]] auto same_number_of_columns =
54  [&](const std::initializer_list<double> &row) {
55  return row.size() == cols();
56  };
57  assert(std::all_of(init.begin(), init.end(), same_number_of_columns));
58 
59  // Finally, allocate memory and copy the data to the internal storage
60  storage.resize(rows() * cols());
61  size_t r = 0;
62  for (const auto &row : init) {
63  size_t c = 0;
64  for (double el : row) {
65  (*this)(r, c) = el;
66  ++c;
67  }
68  ++r;
69  }
70 
71  return *this;
72 }
73 
74 //#endregion -------------------------------------------------------------------
75 
76 //#region Matrix size ----------------------------------------------------------
77 
78 void Matrix::reshape(size_t newrows, size_t newcols) {
79  assert(newrows * newcols == rows() * cols());
80  this->rows_ = newrows;
81  this->cols_ = newcols;
82 }
83 
84 Matrix Matrix::reshaped(size_t newrows, size_t newcols) const {
85  Matrix result = *this;
86  result.reshape(newrows, newcols);
87  return result;
88 }
89 
90 //#endregion -------------------------------------------------------------------
91 
92 //#region Element access -------------------------------------------------------
93 
94 double &Matrix::operator()(size_t row, size_t col) {
95 #if COL_MAJ_ORDER == 1
96  return storage[row + rows_ * col];
97 #else
98  return storage[row * cols_ + col];
99 #endif
100 }
101 
102 const double &Matrix::operator()(size_t row, size_t col) const {
103 #if COL_MAJ_ORDER == 1
104  return storage[row + rows_ * col];
105 #else
106  return storage[row * cols_ + col];
107 #endif
108 }
109 
110 //#endregion -------------------------------------------------------------------
111 
112 //#region Memory management ----------------------------------------------------
113 
115  this->rows_ = 0;
116  this->cols_ = 0;
117  storage_t().swap(this->storage); // replace storage with empty storage
118  // temporary storage goes out of scope and deallocates original storage
119 }
120 
121 //#endregion -------------------------------------------------------------------
122 
123 //#region Filling matrices -----------------------------------------------------
124 
125 void Matrix::fill(double value) {
126  std::fill(storage.begin(), storage.end(), value);
127 }
128 
130  fill(0);
131  for (size_t i = 0; i < std::min(rows(), cols()); ++i)
132  (*this)(i, i) = 1;
133 }
134 
135 #ifndef NO_RANDOM_SUPPORT
136 void Matrix::fill_random(double min, double max,
137  std::default_random_engine::result_type seed) {
138  std::default_random_engine gen(seed);
139  std::uniform_real_distribution<double> dist(min, max);
140  std::generate(storage.begin(), storage.end(), [&] { return dist(gen); });
141 }
142 #endif
143 
144 //#endregion -------------------------------------------------------------------
145 
146 //#region Creating special matrices --------------------------------------------
147 
148 Matrix Matrix::ones(size_t rows, size_t cols) {
149  return constant(rows, cols, 1);
150 }
151 
152 Matrix Matrix::zeros(size_t rows, size_t cols) {
153  Matrix m(rows, cols);
154  return m;
155 }
156 
157 Matrix Matrix::constant(size_t rows, size_t cols, double value) {
158  Matrix m(rows, cols);
159  m.fill(value);
160  return m;
161 }
162 
163 Matrix Matrix::identity(size_t rows, size_t cols) {
164  Matrix m(rows, cols);
165  m.fill_identity();
166  return m;
167 }
168 
169 Matrix Matrix::identity(size_t rows) { return identity(rows, rows); }
170 
171 #ifndef NO_RANDOM_SUPPORT
172 Matrix Matrix::random(size_t rows, size_t cols, double min, double max,
173  std::default_random_engine::result_type seed) {
174  Matrix m(rows, cols);
175  m.fill_random(min, max, seed);
176  return m;
177 }
178 #endif
179 
180 //#endregion -------------------------------------------------------------------
181 
182 //#region Swapping rows and columns --------------------------------------------
183 
184 void Matrix::swap_columns(size_t a, size_t b) {
185  for (size_t r = 0; r < rows(); ++r)
186  std::swap((*this)(r, a), (*this)(r, b));
187 }
188 
189 void Matrix::swap_rows(size_t a, size_t b) {
190  for (size_t c = 0; c < cols(); ++c)
191  std::swap((*this)(a, c), (*this)(b, c));
192 }
193 
194 //#endregion -------------------------------------------------------------------
195 
196 //#region Equality -------------------------------------------------------------
197 
198 bool Matrix::operator==(const Matrix &other) const {
199  // When comparing two matrices with a different size, this is most likely
200  // a bug, so don't return false, fail instead.
201  assert(this->rows() == other.rows());
202  assert(this->cols() == other.cols());
203  // Find the first element of the matrices that differs:
204  auto res = std::mismatch(begin(), end(), other.begin());
205  // If such an element doesn't exist (i.e. when the two matrices are the
206  // same), std::mismatch returns the end iterator:
207  return res.first == end();
208 }
209 
210 //#endregion -------------------------------------------------------------------
211 
212 //#region Norms ----------------------------------------------------------------
213 
219 double Matrix::normFro() const & {
220  // Reinterpret the matrix as one big vector, and compute the dot product
221  // with itself. This is the 2-norm of the vector squared, so the Frobenius
222  // norm of the matrix is the square root of this dot product.
223  // ‖A‖f = ‖vec(A)‖₂ = √(vec(A)ᵀvec(A))
224  return std::sqrt(Vector::dot_unchecked(*this, *this));
225 }
227 
228 double Matrix::normFro() && {
229  // Same as above, but cleans up its storage once it's done.
230  return std::sqrt(Vector::dot_unchecked(std::move(*this), *this));
231 }
232 
233 //#endregion -------------------------------------------------------------------
234 
235 //#region Printing -------------------------------------------------------------
236 
237 #ifndef NO_IOSTREAM_SUPPORT
238 
239 #include <iomanip>
240 #include <iostream>
241 
242 void Matrix::print(std::ostream &os, uint8_t precision, uint8_t width) const {
243  int backup_precision = os.precision();
244  precision = precision > 0 ? precision : backup_precision;
245  width = width > 0 ? width : precision + 9;
246  os.precision(precision);
247  for (size_t r = 0; r < rows(); ++r) {
248  for (size_t c = 0; c < cols(); ++c)
249  os << std::setw(width) << (*this)(r, c);
250  os << std::endl;
251  }
252  os.precision(backup_precision);
253 }
254 
255 std::ostream &operator<<(std::ostream &os, const Matrix &M) {
256  M.print(os);
257  return os;
258 }
259 
260 #endif
261 
262 //#endregion -------------------------------------------------------------------
263 
264 // Vector //
265 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
266 
267 //#region Constructors and assignment ------------------------------------------
268 
269 Vector::Vector(const Matrix &matrix)
270  : Matrix(matrix.storage, matrix.num_elems(), 1) {}
271 
273  : Matrix(std::move(matrix.storage), matrix.num_elems(), 1) {}
274 
275 Vector &Vector::operator=(std::initializer_list<double> init) {
276  // Assign this as a 1×n matrix to reuse the matrix code:
277  static_cast<Matrix &>(*this) = {init};
278  // Then swap the rows and columns to make it a column vector.
279  std::swap(rows_, cols_);
280  return *this;
281 }
282 
283 //#endregion -------------------------------------------------------------------
284 
285 //#region Creating special vectors ---------------------------------------------
286 
287 Vector Vector::ones(size_t size) { return Vector(Matrix::ones(size, 1)); }
288 
289 Vector Vector::zeros(size_t size) { return Vector(Matrix::zeros(size, 1)); }
290 
291 Vector Vector::constant(size_t size, double value) {
292  return Vector(Matrix::constant(size, 1, value));
293 }
294 
295 #ifndef NO_RANDOM_SUPPORT
296 Vector Vector::random(size_t size, double min, double max,
297  std::default_random_engine::result_type seed) {
298  return Vector(Matrix::random(size, 1, min, max, seed));
299 }
300 #endif
301 
302 //#endregion -------------------------------------------------------------------
303 
304 //#region Dot products ---------------------------------------------------------
305 
306 double Vector::dot_unchecked(const Matrix &a, const Matrix &b) {
307  assert(a.num_elems() == b.num_elems());
308  return std::inner_product(a.begin(), a.end(), b.begin(), double(0));
309 }
310 
311 double Vector::dot_unchecked(Matrix &&a, const Matrix &b) {
312  auto result = dot_unchecked(static_cast<const Matrix &>(a), b);
313  a.clear_and_deallocate();
314  return result;
315 }
316 
317 double Vector::dot_unchecked(const Matrix &a, Matrix &&b) {
318  return dot_unchecked(std::move(b), a);
319 }
320 
322  auto result = dot_unchecked(static_cast<const Matrix &>(a),
323  static_cast<const Matrix &>(b));
324  a.clear_and_deallocate();
325  b.clear_and_deallocate();
326  return result;
327 }
328 
329 double Vector::dot(const Vector &a, const Vector &b) {
330  return dot_unchecked(a, b);
331 }
332 
333 double Vector::dot(Vector &&a, const Vector &b) {
334  return dot_unchecked(std::move(a), b);
335 }
336 
337 double Vector::dot(const Vector &a, Vector &&b) {
338  return dot_unchecked(a, std::move(b));
339 }
340 
341 double Vector::dot(Vector &&a, Vector &&b) {
342  return dot_unchecked(std::move(a), std::move(b));
343 }
344 
345 //#endregion -------------------------------------------------------------------
346 
347 //#region Cross products -------------------------------------------------------
348 
350  assert(a.num_elems() == 3);
351  assert(b.num_elems() == 3);
352  double a0 = a(1) * b(2) - a(2) * b(1);
353  double a1 = a(2) * b(0) - a(0) * b(2);
354  double a2 = a(0) * b(1) - a(1) * b(0);
355  a(0) = a0;
356  a(1) = a1;
357  a(2) = a2;
358 }
359 
361  assert(a.num_elems() == 3);
362  assert(b.num_elems() == 3);
363  double a0 = a(2) * b(1) - a(1) * b(2);
364  double a1 = a(0) * b(2) - a(2) * b(0);
365  double a2 = a(1) * b(0) - a(0) * b(1);
366  a(0) = a0;
367  a(1) = a1;
368  a(2) = a2;
369 }
370 
371 void Vector::cross_inplace(Vector &a, const Vector &b) {
373 }
374 
377  b.clear_and_deallocate();
378 }
381 }
382 
385  b.clear_and_deallocate();
386 }
387 
388 Vector Vector::cross(const Vector &a, const Vector &b) {
389  Vector result = a;
390  cross_inplace(result, b);
391  return result;
392 }
393 
394 Vector &&Vector::cross(Vector &&a, const Vector &b) {
395  cross_inplace(a, b);
396  return std::move(a);
397 }
398 
399 Vector &&Vector::cross(const Vector &a, Vector &&b) {
400  cross_inplace_neg(b, a);
401  return std::move(b);
402 }
403 
405  cross_inplace(a, std::move(b));
406  return std::move(a);
407 }
408 
409 //#endregion -------------------------------------------------------------------
410 
411 //#region Vector norms ---------------------------------------------------------
412 
413 double Vector::norm2() const & {
414  // Compute the dot product of the vector with itself. This is the sum of
415  // the squares of the elements, which is the 2-norm of the vector squared.
416  // The 2-norm norm is the square root of this dot product.
417  // ‖v‖₂ = √(vᵀv)
418  return std::sqrt(dot(*this));
419 }
420 
421 double Vector::norm2() && {
422  // Same as above but cleans up its resources when it's done.
423  return std::sqrt(dot(std::move(*this)));
424 }
425 
426 //#endregion -------------------------------------------------------------------
427 
428 // RowVector //
429 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
430 
431 //#region Constructors and assignment ------------------------------------------
432 
434  : Matrix(matrix.storage, 1, matrix.num_elems()) {}
435 
437  : Matrix(std::move(matrix.storage), 1, matrix.num_elems()) {}
438 
439 RowVector &RowVector::operator=(std::initializer_list<double> init) {
440  static_cast<Matrix &>(*this) = {init};
441  return *this;
442 }
443 
444 //#endregion -------------------------------------------------------------------
445 
446 //#region Creating special row vectors -----------------------------------------
447 
449  return RowVector(Matrix::ones(1, size));
450 }
451 
453  return RowVector(Matrix::zeros(1, size));
454 }
455 
456 RowVector RowVector::constant(size_t size, double value) {
457  return RowVector(Matrix::constant(1, size, value));
458 }
459 
460 #ifndef NO_RANDOM_SUPPORT
461 RowVector RowVector::random(size_t size, double min, double max,
462  std::default_random_engine::result_type seed) {
463  return RowVector(Matrix::random(1, size, min, max, seed));
464 }
465 #endif
466 
467 //#endregion -------------------------------------------------------------------
468 
469 //#region Dot products ---------------------------------------------------------
470 
471 double RowVector::dot(const RowVector &a, const RowVector &b) {
472  return Vector::dot_unchecked(a, b);
473 }
474 
475 double RowVector::dot(RowVector &&a, const RowVector &b) {
476  return Vector::dot_unchecked(std::move(a), b);
477 }
478 
479 double RowVector::dot(const RowVector &a, RowVector &&b) {
480  return Vector::dot_unchecked(a, std::move(b));
481 }
482 
484  return Vector::dot_unchecked(std::move(a), std::move(b));
485 }
486 
487 //#endregion -------------------------------------------------------------------
488 
489 //#region Cross products -------------------------------------------------------
490 
493 }
496  b.clear_and_deallocate();
497 }
500 }
503  b.clear_and_deallocate();
504 }
505 
507  RowVector result = a;
508  cross_inplace(result, b);
509  return result;
510 }
512  cross_inplace(a, b);
513  return std::move(a);
514 }
516  cross_inplace_neg(b, a);
517  return std::move(b);
518 }
520  cross_inplace(a, std::move(b));
521  return std::move(a);
522 }
523 
525  return cross(*this, b);
526 }
528  return cross(std::move(*this), b);
529 }
531  return cross(*this, std::move(b));
532 }
534  return cross(std::move(*this), std::move(b));
535 }
536 
537 //#endregion -------------------------------------------------------------------
538 
539 //#region Norms ----------------------------------------------------------------
540 
541 double RowVector::norm2() const & { return std::sqrt(dot(*this)); }
542 
543 double RowVector::norm2() && { return std::sqrt(dot(std::move(*this))); }
544 
545 //#endregion -------------------------------------------------------------------
546 
547 // SquareMatrix //
548 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
549 
550 //#region Constructors and assignment ------------------------------------------
551 
553  std::initializer_list<std::initializer_list<double>> init) {
554  *this = init;
555 }
556 
557 SquareMatrix::SquareMatrix(Matrix &&matrix) : Matrix(std::move(matrix)) {
558  assert(rows() == cols());
559 }
560 
561 SquareMatrix::SquareMatrix(const Matrix &matrix) : Matrix(matrix) {
562  assert(rows() == cols());
563 }
564 
566  std::initializer_list<std::initializer_list<double>> init) {
567  static_cast<Matrix &>(*this) = init;
568  assert(rows() == cols());
569  return *this;
570 }
571 
572 //#endregion -------------------------------------------------------------------
573 
574 //#region Transposition --------------------------------------------------------
575 
577  assert(A.cols() == A.rows() && "Matrix should be square.");
578  for (size_t n = 0; n < A.rows() - 1; ++n)
579  for (size_t m = n + 1; m < A.rows(); ++m)
580  std::swap(A(n, m), A(m, n));
581 }
582 
583 //#endregion -------------------------------------------------------------------
584 
585 //#region Special matrices -----------------------------------------------------
586 
589 }
592 }
593 SquareMatrix SquareMatrix::constant(size_t rows, double value) {
594  return SquareMatrix(Matrix::constant(rows, rows, value));
595 }
597  SquareMatrix m(rows);
598  m.fill_identity();
599  return m;
600 }
601 
602 #ifndef NO_RANDOM_SUPPORT
604 SquareMatrix::random(size_t rows, double min, double max,
605  std::default_random_engine::result_type seed) {
606  return SquareMatrix(Matrix::random(rows, rows, min, max, seed));
607 }
608 #endif
609 
610 //#endregion -------------------------------------------------------------------
611 
612 // Matrix operations //
613 // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: //
614 
615 //#region Matrix multiplication ------------------------------------------------
616 
622 Matrix operator*(const Matrix &A, const Matrix &B) {
623  assert(A.cols() == B.rows() && "Inner dimensions don't match");
624  Matrix C = Matrix::zeros(A.rows(), B.cols());
625  for (size_t j = 0; j < B.cols(); ++j)
626  for (size_t k = 0; k < A.cols(); ++k)
627  for (size_t i = 0; i < A.rows(); ++i)
628  C(i, j) += A(i, k) * B(k, j);
629  return C;
630 }
632 
633 Matrix operator*(Matrix &&A, const Matrix &B) {
634  Matrix result = static_cast<const Matrix &>(A) * //
635  static_cast<const Matrix &>(B);
637  return result;
638 }
639 
640 Matrix operator*(const Matrix &A, Matrix &&B) {
641  Matrix result = static_cast<const Matrix &>(A) * //
642  static_cast<const Matrix &>(B);
644  return result;
645 }
646 
648  Matrix result = static_cast<const Matrix &>(A) * //
649  static_cast<const Matrix &>(B);
651  B.clear_and_deallocate();
652  return result;
653 }
654 
656  return SquareMatrix(static_cast<const Matrix &>(A) *
657  static_cast<const Matrix &>(B));
658 }
660  return SquareMatrix(static_cast<Matrix &&>(A) *
661  static_cast<const Matrix &>(B));
662 }
664  return SquareMatrix(static_cast<const Matrix &>(A) *
665  static_cast<Matrix &&>(B));
666 }
668  return SquareMatrix(static_cast<Matrix &&>(A) * //
669  static_cast<Matrix &&>(B));
670 }
671 
672 Vector operator*(const Matrix &A, const Vector &b) {
673  return Vector(A * static_cast<const Matrix &>(b));
674 }
675 Vector operator*(Matrix &&A, const Vector &b) {
676  return Vector(std::move(A) * static_cast<const Matrix &>(b));
677 }
678 Vector operator*(const Matrix &A, Vector &&b) {
679  return Vector(A * static_cast<Matrix &&>(b));
680 }
682  return Vector(std::move(A) * static_cast<Matrix &&>(b));
683 }
684 
685 RowVector operator*(const RowVector &a, const Matrix &B) {
686  return RowVector(static_cast<const Matrix &>(a) * B);
687 }
689  return RowVector(static_cast<Matrix &&>(a) * B);
690 }
692  return RowVector(static_cast<const Matrix &>(a) * std::move(B));
693 }
695  return RowVector(static_cast<Matrix &&>(a) * std::move(B));
696 }
697 
698 double operator*(const RowVector &a, const Vector &b) {
699  return Vector::dot_unchecked(a, b);
700 }
701 double operator*(RowVector &&a, const Vector &b) {
702  return Vector::dot_unchecked(std::move(a), b);
703 }
704 double operator*(const RowVector &a, Vector &&b) {
705  return Vector::dot_unchecked(a, std::move(b));
706 }
707 double operator*(RowVector &&a, Vector &&b) {
708  return Vector::dot_unchecked(std::move(a), std::move(b));
709 }
710 
711 //#endregion -------------------------------------------------------------------
712 
713 //#region Addition -------------------------------------------------------------
714 
720 Matrix operator+(const Matrix &A, const Matrix &B) {
721  assert(A.rows() == B.rows());
722  assert(A.cols() == B.cols());
723  Matrix C(A.rows(), A.cols());
724  std::transform(A.begin(), A.end(), B.begin(), C.begin(),
725  std::plus<double>());
726  return C;
727 }
729 
730 void operator+=(Matrix &A, const Matrix &B) {
731  assert(A.rows() == B.rows());
732  assert(A.cols() == B.cols());
733  std::transform(A.begin(), A.end(), B.begin(), A.begin(),
734  std::plus<double>());
735 }
736 Matrix &&operator+(Matrix &&A, const Matrix &B) {
737  A += B;
738  return std::move(A);
739 }
740 Matrix &&operator+(const Matrix &A, Matrix &&B) {
741  B += A;
742  return std::move(B);
743 }
745  A += B;
747  return std::move(A);
748 }
749 Vector &&operator+(Vector &&a, const Vector &b) {
750  static_cast<Matrix &>(a) += static_cast<const Matrix &>(b);
751  return std::move(a);
752 }
753 Vector &&operator+(const Vector &a, Vector &&b) {
754  static_cast<Matrix &>(b) += static_cast<const Matrix &>(a);
755  return std::move(b);
756 }
758  static_cast<Matrix &>(a) += static_cast<const Matrix &>(b);
760  return std::move(a);
761 }
763  static_cast<Matrix &>(a) += static_cast<const Matrix &>(b);
764  return std::move(a);
765 }
767  static_cast<Matrix &>(b) += static_cast<const Matrix &>(a);
768  return std::move(b);
769 }
771  static_cast<Matrix &>(a) += static_cast<const Matrix &>(b);
773  return std::move(a);
774 }
776  static_cast<Matrix &>(a) += static_cast<const Matrix &>(b);
777  return std::move(a);
778 }
780  static_cast<Matrix &>(b) += static_cast<const Matrix &>(a);
781  return std::move(b);
782 }
784  static_cast<Matrix &>(a) += static_cast<const Matrix &>(b);
786  return std::move(a);
787 }
788 Vector operator+(const Vector &a, const Vector &b) {
789  return Vector(static_cast<const Matrix &>(a) +
790  static_cast<const Matrix &>(b));
791 }
792 RowVector operator+(const RowVector &a, const RowVector &b) {
793  return RowVector(static_cast<const Matrix &>(a) +
794  static_cast<const Matrix &>(b));
795 }
797  return SquareMatrix(static_cast<const Matrix &>(a) +
798  static_cast<const Matrix &>(b));
799 }
800 
801 //#endregion -------------------------------------------------------------------
802 
803 //#region Subtraction ----------------------------------------------------------
804 
810 Matrix operator-(const Matrix &A, const Matrix &B) {
811  assert(A.rows() == B.rows());
812  assert(A.cols() == B.cols());
813  Matrix C(A.rows(), A.cols());
814  std::transform(A.begin(), A.end(), B.begin(), C.begin(),
815  std::minus<double>());
816  return C;
817 }
819 
820 void operator-=(Matrix &A, const Matrix &B) {
821  assert(A.rows() == B.rows());
822  assert(A.cols() == B.cols());
823  std::transform(A.begin(), A.end(), B.begin(), A.begin(),
824  std::minus<double>());
825 }
826 Matrix &&operator-(Matrix &&A, const Matrix &B) {
827  A -= B;
828  return std::move(A);
829 }
830 Matrix &&operator-(const Matrix &A, Matrix &&B) {
831  std::transform(A.begin(), A.end(), B.begin(), B.begin(),
832  std::minus<double>());
833  return std::move(B);
834 }
836  A -= B;
838  return std::move(A);
839 }
840 Vector &&operator-(Vector &&a, const Vector &b) {
841  static_cast<Matrix &>(a) -= static_cast<const Matrix &>(b);
842  return std::move(a);
843 }
844 Vector &&operator-(const Vector &a, Vector &&b) {
845  static_cast<const Matrix &>(a) - static_cast<Matrix &&>(b);
846  return std::move(b);
847 }
849  static_cast<Matrix &>(a) -= static_cast<const Matrix &>(b);
851  return std::move(a);
852 }
854  static_cast<Matrix &>(a) -= static_cast<const Matrix &>(b);
855  return std::move(a);
856 }
858  static_cast<const Matrix &>(a) - static_cast<Matrix &&>(b);
859  return std::move(b);
860 }
862  static_cast<Matrix &>(a) -= static_cast<const Matrix &>(b);
864  return std::move(a);
865 }
867  static_cast<Matrix &>(a) -= static_cast<const Matrix &>(b);
868  return std::move(a);
869 }
871  static_cast<const Matrix &>(a) - static_cast<Matrix &&>(b);
872  return std::move(b);
873 }
875  static_cast<Matrix &>(a) -= static_cast<const Matrix &>(b);
877  return std::move(a);
878 }
879 Vector operator-(const Vector &a, const Vector &b) {
880  return Vector(static_cast<const Matrix &>(a) -
881  static_cast<const Matrix &>(b));
882 }
883 RowVector operator-(const RowVector &a, const RowVector &b) {
884  return RowVector(static_cast<const Matrix &>(a) -
885  static_cast<const Matrix &>(b));
886 }
888  return SquareMatrix(static_cast<const Matrix &>(a) -
889  static_cast<const Matrix &>(b));
890 }
891 
892 //#endregion -------------------------------------------------------------------
893 
894 //#region Negation -------------------------------------------------------------
895 
904  Matrix result(A.rows(), A.cols());
905  std::transform(A.begin(), A.end(), result.begin(), std::negate<double>());
906  return result;
907 }
909 
911  std::transform(A.begin(), A.end(), A.begin(), std::negate<double>());
912  return std::move(A);
913 }
915  -static_cast<Matrix &&>(a);
916  return std::move(a);
917 }
919  -static_cast<Matrix &&>(a);
920  return std::move(a);
921 }
923  -static_cast<Matrix &&>(a);
924  return std::move(a);
925 }
927  return Vector(-static_cast<const Matrix &>(a));
928 }
930  return RowVector(-static_cast<const Matrix &>(a));
931 }
933  return SquareMatrix(-static_cast<const Matrix &>(a));
934 }
935 
936 //#endregion -------------------------------------------------------------------
937 
938 //#region Scalar multiplication ------------------------------------------------
939 
945 Matrix operator*(const Matrix &A, double s) {
946  Matrix C(A.rows(), A.cols());
947  std::transform(A.begin(), A.end(), C.begin(),
948  [s](double a) { return a * s; });
949  return C;
950 }
952 
953 void operator*=(Matrix &A, double s) {
954  std::transform(A.begin(), A.end(), A.begin(),
955  [s](double a) { return a * s; });
956 }
957 Matrix &&operator*(Matrix &&A, double s) {
958  A *= s;
959  return std::move(A);
960 }
961 Vector operator*(const Vector &a, double s) {
962  return Vector(static_cast<const Matrix &>(a) * s);
963 }
964 RowVector operator*(const RowVector &a, double s) {
965  return RowVector(static_cast<const Matrix &>(a) * s);
966 }
967 SquareMatrix operator*(const SquareMatrix &a, double s) {
968  return SquareMatrix(static_cast<const Matrix &>(a) * s);
969 }
970 Vector &&operator*(Vector &&a, double s) {
971  static_cast<Matrix &>(a) *= s;
972  return std::move(a);
973 }
974 RowVector &&operator*(RowVector &&a, double s) {
975  static_cast<Matrix &>(a) *= s;
976  return std::move(a);
977 }
979  static_cast<Matrix &>(a) *= s;
980  return std::move(a);
981 }
982 
983 Matrix operator*(double s, const Matrix &A) { return A * s; }
984 Matrix &&operator*(double s, Matrix &&A) { return std::move(A) * s; }
985 Vector operator*(double s, const Vector &a) { return a * s; }
986 RowVector operator*(double s, const RowVector &a) { return a * s; }
987 SquareMatrix operator*(double s, const SquareMatrix &a) { return a * s; }
988 Vector &&operator*(double s, Vector &&a) { return std::move(a) * s; }
989 RowVector &&operator*(double s, RowVector &&a) { return std::move(a) * s; }
991  return std::move(a) * s;
992 }
993 
994 //#endregion -------------------------------------------------------------------
995 
996 //#region Scalar division ------------------------------------------------------
997 
1003 Matrix operator/(const Matrix &A, double s) {
1004  Matrix C(A.rows(), A.cols());
1005  std::transform(A.begin(), A.end(), C.begin(),
1006  [s](double a) { return a / s; });
1007  return C;
1008 }
1010 
1011 void operator/=(Matrix &A, double s) {
1012  std::transform(A.begin(), A.end(), A.begin(),
1013  [s](double a) { return a / s; });
1014 }
1015 Matrix &&operator/(Matrix &&A, double s) {
1016  A /= s;
1017  return std::move(A);
1018 }
1019 Vector operator/(const Vector &a, double s) {
1020  return Vector(static_cast<const Matrix &>(a) / s);
1021 }
1022 RowVector operator/(const RowVector &a, double s) {
1023  return RowVector(static_cast<const Matrix &>(a) / s);
1024 }
1025 SquareMatrix operator/(const SquareMatrix &a, double s) {
1026  return SquareMatrix(static_cast<const Matrix &>(a) / s);
1027 }
1028 Vector &&operator/(Vector &&a, double s) {
1029  static_cast<Matrix &>(a) /= s;
1030  return std::move(a);
1031 }
1032 RowVector &&operator/(RowVector &&a, double s) {
1033  static_cast<Matrix &>(a) /= s;
1034  return std::move(a);
1035 }
1037  static_cast<Matrix &>(a) /= s;
1038  return std::move(a);
1039 }
1040 
1041 //#endregion -------------------------------------------------------------------
1042 
1043 //#region Transposition --------------------------------------------------------
1044 
1051  Matrix out(in.cols(), in.rows());
1052  for (size_t n = 0; n < in.rows(); ++n)
1053  for (size_t m = 0; m < in.cols(); ++m)
1054  out(m, n) = in(n, m);
1055  return out;
1056 }
1058 
1065  if (in.rows() == 1 || in.cols() == 1) { // Vectors
1066  Matrix out = in;
1067  out.reshape(in.cols(), in.rows());
1068  return out;
1069  } else { // General matrices (square and rectangular)
1070  return explicit_transpose(in);
1071  }
1072 }
1074 
1081  if (in.rows() == in.cols()) // Square matrices
1082  SquareMatrix::transpose_inplace(in); // → reuse storage
1083  else if (in.rows() == 1 || in.cols() == 1) // Vectors
1084  in.reshape(in.cols(), in.rows()); // → reshape row ↔ column
1085  else // General rectangular matrices
1086  in = explicit_transpose(in); // → full transpose
1087  return std::move(in);
1088 }
1090 
1092  SquareMatrix out = in;
1093  out.transpose_inplace();
1094  return out;
1095 }
1097  in.transpose_inplace();
1098  return std::move(in);
1099 }
1100 
1101 RowVector transpose(const Vector &in) { return RowVector(in); }
1102 RowVector transpose(Vector &&in) { return RowVector(std::move(in)); }
1103 Vector transpose(const RowVector &in) { return Vector(in); }
1104 Vector transpose(RowVector &&in) { return Vector(std::move(in)); }
1105 
1106 //#endregion -------------------------------------------------------------------
std::ostream & operator<<(std::ostream &os, const HouseholderQR &qr)
Print the Q and R matrices of a HouseholderQR object.
General matrix class.
Definition: Matrix.hpp:34
static Matrix constant(size_t rows, size_t cols, double value)
Create a matrix filled with a constant value.
Definition: Matrix.cpp:157
static Matrix identity(size_t rows, size_t cols)
Create an identity matrix.
Definition: Matrix.cpp:163
static Matrix random(size_t rows, size_t cols, double min=0, double max=1, std::default_random_engine::result_type seed=std::default_random_engine::default_seed)
Create a matrix with uniformly distributed random values.
Definition: Matrix.cpp:172
storage_t::iterator end()
Get the iterator to the element past the end of the matrix.
Definition: Matrix.hpp:220
static Matrix zeros(size_t rows, size_t cols)
Create a matrix filled with zeros.
Definition: Matrix.cpp:152
void fill_random(double min=0, double max=1, std::default_random_engine::result_type seed=std::default_random_engine::default_seed)
Fill the matrix with uniformly distributed random values.
Definition: Matrix.cpp:136
storage_t storage
Definition: Matrix.hpp:269
size_t rows_
Definition: Matrix.hpp:268
void swap_columns(size_t a, size_t b)
Swap two columns of the matrix.
Definition: Matrix.cpp:184
Matrix reshaped(size_t newrows, size_t newcols) const
Create a reshaped copy of the matrix.
Definition: Matrix.cpp:84
bool operator==(const Matrix &other) const
Check for equality of two matrices.
Definition: Matrix.cpp:198
double & operator()(size_t row, size_t col)
Get the element at the given position in the matrix.
Definition: Matrix.cpp:94
size_t num_elems() const
Get the number of elements in the matrix:
Definition: Matrix.hpp:85
static Matrix ones(size_t rows, size_t cols)
Create a matrix filled with ones.
Definition: Matrix.cpp:148
void swap_rows(size_t a, size_t b)
Swap two rows of the matrix.
Definition: Matrix.cpp:189
Matrix()=default
Default constructor.
size_t rows() const
Get the number of rows of the matrix.
Definition: Matrix.hpp:81
util::storage_t< double > storage_t
Container to store the elements of the matrix internally.
Definition: Matrix.hpp:40
void clear_and_deallocate()
Set the number of rows and columns to zero, and deallocate the storage.
Definition: Matrix.cpp:114
void reshape(size_t newrows, size_t newcols)
Reshape the matrix.
Definition: Matrix.cpp:78
Matrix & operator=(std::initializer_list< std::initializer_list< double >> init)
Assign the given values to the matrix.
Definition: Matrix.cpp:45
void fill_identity()
Fill the matrix as an identity matrix (all zeros except the diagonal which is one).
Definition: Matrix.cpp:129
double normFro() const &
Compute the Frobenius norm of the matrix.
Definition: Matrix.cpp:219
size_t cols() const
Get the number of columns of the matrix.
Definition: Matrix.hpp:83
size_t cols_
Definition: Matrix.hpp:268
storage_t::iterator begin()
Get the iterator to the first element of the matrix.
Definition: Matrix.hpp:213
void fill(double value)
Fill the matrix with a constant value.
Definition: Matrix.cpp:125
void print(std::ostream &os, uint8_t precision=0, uint8_t width=0) const
Print a matrix.
Definition: Matrix.cpp:242
A row vector (1×n matrix).
Definition: Matrix.hpp:437
static RowVector ones(size_t size)
Create a row vector filled with ones.
Definition: Matrix.cpp:448
size_t size() const
Get the number of elements in the vector.
Definition: Matrix.hpp:469
static void cross_inplace_neg(RowVector &a, const RowVector &b)
Compute the opposite of the cross product of two 3-vectors, overwriting the first vector with the res...
Definition: Matrix.cpp:498
static RowVector constant(size_t size, double value)
Create a row vector filled with a constant value.
Definition: Matrix.cpp:456
static void cross_inplace(RowVector &a, const RowVector &b)
Compute the cross product of two 3-vectors, overwriting the first vector with the result.
Definition: Matrix.cpp:491
static RowVector zeros(size_t size)
Create a row vector filled with zeros.
Definition: Matrix.cpp:452
RowVector & operator=(std::initializer_list< double > init)
Assign a list of values to the column vector.
Definition: Matrix.cpp:439
static RowVector random(size_t size, double min=0, double max=1, std::default_random_engine::result_type seed=std::default_random_engine::default_seed)
Create a row vector with uniformly distributed random values.
Definition: Matrix.cpp:461
double norm2() const &
Compute the 2-norm of the vector.
Definition: Matrix.cpp:541
static RowVector cross(const RowVector &a, const RowVector &b)
Compute the cross product of two 3-vectors.
Definition: Matrix.cpp:506
RowVector()=default
Default constructor.
static double dot(const RowVector &a, const RowVector &b)
Compute the dot product of two vectors.
Definition: Matrix.cpp:471
Square matrix class.
Definition: Matrix.hpp:572
static SquareMatrix constant(size_t rows, double value)
Create a square matrix filled with a constant value.
Definition: Matrix.cpp:593
SquareMatrix & operator=(std::initializer_list< std::initializer_list< double >> init)
Assign the given values to the square matrix.
Definition: Matrix.cpp:565
static SquareMatrix random(size_t rows, double min=0, double max=1, std::default_random_engine::result_type seed=std::default_random_engine::default_seed)
Create a matrix with uniformly distributed random values.
Definition: Matrix.cpp:604
SquareMatrix()=default
Default constructor.
void transpose_inplace()
Transpose the matrix in-place.
Definition: Matrix.hpp:615
static SquareMatrix zeros(size_t rows)
Create a square matrix filled with zeros.
Definition: Matrix.cpp:590
static void transpose_inplace(Matrix &A)
Transpose the matrix in-place.
Definition: Matrix.cpp:576
static SquareMatrix ones(size_t rows)
Create a square matrix filled with ones.
Definition: Matrix.cpp:587
static SquareMatrix identity(size_t rows)
Create a square identity matrix.
Definition: Matrix.cpp:596
A column vector (n×1 matrix).
Definition: Matrix.hpp:278
static void cross_inplace_unchecked(Matrix &a, const Matrix &b)
Compute the cross product of two 3-vectors, overwriting the first vector with the result.
Definition: Matrix.cpp:349
static void cross_inplace(Vector &a, const Vector &b)
Compute the cross product of two 3-vectors, overwriting the first vector with the result.
Definition: Matrix.cpp:371
size_t size() const
Get the number of elements in the vector.
Definition: Matrix.hpp:310
static void cross_inplace_unchecked_neg(Matrix &a, const Matrix &b)
Compute the opposite of the cross product of two 3-vectors, overwriting the first vector with the res...
Definition: Matrix.cpp:360
static Vector random(size_t size, double min=0, double max=1, std::default_random_engine::result_type seed=std::default_random_engine::default_seed)
Create a vector with uniformly distributed random values.
Definition: Matrix.cpp:296
static Vector constant(size_t size, double value)
Create a vector filled with a constant value.
Definition: Matrix.cpp:291
static Vector zeros(size_t size)
Create a vector filled with zeros.
Definition: Matrix.cpp:289
static double dot(const Vector &a, const Vector &b)
Compute the dot product of two vectors.
Definition: Matrix.cpp:329
static Vector cross(const Vector &a, const Vector &b)
Compute the cross product of two 3-vectors.
Definition: Matrix.cpp:388
Vector()=default
Default constructor.
double norm2() const &
Compute the 2-norm of the vector.
Definition: Matrix.cpp:413
static double dot_unchecked(const Matrix &a, const Matrix &b)
Compute the dot product of two vectors.
Definition: Matrix.cpp:306
static void cross_inplace_neg(Vector &a, const Vector &b)
Compute the opposite of the cross product of two 3-vectors, overwriting the first vector with the res...
Definition: Matrix.cpp:379
static Vector ones(size_t size)
Create a vector filled with ones.
Definition: Matrix.cpp:287
Vector & operator=(std::initializer_list< double > init)
Assign a list of values to the column vector.
Definition: Matrix.cpp:275
Matrix operator+(const Matrix &A, const Matrix &B)
Matrix addition.
Definition: Matrix.cpp:720
void operator+=(Matrix &A, const Matrix &B)
Definition: Matrix.cpp:730
Matrix operator*(const Matrix &A, const Matrix &B)
Matrix multiplication.
Definition: Matrix.cpp:622
void operator-=(Matrix &A, const Matrix &B)
Definition: Matrix.cpp:820
Matrix operator-(const Matrix &A, const Matrix &B)
Matrix subtraction.
Definition: Matrix.cpp:810
Matrix transpose(const Matrix &in)
Matrix transpose for rectangular or square matrices and row or column vectors.
Definition: Matrix.cpp:1064
Matrix explicit_transpose(const Matrix &in)
Matrix transpose for general matrices.
Definition: Matrix.cpp:1050
void operator/=(Matrix &A, double s)
Definition: Matrix.cpp:1011
Matrix operator/(const Matrix &A, double s)
Scalar division.
Definition: Matrix.cpp:1003
void operator*=(Matrix &A, double s)
Definition: Matrix.cpp:953