Skip to content
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

Sparse algebra support with OOP API #760

Open
wants to merge 92 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 79 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
5d0ff52
sparse API 1st commit
jalvesz Jan 9, 2024
b1dcbf6
Merge branch 'fortran-lang:master' into sparse
jalvesz Jan 10, 2024
c63c0dd
cmake build
jalvesz Jan 10, 2024
d940ebd
Merge branch 'sparse' of https://github.com/jalvesz/stdlib into sparse
jalvesz Jan 10, 2024
72481be
add data accessors set and get
jalvesz Jan 11, 2024
a7cb6be
fix typo
jalvesz Jan 11, 2024
d48dde5
change ij accessor as subroutine
jalvesz Jan 12, 2024
93c5c3a
Merge branch 'fortran-lang:master' into sparse
jalvesz Jan 28, 2024
6241ca3
fix missing i,j integer declaration
jalvesz Jan 29, 2024
0f2ee3b
Merge branch 'fortran-lang:master' into sparse
jalvesz Feb 9, 2024
c1a85c4
Merge branch 'fortran-lang:master' into sparse
jalvesz Mar 31, 2024
2606691
Merge branch 'fortran-lang:master' into sparse
jalvesz Apr 9, 2024
c34ac70
Merge branch 'fortran-lang:master' into sparse
jalvesz Apr 23, 2024
3126d16
Merge branch 'fortran-lang:master' into sparse
jalvesz Apr 27, 2024
c0bbabb
Merge branch 'fortran-lang:master' into sparse
jalvesz May 3, 2024
2c2431d
add comments and change _t for _type
jalvesz May 3, 2024
d64a045
revert matvec convention to (matrix,x,y)
jalvesz May 3, 2024
6786859
Merge branch 'fortran-lang:master' into sparse
jalvesz May 11, 2024
d926581
Merge branch 'fortran-lang:master' into sparse
jalvesz May 18, 2024
22b477c
Merge branch 'sparse' of https://github.com/jalvesz/stdlib into sparse
jalvesz May 18, 2024
d165b8b
upgrade sparse support with SELLC format and more tests add suffix fo…
jalvesz May 18, 2024
8f72559
include alpha and beta parameters in sparse matvec
jalvesz May 21, 2024
87c867a
wrong ellpack size
jalvesz May 25, 2024
59fe24e
start sparse specs
jalvesz May 25, 2024
43ab25f
fix module name
jalvesz May 25, 2024
838b159
include reference
jalvesz May 25, 2024
c74ad09
add matvec specs
jalvesz May 26, 2024
14bfef9
start adding conversions specs
jalvesz May 26, 2024
23be647
breaking change: rename matvec to spmv for consistency with stdlib bl…
jalvesz May 27, 2024
1d9dabc
Merge branch 'fortran-lang:master' into sparse
jalvesz May 27, 2024
8278f38
Merge branch 'fortran-lang:master' into sparse
jalvesz May 28, 2024
87bfd10
Merge branch 'fortran-lang:master' into sparse
jalvesz Jun 1, 2024
3fa318b
Merge branch 'fortran-lang:master' into sparse
jalvesz Jun 7, 2024
4aae5b4
Merge branch 'fortran-lang:master' into sparse
jalvesz Jun 10, 2024
14e9be0
Merge branch 'fortran-lang:master' into sparse
jalvesz Jun 14, 2024
6e679f5
change storage identifier names
jalvesz Jun 14, 2024
91e619a
add example with conversion and spmv
jalvesz Jun 14, 2024
7117d16
fix example path
jalvesz Jun 14, 2024
5b0aadf
make example runnable with cmake
jalvesz Jun 15, 2024
c0438f0
update spec
jalvesz Jun 15, 2024
e18b3fc
add coo2ordered procedure
jalvesz Jun 15, 2024
79534b3
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
5f35174
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
b3de016
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
181760b
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
147a5c9
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
c832eeb
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
22a70b1
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
9223345
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
827a1ef
Update doc/specs/stdlib_sparse.md
jalvesz Jun 19, 2024
21a8547
change get_value to function and add NaN if out of bounds
jalvesz Jun 19, 2024
da9f171
change is ordered by is_sorted
jalvesz Jun 19, 2024
1cbb982
remove unused base attribute
jalvesz Jun 19, 2024
a3c155a
forgotten base attribute
jalvesz Jun 20, 2024
2fb4e83
make set/get non_overridable
jalvesz Jun 21, 2024
e78c026
replace quicksort 1D by stdlib sort
jalvesz Jun 23, 2024
f25e07d
Setter procedure name change to 'add' covering scalar and array data
jalvesz Jun 27, 2024
c7035c9
add sparse test for add or getting values
jalvesz Jun 27, 2024
82950e0
change name of value retrival function to at
jalvesz Jun 27, 2024
93c1e55
sellc add/at
jalvesz Jun 28, 2024
c1f30f6
refactoring to enable a from_ijv initialization interface
jalvesz Jun 29, 2024
4c16f4a
fix module procedure attribute
jalvesz Jun 29, 2024
944212d
enable creating CSR, ELL and SELLC using the from_ijv interface
jalvesz Jun 29, 2024
dd4dbd8
add example from_ijv
jalvesz Jun 29, 2024
2d7701e
unused matrix
jalvesz Jun 29, 2024
98a564b
add example and spec for add/at
jalvesz Jun 29, 2024
b65d933
example print index
jalvesz Jun 29, 2024
a94272e
add csr2dense direct conversion
jalvesz Jun 29, 2024
e4c1f58
Merge branch 'fortran-lang:master' into sparse
jalvesz Jun 30, 2024
14e2c17
Merge branch 'fortran-lang:master' into sparse
jalvesz Jul 1, 2024
b53eca2
Update doc/specs/stdlib_sparse.md
jalvesz Jul 9, 2024
65e3fcb
Update doc/specs/stdlib_sparse.md
jalvesz Jul 9, 2024
2f56cd4
Update doc/specs/stdlib_sparse.md
jalvesz Jul 9, 2024
941de3a
Update doc/specs/stdlib_sparse.md
jalvesz Jul 9, 2024
575c426
Update doc/specs/stdlib_sparse.md
jalvesz Jul 9, 2024
db73fdc
Update src/stdlib_sparse_kinds.fypp
jalvesz Jul 9, 2024
697afa2
Update src/stdlib_sparse_kinds.fypp
jalvesz Jul 9, 2024
c97e665
Update src/stdlib_sparse_kinds.fypp
jalvesz Jul 9, 2024
ac100a1
Merge branch 'fortran-lang:master' into sparse
jalvesz Jul 9, 2024
9879a9c
Merge branch 'fortran-lang:master' into sparse
jalvesz Jul 9, 2024
dde88a7
refactor spmv as submodule to keep parameters private, rework specs
jalvesz Jul 10, 2024
6ae038b
add an ilp parameter to change in the future for int64 if needed for …
jalvesz Jul 10, 2024
3596f3f
add the _type suffix to all sparse types
jalvesz Jul 12, 2024
a21d1e8
Merge branch 'fortran-lang:master' into sparse
jalvesz Jul 13, 2024
c8d94a3
rollback on submodules
jalvesz Jul 31, 2024
82dbe02
forgotten file in cmake
jalvesz Jul 31, 2024
a8aa247
Merge branch 'fortran-lang:master' into sparse
jalvesz Aug 19, 2024
66b0ce2
Merge branch 'fortran-lang:master' into sparse
jalvesz Aug 21, 2024
4b41aa1
Merge branch 'fortran-lang:master' into sparse
jalvesz Sep 15, 2024
ab112e6
Merge branch 'fortran-lang:master' into sparse
jalvesz Sep 18, 2024
bc0021b
add csc/coo conversions and diagonal extraction
jalvesz Sep 20, 2024
7279461
Merge branch 'fortran-lang:master' into sparse
jalvesz Sep 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 305 additions & 0 deletions doc/specs/stdlib_sparse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
---
title: sparse
---

# The `stdlib_sparse` module

[TOC]

## 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`.

```Fortran
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
jvdp1 marked this conversation as resolved.
Show resolved Hide resolved
jalvesz 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 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.

```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 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.

```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 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.

```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 [the reference](https://arxiv.org/pdf/1307.6209v1)

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
## `add`/`at` - Sparse Matrix data accessors
jvdp1 marked this conversation as resolved.
Show resolved Hide resolved

### Status

Experimental

### Description
Type-bound procedures to enable adding or requesting data in/from a sparse matrix.
jalvesz marked this conversation as resolved.
Show resolved Hide resolved

### Syntax

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

### Arguments

`i`, `intent(in)`: Shall be an integer value or rank-1 array.
jalvesz marked this conversation as resolved.
Show resolved Hide resolved
`j`, `intent(in)`: Shall be an integer value or rank-1 array.
`v`, `intent(in)`: Shall be a `real` or `complex` value or rank-2 array. The type shall be in accordance to the declared sparse matrix object.

### Syntax
jalvesz marked this conversation as resolved.
Show resolved Hide resolved

jvdp1 marked this conversation as resolved.
Show resolved Hide resolved
`v = matrix%at(i,j)`

### Arguments

`i`, `intent(in)` : Shall be an integer value.
`j`, `intent(in)` : Shall be an integer value.
`v`, `result` : 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
```fortran
{!example/linalg/example_sparse_data_accessors.f90!}
```

<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->
## `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)`
Copy link
Contributor

Choose a reason for hiding this comment

The 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`:: Shall be an optional `logical` argument to determine whether data in the COO graph should be sorted while sorting the index array, default `.false.`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not mentioned in the previous syntax section.


### Syntax

`call ` [[stdlib_sparse_conversion(module):from_ijv(interface)]] `(sparse,row,col[,data,nrows,ncols,num_nz_rows,chunk])`

### Arguments

`sparse`, `intent(inout)`: 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.

`row`, `integer(in)`:: rows index array.

`col`, `integer(in)`:: columns index array.

`data`, `real/complex(in)`, `optional`:: `real` or `complex` data array.

`nrows`, `integer(in)`, `optional`:: number of rows, if not given it will be computed from the `row` array.

`ncols`, `integer(in)`, `optional`:: number of columns, if not given it will be computed from the `col` array.

`num_nz_rows`, `integer(in)`, `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.

`chunk`, `integer(in)`, `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.

## Example
```fortran
{!example/linalg/example_sparse_from_ijv.f90!}
```

### 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!}
```
3 changes: 3 additions & 0 deletions example/linalg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ ADD_EXAMPLE(lstsq2)
ADD_EXAMPLE(solve1)
ADD_EXAMPLE(solve2)
ADD_EXAMPLE(solve3)
ADD_EXAMPLE(sparse_from_ijv)
ADD_EXAMPLE(sparse_data_accessors)
ADD_EXAMPLE(sparse_spmv)
ADD_EXAMPLE(svd)
ADD_EXAMPLE(svdvals)
ADD_EXAMPLE(determinant)
Expand Down
49 changes: 49 additions & 0 deletions example/linalg/example_sparse_data_accessors.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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) :: CSR
type(COO_dp) :: 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
Loading
Loading