/* 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 * 2018 ~ 2019 */ #pragma once #include #include #include #include #include #include #include #include "mm/mmiterator.hpp" namespace mm { /* basic grid structure */ template class basic_matrix; /* basic wrapper */ template class matrix; // simple matrix format /* specialisations */ /* specialization of a matrix */ template class vector; // by default, set a column vector template class square_matrix; /* specialisation of a square_matrix for a sub-diagonal composed matrix */ template class multi_diag_matrix; } /* * Matrix class, no access methods */ template class mm::basic_matrix { public: using type = T; template friend class mm::basic_matrix; template friend class mm::matrix; template friend class mm::iter::basic_iterator; template friend class mm::iter::diag_iterator; //template //friend class mm::iter::basic_iterator>; //template //friend class mm::iter::basic_iterator::type, Rows, Cols, typename std::add_const>::type>; basic_matrix(); // from initializer_list basic_matrix(std::initializer_list> l); // copyable and movable basic_matrix(const basic_matrix& other) = default; basic_matrix(basic_matrix&& other) = default; // copy from another matrix template basic_matrix(const basic_matrix& other); 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 transposed() const; //inline basic_matrix td() const { return transposed(); } protected: template basic_matrix(ConstIterator begin, ConstIterator end); private: std::array data; }; template mm::basic_matrix::basic_matrix() { std::fill(data.begin(), data.end(), 0); } template mm::basic_matrix::basic_matrix( std::initializer_list> 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 template mm::basic_matrix::basic_matrix( const mm::basic_matrix& 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 template mm::basic_matrix::basic_matrix( ConstIterator begin, ConstIterator end ) { assert(static_cast(std::distance(begin, end)) >= ((Rows * Cols))); std::copy(begin, end, data.begin()); } template void mm::basic_matrix::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 void mm::basic_matrix::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)); } /* * Matrix object */ template class mm::matrix { public: //template using vec_iterator = mm::iter::basic_iterator>; //template using const_vec_iterator = mm::iter::basic_iterator::type, Rows, Cols, typename std::add_const>::type>; // default zeros constructor matrix() : M(std::make_shared>()), transposed(false) {} // from initializer_list matrix(std::initializer_list> l) : M(std::make_shared>(l)), transposed(false) {} // copyable and movable matrix(const matrix& other) // deep copy : M(std::make_shared>(*other.M)), transposed(other.transposed) {} matrix(basic_matrix&& other) // move ptr : M(other.M), transposed(other.transposed) { other.M = nullptr; } matrix operator=(const basic_matrix& other) // deep copy { *M = *other.M; transposed = other.transposed; } /* * Transposition */ matrix& transpose_d() { transposed = !transposed; return *this; } const matrix transpose() const { return matrix(M, !transposed); } inline matrix& td() { return transpose(); } inline matrix t() const { return transpose(); } // strongly transpose matrix transpose_cpy() const { matrix out(); // copy // TODO } /* * Pointer status */ bool expired() const { return M == nullptr; } /* * Downcasting conditions */ /// downcast to square matrix static inline constexpr bool is_square() { return (Rows == Cols); } inline constexpr square_matrix to_square() const { static_assert(is_square()); return static_cast>(*this); } /// downcast to col_vector static inline constexpr bool is_vector() { return (Rows == 1 || Cols == 1); } inline vector to_vector() const { if constexpr(Cols == 1) return static_cast>(*this); else if (Rows == 1) return vector(*this); // copy into column vector } /* Accessors */ virtual T& at(std::size_t row, std::size_t col) { return (transposed) ? M->data[col * Cols + row] : M->data[row * Cols + col]; } virtual const T& at(std::size_t row, std::size_t col) const { return (transposed) ? M->data[col * Cols + row] : M->data[row * Cols + col]; } std::size_t rows() const { return (transposed) ? Cols : Rows; } std::size_t cols() const { return (transposed) ? Rows : Cols; } virtual mm::matrix::vec_iterator operator[](std::size_t index) { return mm::matrix::vec_iterator(*M, index, 0, !transposed); } virtual mm::matrix::const_vec_iterator operator[](std::size_t index) const { return mm::matrix::const_vec_iterator(*M, index, 0, !transposed); } /* * Basic matematical operations (dimension indipendent) */ mm::matrix& operator+=(const mm::matrix& m) { for (unsigned row = 0; row < std::min(rows(), m.rows()); ++row) for (unsigned col = 0; col < std::min(cols(), m.cols()); ++col) at(row, col) += m.at(row, col); return *this; } mm::matrix& operator-=(const mm::matrix& m) { for (unsigned row = 0; row < std::min(rows(), m.rows()); ++row) for (unsigned col = 0; col < std::min(cols(), m.cols()); ++col) at(row, col) -= m.at(row, col); return *this; } mm::matrix operator*=(const T& k) { for (unsigned row = 0; row < rows(); ++row) for (auto& x : (*this)[row]) x *= k; return *this; } protected: std::shared_ptr> M; // shallow construction matrix(std::shared_ptr> grid, bool tr = false) : M(grid), transposed(tr) {} private: bool transposed; }; /* Basic operator overloading (dimension indipendent) */ template mm::matrix operator+( mm::matrix a, const mm::matrix& b ) { return a += b; } template mm::matrix operator-( mm::matrix a, const mm::matrix& b ) { return a -= b; } template mm::matrix operator*( mm::matrix a, const T& k ) { return a *= k; } template mm::matrix operator*( const T& k, mm::matrix a ) { return a *= k; } // simple multiplication template mm::matrix operator*( const mm::matrix& a, const mm::matrix& b ) { // TODO, adjust asserts for transposed cases static_assert(P1 == P2, "invalid matrix multiplication"); assert(a.cols() == b.rows()); mm::matrix result; const mm::matrix bt = b.t(); // weak transposition //npdebug("Calling *") for (unsigned row = 0; row < M; row++) for (unsigned col = 0; col < N; col++) result.at(row, col) = a[row] * bt[col]; // scalar product return result; } /* * Matrix operator << */ template std::ostream& operator<<(std::ostream& os, const mm::matrix& m) { for (unsigned index = 0; index < m.rows(); index++) { os << "[ "; for (unsigned col = 0; col < m.cols()-1; ++col) { os << std::setw(NumW) << m.at(index, col) << ", "; } os << std::setw(NumW) << m.at(index, m.cols()-1) << " ]\n"; } return os; } /* * Vector, TODO better manage column and row */ template class mm::vector : public mm::matrix { public: using mm::matrix::matrix; vector(std::initializer_list l) : mm::matrix(l) {} }; template mm::vector operator^(const mm::vector& v, const mm::vector& w) { mm::vector out; out[0] = v[1] * w[2] - v[2] * w[2]; out[1] = v[2] * w[0] - v[0] * w[2]; out[2] = v[0] * w[1] - v[1] * w[0]; return out; } /* * Square matrix */ template class mm::square_matrix : public mm::matrix { public: using mm::matrix::matrix; using diag_iterator = mm::iter::diag_iterator>; using const_diag_iterator = mm::iter::diag_iterator::type, N, typename std::add_const>::type>; virtual T trace(); inline T tr() { return trace(); } virtual mm::square_matrix::diag_iterator diag_beg(int row = 0) { return diag_iterator(*(this->M), row, 0); } virtual mm::square_matrix::const_diag_iterator diag_end(int row = 0) const { return const_diag_iterator(*(this->M), row, N); } // TODO, determinant /// in place inverse // TODO, det != 0 // TODO, use gauss jordan for invertible ones //void invert();, TODO, section algorithm /* * Generate the identity */ static inline constexpr mm::square_matrix identity() { mm::square_matrix 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 T mm::square_matrix::trace() { T sum = 0; for (const auto& x : diag_beg()) sum += x; return sum; } // TODO, static assert, for all: Diags > -N, Diags < N // TODO, force Diags to be ordered template class mm::multi_diag_matrix : public mm::square_matrix { T& shared_zero = 0; public: using mm::square_matrix::square_matrix; // TODO, ordered case: dichotomy search O(log(M)) // M = parameter pack size static inline bool constexpr is_in(std::size_t i, std::size_t j) { auto t = std::make_tuple(Diags...); for(unsigned k(0); k < sizeof...(Diags); ++k) if ((i - j) == std::get(t)) return true; return false; } virtual T& at(std::size_t row, std::size_t col) override { if (is_in(row, col)) return mm::square_matrix::at(row, col); shared_zero = 0; return shared_zero; } virtual const T& at(std::size_t row, std::size_t col) const override { if (is_in(row, col)) return mm::square_matrix::at(row, col); return 0; } // TODO, implement limited iterators }; /*template void constexpr diag_mult(const mm::multi_diag_matrix& a, const mm::matrix& b, mm::matrix& result) { static_assert(N == P && N == M, "invalid diagonal multiplication"); auto d = a.diagonal(Diags); if constexpr (Diags < 0) { for (unsigned k = 0; k < M; ++k) for (unsigned i = -Diags; i < N; ++i) result.at(i + Diags, k) += d[i + Diags] * b.at(i, k); } else { for (unsigned k = 0; k < M; ++k) for (unsigned i = Diags; i < N; ++i) result.at(i, k) += d[i - Diags] * b.at(i - Diags, k); } } template mm::matrix operator*( const mm::multi_diag_matrix& a, const mm::matrix& b ) { static_assert(N == P && N == M, "invalid matrix multiplication"); assert(a.cols() == b.rows()); mm::matrix result; (( auto d = a.diagonal(Diags); if constexpr (Diags < 0) { for (unsigned k = 0; k < M; ++k) for (unsigned i = -Diags; i < N; ++i) result.at(i + Diags, k) += d[i + Diags] * b.at(i, k); } else { for (unsigned k = 0; k < M; ++k) for (unsigned i = Diags; i < N; ++i) result.at(i, k) += d[i - Diags] * b.at(i - Diags, k); } ) ...); return result; }*/