summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--include/mm/mmmatrix.hpp374
-rw-r--r--mmmatrix.cpp1
-rw-r--r--test/CMakeLists.txt32
-rw-r--r--test/matrix_example.cpp54
5 files changed, 462 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ed126da..7a14759 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,6 +12,7 @@ list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake)
# build libmm
add_library(mm STATIC
${CMAKE_CURRENT_SOURCE_DIR}/mmvec.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mmmatrix.cpp
)
add_library(MM::mm ALIAS mm)
diff --git a/include/mm/mmmatrix.hpp b/include/mm/mmmatrix.hpp
new file mode 100644
index 0000000..5285429
--- /dev/null
+++ b/include/mm/mmmatrix.hpp
@@ -0,0 +1,374 @@
+/* mmmatrix.hpp
+ * Part of Mathematical library built (ab)using Modern C++ 17 abstractions.
+ *
+ * This library is not intended to be _performant_, it does not contain
+ * hand written SMID / SSE / AVX optimizations. It is instead an example
+ * of highly inefficient (but abstract!) code, where matrices can contain any
+ * data type.
+ *
+ * Naoki Pross <naopross@thearcway.org>
+ * 2018 ~ 2019
+ */
+#pragma once
+
+#include <iostream>
+#include <iomanip>
+#include <cstring>
+#include <cassert>
+#include <initializer_list>
+#include <array>
+
+namespace mm {
+ template<typename T, std::size_t Rows, std::size_t Cols>
+ class basic_matrix;
+
+ /* specialization of basic_matrx for Cols = 1 */
+ template<typename T, std::size_t Rows>
+ class row_vec;
+
+ /* specialization of basic_matrx for Rows = 1 */
+ template<typename T, std::size_t Cols>
+ class col_vec;
+
+ /* shorter name for basic_matrix */
+ template<typename T, std::size_t Rows, std::size_t Cols>
+ class matrix;
+
+ /* specialization of basic_matrix for Rows == Cols */
+ template<typename T, std::size_t N>
+ class square_matrix;
+
+ // template<typename T, std::size_t N>
+ // class diag_matrix;
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+class mm::basic_matrix {
+public:
+ using type = T;
+
+ template<typename U, std::size_t ORows, std::size_t OCols>
+ friend class mm::basic_matrix;
+
+ static constexpr std::size_t rows = Rows;
+ static constexpr std::size_t cols = Cols;
+
+ basic_matrix();
+
+ // from initializer_list
+ basic_matrix(std::initializer_list<std::initializer_list<T>> l);
+
+ // copyable and movable
+ basic_matrix(const basic_matrix<T, Rows, Cols>& other);
+ basic_matrix(basic_matrix<T, Rows, Cols>&& other);
+
+ // copy from another matrix
+ template<std::size_t ORows, std::size_t OCols>
+ basic_matrix(const basic_matrix<T, ORows, OCols>& other);
+
+ // access data
+ T& at(std::size_t row, std::size_t col);
+ const T& at(std::size_t row, std::size_t col) const;
+ // allows to access a matrix M at row j col k with M[j][k]
+ auto operator[](std::size_t index);
+
+ void swap_rows(std::size_t x, std::size_t y);
+ void swap_cols(std::size_t x, std::size_t y);
+
+ // mathematical operations
+ virtual basic_matrix<T, Cols, Rows> transposed() const;
+ inline basic_matrix<T, Cols, Rows> td() const { return transposed(); }
+
+ // bool is_invertible() const;
+ // basic_matrix<T, Rows, Cols> inverse() const;
+
+
+ /// downcast to square matrix
+ static inline constexpr bool is_square() { return (Rows == Cols); }
+ inline constexpr square_matrix<T, Rows> to_square() const {
+ static_assert(is_square());
+ return static_cast<square_matrix<T, Rows>>(*this);
+ }
+
+
+ /// downcast to row_vector
+ static inline constexpr bool is_row_vec() { return (Cols == 1); }
+ inline constexpr row_vec<T, Rows> to_row_vec() const {
+ static_assert(is_row_vec());
+ return static_cast<row_vec<T, Rows>>(*this);
+ }
+
+ /// downcast to col_vector
+ static inline constexpr bool is_col_vec() { return (Rows == 1); }
+ inline constexpr col_vec<T, Cols> to_col_vec() const {
+ static_assert(is_col_vec());
+ return static_cast<col_vec<T, Cols>>(*this);
+ }
+
+protected:
+ template<typename ConstIterator>
+ basic_matrix(ConstIterator begin, ConstIterator end);
+
+private:
+ std::array<T, Rows * Cols> data;
+};
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix() {
+ std::fill(data.begin(), data.end(), 0);
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ std::initializer_list<std::initializer_list<T>> l
+) {
+ assert(l.size() == Rows);
+ auto data_it = data.begin();
+
+ for (auto&& row : l) {
+ data_it = std::copy(row.begin(), row.end(), data_it);
+ }
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ const mm::basic_matrix<T, Rows, Cols>& other
+) : data(other.data) {}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ mm::basic_matrix<T, Rows, Cols>&& other
+) : data(std::forward<decltype(other.data)>(other.data)) {}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+template<std::size_t ORows, std::size_t OCols>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ const mm::basic_matrix<T, ORows, OCols>& other
+) {
+ static_assert((ORows <= Rows),
+ "cannot copy a taller matrix into a smaller one"
+ );
+
+ static_assert((OCols <= Cols),
+ "cannot copy a larger matrix into a smaller one"
+ );
+
+ std::fill(data.begin(), data.end(), 0);
+ for (unsigned row = 0; row < Rows; row++)
+ for (unsigned col = 0; col < Cols; col++)
+ this->at(row, col) = other.at(row, col);
+}
+
+/* protected construtor */
+template<typename T, std::size_t Rows, std::size_t Cols>
+template<typename ConstIterator>
+mm::basic_matrix<T, Rows, Cols>::basic_matrix(
+ ConstIterator begin, ConstIterator end
+) {
+ assert(static_cast<unsigned>(std::distance(begin, end)) >= ((Rows * Cols)));
+ std::copy(begin, end, data.begin());
+}
+
+
+/* member functions */
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+T& mm::basic_matrix<T, Rows, Cols>::at(std::size_t row, std::size_t col) {
+ assert(row < Rows); // "out of row bound"
+ assert(col < Cols); // "out of column bound"
+
+ return data[row * Cols + col];
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+const T& mm::basic_matrix<T, Rows, Cols>::at(std::size_t row, std::size_t col) const {
+ assert(row < Rows); // "out of row bound"
+ assert(col < Cols); // "out of column bound"
+
+ return data[row * Cols + col];
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+auto mm::basic_matrix<T, Rows, Cols>::operator[](std::size_t index) {
+ if constexpr (is_row_vec() || is_col_vec()) {
+ return data.at(index);
+ } else {
+ return row_vec<T, Rows>(
+ data.cbegin() + (index * Cols),
+ data.cbegin() + ((index + 1) * Cols) + 1
+ );
+ }
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+void mm::basic_matrix<T, Rows, Cols>::swap_rows(std::size_t x, std::size_t y) {
+ if (x == y)
+ return;
+
+ for (unsigned col = 0; col < Cols; col++)
+ std::swap(this->at(x, col), this->at(y, col));
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+void mm::basic_matrix<T, Rows, Cols>::swap_cols(std::size_t x, std::size_t y) {
+ if (x == y)
+ return;
+
+ for (unsigned row = 0; row < rows; row++)
+ std::swap(this->at(row, x), this->at(row, y));
+}
+
+template<typename T, std::size_t M, std::size_t N>
+mm::basic_matrix<T, N, M> mm::basic_matrix<T, M, N>::transposed() const {
+ mm::basic_matrix<T, N, M> result;
+
+ for (unsigned row = 0; row < M; row++)
+ for (unsigned col = 0; col < N; col++)
+ result.at(col, row) = this->at(row, col);
+
+ return result;
+}
+
+
+/* operator overloading */
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator+(
+ const mm::basic_matrix<T, Rows, Cols>& a,
+ const mm::basic_matrix<T, Rows, Cols>& b
+) {
+ mm::basic_matrix<T, Rows, Cols> result;
+
+ for (unsigned row = 0; row < Rows; row++)
+ for (unsigned col = 0; col < Cols; col++)
+ result.at(row, col) = a.at(row, col) + b.at(row, col);
+
+ return result;
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator*(
+ const mm::basic_matrix<T, Rows, Cols>& m,
+ const T& scalar
+) {
+ mm::basic_matrix<T, Rows, Cols> result;
+ for (unsigned row = 0; row < Rows; row++)
+ for (unsigned col = 0; col < Cols; col++)
+ result.at(row, col) = m.at(row, col) * scalar;
+
+ return result;
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator*(
+ const T& scalar,
+ const mm::basic_matrix<T, Rows, Cols>& m
+) {
+ return m * scalar;
+}
+
+template<typename T, std::size_t M, std::size_t P1, std::size_t P2, std::size_t N>
+mm::basic_matrix<T, M, N> operator*(
+ const mm::basic_matrix<T, M, P1>& a,
+ const mm::basic_matrix<T, P2, N>& b
+) {
+ static_assert(P1 == P2, "invalid matrix multiplication");
+ mm::basic_matrix<T, M, N> result;
+
+ // TODO: use a more efficient algorithm
+ for (unsigned row = 0; row < M; row++)
+ for (unsigned col = 0; col < N; col++)
+ for (unsigned k = 0; k < P1; k++)
+ result.at(row, col) = a.at(row, k) * b.at(k, col);
+
+ return result;
+}
+
+
+template<typename T, std::size_t Rows, std::size_t Cols>
+mm::basic_matrix<T, Rows, Cols> operator-(
+ const mm::basic_matrix<T, Rows, Cols>& a,
+ const mm::basic_matrix<T, Rows, Cols>& b
+) {
+ return a + (static_cast<T>(-1) * b);
+}
+
+template<typename T, std::size_t Rows, std::size_t Cols, unsigned NumW = 3>
+std::ostream& operator<<(std::ostream& os, const mm::basic_matrix<T, Rows, Cols>& m) {
+ for (unsigned row = 0; row < Rows; row++) {
+ os << "[ ";
+ for (unsigned col = 0; col < (Cols -1); col++) {
+ os << std::setw(NumW) << m.at(row, col) << ", ";
+ }
+ os << std::setw(NumW) << m.at(row, (Cols -1)) << " ]\n";
+ }
+
+ return os;
+}
+
+
+
+
+/* row vector specialization */
+template<typename T, std::size_t Rows>
+class mm::row_vec : public mm::basic_matrix<T, Rows, 1> {
+public:
+ using mm::basic_matrix<T, Rows, 1>::basic_matrix;
+};
+
+/* column vector specialization */
+template<typename T, std::size_t Cols>
+class mm::col_vec : public mm::basic_matrix<T, 1, Cols> {
+public:
+ using mm::basic_matrix<T, 1, Cols>::basic_matrix;
+};
+
+/* general specialization (alias) */
+template<typename T, std::size_t Rows, std::size_t Cols>
+class mm::matrix : public mm::basic_matrix<T, Rows, Cols> {
+public:
+ using mm::basic_matrix<T, Rows, Cols>::basic_matrix;
+};
+
+/* square matrix specializaiton */
+template<typename T, std::size_t N>
+class mm::square_matrix : public mm::basic_matrix<T, N, N> {
+public:
+ using mm::basic_matrix<T, N, N>::basic_matrix;
+
+ /// in place transpose
+ void transpose();
+ inline void t() { transpose(); }
+
+ T trace();
+ inline T tr() { return trace(); }
+
+ /// in place inverse
+ void invert();
+
+ // get the identity of size N
+ static inline constexpr square_matrix<T, N> identity() {
+ square_matrix<T, N> i;
+ for (unsigned row = 0; row < N; row++)
+ for (unsigned col = 0; col < N; col++)
+ i.at(row, col) = (row == col) ? 1 : 0;
+
+ return i;
+ }
+};
+
+
+template<typename T, std::size_t N>
+void mm::square_matrix<T, N>::transpose() {
+ for (unsigned row = 0; row < N; row++)
+ for (unsigned col = 0; col < row; col++)
+ std::swap(this->at(row, col), this->at(col, row));
+}
+
+template<typename T, std::size_t N>
+T mm::square_matrix<T, N>::trace() {
+ T sum = 0;
+ for (unsigned i = 0; i < N; i++)
+ sum += this->at(i, i);
+
+ return sum;
+}
diff --git a/mmmatrix.cpp b/mmmatrix.cpp
new file mode 100644
index 0000000..c0ce9eb
--- /dev/null
+++ b/mmmatrix.cpp
@@ -0,0 +1 @@
+#include "mm/mmmatrix.hpp"
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index e80e737..89e7c47 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,3 +1,4 @@
+# vectors example
add_executable(vec_example
vec_example.cpp
)
@@ -26,3 +27,34 @@ target_link_libraries(vec_example
PRIVATE
MM::mm
)
+
+
+# matrix example
+add_executable(matrix_example
+ matrix_example.cpp
+)
+
+target_compile_options(matrix_example
+ PRIVATE
+ $<$<CXX_COMPILER_ID:GNU>:
+ -pedantic -Wall -Wextra -Wcast-qual -Wcast-align -Wpointer-arith
+ -Winit-self -Wshadow -Wswitch-enum -Wredundant-decls -Wfloat-equal
+ -Wundef -Wvla -Wconversion -Wstrict-aliasing
+ >
+ $<$<CXX_COMPILER_ID:MSVC>:/W4>
+)
+
+target_compile_features(matrix_example
+ PRIVATE
+ cxx_std_17
+)
+
+target_include_directories(matrix_example
+ PRIVATE
+ ${MM_INCLUDE_DIRS}
+)
+
+target_link_libraries(matrix_example
+ PRIVATE
+ MM::mm
+)
diff --git a/test/matrix_example.cpp b/test/matrix_example.cpp
new file mode 100644
index 0000000..e7f6580
--- /dev/null
+++ b/test/matrix_example.cpp
@@ -0,0 +1,54 @@
+#include "mm/mmmatrix.hpp"
+
+#include <iostream>
+#include <complex>
+
+int main(int argc, char *argv[]) {
+ std::cout << "MxN dimensional (int) matrices" << std::endl;
+ mm::matrix<int, 3, 2> a {{1, 2}, {3, 4}, {5, 6}};
+ mm::matrix<int, 3, 2> b {{4, 3}, {9, 1}, {2, 5}};
+ mm::matrix<int, 2, 4> c {{1, 2, 3, 4}, {5, 6, 7, 8}};
+
+ std::cout << "a = \n" << a;
+ std::cout << "b = \n" << b;
+ std::cout << "c = \n" << c;
+ std::cout << std::endl;
+
+ // access elements
+ std::cout << "Access elements" << std::endl;
+ std::cout << "a.at(2,0) = " << a.at(2, 0) << std::endl;
+ std::cout << "a[2][0] = " << a[2][0] << std::endl;;
+ std::cout << std::endl;
+
+ // basic operations
+ std::cout << "Basic operations" << std::endl;
+ std::cout << "a + b = \n" << a + b;
+ std::cout << "a - b = \n" << a - b;
+ std::cout << "a * c = \n" << a * c;
+ std::cout << "a * 2 = \n" << a * 2;
+ std::cout << "2 * a = \n" << 2 * a;
+ std::cout << "a.td() = \n" << a.td(); // or a.trasposed();
+ std::cout << std::endl;
+
+ // special matrices
+ mm::square_matrix<std::complex<int>, 2> f {{{2, 3}, {1, 4}}, {{6, 1}, {-3, 4}}};
+
+ std::cout << "Square matrix" << std::endl;
+ std::cout << "f = \n" << f;
+
+ std::cout << "tr(f) = " << f.tr() /* or f.trace() */ << std::endl;
+
+ f.t();
+ std::cout << "after in place transpose f.t(), f = \n" << f;
+ std::cout << std::endl;
+
+
+ auto identity = mm::square_matrix<int, 3>::identity();
+
+ std::cout << "Identity matrix" << std::endl;
+ std::cout << "I = \n" << identity;
+ std::cout << std::endl;
+
+
+ return 0;
+}