# Sprand fails to construct a SparseVector

**URL:** <https://discourse.julialang.org/t/sprand-fails-to-construct-a-sparsevector/128828>\
**Category:** New to Julia\
**Tags:** question, random, sparsearrays\
**Created:** [May 8, 2025, 5:28am UTC](https://discourse.julialang.org/t/sprand-fails-to-construct-a-sparsevector/128828 "2025-05-08T05:28:33Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 8, 2025, 5:28am UTC](https://discourse.julialang.org/t/sprand-fails-to-construct-a-sparsevector/128828/1 "2025-05-08T05:28:33Z")

</div>

I can construct a random SparseMatrixCSC, but not a SparseVector in the same way. Where am I wrong?

```julia
julia> using SparseArrays

julia> rfn = n -> rand(-9:.1:9, n)
#1 (generic function with 1 method)

julia> sprand(7, 8, .2, rfn)
7×8 SparseMatrixCSC{Float64, Int64} with 13 stored entries:
   ⋅ 4.3 ⋅ 2.5 ⋅ ⋅ 0.1 ⋅ 
   ⋅ ⋅ ⋅ -3.0 ⋅ ⋅ ⋅ ⋅ 
   ⋅ ⋅ ⋅ ⋅ 5.3 ⋅ ⋅ ⋅ 
  5.1 ⋅ 3.8 ⋅ 4.9 ⋅ ⋅ ⋅ 
 -3.4 ⋅ ⋅ ⋅ ⋅ 4.3 ⋅ ⋅ 
   ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 
   ⋅ ⋅ ⋅ ⋅ -0.1 ⋅ 3.0 -6.6

julia> sprand(8, .2, rfn)
ERROR: MethodError: no method matching (::var"#1#2")(::Random.TaskLocalRNG, ::Int64)
The function `#1` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  (::var"#1#2")(::Any)
   @ Main REPL[2]:1

Stacktrace:
 [1] sprand(r::Random.TaskLocalRNG, n::Int64, p::Float64, rfn::var"#1#2")
   @ SparseArrays K:\julia-1.11.5\share\julia\stdlib\v1.11\SparseArrays\src\sparsevector.jl:648
 [2] sprand(n::Int64, p::Float64, rfn::Function)
   @ SparseArrays K:\julia-1.11.5\share\julia\stdlib\v1.11\SparseArrays\src\sparsevector.jl:645
 [3] top-level scope
   @ REPL[4]:1

julia> sprand(8, .2)
8-element SparseVector{Float64, Int64} with 1 stored entry:
  [3] = 0.724307

```

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 8, 2025, 5:53am UTC](https://discourse.julialang.org/t/sprand-fails-to-construct-a-sparsevector/128828/2 "2025-05-08T05:53:13Z")

</div>

As explained in the [`sprand` docstring](https://docs.julialang.org/en/v1/stdlib/SparseArrays/#SparseArrays.sprand), the requirement for the `rfn` argument is that it has two methods, `rfn(k)` and `rfn(rng, k)`. So the following works:

```julia-repl
julia> using SparseArrays

julia> rfn(k) = rand(-9:0.1:9, k)
rfn (generic function with 1 method)

julia> rfn(rng, k) = rand(rng, -9:0.1:9, k)
rfn (generic function with 2 methods)

julia> sprand(7, .2, rfn)
7-element SparseVector{Float64, Int64} with 3 stored entries:
  [4] = 8.5
  [5] = -3.2
  [7] = 4.4

```

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 8, 2025, 6:00am UTC](https://discourse.julialang.org/t/sprand-fails-to-construct-a-sparsevector/128828/3 "2025-05-08T06:00:14Z")

</div>

Make some sense, but I think it is unjustly involved.

```julia
julia> using SparseArrays

julia> my_range, N, p = -9:.1:9, 30, .15
(-9.0:0.1:9.0, 30, 0.15)

julia> my_vector = rand(my_range, N);

julia> my_sparse_vector = sprand(N, p, (rng, n) -> rand(rng, my_range, n))
30-element SparseVector{Float64, Int64} with 3 stored entries:
  [1] = -6.7
  [17] = -5.4
  [21] = 7.7

```

To be honest, the interface is not intuitive or consistent enough.

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 8, 2025, 6:36am UTC](https://discourse.julialang.org/t/sprand-fails-to-construct-a-sparsevector/128828/4 "2025-05-08T06:36:36Z")

</div>

There’s an open issue for that:

> <https://github.com/JuliaSparse/SparseArrays.jl/issues/45>
>
> \`sprand\` accepts a function argument \`rfn\` that is used to generate the non zero… values of a sparse random matrix or vector. I find the calling convention with rfn to be inconsistent and confusing.
> 
> \`\`\`
> julia\> sprand(0,0,1.0,rand)
> 0×0 SparseMatrixCSC{Float64,Int64} with 0 stored entries
> 
> julia\> sprand(1,1,1.0,rand)
> 1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:
> \[1, 1\] = 0.686873
> 
> julia\> sprand(1,1,1.0,i-\>fill(1.0,i))
> 1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:
> \[1, 1\] = 1.0
> 
> \`\`\`
> So far so good. What about generating \`Float32\` values? The function accepts a \`type\` parameter
> 
> \`\`\`
> julia\> sprand(0,0,1.0,rand,Float32)
> 0×0 SparseMatrixCSC{Float32,Int64} with 0 stored entries
> 
> julia\> sprand(1,1,1.0,rand,Float32)
> 1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:
> \[1, 1\] = 0.411008
> 
> julia\> sprand(1,1,1.0,i-\>fill(1.0,i),Float32)
> 1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:
> \[1, 1\] = 1.0
> \`\`\`
> so the type parameter is ignored for non 0x0 matrices (rendering the method effectively type unstable by the way). This seems a bug, and is definitely ugly.
> 
> Moreover, \`sprand\` accepts also a RNG first argument:
> 
> \`\`\`
> using Random; r=Random.MersenneTwister(1);
> 
> julia\> sprand(r,1,1,1.0,rand,Float32)
> 1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:
> \[1, 1\] = 0.644883
> 
> julia\> sprand(r,1,1,1.0,i-\>fill(1.0,i),Float32)
> ERROR: MethodError: no method matching (::getfield(Main, Symbol("##9#10")))(::MersenneTwister, ::Int64)
> Closest candidates are:
> JuliaLang/julia#9(::Any) at REPL\[36\]:1
> Stacktrace:
> \[1\] sprand(::MersenneTwister, ::Int64, ::Int64, ::Float64, ::getfield(Main, Symbol("##9#10")), ::Type{Float32}) at /home/ab/src/julia/build1.x/usr/share/julia/stdlib/v1.2/SparseArrays/src/sparsematrix.jl:1448
> \[2\] top-level scope at none:0
> \`\`\`
> Here we learn that the signature for the passed method should change, now accepting \`r\` as well. I find this overly convoluted (I pass a function that gets passed an argument that I also pass: if I supply the function, I can pass it myself) and a bit inconsistent (the signature of the required \`rfn\` depends on other arguments).
> 
> Another inconsistency: without \`rfn\`, the type parameter can be also specified, but it goes as first argument instead of last.
> 
> Of course, most of these could be clarified in the documentation (now it is a bit terse): 
> 
> \`\`\`
> sprand(\[rng\],\[type\],m,\[n\],p::AbstractFloat,\[rfn\])
> 
> Create a random length m sparse vector or m by n sparse matrix, in which the probability of any element being nonzero is independently
> given by p (and hence the mean density of nonzeros is also exactly p). Nonzero values are sampled from the distribution specified by
> rfn and have the type type. The uniform distribution is used in case rfn is not specified. The optional rng argument specifies a
> random number generator, see Random Numbers.
> 
> Examples
> ≡≡≡≡≡≡≡≡≡≡
> 
> julia\> sprand(Bool, 2, 2, 0.5)
> 2×2 SparseMatrixCSC{Bool,Int64} with 2 stored entries:
> \[1, 1\] = true
> \[2, 1\] = true
>   
> julia\> sprand(Float64, 3, 0.75)
> 3-element SparseVector{Float64,Int64} with 1 stored entry:
> \[3\] = 0.298614
> 
> \`\`\`
> 
> But I really think that the calling convention could be improved very easily. I propose to change the behaviour in the following way: whenever a function is passed, this function accepts always only one argument (the number of values to generate) and no type can be specified. In this way, the caller can supply the function (using a random generator or not) he wants and generating the type of values he want (that will become the Tv type of the matrix). A patch for this is very simple, I can submit it if there is interest. I suppose it will have to wait until 2.x for merging as it will be breaking though.

The `rfn` signature must be preserved for backward compatibility, but if you can think of better signatures that can be added without breaking or removing the existing ones, please submit a comment or PR! I don’t think anyone particularly likes the current interface for sampling sparse arrays with arbitrary distributions.
