diff --git a/include/blast/math/dense/StaticMatrix.hpp b/include/blast/math/dense/StaticMatrix.hpp new file mode 100644 index 0000000..2cb4c34 --- /dev/null +++ b/include/blast/math/dense/StaticMatrix.hpp @@ -0,0 +1,201 @@ +// Copyright (c) 2019-2020 Mikhail Katliar All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace blast +{ + /// @brief Matrix with statically defined size. + /// + /// @tparam T element type of the matrix + /// @tparam M number of rows + /// @tparam N number of columns + /// @tparam SO storage order + template + class StaticMatrix + { + public: + using ElementType = T; + static bool constexpr storageOrder = SO; + + + StaticMatrix() noexcept + { + // Initialize padding elements to 0 to prevent denorms in calculations. + // Denorms can significantly impair performance, see https://github.com/giaf/blasfeo/issues/103 + std::fill_n(v_, capacity_, T {}); + } + + + StaticMatrix(T const& v) noexcept + { + std::fill_n(v_, capacity_, v); + } + + + /** + * @brief Construct from an initializer list. + * + * The initializer list is row-major regardless of the @a RegsterMatrix storage order, + * such that the initializer elements are written in a natural way. + * + * \code + * StaticMatrix m { + * {1., 2., 3.}, + * {4., 5., 6.}, + * {7., 8., 9.} + * }; + * \endcode + * + * @param list list of lists of matrix row elements + * + */ + constexpr StaticMatrix(std::initializer_list> list) + { + std::fill_n(v_, capacity_, T {}); + + if (list.size() != M || determineColumns(list) > N) + throw std::invalid_argument {"Invalid setup of static matrix"}; + + size_t i = 0; + + for (auto const& row : list) + { + size_t j = 0; + + for (const auto& element : row) + { + v_[elementIndex(i, j)] = element; + ++j; + } + + ++i; + } + } + + + StaticMatrix& operator=(T val) noexcept + { + for (size_t i = 0; i < M; ++i) + for (size_t j = 0; j < N; ++j) + (*this)(i, j) = val; + + return *this; + } + + + constexpr T const& operator()(size_t i, size_t j) const noexcept + { + return v_[elementIndex(i, j)]; + } + + + constexpr T& operator()(size_t i, size_t j) + { + return v_[elementIndex(i, j)]; + } + + + static size_t constexpr rows() noexcept + { + return M; + } + + + static size_t constexpr columns() noexcept + { + return N; + } + + + static size_t constexpr spacing() noexcept + { + return spacing_; + } + + + T * data() noexcept + { + return v_; + } + + + T const * data() const noexcept + { + return v_; + } + + + private: + static size_t constexpr spacing_ = nextMultiple(SO == columnMajor ? M : N, SimdSize_v); + static size_t constexpr capacity_ = spacing_ * (SO == columnMajor ? N : M); + + // Alignment of the data elements. + static size_t constexpr alignment_ = CACHE_LINE_SIZE; + + // Aligned element storage. + alignas(alignment_) T v_[capacity_]; + + + size_t elementIndex(size_t i, size_t j) const + { + return SO == columnMajor ? i + spacing_ * j : spacing_ * i + j; + } + }; + + + template + inline size_t constexpr rows(StaticMatrix const& m) noexcept + { + return m.rows(); + } + + + template + inline size_t constexpr columns(StaticMatrix const& m) noexcept + { + return m.columns(); + } + + + template + inline constexpr T * data(StaticMatrix& m) noexcept + { + return m.data(); + } + + + template + inline constexpr T const * data(StaticMatrix const& m) noexcept + { + return m.data(); + } + + + template + struct IsDenseMatrix> : std::true_type {}; + + + template + struct IsStatic> : std::true_type {}; + + + template + struct Spacing> : std::integral_constant::spacing()> {}; + + + template + struct StorageOrderHelper> : std::integral_constant {}; +} diff --git a/include/blast/math/reference/Ger.hpp b/include/blast/math/reference/Ger.hpp new file mode 100644 index 0000000..04774eb --- /dev/null +++ b/include/blast/math/reference/Ger.hpp @@ -0,0 +1,40 @@ +// Copyright (c) 2019-2024 Mikhail Katliar All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +#pragma once + +#include +#include +#include + + +namespace blast :: reference +{ + /** + * @brief Reference implementation of rank-1 update with multiplier + * + * a(i, j) += alpha * x(i) * y(j) + * for i=0...m-1, j=n-1 + * + * @tparam Real real number type + * @tparam VPX vector pointer type for the column vector @a x + * @tparam VPY vector pointer type for the row vector @a y + * @tparam MPA + * + * @param m number of rows in the matrix + * @param n number of columns in the matrix + * @param alpha scalar multiplier + * @param x column vector + * @param y row vector + * @param a matrix to perform update on + * + */ + template + requires VectorPointer && VectorPointer && MatrixPointer + inline void ger(size_t m, size_t n, Real alpha, VPX x, VPY y, MPA a) + { + for (size_t i = 0; i < m; ++i) + for (size_t j = 0; j < n; ++j) + *(~a)(i, j) += alpha * *(~x)(i) * *(~y)(j); + } +} diff --git a/include/blast/util/NextMultiple.hpp b/include/blast/util/NextMultiple.hpp new file mode 100644 index 0000000..8d9fa47 --- /dev/null +++ b/include/blast/util/NextMultiple.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + + +namespace blast +{ + /** + * @brief Rounds up an integral value to the next multiple of a given factor. + // + // @param value The integral value to be rounded up \f$[1..\infty)\f$. + // @param factor The factor of the multiple \f$[1..\infty)\f$. + // @return The next multiple of the given factor. + // + // This function rounds up the given integral value to the next multiple of the given integral + // factor. In case the integral value is already a multiple of the given factor, the value itself + // is returned. + */ + inline size_t constexpr nextMultiple(size_t value, size_t factor) noexcept + { + return value + (factor - value % factor) % factor; + } +} diff --git a/test/blast/math/simd/RegisterMatrixTest.cpp b/test/blast/math/simd/RegisterMatrixTest.cpp index 2ad3866..767c24b 100644 --- a/test/blast/math/simd/RegisterMatrixTest.cpp +++ b/test/blast/math/simd/RegisterMatrixTest.cpp @@ -3,11 +3,11 @@ // license that can be found in the LICENSE file. #include -#include #include #include #include -#include +#include +#include #include #include @@ -131,7 +131,7 @@ namespace blast :: testing StaticMatrix A; for (size_t i = 0; i < rows(A); ++i) for (size_t j = 0; j < columns(A); ++j) - (*A)(i, j) = 1000 * i + j; + A(i, j) = 1000 * i + j; for (size_t m = 1; m <= rows(A); ++m) { @@ -179,7 +179,7 @@ namespace blast :: testing using ET = ElementType_t; StaticMatrix A, B(0.); - randomize(A); + blast::randomize(A); RM ker; ker.load(1., ptr(A, 0, 0)); @@ -374,10 +374,12 @@ namespace blast :: testing blaze::randomize(alpha); TypeParam ker; - ker.load(1., ptr(*C)); + ker.load(1., ptr(C)); ker.ger(alpha, ptr(a), ptr(b)); - BLAST_EXPECT_APPROX_EQ(ker, evaluate(C + alpha * a * b), absTol(), relTol()); + reference::ger(rows(C), columns(C), alpha, ptr(a), ptr(b), ptr(C)); + + BLAST_EXPECT_APPROX_EQ(ker, C, absTol(), relTol()); } @@ -443,7 +445,7 @@ namespace blast :: testing for (size_t n = 0; n <= columns(C); ++n) { TypeParam ker; - ker.load(1., ptr(*C)); + ker.load(1., ptr(C)); ker.ger(ET(1.), ptr(a), ptr(trans(b)), m, n); for (size_t i = 0; i < m; ++i) @@ -471,9 +473,9 @@ namespace blast :: testing randomize(C); TypeParam ker; - ker.load(1., ptr(*C)); + ker.load(1., ptr(C)); ker.ger(ET(1.), ptr(a), ptr(trans(b))); - ker.store(ptr(*D)); + ker.store(ptr(D)); BLAST_EXPECT_EQ(D, evaluate(C + a * trans(b))); }