-
Notifications
You must be signed in to change notification settings - Fork 191
Sparse algebra support with OOP API #760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 41 commits
5d0ff52
b1dcbf6
c63c0dd
d940ebd
72481be
a7cb6be
d48dde5
93c5c3a
6241ca3
0f2ee3b
c1a85c4
2606691
c34ac70
3126d16
c0bbabb
2c2431d
d64a045
6786859
d926581
22b477c
d165b8b
8f72559
87c867a
59fe24e
43ab25f
838b159
c74ad09
14bfef9
23be647
1d9dabc
8278f38
87bfd10
3fa318b
4aae5b4
14e9be0
6e679f5
91e619a
7117d16
5b0aadf
c0438f0
e18b3fc
79534b3
5f35174
b3de016
181760b
147a5c9
c832eeb
22a70b1
9223345
827a1ef
21a8547
da9f171
1cbb982
a3c155a
2fb4e83
e78c026
f25e07d
c7035c9
82950e0
93c1e55
c1f30f6
4c16f4a
944212d
dd4dbd8
2d7701e
98a564b
b65d933
a94272e
e4c1f58
14e2c17
b53eca2
65e3fcb
2f56cd4
941de3a
575c426
db73fdc
697afa2
c97e665
ac100a1
9879a9c
dde88a7
6ae038b
3596f3f
a21d1e8
c8d94a3
82dbe02
a8aa247
66b0ce2
4b41aa1
ab112e6
bc0021b
7279461
a4d9306
cd30636
b68b4c8
62c702b
581d215
59d33f0
beafb3c
7a26174
3746331
3dfcecd
89a993e
ae02481
680d35d
5ceef37
7e45901
22cd23e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
--- | ||
title: sparse | ||
--- | ||
|
||
# The `stdlib_sparse` module | ||
|
||
[TOC] | ||
|
||
## Introduction | ||
|
||
The `stdlib_sparse` module provides several derived types defining known sparse matrix data structures. It also provides basic sparse kernels such as sparse matrix vector and conversion between matrix types. | ||
|
||
## Derived types provided | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --> | ||
### The `sparse_type` abstract derived type | ||
#### Status | ||
|
||
Experimental | ||
|
||
#### Description | ||
The `sparse_type` is defined as an abstract derived type holding the basic common meta data needed to define a sparse matrix. All other sparse types falvors are derived from the `sparse_type`. | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```Fortran | ||
type, public, abstract :: sparse_type | ||
integer :: nrows !> number of rows | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
integer :: ncols !> number of columns | ||
integer :: nnz !> number of non-zero values | ||
integer :: storage !> assumed storage symmetry | ||
integer :: base !> index base = 0 for (C) or 1 (Fortran) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the option to use either C or Fortran indexing. My only concern is: what is the advantage of hard-coding it into the data structure? Shouldn't the internal representation be unique (e.g., 1-based)? My fear is that handling two options for the internal representation would be too complicate when more complex functions are implemented. Instead, wouldn't it be better to add this option only to the ! Get matrix value at (i,j) == (1,4)
val = matrix%at(0, 3, zero_based=.true.)
! Set matrix row from data and 1-based column indices
call matrix%set_row(row=i, data = [1,2,3,4,5], columns=[3,8,35,1,3], zero_based=.false.) |
||
end type | ||
jvdp1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
The storage integer label should be assigned from the module's internal enumerator containing the following three enums: | ||
|
||
```Fortran | ||
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>` 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: | ||
perazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
$$ M = \begin{bmatrix} | ||
9 & 0 & 0 & 0 & -3 \\ | ||
4 & 7 & 0 & 0 & 0 \\ | ||
0 & 8 & -1 & 8 & 0 \\ | ||
4 & 0 & 5 & 6 & 0 \\ | ||
\end{bmatrix} $$ | ||
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --> | ||
### `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`. | ||
|
||
```Fortran | ||
type(COO_sp) :: COO | ||
call COO%malloc(4,5,10) | ||
perazz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 index by compressing the row indeces with a counter pointer `rowptr` enabling to know the first and last non-zero colum index `col` of the given row. | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```Fortran | ||
type(CSR_sp) :: 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 colum, thus an index counter is given by `colptr` which enables accessing the start and ending rows of a given colum in the `row` index table. | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```Fortran | ||
type(CSC_sp) :: 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 the data in a dense matrix of $nrows \times K$ in column major order. By imposing a constant number of zeros per row $K$, this format will incure in additional zeros being stored, but it enables efficient vectorization as memory acces are carried out by constant sized strides. | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```Fortran | ||
type(ELL_sp) :: 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 [here](https://arxiv.org/pdf/1307.6209v1) | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --> | ||
## `spmv` - Sparse Matrix-Vector product | ||
|
||
### Status | ||
|
||
Experimental | ||
|
||
### Description | ||
|
||
Provide sparse matrix-vector product kernels for the current supported sparse matrix types. | ||
|
||
$$y=\alpha*M*x+\beta*y$$ | ||
|
||
### Syntax | ||
|
||
`call ` [[stdlib_sparse_spmv(module):spmv(interface)]] `(matrix,vec_x,vec_y [,alpha,beta])` | ||
|
||
### Arguments | ||
|
||
`matrix`, `intent(in)`: Shall be a `real` or `complex` sparse type matrix. | ||
|
||
`vec_x`, `intent(in)`: Shall be a rank-1 or rank-2 array of `real` or `complex` type array. | ||
|
||
`vec_y`, `intent(inout)`: Shall be a rank-1 or rank-2 array of `real` or `complex` type array. | ||
|
||
`alpha`, `intent(in)`, `optional` : Shall be a scalar value of the same type as `vec_x`. Default value `alpha=1`. | ||
|
||
`beta`, `intent(in)`, `optional` : Shall be a scalar value of the same type as `vec_x`. Default value `beta=0`. | ||
|
||
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --> | ||
## `sparse_conversion` - Sparse matrix to matrix conversions | ||
|
||
### Status | ||
|
||
Experimental | ||
|
||
### Description | ||
|
||
This module provides facility functions for converting between storage formats. | ||
jvdp1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Syntax | ||
|
||
`call ` [[stdlib_sparse_conversion(module):coo2ordered(interface)]] `(coo)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like having subroutines to maximize performance and avoid the issues with function assignment. I wonder if we should also provide 'clean' function interfaces to generate new objects? Just giving it a thought. They would probably pair better with overloaded operators for example, but also in other ways. For example, one could have: type(COO_sp) :: mat
[...]
! Convert to CSR using interface CSR_sp and then do stuff
call my_CSR_kernel( CSR_sp(mat) , x , y, z ) This would require to add an interface interface CSR_sp
module procedure coo2csr_fun
end interface
elemental type(CSR_sp) function coo2csr_fun(COO) result(CSR)
type(COO_sp), intent(in) :: COO
call coo2csr( COO, CSR )
end function |
||
|
||
### Arguments | ||
|
||
`COO`, `intent(inout)`: Shall be any `COO` type. The same object will be returned with the arrays reallocated to the correct size after removing duplicates. | ||
|
||
`sort_data`, `logical(in)`, `optional`:: Boolean optional to determine whether to sort the data in the COO graph while sorting the index array, defult `.false.`. | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Syntax | ||
|
||
`call ` [[stdlib_sparse_conversion(module):dense2coo(interface)]] `(dense,coo)` | ||
|
||
### Arguments | ||
|
||
`dense`, `intent(in)`: Shall be a rank-2 array of `real` or `complex` type. | ||
|
||
`coo`, `intent(inout)`: Shall be a `COO` type of `real` or `complex` type. | ||
jalvesz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Syntax | ||
|
||
`call ` [[stdlib_sparse_conversion(module):coo2dense(interface)]] `(coo,dense)` | ||
|
||
### Arguments | ||
|
||
`coo`, `intent(in)`: Shall be a `COO` type of `real` or `complex` type. | ||
|
||
`dense`, `intent(inout)`: Shall be a rank-2 array of `real` or `complex` type. | ||
|
||
### Syntax | ||
|
||
`call ` [[stdlib_sparse_conversion(module):coo2csr(interface)]] `(coo,csr)` | ||
|
||
### Arguments | ||
|
||
`coo`, `intent(in)`: Shall be a `COO` type of `real` or `complex` type. | ||
|
||
`csr`, `intent(inout)`: Shall be a `CSR` type of `real` or `complex` type. | ||
|
||
### Syntax | ||
|
||
`call ` [[stdlib_sparse_conversion(module):csr2coo(interface)]] `(csr,coo)` | ||
|
||
### Arguments | ||
|
||
`csr`, `intent(in)`: Shall be a `CSR` type of `real` or `complex` type. | ||
|
||
`coo`, `intent(inout)`: Shall be a `COO` type of `real` or `complex` type. | ||
|
||
### Syntax | ||
|
||
`call ` [[stdlib_sparse_conversion(module):csr2sellc(interface)]] `(csr,sellc[,chunk])` | ||
|
||
### Arguments | ||
|
||
`csr`, `intent(in)`: Shall be a `CSR` type of `real` or `complex` type. | ||
|
||
`sellc`, `intent(inout)`: Shall be a `SELLC` type of `real` or `complex` type. | ||
|
||
`chunk`, `intent(in)`, `optional`: chunk size for the Sliced ELLPACK format. | ||
|
||
## Example | ||
```fortran | ||
{!example/linalg/example_sparse_spmv.f90!} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
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) :: COO | ||
type(CSR_dp) :: 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 | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
!! public API | ||
module stdlib_sparse | ||
use stdlib_sparse_kinds | ||
use stdlib_sparse_spmv | ||
use stdlib_sparse_conversion | ||
end module stdlib_sparse |
Uh oh!
There was an error while loading. Please reload this page.