Skip to content

Commit f589311

Browse files
dlfivefiftyjishnubputianyi889
authored
Release 1.0 (#231)
* StepRangeLen to support 0 step size in cumsum #226 (#230) * use StepRangeLen to support 0 step size * v0.13.11 * allow special casing on ∞ length * Restore ± special cases * Update runtests.jl * fix more tests * Update runtests.jl * Structured Broadcasting (#228) * Structured Broadcasting * add jishnubs tests * Update runtests.jl * Fix #97 by adding promote_rules (#234) * Move OneElement from Zygote and overload setindex (#161) (#235) * Add Zeros(T, n...) and Ones(T, n...) constructors (#94( (#233) * Add Zeros(T, n...) and Ones(T, n...) constructors (#94( * increase coverage * Update README.md * Move over OneElement from Zygote * Add tests * Update oneelement.jl * add tests * Update runtests.jl * add docs * ensure type in array convert (#237) * increase coverage * add convert tests * v1.0 * `_fill_dot` support general vectors (#229) * Update fillalgebra.jl * promote_op * add breaking test * add breaking test * fix * accept round-off errors * Update test/runtests.jl Co-authored-by: Sheehan Olver <[email protected]> * update * support inf and nan * fix 1.6 * Update fillalgebra.jl * Update fillalgebra.jl * trying to fix Julia 1.6 * comments * Update runtests.jl * add @inferred --------- Co-authored-by: Sheehan Olver <[email protected]> --------- Co-authored-by: Jishnu Bhattacharya <[email protected]> Co-authored-by: Tianyi Pu <[email protected]>
1 parent 269fd41 commit f589311

File tree

6 files changed

+230
-70
lines changed

6 files changed

+230
-70
lines changed

Project.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "FillArrays"
22
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
3-
version = "0.13.11"
3+
version = "1.0"
44

55
[deps]
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
@@ -9,7 +9,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
99
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1010

1111
[compat]
12-
Aqua = "0.5"
12+
Aqua = "0.5, 0.6"
1313
julia = "1.6"
1414

1515
[extras]

src/FillArrays.jl

+54-8
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Base: size, getindex, setindex!, IndexStyle, checkbounds, convert,
66
+, -, *, /, \, diff, sum, cumsum, maximum, minimum, sort, sort!,
77
any, all, axes, isone, iterate, unique, allunique, permutedims, inv,
88
copy, vec, setindex!, count, ==, reshape, _throw_dmrs, map, zero,
9-
show, view, in, mapreduce, one, reverse, promote_op
9+
show, view, in, mapreduce, one, reverse, promote_op, promote_rule
1010

1111
import LinearAlgebra: rank, svdvals!, tril, triu, tril!, triu!, diag, transpose, adjoint, fill!,
1212
dot, norm2, norm1, normInf, normMinusInf, normp, lmul!, rmul!, diagzero, AdjointAbsVec, TransposeAbsVec,
@@ -18,7 +18,7 @@ import Base.Broadcast: broadcasted, DefaultArrayStyle, broadcast_shape
1818
import Statistics: mean, std, var, cov, cor
1919

2020

21-
export Zeros, Ones, Fill, Eye, Trues, Falses
21+
export Zeros, Ones, Fill, Eye, Trues, Falses, OneElement
2222

2323
import Base: oneto
2424

@@ -34,6 +34,7 @@ const AbstractFillVecOrMat{T} = Union{AbstractFillVector{T},AbstractFillMatrix{T
3434

3535
==(a::AbstractFill, b::AbstractFill) = axes(a) == axes(b) && getindex_value(a) == getindex_value(b)
3636

37+
3738
@inline function _fill_getindex(F::AbstractFill, kj::Integer...)
3839
@boundscheck checkbounds(F, kj...)
3940
getindex_value(F)
@@ -147,15 +148,47 @@ Fill{T,0}(x::T, ::Tuple{}) where T = Fill{T,0,Tuple{}}(x, ()) # ambiguity fix
147148
@inline axes(F::Fill) = F.axes
148149
@inline size(F::Fill) = map(length, F.axes)
149150

151+
"""
152+
getindex_value(F::AbstractFill)
153+
154+
Return the value that `F` is filled with.
155+
156+
# Examples
157+
158+
```jldoctest
159+
julia> f = Ones(3);
160+
161+
julia> FillArrays.getindex_value(f)
162+
1.0
163+
164+
julia> g = Fill(2, 10);
165+
166+
julia> FillArrays.getindex_value(g)
167+
2
168+
```
169+
"""
170+
getindex_value
171+
150172
@inline getindex_value(F::Fill) = F.value
151173

152174
AbstractArray{T}(F::Fill{V,N}) where {T,V,N} = Fill{T}(convert(T, F.value)::T, F.axes)
153175
AbstractArray{T,N}(F::Fill{V,N}) where {T,V,N} = Fill{T}(convert(T, F.value)::T, F.axes)
154176
AbstractFill{T}(F::AbstractFill) where T = AbstractArray{T}(F)
177+
AbstractFill{T,N}(F::AbstractFill) where {T,N} = AbstractArray{T,N}(F)
178+
AbstractFill{T,N,Ax}(F::AbstractFill{<:Any,N,Ax}) where {T,N,Ax} = AbstractArray{T,N}(F)
179+
180+
convert(::Type{AbstractFill{T}}, F::AbstractFill) where T = convert(AbstractArray{T}, F)
181+
convert(::Type{AbstractFill{T,N}}, F::AbstractFill) where {T,N} = convert(AbstractArray{T,N}, F)
182+
convert(::Type{AbstractFill{T,N,Ax}}, F::AbstractFill{<:Any,N,Ax}) where {T,N,Ax} = convert(AbstractArray{T,N}, F)
155183

156184
copy(F::Fill) = Fill(F.value, F.axes)
157185

158-
""" Throws an error if `arr` does not contain one and only one unique value. """
186+
"""
187+
unique_value(arr::AbstractArray)
188+
189+
Return `only(unique(arr))` without intermediate allocations.
190+
Throws an error if `arr` does not contain one and only one unique value.
191+
"""
159192
function unique_value(arr::AbstractArray)
160193
if isempty(arr) error("Cannot convert empty array to Fill") end
161194
val = first(arr)
@@ -274,6 +307,14 @@ for (Typ, funcs, func) in ((:Zeros, :zeros, :zero), (:Ones, :ones, :one))
274307
copy(F::$Typ) = F
275308

276309
getindex(F::$Typ{T,0}) where T = getindex_value(F)
310+
311+
promote_rule(::Type{$Typ{T, N, Axes}}, ::Type{$Typ{V, N, Axes}}) where {T,V,N,Axes} = $Typ{promote_type(T,V),N,Axes}
312+
function convert(::Type{$Typ{T,N,Axes}}, A::$Typ{V,N,Axes}) where {T,V,N,Axes}
313+
convert(T, getindex_value(A)) # checks that the types are convertible
314+
$Typ{T,N,Axes}(axes(A))
315+
end
316+
convert(::Type{$Typ{T,N}}, A::$Typ{V,N,Axes}) where {T,V,N,Axes} = convert($Typ{T,N,Axes}, A)
317+
convert(::Type{$Typ{T}}, A::$Typ{V,N,Axes}) where {T,V,N,Axes} = convert($Typ{T,N,Axes}, A)
277318
end
278319
end
279320

@@ -285,6 +326,8 @@ for TYPE in (:Fill, :AbstractFill, :Ones, :Zeros), STYPE in (:AbstractArray, :Ab
285326
end
286327
end
287328

329+
promote_rule(::Type{<:AbstractFill{T, N, Axes}}, ::Type{<:AbstractFill{V, N, Axes}}) where {T,V,N,Axes} = Fill{promote_type(T,V),N,Axes}
330+
288331
"""
289332
fillsimilar(a::AbstractFill, axes)
290333
@@ -427,7 +470,8 @@ end
427470

428471

429472
## Array
430-
Base.Array{T,N}(F::AbstractFill{V,N}) where {T,V,N} = fill(convert(T, getindex_value(F)), size(F))
473+
Base.Array{T,N}(F::AbstractFill{V,N}) where {T,V,N} =
474+
convert(Array{T,N}, fill(convert(T, getindex_value(F)), size(F)))
431475

432476
# These are in case `zeros` or `ones` are ever faster than `fill`
433477
for (Typ, funcs, func) in ((:Zeros, :zeros, :zero), (:Ones, :ones, :one))
@@ -438,7 +482,7 @@ end
438482

439483
# temporary patch. should be a PR(#48895) to LinearAlgebra
440484
Diagonal{T}(A::AbstractFillMatrix) where T = Diagonal{T}(diag(A))
441-
function convert(::Type{T}, A::AbstractFillMatrix) where T<:Diagonal
485+
function convert(::Type{T}, A::AbstractFillMatrix) where T<:Diagonal
442486
checksquare(A)
443487
isdiag(A) ? T(A) : throw(InexactError(:convert, T, A))
444488
end
@@ -497,14 +541,14 @@ sum(x::AbstractFill) = getindex_value(x)*length(x)
497541
sum(f, x::AbstractFill) = length(x) * f(getindex_value(x))
498542
sum(x::Zeros) = getindex_value(x)
499543

500-
cumsum(x::AbstractFill{<:Any,1}) = range(getindex_value(x); step=getindex_value(x),
501-
length=length(x))
544+
# needed to support infinite case
545+
steprangelen(st...) = StepRangeLen(st...)
546+
cumsum(x::AbstractFill{<:Any,1}) = steprangelen(getindex_value(x), getindex_value(x), length(x))
502547

503548
cumsum(x::ZerosVector) = x
504549
cumsum(x::ZerosVector{Bool}) = x
505550
cumsum(x::OnesVector{II}) where II<:Integer = convert(AbstractVector{II}, oneto(length(x)))
506551
cumsum(x::OnesVector{Bool}) = oneto(length(x))
507-
cumsum(x::AbstractFillVector{Bool}) = cumsum(AbstractFill{Int}(x))
508552

509553

510554
#########
@@ -718,4 +762,6 @@ Base.@propagate_inbounds function view(A::AbstractFill, I::Vararg{Real})
718762
fillsimilar(A)
719763
end
720764

765+
include("oneelement.jl")
766+
721767
end # module

src/fillalgebra.jl

+7-23
Original file line numberDiff line numberDiff line change
@@ -159,38 +159,22 @@ function *(a::Transpose{T, <:AbstractVector{T}}, b::ZerosVector{T}) where T<:Rea
159159
end
160160
*(a::Transpose{T, <:AbstractMatrix{T}}, b::ZerosVector{T}) where T<:Real = mult_zeros(a, b)
161161

162-
# treat zero separately to support ∞-vectors
163-
function _zero_dot(a, b)
164-
axes(a) == axes(b) || throw(DimensionMismatch("dot product arguments have lengths $(length(a)) and $(length(b))"))
165-
zero(promote_type(eltype(a),eltype(b)))
166-
end
167-
168-
_fill_dot(a::Zeros, b::Zeros) = _zero_dot(a, b)
169-
_fill_dot(a::Zeros, b) = _zero_dot(a, b)
170-
_fill_dot(a, b::Zeros) = _zero_dot(a, b)
171-
_fill_dot(a::Zeros, b::AbstractFill) = _zero_dot(a, b)
172-
_fill_dot(a::AbstractFill, b::Zeros) = _zero_dot(a, b)
173-
174-
function _fill_dot(a::AbstractFill, b::AbstractFill)
175-
axes(a) == axes(b) || throw(DimensionMismatch("dot product arguments have lengths $(length(a)) and $(length(b))"))
176-
getindex_value(a)getindex_value(b)*length(b)
177-
end
178-
179162
# support types with fast sum
180-
function _fill_dot(a::AbstractFill, b)
163+
# infinite cases should be supported in InfiniteArrays.jl
164+
# type issues of Bool dot are ignored at present.
165+
function _fill_dot(a::AbstractFillVector{T}, b::AbstractVector{V}) where {T,V}
181166
axes(a) == axes(b) || throw(DimensionMismatch("dot product arguments have lengths $(length(a)) and $(length(b))"))
182-
getindex_value(a)sum(b)
167+
dot(getindex_value(a), sum(b))
183168
end
184169

185-
function _fill_dot(a, b::AbstractFill)
170+
function _fill_dot_rev(a::AbstractVector{T}, b::AbstractFillVector{V}) where {T,V}
186171
axes(a) == axes(b) || throw(DimensionMismatch("dot product arguments have lengths $(length(a)) and $(length(b))"))
187-
sum(a)getindex_value(b)
172+
dot(sum(a), getindex_value(b))
188173
end
189174

190-
191175
dot(a::AbstractFillVector, b::AbstractFillVector) = _fill_dot(a, b)
192176
dot(a::AbstractFillVector, b::AbstractVector) = _fill_dot(a, b)
193-
dot(a::AbstractVector, b::AbstractFillVector) = _fill_dot(a, b)
177+
dot(a::AbstractVector, b::AbstractFillVector) = _fill_dot_rev(a, b)
194178

195179
function dot(u::AbstractVector, E::Eye, v::AbstractVector)
196180
length(u) == size(E,1) && length(v) == size(E,2) ||

src/fillbroadcast.jl

+5-4
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ _broadcasted_zeros(f, a, b) = Zeros{Base.Broadcast.combine_eltypes(f, (a, b))}(b
102102
_broadcasted_ones(f, a, b) = Ones{Base.Broadcast.combine_eltypes(f, (a, b))}(broadcast_shape(axes(a), axes(b)))
103103
_broadcasted_nan(f, a, b) = Fill(convert(Base.Broadcast.combine_eltypes(f, (a, b)), NaN), broadcast_shape(axes(a), axes(b)))
104104

105-
# TODO: remove at next breaking version
106-
_broadcasted_zeros(a, b) = _broadcasted_zeros(+, a, b)
107-
_broadcasted_ones(a, b) = _broadcasted_ones(+, a, b)
108-
109105
broadcasted(::DefaultArrayStyle, ::typeof(+), a::Zeros, b::Zeros) = _broadcasted_zeros(+, a, b)
110106
broadcasted(::DefaultArrayStyle, ::typeof(+), a::Ones, b::Zeros) = _broadcasted_ones(+, a, b)
111107
broadcasted(::DefaultArrayStyle, ::typeof(+), a::Zeros, b::Ones) = _broadcasted_ones(+, a, b)
@@ -247,3 +243,8 @@ broadcasted(::DefaultArrayStyle{N}, ::typeof(Base.literal_pow), ::Base.RefValue{
247243
broadcasted(::DefaultArrayStyle{N}, ::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::Ones{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = Ones{T}(axes(r))
248244
broadcasted(::DefaultArrayStyle{N}, ::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::Zeros{T,N}, ::Base.RefValue{Val{0}}) where {T,N} = Ones{T}(axes(r))
249245
broadcasted(::DefaultArrayStyle{N}, ::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::Zeros{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = Zeros{T}(axes(r))
246+
247+
# supports structured broadcast
248+
if isdefined(LinearAlgebra, :fzero)
249+
LinearAlgebra.fzero(x::Zeros) = zero(eltype(x))
250+
end

src/oneelement.jl

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
OneElement(val, ind, axesorsize) <: AbstractArray
3+
4+
Represents an array with the specified axes (if its a tuple of `AbstractUnitRange`s)
5+
or size (if its a tuple of `Integer`s), with a single entry set to `val` and all others equal to zero,
6+
specified by `ind``.
7+
"""
8+
struct OneElement{T,N,I,A} <: AbstractArray{T,N}
9+
val::T
10+
ind::I
11+
axes::A
12+
OneElement(val::T, ind::I, axes::A) where {T<:Number, I<:NTuple{N,Int}, A<:NTuple{N,AbstractUnitRange}} where {N} = new{T,N,I,A}(val, ind, axes)
13+
end
14+
15+
OneElement(val, inds::NTuple{N,Int}, sz::NTuple{N,Integer}) where N = OneElement(val, inds, oneto.(sz))
16+
"""
17+
OneElement(val, ind::Int, n::Int)
18+
19+
Creates a length `n` vector where the `ind` entry is equal to `val`, and all other entries are zero.
20+
"""
21+
OneElement(val, ind::Int, len::Int) = OneElement(val, (ind,), (len,))
22+
"""
23+
OneElement(ind::Int, n::Int)
24+
25+
Creates a length `n` vector where the `ind` entry is equal to `1`, and all other entries are zero.
26+
"""
27+
OneElement(inds::Int, sz::Int) = OneElement(1, inds, sz)
28+
OneElement{T}(val, inds::NTuple{N,Int}, sz::NTuple{N,Integer}) where {T,N} = OneElement(convert(T,val), inds, oneto.(sz))
29+
OneElement{T}(val, inds::Int, sz::Int) where T = OneElement{T}(val, (inds,), (sz,))
30+
31+
"""
32+
OneElement{T}(val, ind::Int, n::Int)
33+
34+
Creates a length `n` vector where the `ind` entry is equal to `one(T)`, and all other entries are zero.
35+
"""
36+
OneElement{T}(inds::Int, sz::Int) where T = OneElement(one(T), inds, sz)
37+
38+
Base.size(A::OneElement) = map(length, A.axes)
39+
Base.axes(A::OneElement) = A.axes
40+
function Base.getindex(A::OneElement{T,N}, kj::Vararg{Int,N}) where {T,N}
41+
@boundscheck checkbounds(A, kj...)
42+
ifelse(kj == A.ind, A.val, zero(T))
43+
end
44+
45+
Base.replace_in_print_matrix(o::OneElement{<:Any,2}, k::Integer, j::Integer, s::AbstractString) =
46+
o.ind == (k,j) ? s : Base.replace_with_centered_mark(s)
47+
48+
function Base.setindex(A::Zeros{T,N}, v, kj::Vararg{Int,N}) where {T,N}
49+
@boundscheck checkbounds(A, kj...)
50+
OneElement(convert(T, v), kj, axes(A))
51+
end

0 commit comments

Comments
 (0)