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