From b26eb24229010c7dd4156590b8c2aeca2b57e75c Mon Sep 17 00:00:00 2001 From: Hossein Moein Date: Thu, 7 Nov 2024 14:57:04 -0500 Subject: [PATCH 1/4] Starting on the matrix --- docs/HTML/DataFrame.html | 2 +- include/DataFrame/DataFrameStatsVisitors.h | 62 ++++---- include/DataFrame/Utils/Matrix.h | 125 +++++++++++++++ include/DataFrame/Utils/Matrix.tcc | 175 +++++++++++++++++++++ 4 files changed, 332 insertions(+), 32 deletions(-) create mode 100644 include/DataFrame/Utils/Matrix.h create mode 100644 include/DataFrame/Utils/Matrix.tcc diff --git a/docs/HTML/DataFrame.html b/docs/HTML/DataFrame.html index 247188fa..bdeec69e 100644 --- a/docs/HTML/DataFrame.html +++ b/docs/HTML/DataFrame.html @@ -622,7 +622,7 @@

API Reference with code samples &# - Gears    + Gears & Stuff    diff --git a/include/DataFrame/DataFrameStatsVisitors.h b/include/DataFrame/DataFrameStatsVisitors.h index 1982460a..aa7e4733 100644 --- a/include/DataFrame/DataFrameStatsVisitors.h +++ b/include/DataFrame/DataFrameStatsVisitors.h @@ -6382,16 +6382,12 @@ struct LinearFitVisitor { const H &x_begin, const H &x_end, const H &y_begin, const H &y_end) { - const size_type col_s = std::distance(x_begin, x_end); + const size_type col_s = + std::min(std::distance(x_begin, x_end), + std::distance(y_begin, y_end)); const auto thread_level = (col_s < ThreadPool::MUL_THR_THHOLD) ? 0L : ThreadGranularity::get_thread_level(); -#ifdef HMDF_SANITY_EXCEPTIONS - if (col_s != size_type(std::distance(y_begin, y_end))) - throw DataFrameError("LinearFitVisitor: two columns must be " - "of equal sizes"); -#endif // HMDF_SANITY_EXCEPTIONS - value_type sum_x { 0 }; // Sum of all observed x value_type sum_y { 0 }; // Sum of all observed y value_type sum_x2 { 0 }; // Sum of all observed x squared @@ -7543,30 +7539,32 @@ is_normal(const V &column, double epsl, bool check_for_standard) { svisit.post(); const value_type mean = static_cast(svisit.get_mean()); - const value_type std = static_cast(svisit.get_std()); - const value_type high_band_1 = static_cast(mean + std); - const value_type low_band_1 = static_cast(mean - std); + const value_type stdev = static_cast(svisit.get_std()); + const value_type high_band_1 = static_cast(mean + stdev); + const value_type low_band_1 = static_cast(mean - stdev); double count_1 = 0.0; const value_type high_band_2 = - static_cast(mean + std * 2.0); - const value_type low_band_2 = static_cast(mean - std * 2.0); + static_cast(mean + stdev * 2.0); + const value_type low_band_2 = + static_cast(mean - stdev * 2.0); double count_2 = 0.0; const value_type high_band_3 = - static_cast(mean + std * 3.0); - const value_type low_band_3 = static_cast(mean - std * 3.0); + static_cast(mean + stdev * 3.0); + const value_type low_band_3 = + static_cast(mean - stdev * 3.0); double count_3 = 0.0; - for (auto citer : column) [[likely]] { - if (citer >= low_band_1 && citer < high_band_1) { + for (const auto &val : column) [[likely]] { + if (val >= low_band_1 && val < high_band_1) { count_3 += 1; count_2 += 1; count_1 += 1; } - else if (citer >= low_band_2 && citer < high_band_2) { + else if (val >= low_band_2 && val < high_band_2) { count_3 += 1; count_2 += 1; } - else if (citer >= low_band_3 && citer < high_band_3) { + else if (val >= low_band_3 && val < high_band_3) { count_3 += 1; } } @@ -7578,7 +7576,7 @@ is_normal(const V &column, double epsl, bool check_for_standard) { std::fabs((count_3 / col_s) - 0.997) <= epsl) { if (check_for_standard) return (std::fabs(mean - 0) <= epsl && - std::fabs(std - 1.0) <= epsl); + std::fabs(stdev - 1.0) <= epsl); return (true); } return (false); @@ -7597,28 +7595,30 @@ is_lognormal(const V &column, double epsl) { StatsVisitor log_visit; svisit.pre(); - for (auto citer : column) [[likely]] { - svisit(dummy_idx, static_cast(std::log(citer))); - log_visit(dummy_idx, citer); + for (auto val : column) [[likely]] { + svisit(dummy_idx, static_cast(std::log(val))); + log_visit(dummy_idx, val); } svisit.post(); const value_type mean = static_cast(svisit.get_mean()); - const value_type std = static_cast(svisit.get_std()); - const value_type high_band_1 = static_cast(mean + std); - const value_type low_band_1 = static_cast(mean - std); + const value_type stdev = static_cast(svisit.get_std()); + const value_type high_band_1 = static_cast(mean + stdev); + const value_type low_band_1 = static_cast(mean - stdev); double count_1 = 0.0; const value_type high_band_2 = - static_cast(mean + std * 2.0); - const value_type low_band_2 = static_cast(mean - std * 2.0); + static_cast(mean + stdev * 2.0); + const value_type low_band_2 = + static_cast(mean - stdev * 2.0); double count_2 = 0.0; const value_type high_band_3 = - static_cast(mean + std * 3.0); - const value_type low_band_3 = static_cast(mean - std * 3.0); + static_cast(mean + stdev * 3.0); + const value_type low_band_3 = + static_cast(mean - stdev * 3.0); double count_3 = 0.0; - for (auto citer : column) [[likely]] { - const auto log_val = std::log(citer); + for (const auto &val : column) [[likely]] { + const auto log_val = std::log(val); if (log_val >= low_band_1 && log_val < high_band_1) { count_3 += 1; diff --git a/include/DataFrame/Utils/Matrix.h b/include/DataFrame/Utils/Matrix.h new file mode 100644 index 00000000..b10b03f2 --- /dev/null +++ b/include/DataFrame/Utils/Matrix.h @@ -0,0 +1,125 @@ +// Hossein Moein +// November 7, 2024 +/* +Copyright (c) 2019-2026, Hossein Moein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of Hossein Moein and/or the DataFrame nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL Hossein Moein BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include + +#include + +// ---------------------------------------------------------------------------- + +namespace hmdf +{ + +enum class matrix_orient : unsigned char { + + column_major = 1, + row_major = 2, +}; + +// ---------------------------------------------------------------------------- + +template +class Matrix { + +public: + + using size_type = std::size_t; + using value_type = T; + using reference = value_type &; + using const_reference = const value_type &; + using pointer = value_type *; + using const_pointer = const value_type *; + + using self_t = Matrix; + + Matrix() = default; + Matrix(size_type rows, size_type cols, const_reference def_v = T()); + Matrix(const Matrix &) = default; + Matrix(Matrix &&) = default; + ~Matrix() = default; + Matrix &operator = (const Matrix &) = default; + Matrix &operator = (Matrix &&) = default; + + void clear() noexcept; + void swap(Matrix &rhs) noexcept; + bool empty() const noexcept; + void reserve(size_type rows, size_type cols); + + size_type rows() const noexcept; + size_type columns() const noexcept; + +public: + + void resize(size_type rows, size_type cols, const_reference def_v = T()); + + reference at (size_type r, size_type c); + const_reference at (size_type r, size_type c) const; + reference operator() (size_type r, size_type c); + const_reference operator() (size_type r, size_type c) const; + + // Set the given column or row from the given iterator. + // col_data/row_Data iterators must be valid for the length of + // cols_/rows_. + // + template + void set_column(I col_data, size_type col); + + template + void set_row(I row_data, size_type row); + +private: + + using storage_t = std::vector; + + size_type rows_ { 0 }; + size_type cols_ { 0 }; + storage_t matrix_ { }; + +public: + +}; + +} // namespace hmdf + +// ---------------------------------------------------------------------------- + +#ifdef HMDF_DO_NOT_INCLUDE_TCC_FILES +# include +#endif // HMDF_DO_NOT_INCLUDE_TCC_FILES + +// ---------------------------------------------------------------------------- + +// Local Variables: +// mode:C++ +// tab-width:4 +// c-basic-offset:4 +// End: diff --git a/include/DataFrame/Utils/Matrix.tcc b/include/DataFrame/Utils/Matrix.tcc new file mode 100644 index 00000000..ce9a6df8 --- /dev/null +++ b/include/DataFrame/Utils/Matrix.tcc @@ -0,0 +1,175 @@ +// Hossein Moein +// August 9, 2023 +/* +Copyright (c) 2023-2028, Hossein Moein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of Hossein Moein and/or the DataFrame nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL Hossein Moein BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +// ---------------------------------------------------------------------------- + +namespace hmdf +{ + +template +Matrix::Matrix(size_type rows, size_type cols, const_reference def_v) + : rows_(rows), cols_(cols), matrix_(rows * cols, def_v) { } + +// ---------------------------------------------------------------------------- + +template +void +Matrix::clear() noexcept { + + rows_ = cols_ = 0; + matrix_.clear(); +} + +// ---------------------------------------------------------------------------- + +template +void +Matrix::swap(Matrix &rhs) noexcept { + + std::swap(rows_, rhs.rows_); + std::swap(cols_, rhs.cols_); + matrix_.swap(rhs.matrix_); +} + +// ---------------------------------------------------------------------------- + +template +bool +Matrix::empty() noexcept { + + return (rows_ == 0 && cols_ == 0); +} + +// ---------------------------------------------------------------------------- + +template +void +Matrix::reserve(size_type rows, size_type cols) { + + matrix_.reserve(rows * cols); +} + +// ---------------------------------------------------------------------------- + +template +Matrix::size_type +Matrix::rows() const noexcept { return (rows_); } + +// ---------------------------------------------------------------------------- + +template +Matrix::size_type +Matrix::cols() const noexcept { return (cols_); } + +// ---------------------------------------------------------------------------- + +template +void +Matrix::resize(size_type rows, size_type cols, const_reference def_v) { + + rows_ = rows; + cols_ = cols; + matrix_.resize(rows, cols, def_v); +} + +// ---------------------------------------------------------------------------- + +template +Matrix::reference +Matrix::at(size_type row, size_type col) { + + if constexpr (MO == matrix_orient::column_major) + return (matrix_[col * rows_ + row]); + else + return (matrix_[row * cols_ + col]); +} + +// ---------------------------------------------------------------------------- + +template +Matrix::const_reference +Matrix::at(size_type row, size_type col) const { + + if constexpr (MO == matrix_orient::column_major) + return (matrix_[col * rows_ + row]); + else + return (matrix_[row * cols_ + col]); +} + +// ---------------------------------------------------------------------------- + +template +Matrix::reference +Matrix::operator() (size_type row, size_type col) { + + return (at(row, col)); +} + +// ---------------------------------------------------------------------------- + +template +Matrix::const_reference +Matrix::operator() (size_type row, size_type col) const { + + return (at(row, col)); +} + +// ---------------------------------------------------------------------------- + +template +template +void +Matrix::set_column(I col_data, size_type col) { + + for (size_type r = 0; r < rows(); ++r) + at(r, col) = *col_data++; +} + +// ---------------------------------------------------------------------------- + +template +template +void +Matrix::set_row(I row_data, size_type row) { + + for (size_type c = 0; c < columns(); ++c) + at(row, c) = *row_data++; +} + +} // namespace hmdf + +// ---------------------------------------------------------------------------- + +// Local Variables: +// mode:C++ +// tab-width:4 +// c-basic-offset:4 +// End: From 02b2f5a2d79196000eab5729943ee7fb803bfa19 Mon Sep 17 00:00:00 2001 From: Hossein Moein Date: Sat, 9 Nov 2024 13:34:36 -0500 Subject: [PATCH 2/4] Basics of matrix object is done --- docs/HTML/self_contained.html | 2 +- include/DataFrame/Utils/Matrix.h | 799 ++++++++++++++++++++++++++++- include/DataFrame/Utils/Matrix.tcc | 10 +- src/CommonMakefile.mk | 15 +- test/CMakeLists.txt | 7 + test/matrix_tester.cc | 79 +++ 6 files changed, 900 insertions(+), 12 deletions(-) create mode 100644 test/matrix_tester.cc diff --git a/docs/HTML/self_contained.html b/docs/HTML/self_contained.html index 44def6e1..35c17038 100644 --- a/docs/HTML/self_contained.html +++ b/docs/HTML/self_contained.html @@ -51,7 +51,7 @@ It also has some disadvantages:
  • There might be functionalities that are hard/time-consuming to implement that are already there
  • -
  • If you find a battle-test library, the debugging is already done for you
  • +
  • If you find a battle-tested library, the debugging is already done for you
  • There might be industry-wide standards/trends that you want to follow by using a reputed library

diff --git a/include/DataFrame/Utils/Matrix.h b/include/DataFrame/Utils/Matrix.h index b10b03f2..964b3897 100644 --- a/include/DataFrame/Utils/Matrix.h +++ b/include/DataFrame/Utils/Matrix.h @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include // ---------------------------------------------------------------------------- @@ -75,20 +76,22 @@ class Matrix { void reserve(size_type rows, size_type cols); size_type rows() const noexcept; - size_type columns() const noexcept; + size_type cols() const noexcept; + + static constexpr matrix_orient orientation(); public: void resize(size_type rows, size_type cols, const_reference def_v = T()); - reference at (size_type r, size_type c); - const_reference at (size_type r, size_type c) const; + reference at(size_type r, size_type c); + const_reference at(size_type r, size_type c) const; reference operator() (size_type r, size_type c); const_reference operator() (size_type r, size_type c) const; // Set the given column or row from the given iterator. // col_data/row_Data iterators must be valid for the length of - // cols_/rows_. + // columns/rows. // template void set_column(I col_data, size_type col); @@ -104,15 +107,801 @@ class Matrix { size_type cols_ { 0 }; storage_t matrix_ { }; + // + // Iterators + // + public: + class row_iterator; + class row_const_iterator : public std::random_access_iterator_tag { + + public: + + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using size_type = long; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using difference_type = typename std::vector::difference_type; + + public: + + inline row_const_iterator () = default; + + inline row_const_iterator(const self_t *m, + size_type row = 0, + size_type col = 0) + : mptr_ (m), row_ (row), col_(col) { } + + inline bool operator == (const row_const_iterator &rhs) const { + + return (mptr_ == rhs.mptr_ && + row_ == rhs.row_ && + col_ == rhs.col_); + } + inline bool operator != (const row_const_iterator &rhs) const { + + return (! (*this == rhs)); + } + inline bool + operator > (const row_const_iterator &rhs) const noexcept { + + return (row_ > rhs.row_ || (row_ == rhs.row_ && col_ > rhs.col_)); + } + inline bool + operator >= (const row_const_iterator &rhs) const noexcept { + + return (row_ > rhs.row_ || (row_ == rhs.row_ && col_ >= rhs.col_)); + } + inline bool + operator < (const row_const_iterator &rhs) const noexcept { + + return (! (*this >= rhs)); + } + inline bool + operator <= (const row_const_iterator &rhs) const noexcept { + + return (! (*this > rhs)); + } + + // Following STL style, this iterator appears as a pointer + // to value_type. + // + inline const_pointer operator -> () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + inline const_reference operator * () const noexcept { + + return (mptr_->at(row_, col_)); + } + inline operator const_pointer () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + + // We are following STL style iterator interface. + // + inline row_const_iterator &operator ++ () noexcept { // ++Prefix + + col_ += 1; + if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + return (*this); + } + inline row_const_iterator operator ++ (int) noexcept { // Postfix++ + + const size_type row = row_; + const size_type col = col_; + + col_ += 1; + if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + return (row_const_iterator (mptr_, row, col)); + } + + inline row_const_iterator &operator += (size_type i) noexcept { + + col_ += i; + if (col_ >= mptr_->cols()) { + row_ += col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (*this); + } + + inline row_const_iterator &operator -- () noexcept { // --Prefix + + col_ -= 1; + if (col_ < 0) { col_ = mptr_->cols() - 1; row_ -= 1; } + return (*this); + } + inline row_const_iterator operator -- (int) noexcept { // Postfix-- + + const size_type row = row_; + const size_type col = col_; + + col_ -= 1; + if (col_ < 0) { col_ = mptr_->cols() - 1; row_ -= 1; } + return (row_const_iterator (mptr_, row, col)); + } + + inline row_const_iterator &operator -= (int i) noexcept { + + col_ -= i; + if (col_ < 0) { + row_ -= col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (*this); + } + + inline row_const_iterator operator + (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + col_ += i; + if (col_ >= mptr_->cols()) { + row_ += col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (row_const_iterator (mptr_, row, col)); + } + + inline row_const_iterator operator - (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + col_ -= i; + if (col_ < 0) { + row_ -= col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (row_const_iterator (mptr_, row, col)); + } + + inline row_const_iterator operator + (int i) noexcept { + + return (*this + size_type(i)); + } + + inline row_const_iterator operator - (int i) noexcept { + + return (*this - size_type(i)); + } + + friend difference_type + operator - (row_const_iterator lhs, row_const_iterator rhs) noexcept { + + const size_type row_diff = lhs.row_ - rhs.row_; + const size_type col_diff = lhs.col_ - rhs.col_; + + assert(lhs.mptr_ == rhs.mptr); + return (difference_type( + std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); + } + + private: + + const self_t *mptr_ { nullptr }; + size_type row_ { 0 }; + size_type col_ { 0 }; + }; + + // It goes through the matrix row-by-row starting at [0, 0] + // + class row_iterator : public std::random_access_iterator_tag { + + public: + + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using size_type = long; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using difference_type = typename std::vector::difference_type; + + public: + + inline row_iterator () = default; + + inline row_iterator (self_t *m, size_type row = 0, size_type col = 0) + : mptr_ (m), row_ (row), col_(col) { } + + inline bool operator == (const row_iterator &rhs) const { + + return (mptr_ == rhs.mptr_ && + row_ == rhs.row_ && + col_ == rhs.col_); + } + inline bool operator != (const row_iterator &rhs) const { + + return (! (*this == rhs)); + } + inline bool operator > (const row_iterator &rhs) const noexcept { + + return (row_ > rhs.row_ || (row_ == rhs.row_ && col_ > rhs.col_)); + } + inline bool operator >= (const row_iterator &rhs) const noexcept { + + return (row_ > rhs.row_ || (row_ == rhs.row_ && col_ >= rhs.col_)); + } + inline bool operator < (const row_iterator &rhs) const noexcept { + + return (! (*this >= rhs)); + } + inline bool operator <= (const row_iterator &rhs) const noexcept { + + return (! (*this > rhs)); + } + + // Following STL style, this iterator appears as a pointer + // to value_type. + // + inline pointer operator -> () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + inline reference operator * () const noexcept { + + return (mptr_->at(row_, col_)); + } + inline operator pointer () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + + // We are following STL style iterator interface. + // + inline row_iterator &operator ++ () noexcept { // ++Prefix + + col_ += 1; + if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + return (*this); + } + inline row_iterator operator ++ (int) noexcept { // Postfix++ + + const size_type row = row_; + const size_type col = col_; + + col_ += 1; + if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + return (row_iterator (mptr_, row, col)); + } + + inline row_iterator &operator += (size_type i) noexcept { + + col_ += i; + if (col_ >= mptr_->cols()) { + row_ += col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (*this); + } + + inline row_iterator &operator -- () noexcept { // --Prefix + + col_ -= 1; + if (col_ < 0) { col_ = mptr_->cols() - 1; row_ -= 1; } + return (*this); + } + inline row_iterator operator -- (int) noexcept { // Postfix-- + + const size_type row = row_; + const size_type col = col_; + + col_ -= 1; + if (col_ < 0) { col_ = mptr_->cols() - 1; row_ -= 1; } + return (row_iterator (mptr_, row, col)); + } + + inline row_iterator &operator -= (int i) noexcept { + + col_ -= i; + if (col_ < 0) { + row_ -= col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (*this); + } + + inline row_iterator operator + (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + col_ += i; + if (col_ >= mptr_->cols()) { + row_ += col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (row_iterator (mptr_, row, col)); + } + + inline row_iterator operator - (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + col_ -= i; + if (col_ < 0) { + row_ -= col_ / mptr_->cols(); + col_ %= mptr_->cols(); + } + return (row_iterator (mptr_, row, col)); + } + + inline row_iterator operator + (int i) noexcept { + + return (*this + size_type(i)); + } + + inline row_iterator operator - (int i) noexcept { + + return (*this - size_type(i)); + } + + friend difference_type + operator - (row_iterator lhs, row_iterator rhs) noexcept { + + const size_type row_diff = lhs.row_ - rhs.row_; + const size_type col_diff = lhs.col_ - rhs.col_; + + assert(lhs.mptr_ == rhs.mptr); + return (difference_type( + std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); + } + + private: + + self_t *mptr_ { nullptr }; + size_type row_ { 0 }; + size_type col_ { 0 }; + }; + + class col_iterator; + class col_const_iterator : public std::random_access_iterator_tag { + + public: + + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using size_type = long; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using difference_type = typename std::vector::difference_type; + + public: + + inline col_const_iterator () = default; + + inline col_const_iterator(const self_t *m, + size_type row = 0, + size_type col = 0) + : mptr_ (m), row_ (row), col_(col) { } + + inline bool operator == (const col_const_iterator &rhs) const { + + return (mptr_ == rhs.mptr_ && + row_ == rhs.row_ && + col_ == rhs.col_); + } + inline bool operator != (const col_const_iterator &rhs) const { + + return (! (*this == rhs)); + } + inline bool + operator > (const col_const_iterator &rhs) const noexcept { + + return (col_ > rhs.col_ || (col_ == rhs.col_ && row_ > rhs.row_)); + } + inline bool + operator >= (const col_const_iterator &rhs) const noexcept { + + return (col_ > rhs.col_ || (col_ == rhs.col_ && row_ >= rhs.row_)); + } + inline bool + operator < (const col_const_iterator &rhs) const noexcept { + + return (! (*this >= rhs)); + } + inline bool + operator <= (const col_const_iterator &rhs) const noexcept { + + return (! (*this > rhs)); + } + + // Following STL style, this iterator appears as a pointer + // to value_type. + // + inline const_pointer operator -> () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + inline const_reference operator * () const noexcept { + + return (mptr_->at(row_, col_)); + } + inline operator const_pointer () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + + // We are following STL style iterator interface. + // + inline col_const_iterator &operator ++ () noexcept { // ++Prefix + + row_ += 1; + if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + return (*this); + } + inline col_const_iterator operator ++ (int) noexcept { // Postfix++ + + const size_type row = row_; + const size_type col = col_; + + row_ += 1; + if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + return (col_const_iterator (mptr_, row, col)); + } + + inline col_const_iterator &operator += (size_type i) noexcept { + + row_ += i; + if (row_ >= mptr_->rows()) { + col_ += row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (*this); + } + + inline col_const_iterator &operator -- () noexcept { // --Prefix + + row_ -= 1; + if (row_ < 0) { row_ = mptr_->rows() - 1; col_ -= 1; } + return (*this); + } + inline col_const_iterator operator -- (int) noexcept { // Postfix-- + + const size_type row = row_; + const size_type col = col_; + + row_ -= 1; + if (row_ < 0) { row_ = mptr_->rows() - 1; col_ -= 1; } + return (col_const_iterator (mptr_, row, col)); + } + + inline col_const_iterator &operator -= (int i) noexcept { + + row_ -= i; + if (row_ < 0) { + col_ -= row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (*this); + } + + inline col_const_iterator operator + (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + row_ += i; + if (row_ >= mptr_->rows()) { + col_ += row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (col_const_iterator (mptr_, row, col)); + } + + inline col_const_iterator operator - (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + row_ -= i; + if (row_ < 0) { + col_ -= row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (col_const_iterator (mptr_, row, col)); + } + + inline col_const_iterator operator + (int i) noexcept { + + return (*this + size_type(i)); + } + + inline col_const_iterator operator - (int i) noexcept { + + return (*this - size_type(i)); + } + + friend difference_type + operator - (col_const_iterator lhs, col_const_iterator rhs) noexcept { + + const size_type row_diff = lhs.row_ - rhs.row_; + const size_type col_diff = lhs.col_ - rhs.col_; + + assert(lhs.mptr_ == rhs.mptr); + return (difference_type( + std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); + } + + private: + + const self_t *mptr_ { nullptr }; + size_type row_ { 0 }; + size_type col_ { 0 }; + }; + + // It goes through the matrix row-by-row starting at [0, 0] + // + class col_iterator : public std::random_access_iterator_tag { + + public: + + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using size_type = long; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using difference_type = typename std::vector::difference_type; + + public: + + inline col_iterator() = default; + + inline col_iterator (self_t *m, size_type row = 0, size_type col = 0) + : mptr_ (m), row_ (row), col_(col) { } + + inline bool operator == (const col_iterator &rhs) const { + + return (mptr_ == rhs.mptr_ && + row_ == rhs.row_ && + col_ == rhs.col_); + } + inline bool operator != (const col_iterator &rhs) const { + + return (! (*this == rhs)); + } + inline bool operator > (const col_iterator &rhs) const noexcept { + + return (col_ > rhs.col_ || (col_ == rhs.col_ && row_ > rhs.row_)); + } + inline bool operator >= (const col_iterator &rhs) const noexcept { + + return (col_ > rhs.col_ || (col_ == rhs.col_ && row_ >= rhs.row_)); + } + inline bool operator < (const col_iterator &rhs) const noexcept { + + return (! (*this >= rhs)); + } + inline bool operator <= (const col_iterator &rhs) const noexcept { + + return (! (*this > rhs)); + } + + // Following STL style, this iterator appears as a pointer + // to value_type. + // + inline pointer operator -> () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + inline reference operator * () const noexcept { + + return (mptr_->at(row_, col_)); + } + inline operator pointer () const noexcept { + + return (&(mptr_->at(row_, col_))); + } + + // We are following STL style iterator interface. + // + inline col_iterator &operator ++ () noexcept { // ++Prefix + + row_ += 1; + if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + return (*this); + } + inline col_iterator operator ++ (int) noexcept { // Postfix++ + + const size_type row = row_; + const size_type col = col_; + + row_ += 1; + if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + return (col_iterator (mptr_, row, col)); + } + + inline col_iterator &operator += (size_type i) noexcept { + + row_ += i; + if (row_ >= mptr_->rows()) { + col_ += row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (*this); + } + + inline col_iterator &operator -- () noexcept { // --Prefix + + row_ -= 1; + if (row_ < 0) { row_ = mptr_->rows() - 1; col_ -= 1; } + return (*this); + } + inline col_iterator operator -- (int) noexcept { // Postfix-- + + const size_type row = row_; + const size_type col = col_; + + row_ -= 1; + if (row_ < 0) { row_ = mptr_->rows() - 1; col_ -= 1; } + return (col_iterator (mptr_, row, col)); + } + + inline col_iterator &operator -= (int i) noexcept { + + row_ -= i; + if (row_ < 0) { + col_ -= row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (*this); + } + + inline col_iterator operator + (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + row_ += i; + if (row_ >= mptr_->rows()) { + col_ += row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (col_iterator (mptr_, row, col)); + } + + inline col_iterator operator - (size_type i) noexcept { + + const size_type row = row_; + const size_type col = col_; + + row_ -= i; + if (row_ < 0) { + col_ -= row_ / mptr_->rows(); + row_ %= mptr_->rows(); + } + return (col_iterator (mptr_, row, col)); + } + + inline col_iterator operator + (int i) noexcept { + + return (*this + size_type(i)); + } + + inline col_iterator operator - (int i) noexcept { + + return (*this - size_type(i)); + } + + friend difference_type + operator - (col_iterator lhs, col_iterator rhs) noexcept { + + const size_type row_diff = lhs.row_ - rhs.row_; + const size_type col_diff = lhs.col_ - rhs.col_; + + assert(lhs.mptr_ == rhs.mptr); + return (difference_type( + std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); + } + + private: + + self_t *mptr_ { nullptr }; + size_type row_ { 0 }; + size_type col_ { 0 }; + }; + +public: + + // Forwards + // + inline row_iterator row_begin() noexcept { + + return (row_iterator(this, 0, 0)); + } + inline row_const_iterator row_cbegin() const noexcept { + + return (row_const_iterator(this, 0, 0)); + } + inline row_iterator row_end() noexcept { + + return (row_iterator(this, rows(), cols())); + } + inline row_const_iterator row_cend() const noexcept { + + return (row_const_iterator(this, rows(), cols())); + } + + inline col_iterator col_begin() noexcept { + + return (col_iterator(this, 0, 0)); + } + inline col_const_iterator col_cbegin() const noexcept { + + return (col_const_iterator(this, 0, 0)); + } + inline col_iterator col_end() noexcept { + + return (col_iterator(this, rows(), cols())); + } + inline col_const_iterator col_cend() const noexcept { + + return (col_const_iterator(this, rows(), cols())); + } + + using reverse_row_iterator = std::reverse_iterator; + using reverse_row_const_iterator = + std::reverse_iterator; + using reverse_col_iterator = std::reverse_iterator; + using reverse_col_const_iterator = + std::reverse_iterator; + + // Reverses + // + inline reverse_row_iterator row_rbegin() noexcept { + + return (std::make_reverse_iterator(row_end())); + } + inline reverse_row_const_iterator row_crbegin() const noexcept { + + return (std::make_reverse_iterator(row_cend())); + } + inline reverse_row_iterator row_rend() noexcept { + + return (std::make_reverse_iterator(row_begin())); + } + inline reverse_row_const_iterator row_crend() const noexcept { + + return (std::make_reverse_iterator(row_cbegin())); + } + + inline reverse_col_iterator col_rbegin() noexcept { + + return (std::make_reverse_iterator(col_end())); + } + inline reverse_col_const_iterator col_crbegin() const noexcept { + + return (std::make_reverse_iterator(col_cend())); + } + inline reverse_col_iterator col_rend() noexcept { + + return (std::make_reverse_iterator(col_begin())); + } + inline reverse_col_const_iterator col_crend() const noexcept { + + return (std::make_reverse_iterator(col_cbegin())); + } }; } // namespace hmdf // ---------------------------------------------------------------------------- -#ifdef HMDF_DO_NOT_INCLUDE_TCC_FILES +#ifndef HMDF_DO_NOT_INCLUDE_TCC_FILES # include #endif // HMDF_DO_NOT_INCLUDE_TCC_FILES diff --git a/include/DataFrame/Utils/Matrix.tcc b/include/DataFrame/Utils/Matrix.tcc index ce9a6df8..f6c03e2d 100644 --- a/include/DataFrame/Utils/Matrix.tcc +++ b/include/DataFrame/Utils/Matrix.tcc @@ -63,7 +63,7 @@ Matrix::swap(Matrix &rhs) noexcept { template bool -Matrix::empty() noexcept { +Matrix::empty() const noexcept { return (rows_ == 0 && cols_ == 0); } @@ -160,10 +160,16 @@ template void Matrix::set_row(I row_data, size_type row) { - for (size_type c = 0; c < columns(); ++c) + for (size_type c = 0; c < cols(); ++c) at(row, c) = *row_data++; } +// ---------------------------------------------------------------------------- + +template +constexpr matrix_orient +Matrix::orientation() { return (MO); } + } // namespace hmdf // ---------------------------------------------------------------------------- diff --git a/src/CommonMakefile.mk b/src/CommonMakefile.mk index 73ac4bb9..4d808b2f 100644 --- a/src/CommonMakefile.mk +++ b/src/CommonMakefile.mk @@ -79,7 +79,9 @@ HEADERS = $(LOCAL_INCLUDE_DIR)/DataFrame/Vectors/HeteroVector.h \ $(LOCAL_INCLUDE_DIR)/DataFrame/Utils/FixedSizePriorityQueue.h \ $(LOCAL_INCLUDE_DIR)/DataFrame/Utils/AlignedAllocator.h \ $(LOCAL_INCLUDE_DIR)/DataFrame/Utils/FixedSizeAllocator.h \ - $(LOCAL_INCLUDE_DIR)/DataFrame/Utils/Endianness.h + $(LOCAL_INCLUDE_DIR)/DataFrame/Utils/Endianness.h \ + $(LOCAL_INCLUDE_DIR)/DataFrame/Utils/Matrix.h \ + $(LOCAL_INCLUDE_DIR)/DataFrame/Utils/Matrix.tcc LIB_NAME = DataFrame TARGET_LIB = $(LOCAL_LIB_DIR)/lib$(LIB_NAME).a @@ -101,7 +103,8 @@ TARGETS += $(TARGET_LIB) \ $(LOCAL_BIN_DIR)/vector_ptr_view_tester \ $(LOCAL_BIN_DIR)/meta_prog_tester \ $(LOCAL_BIN_DIR)/date_time_tester \ - $(LOCAL_BIN_DIR)/gen_rand_tester + $(LOCAL_BIN_DIR)/gen_rand_tester \ + $(LOCAL_BIN_DIR)/matrix_tester # ----------------------------------------------------------------------------- @@ -222,6 +225,10 @@ GEN_RAND_TESTER_OBJ = $(LOCAL_OBJ_DIR)/gen_rand_tester.o $(LOCAL_BIN_DIR)/gen_rand_tester: $(TARGET_LIB) $(GEN_RAND_TESTER_OBJ) $(CXX) -o $@ $(GEN_RAND_TESTER_OBJ) $(LIBS) +MATRIX_TESTER_OBJ = $(LOCAL_OBJ_DIR)/matrix_tester.o +$(LOCAL_BIN_DIR)/matrix_tester: $(TARGET_LIB) $(MATRIX_TESTER_OBJ) + $(CXX) -o $@ $(MATRIX_TESTER_OBJ) $(LIBS) + # ----------------------------------------------------------------------------- depend: @@ -236,7 +243,7 @@ clean: $(HELLO_WORLD_OBJ) $(DATAFRAME_PERFORMANCE_2_OBJ) \ $(DATAFRAME_THREAD_SAFTY_OBJ) $(DATAFRAME_TESTER_SCHEMA_OBJ) \ $(ALLOCATOR_TESTER_OBJ) $(LINKEDIN_BENCHMARK_OBJ) \ - $(DATAFRAME_READ_LARGE_FILE_OBJ) + $(DATAFRAME_READ_LARGE_FILE_OBJ) $(MATRIX_TESTER_OBJ) clobber: rm -f $(LIB_OBJS) $(TARGETS) $(DATAFRAME_TESTER_OBJ) $(VECTORS_TESTER_OBJ) \ @@ -248,7 +255,7 @@ clobber: $(ALLOCATOR_TESTER_OBJ) $(DATAFRAME_PERFORMANCE_OBJ) \ $(DATAFRAME_PERFORMANCE_2_OBJ) \ $(META_PROG_OBJ) $(LINKEDIN_BENCHMARK_OBJ) \ - $(DATAFRAME_READ_LARGE_FILE_OBJ) + $(DATAFRAME_READ_LARGE_FILE_OBJ) $(MATRIX_TESTER_OBJ) install_lib: cp -pf $(TARGET_LIB) $(PROJECT_LIB_DIR)/. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f312d6f6..0f6bdd0f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -52,6 +52,13 @@ if(MSVC) set_tests_properties(vectors_tester PROPERTIES DISABLED TRUE) endif() +add_executable(matrix_tester matrix_tester.cc) +target_link_libraries(matrix_tester PRIVATE DataFrame) +target_compile_options(matrix_tester + PRIVATE $<$:/bigobj> +) +add_test(NAME matrix_tester COMMAND matrix_tester) + if(NOT MSVC) # MSVC compiler craps out on immediately executing lambdas add_executable(allocator_tester allocator_tester.cc) diff --git a/test/matrix_tester.cc b/test/matrix_tester.cc new file mode 100644 index 00000000..5ed370e0 --- /dev/null +++ b/test/matrix_tester.cc @@ -0,0 +1,79 @@ +/* +Copyright (c) 2019-2026, Hossein Moein +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of Hossein Moein and/or the DataFrame nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL Hossein Moein BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include +#include + +using namespace hmdf; + +// ----------------------------------------------------------------------------- + +using row_mat_t = Matrix; +using col_mat_t = Matrix; + +static constexpr std::size_t ROWS = 5; +static constexpr std::size_t COLS = 6; + +// ----------------------------------------------------------------------------- + +int main(int, char *[]) { + + row_mat_t row_mat { ROWS, COLS }; + col_mat_t col_mat { ROWS, COLS }; + std::size_t value { 0 }; + + for (std::size_t r = 0; r < row_mat.rows(); ++r) + for (std::size_t c = 0; c < row_mat.cols(); ++c) + row_mat(r, c) = value++; + + value = 0; + for (std::size_t c = 0; c < col_mat.cols(); ++c) + for (std::size_t r = 0; r < col_mat.rows(); ++r) + col_mat(r, c) = value++; + + value = 0; + for (std::size_t r = 0; r < row_mat.rows(); ++r) + for (std::size_t c = 0; c < row_mat.cols(); ++c) + assert(row_mat(r, c) == value++); + + value = 0; + for (std::size_t c = 0; c < col_mat.cols(); ++c) + for (std::size_t r = 0; r < col_mat.rows(); ++r) + assert(col_mat(r, c) == value++); + + return (0); +} + +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode:C++ +// tab-width:4 +// c-basic-offset:4 +// End: From 2b0fd33a3d26a2158633a3208d9564d56949eba8 Mon Sep 17 00:00:00 2001 From: Hossein Moein Date: Mon, 11 Nov 2024 09:27:07 -0500 Subject: [PATCH 3/4] Stable matrix version --- include/DataFrame/Utils/Matrix.h | 100 +++++++++++++++++++++++++---- include/DataFrame/Utils/Matrix.tcc | 44 +++++++++++++ test/matrix_tester.cc | 71 +++++++++++++++++--- 3 files changed, 191 insertions(+), 24 deletions(-) diff --git a/include/DataFrame/Utils/Matrix.h b/include/DataFrame/Utils/Matrix.h index 964b3897..fff70908 100644 --- a/include/DataFrame/Utils/Matrix.h +++ b/include/DataFrame/Utils/Matrix.h @@ -53,15 +53,20 @@ class Matrix { public: - using size_type = std::size_t; + using size_type = long; using value_type = T; using reference = value_type &; using const_reference = const value_type &; using pointer = value_type *; using const_pointer = const value_type *; - using self_t = Matrix; + using trans_result_t = + typename std::conditional< + MO == matrix_orient::column_major, + Matrix, + Matrix>::type; + Matrix() = default; Matrix(size_type rows, size_type cols, const_reference def_v = T()); Matrix(const Matrix &) = default; @@ -99,6 +104,9 @@ class Matrix { template void set_row(I row_data, size_type row); + trans_result_t transpose() const noexcept; + Matrix transpose2() const noexcept; + private: using storage_t = std::vector; @@ -114,7 +122,7 @@ class Matrix { public: class row_iterator; - class row_const_iterator : public std::random_access_iterator_tag { + class row_const_iterator : public std::random_access_iterator_tag { public: @@ -127,8 +135,6 @@ class Matrix { using const_reference = const value_type &; using difference_type = typename std::vector::difference_type; - public: - inline row_const_iterator () = default; inline row_const_iterator(const self_t *m, @@ -189,6 +195,10 @@ class Matrix { col_ += 1; if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } inline row_const_iterator operator ++ (int) noexcept { // Postfix++ @@ -198,6 +208,10 @@ class Matrix { col_ += 1; if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (row_const_iterator (mptr_, row, col)); } @@ -208,6 +222,10 @@ class Matrix { row_ += col_ / mptr_->cols(); col_ %= mptr_->cols(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } @@ -247,6 +265,10 @@ class Matrix { row_ += col_ / mptr_->cols(); col_ %= mptr_->cols(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (row_const_iterator (mptr_, row, col)); } @@ -279,7 +301,7 @@ class Matrix { const size_type row_diff = lhs.row_ - rhs.row_; const size_type col_diff = lhs.col_ - rhs.col_; - assert(lhs.mptr_ == rhs.mptr); + assert(lhs.mptr_ == rhs.mptr_); return (difference_type( std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); } @@ -293,7 +315,7 @@ class Matrix { // It goes through the matrix row-by-row starting at [0, 0] // - class row_iterator : public std::random_access_iterator_tag { + class row_iterator : public std::random_access_iterator_tag { public: @@ -362,6 +384,10 @@ class Matrix { col_ += 1; if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } inline row_iterator operator ++ (int) noexcept { // Postfix++ @@ -371,6 +397,10 @@ class Matrix { col_ += 1; if (col_ >= mptr_->cols()) { col_ = 0; row_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (row_iterator (mptr_, row, col)); } @@ -381,6 +411,10 @@ class Matrix { row_ += col_ / mptr_->cols(); col_ %= mptr_->cols(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } @@ -420,6 +454,10 @@ class Matrix { row_ += col_ / mptr_->cols(); col_ %= mptr_->cols(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (row_iterator (mptr_, row, col)); } @@ -449,10 +487,11 @@ class Matrix { friend difference_type operator - (row_iterator lhs, row_iterator rhs) noexcept { + assert(lhs.mptr_ == rhs.mptr_); + const size_type row_diff = lhs.row_ - rhs.row_; const size_type col_diff = lhs.col_ - rhs.col_; - assert(lhs.mptr_ == rhs.mptr); return (difference_type( std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); } @@ -465,7 +504,7 @@ class Matrix { }; class col_iterator; - class col_const_iterator : public std::random_access_iterator_tag { + class col_const_iterator : public std::random_access_iterator_tag { public: @@ -540,6 +579,10 @@ class Matrix { row_ += 1; if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } inline col_const_iterator operator ++ (int) noexcept { // Postfix++ @@ -549,6 +592,10 @@ class Matrix { row_ += 1; if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (col_const_iterator (mptr_, row, col)); } @@ -559,6 +606,10 @@ class Matrix { col_ += row_ / mptr_->rows(); row_ %= mptr_->rows(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } @@ -598,6 +649,10 @@ class Matrix { col_ += row_ / mptr_->rows(); row_ %= mptr_->rows(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (col_const_iterator (mptr_, row, col)); } @@ -627,12 +682,13 @@ class Matrix { friend difference_type operator - (col_const_iterator lhs, col_const_iterator rhs) noexcept { + assert(lhs.mptr_ == rhs.mptr_); + const size_type row_diff = lhs.row_ - rhs.row_; const size_type col_diff = lhs.col_ - rhs.col_; - assert(lhs.mptr_ == rhs.mptr); return (difference_type( - std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); + std::abs(col_diff) * rhs.mptr_->rows() - row_diff)); } private: @@ -644,7 +700,7 @@ class Matrix { // It goes through the matrix row-by-row starting at [0, 0] // - class col_iterator : public std::random_access_iterator_tag { + class col_iterator : public std::random_access_iterator_tag { public: @@ -713,6 +769,10 @@ class Matrix { row_ += 1; if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } inline col_iterator operator ++ (int) noexcept { // Postfix++ @@ -722,6 +782,10 @@ class Matrix { row_ += 1; if (row_ >= mptr_->rows()) { row_ = 0; col_ += 1; } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (col_iterator (mptr_, row, col)); } @@ -732,6 +796,10 @@ class Matrix { col_ += row_ / mptr_->rows(); row_ %= mptr_->rows(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (*this); } @@ -771,6 +839,10 @@ class Matrix { col_ += row_ / mptr_->rows(); row_ %= mptr_->rows(); } + if (col_ >= mptr_->cols() || row_ >= mptr_->rows()) { + col_ = mptr_->cols(); + row_ = mptr_->rows(); + } return (col_iterator (mptr_, row, col)); } @@ -803,9 +875,9 @@ class Matrix { const size_type row_diff = lhs.row_ - rhs.row_; const size_type col_diff = lhs.col_ - rhs.col_; - assert(lhs.mptr_ == rhs.mptr); + assert(lhs.mptr_ == rhs.mptr_); return (difference_type( - std::abs(row_diff) * rhs.mptr_->cols() - col_diff)); + std::abs(col_diff) * rhs.mptr_->rows() - row_diff)); } private: diff --git a/include/DataFrame/Utils/Matrix.tcc b/include/DataFrame/Utils/Matrix.tcc index f6c03e2d..9f50bd98 100644 --- a/include/DataFrame/Utils/Matrix.tcc +++ b/include/DataFrame/Utils/Matrix.tcc @@ -170,6 +170,50 @@ template constexpr matrix_orient Matrix::orientation() { return (MO); } +// ---------------------------------------------------------------------------- + +template +Matrix::trans_result_t +Matrix::transpose() const noexcept { + + trans_result_t result { cols(), rows() }; + + if constexpr (MO == matrix_orient::column_major) { + for (size_type c = 0; c < cols(); ++c) + for (size_type r = 0; r < rows(); ++r) + result(c, r) = at(r, c); + } + else { + for (size_type r = 0; r < rows(); ++r) + for (size_type c = 0; c < cols(); ++c) + result(c, r) = at(r, c); + } + + return (result); +} + +// ---------------------------------------------------------------------------- + +template +Matrix +Matrix::transpose2() const noexcept { + + Matrix result { cols(), rows() }; + + if constexpr (MO == matrix_orient::column_major) { + for (size_type c = 0; c < cols(); ++c) + for (size_type r = 0; r < rows(); ++r) + result(c, r) = at(r, c); + } + else { + for (size_type r = 0; r < rows(); ++r) + for (size_type c = 0; c < cols(); ++c) + result(c, r) = at(r, c); + } + + return (result); +} + } // namespace hmdf // ---------------------------------------------------------------------------- diff --git a/test/matrix_tester.cc b/test/matrix_tester.cc index 5ed370e0..b47f735c 100644 --- a/test/matrix_tester.cc +++ b/test/matrix_tester.cc @@ -37,8 +37,8 @@ using namespace hmdf; using row_mat_t = Matrix; using col_mat_t = Matrix; -static constexpr std::size_t ROWS = 5; -static constexpr std::size_t COLS = 6; +static constexpr long ROWS = 5; +static constexpr long COLS = 6; // ----------------------------------------------------------------------------- @@ -48,25 +48,76 @@ int main(int, char *[]) { col_mat_t col_mat { ROWS, COLS }; std::size_t value { 0 }; - for (std::size_t r = 0; r < row_mat.rows(); ++r) - for (std::size_t c = 0; c < row_mat.cols(); ++c) + for (long r = 0; r < row_mat.rows(); ++r) + for (long c = 0; c < row_mat.cols(); ++c) row_mat(r, c) = value++; value = 0; - for (std::size_t c = 0; c < col_mat.cols(); ++c) - for (std::size_t r = 0; r < col_mat.rows(); ++r) + for (long c = 0; c < col_mat.cols(); ++c) + for (long r = 0; r < col_mat.rows(); ++r) col_mat(r, c) = value++; + // Print the stuff out + // + for (long r = 0; r < row_mat.rows(); ++r) { + for (long c = 0; c < row_mat.cols(); ++c) { + std::cout << row_mat(r, c) << ", "; + } + std::cout << '\n'; + } + std::cout << "\n\n"; + for (long r = 0; r < col_mat.rows(); ++r) { + for (long c = 0; c < col_mat.cols(); ++c) { + std::cout << col_mat(r, c) << ", "; + } + std::cout << '\n'; + } + value = 0; - for (std::size_t r = 0; r < row_mat.rows(); ++r) - for (std::size_t c = 0; c < row_mat.cols(); ++c) + for (long r = 0; r < row_mat.rows(); ++r) + for (long c = 0; c < row_mat.cols(); ++c) assert(row_mat(r, c) == value++); value = 0; - for (std::size_t c = 0; c < col_mat.cols(); ++c) - for (std::size_t r = 0; r < col_mat.rows(); ++r) + for (long c = 0; c < col_mat.cols(); ++c) + for (long r = 0; r < col_mat.rows(); ++r) assert(col_mat(r, c) == value++); + value = 0; + for (auto citer = row_mat.row_cbegin(); + citer != row_mat.row_cend(); ++citer) + assert(*citer == value++); + + value = 0; + for (auto citer = col_mat.col_cbegin(); + citer != col_mat.col_cend(); ++citer) + assert(*citer == value++); + + col_mat_t empty_mat { }; + + assert(empty_mat.empty()); + assert(empty_mat.rows() == 0); + assert(empty_mat.cols() == 0); + for (auto citer = empty_mat.row_cbegin(); + citer != empty_mat.row_cend(); ++citer) + assert(*citer == value++); + + auto col_iter1 = col_mat.col_begin(); + auto col_iter2 = col_mat.col_begin(); + auto row_iter1 = col_mat.row_begin(); + auto row_iter2 = col_mat.row_begin(); + + col_iter2 += 7; + row_iter2 += 7; + + assert(*col_iter1 == 0); + assert(*row_iter1 == 0); + assert(*col_iter2 == 7); + assert(*row_iter2 == 6); + + assert(((col_iter1 - col_iter2) == 7)); + assert(((row_iter1 - row_iter2) == 7)); + return (0); } From 266e347470eea35d6a14ba9e5238a5f72cc9323a Mon Sep 17 00:00:00 2001 From: Hossein Moein Date: Tue, 12 Nov 2024 16:00:43 -0500 Subject: [PATCH 4/4] Added arithmetic operators and tests --- include/DataFrame/Utils/Matrix.h | 202 ++++++++++++++++++++++++++++++- test/matrix_tester.cc | 67 ++++++++++ 2 files changed, 268 insertions(+), 1 deletion(-) diff --git a/include/DataFrame/Utils/Matrix.h b/include/DataFrame/Utils/Matrix.h index fff70908..9fe12974 100644 --- a/include/DataFrame/Utils/Matrix.h +++ b/include/DataFrame/Utils/Matrix.h @@ -48,7 +48,7 @@ enum class matrix_orient : unsigned char { // ---------------------------------------------------------------------------- -template +template class Matrix { public: @@ -969,6 +969,206 @@ class Matrix { } }; +// ---------------------------------------------------------------------------- + +template +static inline bool +operator != (const Matrix &lhs, const Matrix &rhs) { + + if (lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()) { + if constexpr (MO1 == matrix_orient::column_major) { + for (long c = 0; c < lhs.cols(); ++c) + for (long r = 0; r < lhs.rows(); ++r) + if (lhs(r, c) != rhs(r, c)) + return (true); + } + else { + for (long r = 0; r < lhs.rows(); ++r) + for (long c = 0; c < lhs.cols(); ++c) + if (lhs(r, c) != rhs(r, c)) + return (true); + } + } + else return (true); + + return (false); +} + +// ---------------------------------------------------------------------------- + +template +static inline bool +operator == (const Matrix &lhs, const Matrix &rhs) { + + return (! (lhs != rhs)); +} + +// ---------------------------------------------------------------------------- + +template +static inline Matrix +operator + (const Matrix &lhs, const Matrix &rhs) { + + assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + + auto result = lhs; + + if constexpr (MO1 == matrix_orient::column_major) { + for (long c = 0; c < lhs.cols(); ++c) + for (long r = 0; r < lhs.rows(); ++r) + result(r, c) += rhs(r, c); + } + else { + for (long r = 0; r < lhs.rows(); ++r) + for (long c = 0; c < lhs.cols(); ++c) + result(r, c) += rhs(r, c); + } + return (result); +} + +// ---------------------------------------------------------------------------- + +template +static inline Matrix +operator - (const Matrix &lhs, const Matrix &rhs) { + + assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + + auto result = lhs; + + if constexpr (MO1 == matrix_orient::column_major) { + for (long c = 0; c < lhs.cols(); ++c) + for (long r = 0; r < lhs.rows(); ++r) + result(r, c) -= rhs(r, c); + } + else { + for (long r = 0; r < lhs.rows(); ++r) + for (long c = 0; c < lhs.cols(); ++c) + result(r, c) -= rhs(r, c); + } + return (result); +} + +// ---------------------------------------------------------------------------- + +template +static inline Matrix & +operator += (Matrix &lhs, const Matrix &rhs) { + + assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + + if constexpr (MO1 == matrix_orient::column_major) { + for (long c = 0; c < lhs.cols(); ++c) + for (long r = 0; r < lhs.rows(); ++r) + lhs(r, c) += rhs(r, c); + } + else { + for (long r = 0; r < lhs.rows(); ++r) + for (long c = 0; c < lhs.cols(); ++c) + lhs(r, c) += rhs(r, c); + } + return (lhs); +} + +// ---------------------------------------------------------------------------- + +template +static inline Matrix & +operator -= (Matrix &lhs, const Matrix &rhs) { + + assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + + if constexpr (MO1 == matrix_orient::column_major) { + for (long c = 0; c < lhs.cols(); ++c) + for (long r = 0; r < lhs.rows(); ++r) + lhs(r, c) -= rhs(r, c); + } + else { + for (long r = 0; r < lhs.rows(); ++r) + for (long c = 0; c < lhs.cols(); ++c) + lhs(r, c) -= rhs(r, c); + } + return (lhs); +} + +// ---------------------------------------------------------------------------- + +// Naïve but cache friendly O(n^3) algorithm +// +template +static Matrix +operator * (const Matrix &lhs, const Matrix &rhs) { + + assert(lhs.cols() == rhs.rows()); + + const long lhs_rows { lhs.rows() }; + const long lhs_cols { lhs.cols() }; + const long rhs_cols { rhs.cols() }; + Matrix result { lhs_rows, rhs_cols }; + + constexpr long large_dim = 100; + + // Using SIMD for large matrixes + // + if (lhs_cols >= large_dim && rhs_cols >= large_dim) { + constexpr long block = 8; + + if constexpr (MO1 == matrix_orient::column_major) { + for (long c = 0; c < rhs_cols; ++c) { + for (long r = 0; r < lhs_rows; ++r) { + for (long k = 0; k < lhs_cols; k += block) { + const long min_s = std::min(block, lhs_cols); + +#pragma unroll(block) + // This loop should be optimized/unrolled by the + // compiler and use SIMD to execute in parallel + // + for (long w = 0; w < min_s; ++w) { + result(r, c) += lhs(k + w, r) * rhs(c, k + w); + } + } + } + } + } + else { // matrix_orient::row_major + for (long r = 0; r < lhs_rows; ++r) { + for (long c = 0; c < rhs_cols; ++c) { + for (long k = 0; k < lhs_cols; k += block) { + const long min_s = std::min(block, lhs_cols); + +#pragma unroll(block) + // This loop should be optimized/unrolled by the + // compiler and use SIMD to execute in parallel + // + for (long w = 0; w < min_s; ++w) { + result(r, c) += lhs(r, k + w) * rhs(k + w, c); + } + } + } + } + } + } + + // Naïve but cache friendly O(n^3) algorithm for smaller matrixes + // + else { + if constexpr (MO1 == matrix_orient::column_major) { + for (long c = 0; c < rhs_cols; ++c) + for (long r = 0; r < lhs_rows; ++r) + for (long k = 0; k < lhs_cols; ++k) + result(r, c) += lhs(k, r) * rhs(c, k); + } + else { // matrix_orient::row_major + for (long r = 0; r < lhs_rows; ++r) + for (long c = 0; c < rhs_cols; ++c) + for (long k = 0; k < lhs_cols; ++k) + result(r, c) += lhs(r, k) * rhs(k, c); + } + } + + return (result); +} + } // namespace hmdf // ---------------------------------------------------------------------------- diff --git a/test/matrix_tester.cc b/test/matrix_tester.cc index b47f735c..aca12bcc 100644 --- a/test/matrix_tester.cc +++ b/test/matrix_tester.cc @@ -59,6 +59,7 @@ int main(int, char *[]) { // Print the stuff out // + std::cout << "Row matrix\n"; for (long r = 0; r < row_mat.rows(); ++r) { for (long c = 0; c < row_mat.cols(); ++c) { std::cout << row_mat(r, c) << ", "; @@ -66,6 +67,7 @@ int main(int, char *[]) { std::cout << '\n'; } std::cout << "\n\n"; + std::cout << "Column matrix\n"; for (long r = 0; r < col_mat.rows(); ++r) { for (long c = 0; c < col_mat.cols(); ++c) { std::cout << col_mat(r, c) << ", "; @@ -118,6 +120,71 @@ int main(int, char *[]) { assert(((col_iter1 - col_iter2) == 7)); assert(((row_iter1 - row_iter2) == 7)); + const auto col_mat2 = col_mat; + + assert(col_mat != row_mat); + assert(col_mat == col_mat2); + + auto tran_mat = col_mat.transpose(); + auto tran_mat2 = col_mat.transpose2(); + + assert(tran_mat == tran_mat2); + for (long r = 0; r < tran_mat.rows(); ++r) + for (long c = 0; c < tran_mat.cols(); ++c) + assert(tran_mat(r, c) == col_mat(c, r)); + + // + // Test arithmetic functions + // + + auto sum_mat = col_mat + row_mat; + + assert(sum_mat(0, 0) == 0); + assert(sum_mat(4, 5) == 58); + assert(sum_mat(1, 1) == 13); + assert(sum_mat(3, 4) == 45); + + sum_mat += col_mat; + assert(sum_mat(0, 0) == 0); + assert(sum_mat(4, 5) == 87); + assert(sum_mat(1, 1) == 19); + assert(sum_mat(3, 4) == 68); + + row_mat_t lhs_mat { ROWS, COLS }; + col_mat_t rhs_mat { COLS, COLS }; + + value = 0; + for (long r = 0; r < lhs_mat.rows(); ++r) + for (long c = 0; c < lhs_mat.cols(); ++c) + lhs_mat(r, c) = value++; + value = 0; + for (long c = 0; c < rhs_mat.cols(); ++c) + for (long r = 0; r < rhs_mat.rows(); ++r) + rhs_mat(r, c) = value++; + + auto multi_mat = lhs_mat * rhs_mat; + + assert(multi_mat(0, 0) == 55); + assert(multi_mat(4, 5) == 5185); + assert(multi_mat(1, 1) == 451); + assert(multi_mat(3, 4) == 3277); + + col_mat_t big_lhs_mat { 100, 100 }; + col_mat_t big_rhs_mat { 100, 100 }; + + for (long c = 0; c < 100; ++c) + for (long r = 0; r < 100; ++r) { + big_lhs_mat(r, c) = c + 1; + big_rhs_mat(r, c) = c + 1; + } + + auto big_multi_mat = big_lhs_mat * big_rhs_mat; + + assert(big_multi_mat(0, 0) == 5050); + assert(big_multi_mat(99, 99) == 505000); + assert(big_multi_mat(98, 2) == 499950); + assert(big_multi_mat(2, 5) == 15150); + return (0); }