Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use missing instead of nothing for dynamic values #232

Merged
merged 8 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 2 additions & 17 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "ArrayInterface"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "3.2.2"
version = "4"

[deps]
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
Expand All @@ -14,20 +14,5 @@ Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
Compat = "3.37"
IfElse = "0.1"
Requires = "0.5, 1.0"
Static = "0.4"
Static = "0.5"
julia = "1.2"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
BandedMatrices = "aae01518-5342-5314-be14-df237901396f"
BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0"
IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
LabelledArrays = "2ee39098-c373-598a-b85f-a56591580800"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
SuiteSparse = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "LabelledArrays", "StaticArrays", "BandedMatrices", "BlockBandedMatrices", "SuiteSparse", "Random", "OffsetArrays", "Aqua", "IfElse"]
1 change: 0 additions & 1 deletion docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ ArrayInterface.matrix_colors
ArrayInterface.offset1
ArrayInterface.offsets
ArrayInterface.parent_type
ArrayInterface.reduce_tup
ArrayInterface.restructure
ArrayInterface.safevec
ArrayInterface.setindex!
Expand Down
8 changes: 4 additions & 4 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ julia> ArrayInterface.size(a)
(static(1), 3)

julia> ArrayInterface.known_size(typeof(a))
(1, nothing)
(1, missing)

```

Expand All @@ -62,8 +62,8 @@ Methods should avoid forcing conversion to static sizes when dynamic sizes could
Fore example, `fxn(x) = _fxn(Static.static(ArrayInterface.size(x)), x)` would result in dynamic dispatch if `x` is an instance of `Matrix`.
Additionally, `ArrayInterface.size` should only be used outside of generated functions to avoid possible world age issues.

Generally, `ArrayInterface.size` uses the return of `known_size` to form a static value for those dimensions with known length and only queries dimensions corresponding to `nothing`.
For example, the previous example had a known size of `(1, nothing)`.
Generally, `ArrayInterface.size` uses the return of `known_size` to form a static value for those dimensions with known length and only queries dimensions corresponding to `missing`.
For example, the previous example had a known size of `(1, missing)`.
Therefore, `ArrayInterface.size` would have compile time information about the first dimension returned as `static(1)` and would only look up the size of the second dimension at run time.
This means the above example `ArrayInterface.size(a)` would lower to code similar to this at compile time: `Static.StaticInt(1), Base.arraysize(x, 1)`.
Generic support for `ArrayInterface.known_size` relies on calling `known_length` for each type returned from `axes_types`.
Expand Down Expand Up @@ -155,7 +155,7 @@ using ArrayInterface: axes_types, parent_type, to_dims
for dim in 1:ndims(A)
# offset relative to parent array
O = relative_known_offsets(A, dim)
if O === nothing # offset is not known at compile time and is an `Int`
if O === missing # offset is not known at compile time and is an `Int`
push!(out.args, :(IdOffsetRange{Int, axes_types($P, $(static(dim)))}))
else # offset is known, therefore it is a `StaticInt`
push!(out.args, :(IdOffsetRange{StaticInt{$O}, axes_types($P, $(static(dim))}))
Expand Down
226 changes: 214 additions & 12 deletions src/ArrayInterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ using Requires
using LinearAlgebra
using SparseArrays
using Static
using Static: Zero, One, nstatic, _get_tuple, eq, ne, gt, ge, lt, le, eachop, eachop_tuple,
find_first_eq, permute, invariant_permutation
using Static: Zero, One, nstatic, eq, ne, gt, ge, lt, le, eachop, eachop_tuple,
find_first_eq, permute, invariant_permutation, field_type, reduce_tup
using Base.Cartesian
import Compat

Expand Down Expand Up @@ -81,10 +81,10 @@ buffer(x::SparseMatrixCSC) = getfield(x, :nzval)
buffer(x::SparseVector) = getfield(x, :nzval)

"""
known_length(::Type{T}) -> Union{Int,Nothing}
known_length(::Type{T}) -> Union{Int,Missing}

If `length` of an instance of type `T` is known at compile time, return it.
Otherwise, return `nothing`.
Otherwise, return `missing`.
"""
known_length(x) = known_length(typeof(x))
known_length(::Type{<:NamedTuple{L}}) where {L} = length(L)
Expand All @@ -95,13 +95,11 @@ known_length(::Type{<:Number}) = 1
known_length(::Type{<:AbstractCartesianIndex{N}}) where {N} = N
function known_length(::Type{T}) where {T}
if parent_type(T) <: T
return nothing
return missing
else
return _known_length(known_size(T))
return prod(known_size(T))
end
end
_known_length(x::Tuple{Vararg{Union{Nothing,Int}}}) = nothing
_known_length(x::Tuple{Vararg{Int}}) = prod(x)

"""
can_change_size(::Type{T}) -> Bool
Expand Down Expand Up @@ -419,10 +417,10 @@ Indicates the most efficient way to access elements from the collection in low-l
For `GPUArrays`, will return `ArrayInterface.GPU()`.
For `AbstractArray` supporting a `pointer` method, returns `ArrayInterface.CPUPointer()`.
For other `AbstractArray`s and `Tuple`s, returns `ArrayInterface.CPUIndex()`.
Otherwise, returns `nothing`.
Otherwise, returns `missing`.
"""
device(A) = device(typeof(A))
device(::Type) = nothing
device(::Type) = missing
device(::Type{<:Tuple}) = CPUTuple()
device(::Type{T}) where {T<:Array} = CPUPointer()
device(::Type{T}) where {T<:AbstractArray} = _device(has_parent(T), T)
Expand Down Expand Up @@ -601,7 +599,7 @@ end

function Base.length(A::AbstractArray2)
len = known_length(A)
if len === nothing
if len === missing
return Int(prod(size(A)))
else
return Int(len)
Expand Down Expand Up @@ -807,6 +805,53 @@ function __init__()
end

@require BandedMatrices = "aae01518-5342-5314-be14-df237901396f" begin
struct BandedMatrixIndex <: MatrixIndex
count::Int
rowsize::Int
colsize::Int
bandinds::Array{Int,1}
bandsizes::Array{Int,1}
isrow::Bool
end

Base.firstindex(i::BandedMatrixIndex) = 1
Base.lastindex(i::BandedMatrixIndex) = i.count
Base.length(i::BandedMatrixIndex) = lastindex(i)
@propagate_inbounds function Base.getindex(ind::BandedMatrixIndex, i::Int)
@boundscheck 1 <= i <= ind.count || throw(BoundsError(ind, i))
_i = i
p = 1
while _i - ind.bandsizes[p] > 0
_i -= ind.bandsizes[p]
p += 1
end
bandind = ind.bandinds[p]
startfromone = !xor(ind.isrow, (bandind > 0))
if startfromone
return _i
else
return _i + abs(bandind)
end
end

function _bandsize(bandind, rowsize, colsize)
-(rowsize - 1) <= bandind <= colsize - 1 || throw(ErrorException("Invalid Bandind"))
if (bandind * (colsize - rowsize) > 0) & (abs(bandind) <= abs(colsize - rowsize))
return min(rowsize, colsize)
elseif bandind * (colsize - rowsize) <= 0
return min(rowsize, colsize) - abs(bandind)
else
return min(rowsize, colsize) - abs(bandind) + abs(colsize - rowsize)
end
end

function BandedMatrixIndex(rowsize, colsize, lowerbandwidth, upperbandwidth, isrow)
upperbandwidth > -lowerbandwidth || throw(ErrorException("Invalid Bandwidths"))
bandinds = upperbandwidth:-1:-lowerbandwidth
bandsizes = [_bandsize(band, rowsize, colsize) for band in bandinds]
BandedMatrixIndex(sum(bandsizes), rowsize, colsize, bandinds, bandsizes, isrow)
end

function findstructralnz(x::BandedMatrices.BandedMatrix)
l, u = BandedMatrices.bandwidths(x)
rowsize, colsize = Base.size(x)
Expand All @@ -829,6 +874,82 @@ function __init__()

@require BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" begin
@require BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" begin
struct BlockBandedMatrixIndex <: MatrixIndex
count::Int
refinds::Array{Int,1}
refcoords::Array{Int,1}# storing col or row inds at ref points
isrow::Bool
end
Base.firstindex(i::BlockBandedMatrixIndex) = 1
Base.lastindex(i::BlockBandedMatrixIndex) = i.count
Base.length(i::BlockBandedMatrixIndex) = lastindex(i)
function BlockBandedMatrixIndex(nrowblock, ncolblock, rowsizes, colsizes, l, u)
blockrowind = BandedMatrixIndex(nrowblock, ncolblock, l, u, true)
blockcolind = BandedMatrixIndex(nrowblock, ncolblock, l, u, false)
sortedinds = sort(
[(blockrowind[i], blockcolind[i]) for i = 1:length(blockrowind)],
by = x -> x[1],
)
sort!(sortedinds, by = x -> x[2], alg = InsertionSort)# stable sort keeps the second index in order
refinds = Array{Int,1}()
refrowcoords = Array{Int,1}()
refcolcoords = Array{Int,1}()
rowheights = pushfirst!(copy(rowsizes), 1)
cumsum!(rowheights, rowheights)
blockheight = 0
blockrow = 1
blockcol = 1
currenti = 1
lastrowind = sortedinds[1][1] - 1
lastcolind = sortedinds[1][2]
for ind in sortedinds
rowind, colind = ind
if colind == lastcolind
if rowind > lastrowind
blockheight += rowsizes[rowind]
end
else
for j = blockcol:blockcol+colsizes[lastcolind]-1
push!(refinds, currenti)
push!(refrowcoords, blockrow)
push!(refcolcoords, j)
currenti += blockheight
end
blockcol += colsizes[lastcolind]
blockrow = rowheights[rowind]
blockheight = rowsizes[rowind]
end
lastcolind = colind
lastrowind = rowind
end
for j = blockcol:blockcol+colsizes[lastcolind]-1
push!(refinds, currenti)
push!(refrowcoords, blockrow)
push!(refcolcoords, j)
currenti += blockheight
end
push!(refinds, currenti)# guard
push!(refrowcoords, -1)
push!(refcolcoords, -1)
rowindobj = BlockBandedMatrixIndex(currenti - 1, refinds, refrowcoords, true)
colindobj = BlockBandedMatrixIndex(currenti - 1, refinds, refcolcoords, false)
rowindobj, colindobj
end
@propagate_inbounds function Base.getindex(ind::BlockBandedMatrixIndex, i::Int)
@boundscheck 1 <= i <= ind.count || throw(BoundsError(ind, i))
p = 1
while i - ind.refinds[p] >= 0
p += 1
end
p -= 1
_i = i - ind.refinds[p]
if ind.isrow
return ind.refcoords[p] + _i
else
return ind.refcoords[p]
end
end

function findstructralnz(x::BlockBandedMatrices.BlockBandedMatrix)
l, u = BlockBandedMatrices.blockbandwidths(x)
nrowblock = BlockBandedMatrices.blocksize(x, 1)
Expand All @@ -844,6 +965,87 @@ function __init__()
u,
)
end
struct BandedBlockBandedMatrixIndex <: MatrixIndex
count::Int
refinds::Array{Int,1}
refcoords::Array{Int,1}# storing col or row inds at ref points
reflocalinds::Array{BandedMatrixIndex,1}
isrow::Bool
end
Base.firstindex(i::BandedBlockBandedMatrixIndex) = 1
Base.lastindex(i::BandedBlockBandedMatrixIndex) = i.count
Base.length(i::BandedBlockBandedMatrixIndex) = lastindex(i)
@propagate_inbounds function Base.getindex(ind::BandedBlockBandedMatrixIndex, i::Int)
@boundscheck 1 <= i <= ind.count || throw(BoundsError(ind, i))
p = 1
while i - ind.refinds[p] >= 0
p += 1
end
p -= 1
_i = i - ind.refinds[p] + 1
ind.reflocalinds[p][_i] + ind.refcoords[p] - 1
end


function BandedBlockBandedMatrixIndex(
nrowblock,
ncolblock,
rowsizes,
colsizes,
l,
u,
lambda,
mu,
)
blockrowind = BandedMatrixIndex(nrowblock, ncolblock, l, u, true)
blockcolind = BandedMatrixIndex(nrowblock, ncolblock, l, u, false)
sortedinds = sort(
[(blockrowind[i], blockcolind[i]) for i = 1:length(blockrowind)],
by = x -> x[1],
)
sort!(sortedinds, by = x -> x[2], alg = InsertionSort)# stable sort keeps the second index in order
rowheights = pushfirst!(copy(rowsizes), 1)
cumsum!(rowheights, rowheights)
colwidths = pushfirst!(copy(colsizes), 1)
cumsum!(colwidths, colwidths)
currenti = 1
refinds = Array{Int,1}()
refrowcoords = Array{Int,1}()
refcolcoords = Array{Int,1}()
reflocalrowinds = Array{BandedMatrixIndex,1}()
reflocalcolinds = Array{BandedMatrixIndex,1}()
for ind in sortedinds
rowind, colind = ind
localrowind =
BandedMatrixIndex(rowsizes[rowind], colsizes[colind], lambda, mu, true)
localcolind =
BandedMatrixIndex(rowsizes[rowind], colsizes[colind], lambda, mu, false)
push!(refinds, currenti)
push!(refrowcoords, rowheights[rowind])
push!(refcolcoords, colwidths[colind])
push!(reflocalrowinds, localrowind)
push!(reflocalcolinds, localcolind)
currenti += localrowind.count
end
push!(refinds, currenti)
push!(refrowcoords, -1)
push!(refcolcoords, -1)
rowindobj = BandedBlockBandedMatrixIndex(
currenti - 1,
refinds,
refrowcoords,
reflocalrowinds,
true,
)
colindobj = BandedBlockBandedMatrixIndex(
currenti - 1,
refinds,
refcolcoords,
reflocalcolinds,
false,
)
rowindobj, colindobj
end

function findstructralnz(x::BlockBandedMatrices.BandedBlockBandedMatrix)
l, u = BlockBandedMatrices.blockbandwidths(x)
Expand Down Expand Up @@ -936,7 +1138,7 @@ function __init__()
Static.eachop_tuple(_offset_axis_type, Static.nstatic(Val(ndims(T))), ArrayInterface.parent_type(T))
end
function ArrayInterface.known_offsets(::Type{A}) where {A<:OffsetArrays.OffsetArray}
ntuple(identity -> nothing, Val(ndims(A)))
ntuple(identity -> missing, Val(ndims(A)))
end
function ArrayInterface.offsets(A::OffsetArrays.OffsetArray)
map(+, ArrayInterface.offsets(parent(A)), relative_offsets(A))
Expand Down
Loading