diff --git a/RandBLAS.hh b/RandBLAS.hh index 131d37c8..d789a753 100644 --- a/RandBLAS.hh +++ b/RandBLAS.hh @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #endif diff --git a/RandBLAS/skge.hh b/RandBLAS/skge.hh index 75084517..5ec0b8d4 100644 --- a/RandBLAS/skge.hh +++ b/RandBLAS/skge.hh @@ -64,6 +64,8 @@ using namespace RandBLAS::sparse; */ +// MARK: SUBMAT(S), LEFT + // ============================================================================= /// \fn sketch_general(blas::Layout layout, blas::Op opS, blas::Op opA, int64_t d, /// int64_t n, int64_t m, T alpha, SKOP &S, int64_t ro_s, int64_t co_s, @@ -249,6 +251,8 @@ inline void sketch_general( ); } +// MARK: SUBMAT(S), RIGHT + // ============================================================================= /// \fn sketch_general(blas::Layout layout, blas::Op opA, blas::Op opS, int64_t m, int64_t d, int64_t n, /// T alpha, const T *A, int64_t lda, SKOP &S, @@ -419,6 +423,9 @@ inline void sketch_general( ); } + +// MARK: FULL(S), LEFT + // ============================================================================= /// \fn sketch_general(blas::Layout layout, blas::Op opS, blas::Op opA, int64_t d, /// int64_t n, int64_t m, T alpha, SKOP &S, const T *A, int64_t lda, T beta, T *B, int64_t ldb @@ -431,15 +438,6 @@ inline void sketch_general( /// /// where :math:`\alpha` and :math:`\beta` are real scalars, :math:`\op(X)` either returns a matrix :math:`X` /// or its transpose, and :math:`S` is a sketching operator. -/// -/// .. dropdown:: FAQ -/// :animate: fade-in-slide-down -/// -/// **What are** :math:`\mat(A)` **and** :math:`\mat(B)` **?** -/// -/// Their shapes are defined implicitly by :math:`(d, m, n, \opA).` -/// Their precise contents are determined by :math:`(A, \lda),` :math:`(B, \ldb),` -/// and "layout", following the same convention as the Level 3 BLAS function "GEMM." /// /// .. dropdown:: Full parameter descriptions /// :animate: fade-in-slide-down @@ -530,6 +528,8 @@ inline void sketch_general( return sketch_general(layout, opS, opA, d, n, m, alpha, S, 0, 0, A, lda, beta, B, ldb); }; +// MARK: FULL(S), RIGHT + // ============================================================================= /// \fn sketch_general(blas::Layout layout, blas::Op opA, blas::Op opS, int64_t m, int64_t d, int64_t n, /// T alpha, const T *A, int64_t lda, SKOP &S, T beta, T *B, int64_t ldb @@ -542,15 +542,6 @@ inline void sketch_general( /// /// where :math:`\alpha` and :math:`\beta` are real scalars, :math:`\op(X)` either returns a matrix :math:`X` /// or its transpose, and :math:`S` is a sketching operator. -/// -/// .. dropdown:: FAQ -/// :animate: fade-in-slide-down -/// -/// **What are** :math:`\mat(A)` **and** :math:`\mat(B)` **?** -/// -/// Their shapes are defined implicitly by :math:`(m, d, n, \opA).` -/// Their precise contents are determined by :math:`(A, \lda),` :math:`(B, \ldb),` -/// and "layout", following the same convention as the Level 3 BLAS function "GEMM." /// /// .. dropdown:: Full parameter descriptions /// :animate: fade-in-slide-down @@ -640,198 +631,5 @@ inline void sketch_general( return sketch_general(layout, opA, opS, m, d, n, alpha, A, lda, S, 0, 0, beta, B, ldb); }; -// ============================================================================= -/// \fn sketch_vector(blas::Op opS, int64_t d, int64_t m, T alpha, SKOP &S, -/// int64_t ro_s, int64_t co_s, const T *x, int64_t incx, T beta, T *y, int64_t incy -/// ) -/// @verbatim embed:rst:leading-slashes -/// Perform a GEMV-like operation. If :math:`{\opS} = \texttt{NoTrans},` then we perform -/// -/// .. math:: -/// \mat(y) = \alpha \cdot \underbrace{\submat(S)}_{d \times m} \cdot \underbrace{\mat(x)}_{m \times 1} + \beta \cdot \underbrace{\mat(y)}_{d \times 1}, \tag{$\star$} -/// -/// otherwise, we perform -/// -/// .. math:: -/// \mat(y) = \alpha \cdot \underbrace{\submat(S)^T}_{m \times d} \cdot \underbrace{\mat(x)}_{d \times 1} + \beta \cdot \underbrace{\mat(y)}_{m \times 1}, \tag{$\diamond$} -/// -/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is a sketching operator. -/// -/// .. dropdown:: FAQ -/// :animate: fade-in-slide-down -/// -/// **What are** :math:`\mat(x)` **and** :math:`\mat(y)` **?** -/// -/// Their shapes are defined as tall vectors of dimension :math:`(\mat(x), L_x \times 1),` :math:`(\mat(y), L_y \times 1),` -/// where :math:`(L_x, L_y)` are lengths so that :math:`\opS(\submat(S)) \mat(x)` is well-defined and the same shape as :math:`\mat(y).` -/// Their precise contents are determined in a way that is identical to the Level 2 BLAS function "GEMV." -/// -/// **Why no "layout" argument?** -/// -/// The GEMV in CBLAS accepts a parameter that specifies row-major or column-major layout of the matrix. -/// Since our matrix is a sketching operator, and since RandBLAS has no notion of the layout of a sketching operator, we do not have a layout parameter. -/// -/// .. dropdown:: Full parameter descriptions -/// :animate: fade-in-slide-down -/// -/// opS - [in] -/// * Either Op::Trans or Op::NoTrans. -/// * If :math:`\opS` = NoTrans, then :math:`\op(\submat(S)) = \submat(S).` -/// * If :math:`\opS` = Trans, then :math:`\op(\submat(S)) = \submat(S)^T.` -/// -/// d - [in] -/// * A nonnegative integer. -/// * The number of rows in :math:`\submat(S).` -/// -/// m - [in] -/// * A nonnegative integer. -/// * The number of columns in :math:`\submat(S).` -/// -/// alpha - [in] -/// * A real scalar. -/// * If zero, then :math:`x` is not accessed. -/// -/// S - [in] -/// * A DenseSkOp or SparseSkOp object. -/// * Defines :math:`\submat(S).` -/// -/// ro_s - [in] -/// * A nonnegative integer. -/// * :math:`\submat(S)` is a contiguous submatrix of :math:`S[\texttt{ro_s}:(\texttt{ro_s} + d), :].` -/// -/// co_s - [in] -/// * A nonnegative integer. -/// * :math:`\submat(S)` is a contiguous submatrix of :math:`S[:,\texttt{co_s}:(\texttt{co_s} + m)].` -/// -/// x - [in] -/// * Pointer to a 1D array of real scalars. -/// * Defines :math:`\mat(x).` -/// -/// incx - [in] -/// * A positive integer. -/// * Stride between elements of x. -/// -/// beta - [in] -/// * A real scalar. -/// * If zero, then :math:`y` need not be set on input. -/// -/// y - [in, out] -/// * Pointer to 1D array of real scalars. -/// * On entry, defines :math:`\mat(y)` on the RIGHT-hand side of -/// :math:`(\star)` (if :math:`\opS = \texttt{NoTrans}`) or -/// :math:`(\diamond)` (if :math:`\opS = \texttt{Trans}`) -/// * On exit, defines :math:`\mat(y)` on the LEFT-hand side of the same. -/// -/// incy - [in] -/// * A positive integer. -/// * Stride between elements of y. -/// -/// @endverbatim -template -inline void sketch_vector( - blas::Op opS, - int64_t d, // rows in submat(S) - int64_t m, // cols in submat(S) - T alpha, - SKOP &S, - int64_t ro_s, - int64_t co_s, - const T *x, - int64_t incx, - T beta, - T *y, - int64_t incy -) { - int64_t _d, _m; - if (opS == blas::Op::Trans) { - _d = m; - _m = d; - } else { - _d = d; - _m = m; - } - return sketch_general(blas::Layout::RowMajor, opS, blas::Op::NoTrans, _d, 1, _m, alpha, S, ro_s, co_s, x, incx, beta, y, incy); -} - -// ============================================================================= -/// \fn sketch_vector(blas::Op opS, T alpha, SKOP &S, -/// const T *x, int64_t incx, T beta, T *y, int64_t incy -/// ) -/// @verbatim embed:rst:leading-slashes -/// Perform a GEMV-like operation: -/// -/// .. math:: -/// \mat(y) = \alpha \cdot \op(S) \cdot \mat(x) + \beta \cdot \mat(y), \tag{$\star$} -/// -/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is a sketching operator. -/// -/// .. dropdown:: FAQ -/// :animate: fade-in-slide-down -/// -/// **What are** :math:`\mat(x)` **and** :math:`\mat(y)` **?** -/// -/// Their shapes are defined as tall vectors of dimension :math:`(\mat(x), L_x \times 1),` :math:`(\mat(y), L_y \times 1),` -/// where :math:`(L_x, L_y)` are lengths so that :math:`\opS(S) \mat(x)` is well-defined and the same shape as :math:`\mat(y).` -/// Their precise contents are determined in a way that is identical to the Level 2 BLAS function "GEMV." -/// -/// **Why no "layout" argument?** -/// -/// The GEMV in CBLAS accepts a parameter that specifies row-major or column-major layout of the matrix. -/// Since our matrix is a sketching operator, and since RandBLAS has no notion of the layout of a sketching operator, we do not have a layout parameter. -/// -/// .. dropdown:: Full parameter descriptions -/// :animate: fade-in-slide-down -/// -/// opS - [in] -/// * Either Op::Trans or Op::NoTrans. -/// * If :math:`\opS` = NoTrans, then :math:`\op(S) = S.` -/// * If :math:`\opS` = Trans, then :math:`\op(S) = S^T.` -/// -/// alpha - [in] -/// * A real scalar. -/// * If zero, then :math:`x` is not accessed. -/// -/// S - [in] -/// * A DenseSkOp or SparseSkOp object. -/// -/// x - [in] -/// * Pointer to a 1D array of real scalars. -/// * Defines :math:`\mat(x).` -/// -/// incx - [in] -/// * A positive integer. -/// * Stride between elements of x. -/// -/// beta - [in] -/// * A real scalar. -/// * If zero, then :math:`y` need not be set on input. -/// -/// y - [in, out] -/// * Pointer to 1D array of real scalars. -/// * On entry, defines :math:`\mat(y)` on the RIGHT-hand side of -/// :math:`(\star).` -/// * On exit, defines :math:`\mat(y)` on the LEFT-hand side of the same. -/// -/// incy - [in] -/// * A positive integer. -/// * Stride between elements of y. -/// -/// @endverbatim -template -inline void sketch_vector( - blas::Op opS, - T alpha, - SKOP &S, - const T *x, - int64_t incx, - T beta, - T *y, - int64_t incy -) { - int64_t d = S.dist.n_rows; - int64_t m = S.dist.n_cols; - return sketch_vector(opS, d, m, alpha, S, 0, 0, x, incx, beta, y, incy); -} - } // end namespace RandBLAS #endif diff --git a/RandBLAS/sksy.hh b/RandBLAS/sksy.hh new file mode 100644 index 00000000..19aa232d --- /dev/null +++ b/RandBLAS/sksy.hh @@ -0,0 +1,541 @@ +// Copyright, 2024. See LICENSE for copyright holder information. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) 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. +// +// (3) Neither the name of the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. +// + +#ifndef randblas_sksy_hh +#define randblas_sksy_hh + +#include "RandBLAS/util.hh" +#include "RandBLAS/base.hh" +#include "RandBLAS/skge.hh" + +namespace RandBLAS { + +using namespace RandBLAS::dense; +using namespace RandBLAS::sparse; + +/* Intended macro definitions. + + .. |mat| mathmacro:: \operatorname{mat} + .. |submat| mathmacro:: \operatorname{submat} + .. |lda| mathmacro:: \texttt{lda} + .. |ldb| mathmacro:: \texttt{ldb} +*/ + +// MARK: SUBMAT(S) + +// ============================================================================= +/// \fn sketch_symmetric(blas::Layout layout, int64_t n, +/// int64_t d, T alpha, const T *A, int64_t lda, +/// SKOP &S, int64_t ro_s, int64_t co_s, +/// T beta, T *B, int64_t ldb, T sym_check_tol = 0 +/// ) +/// @verbatim embed:rst:leading-slashes +/// Check that :math:`\mat(A)` is symmetric up to tolerance :math:`\texttt{sym_check_tol}`, then sketch from the right in a SYMM-like operation +/// +/// .. math:: +/// \mat(B) = \alpha \cdot \underbrace{\mat(A)}_{n \times n} \cdot \underbrace{\submat(S)}_{n \times d} + \beta \cdot \underbrace{\mat(B)}_{n \times d}, \tag{$\star$} +/// +/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is a sketching operator. +/// +/// .. dropdown:: FAQ +/// :animate: fade-in-slide-down +/// +/// **What's** :math:`\mat(A)?` +/// +/// It's a symmetric matrix of order :math:`n`. Its precise contents depend on :math:`(A, \lda)`, +/// according to +/// +/// .. math:: +/// \mat(A)[i, j] = A[i + j \cdot \lda] = A[i \cdot \lda + j]. +/// +/// Note that the the "layout" parameter passed to this function is not used here. +/// That's because this function requires :math:`\mat(A)` to be stored in the format +/// of a general matrix (with both upper and lower triangles). +/// +/// This function's default behavior is to check that :math:`\mat(A)` is symmetric before +/// attempting sketching. That check can be skipped (at your own peril!) by calling this +/// function with sym_check_tol < 0. +/// +/// **What's** :math:`\mat(B)?` +/// +/// It's an :math:`n \times d` matrix. Its precise contents depend on :math:`(B,\ldb)` and "layout." +/// +/// If layout == ColMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i + j \cdot \ldb]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq n.` +/// +/// If layout == RowMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i \cdot \ldb + j]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq d.` +/// +/// **What is** :math:`\submat(S)` **?** +/// +/// It's the :math:`n \times d` submatrix of :math:`{S}` whose upper-left corner appears +/// at index :math:`(\texttt{ro_s}, \texttt{co_s})` of :math:`{S}.` +/// +/// .. dropdown:: Full parameter descriptions +/// :animate: fade-in-slide-down +/// +/// layout - [in] +/// * Either Layout::ColMajor or Layout::RowMajor +/// * Matrix storage for :math:`\mat(B).` +/// +/// n - [in] +/// * A nonnegative integer. +/// * The number of rows in :math:`\mat(B).` +/// * The number of rows and columns in :math:`\mat(A).` +/// +/// d - [in] +/// * A nonnegative integer. +/// * The number of columns in :math:`\mat(B)` and :math:`\submat(S).` +/// +/// alpha - [in] +/// * A real scalar. +/// * If zero, then :math:`A` is not accessed. +/// +/// A - [in] +/// * Pointer to a 1D array of real scalars. +/// * Defines :math:`\mat(A).` +/// +/// lda - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(A)` when reading from :math:`A.` +/// +/// S - [in] +/// * A DenseSkOp or SparseSkOp object. +/// * Defines :math:`\submat(S).` +/// +/// ro_s - [in] +/// * A nonnegative integer. +/// * The rows of :math:`\submat(S)` are a contiguous subset of rows of :math:`S.` +/// * The rows of :math:`\submat(S)` start at :math:`S[\texttt{ro_s}, :].` +/// +/// co_s - [in] +/// * A nonnnegative integer. +/// * The columns of :math:`\submat(S)` are a contiguous subset of columns of :math:`S.` +/// * The columns of :math:`\submat(S)` start at :math:`S[:,\texttt{co_s}].` +/// +/// beta - [in] +/// * A real scalar. +/// * If zero, then :math:`B` need not be set on input. +/// +/// B - [in,out] +/// * Pointer to 1D array of real scalars. +/// * On entry, defines :math:`\mat(B)` +/// on the RIGHT-hand side of :math:`(\star).` +/// * On exit, defines :math:`\mat(B)` +/// on the LEFT-hand side of :math:`(\star).` +/// +/// ldb - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(B)` when reading from :math:`B.` +/// +/// @endverbatim +template +inline void sketch_symmetric( + // B = alpha*A*S + beta*B, where A is a symmetric matrix stored in the format of a general matrix. + blas::Layout layout, + int64_t n, // number of rows in B + int64_t d, // number of columns in B + T alpha, + const T* A, + int64_t lda, + SKOP &S, + int64_t ro_s, + int64_t co_s, + T beta, + T* B, + int64_t ldb, + T sym_check_tol = 0 +) { + RandBLAS::util::require_symmetric(layout, A, n, lda, sym_check_tol); + sketch_general(layout, blas::Op::NoTrans, blas::Op::NoTrans, n, d, n, alpha, A, lda, S, ro_s, co_s, beta, B, ldb); +} + + +// ============================================================================= +/// \fn sketch_symmetric(blas::Layout layout, int64_t d, +/// int64_t n, T alpha, SKOP &S, int64_t ro_s, int64_t co_s, +/// const T *A, int64_t lda, T beta, T *B, int64_t ldb, T sym_check_tol = 0 +/// ) +/// @verbatim embed:rst:leading-slashes +/// Check that :math:`\mat(A)` is symmetric up to tolerance :math:`\texttt{sym_check_tol}`, then sketch from the left in a SYMM-like operation +/// +/// .. math:: +/// \mat(B) = \alpha \cdot \underbrace{\submat(S)}_{d \times n} \cdot \underbrace{\mat(A)}_{n \times n} + \beta \cdot \underbrace{\mat(B)}_{d \times n}, \tag{$\star$} +/// +/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is a sketching operator. +/// +/// .. dropdown:: FAQ +/// :animate: fade-in-slide-down +/// +/// **What's** :math:`\mat(A)?` +/// +/// It's a symmetric matrix of order :math:`n`. Its precise contents depend on :math:`(A, \lda)`, +/// according to +/// +/// .. math:: +/// \mat(A)[i, j] = A[i + j \cdot \lda] = A[i \cdot \lda + j]. +/// +/// Note that the the "layout" parameter passed to this function is not used here. +/// That's because this function requires :math:`\mat(A)` to be stored in the format +/// of a general matrix (with both upper and lower triangles). +/// +/// This function's default behavior is to check that :math:`\mat(A)` is symmetric before +/// attempting sketching. That check can be skipped (at your own peril!) by calling this +/// function with sym_check_tol < 0. +/// +/// **What's** :math:`\mat(B)?` +/// +/// It's a :math:`d \times n` matrix. Its precise contents depend on :math:`(B,\ldb)` and "layout." +/// +/// If layout == ColMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i + j \cdot \ldb]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq d.` +/// +/// If layout == RowMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i \cdot \ldb + j]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq n.` +/// +/// **What is** :math:`\submat(S)` **?** +/// +/// It's the :math:`d \times n` submatrix of :math:`{S}` whose upper-left corner appears +/// at index :math:`(\texttt{ro_s}, \texttt{co_s})` of :math:`{S}.` +/// +/// .. dropdown:: Full parameter descriptions +/// :animate: fade-in-slide-down +/// +/// layout - [in] +/// * Either Layout::ColMajor or Layout::RowMajor +/// * Matrix storage for :math:`\mat(B).` +/// +/// d - [in] +/// * A nonnegative integer. +/// * The number of rows in :math:`\mat(B)` and :math:`\submat(S).` +/// +/// n - [in] +/// * A nonnegative integer. +/// * The number of columns in :math:`\mat(B).` +/// * The number of rows and columns in :math:`\mat(A).` +/// +/// alpha - [in] +/// * A real scalar. +/// * If zero, then :math:`A` is not accessed. +/// +/// S - [in] +/// * A DenseSkOp or SparseSkOp object. +/// * Defines :math:`\submat(S).` +/// +/// ro_s - [in] +/// * A nonnegative integer. +/// * The rows of :math:`\submat(S)` are a contiguous subset of rows of :math:`S.` +/// * The rows of :math:`\submat(S)` start at :math:`S[\texttt{ro_s}, :].` +/// +/// co_s - [in] +/// * A nonnnegative integer. +/// * The columns of :math:`\submat(S)` are a contiguous subset of columns of :math:`S.` +/// * The columns of :math:`\submat(S)` start at :math:`S[:,\texttt{co_s}].` +/// +/// A - [in] +/// * Pointer to a 1D array of real scalars. +/// * Defines :math:`\mat(A).` +/// +/// lda - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(A)` when reading from :math:`A.` +/// +/// beta - [in] +/// * A real scalar. +/// * If zero, then :math:`B` need not be set on input. +/// +/// B - [in,out] +/// * Pointer to 1D array of real scalars. +/// * On entry, defines :math:`\mat(B)` +/// on the RIGHT-hand side of :math:`(\star).` +/// * On exit, defines :math:`\mat(B)` +/// on the LEFT-hand side of :math:`(\star).` +/// +/// ldb - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(B)` when reading from :math:`B.` +/// +/// @endverbatim +template +inline void sketch_symmetric( + // B = alpha*S*A + beta*B + blas::Layout layout, + int64_t d, // number of rows in B + int64_t n, // number of columns in B + T alpha, + SKOP &S, + int64_t ro_s, + int64_t co_s, + const T* A, + int64_t lda, + T beta, + T* B, + int64_t ldb, + T sym_check_tol = 0 +) { + RandBLAS::util::require_symmetric(layout, A, n, lda, sym_check_tol); + sketch_general(layout, blas::Op::NoTrans, blas::Op::NoTrans, d, n, n, alpha, S, ro_s, co_s, A, lda, beta, B, ldb); +} + +// MARK: FULL(S) + +// ============================================================================= +/// \fn sketch_symmetric(blas::Layout layout, T alpha, +/// const T *A, int64_t lda, SKOP &S, +/// T beta, T *B, int64_t ldb, T sym_check_tol = 0 +/// ) +/// @verbatim embed:rst:leading-slashes +/// Check that :math:`\mat(A)` is symmetric up to tolerance :math:`\texttt{sym_check_tol}`, then sketch from the right in a SYMM-like operation +/// +/// .. math:: +/// \mat(B) = \alpha \cdot \underbrace{\mat(A)}_{n \times n} \cdot S + \beta \cdot \underbrace{\mat(B)}_{n \times d}, \tag{$\star$} +/// +/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is an :math:`n \times d` sketching operator. +/// +/// .. dropdown:: FAQ +/// :animate: fade-in-slide-down +/// +/// **What's** :math:`\mat(A)?` +/// +/// It's a symmetric matrix of order :math:`n`, where :math:`n = \texttt{S.dist.n_cols}`. +/// Its precise contents depend on :math:`(A, \lda)`, according to +/// +/// .. math:: +/// \mat(A)[i, j] = A[i + j \cdot \lda] = A[i \cdot \lda + j]. +/// +/// Note that the the "layout" parameter passed to this function is not used here. +/// That's because this function requires :math:`\mat(A)` to be stored in the format +/// of a general matrix (with both upper and lower triangles). +/// +/// This function's default behavior is to check that :math:`\mat(A)` is symmetric before +/// attempting sketching. That check can be skipped (at your own peril!) by calling this +/// function with sym_check_tol < 0. +/// +/// **What's** :math:`\mat(B)?` +/// +/// It's an :math:`n \times d` matrix, where :math:`n = \texttt{S.dist.n_cols}` +/// and :math:`d = \texttt{S.dist.n_rows}`. +/// Its precise contents depend on :math:`(B,\ldb)` and "layout." +/// +/// If layout == ColMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i + j \cdot \ldb]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq n.` +/// +/// If layout == RowMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i \cdot \ldb + j]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq d.` +/// +/// .. dropdown:: Full parameter descriptions +/// :animate: fade-in-slide-down +/// +/// layout - [in] +/// * Either Layout::ColMajor or Layout::RowMajor +/// * Matrix storage for :math:`\mat(B).` +/// +/// alpha - [in] +/// * A real scalar. +/// * If zero, then :math:`A` is not accessed. +/// +/// A - [in] +/// * Pointer to a 1D array of real scalars. +/// * Defines :math:`\mat(A).` +/// +/// lda - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(A)` when reading from :math:`A.` +/// +/// S - [in] +/// * A DenseSkOp or SparseSkOp object. +/// +/// beta - [in] +/// * A real scalar. +/// * If zero, then :math:`B` need not be set on input. +/// +/// B - [in,out] +/// * Pointer to 1D array of real scalars. +/// * On entry, defines :math:`\mat(B)` +/// on the RIGHT-hand side of :math:`(\star).` +/// * On exit, defines :math:`\mat(B)` +/// on the LEFT-hand side of :math:`(\star).` +/// +/// ldb - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(B)` when reading from :math:`B.` +/// +/// @endverbatim +template +inline void sketch_symmetric( + // B = alpha*A*S + beta*B, where A is a symmetric matrix stored in the format of a general matrix. + blas::Layout layout, + T alpha, + const T* A, + int64_t lda, + SKOP &S, + T beta, + T* B, + int64_t ldb, + T sym_check_tol = 0 +) { + int64_t n = S.dist.n_rows; + int64_t d = S.dist.n_cols; + RandBLAS::util::require_symmetric(layout, A, n, lda, sym_check_tol); + sketch_general(layout, blas::Op::NoTrans, blas::Op::NoTrans, n, d, n, alpha, A, lda, S, 0, 0, beta, B, ldb); +} + + +// ============================================================================= +/// \fn sketch_symmetric(blas::Layout layout, T alpha, SKOP &S, +/// const T *A, int64_t lda, T beta, T *B, int64_t ldb, T sym_check_tol = 0 +/// ) +/// @verbatim embed:rst:leading-slashes +/// Check that :math:`\mat(A)` is symmetric up to tolerance :math:`\texttt{sym_check_tol}`, then sketch from the left in a SYMM-like operation +/// +/// .. math:: +/// \mat(B) = \alpha \cdot S \cdot \underbrace{\mat(A)}_{n \times n} + \beta \cdot \underbrace{\mat(B)}_{d \times n}, \tag{$\star$} +/// +/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is a :math:`d \times n` sketching operator. +/// +/// .. dropdown:: FAQ +/// :animate: fade-in-slide-down +/// +/// **What's** :math:`\mat(A)?` +/// +/// It's a symmetric matrix of order :math:`n`. Its precise contents depend on :math:`(A, \lda)`, +/// according to +/// +/// .. math:: +/// \mat(A)[i, j] = A[i + j \cdot \lda] = A[i \cdot \lda + j]. +/// +/// Note that the the "layout" parameter passed to this function is not used here. +/// That's because this function requires :math:`\mat(A)` to be stored in the format +/// of a general matrix (with both upper and lower triangles). +/// +/// This function's default behavior is to check that :math:`\mat(A)` is symmetric before +/// attempting sketching. That check can be skipped (at your own peril!) by calling this +/// function with sym_check_tol < 0. +/// +/// **What's** :math:`\mat(B)?` +/// +/// It's a :math:`d \times n` matrix. Its precise contents depend on :math:`(B,\ldb)` and "layout." +/// +/// If layout == ColMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i + j \cdot \ldb]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq d.` +/// +/// If layout == RowMajor, then +/// +/// .. math:: +/// \mat(B)[i, j] = B[i \cdot \ldb + j]. +/// +/// In this case, :math:`\ldb` must be :math:`\geq n.` +/// +/// .. dropdown:: Full parameter descriptions +/// :animate: fade-in-slide-down +/// +/// layout - [in] +/// * Either Layout::ColMajor or Layout::RowMajor +/// * Matrix storage for :math:`\mat(B).` +/// +/// alpha - [in] +/// * A real scalar. +/// * If zero, then :math:`A` is not accessed. +/// +/// S - [in] +/// * A DenseSkOp or SparseSkOp object. +/// +/// A - [in] +/// * Pointer to a 1D array of real scalars. +/// * Defines :math:`\mat(A).` +/// +/// lda - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(A)` when reading from :math:`A.` +/// +/// beta - [in] +/// * A real scalar. +/// * If zero, then :math:`B` need not be set on input. +/// +/// B - [in,out] +/// * Pointer to 1D array of real scalars. +/// * On entry, defines :math:`\mat(B)` +/// on the RIGHT-hand side of :math:`(\star).` +/// * On exit, defines :math:`\mat(B)` +/// on the LEFT-hand side of :math:`(\star).` +/// +/// ldb - [in] +/// * A nonnegative integer. +/// * Leading dimension of :math:`\mat(B)` when reading from :math:`B.` +/// +/// @endverbatim +template +inline void sketch_symmetric( + // B = alpha*S*A + beta*B + blas::Layout layout, + T alpha, + SKOP &S, + const T* A, + int64_t lda, + T beta, + T* B, + int64_t ldb, + T sym_check_tol = 0 +) { + int64_t d = S.dist.n_rows; + int64_t n = S.dist.n_cols; + RandBLAS::util::require_symmetric(layout, A, n, lda, sym_check_tol); + sketch_general(layout, blas::Op::NoTrans, blas::Op::NoTrans, d, n, n, alpha, S, 0, 0, A, lda, beta, B, ldb); +} + +} // end namespace RandBLAS +#endif diff --git a/RandBLAS/skve.hh b/RandBLAS/skve.hh new file mode 100644 index 00000000..da473502 --- /dev/null +++ b/RandBLAS/skve.hh @@ -0,0 +1,263 @@ +// Copyright, 2024. See LICENSE for copyright holder information. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) 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. +// +// (3) Neither the name of the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. +// + +#ifndef randblas_skve_hh +#define randblas_skve_hh + +#include "RandBLAS/base.hh" +#include "RandBLAS/exceptions.hh" +#include "RandBLAS/random_gen.hh" +#include "RandBLAS/skge.hh" + +#include +#include +#include +#include + +#include +#include + + +namespace RandBLAS { + +using namespace RandBLAS::dense; +using namespace RandBLAS::sparse; + + +/* Intended macro definitions. + + .. |op| mathmacro:: \operatorname{op} + .. |mat| mathmacro:: \operatorname{mat} + .. |submat| mathmacro:: \operatorname{submat} + .. |lda| mathmacro:: \texttt{lda} + .. |ldb| mathmacro:: \texttt{ldb} + .. |opA| mathmacro:: \texttt{opA} + .. |opS| mathmacro:: \texttt{opS} +*/ + + +// MARK: SUBMAT(S) + +// ============================================================================= +/// \fn sketch_vector(blas::Op opS, int64_t d, int64_t m, T alpha, SKOP &S, +/// int64_t ro_s, int64_t co_s, const T *x, int64_t incx, T beta, T *y, int64_t incy +/// ) +/// @verbatim embed:rst:leading-slashes +/// Perform a GEMV-like operation. If :math:`{\opS} = \texttt{NoTrans},` then we perform +/// +/// .. math:: +/// \mat(y) = \alpha \cdot \underbrace{\submat(S)}_{d \times m} \cdot \underbrace{\mat(x)}_{m \times 1} + \beta \cdot \underbrace{\mat(y)}_{d \times 1}, \tag{$\star$} +/// +/// otherwise, we perform +/// +/// .. math:: +/// \mat(y) = \alpha \cdot \underbrace{\submat(S)^T}_{m \times d} \cdot \underbrace{\mat(x)}_{d \times 1} + \beta \cdot \underbrace{\mat(y)}_{m \times 1}, \tag{$\diamond$} +/// +/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is a sketching operator. +/// +/// .. dropdown:: FAQ +/// :animate: fade-in-slide-down +/// +/// **What are** :math:`\mat(x)` **and** :math:`\mat(y)` **?** +/// +/// They are vectors of shapes :math:`(\mat(x), L_x \times 1)` and :math:`(\mat(y), L_y \times 1),` +/// where :math:`(L_x, L_y)` are lengths so that :math:`\opS(\submat(S)) \mat(x)` is well-defined and the same shape as :math:`\mat(y).` +/// Their precise contents are determined in a way that is identical to GEMV from BLAS. +/// +/// **Why no "layout" argument?** +/// +/// GEMV in CBLAS accepts a parameter that specifies row-major or column-major layout of the matrix operand. +/// Since our matrix is a sketching operator, and since RandBLAS has no notion of the layout of a sketching operator, we do not have a layout parameter. +/// +/// .. dropdown:: Full parameter descriptions +/// :animate: fade-in-slide-down +/// +/// opS - [in] +/// * Either Op::Trans or Op::NoTrans. +/// * If :math:`\opS` = NoTrans, then :math:`\op(\submat(S)) = \submat(S).` +/// * If :math:`\opS` = Trans, then :math:`\op(\submat(S)) = \submat(S)^T.` +/// +/// d - [in] +/// * A nonnegative integer. +/// * The number of rows in :math:`\submat(S).` +/// +/// m - [in] +/// * A nonnegative integer. +/// * The number of columns in :math:`\submat(S).` +/// +/// alpha - [in] +/// * A real scalar. +/// * If zero, then :math:`x` is not accessed. +/// +/// S - [in] +/// * A DenseSkOp or SparseSkOp object. +/// * Defines :math:`\submat(S).` +/// +/// ro_s - [in] +/// * A nonnegative integer. +/// * :math:`\submat(S)` is a contiguous submatrix of :math:`S[\texttt{ro_s}:(\texttt{ro_s} + d), :].` +/// +/// co_s - [in] +/// * A nonnegative integer. +/// * :math:`\submat(S)` is a contiguous submatrix of :math:`S[:,\texttt{co_s}:(\texttt{co_s} + m)].` +/// +/// x - [in] +/// * Pointer to a 1D array of real scalars. +/// * Defines :math:`\mat(x).` +/// +/// incx - [in] +/// * A positive integer. +/// * Stride between elements of x. +/// +/// beta - [in] +/// * A real scalar. +/// * If zero, then :math:`y` need not be set on input. +/// +/// y - [in, out] +/// * Pointer to 1D array of real scalars. +/// * On entry, defines :math:`\mat(y)` on the RIGHT-hand side of +/// :math:`(\star)` (if :math:`\opS = \texttt{NoTrans}`) or +/// :math:`(\diamond)` (if :math:`\opS = \texttt{Trans}`) +/// * On exit, defines :math:`\mat(y)` on the LEFT-hand side of the same. +/// +/// incy - [in] +/// * A positive integer. +/// * Stride between elements of y. +/// +/// @endverbatim +template +inline void sketch_vector( + blas::Op opS, + int64_t d, // rows in submat(S) + int64_t m, // cols in submat(S) + T alpha, + SKOP &S, + int64_t ro_s, + int64_t co_s, + const T *x, + int64_t incx, + T beta, + T *y, + int64_t incy +) { + int64_t _d, _m; + if (opS == blas::Op::Trans) { + _d = m; + _m = d; + } else { + _d = d; + _m = m; + } + return sketch_general(blas::Layout::RowMajor, opS, blas::Op::NoTrans, _d, 1, _m, alpha, S, ro_s, co_s, x, incx, beta, y, incy); +} + +// MARK: FULL(S) + +// ============================================================================= +/// \fn sketch_vector(blas::Op opS, T alpha, SKOP &S, +/// const T *x, int64_t incx, T beta, T *y, int64_t incy +/// ) +/// @verbatim embed:rst:leading-slashes +/// Perform a GEMV-like operation: +/// +/// .. math:: +/// \mat(y) = \alpha \cdot \op(S) \cdot \mat(x) + \beta \cdot \mat(y), \tag{$\star$} +/// +/// where :math:`\alpha` and :math:`\beta` are real scalars and :math:`S` is a sketching operator. +/// +/// .. dropdown:: FAQ +/// :animate: fade-in-slide-down +/// +/// **What are** :math:`\mat(x)` **and** :math:`\mat(y)` **?** +/// +/// They are vectors of shapes :math:`(\mat(x), L_x \times 1)` and :math:`(\mat(y), L_y \times 1),` +/// where :math:`(L_x, L_y)` are lengths so that :math:`\opS(S) \mat(x)` is well-defined and the same shape as :math:`\mat(y).` +/// Their precise contents are determined in a way that is identical to GEMV from BLAS. +/// +/// **Why no "layout" argument?** +/// +/// GEMV in CBLAS accepts a parameter that specifies row-major or column-major layout of the matrix operand. +/// Since our matrix is a sketching operator, and since RandBLAS has no notion of the layout of a sketching operator, we do not have a layout parameter. +/// +/// .. dropdown:: Full parameter descriptions +/// :animate: fade-in-slide-down +/// +/// opS - [in] +/// * Either Op::Trans or Op::NoTrans. +/// * If :math:`\opS` = NoTrans, then :math:`\op(S) = S.` +/// * If :math:`\opS` = Trans, then :math:`\op(S) = S^T.` +/// +/// alpha - [in] +/// * A real scalar. +/// * If zero, then :math:`x` is not accessed. +/// +/// S - [in] +/// * A DenseSkOp or SparseSkOp object. +/// +/// x - [in] +/// * Pointer to a 1D array of real scalars. +/// * Defines :math:`\mat(x).` +/// +/// incx - [in] +/// * A positive integer. +/// * Stride between elements of x. +/// +/// beta - [in] +/// * A real scalar. +/// * If zero, then :math:`y` need not be set on input. +/// +/// y - [in, out] +/// * Pointer to 1D array of real scalars. +/// * On entry, defines :math:`\mat(y)` on the RIGHT-hand side of +/// :math:`(\star).` +/// * On exit, defines :math:`\mat(y)` on the LEFT-hand side of the same. +/// +/// incy - [in] +/// * A positive integer. +/// * Stride between elements of y. +/// +/// @endverbatim +template +inline void sketch_vector( + blas::Op opS, + T alpha, + SKOP &S, + const T *x, + int64_t incx, + T beta, + T *y, + int64_t incy +) { + int64_t d = S.dist.n_rows; + int64_t m = S.dist.n_cols; + return sketch_vector(opS, d, m, alpha, S, 0, 0, x, incx, beta, y, incy); +} + +} // end namespace RandBLAS +#endif diff --git a/RandBLAS/util.hh b/RandBLAS/util.hh index 6c93365b..579fd932 100644 --- a/RandBLAS/util.hh +++ b/RandBLAS/util.hh @@ -30,6 +30,7 @@ #ifndef randblas_util_hh #define randblas_util_hh +#include #include #include #include @@ -149,6 +150,112 @@ std::string type_name() { // call as type_name() return r; } +template +void symmetrize(blas::Layout layout, blas::Uplo uplo, T* A, int64_t n, int64_t lda) { + + auto [inter_row_stride, inter_col_stride] = layout_to_strides(layout, lda); + #define matA(_i, _j) A[(_i)*inter_row_stride + (_j)*inter_col_stride] + if (uplo == blas::Uplo::Upper) { + // copy to lower + for (int64_t i = 0; i < n; ++i) { + for (int64_t j = i+1; j < n; ++j) { + matA(j,i) = matA(i,j); + } + } + } else if (uplo == blas::Uplo::Lower) { + // copy to upper + for (int64_t i = 0; i < n; ++i) { + for (int64_t j = i+1; j < n; ++j) { + matA(i,j) = matA(j,i); + } + } + } + #undef matA + return; +} + +template +void require_symmetric(blas::Layout layout, const T* A, int64_t n, int64_t lda, T tol) { + if (tol < 0) + return; + auto [inter_row_stride, inter_col_stride] = layout_to_strides(layout, lda); + #define matA(_i, _j) A[(_i)*inter_row_stride + (_j)*inter_col_stride] + for (int64_t i = 0; i < n; ++i) { + for (int64_t j = i+1; j < n; ++j) { + T Aij = matA(i,j); + T Aji = matA(j,i); + T viol = abs(Aij - Aji); + T rel_tol = (abs(Aij) + abs(Aji) + 1)*tol; + if (viol > rel_tol) { + std::string message = "Symmetry check failed. |A(%i,%i) - A(%i,%i)| was %d, which exceeds tolerance of %d."; + auto _message = message.c_str(); + randblas_error_if_msg(viol > rel_tol, _message, i, j, j, i, viol, rel_tol); + } + } + } + #undef matA + return; +} + +/** + * In-place transpose of square matrix of order n, with leading dimension lda. + * Turns out that "layout" doesn't matter here. +*/ +template +void transpose_square(T* A, int64_t n, int64_t lda) { + #define matA(_i, _j) A[(_i) + lda*(_j)] + for (int64_t i = 0; i < n; ++i) { + for (int64_t j = i+1; j < n; ++j) { + std::swap(matA(i,j), matA(j,i)); + } + } + #undef matA + return; +} + + +template +void flip_layout(blas::Layout layout_in, int64_t m, int64_t n, std::vector &A, int64_t lda_in, int64_t lda_out) { + using blas::Layout; + Layout layout_out; + int64_t len_buff_A_out; + if (layout_in == Layout::ColMajor) { + layout_out = Layout::RowMajor; + randblas_require(lda_in >= m); + randblas_require(lda_out >= n); + len_buff_A_out = lda_out * m; + } else { + layout_out = Layout::ColMajor; + randblas_require(lda_in >= n); + randblas_require(lda_out >= m); + len_buff_A_out = lda_out * n; + } + // irs = inter row stride (stepping down a column) + // ics = inter column stride (stepping across a row) + auto [irs_in, ics_in] = layout_to_strides(layout_in, lda_in); + auto [irs_out, ics_out] = layout_to_strides(layout_out, lda_out); + + if (len_buff_A_out >= (int64_t) A.size()) { + A.resize(len_buff_A_out); + } + std::vector A_in(A); + T* A_buff_in = A_in.data(); + T* A_buff_out = A.data(); + + #define A_IN(_i, _j) A_buff_in[(_i)*irs_in + (_j)*ics_in] + #define A_OUT(_i, _j) A_buff_out[(_i)*irs_out + (_j)*ics_out] + for (int64_t i = 0; i < m; ++i) { + for (int64_t j = 0; j < n; ++j) { + A_OUT(i,j) = A_IN(i,j); + } + } + A.erase(A.begin() + len_buff_A_out, A.end()); + A.resize(len_buff_A_out); + #undef A_IN + #undef A_OUT + return; +} + } // end namespace RandBLAS::util #endif diff --git a/rtd/source/api_reference/index.rst b/rtd/source/api_reference/index.rst index 47429a0c..e25e20a8 100644 --- a/rtd/source/api_reference/index.rst +++ b/rtd/source/api_reference/index.rst @@ -15,5 +15,5 @@ API Reference Computing a sketch: dense data Representing sparse data Computing a sketch: sparse data - Other sparse matrix operations + Sparse BLAS operations diff --git a/rtd/source/api_reference/sketch_dense.rst b/rtd/source/api_reference/sketch_dense.rst index 2b4fc2a5..7b8ae763 100644 --- a/rtd/source/api_reference/sketch_dense.rst +++ b/rtd/source/api_reference/sketch_dense.rst @@ -11,31 +11,86 @@ Computing a sketch: dense data ****************************************** -Full matrix-matrix operations -============================= +RandBLAS has adaptions of GEMM, GEMV, and SYMM when one of their matrix operands is a sketching operator. +These adaptations are provided through overloaded functions named sketch_general, sketch_vector, and sketch_symmetric. -.. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opS, blas::Op opA, int64_t d, int64_t n, int64_t m, T alpha, SKOP &S, const T *A, int64_t lda, T beta, T *B, int64_t ldb) - :project: RandBLAS +Out of the functions presented here, only sketch_general has low-level implementations; +sketch_vector and sketch_symmetric are basic wrappers around sketch_general, and are provided to make +to make implementations less error-prone for when porting code that currently uses BLAS +or a BLAS-like interface. -.. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opA, blas::Op opS, int64_t m, int64_t d, int64_t n, T alpha, const T *A, int64_t lda, SKOP &S, T beta, T *B, int64_t ldb) - :project: RandBLAS +Analogs to GEMM +=============== +.. dropdown:: :math:`B = \alpha \cdot \op(S)\cdot \op(A) + \beta \cdot B` + :animate: fade-in-slide-down + :color: light -Full matrix-vector operations -============================= + .. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opS, blas::Op opA, int64_t d, int64_t n, int64_t m, T alpha, SKOP &S, const T *A, int64_t lda, T beta, T *B, int64_t ldb) + :project: RandBLAS -.. doxygenfunction:: sketch_vector(blas::Op opS, T alpha, SKOP &S, const T *x, int64_t incx, T beta, T *y, int64_t incy) - :project: RandBLAS +.. dropdown:: :math:`B = \alpha \cdot \op(A)\cdot \op(S) + \beta \cdot B` + :animate: fade-in-slide-down + :color: light + .. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opA, blas::Op opS, int64_t m, int64_t d, int64_t n, T alpha, const T *A, int64_t lda, SKOP &S, T beta, T *B, int64_t ldb) + :project: RandBLAS -Submatrix operations -==================== +.. dropdown:: Variants using :math:`\op(\submat(S))` + :animate: fade-in-slide-down + :color: light -.. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opS, blas::Op opA, int64_t d, int64_t n, int64_t m, T alpha, SKOP &S, int64_t S_ro, int64_t S_co, const T *A, int64_t lda, T beta, T *B, int64_t ldb) - :project: RandBLAS + .. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opS, blas::Op opA, int64_t d, int64_t n, int64_t m, T alpha, SKOP &S, int64_t S_ro, int64_t S_co, const T *A, int64_t lda, T beta, T *B, int64_t ldb) + :project: RandBLAS -.. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opA, blas::Op opS, int64_t m, int64_t d, int64_t n, T alpha, const T *A, int64_t lda, SKOP &S, int64_t S_ro, int64_t S_co, T beta, T *B, int64_t ldb) - :project: RandBLAS + .. doxygenfunction:: RandBLAS::sketch_general(blas::Layout layout, blas::Op opA, blas::Op opS, int64_t m, int64_t d, int64_t n, T alpha, const T *A, int64_t lda, SKOP &S, int64_t S_ro, int64_t S_co, T beta, T *B, int64_t ldb) + :project: RandBLAS + + +Analogs to SYMM +=============== + +.. dropdown:: :math:`B = \alpha \cdot S \cdot A + \beta \cdot B` + :animate: fade-in-slide-down + :color: light + + .. doxygenfunction:: RandBLAS::sketch_symmetric(blas::Layout layout, int64_t d, int64_t n, T alpha, SKOP &S, const T *A, int64_t lda, T beta, T *B, int64_t ldb, T sym_check_tol = 0) + :project: RandBLAS + +.. dropdown:: :math:`B = \alpha \cdot A \cdot S + \beta \cdot B` + :animate: fade-in-slide-down + :color: light + + .. doxygenfunction:: RandBLAS::sketch_symmetric(blas::Layout layout, int64_t n, int64_t d, T alpha, const T *A, int64_t lda, SKOP &S, T beta, T *B, int64_t ldb, T sym_check_tol = 0) + :project: RandBLAS + + +.. dropdown:: Variants using :math:`\submat(S)` + :animate: fade-in-slide-down + :color: light + + .. doxygenfunction:: RandBLAS::sketch_symmetric(blas::Layout layout, int64_t d, int64_t n, T alpha, SKOP &S, int64_t ro_s, int64_t co_s, const T *A, int64_t lda, T beta, T *B, int64_t ldb, T sym_check_tol = 0) + :project: RandBLAS + + .. doxygenfunction:: RandBLAS::sketch_symmetric(blas::Layout layout, int64_t n, int64_t d, T alpha, const T *A, int64_t lda, SKOP &S, int64_t ro_s, int64_t co_s, T beta, T *B, int64_t ldb, T sym_check_tol = 0) + :project: RandBLAS + + + +Analogs to GEMV +=============== + +.. dropdown:: :math:`y = \alpha \cdot \op(S) \cdot x + \beta \cdot y` + :animate: fade-in-slide-down + :color: light + + .. doxygenfunction:: sketch_vector(blas::Op opS, T alpha, SKOP &S, const T *x, int64_t incx, T beta, T *y, int64_t incy) + :project: RandBLAS + +.. dropdown:: Variants using :math:`\op(\submat(S))` + :animate: fade-in-slide-down + :color: light + + .. doxygenfunction:: sketch_vector(blas::Op opS, int64_t d, int64_t m, T alpha, SKOP &S, int64_t ro_s, int64_t co_s, const T *x, int64_t incx, T beta, T *y, int64_t incy) + :project: RandBLAS -.. doxygenfunction:: sketch_vector(blas::Op opS, int64_t d, int64_t m, T alpha, SKOP &S, int64_t ro_s, int64_t co_s, const T *x, int64_t incx, T beta, T *y, int64_t incy) - :project: RandBLAS \ No newline at end of file diff --git a/rtd/source/api_reference/sparse_matrices.rst b/rtd/source/api_reference/sparse_matrices.rst index 7dd69cfa..79e939ae 100644 --- a/rtd/source/api_reference/sparse_matrices.rst +++ b/rtd/source/api_reference/sparse_matrices.rst @@ -8,7 +8,7 @@ .. |ttt| mathmacro:: \texttt ******************************** -Representing sparse data +Representing sparse matrices ******************************** diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1909e356..822c1701 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,17 +6,18 @@ if (GTest_FOUND) set(tmp TRUE) add_executable(RandBLAS_tests - linop_common.hh comparison.hh - test_sketch_vector.cc - # dense sketching operators for dense data matrices - test_dense_skops/test_construction.cc - test_dense_skops/test_lskge3.cc - test_dense_skops/test_rskge3.cc - # sparse sketching operators for dense data matrices - test_sparse_skops/test_construction.cc - test_sparse_skops/test_lskges.cc - test_sparse_skops/test_rskges.cc + test_datastructures/test_denseskop.cc + test_datastructures/test_sparseskop.cc + + test_matmul_cores/linop_common.hh + test_matmul_cores/test_lskge3.cc + test_matmul_cores/test_rskge3.cc + test_matmul_cores/test_lskges.cc + test_matmul_cores/test_rskges.cc + + test_matmul_wrappers/test_sketch_vector.cc + test_matmul_wrappers/test_sketch_symmetric.cc ) target_link_libraries(RandBLAS_tests RandBLAS @@ -26,15 +27,18 @@ if (GTest_FOUND) gtest_discover_tests(RandBLAS_tests) add_executable(SparseRandBLAS_tests - # dense sketching operators for sparse matrices comparison.hh - test_sparse_data/common.hh - test_sparse_data/test_left_multiply.hh - test_sparse_data/test_right_multiply.hh - test_sparse_data/test_csc.cc - test_sparse_data/test_csr.cc - test_sparse_data/test_coo.cc - test_sparse_data/test_conversions.cc + + test_datastructures/test_spmats/common.hh + test_datastructures/test_spmats/test_csc.cc + test_datastructures/test_spmats/test_csr.cc + test_datastructures/test_spmats/test_coo.cc + test_datastructures/test_spmats/test_conversions.cc + + test_matmul_cores/test_spmm/spmm_test_helpers.hh + test_matmul_cores/test_spmm/test_spmm_csc.cc + test_matmul_cores/test_spmm/test_spmm_csr.cc + test_matmul_cores/test_spmm/test_spmm_coo.cc ) target_link_libraries(SparseRandBLAS_tests RandBLAS @@ -46,5 +50,5 @@ if (GTest_FOUND) endif() message(STATUS "Checking for regression tests ... ${tmp}") -add_executable(test_rng_speed test_rng_speed.cc) +add_executable(test_rng_speed test_basic_rng/benchmark_speed.cc) target_link_libraries(test_rng_speed RandBLAS) diff --git a/test/DevNotes.md b/test/DevNotes.md new file mode 100644 index 00000000..6a2a1753 --- /dev/null +++ b/test/DevNotes.md @@ -0,0 +1,78 @@ +# Developer notes for RandBLAS' testing infrastructure + + +This document doesn't don't defend previous design decisions. +It just explains how things work right now. +That's easier for me (Riley) to write, and it's more useful to others. +(Plus, a good description of what we do will make pros and cons of the current approach self-evident.) + +None of our testing infrastructure is considered part of the public API. + +## Contents + +### matmul_wrappers + + * sketch_vector. It reduces to the same sketch_general no matter the type of the sketching operator. + * sketch_sparse. It reduces to left_spmm/right_spmm no matter the type of the data matrix. (The + sketching operator type is naturally fixed to DenseSkOp.) + * sketch_symmetric. It reduces to the same sketch_general no matter the type of the sketching operator. + +### matmul_cores + + * lskges, rskges, lskge3, rskge3. The rskgex functions could reduce to lskgex by transposing the + product and flipping the layout. Strictly speaking, the rskgex functions don't do that, but they + easily could. In any case, we currently have similar tests for rskgex and lskgex. + * left_spmm and right_spmm. The right_spmm implementation falls back on left_spmm. Despite this, + right_spmm has its own set of tests. + +I suspect that the tests for rskgex and right_spmm hit code paths that are currently untested, +but I haven't actually verified this. + +### next_folder ... + +## Next topic .... + + + +# OLD + + +Right-multplication by a structured linear operator in a GEMM-like API can +always be reduced to left-multiplication by flipping transposition flags and +layout parameters. So, why have equally fleshed-out tests(/test tooling) for +both cases? + +Short answer: maybe it was a bad idea. + +Big picture defensive answer: + + Different linear operators vary in the extent to which code for their + action on the left can be reduced to their action on the right. Action + on the right is equivalent to adjoint-action from the left. + + Someone who's adding a new linear operator might prefer to think mostly + in terms of right-multiplication, and just have left-multiplication reduce + to adjoint-action from the right. + + We want someone who adds new functionality to benefit from our testing infrastructure. So we made infrastructure to test GEMM-like APIs where + one operand is structured, and it's easy to get started using this + infrastructure because it's equally valid to start with tests that + multiply only for one side and only another. + +Specifics: + + RSKGE3 doesn't actually reduce to LSKGE3. It could, but it was + easy enough to have it reduce directly to GEMM, and reducing + directly to GEMM had the advantage of improved readibility. We don't + test all possible combinations of flags (we omit when both arguments + are transposed) but the combinationed we leave untested are unrelated + to flow-of-control. + + RSKGES reduces to right_spmm, which does indeed fall back on + left_spmm. But left_spmm has a large number codepaths (twelve!). + It would have been awkward to write tests that hit all of those codepaths + directly. Instead, we write a smaller set of tests for left_spmm + and right_spmm, and count on the right_spmm tests to hit complementary + codepaths compared to the paths hit in the left_spmm tests. + (Admission: we don't know for sure if all codepaths are hit. Verifying + that is on our TODO list.) diff --git a/test/comparison.hh b/test/comparison.hh index 74a62356..c8f69b27 100644 --- a/test/comparison.hh +++ b/test/comparison.hh @@ -30,6 +30,7 @@ #ifndef randblas_test_comparisons_hh #define randblas_test_comparisons_hh +#include "RandBLAS.hh" #include #include #include @@ -39,6 +40,8 @@ namespace test::comparison { +using blas::Layout; +using blas::Op; /** Tests two floating point numbers for approximate equality. * See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ @@ -114,6 +117,9 @@ void buffs_approx_equal( } } +// TODO: Make macros that can automatically inject __PRETTY_FUNCTION__, __FILE__, and __LINE__ into the calls below. +// This is slightly complicated by the presence of optional arguments in the function definition. + template void buffs_approx_equal( int64_t size, diff --git a/test/test_rng_speed.cc b/test/test_basic_rng/benchmark_speed.cc similarity index 100% rename from test/test_rng_speed.cc rename to test/test_basic_rng/benchmark_speed.cc diff --git a/test/test_dense_skops/test_construction.cc b/test/test_datastructures/test_denseskop.cc similarity index 99% rename from test/test_dense_skops/test_construction.cc rename to test/test_datastructures/test_denseskop.cc index dcac1c80..a17e3ee6 100644 --- a/test/test_dense_skops/test_construction.cc +++ b/test/test_datastructures/test_denseskop.cc @@ -32,7 +32,7 @@ #include "RandBLAS/random_gen.hh" #include "RandBLAS/dense_skops.hh" #include "RandBLAS/util.hh" -#include "../comparison.hh" +#include "test/comparison.hh" #include @@ -94,8 +94,7 @@ std::ostream &operator<<(std::ostream &os, std::vector &v) { return os; } -class TestDenseMoments : public ::testing::Test -{ +class TestDenseMoments : public ::testing::Test { protected: virtual void SetUp(){}; @@ -159,6 +158,7 @@ TEST_F(TestDenseMoments, Uniform) } } + class TestSubmatGeneration : public ::testing::Test { @@ -403,6 +403,7 @@ TEST_F(TestFillAxis, short_axis_2x4) { auto_transpose(2, 4, RandBLAS::MajorAxis::Short); } + class TestStateUpdate : public ::testing::Test { protected: diff --git a/test/test_sparse_skops/test_construction.cc b/test/test_datastructures/test_sparseskop.cc similarity index 99% rename from test/test_sparse_skops/test_construction.cc rename to test/test_datastructures/test_sparseskop.cc index a13fa91d..9843cb51 100644 --- a/test/test_sparse_skops/test_construction.cc +++ b/test/test_datastructures/test_sparseskop.cc @@ -30,7 +30,7 @@ #include #include #include -#include "../comparison.hh" +#include "test/comparison.hh" #include #include diff --git a/test/test_sparse_data/common.hh b/test/test_datastructures/test_spmats/common.hh similarity index 83% rename from test/test_sparse_data/common.hh rename to test/test_datastructures/test_spmats/common.hh index dff835dc..173304c7 100644 --- a/test/test_sparse_data/common.hh +++ b/test/test_datastructures/test_spmats/common.hh @@ -41,34 +41,10 @@ #include "RandBLAS/sparse_data/conversions.hh" -namespace test::sparse_data::common { +namespace test::test_datastructures::test_spmats { -using namespace RandBLAS::sparse_data; -using namespace RandBLAS::sparse_data::csr; -using namespace RandBLAS::sparse_data::conversions; using blas::Layout; -template -void sparseskop_to_dense( - RandBLAS::SparseSkOp &S0, - T *mat, - Layout layout -) { - RandBLAS::SparseDist D = S0.dist; - for (int64_t i = 0; i < D.n_rows * D.n_cols; ++i) - mat[i] = 0.0; - auto idx = [D, layout](int64_t i, int64_t j) { - return (layout == Layout::ColMajor) ? (i + j*D.n_rows) : (j + i*D.n_cols); - }; - int64_t nnz = RandBLAS::sparse::nnz(S0); - for (int64_t i = 0; i < nnz; ++i) { - sint_t row = S0.rows[i]; - sint_t col = S0.cols[i]; - T val = S0.vals[i]; - mat[idx(row, col)] = val; - } -} - template void iid_sparsify_random_dense( int64_t n_rows, @@ -130,7 +106,7 @@ void coo_from_diag( T* vals, int64_t nnz, int64_t offset, - COOMatrix &spmat + RandBLAS::sparse_data::COOMatrix &spmat ) { spmat.reserve(nnz); int64_t ell = 0; diff --git a/test/test_sparse_data/test_conversions.cc b/test/test_datastructures/test_spmats/test_conversions.cc similarity index 95% rename from test/test_sparse_data/test_conversions.cc rename to test/test_datastructures/test_spmats/test_conversions.cc index 0cd74497..06b7f936 100644 --- a/test/test_sparse_data/test_conversions.cc +++ b/test/test_datastructures/test_spmats/test_conversions.cc @@ -27,13 +27,16 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "test/test_sparse_data/common.hh" -#include "../comparison.hh" +#include "../../comparison.hh" +#include "common.hh" #include using namespace RandBLAS::sparse_data; -using namespace test::sparse_data::common; +using namespace test::test_datastructures::test_spmats; +using namespace RandBLAS::sparse_data::conversions; using namespace RandBLAS::sparse_data::csc; +using namespace RandBLAS::sparse_data::csr; +using namespace RandBLAS::sparse_data::coo; using blas::Layout; class TestSparseTranspose : public ::testing::Test diff --git a/test/test_datastructures/test_spmats/test_coo.cc b/test/test_datastructures/test_spmats/test_coo.cc new file mode 100644 index 00000000..a9bb0bbd --- /dev/null +++ b/test/test_datastructures/test_spmats/test_coo.cc @@ -0,0 +1,237 @@ +// Copyright, 2024. See LICENSE for copyright holder information. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) 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. +// +// (3) Neither the name of the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 "../../comparison.hh" +#include "common.hh" +#include +#include +#include + +using namespace RandBLAS::sparse_data; +using namespace RandBLAS::sparse_data::coo; +using namespace test::test_datastructures::test_spmats; +using namespace RandBLAS::sparse_data::conversions; +using blas::Layout; + + +template +void sparseskop_to_dense( + RandBLAS::SparseSkOp &S0, + T *mat, + Layout layout +) { + RandBLAS::SparseDist D = S0.dist; + for (int64_t i = 0; i < D.n_rows * D.n_cols; ++i) + mat[i] = 0.0; + auto idx = [D, layout](int64_t i, int64_t j) { + return (layout == Layout::ColMajor) ? (i + j*D.n_rows) : (j + i*D.n_cols); + }; + int64_t nnz = RandBLAS::sparse::nnz(S0); + for (int64_t i = 0; i < nnz; ++i) { + sint_t row = S0.rows[i]; + sint_t col = S0.cols[i]; + T val = S0.vals[i]; + mat[idx(row, col)] = val; + } +} + + +class TestCOO : public ::testing::Test { + protected: + + virtual void SetUp(){}; + + virtual void TearDown(){}; + + template + void test_to_from_dense(int64_t n) { + COOMatrix A(n, n); + std::vector actual(n * n); + RandBLAS::RNGState s(0); + iid_sparsify_random_dense(n, n, Layout::ColMajor, actual.data(), 0.8, s); + + dense_to_coo(1, n, actual.data(), 0.0, A); + std::vector expect(n * n); + coo_to_dense(A, 1, n, expect.data()); + + test::comparison::buffs_approx_equal(actual.data(), expect.data(), n * n, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + EXPECT_GT(A.nnz, 0); + EXPECT_LT(A.nnz, n*n); + return; + } + + template + void test_sort_order(int64_t n) { + COOMatrix A(n, n); + std::vector mat(n * n); + RandBLAS::RNGState s(0); + iid_sparsify_random_dense(n, n, Layout::ColMajor, mat.data(), 0.25, s); + dense_to_coo(1, n, mat.data(), 0.0, A); + + sort_coo_data(NonzeroSort::CSC, A); + EXPECT_EQ(A.sort, NonzeroSort::CSC); + auto sort = coo_sort_type(A.nnz, A.rows, A.cols); + EXPECT_EQ(sort, NonzeroSort::CSC); + + sort_coo_data(NonzeroSort::CSR, A); + EXPECT_EQ(A.sort, NonzeroSort::CSR); + sort = coo_sort_type(A.nnz, A.rows, A.cols); + EXPECT_EQ(sort, NonzeroSort::CSR); + + sort_coo_data(NonzeroSort::CSC, A); + EXPECT_EQ(A.sort, NonzeroSort::CSC); + sort = coo_sort_type(A.nnz, A.rows, A.cols); + EXPECT_EQ(sort, NonzeroSort::CSC); + return; + } +}; + + +TEST_F(TestCOO, to_from_dense) { + test_to_from_dense(3); + test_to_from_dense(4); + test_to_from_dense(10); +} + +TEST_F(TestCOO, sort_order) { + test_sort_order(3); + test_sort_order(7); + test_sort_order(10); +} + + +class Test_SkOp_to_COO : public ::testing::Test { + protected: + std::vector keys{42, 0, 1}; + std::vector vec_nnzs{(int64_t) 1, (int64_t) 2, (int64_t) 3, (int64_t) 7}; + + virtual void SetUp(){}; + + virtual void TearDown(){}; + + template + void sparse_skop_to_coo(int64_t d, int64_t m, int64_t key_index, int64_t nnz_index, RandBLAS::MajorAxis ma) { + RandBLAS::SparseSkOp S( + {d, m, vec_nnzs[nnz_index], ma}, keys[key_index] + ); + auto A = RandBLAS::sparse::coo_view_of_skop(S); + + EXPECT_EQ(S.dist.n_rows, A.n_rows); + EXPECT_EQ(S.dist.n_cols, A.n_cols); + EXPECT_EQ(RandBLAS::sparse::nnz(S), A.nnz); + + std::vector S_dense(d * m); + sparseskop_to_dense(S, S_dense.data(), Layout::ColMajor); + std::vector A_dense(d * m); + coo_to_dense(A, Layout::ColMajor, A_dense.data()); + + test::comparison::buffs_approx_equal(S_dense.data(), A_dense.data(), d * m, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + return; + } +}; + +TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_1) { + sparse_skop_to_coo(7, 20, 0, 0, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 1, 0, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 2, 0, RandBLAS::MajorAxis::Short); +} + +TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_2) { + sparse_skop_to_coo(7, 20, 0, 1, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 1, 1, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 2, 1, RandBLAS::MajorAxis::Short); +} + +TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_3) { + sparse_skop_to_coo(7, 20, 0, 2, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 1, 2, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 2, 2, RandBLAS::MajorAxis::Short); +} + +TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_7) { + sparse_skop_to_coo(7, 20, 0, 3, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 1, 3, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(7, 20, 2, 3, RandBLAS::MajorAxis::Short); +} + +TEST_F(Test_SkOp_to_COO, SASO_Dim_15by7) { + sparse_skop_to_coo(15, 7, 0, 0, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(15, 7, 1, 0, RandBLAS::MajorAxis::Short); + + sparse_skop_to_coo(15, 7, 0, 1, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(15, 7, 1, 1, RandBLAS::MajorAxis::Short); + + sparse_skop_to_coo(15, 7, 0, 2, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(15, 7, 1, 2, RandBLAS::MajorAxis::Short); + + sparse_skop_to_coo(15, 7, 0, 3, RandBLAS::MajorAxis::Short); + sparse_skop_to_coo(15, 7, 1, 3, RandBLAS::MajorAxis::Short); +} + +TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_1) { + sparse_skop_to_coo(7, 20, 0, 0, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 1, 0, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 2, 0, RandBLAS::MajorAxis::Long); +} + +TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_2) { + sparse_skop_to_coo(7, 20, 0, 1, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 1, 1, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 2, 1, RandBLAS::MajorAxis::Long); +} + +TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_3) { + sparse_skop_to_coo(7, 20, 0, 2, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 1, 2, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 2, 2, RandBLAS::MajorAxis::Long); +} + +TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_7) { + sparse_skop_to_coo(7, 20, 0, 3, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 1, 3, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(7, 20, 2, 3, RandBLAS::MajorAxis::Long); +} + +TEST_F(Test_SkOp_to_COO, LASO_Dim_15by7) { + sparse_skop_to_coo(15, 7, 0, 0, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(15, 7, 1, 0, RandBLAS::MajorAxis::Long); + + sparse_skop_to_coo(15, 7, 0, 1, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(15, 7, 1, 1, RandBLAS::MajorAxis::Long); + + sparse_skop_to_coo(15, 7, 0, 2, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(15, 7, 1, 2, RandBLAS::MajorAxis::Long); + + sparse_skop_to_coo(15, 7, 0, 3, RandBLAS::MajorAxis::Long); + sparse_skop_to_coo(15, 7, 1, 3, RandBLAS::MajorAxis::Long); +} diff --git a/test/test_datastructures/test_spmats/test_csc.cc b/test/test_datastructures/test_spmats/test_csc.cc new file mode 100644 index 00000000..7c081fc6 --- /dev/null +++ b/test/test_datastructures/test_spmats/test_csc.cc @@ -0,0 +1,162 @@ +// Copyright, 2024. See LICENSE for copyright holder information. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) 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. +// +// (3) Neither the name of the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 "../../comparison.hh" +#include "common.hh" +#include +#include +#include + +using namespace RandBLAS::sparse_data; +using namespace RandBLAS::sparse_data::csc; +using namespace test::test_datastructures::test_spmats; +using namespace RandBLAS::sparse_data::conversions; +using blas::Layout; + + +class TestCSC_Conversions : public ::testing::Test { + protected: + + virtual void SetUp(){}; + + virtual void TearDown(){}; + + template + static void test_csc_from_random_sparsified(Layout layout, int64_t m, int64_t n, T p) { + // Step 1. get dense representation of random sparse matrix + RandBLAS::RNGState s(0); + auto dn_mat = new T[m * n]; + iid_sparsify_random_dense(m, n, layout, dn_mat, p, s); + + // Step 2. convert the dense representation into a CSC matrix + CSCMatrix spmat(m, n, IndexBase::Zero); + dense_to_csc(layout, dn_mat, 0.0, spmat); + + // Step 3. reconstruct the dense representation of dn_mat from the CSC matrix. + auto dn_mat_recon = new T[m * n]; + csc_to_dense(spmat, layout, dn_mat_recon); + + // check equivalence of dn_mat and dn_mat_recon + test::comparison::buffs_approx_equal(dn_mat, dn_mat_recon, m * n, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + + delete [] dn_mat; + delete [] dn_mat_recon; + } + + template + static void test_csc_from_diag_coo(int64_t m, int64_t n, int64_t offset) { + int64_t len = (offset >= 0) ? std::min(m, n - offset) : std::min(m + offset, n); + randblas_require(len > 0); + T *diag = new T[len]{0.0}; + for (int i = 1; i <= len; ++i) + diag[i-1] = (T) i * 0.5; + T *mat_expect = new T[m * n]{0.0}; + #define MAT_EXPECT(_i, _j) mat_expect[(_i) + m*(_j)] + if (offset >= 0) { + for (int64_t ell = 0; ell < len; ++ell) + MAT_EXPECT(ell, ell + offset) = diag[ell]; + } else { + for (int64_t ell = 0; ell < len; ++ell) + MAT_EXPECT(ell - offset, ell) = diag[ell]; + } + + CSCMatrix csc(m, n); + COOMatrix coo(m, n); + coo_from_diag(diag, len, offset, coo); + coo_to_csc(coo, csc); + T *mat_actual = new T[m * n]{0.0}; + csc_to_dense(csc, Layout::ColMajor, mat_actual); + + test::comparison::matrices_approx_equal( + Layout::ColMajor, Layout::ColMajor, blas::Op::NoTrans, + m, n, mat_expect, m, mat_actual, m, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + + delete [] mat_expect; + delete [] diag; + delete [] mat_actual; + return; + } +}; + +TEST_F(TestCSC_Conversions, dense_random_rowmajor) { + test_csc_from_random_sparsified(Layout::RowMajor, 10, 5, 0.7); +} + +TEST_F(TestCSC_Conversions, dense_random_colmajor) { + test_csc_from_random_sparsified(Layout::ColMajor, 10, 5, 0.7); +} + +TEST_F(TestCSC_Conversions, coo_diagonal_square_zero_offset) { + test_csc_from_diag_coo(5, 5, 0); +} + +TEST_F(TestCSC_Conversions, coo_diagonal_square_pos_offset) { + test_csc_from_diag_coo(5, 5, 1); + test_csc_from_diag_coo(5, 5, 2); + test_csc_from_diag_coo(5, 5, 3); + test_csc_from_diag_coo(5, 5, 4); +} + +TEST_F(TestCSC_Conversions, coo_diagonal_square_neg_offset) { + test_csc_from_diag_coo(5, 5, -1); + test_csc_from_diag_coo(5, 5, -2); + test_csc_from_diag_coo(5, 5, -3); + test_csc_from_diag_coo(5, 5, -4); +} + +TEST_F(TestCSC_Conversions, coo_diagonal_rectangular_zero_offset) { + test_csc_from_diag_coo(5, 10, 0); + test_csc_from_diag_coo(10, 5, 0); +} + +TEST_F(TestCSC_Conversions, coo_diagonal_rectangular_pos_offset) { + test_csc_from_diag_coo(10, 5, 1); + test_csc_from_diag_coo(10, 5, 2); + test_csc_from_diag_coo(10, 5, 3); + test_csc_from_diag_coo(10, 5, 4); + test_csc_from_diag_coo(5, 10, 1); + test_csc_from_diag_coo(5, 10, 2); + test_csc_from_diag_coo(5, 10, 3); + test_csc_from_diag_coo(5, 10, 4); +} + +TEST_F(TestCSC_Conversions, coo_diagonal_rectangular_neg_offset) { + test_csc_from_diag_coo(10, 5, -1); + test_csc_from_diag_coo(10, 5, -2); + test_csc_from_diag_coo(10, 5, -3); + test_csc_from_diag_coo(10, 5, -4); + test_csc_from_diag_coo(5, 10, -1); + test_csc_from_diag_coo(5, 10, -2); + test_csc_from_diag_coo(5, 10, -3); + test_csc_from_diag_coo(5, 10, -4); + } diff --git a/test/test_datastructures/test_spmats/test_csr.cc b/test/test_datastructures/test_spmats/test_csr.cc new file mode 100644 index 00000000..294cb0f2 --- /dev/null +++ b/test/test_datastructures/test_spmats/test_csr.cc @@ -0,0 +1,191 @@ +// Copyright, 2024. See LICENSE for copyright holder information. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) 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. +// +// (3) Neither the name of the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 "../../comparison.hh" +#include "common.hh" +#include +#include +#include + +using namespace RandBLAS::sparse_data; +using namespace RandBLAS::sparse_data::csr; +using namespace test::test_datastructures::test_spmats; +using namespace RandBLAS::sparse_data::conversions; +using blas::Layout; + + +class TestCSR_Conversions : public ::testing::Test +{ + protected: + + virtual void SetUp(){}; + + virtual void TearDown(){}; + + template + static void test_csr_to_dense_diagonal(int64_t n) { + CSRMatrix A(n, n, IndexBase::Zero); + A.reserve(n); + for (int i = 0; i < n; ++i) { + A.vals[i] = 1.0 + (T) i; + A.rowptr[i] = i; + A.colidxs[i] = i; + } + A.rowptr[n] = n; + T *mat = new T[n*n]; + csr_to_dense(A, 1, n, mat); + T *eye = new T[n*n]{0.0}; + for (int i = 0; i < n; ++i) + eye[i + n*i] = 1.0 + (T) i; + test::comparison::buffs_approx_equal(mat, eye, n * n, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + + delete [] eye; + delete [] mat; + return; + } + + template + static void test_csr_from_random_sparsified(Layout layout, int64_t m, int64_t n, T p) { + // Step 1. get dense representation of random sparse matrix + RandBLAS::RNGState s(0); + auto dn_mat = new T[m * n]; + iid_sparsify_random_dense(m, n, layout, dn_mat, p, s); + + // Step 2. convert the dense representation into a CSR matrix + CSRMatrix spmat(m, n, IndexBase::Zero); + dense_to_csr(layout, dn_mat, 0.0, spmat); + + // Step 3. reconstruct the dense representation of dn_mat from the CSR matrix. + auto dn_mat_recon = new T[m * n]; + csr_to_dense(spmat, layout, dn_mat_recon); + + // check equivalence of dn_mat and dn_mat_recon + test::comparison::buffs_approx_equal(dn_mat, dn_mat_recon, m * n, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + + delete [] dn_mat; + delete [] dn_mat_recon; + } + + template + static void test_csr_from_diag_coo(int64_t m, int64_t n, int64_t offset) { + int64_t len = (offset >= 0) ? std::min(m, n - offset) : std::min(m + offset, n); + randblas_require(len > 0); + T *diag = new T[len]{0.0}; + for (int i = 1; i <= len; ++i) + diag[i-1] = (T) i * 0.5; + T *mat_expect = new T[m * n]{0.0}; + #define MAT_EXPECT(_i, _j) mat_expect[(_i) + m*(_j)] + if (offset >= 0) { + for (int64_t ell = 0; ell < len; ++ell) + MAT_EXPECT(ell, ell + offset) = diag[ell]; + } else { + for (int64_t ell = 0; ell < len; ++ell) + MAT_EXPECT(ell - offset, ell) = diag[ell]; + } + + CSRMatrix csr(m, n); + COOMatrix coo(m, n); + coo_from_diag(diag, len, offset, coo); + coo_to_csr(coo, csr); + T *mat_actual = new T[m * n]{0.0}; + csr_to_dense(csr, Layout::ColMajor, mat_actual); + + test::comparison::matrices_approx_equal( + Layout::ColMajor, Layout::ColMajor, blas::Op::NoTrans, + m, n, mat_expect, m, mat_actual, m, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + delete [] mat_expect; + delete [] diag; + delete [] mat_actual; + return; + } +}; + +TEST_F(TestCSR_Conversions, dense_square_diagonal) { + test_csr_to_dense_diagonal(3); + } + +TEST_F(TestCSR_Conversions, dense_random_rowmajor) { + test_csr_from_random_sparsified(Layout::RowMajor, 10, 5, 0.7); +} + +TEST_F(TestCSR_Conversions, dense_random_colmajor) { + test_csr_from_random_sparsified(Layout::ColMajor, 10, 5, 0.7); +} + +TEST_F(TestCSR_Conversions, coo_diagonal_square_zero_offset) { + test_csr_from_diag_coo(5, 5, 0); +} + +TEST_F(TestCSR_Conversions, coo_diagonal_square_pos_offset) { + test_csr_from_diag_coo(5, 5, 1); + test_csr_from_diag_coo(5, 5, 2); + test_csr_from_diag_coo(5, 5, 3); + test_csr_from_diag_coo(5, 5, 4); +} + +TEST_F(TestCSR_Conversions, coo_diagonal_square_neg_offset) { + test_csr_from_diag_coo(5, 5, -1); + test_csr_from_diag_coo(5, 5, -2); + test_csr_from_diag_coo(5, 5, -3); + test_csr_from_diag_coo(5, 5, -4); +} + +TEST_F(TestCSR_Conversions, coo_diagonal_rectangular_zero_offset) { + test_csr_from_diag_coo(5, 10, 0); + test_csr_from_diag_coo(10, 5, 0); +} + +TEST_F(TestCSR_Conversions, coo_diagonal_rectangular_pos_offset) { + test_csr_from_diag_coo(10, 5, 1); + test_csr_from_diag_coo(10, 5, 2); + test_csr_from_diag_coo(10, 5, 3); + test_csr_from_diag_coo(10, 5, 4); + test_csr_from_diag_coo(5, 10, 1); + test_csr_from_diag_coo(5, 10, 2); + test_csr_from_diag_coo(5, 10, 3); + test_csr_from_diag_coo(5, 10, 4); +} + +TEST_F(TestCSR_Conversions, coo_diagonal_rectangular_neg_offset) { + test_csr_from_diag_coo(10, 5, -1); + test_csr_from_diag_coo(10, 5, -2); + test_csr_from_diag_coo(10, 5, -3); + test_csr_from_diag_coo(10, 5, -4); + test_csr_from_diag_coo(5, 10, -1); + test_csr_from_diag_coo(5, 10, -2); + test_csr_from_diag_coo(5, 10, -3); + test_csr_from_diag_coo(5, 10, -4); + } + diff --git a/test/linop_common.hh b/test/test_matmul_cores/linop_common.hh similarity index 100% rename from test/linop_common.hh rename to test/test_matmul_cores/linop_common.hh diff --git a/test/test_dense_skops/test_lskge3.cc b/test/test_matmul_cores/test_lskge3.cc similarity index 99% rename from test/test_dense_skops/test_lskge3.cc rename to test/test_matmul_cores/test_lskge3.cc index 7c18e15f..921dad05 100644 --- a/test/test_dense_skops/test_lskge3.cc +++ b/test/test_matmul_cores/test_lskge3.cc @@ -27,7 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "../linop_common.hh" +#include "test/test_matmul_cores/linop_common.hh" #include using RandBLAS::DenseDist; diff --git a/test/test_sparse_skops/test_lskges.cc b/test/test_matmul_cores/test_lskges.cc similarity index 99% rename from test/test_sparse_skops/test_lskges.cc rename to test/test_matmul_cores/test_lskges.cc index 3bc1a737..1bbd0d03 100644 --- a/test/test_sparse_skops/test_lskges.cc +++ b/test/test_matmul_cores/test_lskges.cc @@ -27,7 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "../linop_common.hh" +#include "test/test_matmul_cores/linop_common.hh" #include using RandBLAS::SparseDist; @@ -185,7 +185,6 @@ TEST_F(TestLSKGES, sketch_saso_rowMajor_oneThread) } } - TEST_F(TestLSKGES, sketch_laso_rowMajor_oneThread) { for (int64_t k_idx : {0, 1, 2}) { diff --git a/test/test_dense_skops/test_rskge3.cc b/test/test_matmul_cores/test_rskge3.cc similarity index 99% rename from test/test_dense_skops/test_rskge3.cc rename to test/test_matmul_cores/test_rskge3.cc index d90b06f6..2bd8f949 100644 --- a/test/test_dense_skops/test_rskge3.cc +++ b/test/test_matmul_cores/test_rskge3.cc @@ -27,7 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "../linop_common.hh" +#include "test/test_matmul_cores/linop_common.hh" #include using namespace test::linop_common; diff --git a/test/test_sparse_skops/test_rskges.cc b/test/test_matmul_cores/test_rskges.cc similarity index 99% rename from test/test_sparse_skops/test_rskges.cc rename to test/test_matmul_cores/test_rskges.cc index 6364a348..35b62f61 100644 --- a/test/test_sparse_skops/test_rskges.cc +++ b/test/test_matmul_cores/test_rskges.cc @@ -27,7 +27,7 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "../linop_common.hh" +#include "test/test_matmul_cores/linop_common.hh" #include using namespace test::linop_common; @@ -193,7 +193,6 @@ TEST_F(TestRSKGES, sketch_saso_rowMajor_oneThread) } } - TEST_F(TestRSKGES, sketch_laso_rowMajor_oneThread) { for (int64_t k_idx : {0, 1, 2}) { diff --git a/test/test_sparse_data/test_left_multiply.hh b/test/test_matmul_cores/test_spmm/spmm_test_helpers.hh similarity index 59% rename from test/test_sparse_data/test_left_multiply.hh rename to test/test_matmul_cores/test_spmm/spmm_test_helpers.hh index 0c9f91ca..5190e256 100644 --- a/test/test_sparse_data/test_left_multiply.hh +++ b/test/test_matmul_cores/test_spmm/spmm_test_helpers.hh @@ -27,21 +27,25 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "../linop_common.hh" -#include "common.hh" +#include "test/test_datastructures/test_spmats/common.hh" +#include "test/test_matmul_cores/linop_common.hh" #include -using namespace test::sparse_data::common; using namespace test::linop_common; using blas::Layout; +using test::test_datastructures::test_spmats::iid_sparsify_random_dense; +// ^ Not used in this file, but required by client files. + template -class TestLeftMultiply_Sparse : public ::testing::Test -{ - using T = typename SpMat::scalar_t; +class TestLeftMultiply_Sparse : public ::testing::Test { // C = alpha * opA(submat(A)) @ opB(B) + beta * C + // // In what follows, "self" refers to A and "other" refers to B. + // + + using T = typename SpMat::scalar_t; protected: virtual void SetUp(){}; @@ -116,3 +120,75 @@ class TestLeftMultiply_Sparse : public ::testing::Test }; +template +class TestRightMultiply_Sparse : public ::testing::Test { + // C = alpha * opB(B) @ opA(submat(A)) + beta * C + // + // In what follows, "self" refers to A and "other" refers to B. + // + + using T = typename SpMat::scalar_t; + protected: + virtual void SetUp(){}; + virtual void TearDown(){}; + + virtual SpMat make_test_matrix(int64_t m, int64_t n, T nonzero_prob, uint32_t key = 0) = 0; + + void multiply_eye(uint32_t key, int64_t m, int64_t n, Layout layout, T p) { + auto A = this->make_test_matrix(m, n, p, key); + test_right_apply_submatrix_to_eye(1.0, A, m, n, 0, 0, layout, 0.0, 0); + } + + void alpha_beta(uint32_t key, T alpha, T beta, int64_t m, int64_t n, Layout layout, T p) { + auto A = this->make_test_matrix(m, n, p, key); + test_right_apply_submatrix_to_eye(alpha, A, m, n, 0, 0, layout, beta, 0); + } + + void transpose_self(uint32_t key, int64_t m, int64_t n, Layout layout, T p) { + auto A = this->make_test_matrix(m, n, p, key); + test_right_apply_tranpose_to_eye(A, layout, 0); + } + + void submatrix_self( + uint32_t key, // key for RNG that generates sparse A + int64_t d, // cols in A + int64_t m, // rows in A, columns in B = eye(m) + int64_t d0, // cols in A0 + int64_t m0, // rows in A0 + int64_t A_ro, // row offset for A in A0 + int64_t A_co, // col offset for A in A0 + Layout layout, // layout of dense matrix input and output + T p + ) { + auto A0 = this->make_test_matrix(m0, d0, p, key); + test_right_apply_submatrix_to_eye(1.0, A0, m, d, A_ro, A_co, layout, 0.0, 0); + } + + void transpose_other( + uint32_t key, // key for RNG that generates sparse A + int64_t d, // cols in A + int64_t n, // rows in A and B + int64_t m, // cols in B + Layout layout, // layout of dense matrix input and output + T p + ) { + auto A = this->make_test_matrix(n, d, p, key); + test_right_apply_to_transposed(A, m, layout, 0); + } + + void submatrix_other( + uint32_t key, // key for RNG that generates sparse A + int64_t d, // cols in A + int64_t m, // rows in B + int64_t n, // rows in A, columns in B + int64_t m0, // rows in B0 + int64_t n0, // cols in B0 + int64_t B_ro, // row offset for B in B0 + int64_t B_co, // col offset for B in B0 + Layout layout, // layout of dense matrix input and output + T p + ) { + auto A = this->make_test_matrix(n, d, p, key); + test_right_apply_to_submatrix(A, m, m0, n0, B_ro, B_co, layout, 0); + } +}; diff --git a/test/test_sparse_data/test_coo.cc b/test/test_matmul_cores/test_spmm/test_spmm_coo.cc similarity index 71% rename from test/test_sparse_data/test_coo.cc rename to test/test_matmul_cores/test_spmm/test_spmm_coo.cc index 9380f28f..119b743d 100644 --- a/test/test_sparse_data/test_coo.cc +++ b/test/test_matmul_cores/test_spmm/test_spmm_coo.cc @@ -27,193 +27,14 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "test/test_sparse_data/test_left_multiply.hh" -#include "test/test_sparse_data/test_right_multiply.hh" +#include "test/test_matmul_cores/test_spmm/spmm_test_helpers.hh" #include using namespace RandBLAS::sparse_data; using namespace RandBLAS::sparse_data::coo; -using namespace test::sparse_data::common; using blas::Layout; -class TestCOO : public ::testing::Test -{ - protected: - - virtual void SetUp(){}; - - virtual void TearDown(){}; - - template - void test_to_from_dense(int64_t n) { - COOMatrix A(n, n); - std::vector actual(n * n); - RandBLAS::RNGState s(0); - iid_sparsify_random_dense(n, n, Layout::ColMajor, actual.data(), 0.8, s); - - dense_to_coo(1, n, actual.data(), 0.0, A); - std::vector expect(n * n); - coo_to_dense(A, 1, n, expect.data()); - - test::comparison::buffs_approx_equal(actual.data(), expect.data(), n * n, - __PRETTY_FUNCTION__, __FILE__, __LINE__ - ); - EXPECT_GT(A.nnz, 0); - EXPECT_LT(A.nnz, n*n); - return; - } - - template - void test_sort_order(int64_t n) { - COOMatrix A(n, n); - std::vector mat(n * n); - RandBLAS::RNGState s(0); - iid_sparsify_random_dense(n, n, Layout::ColMajor, mat.data(), 0.25, s); - dense_to_coo(1, n, mat.data(), 0.0, A); - - sort_coo_data(NonzeroSort::CSC, A); - EXPECT_EQ(A.sort, NonzeroSort::CSC); - auto sort = coo_sort_type(A.nnz, A.rows, A.cols); - EXPECT_EQ(sort, NonzeroSort::CSC); - - sort_coo_data(NonzeroSort::CSR, A); - EXPECT_EQ(A.sort, NonzeroSort::CSR); - sort = coo_sort_type(A.nnz, A.rows, A.cols); - EXPECT_EQ(sort, NonzeroSort::CSR); - - sort_coo_data(NonzeroSort::CSC, A); - EXPECT_EQ(A.sort, NonzeroSort::CSC); - sort = coo_sort_type(A.nnz, A.rows, A.cols); - EXPECT_EQ(sort, NonzeroSort::CSC); - return; - } - -}; - -TEST_F(TestCOO, to_from_dense) { - test_to_from_dense(3); - test_to_from_dense(4); - test_to_from_dense(10); -} - -TEST_F(TestCOO, sort_order) { - test_sort_order(3); - test_sort_order(7); - test_sort_order(10); -} - - -class Test_SkOp_to_COO : public ::testing::Test -{ - protected: - std::vector keys{42, 0, 1}; - std::vector vec_nnzs{(int64_t) 1, (int64_t) 2, (int64_t) 3, (int64_t) 7}; - - virtual void SetUp(){}; - - virtual void TearDown(){}; - - template - void sparse_skop_to_coo(int64_t d, int64_t m, int64_t key_index, int64_t nnz_index, RandBLAS::MajorAxis ma) { - RandBLAS::SparseSkOp S( - {d, m, vec_nnzs[nnz_index], ma}, keys[key_index] - ); - auto A = RandBLAS::sparse::coo_view_of_skop(S); - - EXPECT_EQ(S.dist.n_rows, A.n_rows); - EXPECT_EQ(S.dist.n_cols, A.n_cols); - EXPECT_EQ(RandBLAS::sparse::nnz(S), A.nnz); - - std::vector S_dense(d * m); - sparseskop_to_dense(S, S_dense.data(), Layout::ColMajor); - std::vector A_dense(d * m); - coo_to_dense(A, Layout::ColMajor, A_dense.data()); - - test::comparison::buffs_approx_equal(S_dense.data(), A_dense.data(), d * m, - __PRETTY_FUNCTION__, __FILE__, __LINE__ - ); - return; - } -}; - -TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_1) { - sparse_skop_to_coo(7, 20, 0, 0, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 1, 0, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 2, 0, RandBLAS::MajorAxis::Short); -} - -TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_2) { - sparse_skop_to_coo(7, 20, 0, 1, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 1, 1, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 2, 1, RandBLAS::MajorAxis::Short); -} - -TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_3) { - sparse_skop_to_coo(7, 20, 0, 2, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 1, 2, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 2, 2, RandBLAS::MajorAxis::Short); -} - -TEST_F(Test_SkOp_to_COO, SASO_Dim_7by20_nnz_7) { - sparse_skop_to_coo(7, 20, 0, 3, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 1, 3, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(7, 20, 2, 3, RandBLAS::MajorAxis::Short); -} - -TEST_F(Test_SkOp_to_COO, SASO_Dim_15by7) { - sparse_skop_to_coo(15, 7, 0, 0, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(15, 7, 1, 0, RandBLAS::MajorAxis::Short); - - sparse_skop_to_coo(15, 7, 0, 1, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(15, 7, 1, 1, RandBLAS::MajorAxis::Short); - - sparse_skop_to_coo(15, 7, 0, 2, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(15, 7, 1, 2, RandBLAS::MajorAxis::Short); - - sparse_skop_to_coo(15, 7, 0, 3, RandBLAS::MajorAxis::Short); - sparse_skop_to_coo(15, 7, 1, 3, RandBLAS::MajorAxis::Short); -} - -TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_1) { - sparse_skop_to_coo(7, 20, 0, 0, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 1, 0, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 2, 0, RandBLAS::MajorAxis::Long); -} - -TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_2) { - sparse_skop_to_coo(7, 20, 0, 1, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 1, 1, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 2, 1, RandBLAS::MajorAxis::Long); -} - -TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_3) { - sparse_skop_to_coo(7, 20, 0, 2, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 1, 2, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 2, 2, RandBLAS::MajorAxis::Long); -} - -TEST_F(Test_SkOp_to_COO, LASO_Dim_7by20_nnz_7) { - sparse_skop_to_coo(7, 20, 0, 3, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 1, 3, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(7, 20, 2, 3, RandBLAS::MajorAxis::Long); -} - -TEST_F(Test_SkOp_to_COO, LASO_Dim_15by7) { - sparse_skop_to_coo(15, 7, 0, 0, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(15, 7, 1, 0, RandBLAS::MajorAxis::Long); - - sparse_skop_to_coo(15, 7, 0, 1, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(15, 7, 1, 1, RandBLAS::MajorAxis::Long); - - sparse_skop_to_coo(15, 7, 0, 2, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(15, 7, 1, 2, RandBLAS::MajorAxis::Long); - - sparse_skop_to_coo(15, 7, 0, 3, RandBLAS::MajorAxis::Long); - sparse_skop_to_coo(15, 7, 1, 3, RandBLAS::MajorAxis::Long); -} - - template class TestLeftMultiply_COO : public TestLeftMultiply_Sparse> { COOMatrix make_test_matrix(int64_t m, int64_t n, T nonzero_prob, uint32_t key = 0) { diff --git a/test/test_sparse_data/test_csc.cc b/test/test_matmul_cores/test_spmm/test_spmm_csc.cc similarity index 74% rename from test/test_sparse_data/test_csc.cc rename to test/test_matmul_cores/test_spmm/test_spmm_csc.cc index 8d1475eb..1ee95552 100644 --- a/test/test_sparse_data/test_csc.cc +++ b/test/test_matmul_cores/test_spmm/test_spmm_csc.cc @@ -27,153 +27,15 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "test/test_sparse_data/test_left_multiply.hh" -#include "test/test_sparse_data/test_right_multiply.hh" +#include "test/test_matmul_cores/test_spmm/spmm_test_helpers.hh" #include #include using namespace RandBLAS::sparse_data; using namespace RandBLAS::sparse_data::csc; -using namespace test::sparse_data::common; using blas::Layout; -class TestCSC_Conversions : public ::testing::Test -{ - protected: - - virtual void SetUp(){}; - - virtual void TearDown(){}; - - template - static void test_csc_from_random_sparsified(Layout layout, int64_t m, int64_t n, T p) { - // Step 1. get dense representation of random sparse matrix - RandBLAS::RNGState s(0); - auto dn_mat = new T[m * n]; - iid_sparsify_random_dense(m, n, layout, dn_mat, p, s); - - // Step 2. convert the dense representation into a CSC matrix - CSCMatrix spmat(m, n, IndexBase::Zero); - dense_to_csc(layout, dn_mat, 0.0, spmat); - - // Step 3. reconstruct the dense representation of dn_mat from the CSC matrix. - auto dn_mat_recon = new T[m * n]; - csc_to_dense(spmat, layout, dn_mat_recon); - - // check equivalence of dn_mat and dn_mat_recon - test::comparison::buffs_approx_equal(dn_mat, dn_mat_recon, m * n, - __PRETTY_FUNCTION__, __FILE__, __LINE__ - ); - - delete [] dn_mat; - delete [] dn_mat_recon; - } - - template - static void test_csc_from_diag_coo(int64_t m, int64_t n, int64_t offset) { - int64_t len = (offset >= 0) ? std::min(m, n - offset) : std::min(m + offset, n); - randblas_require(len > 0); - T *diag = new T[len]{0.0}; - for (int i = 1; i <= len; ++i) - diag[i-1] = (T) i * 0.5; - T *mat_expect = new T[m * n]{0.0}; - #define MAT_EXPECT(_i, _j) mat_expect[(_i) + m*(_j)] - if (offset >= 0) { - for (int64_t ell = 0; ell < len; ++ell) - MAT_EXPECT(ell, ell + offset) = diag[ell]; - } else { - for (int64_t ell = 0; ell < len; ++ell) - MAT_EXPECT(ell - offset, ell) = diag[ell]; - } - - CSCMatrix csc(m, n); - COOMatrix coo(m, n); - coo_from_diag(diag, len, offset, coo); - coo_to_csc(coo, csc); - T *mat_actual = new T[m * n]{0.0}; - csc_to_dense(csc, Layout::ColMajor, mat_actual); - - test::comparison::matrices_approx_equal( - Layout::ColMajor, Layout::ColMajor, blas::Op::NoTrans, - m, n, mat_expect, m, mat_actual, m, - __PRETTY_FUNCTION__, __FILE__, __LINE__ - ); - - delete [] mat_expect; - delete [] diag; - delete [] mat_actual; - return; - } -}; - -TEST_F(TestCSC_Conversions, dense_random_rowmajor) { - test_csc_from_random_sparsified(Layout::RowMajor, 10, 5, 0.7); -} - -TEST_F(TestCSC_Conversions, dense_random_colmajor) { - test_csc_from_random_sparsified(Layout::ColMajor, 10, 5, 0.7); -} - -TEST_F(TestCSC_Conversions, coo_diagonal_square_zero_offset) { - test_csc_from_diag_coo(5, 5, 0); -} - -TEST_F(TestCSC_Conversions, coo_diagonal_square_pos_offset) { - test_csc_from_diag_coo(5, 5, 1); - test_csc_from_diag_coo(5, 5, 2); - test_csc_from_diag_coo(5, 5, 3); - test_csc_from_diag_coo(5, 5, 4); -} - -TEST_F(TestCSC_Conversions, coo_diagonal_square_neg_offset) { - test_csc_from_diag_coo(5, 5, -1); - test_csc_from_diag_coo(5, 5, -2); - test_csc_from_diag_coo(5, 5, -3); - test_csc_from_diag_coo(5, 5, -4); -} - -TEST_F(TestCSC_Conversions, coo_diagonal_rectangular_zero_offset) { - test_csc_from_diag_coo(5, 10, 0); - test_csc_from_diag_coo(10, 5, 0); -} - -TEST_F(TestCSC_Conversions, coo_diagonal_rectangular_pos_offset) { - test_csc_from_diag_coo(10, 5, 1); - test_csc_from_diag_coo(10, 5, 2); - test_csc_from_diag_coo(10, 5, 3); - test_csc_from_diag_coo(10, 5, 4); - test_csc_from_diag_coo(5, 10, 1); - test_csc_from_diag_coo(5, 10, 2); - test_csc_from_diag_coo(5, 10, 3); - test_csc_from_diag_coo(5, 10, 4); -} - -TEST_F(TestCSC_Conversions, coo_diagonal_rectangular_neg_offset) { - test_csc_from_diag_coo(10, 5, -1); - test_csc_from_diag_coo(10, 5, -2); - test_csc_from_diag_coo(10, 5, -3); - test_csc_from_diag_coo(10, 5, -4); - test_csc_from_diag_coo(5, 10, -1); - test_csc_from_diag_coo(5, 10, -2); - test_csc_from_diag_coo(5, 10, -3); - test_csc_from_diag_coo(5, 10, -4); - } - - -// template -// CSCMatrix make_test_matrix(int64_t m, int64_t n, T nonzero_prob, uint32_t key = 0) { -// randblas_require(nonzero_prob >= 0); -// randblas_require(nonzero_prob <= 1); -// CSCMatrix A(m, n); -// std::vector actual(m * n); -// RandBLAS::RNGState s(key); -// iid_sparsify_random_dense(m, n, Layout::ColMajor, actual.data(), 1 - nonzero_prob, s); -// dense_to_csc(Layout::ColMajor, actual.data(), 0.0, A); -// return A; -// } - - template class TestLeftMultiply_CSC : public TestLeftMultiply_Sparse> { CSCMatrix make_test_matrix(int64_t m, int64_t n, T nonzero_prob, uint32_t key = 0) { diff --git a/test/test_sparse_data/test_csr.cc b/test/test_matmul_cores/test_spmm/test_spmm_csr.cc similarity index 71% rename from test/test_sparse_data/test_csr.cc rename to test/test_matmul_cores/test_spmm/test_spmm_csr.cc index 1a2de5bc..d8618a02 100644 --- a/test/test_sparse_data/test_csr.cc +++ b/test/test_matmul_cores/test_spmm/test_spmm_csr.cc @@ -27,181 +27,15 @@ // POSSIBILITY OF SUCH DAMAGE. // -#include "test/test_sparse_data/test_left_multiply.hh" -#include "test/test_sparse_data/test_right_multiply.hh" +#include "test/test_matmul_cores/test_spmm/spmm_test_helpers.hh" #include #include using namespace RandBLAS::sparse_data; using namespace RandBLAS::sparse_data::csr; -using namespace test::sparse_data::common; using blas::Layout; -class TestCSR_Conversions : public ::testing::Test -{ - protected: - - virtual void SetUp(){}; - - virtual void TearDown(){}; - - template - static void test_csr_to_dense_diagonal(int64_t n) { - CSRMatrix A(n, n, IndexBase::Zero); - A.reserve(n); - for (int i = 0; i < n; ++i) { - A.vals[i] = 1.0 + (T) i; - A.rowptr[i] = i; - A.colidxs[i] = i; - } - A.rowptr[n] = n; - T *mat = new T[n*n]; - csr_to_dense(A, 1, n, mat); - T *eye = new T[n*n]{0.0}; - for (int i = 0; i < n; ++i) - eye[i + n*i] = 1.0 + (T) i; - test::comparison::buffs_approx_equal(mat, eye, n * n, - __PRETTY_FUNCTION__, __FILE__, __LINE__ - ); - - delete [] eye; - delete [] mat; - return; - } - - template - static void test_csr_from_random_sparsified(Layout layout, int64_t m, int64_t n, T p) { - // Step 1. get dense representation of random sparse matrix - RandBLAS::RNGState s(0); - auto dn_mat = new T[m * n]; - iid_sparsify_random_dense(m, n, layout, dn_mat, p, s); - - // Step 2. convert the dense representation into a CSR matrix - CSRMatrix spmat(m, n, IndexBase::Zero); - dense_to_csr(layout, dn_mat, 0.0, spmat); - - // Step 3. reconstruct the dense representation of dn_mat from the CSR matrix. - auto dn_mat_recon = new T[m * n]; - csr_to_dense(spmat, layout, dn_mat_recon); - - // check equivalence of dn_mat and dn_mat_recon - test::comparison::buffs_approx_equal(dn_mat, dn_mat_recon, m * n, - __PRETTY_FUNCTION__, __FILE__, __LINE__ - ); - - delete [] dn_mat; - delete [] dn_mat_recon; - } - - template - static void test_csr_from_diag_coo(int64_t m, int64_t n, int64_t offset) { - int64_t len = (offset >= 0) ? std::min(m, n - offset) : std::min(m + offset, n); - randblas_require(len > 0); - T *diag = new T[len]{0.0}; - for (int i = 1; i <= len; ++i) - diag[i-1] = (T) i * 0.5; - T *mat_expect = new T[m * n]{0.0}; - #define MAT_EXPECT(_i, _j) mat_expect[(_i) + m*(_j)] - if (offset >= 0) { - for (int64_t ell = 0; ell < len; ++ell) - MAT_EXPECT(ell, ell + offset) = diag[ell]; - } else { - for (int64_t ell = 0; ell < len; ++ell) - MAT_EXPECT(ell - offset, ell) = diag[ell]; - } - - CSRMatrix csr(m, n); - COOMatrix coo(m, n); - coo_from_diag(diag, len, offset, coo); - coo_to_csr(coo, csr); - T *mat_actual = new T[m * n]{0.0}; - csr_to_dense(csr, Layout::ColMajor, mat_actual); - - test::comparison::matrices_approx_equal( - Layout::ColMajor, Layout::ColMajor, blas::Op::NoTrans, - m, n, mat_expect, m, mat_actual, m, - __PRETTY_FUNCTION__, __FILE__, __LINE__ - ); - delete [] mat_expect; - delete [] diag; - delete [] mat_actual; - return; - } -}; - -TEST_F(TestCSR_Conversions, dense_square_diagonal) { - test_csr_to_dense_diagonal(3); - } - -TEST_F(TestCSR_Conversions, dense_random_rowmajor) { - test_csr_from_random_sparsified(Layout::RowMajor, 10, 5, 0.7); -} - -TEST_F(TestCSR_Conversions, dense_random_colmajor) { - test_csr_from_random_sparsified(Layout::ColMajor, 10, 5, 0.7); -} - -TEST_F(TestCSR_Conversions, coo_diagonal_square_zero_offset) { - test_csr_from_diag_coo(5, 5, 0); -} - -TEST_F(TestCSR_Conversions, coo_diagonal_square_pos_offset) { - test_csr_from_diag_coo(5, 5, 1); - test_csr_from_diag_coo(5, 5, 2); - test_csr_from_diag_coo(5, 5, 3); - test_csr_from_diag_coo(5, 5, 4); -} - -TEST_F(TestCSR_Conversions, coo_diagonal_square_neg_offset) { - test_csr_from_diag_coo(5, 5, -1); - test_csr_from_diag_coo(5, 5, -2); - test_csr_from_diag_coo(5, 5, -3); - test_csr_from_diag_coo(5, 5, -4); -} - -TEST_F(TestCSR_Conversions, coo_diagonal_rectangular_zero_offset) { - test_csr_from_diag_coo(5, 10, 0); - test_csr_from_diag_coo(10, 5, 0); -} - -TEST_F(TestCSR_Conversions, coo_diagonal_rectangular_pos_offset) { - test_csr_from_diag_coo(10, 5, 1); - test_csr_from_diag_coo(10, 5, 2); - test_csr_from_diag_coo(10, 5, 3); - test_csr_from_diag_coo(10, 5, 4); - test_csr_from_diag_coo(5, 10, 1); - test_csr_from_diag_coo(5, 10, 2); - test_csr_from_diag_coo(5, 10, 3); - test_csr_from_diag_coo(5, 10, 4); -} - -TEST_F(TestCSR_Conversions, coo_diagonal_rectangular_neg_offset) { - test_csr_from_diag_coo(10, 5, -1); - test_csr_from_diag_coo(10, 5, -2); - test_csr_from_diag_coo(10, 5, -3); - test_csr_from_diag_coo(10, 5, -4); - test_csr_from_diag_coo(5, 10, -1); - test_csr_from_diag_coo(5, 10, -2); - test_csr_from_diag_coo(5, 10, -3); - test_csr_from_diag_coo(5, 10, -4); - } - - -// template -// CSRMatrix make_test_matrix(int64_t m, int64_t n, T nonzero_prob, uint32_t key = 0) { -// randblas_require(nonzero_prob >= 0); -// randblas_require(nonzero_prob <= 1); -// CSRMatrix A(m, n); -// std::vector actual(m * n); -// RandBLAS::RNGState s(key); -// iid_sparsify_random_dense(m, n, Layout::ColMajor, actual.data(), 1 - nonzero_prob, s); -// dense_to_csr(Layout::ColMajor, actual.data(), 0.0, A); -// return A; -// } - - - template class TestLeftMultiply_CSR : public TestLeftMultiply_Sparse> { CSRMatrix make_test_matrix(int64_t m, int64_t n, T nonzero_prob, uint32_t key = 0) { diff --git a/test/test_matmul_wrappers/DevNotes.md b/test/test_matmul_wrappers/DevNotes.md new file mode 100644 index 00000000..d339d2c5 --- /dev/null +++ b/test/test_matmul_wrappers/DevNotes.md @@ -0,0 +1,23 @@ +# Notes on functionality tested in test_matmul_wrappers + + +## Tests for sketch_sparse + + +## Tests for sketch_symmetric +Right now this falls back on sketch_general, which means it suffices to +test with only DenseSkOp. + +## Tests for sketch_vector +Right now sketch_vector falls back on sketch_general, which means it suffices +to test with only DenseSkOp. +There's an argument to be made for it to directly handle +dispatching of GEMV (for DenseSkOp) and SPMV (for SparseSkOp). +Additional tests would be warranted if we made that change. + +*Note.* We have some infrastructure in place for SPMV, +in the forms of + apply_csc_to_vector_from_left_ki +and + apply_csr_to_vector_from_left_ik. +Those functions basically assume alpha = beta = 1. diff --git a/test/test_matmul_wrappers/test_sketch_sparse.cc b/test/test_matmul_wrappers/test_sketch_sparse.cc new file mode 100644 index 00000000..e69de29b diff --git a/test/test_matmul_wrappers/test_sketch_symmetric.cc b/test/test_matmul_wrappers/test_sketch_symmetric.cc new file mode 100644 index 00000000..0b48fc01 --- /dev/null +++ b/test/test_matmul_wrappers/test_sketch_symmetric.cc @@ -0,0 +1,347 @@ +// Copyright, 2024. See LICENSE for copyright holder information. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) 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. +// +// (3) Neither the name of the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 "RandBLAS/config.h" +#include "RandBLAS/base.hh" +#include "RandBLAS/random_gen.hh" +#include "RandBLAS/dense_skops.hh" +#include "RandBLAS/util.hh" +#include "RandBLAS/sksy.hh" + +using blas::Layout; +using blas::Uplo; +using RandBLAS::DenseDistName; +using RandBLAS::DenseDist; +using RandBLAS::DenseSkOp; +using RandBLAS::RNGState; +using RandBLAS::MajorAxis; + +#include "test/comparison.hh" + +#include +#include + + +template +void random_symmetric_mat(int64_t n, T* A, int64_t lda, STATE s) { + // This function can be interpreted as first generating a random lda-by-lda symmetric matrix + // whose entries in the upper triangle are iid, then symmetrizing that matrix, then + // zeroing out all entries outside the leading principal submatrix of order n. + RandBLAS::fill_dense({lda, lda}, n, n, 0, 0, A, s); + RandBLAS::util::symmetrize(Layout::ColMajor, Uplo::Upper, A, n, lda); + return; +} + +template +blas::Side sketch_symmetric_side( + blas::Side side_skop, blas::Layout layout, int64_t rows_out, int64_t cols_out, + T alpha, const T* A, int64_t lda, SKOP &S, int64_t ro_s, int64_t co_s, T beta, T* B, int64_t ldb +) { + if (side_skop == blas::Side::Left) { + RandBLAS::sketch_symmetric(layout, rows_out, cols_out, alpha, S, ro_s, co_s, A, lda, beta, B, ldb); + return blas::Side::Right; + } else { + RandBLAS::sketch_symmetric(layout, rows_out, cols_out, alpha, A, lda, S, ro_s, co_s, beta, B, ldb); + return blas::Side::Left; + } +} + +RandBLAS::dims64_t dims_of_sketch_symmetric_output(int64_t d, int64_t n, blas::Side side_skop) { + // n : dimensional parameter for the n-x-n symmetric matrices used in tests below. + // d : the embedding dimension for the sketching operator (d < n for sketching and d > n for lifting). + // side : Left if the sketching operator multiplies the n-x-n matrix from the left; Right otherwise. + if (side_skop == blas::Side::Left) { + return {d, n}; + } else { + return {n, d}; + } +} + +class TestSketchSymmetric : public ::testing::Test { + protected: + + template + static void test_same_layouts( + uint32_t seed_a, uint32_t seed_skop, MajorAxis ma, T alpha, int64_t d, int64_t n, int64_t lda, T beta, blas::Side side_skop + ) { + auto [rows_out, cols_out] = dims_of_sketch_symmetric_output(d, n, side_skop); + std::vector A(lda*lda, 0.0); + random_symmetric_mat(n, A.data(), lda, RNGState(seed_a)); + DenseDist D(rows_out, cols_out, DenseDistName::Uniform, ma); + DenseSkOp S(D, seed_skop); + RandBLAS::fill_dense(S); + int64_t lds = (S.layout == Layout::RowMajor) ? cols_out : rows_out; + int64_t ldb = lds; + uint32_t seed_b = seed_a + 42; + std::vector B_actual(d*n); + RandBLAS::fill_dense(D, B_actual.data(), RNGState(seed_b)); + std::vector B_expect(B_actual); + + // Compute the actual output + auto side_a = sketch_symmetric_side(side_skop, S.layout, rows_out, cols_out, alpha, A.data(), lda, S, 0, 0, beta, B_actual.data(), ldb); + // Compute the expected output + blas::symm(S.layout, side_a, Uplo::Upper, rows_out, cols_out, alpha, A.data(), lda, S.buff, lds, beta, B_expect.data(), ldb); + + test::comparison::matrices_approx_equal( + S.layout, blas::Op::NoTrans, rows_out, cols_out, B_actual.data(), ldb, B_expect.data(), ldb, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + return; + } + + template + static void test_opposing_layouts( + uint32_t seed_a, uint32_t seed_skop, MajorAxis ma, T alpha, int64_t d, int64_t n, int64_t lda, T beta, blas::Side side_skop + ) { + auto [rows_out, cols_out] = dims_of_sketch_symmetric_output(d, n, side_skop); + std::vector A(lda*lda, 0.0); + random_symmetric_mat(n, A.data(), lda, RNGState(seed_a)); + DenseDist D(rows_out, cols_out, DenseDistName::Uniform, ma); + DenseSkOp S(D, seed_skop); + RandBLAS::fill_dense(S); + int64_t lds_init, ldb; + Layout layout_B; + if (S.layout == Layout::RowMajor) { + layout_B = Layout::ColMajor; + ldb = rows_out; + lds_init = cols_out; + } else { + layout_B = Layout::RowMajor; + ldb = cols_out; + lds_init = rows_out; + } + uint32_t seed_b = seed_a + 42; + std::vector B_actual(d*n); + RandBLAS::fill_dense(D, B_actual.data(), RNGState(seed_b)); + std::vector B_expect(B_actual); + // Compute the actual output + auto side_a = sketch_symmetric_side(side_skop, layout_B, rows_out, cols_out, alpha, A.data(), lda, S, 0, 0, beta, B_actual.data(), ldb); + // Compute the expected output + std::vector S_flipped(S.buff, S.buff + d*n); + RandBLAS::util::flip_layout(S.layout, rows_out, cols_out, S_flipped, lds_init, ldb); + blas::symm(layout_B, side_a, Uplo::Upper, rows_out, cols_out, alpha, A.data(), lda, S_flipped.data(), ldb, beta, B_expect.data(), ldb); + + test::comparison::matrices_approx_equal( + layout_B, blas::Op::NoTrans, rows_out, cols_out, B_actual.data(), ldb, B_expect.data(), ldb, + __PRETTY_FUNCTION__, __FILE__, __LINE__ + ); + return; + } +}; + + +// MARK: SAME LAYOUTS + +TEST_F(TestSketchSymmetric, left_sketch_10_to_3_same_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Left); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Left); +} + +TEST_F(TestSketchSymmetric, left_lift_same_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Left); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Left); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Left); +} + +TEST_F(TestSketchSymmetric, right_sketch_10_to_3_same_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Right); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Right); +} + +TEST_F(TestSketchSymmetric, right_lift_same_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + test_same_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_same_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Right); + test_same_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Right); + test_same_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Right); +} + + +// MARK: OPPOSING LAYOUTS + +TEST_F(TestSketchSymmetric, left_sketch_10_to_3_opposing_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Left); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Left); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Left); +} + +TEST_F(TestSketchSymmetric, left_lift_opposing_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Left); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Left); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Left); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Left); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Left); +} + +TEST_F(TestSketchSymmetric, right_sketch_10_to_3_opposing_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, 0.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, 0.0, blas::Side::Right); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 10, -1.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Right); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 3, 10, 19, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 3, 10, 19, -1.0, blas::Side::Right); +} + + +TEST_F(TestSketchSymmetric, right_lift_opposing_layouts) { + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, 0.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = 0.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, 0.0, blas::Side::Right); + // LDA=10, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts( 0, 1, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + test_opposing_layouts( 0, 1, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 13, 10, 10, -1.0, blas::Side::Right); + // LDA=19, (seed_a, seed_skop) = (0, 1) then (31, 33), beta = -1.0 + test_opposing_layouts(0, 1, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Right); + test_opposing_layouts(0, 1, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Short, 0.5, 50, 10, 19, -1.0, blas::Side::Right); + test_opposing_layouts(31, 33, MajorAxis::Long, 0.5, 50, 10, 19, -1.0, blas::Side::Right); +} diff --git a/test/test_sketch_vector.cc b/test/test_matmul_wrappers/test_sketch_vector.cc similarity index 94% rename from test/test_sketch_vector.cc rename to test/test_matmul_wrappers/test_sketch_vector.cc index aa661f62..279c4a2f 100644 --- a/test/test_sketch_vector.cc +++ b/test/test_matmul_wrappers/test_sketch_vector.cc @@ -32,9 +32,10 @@ #include "RandBLAS/random_gen.hh" #include "RandBLAS/dense_skops.hh" #include "RandBLAS/util.hh" -#include "comparison.hh" #include "RandBLAS/skge.hh" +#include "test/comparison.hh" + #include #include @@ -180,21 +181,16 @@ class TestSketchVector : public ::testing::Test } }; -//////////////////////////////////////////////////////////////////////// -// -// -// Sketching vectors (vary tall vs wide operators) -// -// -//////////////////////////////////////////////////////////////////////// -TEST_F(TestSketchVector, test_sketch_vec_tallSK) + +TEST_F(TestSketchVector, sketch_vec_tallSK) { for (uint32_t seed : {0, 1, 2}) { test_sketch_vec_tallSK(seed, 1000, 100, 2, 3); test_sketch_vec_tallSK(seed, 1013, 101, 3, 2); } } -TEST_F(TestSketchVector, test_transpose_compatible) + +TEST_F(TestSketchVector, transpose_compatible) { for (uint32_t seed : {0, 1, 2}) { test_transpose_compatible(seed, 100, 1000, 2, 3); @@ -202,7 +198,7 @@ TEST_F(TestSketchVector, test_transpose_compatible) } } -TEST_F(TestSketchVector, test_sketch_vec_wide) +TEST_F(TestSketchVector, sketch_vec_wide) { for (uint32_t seed : {0, 1, 2}) { test_sketch_vec_wide(seed, 100, 1000, 2, 3); @@ -210,10 +206,10 @@ TEST_F(TestSketchVector, test_sketch_vec_wide) } } -TEST_F(TestSketchVector, test_apply_transposed_to_vector) +TEST_F(TestSketchVector, apply_transposed_to_vector) { for (uint32_t seed : {0, 1, 2}) { test_apply_transposed_to_vector(seed, 100, 1000, 2, 3); test_apply_transposed_to_vector(seed, 101, 1013, 3, 2); } -} \ No newline at end of file +} diff --git a/test/test_sparse_data/test_right_multiply.hh b/test/test_sparse_data/test_right_multiply.hh deleted file mode 100644 index 141c5c1e..00000000 --- a/test/test_sparse_data/test_right_multiply.hh +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright, 2024. See LICENSE for copyright holder information. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// (1) Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// (2) 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. -// -// (3) Neither the name of the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 "../linop_common.hh" -#include "common.hh" -#include - -using namespace test::sparse_data::common; -using namespace test::linop_common; -using blas::Layout; - - -template -class TestRightMultiply_Sparse : public ::testing::Test -{ - // C = alpha * opB(B) @ opA(submat(A)) + beta * C - // - // In what follows, "self" refers to A and "other" refers to B. - // - using T = typename SpMat::scalar_t; - protected: - virtual void SetUp(){}; - virtual void TearDown(){}; - - virtual SpMat make_test_matrix(int64_t m, int64_t n, T nonzero_prob, uint32_t key = 0) = 0; - - void multiply_eye(uint32_t key, int64_t m, int64_t n, Layout layout, T p) { - auto A = this->make_test_matrix(m, n, p, key); - test_right_apply_submatrix_to_eye(1.0, A, m, n, 0, 0, layout, 0.0, 0); - } - - void alpha_beta(uint32_t key, T alpha, T beta, int64_t m, int64_t n, Layout layout, T p) { - auto A = this->make_test_matrix(m, n, p, key); - test_right_apply_submatrix_to_eye(alpha, A, m, n, 0, 0, layout, beta, 0); - } - - void transpose_self(uint32_t key, int64_t m, int64_t n, Layout layout, T p) { - auto A = this->make_test_matrix(m, n, p, key); - test_right_apply_tranpose_to_eye(A, layout, 0); - } - - void submatrix_self( - uint32_t key, // key for RNG that generates sparse A - int64_t d, // cols in A - int64_t m, // rows in A, columns in B = eye(m) - int64_t d0, // cols in A0 - int64_t m0, // rows in A0 - int64_t A_ro, // row offset for A in A0 - int64_t A_co, // col offset for A in A0 - Layout layout, // layout of dense matrix input and output - T p - ) { - auto A0 = this->make_test_matrix(m0, d0, p, key); - test_right_apply_submatrix_to_eye(1.0, A0, m, d, A_ro, A_co, layout, 0.0, 0); - } - - void transpose_other( - uint32_t key, // key for RNG that generates sparse A - int64_t d, // cols in A - int64_t n, // rows in A and B - int64_t m, // cols in B - Layout layout, // layout of dense matrix input and output - T p - ) { - auto A = this->make_test_matrix(n, d, p, key); - test_right_apply_to_transposed(A, m, layout, 0); - } - - void submatrix_other( - uint32_t key, // key for RNG that generates sparse A - int64_t d, // cols in A - int64_t m, // rows in B - int64_t n, // rows in A, columns in B - int64_t m0, // rows in B0 - int64_t n0, // cols in B0 - int64_t B_ro, // row offset for B in B0 - int64_t B_co, // col offset for B in B0 - Layout layout, // layout of dense matrix input and output - T p - ) { - auto A = this->make_test_matrix(n, d, p, key); - test_right_apply_to_submatrix(A, m, m0, n0, B_ro, B_co, layout, 0); - } -}; \ No newline at end of file