diff --git a/NEWS.md b/NEWS.md index d4f46aa40f6c73..526896bf7de5bc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -33,6 +33,7 @@ Standard library changes #### LinearAlgebra * The BLAS submodule now supports the level-2 BLAS subroutine `hpmv!` ([#34211]). * `normalize` now supports multidimensional arrays ([#34239]) +* The BLAS submodule now supports the level-2 BLAS subroutine `spmv!` ([#34320]). #### Markdown diff --git a/stdlib/LinearAlgebra/src/blas.jl b/stdlib/LinearAlgebra/src/blas.jl index a31515b336db82..46d52b64a9ac51 100644 --- a/stdlib/LinearAlgebra/src/blas.jl +++ b/stdlib/LinearAlgebra/src/blas.jl @@ -31,6 +31,7 @@ export hpmv!, sbmv!, sbmv, + spmv!, symv!, symv, trsv!, @@ -964,6 +965,82 @@ Return the updated `y`. """ sbmv! +### spmv!, (SP) symmetric packed matrix-vector operation defined as y := alpha*A*x + beta*y. +for (fname, elty) in ((:dspmv_, :Float64), + (:sspmv_, :Float32)) + @eval begin + function spmv!(uplo::AbstractChar, + n::BlasInt, + α::$elty, + AP::Union{Ptr{$elty}, AbstractArray{$elty}}, + x::Union{Ptr{$elty}, AbstractArray{$elty}}, + incx::Integer, + β::$elty, + y::Union{Ptr{$elty}, AbstractArray{$elty}}, + incy::Integer) + + ccall((@blasfunc($fname), libblas), Cvoid, + (Ref{UInt8}, # uplo, + Ref{BlasInt}, # n, + Ref{$elty}, # α, + Ptr{$elty}, # AP, + Ptr{$elty}, # x, + Ref{BlasInt}, # incx, + Ref{$elty}, # β, + Ptr{$elty}, # y, out + Ref{BlasInt}), # incy + uplo, + n, + α, + AP, + x, + incx, + β, + y, + incy) + end + end +end + +function spmv!(uplo::AbstractChar, + α::Real, AP::Union{DenseArray{T}, AbstractVector{T}}, x::Union{DenseArray{T}, AbstractVector{T}}, + β::Real, y::Union{DenseArray{T}, AbstractVector{T}}) where {T <: BlasReal} + require_one_based_indexing(AP, x, y) + N = length(x) + if N != length(y) + throw(DimensionMismatch("x has length $(N), but y has length $(length(y))")) + end + if length(AP) < Int64(N*(N+1)/2) + throw(DimensionMismatch("Packed symmetric matrix A has size smaller than length(x) = $(N).")) + end + GC.@preserve x y AP spmv!(uplo, BlasInt(N), convert(T, α), AP, pointer(x), BlasInt(stride(x, 1)), convert(T, β), pointer(y), BlasInt(stride(y, 1))) + y +end + +""" + spmv!(uplo, α, AP, x, β, y) + +Update vector `y` as `α*A*x + β*y`, where `A` is a symmetric matrix provided +in packed format `AP`. + +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 inputs `α` and `β` must be real. + +The array inputs `x`, `y` and `AP` must all be vectors of `Float32` or `Float64`. + +Return the updated `y`. +""" +spmv! + ### hbmv, (HB) Hermitian banded matrix-vector multiplication for (fname, elty) in ((:zhbmv_,:ComplexF64), (:chbmv_,:ComplexF32)) diff --git a/stdlib/LinearAlgebra/test/blas.jl b/stdlib/LinearAlgebra/test/blas.jl index 4a21fbb3a5a3d7..acf67c48a5103b 100644 --- a/stdlib/LinearAlgebra/test/blas.jl +++ b/stdlib/LinearAlgebra/test/blas.jl @@ -241,6 +241,46 @@ Random.seed!(100) end end + # spmv! + if elty in (Float32, Float64) + @testset "spmv!" begin + # Both matrix dimensions n coincide, as we have symmetric matrices. + # Define the inputs and outputs of spmv!, y = α*A*x+β*y + α = rand(elty) + M = rand(elty, n, n) + A = Symmetric(M) + x = rand(elty, n) + β = rand(elty) + y = rand(elty, n) + + y_result_julia = α*A*x+β*y + + # Create lower triangular packing of A + AP = typeof(A[1,1])[] + for j in 1:n + for i in j:n + push!(AP, A[i,j]) + end + end + + y_result_blas_lower = copy(y) + BLAS.spmv!('L', α, AP, x, β, y_result_blas_lower) + @test y_result_julia≈y_result_blas_lower + + # Create upper triangular packing of A + AP = typeof(A[1,1])[] + for j in 1:n + for i in 1:j + push!(AP, A[i,j]) + end + end + + y_result_blas_upper = copy(y) + BLAS.spmv!('U', α, AP, x, β, y_result_blas_upper) + @test y_result_julia≈y_result_blas_upper + end + end + #trsm A = triu(rand(elty,n,n)) B = rand(elty,(n,n))