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

add spr! to LinearAlgebra.BLAS #42830

Merged
merged 3 commits into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Standard library changes
#### Package Manager

#### LinearAlgebra
* The BLAS submodule now supports the level-2 BLAS subroutine `spr!` ([#42830]).

#### Markdown

Expand Down
66 changes: 66 additions & 0 deletions stdlib/LinearAlgebra/src/blas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export
sbmv!,
sbmv,
spmv!,
spr!,
symv!,
symv,
trsv!,
Expand Down Expand Up @@ -1145,6 +1146,71 @@ Return the updated `y`.
"""
spmv!

### spr!, (SP) symmetric packed matrix-vector operation defined as A := alpha*x*x' + A
for (fname, elty) in ((:dspr_, :Float64),
(:sspr_, :Float32))
@eval begin
function spr!(uplo::AbstractChar,
n::Integer,
α::$elty,
x::Union{Ptr{$elty}, AbstractArray{$elty}},
incx::Integer,
AP::Union{Ptr{$elty}, AbstractArray{$elty}})

ccall((@blasfunc($fname), libblastrampoline), Cvoid,
(Ref{UInt8}, # uplo,
Ref{BlasInt}, # n,
Ref{$elty}, # α,
Ptr{$elty}, # x,
Ref{BlasInt}, # incx,
Ptr{$elty}, # AP,
Clong), # length of uplo
uplo,
n,
α,
x,
incx,
AP,
1)
return AP
end
end
end

function spr!(uplo::AbstractChar,
α::Real, x::Union{DenseArray{T}, AbstractVector{T}},
AP::Union{DenseArray{T}, AbstractVector{T}}) where {T <: BlasReal}
Comment on lines +1181 to +1182
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a usecase for x::DenseArray or would it make more sense to restrict it to just AbstractVector?

For AP, on the other hand, I don’t think we should allow AbstractVector without checking that its stride is compatible, i.e., chkstride1(AP).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i too wondered about x::DenseArray. but that is what spmv! inputs. is there a use case for it there? smpv! also does not check the stride of AP. happy to change it here, but would be nice to keep everything in sync.

require_one_based_indexing(AP, x)
N = length(x)
dkarrasch marked this conversation as resolved.
Show resolved Hide resolved
if 2*length(AP) < N*(N + 1)
throw(DimensionMismatch("Packed symmetric matrix A has size smaller than length(x) = $(N)."))
end
return spr!(uplo, N, convert(T, α), x, stride(x, 1), AP)
Copy link
Contributor

@sostock sostock Nov 1, 2021

Choose a reason for hiding this comment

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

This will use the wrong pointer for x if stride(x,1) is negative. However, no BLAS wrappers except for gemv! (edit: and dot) currently handle negative strides correctly, so it may be ok to leave this for a separate PR.

(Support for negative strides in gemv! was added in #41513 and I plan to add it for the other BLAS functions once there is a decision on whether to merge #42012 or #42054.)

end

"""
spr!(uplo, α, x, AP)
Update matrix `A` as `α*A*x*x'`, where `A` is a symmetric matrix provided
in packed format `AP` and `x` is a vector.
With `uplo = 'U'`, the array AP must contain the upper triangular part of the
symmetric matrix packed sequentially, column by column, so that `AP[1]`
contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[1, 2]` and `A[2, 2]`
respectively, and so on.
With `uplo = 'L'`, the array AP must contain the lower triangular part of the
symmetric matrix packed sequentially, column by column, so that `AP[1]`
contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[2, 1]` and `A[3, 1]`
respectively, and so on.
The scalar input `α` must be real.
The array inputs `x` and `AP` must all be of `Float32` or `Float64` type.
Return the updated `A`.
dkarrasch marked this conversation as resolved.
Show resolved Hide resolved
"""
spr!

### hbmv, (HB) Hermitian banded matrix-vector multiplication
for (fname, elty) in ((:zhbmv_,:ComplexF64),
(:chbmv_,:ComplexF32))
Expand Down
31 changes: 31 additions & 0 deletions stdlib/LinearAlgebra/test/blas.jl
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,37 @@ Random.seed!(100)
end
end

# spr!
if elty in (Float32, Float64)
@testset "spr! $elty" begin
α = rand(elty)
M = rand(elty, n, n)
AL = Symmetric(M, :L)
AU = Symmetric(M, :U)
x = rand(elty, n)

function pack(A, uplo)
AP = elty[]
for j in 1:n
for i in (uplo==:L ? (j:n) : (1:j))
push!(AP, A[i,j])
end
end
return AP
end

ALP_result_julia_lower = pack*x*x' + AL, :L)
ALP_result_blas_lower = pack(AL, :L)
BLAS.spr!('L', α, x, ALP_result_blas_lower)
@test ALP_result_julia_lower ALP_result_blas_lower

AUP_result_julia_upper = pack*x*x' + AU, :U)
AUP_result_blas_upper = pack(AU, :U)
BLAS.spr!('U', α, x, AUP_result_blas_upper)
@test AUP_result_julia_upper AUP_result_blas_upper
end
end

#trsm
A = triu(rand(elty,n,n))
B = rand(elty,(n,n))
Expand Down