Repository navigation
Make whole views of a sparse vector or column behave like the parent for any index type - #1119
Merged
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1119 +/- ##
==========================================
- Coverage 94.73% 94.68% -0.05%
==========================================
Files 16 16
Lines 10325 10433 +108
==========================================
+ Hits 9781 9879 +98
- Misses 544 554 +10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…s views dispatch to the sparse methods
`length` and `size` of a `SparseVector` and a `FixedSparseVector` return an
`Int` when every value of the index type fits in one, and `Ti` otherwise, so
the whole view of a narrow-indexed vector is a `SparseVectorView`. The alias is
widened to any `Slice{<:OneTo}` for the wider index types, and `count` over
sparse vector views uses the stored entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ViralBShah
force-pushed
the
vs/vectorview-index-type
branch
from
October 26, 2026 15:02
1ab862c to
803d0d2
Compare
view(x, :) of a non-Int-indexed SparseVector to the SparseVectorView methodsInt from length and axes of a SparseVector, so that its views dispatch to the sparse methods
Revert the `Int` length: #176 (JuliaLang/julia#45937) decided that `length` returns the index type. The widened `SparseVectorView` alias and the O(nnz) `count` on sparse vector views stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Int from length and axes of a SparseVector, so that its views dispatch to the sparse methodsview(x, :) of a non-Int-indexed SparseVector and count of a sparse vector view to the O(nnz) methods
…n `SparseVector` `SparseVector(v)`, and through it `sparse`, `convert`, concatenation and `kron`, read a whole view as a dense vector: O(n), stored zeros dropped and `Int` indices. The copy constructors now take `SparseVectorOrView`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
view(x, :) of a non-Int-indexed SparseVector and count of a sparse vector view to the O(nnz) methods
ViralBShah
added a commit
that referenced
this pull request
Oct 27, 2026
A range view of a sparse vector, `view(x, 2:6)`, is converted to a
`SparseVector` by reading it as a dense vector, so `SparseVector`,
`sparse`, `sparsevec`, `convert` and sparse `vcat`, `hcat` and `hvcat`
take O(n), drop stored zeros and give `Int` indices, and `kron` of a
range view is dense.
```julia
using SparseArrays
x = sparsevec(Int32[2, 4, 5], [1.0, 0.0, 3.0], 7); v = view(x, 2:6)
```
**Before**
```
SparseVector(v) = sparsevec([1, 4], [1.0, 3.0], 5)
vcat(v, v) = sparsevec([1, 4, 6, 9], [1.0, 3.0, 1.0, 3.0], 10)
typeof(kron(v, x)) = Vector{Float64}
```
**After**
```
SparseVector(v) = sparsevec(Int32[1, 3, 4], [1.0, 0.0, 3.0], 5)
vcat(v, v) = sparsevec(Int32[1, 3, 4, 6, 8, 9], [1.0, 0.0, 3.0, 1.0, 0.0, 3.0], 10)
typeof(kron(v, x)) = SparseVector{Float64, Int32}
```
For `w = view(sparsevec([1, n], [1.0, 2.0], n), 1:n)` with `n = 10^7`
and `y = sparsevec([1], [1.0], 2)`, the minimum of three runs and the
`@allocated` of a warm call:
| `f(w)` | main | branch |
|-------------------|--------------------|--------------------|
| `SparseVector(w)` | 0.030 s, 19.1 MiB | 2.3e-7 s, 240 B |
| `vcat(w, w)` | 0.056 s, 38.2 MiB | 3.8e-7 s, 1.1 KiB |
| `kron(w, y)` | 0.067 s, 152.6 MiB | 3.8e-7 s, 976 B |
The `SparseVector` copy constructors and `sparsevec`, which #1119
widened to whole views, also take `SparseVectorPartialView`, whose
`nonzeroinds` and `nonzeros` give its stored entries relative to the
range. `convert` and the concatenations reach them through
`_makesparse`. `kron` takes range views in `_SparseKronGroup` and in its
vector methods, so they go through the same conversion. The
`_sparsevector` helper that sparse `map` uses for views now calls
`SparseVector` instead of repeating it.
This touches dispatch.
Potential breakage: for a range view, these conversions and
concatenations keep its stored zeros and the parent's index type, and
`kron` of one is sparse rather than dense, as for `x[2:6]`.
Products with a range view are in #1135.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
30 tasks done
ViralBShah
added a commit
that referenced
this pull request
Oct 27, 2026
Backports of the merged PRs labelled `backport 1.14`, cherry-picked with `-x` in the order they were merged on `main`. `release-1.14` starts at b0aa455 (#1104), the commit JuliaLang/julia's `release-1.14` pins, and every PR merged on `main` since then is labelled, so this branch's tree is identical to `main`'s at 9bfad27. - [x] #1105 - [x] #1011 - [x] #1107 - [x] #1108 - [x] #1110 - [x] #1111 - [x] #1114 - [x] #1112 - [x] #1115 - [x] #1117 - [x] #1118 - [x] #1109 - [x] #1113 - [x] #1122 - [x] #1123 - [x] #1120 - [x] #1124 - [x] #1125 - [x] #1116 - [x] #1121 - [x] #1126 - [x] #1128 - [x] #1130 - [x] #1131 - [x] #1119 - [x] #1133 - [x] #1127 - [x] #1135 - [x] #1134 - [x] #1137 #1137 removes the first-call compile time that #1128 added, which the TTFX check flagged on this PR. #1132 merged into the branch of #1131 and is part of its commit. Not bug fixes: #1011 adds a deprecation, #1109 changes the `sprand` value stream for a value function, #1113 changes the result shape of `SparseMatrixCSC` of a vector view, and #1119, #1133, #1134 and #1135 add methods for views. They are included because they are labelled and 1.14 is not released yet. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Viral B. Shah <ViralBShah@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A whole view of a sparse vector,
view(x, :), or of a sparse column,view(A, :, j), should behave like the vector it views. Two things stopped it.view(x, :)of aSparseVector{Tv,Int32}is not aSparseVectorView, so it misses every O(nnz) method on that alias:sum,maximum,normanddotvisit allnelements,rmul!andlmul!take the dense fallback,kronis dense andfindnzthrows. And for any index type,SparseVector(v)of a whole view, whichsparse,convert,vcat,hcatandkronall go through, reads it as a dense vector: O(n), stored zeros dropped,Intindices.Before
After
For
v32 = view(sparsevec(Int32[1, n], [1.0, 2.0], n), :)andv64likewise, withn = 10^7, the minimum of three runs and the@allocatedof a warm call:f(v)v32, mainv32, branchv64, mainv64, branchsummaximumdot(v, v)count(!iszero, v)findnzMethodErrorSparseVectorvcat(v, v)A
SparseVectorstores its length asTi, soaxes(x32)is(OneTo{Int32}(n),)andview(x32, :)slices aSlice{OneTo{Int32}}, whileSparseVectorViewnamedSlice{OneTo{Int}}only. A matrix stores its size asInt, soSparseColumnViewalready matched for every index type. The fix widensSparseVectorViewto<:Tuple{Base.Slice{<:Base.OneTo}}; no method on the alias assumesIntaxes, since they read the parent throughnonzeros,nonzeroindsandlength. TheSparseVectorcopy constructors now takeSparseVectorOrViewrather thanAbstractCompressedVector, so a whole view or column view is copied from its stored entries with the parent's index type, andsparsevecof one copies it through them.counton aSparseVectoralready countedfover the stored values plusfof one zero times the number of unstored entries; that method (_simple_count) now also covers the whole and range views, and takes the zero from_densezero.This touches dispatch.
Potential breakage, in each case towards what the parent already gives:
SparseVector(v),sparse(v),convert(SparseVector, v)and sparsevcat,hcat,hvcatandkronthat take one keep its stored zeros, sonnzof the result can be larger than before.Int.vof aSparseVectorwith a non-Intindex type:nonzeros(v)is the parent'sVectorrather than aSubArrayof it;nonzeroinds(v)is the parent's own index vector rather than a fresh copy, so writing to it changes the parent;findfirst,findlast,findnextandfindprevreturnTirather thanInt;diff(v)keeps a stored zero;kron(v, x)andkron(v, A)are sparse rather than dense; andA * vwith anInt-indexedAis aSparseVector{Tv,Int}rather than aSparseVector{Tv,Ti}.lengthkeeps returningTi, as #176 (JuliaLang/julia#45937) decided. Range views such asview(x, 2:4)still convert through the dense path, andspdiagm(0 => view(x, :))still stores every diagonal entry; both are left for separate PRs.🤖 Generated with Claude Code