JuliaArrays / BlockArrays.jl

BlockArrays for Julia
http://juliaarrays.github.io/BlockArrays.jl/
Other
193 stars 27 forks source link

zero dimensional BlockArray and BlockedArray are broken #409

Closed ogauthe closed 1 month ago

ogauthe commented 1 month ago

Hi!

I am writing generic code using BlockArrays that should work for any dimension. I run into issues with zero dimension arrays:

  1. It is not possible to construct a BlockArray from a zero dimensional Julia Array:
    using BlockArrays
    zerodim = ones(())
    BlockArray(zerodim)  # MethodError
    
    ERROR: MethodError: no method matching _BlockArray(::Array{Float64, 0}, ::Tuple{})

Closest candidates are: _BlockArray(::Type{R}, ::Tuple{Vararg{AbstractUnitRange{<:Integer}, N}}) where {T, N, R<:(AbstractArray{<:AbstractArray{T, N}, N})} @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/blockarray.jl:93 _BlockArray(::R, ::BS) where {T, N, R<:(AbstractArray{<:AbstractArray{T, N}, N}), BS<:Tuple{Vararg{AbstractUnitRange{<:Integer}, N}}} @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/blockarray.jl:65 _BlockArray(::R, ::BS) where {N, R<:(AbstractArray{<:AbstractArray{V, N} where V, N}), BS<:Tuple{Vararg{AbstractUnitRange{<:Integer}, N}}} @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/blockarray.jl:77

Stacktrace: [1] (BlockArray{Float64, N, R} where {N, R<:(AbstractArray{<:AbstractArray{…}, N})})(arr::Array{Float64, 0}, baxes::Tuple{}) @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/blockarray.jl:265 [2] BlockArray(arr::Array{Float64, 0}, baxes::Tuple{}) @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/blockarray.jl:271 [3] (BlockArray{Float64, 0, R, BS} where {R<:(AbstractArray{<:AbstractArray{…}, 0}), BS<:Tuple{}})(A::Array{Float64, 0}) @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/blockarray.jl:390 [4] BlockArray(A::Array{Float64, 0}) @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/blockarray.jl:393 [5] top-level scope @ REPL[200]:1 Some type information was truncated. Use show(err) to see complete types.


2. It is possible to construct a `BlockedArray`. It is possible to access its coefficient with no index, but not with an empty `Block`
```julia
using BlockArrays
zerodim = ones(())
blockedarray = BlockedArray(zerodim)  # Ok
@show blockedarray[]  # Ok
blockedarray[Block()]  # StackOverflow
ERROR: StackOverflowError:
Stacktrace:
     [1] unsafe_view(::Array{Float64, 0})
       @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/views.jl:94
     [2] view(::Array{Float64, 0})
       @ Base ./subarray.jl:186
--- the last 2 lines are repeated 39990 more times ---
 [79983] unsafe_view(::Array{Float64, 0})
       @ BlockArrays ~/.julia/packages/BlockArrays/xi4on/src/views.jl:94

Julia v1.10.4 BlockArrays v1.1.0

dlfivefifty commented 1 month ago

Any chance you can put together a PR?

ogauthe commented 1 month ago

I can give it a try.

I am surprised by view behavior: is it expected to overload Base for a non-blockarray?

julia> zerodim = ones(())
0-dimensional Array{Float64, 0}:
1.0

julia> view(zerodim)
0-dimensional view(::Array{Float64, 0}) with eltype Float64:
1.0

julia> using BlockArrays

julia> view(zerodim)
ERROR: StackOverflowError:
Stacktrace:
     [1] view(::Array{Float64, 0})
       @ Base ./subarray.jl:186
     [2] unsafe_view(::Array{Float64, 0})
       @ BlockArrays ~/Documents/itensor/BlockArrays.jl/src/views.jl:94
--- the last 2 lines are repeated 39990 more times ---
 [79983] view(::Array{Float64, 0})
       @ Base ./subarray.jl:186
dlfivefifty commented 1 month ago

That’s a bug, the sort of thing that comes up if you do f(A::MyType…) and base does f(::OtherType…)

jishnub commented 1 month ago

In this case, the problematic method is

@propagate_inbounds  function Base.unsafe_view(
        A::Array{<:Any, N},
        I::Vararg{BlockSlice{<:BlockIndexRange{1}}, N}) where {N}
    return view(A, map(x -> x.indices, I)...)
end

where, if N == 0, it becomes Base.unsafe_view(::Array{<:Any,0}), which is firstly type-piracy, and secondly, self-recursive. Fixing this isn't trivial, though.

dlfivefifty commented 1 month ago

can we change the override to unsafe_view(A::Array, I1::BlockSlice, Is…) ?

it can call an internal method that has the more restrictive signature