Some thoughts on improving the doc of SparseArrays.jl

The stdlib SparseArrays.jl is mainly about SparseMatrixCSC and SparseVector.
From my point of view, its doc reads a bit immature (Edit: this word was used improperly. I hadn’t learnt its meaning. I’m not an native speaker, please don’t take it serious :slightly_smiling_face:), due in part to

  1. there are not very many examples which can showcase its expertise.
  2. relations to LinearAlgebra.jl is not elaborate. (They shouldn’t be separate packages, but should work well together in tandem.) (an exemplary question is to ask why we need sparse(I, 2, 2) given the existence of Diagonal(ones(2)).)

And particularly, I have a suggestion I don’t know if it is correct and insightful:
I find that the intro of SparseVector here is somewhat abrupt. A better intro could be

using LinearAlgebra, SparseArrays
function get_a_SparseMatrixCSC(N)
    A, v = spzeros(N, N), rand(N)
    A[1, :], A[:, 1] = v, v
    A
end
julia> A = get_a_SparseMatrixCSC(3)
3×3 SparseMatrixCSC{Float64, Int64} with 5 stored entries:
 0.728406    0.595737  0.00605678
 0.595737     ⋅         ⋅
 0.00605678   ⋅         ⋅

julia> sv = A[:, 1]
3-element SparseVector{Float64, Int64} with 3 stored entries:
  [1]  =  0.728406
  [2]  =  0.595737
  [3]  =  0.00605678

Thus we notice that a SparseVector arises naturally as a slice of a SparseMatrixCSC.
Furthermore, I assume that if the slice is intended to be read-only, then it could had been better to do sv = view(A, :, 1), am I right?
Additionally, we might testify the assertion that column slice is faster:

using BenchmarkTools
function rv_dot_u(A, u) return dot(view(A, 1, :), u) end
function cv_dot_u(A, u) return dot(view(A, :, 1), u) end
N = Int(1e6);
A, u = get_a_SparseMatrixCSC(N), rand(N);

julia> @btime rv_dot_u(A, u)
  2.005 ms (1 allocation: 16 bytes)
249983.8160420952

julia> @btime cv_dot_u(A, u)
  1.113 ms (1 allocation: 16 bytes)
249983.8160420952

(I wonder if this is the standard way to do dot product. Are there ways faster in this context?)
hope to hear about some comments :slight_smile:

In fact, a SparseMatrixCSC can also be derived from a SparseVector by an outer product.
But I find that a view cannot accomplish this (see the last line). I don’t know whether this is intended.

julia> A
3×3 SparseMatrixCSC{Float64, Int64} with 5 stored entries:
 0.449595  0.830722  0.039913
 0.830722   ⋅         ⋅ 
 0.039913   ⋅         ⋅ 

julia> sv, vv = A[:, 1], view(A, :, 1);

julia> sv * sv'
3×3 SparseMatrixCSC{Float64, Int64} with 9 stored entries:
 0.202135   0.373488   0.0179447
 0.373488   0.690099   0.0331566
 0.0179447  0.0331566  0.00159305

julia> sv * vv'
3×3 SparseMatrixCSC{Float64, Int64} with 9 stored entries:
 0.202135   0.373488   0.0179447
 0.373488   0.690099   0.0331566
 0.0179447  0.0331566  0.00159305

julia> vv * vv'
3×3 Matrix{Float64}:
 0.202135   0.373488   0.0179447
 0.373488   0.690099   0.0331566
 0.0179447  0.0331566  0.00159305

julia> typeof(ans) # it is a dense matrix!
Matrix{Float64} (alias for Array{Float64, 2})

As well as this case

julia> sv
100-element SparseVector{Float64, Int64} with 4 stored entries:
  [7 ]  =  0.876515
  [11]  =  0.675204
  [29]  =  0.511477
  [93]  =  0.584251

julia> vsv = view(sv, 1:7)
7-element view(::SparseVector{Float64, Int64}, 1:7) with eltype Float64:
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.8765145586828492

julia> vsv * vsv'
7×7 Matrix{Float64}:
 0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0  0.0  0.768278