From b26e8dadc93765f572014eb5a32f4302a212a837 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 12 Oct 2026 16:46:01 -0400 Subject: [PATCH 1/4] Restore type-stable `StridedView` construction on Julia 1.10 Co-Authored-By: Claude Opus 5.5 --- src/stridedview.jl | 79 ++++++++++++++-------------------------------- test/runtests.jl | 5 +-- 2 files changed, 27 insertions(+), 57 deletions(-) diff --git a/src/stridedview.jl b/src/stridedview.jl index ac5dea3..07a7113 100644 --- a/src/stridedview.jl +++ b/src/stridedview.jl @@ -39,28 +39,31 @@ function StridedView( size::NTuple{N, Int} = size(parent), strides::NTuple{N, Int} = strides(parent), offset::Int = 0, - op::F = identity; - normalize::Bool = true + op::F = identity ) where {N, F} T = Base.promote_op(op, eltype(parent)) - return StridedView{T}(parent, size, strides, offset, op; normalize) + return StridedView{T}(parent, size, strides, offset, op) end function StridedView{T}( parent::DenseArray, size::NTuple{N, Int} = size(parent), strides::NTuple{N, Int} = strides(parent), offset::Int = 0, - op::F = identity; - normalize::Bool = true + op::F = identity ) where {T, N, F} - # `normalize = false` asserts that `parent` and `strides` are already normalized, - # e.g. because they are taken from an existing `StridedView`. - normalize || return StridedView{T, N, typeof(parent), F}(parent, size, strides, offset, op) parent′ = _normalizeparent(parent) strides′ = _normalizestrides(size, strides) return StridedView{T, N, typeof(parent′), F}(parent′, size, strides′, offset, op) end +# `parent` and `strides` taken from an existing `StridedView` are already normalized +function _stridedview( + ::Type{T}, parent::DenseArray, size::NTuple{N, Int}, strides::NTuple{N, Int}, + offset::Int, op::F + ) where {T, N, F} + return StridedView{T, N, typeof(parent), F}(parent, size, strides, offset, op) +end + StridedView(a::StridedView) = a StridedView(a::Adjoint) = StridedView(a')' StridedView(a::Transpose) = transpose(StridedView(transpose(a))) @@ -76,10 +79,7 @@ function StridedView(a::Base.ReinterpretArray{T, N}) where {T, N} throw(ArgumentError("Cannot create StridedView with reinterpretation from $S to $T")) b.op isa FN || throw(ArgumentError("Cannot create StridedView with reinterpretation from view with non-identity operation")) - return StridedView{T}( - b.parent, size(b), strides(b), offset(b), identity; - normalize = false - ) + return _stridedview(T, b.parent, size(b), strides(b), offset(b), identity) end # trait @@ -150,13 +150,9 @@ end function Base.getindex(a::StridedView{T, N}, I::Vararg{SliceIndex, N}) where {T, N} newsize = _computeviewsize(a.size, I) newstrides = _normalizestrides(newsize, _computeviewstrides(a.strides, I)) - return StridedView{T}( - a.parent, - newsize, - newstrides, - a.offset + _computeviewoffset(a.strides, I), - a.op; - normalize = false + return _stridedview( + T, a.parent, newsize, newstrides, + a.offset + _computeviewoffset(a.strides, I), a.op ) end @@ -182,29 +178,20 @@ end Base.conj(a::StridedView{<:Real}) = a function Base.conj(a::StridedView{T}) where {T <: Complex} newop = _conj(a.op) - return StridedView{T}( - a.parent, a.size, a.strides, a.offset, newop; - normalize = false - ) + return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.conj(a::StridedView) S = Base.promote_op(a.op, eltype(a)) newop = _conj(a.op) T = Base.promote_op(newop, S) - return StridedView{T}( - a.parent, a.size, a.strides, a.offset, newop; - normalize = false - ) + return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.permutedims(a::StridedView{T, N}, p) where {T, N} _isperm(N, p) || throw(ArgumentError("Invalid permutation of length $N: $p")) newsize = ntuple(n -> size(a, p[n]), Val(N)) newstrides = ntuple(n -> stride(a, p[n]), Val(N)) - return StridedView{T}( - a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op; - normalize = false - ) + return _stridedview(T, a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op) end LinearAlgebra.transpose(a::StridedView{<:Number, 2}) = permutedims(a, (2, 1)) @@ -214,10 +201,7 @@ function LinearAlgebra.adjoint(a::StridedView{<:Any, 2}) # act recursively, like newop = _adjoint(a.op) T = Base.promote_op(newop, S) return permutedims( - StridedView{T}( - a.parent, a.size, a.strides, a.offset, newop; - normalize = false - ), (2, 1) + _stridedview(T, a.parent, a.size, a.strides, a.offset, newop), (2, 1) ) end function LinearAlgebra.transpose(a::StridedView{<:Any, 2}) # act recursively, like Base @@ -225,10 +209,7 @@ function LinearAlgebra.transpose(a::StridedView{<:Any, 2}) # act recursively, li newop = _transpose(a.op) T = Base.promote_op(newop, S) return permutedims( - StridedView{T}( - a.parent, a.size, a.strides, a.offset, newop; - normalize = false - ), (2, 1) + _stridedview(T, a.parent, a.size, a.strides, a.offset, newop), (2, 1) ) end @@ -238,26 +219,17 @@ Base.map(::FA, a::StridedView{<:Number}) = conj(a) function Base.map(::FC, a::StridedView) T = Base.promote_op(conj, eltype(a)) newop = _conj(a.op) - return StridedView{T}( - a.parent, a.size, a.strides, a.offset, newop; - normalize = false - ) + return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.map(::FT, a::StridedView) T = Base.promote_op(transpose, eltype(a)) newop = _transpose(a.op) - return StridedView{T}( - a.parent, a.size, a.strides, a.offset, newop; - normalize = false - ) + return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.map(::FA, a::StridedView) T = Base.promote_op(adjoint, eltype(a)) newop = _adjoint(a.op) - return StridedView{T}( - a.parent, a.size, a.strides, a.offset, newop; - normalize = false - ) + return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end # Creating or transforming StridedView by slicing @@ -302,10 +274,7 @@ function sreshape(a::StridedView{T}, newsize::Dims) where {T} newstrides = _computereshapestrides(newsize, _simplifydims(size(a), strides(a))...) end isnothing(newstrides) && throw(ReshapeException(newsize, size(a), strides(a))) - return StridedView{T}( - a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op; - normalize = false - ) + return _stridedview(T, a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op) end sreshape(a::AbstractArray, newsize::Dims) = sreshape(StridedView(a), newsize) diff --git a/test/runtests.jl b/test/runtests.jl index 778f16a..0004fa9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -234,9 +234,10 @@ if !is_buildkite @test sreshape(B, size(B)) === B @test strides(permutedims(B, (2, 1, 3))) == (1, 1, 4) @test strides(B[1:2:3, :, :]) == (2, 4, 4) - @test StridedView( - B.parent, size(B), strides(B), B.offset, B.op; normalize = false + @test StridedViews._stridedview( + eltype(B), B.parent, size(B), strides(B), B.offset, B.op ) === B + @test @inferred(StridedView(A)) == A C = sreshape(B, (2, 1, 1, 4, 1, 1)) @test strides(C) == (1, 2, 2, 2, 8, 8) @test C == reshape(A, (2, 1, 1, 4, 1, 1)) From 51a18adad2ca8887cfdc3cf63b7f1ff08595e3c2 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Mon, 12 Oct 2026 17:02:29 -0400 Subject: [PATCH 2/4] Bump version to 0.5.5 Co-Authored-By: Claude Opus 5.5 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 68d7765..e730c7a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "StridedViews" uuid = "4db3bf67-4bd7-4b4e-b153-31dc3fb37143" authors = ["Lukas Devos ", "Jutho Haegeman "] -version = "0.5.4" +version = "0.5.5" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" From 91f9ede2ea7a3aedb1dd7dcb13cc1117f9bf0940 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Tue, 13 Oct 2026 09:12:56 -0400 Subject: [PATCH 3/4] Rename `_stridedview` to `unsafe_stridedview` Co-Authored-By: Claude Opus 5.5 --- src/stridedview.jl | 24 ++++++++++++------------ test/runtests.jl | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/stridedview.jl b/src/stridedview.jl index 07a7113..c382917 100644 --- a/src/stridedview.jl +++ b/src/stridedview.jl @@ -57,7 +57,7 @@ function StridedView{T}( end # `parent` and `strides` taken from an existing `StridedView` are already normalized -function _stridedview( +function unsafe_stridedview( ::Type{T}, parent::DenseArray, size::NTuple{N, Int}, strides::NTuple{N, Int}, offset::Int, op::F ) where {T, N, F} @@ -79,7 +79,7 @@ function StridedView(a::Base.ReinterpretArray{T, N}) where {T, N} throw(ArgumentError("Cannot create StridedView with reinterpretation from $S to $T")) b.op isa FN || throw(ArgumentError("Cannot create StridedView with reinterpretation from view with non-identity operation")) - return _stridedview(T, b.parent, size(b), strides(b), offset(b), identity) + return unsafe_stridedview(T, b.parent, size(b), strides(b), offset(b), identity) end # trait @@ -150,7 +150,7 @@ end function Base.getindex(a::StridedView{T, N}, I::Vararg{SliceIndex, N}) where {T, N} newsize = _computeviewsize(a.size, I) newstrides = _normalizestrides(newsize, _computeviewstrides(a.strides, I)) - return _stridedview( + return unsafe_stridedview( T, a.parent, newsize, newstrides, a.offset + _computeviewoffset(a.strides, I), a.op ) @@ -178,20 +178,20 @@ end Base.conj(a::StridedView{<:Real}) = a function Base.conj(a::StridedView{T}) where {T <: Complex} newop = _conj(a.op) - return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) + return unsafe_stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.conj(a::StridedView) S = Base.promote_op(a.op, eltype(a)) newop = _conj(a.op) T = Base.promote_op(newop, S) - return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) + return unsafe_stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.permutedims(a::StridedView{T, N}, p) where {T, N} _isperm(N, p) || throw(ArgumentError("Invalid permutation of length $N: $p")) newsize = ntuple(n -> size(a, p[n]), Val(N)) newstrides = ntuple(n -> stride(a, p[n]), Val(N)) - return _stridedview(T, a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op) + return unsafe_stridedview(T, a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op) end LinearAlgebra.transpose(a::StridedView{<:Number, 2}) = permutedims(a, (2, 1)) @@ -201,7 +201,7 @@ function LinearAlgebra.adjoint(a::StridedView{<:Any, 2}) # act recursively, like newop = _adjoint(a.op) T = Base.promote_op(newop, S) return permutedims( - _stridedview(T, a.parent, a.size, a.strides, a.offset, newop), (2, 1) + unsafe_stridedview(T, a.parent, a.size, a.strides, a.offset, newop), (2, 1) ) end function LinearAlgebra.transpose(a::StridedView{<:Any, 2}) # act recursively, like Base @@ -209,7 +209,7 @@ function LinearAlgebra.transpose(a::StridedView{<:Any, 2}) # act recursively, li newop = _transpose(a.op) T = Base.promote_op(newop, S) return permutedims( - _stridedview(T, a.parent, a.size, a.strides, a.offset, newop), (2, 1) + unsafe_stridedview(T, a.parent, a.size, a.strides, a.offset, newop), (2, 1) ) end @@ -219,17 +219,17 @@ Base.map(::FA, a::StridedView{<:Number}) = conj(a) function Base.map(::FC, a::StridedView) T = Base.promote_op(conj, eltype(a)) newop = _conj(a.op) - return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) + return unsafe_stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.map(::FT, a::StridedView) T = Base.promote_op(transpose, eltype(a)) newop = _transpose(a.op) - return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) + return unsafe_stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end function Base.map(::FA, a::StridedView) T = Base.promote_op(adjoint, eltype(a)) newop = _adjoint(a.op) - return _stridedview(T, a.parent, a.size, a.strides, a.offset, newop) + return unsafe_stridedview(T, a.parent, a.size, a.strides, a.offset, newop) end # Creating or transforming StridedView by slicing @@ -274,7 +274,7 @@ function sreshape(a::StridedView{T}, newsize::Dims) where {T} newstrides = _computereshapestrides(newsize, _simplifydims(size(a), strides(a))...) end isnothing(newstrides) && throw(ReshapeException(newsize, size(a), strides(a))) - return _stridedview(T, a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op) + return unsafe_stridedview(T, a.parent, newsize, _normalizestrides(newsize, newstrides), a.offset, a.op) end sreshape(a::AbstractArray, newsize::Dims) = sreshape(StridedView(a), newsize) diff --git a/test/runtests.jl b/test/runtests.jl index 0004fa9..ae42ec0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -234,7 +234,7 @@ if !is_buildkite @test sreshape(B, size(B)) === B @test strides(permutedims(B, (2, 1, 3))) == (1, 1, 4) @test strides(B[1:2:3, :, :]) == (2, 4, 4) - @test StridedViews._stridedview( + @test StridedViews.unsafe_stridedview( eltype(B), B.parent, size(B), strides(B), B.offset, B.op ) === B @test @inferred(StridedView(A)) == A From c2580731539788dceb881534ff35316d5871a798 Mon Sep 17 00:00:00 2001 From: lkdvos Date: Tue, 13 Oct 2026 09:21:30 -0400 Subject: [PATCH 4/4] Mark `unsafe_stridedview` as public Co-Authored-By: Claude Opus 5.5 --- src/StridedViews.jl | 3 +++ src/stridedview.jl | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/StridedViews.jl b/src/StridedViews.jl index dc2e1c2..6bf1d5e 100644 --- a/src/StridedViews.jl +++ b/src/StridedViews.jl @@ -6,6 +6,9 @@ const SliceIndex = Union{RangeIndex, Colon} using LinearAlgebra export StridedView, sreshape, sview, isstrided +@static if VERSION >= v"1.11.0-DEV.469" + eval(Expr(:public, :unsafe_stridedview)) +end include("auxiliary.jl") include("stridedview.jl") diff --git a/src/stridedview.jl b/src/stridedview.jl index c382917..a996aac 100644 --- a/src/stridedview.jl +++ b/src/stridedview.jl @@ -56,7 +56,12 @@ function StridedView{T}( return StridedView{T, N, typeof(parent′), F}(parent′, size, strides′, offset, op) end -# `parent` and `strides` taken from an existing `StridedView` are already normalized +""" + unsafe_stridedview(T, parent, size, strides, offset, op) + +Construct a `StridedView{T}` without normalizing `parent` and `strides`, which are assumed to +be normalized already, e.g. because they are taken from an existing `StridedView`. +""" function unsafe_stridedview( ::Type{T}, parent::DenseArray, size::NTuple{N, Int}, strides::NTuple{N, Int}, offset::Int, op::F