sparse

The stdlib_sparse module

Introduction

The 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 matrix derived types

The sparse_type abstract derived type

Status

Experimental

Description

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 format

Status

Experimental

Description

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 format

Status

Experimental

Description

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 format

Status

Experimental

Description

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 format

Status

Experimental

Description

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 format

Status

Experimental

Description

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 accessors

Status

Experimental

Description

Type-bound procedures to enable adding data in a sparse matrix.

Syntax

call matrix%add(i,j,v) or call matrix%add(i(:),j(:),v(:,:))

Arguments

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 accessors

Status

Experimental

Description

Type-bound procedures to enable requesting data from a sparse matrix.

Syntax

v = matrix%at(i,j)

Arguments

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.

Example

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(:,:)
    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(5,5) , source = 0._dp)
    do i = 0, 3
        dense(1+i:2+i,1+i:2+i) = dense(1+i:2+i,1+i:2+i) + mat
    end do

    print *, 'Original Matrix'
    do j = 1 , 5
        print '(5f8.1)',dense(j,:)
    end do

    ! Initialize CSR data and reset dense reference matrix
    call dense2coo(dense,COO)
    call coo2csr(COO,CSR)
    CSR%data = 0._dp
    dense = 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)
        print '(A,I2)', 'Add block :', i+1
        do j = 1 , 5
            print '(5f8.1)',dense(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 product

Status

Experimental

Description

Provide sparse matrix-vector product kernels for the current supported sparse matrix types.

Syntax

call spmv (matrix,vec_x,vec_y [,alpha,beta,op])

Arguments

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

Sparse matrix to matrix conversions

Status

Experimental

Description

This module provides facility functions for converting between storage formats.

Syntax

call stdlib_sparse_conversion (coo[,sort_data])

Arguments

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.

Syntax

call from_ijv (sparse,row,col[,data,nrows,ncols,num_nz_rows,chunk])

Arguments

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.

Example

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,x,3f8.1)', ELL%index(i,:) , ELL%data(i,:)
    end do

end program example_sparse_from_ijv

Syntax

call diag (matrix,diagonal)

Arguments

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.

Note

If the diagonal array has not been previously allocated, the diag subroutine will allocate it using the nrows of the matrix.

Syntax

call dense2coo (dense,coo)

Arguments

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.

Syntax

call coo2dense (coo,dense)

Arguments

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.

Syntax

call coo2csr (coo,csr)

Arguments

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.

Syntax

call coo2csc (coo,csc)

Arguments

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.

Syntax

call csr2coo (csr,coo)

Arguments

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.

Syntax

call csr2sellc (csr,sellc[,chunk])

Arguments

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.

Syntax

call csr2sellc (csr,ell[,num_nz_rows])

Arguments

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.

Syntax

call csc2coo (csc,coo)

Arguments

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.

Example

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