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

added sparse matrix inner product #27470

Merged
merged 1 commit into from
Jun 12, 2018
Merged

added sparse matrix inner product #27470

merged 1 commit into from
Jun 12, 2018

Conversation

jebej
Copy link
Contributor

@jebej jebej commented Jun 7, 2018

Provides a nice speedup relative to the generic method currently dispatched by vecdot.

Please review the style and tell me if anything needs to be changed.

Tests to be added and final name TBD pending resolution of #25565.

@ViralBShah ViralBShah added the sparse Sparse arrays label Jun 7, 2018
@ararslan ararslan added needs tests Unit tests are required for this change needs news A NEWS entry is required for this change labels Jun 7, 2018
@jebej
Copy link
Contributor Author

jebej commented Jun 7, 2018

Tests added. If anyone can, please cancel the two earlier jobs running on AppVeyor to let the last one go through. Travis seems to have done that automatically.

edit: jobs can be removed on CircleCI as well

@@ -203,6 +203,28 @@ function spmatmul(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti};
return C
end

# Frobenius inner product: trace(A†B)
Copy link
Contributor

Choose a reason for hiding this comment

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

what is ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just trace(A'*B)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A dagger, for the conjugate transpose. That's how I had it in my package, but I can change it to whatever notation is preferred (maybe ')

Copy link
Contributor

Choose a reason for hiding this comment

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

It is ' in Julia, so that would make sense.

for i1 = A.colptr[j]:A.colptr[j+1]-1
ra = A.rowval[i1]
for i2 = B.colptr[j]:B.colptr[j+1]-1
rb = B.rowval[i2]
Copy link
Contributor

Choose a reason for hiding this comment

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

I realise it's a bit pedantic, but maybe keep the indices consistent? i.e. ia instead of i1 and ib instead of i2.

@inbounds for j = 1:n
for i1 = A.colptr[j]:A.colptr[j+1]-1
ra = A.rowval[i1]
for i2 = B.colptr[j]:B.colptr[j+1]-1
Copy link
Contributor

Choose a reason for hiding this comment

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

you shouldn't need to restart this iterator

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah you are right, I'll improve the algorithm

Copy link
Contributor

@simonbyrne simonbyrne Jun 7, 2018

Choose a reason for hiding this comment

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

it's not the prettiest, but you could do something like:

ia = ia_nxt; ib = ib_nxt
ia_nxt = A.colptr[j+1]; ib_nxt = B.colptr[j+1]

if ia < ia_nxt && ib < ib_nxt
    ra = A.rowval[ia];  rb = B.rowval[ib]
    while true
        if ra < rb
            ia += 1
            ia < ia_nxt || break
            ra = A.rowval[ia];
        elseif ra > rb
            ib += 1
            ib < ib_nxt || break
            rb = B.rowval[ib]
        else # ra == rb
            r += A.nzval[ia]' * B.nzval[ib]
            ia += 1; ib += 1
            ia < ia_nxt && ib < ib_nxt || break
            ra = A.rowval[ia]; rb = B.rowval[ib]
        end
    end
end

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 compared yours (which has a small typo on the first line) with one where I just updated the lower bound of the loop and yours was faster

ra = A.rowval[ia]; rb = B.rowval[ib]
while true
if ra < rb
ia += 1
Copy link
Member

Choose a reason for hiding this comment

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

This increment (and similar increments below) may yield type instability when the relevant SparseMatrixCSC's colptr type is not Int?

function vecdot(A::SparseMatrixCSC{T1,S1},B::SparseMatrixCSC{T2,S2}) where {T1,T2,S1,S2}
m, n = size(A)
size(B) == (m,n) || throw(DimensionMismatch("matrices must have the same dimensions"))
r = zero(promote_type(T1,T2))
Copy link
Member

@Sacha0 Sacha0 Jun 8, 2018

Choose a reason for hiding this comment

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

Rather than promote_type(T1, T2), initialization of r should probably reflect the actual operation? (Edit: See, e.g., the generic implementation in stdlib/LinearAlgebra/src/generic.jl:651 .)

ib < ib_nxt || break
rb = B.rowval[ib]
else # ra == rb
r += A.nzval[ia]' * B.nzval[ib]
Copy link
Member

Choose a reason for hiding this comment

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

Update of r should probably call (vec)dot? See, e.g., the generic implementation in stdlib/LinearAlgebra/src/generic.jl:651.

@jebej
Copy link
Contributor Author

jebej commented Jun 8, 2018

Comments addressed. I'd like to change vecdot to inner once #27401 is done. Also, what should go in NEWS? Just something like "added an optimized method for sparse inner products"?

@fredrikekre fredrikekre removed needs news A NEWS entry is required for this change needs tests Unit tests are required for this change labels Jun 8, 2018
ra = A.rowval[ia]; rb = B.rowval[ib]
while true
if ra < rb
ia += one(S1)
Copy link
Member

@Sacha0 Sacha0 Jun 10, 2018

Choose a reason for hiding this comment

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

Here you need an additive identity, so oneunit rather than one :).

(Edit: And likewise below.)

@jebej
Copy link
Contributor Author

jebej commented Jun 11, 2018

Barring style changes to the NEWS entry, I think this only needs to wait for the conclusion of #27401 for the final function name.

@simonbyrne simonbyrne merged commit 4420717 into JuliaLang:master Jun 12, 2018
@simonbyrne
Copy link
Contributor

thanks!

@jebej jebej deleted the inner-sparse branch June 12, 2018 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sparse Sparse arrays
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants