From 05b76ad4a1c4a5cdaa6c6ca4c545cd7b5093e6ea Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:00:36 +0200 Subject: [PATCH 1/7] Fix `OneElement` constructor docstring (#386) --- src/oneelement.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oneelement.jl b/src/oneelement.jl index 01cdea64..556f692c 100644 --- a/src/oneelement.jl +++ b/src/oneelement.jl @@ -34,7 +34,7 @@ OneElement{T}(val, inds::NTuple{N,Int}, sz::NTuple{N,Integer}) where {T,N} = One OneElement{T}(val, inds::Int, sz::Int) where T = OneElement{T}(val, (inds,), (sz,)) """ - OneElement{T}(val, ind::Int, n::Int) + OneElement{T}(ind::Int, n::Int) Creates a length `n` vector where the `ind` entry is equal to `one(T)`, and all other entries are zero. """ From 6bab7620bbe9700a485f4d78e258dd6ded7a4e4f Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 27 Aug 2024 12:48:17 +0530 Subject: [PATCH 2/7] Diag for OneElement returns a OneElement (#383) --- src/oneelement.jl | 8 ++++++++ test/runtests.jl | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/oneelement.jl b/src/oneelement.jl index 556f692c..ed375f98 100644 --- a/src/oneelement.jl +++ b/src/oneelement.jl @@ -392,6 +392,14 @@ function triu(A::OneElementMatrix, k::Integer=0) OneElement(nzband < k ? zero(A.val) : A.val, A.ind, axes(A)) end +# diag +function diag(O::OneElementMatrix, k::Integer=0) + Base.require_one_based_indexing(O) + len = length(diagind(O, k)) + ind = O.ind[2] - O.ind[1] == k ? (k >= 0 ? O.ind[2] - k : O.ind[1] + k) : len + 1 + OneElement(getindex_value(O), ind, len) +end + # broadcast function broadcasted(::DefaultArrayStyle{N}, ::typeof(conj), r::OneElement{<:Any,N}) where {N} diff --git a/test/runtests.jl b/test/runtests.jl index 0163df8a..ad5f50a7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2699,6 +2699,16 @@ end B = OneElement(2, (1, 2), (Base.IdentityUnitRange(1:1), Base.IdentityUnitRange(2:2))) @test repr(B) == "OneElement(2, (1, 2), (Base.IdentityUnitRange(1:1), Base.IdentityUnitRange(2:2)))" end + + @testset "diag" begin + @testset for sz in [(0,0), (0,1), (1,0), (1,1), (4,4), (4,6), (6,3)], ind in CartesianIndices(sz) + O = OneElement(4, Tuple(ind), sz) + @testset for k in -maximum(sz):maximum(sz) + @test diag(O, k) == diag(Array(O), k) + @test diag(O, k) isa OneElement{Int,1} + end + end + end end @testset "repeat" begin From 7b2bb11e70547ee02a9c8099420aeb606167e806 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 27 Aug 2024 12:48:38 +0530 Subject: [PATCH 3/7] Negating a OneElement produces a OneElement (#381) --- src/oneelement.jl | 2 ++ test/runtests.jl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/oneelement.jl b/src/oneelement.jl index ed375f98..a46f53a4 100644 --- a/src/oneelement.jl +++ b/src/oneelement.jl @@ -141,6 +141,8 @@ function isone(A::OneElementMatrix) isone(getindex_value(A)) end +-(O::OneElement) = OneElement(-O.val, O.ind, O.axes) + *(x::OneElement, b::Number) = OneElement(x.val * b, x.ind, x.axes) *(b::Number, x::OneElement) = OneElement(b * x.val, x.ind, x.axes) /(x::OneElement, b::Number) = OneElement(x.val / b, x.ind, x.axes) diff --git a/test/runtests.jl b/test/runtests.jl index ad5f50a7..c0f36e7f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2171,6 +2171,7 @@ end @test FillArrays.nzind(e₁) == CartesianIndex(2) @test e₁[2] === e₁[2,1] === e₁[2,1,1] === 1 @test_throws BoundsError e₁[6] + @test -e₁ === OneElement(-1, 2, 5) f₁ = AbstractArray{Float64}(e₁) @test f₁ isa OneElement{Float64,1} @@ -2190,6 +2191,7 @@ end V = OneElement(2, (2,3), (3,4)) @test V == [0 0 0 0; 0 0 2 0; 0 0 0 0] @test FillArrays.nzind(V) == CartesianIndex(2,3) + @test -V == OneElement(-2, (2,3), (3,4)) Vf = AbstractArray{Float64}(V) @test Vf isa OneElement{Float64,2} From 414dba754c8c0dc23eadaf28e023425315276061 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 27 Aug 2024 12:55:21 +0530 Subject: [PATCH 4/7] sum for OneElement (#375) * sum for OneElement * Add tests * Accept dims in sum * Add tests * Bump version to v1.13.0 * Ensure that init kwarg works * Update tests for v1.6 --------- Co-authored-by: Sheehan Olver --- Project.toml | 2 +- src/oneelement.jl | 17 +++++++++------- test/runtests.jl | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index f22c04ab..4ec9f49a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "FillArrays" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.12.0" +version = "1.13.0" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/src/oneelement.jl b/src/oneelement.jl index a46f53a4..39687a2a 100644 --- a/src/oneelement.jl +++ b/src/oneelement.jl @@ -159,13 +159,6 @@ function *(A::OneElementMatrix, B::OneElementVecOrMat) OneElement(val, (A.ind[1], B.ind[2:end]...), (axes(A,1), axes(B)[2:end]...)) end -function *(A::AbstractFillMatrix, x::OneElementVector) - check_matmul_sizes(A, x) - val = getindex_value(A) * getindex_value(x) - Fill(val, (axes(A,1),)) -end -*(A::AbstractZerosMatrix, x::OneElementVector) = mult_zeros(A, x) - *(A::OneElementMatrix, x::AbstractZerosVector) = mult_zeros(A, x) function *(A::OneElementMatrix, B::AbstractFillVector) @@ -448,3 +441,13 @@ _maybesize(t) = t Base.show(io::IO, A::OneElement) = print(io, OneElement, "(", A.val, ", ", A.ind, ", ", _maybesize(axes(A)), ")") Base.show(io::IO, A::OneElement{<:Any,1,Tuple{Int},Tuple{Base.OneTo{Int}}}) = print(io, OneElement, "(", A.val, ", ", A.ind[1], ", ", size(A,1), ")") + +# mapreduce +Base.sum(O::OneElement; dims=:, kw...) = _sum(O, dims; kw...) +_sum(O::OneElement, ::Colon; kw...) = sum((getindex_value(O),); kw...) +function _sum(O::OneElement, dims; kw...) + v = _sum(O, :; kw...) + ax = Base.reduced_indices(axes(O), dims) + ind = ntuple(x -> x in dims ? first(ax[x]) + (O.ind[x] in axes(O)[x]) - 1 : O.ind[x], ndims(O)) + OneElement(v, ind, ax) +end diff --git a/test/runtests.jl b/test/runtests.jl index c0f36e7f..cd6555b5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2702,6 +2702,56 @@ end @test repr(B) == "OneElement(2, (1, 2), (Base.IdentityUnitRange(1:1), Base.IdentityUnitRange(2:2)))" end + @testset "sum" begin + @testset "OneElement($v, $ind, $sz)" for (v, ind, sz) in ( + (Int8(2), 3, 4), + (3.0, 5, 4), + (3.0, 0, 0), + (SMatrix{2,2}(1:4), (4, 2), (12,6)), + ) + O = OneElement(v,ind,sz) + A = Array(O) + if VERSION >= v"1.10" + @test @inferred(sum(O)) === sum(A) + else + @test @inferred(sum(O)) == sum(A) + end + @test @inferred(sum(O, init=zero(eltype(O)))) === sum(A, init=zero(eltype(O))) + @test @inferred(sum(x->1, O, init=0)) === sum(Fill(1, axes(O)), init=0) + end + + @testset for O in (OneElement(Int8(2), (1,2), (2,4)), + OneElement(3, (1,2,3), (2,4,4)), + OneElement(2.0, (3,2,5), (2,3,2)), + OneElement(SMatrix{2,2}(1:4), (1,2), (2,4)), + ) + A = Array(O) + init = sum((zero(FillArrays.getindex_value(O)),)) + for i in 1:3 + @test @inferred(sum(O, dims=i)) == sum(A, dims=i) + @test @inferred(sum(O, dims=i, init=init)) == sum(A, dims=i, init=init) + @test @inferred(sum(x->1, O, dims=i, init=0)) == sum(Fill(1, axes(O)), dims=i, init=0) + end + @test @inferred(sum(O, dims=1:1)) == sum(A, dims=1:1) + @test @inferred(sum(O, dims=1:2)) == sum(A, dims=1:2) + @test @inferred(sum(O, dims=1:3)) == sum(A, dims=1:3) + @test @inferred(sum(O, dims=(1,))) == sum(A, dims=(1,)) + @test @inferred(sum(O, dims=(1,2))) == sum(A, dims=(1,2)) + @test @inferred(sum(O, dims=(1,3))) == sum(A, dims=(1,3)) + @test @inferred(sum(O, dims=(2,3))) == sum(A, dims=(2,3)) + @test @inferred(sum(O, dims=(1,2,3))) == sum(A, dims=(1,2,3)) + @test @inferred(sum(O, dims=1:1, init=init)) == sum(A, dims=1:1, init=init) + @test @inferred(sum(O, dims=1:2, init=init)) == sum(A, dims=1:2, init=init) + @test @inferred(sum(O, dims=1:3, init=init)) == sum(A, dims=1:3, init=init) + @test @inferred(sum(O, dims=(1,), init=init)) == sum(A, dims=(1,), init=init) + @test @inferred(sum(O, dims=(1,2), init=init)) == sum(A, dims=(1,2), init=init) + @test @inferred(sum(O, dims=(1,3), init=init)) == sum(A, dims=(1,3), init=init) + @test @inferred(sum(O, dims=(2,3), init=init)) == sum(A, dims=(2,3), init=init) + @test @inferred(sum(O, dims=(1,2,3), init=init)) == sum(A, dims=(1,2,3), init=init) + @test @inferred(sum(x->1, O, dims=(1,2,3), init=0)) == sum(Fill(1, axes(O)), dims=(1,2,3), init=0) + end + end + @testset "diag" begin @testset for sz in [(0,0), (0,1), (1,0), (1,1), (4,4), (4,6), (6,3)], ind in CartesianIndices(sz) O = OneElement(4, Tuple(ind), sz) From f72e220d9144e25fab4e1706357250f19c6b0a29 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 27 Aug 2024 12:56:00 +0530 Subject: [PATCH 5/7] Specialize broadcasting more unary functions over a OneElement (#384) * Specialize broadcasting more unary functions over a OneElement * Add tests --- src/oneelement.jl | 12 ++++-------- test/runtests.jl | 4 +++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/oneelement.jl b/src/oneelement.jl index 39687a2a..a98267a6 100644 --- a/src/oneelement.jl +++ b/src/oneelement.jl @@ -397,14 +397,10 @@ end # broadcast -function broadcasted(::DefaultArrayStyle{N}, ::typeof(conj), r::OneElement{<:Any,N}) where {N} - OneElement(conj(r.val), r.ind, axes(r)) -end -function broadcasted(::DefaultArrayStyle{N}, ::typeof(real), r::OneElement{<:Any,N}) where {N} - OneElement(real(r.val), r.ind, axes(r)) -end -function broadcasted(::DefaultArrayStyle{N}, ::typeof(imag), r::OneElement{<:Any,N}) where {N} - OneElement(imag(r.val), r.ind, axes(r)) +for f in (:abs, :abs2, :conj, :real, :imag) + @eval function broadcasted(::DefaultArrayStyle{N}, ::typeof($f), r::OneElement{<:Any,N}) where {N} + OneElement($f(r.val), r.ind, axes(r)) + end end function broadcasted(::DefaultArrayStyle{N}, ::typeof(^), r::OneElement{<:Any,N}, x::Number) where {N} OneElement(r.val^x, r.ind, axes(r)) diff --git a/test/runtests.jl b/test/runtests.jl index cd6555b5..ee2fb9a7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2669,9 +2669,11 @@ end end @testset "broadcasting" begin - for v in (OneElement(2, 3, 4), OneElement(2im, (1,2), (3,4))) + for v in (OneElement(-2, 3, 4), OneElement(2im, (1,2), (3,4))) w = Array(v) n = 2 + @test abs.(v) == abs.(w) + @test abs2.(v) == abs2.(w) @test real.(v) == real.(w) @test imag.(v) == imag.(w) @test conj.(v) == conj.(w) From 554ddf42b8c5aa6a05e026868bddbf8bb8d8efe9 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 27 Aug 2024 12:58:22 +0530 Subject: [PATCH 6/7] Fix reshape for OneElement when indices lie outside axes (#378) --- src/oneelement.jl | 11 ++++++++--- test/runtests.jl | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/oneelement.jl b/src/oneelement.jl index a98267a6..9b8b8b12 100644 --- a/src/oneelement.jl +++ b/src/oneelement.jl @@ -419,9 +419,14 @@ end function Base.reshape(A::OneElement, shape::Tuple{Vararg{Int}}) prod(shape) == length(A) || throw(DimensionMismatch("new dimension $shape must be consistent with array size $(length(A))")) - # we use the fact that the linear index of the non-zero value is preserved - oldlinind = LinearIndices(A)[A.ind...] - newcartind = CartesianIndices(shape)[oldlinind] + if all(in.(A.ind, axes(A))) + # we use the fact that the linear index of the non-zero value is preserved + oldlinind = LinearIndices(A)[A.ind...] + newcartind = CartesianIndices(shape)[oldlinind] + else + # arbitrarily set to some value outside the domain + newcartind = shape .+ 1 + end OneElement(A.val, Tuple(newcartind), shape) end diff --git a/test/runtests.jl b/test/runtests.jl index ee2fb9a7..258f7920 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2328,6 +2328,10 @@ end end O = OneElement(2, (), ()) @test reshape(O, ()) === O + + O = OneElement(5, 3) + @test reshape(O, 1, 3) == reshape(Array(O), 1, 3) + @test reshape(reshape(O, 1, 3), 3) == O end @testset "isassigned" begin From 7b640420e7b7364fb80e55ebc77a8c240b8c0880 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 27 Aug 2024 13:01:06 +0530 Subject: [PATCH 7/7] Fix issymmetric/ishermitian for block and empty matrices (#377) --- src/FillArrays.jl | 4 ++-- test/runtests.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FillArrays.jl b/src/FillArrays.jl index 170e07d3..bb7ae06d 100644 --- a/src/FillArrays.jl +++ b/src/FillArrays.jl @@ -66,8 +66,8 @@ end rank(F::AbstractFill) = iszero(getindex_value(F)) ? 0 : 1 IndexStyle(::Type{<:AbstractFill{<:Any,N,<:NTuple{N,Base.OneTo{Int}}}}) where N = IndexLinear() -issymmetric(F::AbstractFillMatrix) = axes(F,1) == axes(F,2) -ishermitian(F::AbstractFillMatrix) = issymmetric(F) && iszero(imag(getindex_value(F))) +issymmetric(F::AbstractFillMatrix) = axes(F,1) == axes(F,2) && (isempty(F) || issymmetric(getindex_value(F))) +ishermitian(F::AbstractFillMatrix) = axes(F,1) == axes(F,2) && (isempty(F) || ishermitian(getindex_value(F))) Base.IteratorSize(::Type{<:AbstractFill{T,N,Axes}}) where {T,N,Axes} = _IteratorSize(Axes) _IteratorSize(::Type{Tuple{}}) = Base.HasShape{0}() diff --git a/test/runtests.jl b/test/runtests.jl index 258f7920..85f6e7d3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -608,7 +608,7 @@ end end @testset "ishermitian" begin - for el in (2, 3+0im, 4+5im), size in [(3,3), (3,4)] + @testset for el in (2, 3+0im, 4+5im, [1 2; 3 4], fill(2, 2, 2)), size in [(3,3), (3,4), (0,0), (0,1)] @test issymmetric(Fill(el, size...)) == issymmetric(fill(el, size...)) @test ishermitian(Fill(el, size...)) == ishermitian(fill(el, size...)) end