From 4ac76f4fc14a382a529da626b779e640c577bece Mon Sep 17 00:00:00 2001 From: Hossein Moein Date: Wed, 4 Dec 2024 09:57:37 -0500 Subject: [PATCH] Made matrix +/- operations multithreading --- include/DataFrame/DataFrameTypes.h | 16 +++ include/DataFrame/Utils/Matrix.h | 200 +++++++++++++++++++++++------ include/DataFrame/Utils/Matrix.tcc | 44 ++++--- 3 files changed, 204 insertions(+), 56 deletions(-) diff --git a/include/DataFrame/DataFrameTypes.h b/include/DataFrame/DataFrameTypes.h index 149949d8..e373695f 100644 --- a/include/DataFrame/DataFrameTypes.h +++ b/include/DataFrame/DataFrameTypes.h @@ -657,6 +657,22 @@ struct StationaryParams { // ---------------------------------------------------------------------------- +enum class stationary_test : unsigned char { + + kpss = 1, // Kwiatkowski-Phillips-Schmidt–Shin test + adf = 2, // Augmented Dickey-Fuller test +}; + +struct StationaryTestParams { + + // Only considered for ADF test + // + std::size_t adf_lag { 1 }; + bool adf_with_trend { false }; +}; + +// ---------------------------------------------------------------------------- + template struct RandGenParams { diff --git a/include/DataFrame/Utils/Matrix.h b/include/DataFrame/Utils/Matrix.h index 83c4fa9f..95fc60cc 100644 --- a/include/DataFrame/Utils/Matrix.h +++ b/include/DataFrame/Utils/Matrix.h @@ -114,7 +114,10 @@ class Matrix { using storage_t = std::vector; - size_type ppivot_(size_type pivot_row) noexcept; + inline size_type + ppivot_(size_type pivot_row, + size_type self_rows, + size_type self_cols) noexcept; size_type rows_ { 0 }; size_type cols_ { 0 }; @@ -1014,20 +1017,51 @@ template static inline Matrix operator + (const Matrix &lhs, const Matrix &rhs) { - assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + const long lhs_rows = lhs.rows(); + const long lhs_cols = lhs.cols(); - auto result = lhs; +#ifdef HMDF_SANITY_EXCEPTIONS + assert(lhs_rows == rhs.rows() && lhs_cols == rhs.cols()); +#endif // HMDF_SANITY_EXCEPTIONS - 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); + auto result = lhs; + auto col_lbd = + [lhs_rows, &result, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long c = begin; c < end; ++c) + for (long r = 0; r < lhs_rows; ++r) + result(r, c) += rhs(r, c); + }; + auto row_lbd = + [lhs_cols, &result, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long r = begin; r < end; ++r) + for (long c = 0; c < lhs_cols; ++c) + result(r, c) += rhs(r, c); + }; + const long thread_level = + (lhs_cols >= 20000L || lhs_rows >= 20000L) + ? ThreadGranularity::get_thread_level() : 0; + + if (thread_level > 2) { + std::vector> futures; + + if constexpr (MO1 == matrix_orient::column_major) + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_cols, std::move(col_lbd)); + else // matrix_orient::row_major + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_rows, std::move(row_lbd)); + + for (auto &fut : futures) fut.get(); } else { - for (long r = 0; r < lhs.rows(); ++r) - for (long c = 0; c < lhs.cols(); ++c) - result(r, c) += rhs(r, c); + if constexpr (MO1 == matrix_orient::column_major) + col_lbd(0L, lhs_cols); + else // matrix_orient::row_major + row_lbd(0L, lhs_rows); } + return (result); } @@ -1037,20 +1071,51 @@ template static inline Matrix operator - (const Matrix &lhs, const Matrix &rhs) { - assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + const long lhs_rows = lhs.rows(); + const long lhs_cols = lhs.cols(); + +#ifdef HMDF_SANITY_EXCEPTIONS + assert(lhs_rows == rhs.rows() && lhs_cols == rhs.cols()); +#endif // HMDF_SANITY_EXCEPTIONS + + auto result = lhs; + auto col_lbd = + [lhs_rows, &result, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long c = begin; c < end; ++c) + for (long r = 0; r < lhs_rows; ++r) + result(r, c) -= rhs(r, c); + }; + auto row_lbd = + [lhs_cols, &result, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long r = begin; r < end; ++r) + for (long c = 0; c < lhs_cols; ++c) + result(r, c) -= rhs(r, c); + }; + const long thread_level = + (lhs_cols >= 20000L || lhs_rows >= 20000L) + ? ThreadGranularity::get_thread_level() : 0; - auto result = lhs; + if (thread_level > 2) { + std::vector> futures; - 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); + if constexpr (MO1 == matrix_orient::column_major) + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_cols, std::move(col_lbd)); + else // matrix_orient::row_major + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_rows, std::move(row_lbd)); + + for (auto &fut : futures) fut.get(); } else { - for (long r = 0; r < lhs.rows(); ++r) - for (long c = 0; c < lhs.cols(); ++c) - result(r, c) -= rhs(r, c); + if constexpr (MO1 == matrix_orient::column_major) + col_lbd(0L, lhs_cols); + else // matrix_orient::row_major + row_lbd(0L, lhs_rows); } + return (result); } @@ -1060,18 +1125,50 @@ template static inline Matrix & operator += (Matrix &lhs, const Matrix &rhs) { - assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + const long lhs_rows = lhs.rows(); + const long lhs_cols = lhs.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); +#ifdef HMDF_SANITY_EXCEPTIONS + assert(lhs_rows == rhs.rows() && lhs_cols == rhs.cols()); +#endif // HMDF_SANITY_EXCEPTIONS + + auto col_lbd = + [lhs_rows, &lhs, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long c = begin; c < end; ++c) + for (long r = 0; r < lhs_rows; ++r) + lhs(r, c) += rhs(r, c); + }; + auto row_lbd = + [lhs_cols, &lhs, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long r = begin; r < end; ++r) + for (long c = 0; c < lhs_cols; ++c) + lhs(r, c) += rhs(r, c); + }; + const long thread_level = + (lhs_cols >= 20000L || lhs_rows >= 20000L) + ? ThreadGranularity::get_thread_level() : 0; + + if (thread_level > 2) { + std::vector> futures; + + if constexpr (MO1 == matrix_orient::column_major) + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_cols, std::move(col_lbd)); + else // matrix_orient::row_major + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_rows, std::move(row_lbd)); + + for (auto &fut : futures) fut.get(); } else { - for (long r = 0; r < lhs.rows(); ++r) - for (long c = 0; c < lhs.cols(); ++c) - lhs(r, c) += rhs(r, c); + if constexpr (MO1 == matrix_orient::column_major) + col_lbd(0L, lhs_cols); + else // matrix_orient::row_major + row_lbd(0L, lhs_rows); } + return (lhs); } @@ -1081,18 +1178,50 @@ template static inline Matrix & operator -= (Matrix &lhs, const Matrix &rhs) { - assert(lhs.rows() == rhs.rows() && lhs.cols() == rhs.cols()); + const long lhs_rows = lhs.rows(); + const long lhs_cols = lhs.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); +#ifdef HMDF_SANITY_EXCEPTIONS + assert(lhs_rows == rhs.rows() && lhs_cols == rhs.cols()); +#endif // HMDF_SANITY_EXCEPTIONS + + auto col_lbd = + [lhs_rows, &lhs, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long c = begin; c < end; ++c) + for (long r = 0; r < lhs_rows; ++r) + lhs(r, c) -= rhs(r, c); + }; + auto row_lbd = + [lhs_cols, &lhs, &rhs = std::as_const(rhs)] + (auto begin, auto end) -> void { + for (long r = begin; r < end; ++r) + for (long c = 0; c < lhs_cols; ++c) + lhs(r, c) -= rhs(r, c); + }; + const long thread_level = + (lhs_cols >= 20000L || lhs_rows >= 20000L) + ? ThreadGranularity::get_thread_level() : 0; + + if (thread_level > 2) { + std::vector> futures; + + if constexpr (MO1 == matrix_orient::column_major) + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_cols, std::move(col_lbd)); + else // matrix_orient::row_major + futures = ThreadGranularity::thr_pool_.parallel_loop( + 0L, lhs_rows, std::move(row_lbd)); + + for (auto &fut : futures) fut.get(); } else { - for (long r = 0; r < lhs.rows(); ++r) - for (long c = 0; c < lhs.cols(); ++c) - lhs(r, c) -= rhs(r, c); + if constexpr (MO1 == matrix_orient::column_major) + col_lbd(0L, lhs_cols); + else // matrix_orient::row_major + row_lbd(0L, lhs_rows); } + return (lhs); } @@ -1104,7 +1233,6 @@ template static Matrix operator * (const Matrix &lhs, const Matrix &rhs) { - const long lhs_rows { lhs.rows() }; const long lhs_cols { lhs.cols() }; const long rhs_cols { rhs.cols() }; diff --git a/include/DataFrame/Utils/Matrix.tcc b/include/DataFrame/Utils/Matrix.tcc index 8847f9b1..ebf9507c 100644 --- a/include/DataFrame/Utils/Matrix.tcc +++ b/include/DataFrame/Utils/Matrix.tcc @@ -221,13 +221,15 @@ Matrix::transpose2() const noexcept { // ---------------------------------------------------------------------------- template -Matrix::size_type -Matrix::ppivot_(size_type pivot_row) noexcept { +inline Matrix::size_type +Matrix::ppivot_(size_type pivot_row, + size_type self_rows, + size_type self_cols) noexcept { size_type max_row { pivot_row }; value_type max_value { value_type(std::fabs(at(pivot_row, pivot_row))) }; - for (size_type r = pivot_row + 1; r < rows(); ++r) { + for (size_type r = pivot_row + 1; r < self_rows; ++r) { const value_type tmp { value_type(std::fabs(at(r, pivot_row))) }; if (tmp > max_value && tmp != value_type(0)) { @@ -240,7 +242,7 @@ Matrix::ppivot_(size_type pivot_row) noexcept { return (NOPOS_); if (max_row != pivot_row) { - for (size_type c = 0; c < cols(); ++c) + for (size_type c = 0; c < self_cols; ++c) std::swap(at(pivot_row, c), at(max_row, c)); return (max_row); } @@ -257,38 +259,40 @@ Matrix::inverse() const { if (rows() != cols()) throw DataFrameError("Matrix::inverse(): Matrix must be squared"); - Matrix aux_mat = *this; - Matrix result { rows(), cols(), 0 }; + const size_type self_rows = rows(); + const size_type self_cols = cols(); + Matrix aux_mat { *this }; + Matrix result { self_rows, self_cols, 0 }; // First make identity matrix // - for (size_type d = 0; d < result.cols(); ++d) - result(d, d) = value_type(1); + for (size_type d = 0; d < self_cols; ++d) + result.at(d, d) = value_type(1); - for (size_type r = 0; r < aux_mat.rows (); ++r) { - const size_type idx = aux_mat.ppivot_(r); + for (size_type r = 0; r < self_rows; ++r) { + const size_type idx = aux_mat.ppivot_(r, self_rows, self_cols); if (idx == NOPOS_) throw DataFrameError("Matrix::inverse(): Singular matrix"); if (idx != 0) - for (size_type c = 0; c < aux_mat.cols(); ++c) - std::swap(result(r, c), result(idx, c)); + for (size_type c = 0; c < self_cols; ++c) + std::swap(result.at(r, c), result.at(idx, c)); const value_type diag = aux_mat.at(r, r); - for (size_type c = 0; c < aux_mat.cols(); ++c) { + for (size_type c = 0; c < self_cols; ++c) { aux_mat.at(r, c) /= diag; - result(r, c) /= diag; + result.at(r, c) /= diag; } - for (size_type rr = 0; rr < aux_mat.rows (); ++rr) { - if (rr != r) { - const value_type off_diag = aux_mat.at(rr, r); + for (size_type r2 = 0; r2 < self_rows; ++r2) { + if (r2 != r) { + const value_type off_diag = aux_mat.at(r2, r); - for (size_type c = 0; c < aux_mat.cols(); ++c) { - aux_mat.at(rr, c) -= off_diag * aux_mat.at(r, c); - result(rr, c) -= off_diag * result(r, c); + for (size_type c = 0; c < self_cols; ++c) { + aux_mat.at(r2, c) -= off_diag * aux_mat.at(r, c); + result.at(r2, c) -= off_diag * result.at(r, c); } } }