BallisticLA / RandBLAS

A header-only C++ library for sketching in randomized linear algebra
https://randblas.readthedocs.io/en/latest/
Other
66 stars 4 forks source link

Viable API for dense and sparse sketching #16

Closed rileyjmurray closed 1 year ago

rileyjmurray commented 1 year ago

This PR contains a complete rewrite of RandBLAS' API. The important functionality is declared in three headers: base.hh, sparse.hh, and dense.hh, which define functionality in the RandBLAS::base, RandBLAS::sparse, and RandBLAS::dense namespaces. Here's a breakdown of what can be found in each of these headers

RandBLAS/include/base.hh

This defines functionality for the counter-based random number generators (CBRNGs) that RandBLAS uses for basic random number generation.

The state of a CBRNG is specified by a "counter" and a "key". Conceptually these can be thought of as 128-bit or 256-bit integers. The key specifies the stream of numbers that a CBRNG will produce. One accesses random numbers within the stream by specifying the value of the counter.

From an implementation standpoint, there is a bit of complexity in representing counters and keys. This is because the 128-bit / 256-bit integers will actually be represented by arrays of simpler numeric types. The functionality defined in this header file tries to hide that complexity. It also hides the fact that we're relying on Random123 for implementation of our CBRNGs.

Important definitions

RNGName is a character-based enumeration used to specify the type of CBRNG.

RNGState is a type of struct for carrying counters, keys, and an RNGName.

Random123_RNGState<T_gen> is a type of struct that carries counters and keys for the CBRNG T_gen.

RandBLAS/include/sparse.hh

This defines the API for constructing and using sparse sketching operators. The function lskges reads as "left-sketch a general matrix, with a sparse sketching operator". Important definitions are below.

SparseDistName is a character-based enumeration that specifies qualitative information in a sparse sketching operator. It's used to indicate whether one wants to work with "short-axis-sparse operators" (SASOs) or "long-axis-sparse operators" (LASOs), in the sense of the RandLAPACK book. Right now, sparse.cc only implements SASOs, and the SASOs must have more columns than rows. Note that any sketching operator with more columns than rows can only be used to sketch from the left.

SparseDist is a type of struct that specifies the distribution over sparse sketching operators. SparseDist.family specifies whether we're working with SASOs or LASOs, while SparseDist.n_rows and SparseDist.n_col specify the dimensions of the sketching operator. SparseDist.vec_nnz is the number of nonzeros in each short-axis vector (for SASOs) or long-axis vector (for LASOs).

SparseSkOp is a type of struct for representing a specific sparse sketching operator. The distribution from which the sketching operator is sampled is specified in SparseSkOp.dist. The state of the CBRNG used to define the sketching operator is given in SparseSkOp.seed_state. After a program defines a SparseSkOp object S, the next time it needs to seed a CBRNG it should do so with S.next_state.

RandBLAS/include/dense.hh

This defines the API for constructing and using dense sketching operators. The function lskge3 reads as "left-sketch a general matrix, with a BLAS 3 operation". This header file defines similar enumerations and structs as sparse.hh. Differences are highlighted below.

DenseDistName is a character-based enumeration analogous to SparseDistName. The options are ...

Of these, only Gaussian and Uniform are implemented.

DenseDist is a type of struct that's analogous to SparseDist. The only difference between these two structs is that the former lacks the .vec_nnz field of the latter.

DenseSkOp is a type of struct that's analogous to SparseSkOp. One difference is that it features an attribute DenseDist.layout which says whether the buffer that represents a sketching operator should be interpreted in row-major or column-major order. Implementations in dense.cc also allow lazy instantiation of the memory that's needed for a DenseSkOp object. The behavior of lazy memory allocation or lazy random number generation are governed by DenseSkOp.filled, DenseSkOp.persistent, and DenseSkOp.buff.