stdlib_sparse moduleThe stdlib_sparse module provides derived types for standard sparse matrix data structures. It also provides math kernels such as sparse matrix-vector product and conversion between matrix types.
sparse_type abstract derived typeExperimental
The parent sparse_type is as an abstract derived type holding the basic common meta data needed to define a sparse matrix, as well as shared APIs. All sparse matrix flavors are extended from the sparse_type.
type, public, abstract :: sparse_type
integer :: nrows !! number of rows
integer :: ncols !! number of columns
integer :: nnz !! number of non-zero values
integer :: storage !! assumed storage symmetry
end type
The storage integer label should be assigned from the module's internal enumerator containing the following three enums:
enum, bind(C)
enumerator :: sparse_full !! Full Sparse matrix (no symmetry considerations)
enumerator :: sparse_lower !! Symmetric Sparse matrix with triangular inferior storage
enumerator :: sparse_upper !! Symmetric Sparse matrix with triangular supperior storage
end enum
In the following, all sparse kinds will be presented in two main flavors: a data-less type <matrix>_type useful for topological graph operations. And real/complex valued types <matrix>_<kind>_type containing the data buffer for the matrix values. The following rectangular matrix will be used to showcase how each sparse matrix holds the data internally:
COO: The COOrdinates compressed sparse formatExperimental
The COO, triplet or ijv format defines all non-zero elements of the matrix by explicitly allocating the i,j index and the value of the matrix. While some implementations use separate row and col arrays for the index, here we use a 2D array in order to promote fast memory acces to ij.
type(COO_sp_type) :: COO
call COO%malloc(4,5,10)
COO%data(:) = real([9,-3,4,7,8,-1,8,4,5,6])
COO%index(1:2,1) = [1,1]
COO%index(1:2,2) = [1,5]
COO%index(1:2,3) = [2,1]
COO%index(1:2,4) = [2,2]
COO%index(1:2,5) = [3,2]
COO%index(1:2,6) = [3,3]
COO%index(1:2,7) = [3,4]
COO%index(1:2,8) = [4,1]
COO%index(1:2,9) = [4,3]
COO%index(1:2,10) = [4,4]
CSR: The Compressed Sparse Row or Yale formatExperimental
The Compressed Sparse Row or Yale format CSR stores the matrix structure by compressing the row indices with a counter pointer rowptr enabling to know the first and last non-zero column index col of the given row.
type(CSR_sp_type) :: CSR
call CSR%malloc(4,5,10)
CSR%data(:) = real([9,-3,4,7,8,-1,8,4,5,6])
CSR%col(:) = [1,5,1,2,2,3,4,1,3,4]
CSR%rowptr(:) = [1,3,5,8,11]
CSC: The Compressed Sparse Column formatExperimental
The Compressed Sparse Colum CSC is similar to the CSR format but values are accesed first by column, thus an index counter is given by colptr which enables to know the first and last non-zero row index of a given colum.
type(CSC_sp_type) :: CSC
call CSC%malloc(4,5,10)
CSC%data(:) = real([9,4,4,7,8,-1,5,8,6,-3])
CSC%row(:) = [1,2,4,2,3,3,4,3,4,1]
CSC%colptr(:) = [1,4,6,8,10,11]
ELLPACK: ELL-pack storage formatExperimental
The ELL format stores data in a dense matrix of $nrows \times K$ in column major order. By imposing a constant number of elements per row $K$, this format will incur in additional zeros being stored, but it enables efficient vectorization as memory acces is carried out by constant sized strides.
type(ELL_sp_type) :: ELL
call ELL%malloc(num_rows=4,num_cols=5,num_nz_row=3)
ELL%data(1,1:3) = real([9,-3,0])
ELL%data(2,1:3) = real([4,7,0])
ELL%data(3,1:3) = real([8,-1,8])
ELL%data(4,1:3) = real([4,5,6])
ELL%index(1,1:3) = [1,5,0]
ELL%index(2,1:3) = [1,2,0]
ELL%index(3,1:3) = [2,3,4]
ELL%index(4,1:3) = [1,3,4]
SELL-C: The Sliced ELLPACK with Constant blocks formatExperimental
The Sliced ELLPACK format SELLC is a variation of the ELLPACK format. This modification reduces the storage size compared to the ELLPACK format but maintaining its efficient data access scheme. It can be seen as an intermediate format between CSR and ELLPACK. For more details read the reference
add- sparse matrix data accessorsExperimental
Type-bound procedures to enable adding data in a sparse matrix.
call matrix%add(i,j,v) or
call matrix%add(i(:),j(:),v(:,:))
i: Shall be an integer value or rank-1 array. It is an intent(in) argument.
j: Shall be an integer value or rank-1 array. It is an intent(in) argument.
v: Shall be a real or complex value or rank-2 array. The type shall be in accordance to the declared sparse matrix object. It is an intent(in) argument.
at- sparse matrix data accessorsExperimental
Type-bound procedures to enable requesting data from a sparse matrix.
v = matrix%at(i,j)
i : Shall be an integer value. It is an intent(in) argument.
j : Shall be an integer value. It is an intent(in) argument.
v : Shall be a real or complex value in accordance to the declared sparse matrix object. If the ij tuple is within the sparse pattern, v contains the value in the data buffer. If the ij tuple is outside the sparse pattern, v is equal 0. If the ij tuple is outside the matrix pattern (nrows,ncols), v is NaN.
program example_sparse_data_accessors
use stdlib_linalg_constants, only: dp
use stdlib_sparse
implicit none
real(dp) :: mat(2, 2)
real(dp), allocatable :: dense_matrix(:, :)
type(CSR_dp_type) :: CSR
type(COO_dp_type) :: COO
integer :: i, j, locdof(2)
! Initial data
mat(:, 1) = [1._dp, 2._dp]
mat(:, 2) = [2._dp, 1._dp]
allocate (dense_matrix(5, 5), source=0._dp)
do i = 0, 3
dense_matrix(1 + i:2 + i, 1 + i:2 + i) = dense_matrix(1 + i:2 + i, 1 + i:2 + i) + mat
end do
print *, 'Original Matrix'
do j = 1, 5
print '(5f8.1)', dense_matrix(j, :)
end do
! Initialize CSR data and reset dense reference matrix
call dense2coo(dense_matrix, COO)
call coo2csr(COO, CSR)
CSR%data = 0._dp
dense_matrix = 0._dp
! Iteratively add blocks of data
do i = 0, 3
locdof(1:2) = [1 + i, 2 + i]
call CSR%add(locdof, locdof, mat)
! lets print a dense view of every step
call csr2dense(CSR, dense_matrix)
print '(A,I2)', 'Add block :', i + 1
do j = 1, 5
print '(5f8.1)', dense_matrix(j, :)
end do
end do
! Request values from the matrix
print *, ''
print *, 'within sparse pattern :', CSR%at(2, 1)
print *, 'outside sparse pattern :', CSR%at(5, 2)
print *, 'outside matrix pattern :', CSR%at(7, 7)
end program example_sparse_data_accessors
spmv - Sparse Matrix-Vector productExperimental
Provide sparse matrix-vector product kernels for the current supported sparse matrix types.
call spmv (matrix,vec_x,vec_y [,alpha,beta,op])
matrix: Shall be a real or complex sparse type matrix. It is an intent(in) argument.
vec_x: Shall be a rank-1 or rank-2 array of real or complex type array. It is an intent(in) argument.
vec_y: Shall be a rank-1 or rank-2 array of real or complex type array. . It is an intent(inout) argument.
alpha, optional : Shall be a scalar value of the same type as vec_x. Default value alpha=1. It is an intent(in) argument.
beta, optional : Shall be a scalar value of the same type as vec_x. Default value beta=0. It is an intent(in) argument.
op, optional: In-place operator identifier. Shall be a character(1) argument. It can have any of the following values: N: no transpose, T: transpose, H: hermitian or complex transpose. These values are provided as constants by the stdlib_sparse module: sparse_op_none, sparse_op_transpose, sparse_op_hermitian
Experimental
This module provides facility functions for converting between storage formats.
call stdlib_sparse_conversion (coo[,sort_data])
COO : Shall be any COO type. The same object will be returned with the arrays reallocated to the correct size after removing duplicates. It is an intent(inout) argument.
sort_data, optional : Shall be a logical argument to determine whether data in the COO graph should be sorted while sorting the index array, default .false.. It is an intent(in) argument.
call from_ijv (sparse,row,col[,data,nrows,ncols,num_nz_rows,chunk])
sparse : Shall be a COO, CSR, ELL or SELLC type. The graph object will be returned with a canonical shape after sorting and removing duplicates from the (row,col,data) triplet. If the graph is COO_type no data buffer is allowed. It is an intent(inout) argument.
row : rows index array. It is an intent(in) argument.
col : columns index array. It is an intent(in) argument.
data, optional: real or complex data array. It is an intent(in) argument.
nrows, optional: number of rows, if not given it will be computed from the row array. It is an intent(in) argument.
ncols, optional: number of columns, if not given it will be computed from the col array. It is an intent(in) argument.
num_nz_rows, optional: number of non zeros per row, only valid in the case of an ELL matrix, by default it will computed from the largest row. It is an intent(in) argument.
chunk, optional: chunk size, only valid in the case of a SELLC matrix, by default it will be taken from the SELLC default attribute chunk size. It is an intent(in) argument.
program example_sparse_from_ijv
use stdlib_linalg_constants, only: dp
use stdlib_sparse
implicit none
integer :: row(10), col(10)
real(dp) :: data(10)
type(COO_dp_type) :: COO
type(CSR_dp_type) :: CSR
type(ELL_dp_type) :: ELL
integer :: i, j
! Initial data
row = [1,1,2,2,3,3,3,4,4,4]
col = [1,5,1,2,2,3,4,1,3,4]
data = real([9,-3,4,7,8,-1,8,4,5,6] , kind = dp )
! Create a COO matrix from triplet
call from_ijv(COO,row,col,data)
print *, 'COO'
print *, ' i, j, v'
do i = 1, COO%nnz
print '(2I4,f8.1)', COO%index(:,i), COO%data(i)
end do
! Create a CSR matrix from triplet
call from_ijv(CSR,row,col,data)
print *, 'CSR'
print '(A,5I8)', 'rowptr :', CSR%rowptr
print '(A,10I8)', 'col :', CSR%col
print '(A,10f8.1)', 'data :', CSR%data
! Create an ELL matrix from triplet
call from_ijv(ELL,row,col,data)
print *, 'ELL'
print *, ' index | data'
do i = 1, ELL%nrows
print '(3I4,1x,3f8.1)', ELL%index(i,:) , ELL%data(i,:)
end do
end program example_sparse_from_ijv
call diag (matrix,diagonal)
matrix : Shall be a dense, COO, CSR or ELL type. It is an intent(in) argument.
diagonal : A rank-1 array of the same type as the matrix. It is an intent(inout) and allocatable argument.
If the diagonal array has not been previously allocated, the diag subroutine will allocate it using the nrows of the matrix.
call dense2coo (dense,coo)
dense : Shall be a rank-2 array of real or complex type. It is an intent(in) argument.
coo : Shall be a COO type of real or complex type. It is an intent(out) argument.
call coo2dense (coo,dense)
coo : Shall be a COO type of real or complex type. It is an intent(in) argument.
dense : Shall be a rank-2 array of real or complex type. It is an intent(out) argument.
call coo2csr (coo,csr)
coo : Shall be a COO type of real or complex type. It is an intent(in) argument.
csr : Shall be a CSR type of real or complex type. It is an intent(out) argument.
call coo2csc (coo,csc)
coo : Shall be a COO type of real or complex type. It is an intent(in) argument.
csc : Shall be a CSC type of real or complex type. It is an intent(out) argument.
call csr2coo (csr,coo)
csr : Shall be a CSR type of real or complex type. It is an intent(in) argument.
coo : Shall be a COO type of real or complex type. It is an intent(out) argument.
call csr2sellc (csr,sellc[,chunk])
csr : Shall be a CSR type of real or complex type. It is an intent(in) argument.
sellc : Shall be a SELLC type of real or complex type. It is an intent(out) argument.
chunk, optional: chunk size for the Sliced ELLPACK format. It is an intent(in) argument.
call csr2sellc (csr,ell[,num_nz_rows])
csr : Shall be a CSR type of real or complex type. It is an intent(in) argument.
ell : Shall be a ELL type of real or complex type. It is an intent(out) argument.
num_nz_rows, optional: number of non zeros per row. If not give, it will correspond to the size of the longest row in the CSR matrix. It is an intent(in) argument.
call csc2coo (csc,coo)
csc : Shall be a CSC type of real or complex type. It is an intent(in) argument.
coo : Shall be a COO type of real or complex type. It is an intent(out) argument.
program example_sparse_spmv
use stdlib_linalg_constants, only: dp
use stdlib_sparse
implicit none
integer, parameter :: m = 4, n = 2
real(dp) :: A(m,n), x(n)
real(dp) :: y_dense(m), y_coo(m), y_csr(m)
real(dp) :: alpha, beta
type(COO_dp_type) :: COO
type(CSR_dp_type) :: CSR
call random_number(A)
! Convert from dense to COO and CSR matrices
call dense2coo( A , COO )
call coo2csr( COO , CSR )
! Initialize vectors
x = 1._dp
y_dense = 2._dp
y_coo = y_dense
y_csr = y_dense
! Perform matrix-vector product
alpha = 3._dp; beta = 2._dp
y_dense = alpha * matmul(A,x) + beta * y_dense
call spmv( COO , x , y_coo , alpha = alpha, beta = beta )
call spmv( CSR , x , y_csr , alpha = alpha, beta = beta )
print *, 'dense :', y_dense
print *, 'coo :', y_coo
print *, 'csr :', y_csr
end program example_sparse_spmv