-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
draft: symmetric sparse matrix support #22200
Changes from all commits
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,67 @@ | ||
# This file is a part of Julia. License is MIT: https://julialang.org/license | ||
|
||
(*)(A::Symmetric{TA,SparseMatrixCSC{TA,S}}, x::StridedVecOrMat{Tx}) where {TA,S,Tx} = A_mul_B(A, x) | ||
|
||
function A_mul_B!(α::Number, A::Symmetric{TA,SparseMatrixCSC{TA,S}}, B::StridedVecOrMat, β::Number, C::StridedVecOrMat) where {TA,S} | ||
A.data.n == size(B, 1) || throw(DimensionMismatch()) | ||
A.data.m == size(C, 1) || throw(DimensionMismatch()) | ||
A.uplo == 'U' ? A_mul_B_U_kernel!(α, A, B, β, C) : A_mul_B_L_kernel!(α, A, B, β, C) | ||
end | ||
|
||
function A_mul_B(A::Symmetric{TA,SparseMatrixCSC{TA,S}}, x::StridedVector{Tx}) where {TA,S,Tx} | ||
T = promote_type(TA, Tx) | ||
A_mul_B!(one(T), A, x, zero(T), similar(x, T, A.data.n)) | ||
end | ||
|
||
function A_mul_B(A::Symmetric{TA,SparseMatrixCSC{TA,S}}, B::StridedMatrix{Tx}) where {TA,S,Tx} | ||
T = promote_type(TA, Tx) | ||
A_mul_B!(one(T), A, B, zero(T), similar(B, T, (A.data.n, size(B, 2)))) | ||
end | ||
|
||
function A_mul_B_U_kernel!(α::Number, A::Symmetric{TA,SparseMatrixCSC{TA,S}}, B::StridedVecOrMat, β::Number, C::StridedVecOrMat) where {TA,S} | ||
colptr = A.data.colptr | ||
rowval = A.data.rowval | ||
nzval = A.data.nzval | ||
if β != 1 | ||
β != 0 ? scale!(C, β) : fill!(C, zero(eltype(C))) | ||
end | ||
@inbounds for k = 1 : size(C, 2) | ||
@inbounds for col = 1 : A.data.n | ||
αxj = α * B[col, k] | ||
tmp = TA(0) | ||
@inbounds for j = colptr[col] : (colptr[col + 1] - 1) | ||
row = rowval[j] | ||
row > col && break # assume indices are sorted | ||
a = nzval[j] | ||
C[row, k] += a * αxj | ||
row == col || (tmp += a * B[row, k]) | ||
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 think this should have an alpha 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. Of course, thanks. I chose to multiply 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. ps: should the kernels be exported? 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 don't think so. |
||
end | ||
C[col, k] += α * tmp | ||
end | ||
end | ||
C | ||
end | ||
|
||
function A_mul_B_L_kernel!(α::Number, A::Symmetric{TA,SparseMatrixCSC{TA,S}}, B::StridedVecOrMat, β::Number, C::StridedVecOrMat) where {TA,S} | ||
colptr = A.data.colptr | ||
rowval = A.data.rowval | ||
nzval = A.data.nzval | ||
if β != 1 | ||
β != 0 ? scale!(C, β) : fill!(C, zero(eltype(C))) | ||
end | ||
@inbounds for k = 1 : size(C, 2) | ||
@inbounds for col = 1 : A.data.n | ||
αxj = α * B[col, k] | ||
tmp = TA(0) | ||
@inbounds for j = (colptr[col + 1] - 1) : -1 : colptr[col] | ||
row = rowval[j] | ||
row < col && break | ||
a = nzval[j] | ||
C[row, k] += a * αxj | ||
row == col || (tmp += a * B[row, k]) | ||
end | ||
C[col, k] += α * tmp | ||
end | ||
end | ||
C | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are just 2 lines that are different between the
U
/L
kernels. I think we can merge them, and add aVal{:U}
/Val{:L}
as last argument to the kernel? Something like:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a performance impact for that, which is why I separated them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, there's a performance impact for allowing
C
to be general, which makes me think that a separate version whereC
is just a vector is desirable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should be compiled away.
If the impact is noticeable it makes sense to have a separate kernel for matvec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the matrix in my gist, here's the benchmark of
A * b
:Now
U * b
andL * b
using separate kernels:and
U * b
andL * b
using the joint kernel (after obvious corrections):I didn't know about
Val
. It improves over my initial joint version somewhat, but there'll still be a noticeable difference over large amounts of matrix-vector products.