JuliaArrays / OffsetArrays.jl

Fortran-like arrays with arbitrary, zero or negative starting indices.
Other
195 stars 40 forks source link

`IdOffsetRange(0x01:0x03, -1)` -- corner case for zero-based `Unsigned` indices: `OA(SparseVector{Float64, UInt}, 0:(n-1))` #320

Open denius opened 1 year ago

denius commented 1 year ago

Hello,

It is a strange issue or maybe I'm missing something. Is it issue of SparseArrays or OffsetArrays?

With UInt64 can't create OffsetArray(SparseVector{Float64, UInt64}, 0:2), MWE:

julia> using OffsetArrays, SparseArrays

julia> sv = SparseVector(3, UInt64[1], Float64[2.0])
3-element SparseVector{Float64, UInt64} with 1 stored entry:
  [1]  =  2.0

julia> osv = OffsetVector(sv, 0:2)
ERROR: InexactError: check_top_bit(Int64, 18446744073709551615)
Stacktrace:
  [1] throw_inexacterror(f::Symbol, #unused#::Type{Int64}, val::UInt64)
    @ Core ./boot.jl:614
  [2] check_top_bit
    @ ./boot.jl:628 [inlined]
  [3] toInt64
    @ ./boot.jl:689 [inlined]
  [4] Int64
    @ ./boot.jl:764 [inlined]
  [5] map
    @ ./tuple.jl:221 [inlined]
  [6] #_#46
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:235 [inlined]
  [7] OffsetArray
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:234 [inlined]
  [8] #OffsetVector#12
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:167 [inlined]
  [9] OffsetArray
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:165 [inlined]
 [10] #OffsetVector#27
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:204 [inlined]
 [11] OffsetArray
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:197 [inlined]
 [12] #OffsetVector#29
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:207 [inlined]
 [13] (OffsetVector{T} where T)(A::SparseVector{Float64, UInt64}, inds::UnitRange{Int64})
    @ OffsetArrays ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:207
 [14] top-level scope
    @ REPL[90]:1

With UInt32 OffsetArray created, but can't display it in REPL:

julia> sv = SparseVector(3, UInt32[1], Float64[2.0])
3-element SparseVector{Float64, UInt32} with 1 stored entry:
  [1]  =  2.0

julia> osv = OffsetVector(sv, 0:2)
Error showing value of type OffsetVector{Float64, SparseVector{Float64, UInt32}}:
ERROR: InexactError: trunc(UInt32, -1)
Stacktrace:
  [1] throw_inexacterror(f::Symbol, #unused#::Type{UInt32}, val::Int64)
    @ Core ./boot.jl:614
  [2] checked_trunc_uint
    @ ./boot.jl:644 [inlined]
  [3] toUInt32
    @ ./boot.jl:728 [inlined]
  [4] UInt32
    @ ./boot.jl:768 [inlined]
  [5] convert
    @ ./number.jl:7 [inlined]
  [6] IdOffsetRange
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/axes.jl:115 [inlined]
  [7] map
    @ ./tuple.jl:246 [inlined]
  [8] axes
    @ ~/.julia/packages/OffsetArrays/WvkHl/src/OffsetArrays.jl:295 [inlined]
  [9] summary
    @ ./show.jl:2814 [inlined]
 [10] show(io::IOContext{Base.TTY}, #unused#::MIME{Symbol("text/plain")}, X::OffsetVector{Float64, SparseVector{Float64, UInt32}})
    @ Base ./arrayshow.jl:368
......

julia> dump(osv)
OffsetVector{Float64, SparseVector{Float64, UInt32}}
  parent: SparseVector{Float64, UInt32}
    n: UInt32 0x00000003
    nzind: Array{UInt32}((1,)) UInt32[0x00000001]
    nzval: Array{Float64}((1,)) [2.0]
  offsets: Tuple{Int64}
    1: Int64 -1

Environment:

julia> versioninfo()
Julia Version 1.8.5
Commit 17cfb8e65e (2023-01-08 06:45 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 24 × Intel(R) Xeon(R) Gold 6146 CPU @ 3.20GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, skylake-avx512)
  Threads: 8 on 24 virtual cores
Environment:
  JULIA_NUM_THREADS = 8
  JULIA_CUDA_USE_BINARYBUILDER = false

Thanks.

denius commented 1 year ago

Found source of errors: https://github.com/JuliaArrays/OffsetArrays.jl/blob/08dc3711b88930e59a27d1b870415c394219e4e0/src/axes.jl#L116

Here convert(T, offset), i.e. convert(UInt, -1).

Is it possible to solve this issue? I just need SparseVectors with UInt zero-based indices.

jishnub commented 1 year ago

That's an interesting corner case, and I can't think of a quick fix. Perhaps we need to relax the type signatures of IdDffsetRange so that the offsets may be signed. Unfortunately, this will need to be benchmarked quite a bit, so it'll take a while to get added. If you have the time, and you're familiar with the package, please feel free to make a PR