Skip to content

Commit 999a4ff

Browse files
authored
Deprecate cumsum, cumprod, cumsum_kbn, and accumulate when dim isn't specified (#24684)
* Deprecate cumsum, cumprod, cumsum_kbn, and accumulate when dim isn't specified and dimension is larger than one * Add docstrings for accumulation methods on vectors Fix bug in accumulate when initial value is provided since the implementation doesn't support multidimentional arrays. Move the accumulate methods with initial values to the end * Adjust docstrings to match methods * axis -> dim * Update NEWS.md
1 parent 35d72bc commit 999a4ff

File tree

5 files changed

+190
-95
lines changed

5 files changed

+190
-95
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,9 @@ Deprecated or removed
663663
* `a:b` is deprecated for constructing a `StepRange` when `a` and `b` have physical units
664664
(Dates and Times). Use `a:s:b`, where `s = Dates.Day(1)` or `s = Dates.Second(1)`.
665665

666+
* `cumsum`, `cumprod`, `accumulate`, and their mutating versions now require a `dim`
667+
argument instead of defaulting to using the first dimension ([#24684]).
668+
666669
Command-line option changes
667670
---------------------------
668671

base/abstractarraymath.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,12 @@ end
265265
# TODO: Needs a separate IndexCartesian method, this is only fast for IndexLinear
266266

267267
"""
268-
cumsum_kbn(A, [dim::Integer=1])
268+
cumsum_kbn(A, dim::Integer)
269269
270270
Cumulative sum along a dimension, using the Kahan-Babuska-Neumaier compensated summation
271-
algorithm for additional accuracy. The dimension defaults to 1.
271+
algorithm for additional accuracy.
272272
"""
273-
function cumsum_kbn(A::AbstractArray{T}, axis::Integer=1) where T<:AbstractFloat
273+
function cumsum_kbn(A::AbstractArray{T}, axis::Integer) where T<:AbstractFloat
274274
dimsA = size(A)
275275
ndimsA = ndims(A)
276276
axis_size = dimsA[axis]

base/deprecated.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,6 +2105,9 @@ end
21052105
@deprecate chol!(x::Number, uplo) chol(x) false
21062106
end
21072107

2108+
@deprecate cumsum(A::AbstractArray) cumsum(A, 1)
2109+
@deprecate cumsum_kbn(A::AbstractArray) cumsum_kbn(A, 1)
2110+
@deprecate cumprod(A::AbstractArray) cumprod(A, 1)
21082111

21092112
# issue #16307
21102113
@deprecate finalizer(o, f::Function) finalizer(f, o)

base/multidimensional.jl

Lines changed: 180 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -675,25 +675,25 @@ function accumulate_pairwise(op, v::AbstractVector{T}) where T
675675
return accumulate_pairwise!(op, out, v)
676676
end
677677

678-
function cumsum!(out, v::AbstractVector, axis::Integer=1)
678+
function cumsum!(out, v::AbstractVector, dim::Integer)
679679
# we dispatch on the possibility of numerical stability issues
680-
_cumsum!(out, v, axis, TypeArithmetic(eltype(out)))
680+
_cumsum!(out, v, dim, TypeArithmetic(eltype(out)))
681681
end
682682

683-
function _cumsum!(out, v, axis, ::ArithmeticRounds)
684-
axis == 1 ? accumulate_pairwise!(+, out, v) : copy!(out, v)
683+
function _cumsum!(out, v, dim, ::ArithmeticRounds)
684+
dim == 1 ? accumulate_pairwise!(+, out, v) : copy!(out, v)
685685
end
686-
function _cumsum!(out, v, axis, ::ArithmeticUnknown)
687-
_cumsum!(out, v, axis, ArithmeticRounds())
686+
function _cumsum!(out, v, dim, ::ArithmeticUnknown)
687+
_cumsum!(out, v, dim, ArithmeticRounds())
688688
end
689-
function _cumsum!(out, v, axis, ::TypeArithmetic)
690-
axis == 1 ? accumulate!(+, out, v) : copy!(out, v)
689+
function _cumsum!(out, v, dim, ::TypeArithmetic)
690+
dim == 1 ? accumulate!(+, out, v) : copy!(out, v)
691691
end
692692

693693
"""
694-
cumsum(A, dim=1)
694+
cumsum(A, dim::Integer)
695695
696-
Cumulative sum along a dimension `dim`. See also [`cumsum!`](@ref)
696+
Cumulative sum along the dimension `dim`. See also [`cumsum!`](@ref)
697697
to use a preallocated output array, both for performance and to control the precision of the
698698
output (e.g. to avoid overflow).
699699
@@ -714,22 +714,52 @@ julia> cumsum(a,2)
714714
4 9 15
715715
```
716716
"""
717-
function cumsum(A::AbstractArray{T}, axis::Integer=1) where T
717+
function cumsum(A::AbstractArray{T}, dim::Integer) where T
718718
out = similar(A, rcum_promote_type(+, T))
719-
cumsum!(out, A, axis)
719+
cumsum!(out, A, dim)
720720
end
721721

722722
"""
723-
cumsum!(B, A, dim::Integer=1)
723+
cumsum(x::AbstractVector)
724724
725-
Cumulative sum of `A` along a dimension, storing the result in `B`. See also [`cumsum`](@ref).
725+
Cumulative sum a vector. See also [`cumsum!`](@ref)
726+
to use a preallocated output array, both for performance and to control the precision of the
727+
output (e.g. to avoid overflow).
728+
729+
```jldoctest
730+
julia> cumsum([1, 1, 1])
731+
3-element Array{Int64,1}:
732+
1
733+
2
734+
3
735+
736+
julia> cumsum([fill(1, 2) for i in 1:3])
737+
3-element Array{Array{Int64,1},1}:
738+
[1, 1]
739+
[2, 2]
740+
[3, 3]
741+
```
742+
"""
743+
cumsum(x::AbstractVector) = cumsum(x, 1)
744+
745+
"""
746+
cumsum!(B, A, dim::Integer)
747+
748+
Cumulative sum of `A` along the dimension `dim`, storing the result in `B`. See also [`cumsum`](@ref).
749+
"""
750+
cumsum!(B, A, dim::Integer) = accumulate!(+, B, A, dim)
751+
752+
"""
753+
cumsum!(y::AbstractVector, x::AbstractVector)
754+
755+
Cumulative sum of a vector `x`, storing the result in `y`. See also [`cumsum`](@ref).
726756
"""
727-
cumsum!(B, A, axis::Integer=1) = accumulate!(+, B, A, axis)
757+
cumsum!(y::AbstractVector, x::AbstractVector) = cumsum!(y, x, 1)
728758

729759
"""
730-
cumprod(A, dim=1)
760+
cumprod(A, dim::Integer)
731761
732-
Cumulative product along a dimension `dim`. See also
762+
Cumulative product along the dimension `dim`. See also
733763
[`cumprod!`](@ref) to use a preallocated output array, both for performance and
734764
to control the precision of the output (e.g. to avoid overflow).
735765
@@ -750,20 +780,79 @@ julia> cumprod(a,2)
750780
4 20 120
751781
```
752782
"""
753-
cumprod(A::AbstractArray, axis::Integer=1) = accumulate(*, A, axis)
783+
cumprod(A::AbstractArray, dim::Integer) = accumulate(*, A, dim)
784+
785+
"""
786+
cumprod(x::AbstractVector)
787+
788+
Cumulative product of a vector. See also
789+
[`cumprod!`](@ref) to use a preallocated output array, both for performance and
790+
to control the precision of the output (e.g. to avoid overflow).
791+
792+
```jldoctest
793+
julia> cumprod(fill(1//2, 3))
794+
3-element Array{Rational{Int64},1}:
795+
1//2
796+
1//4
797+
1//8
798+
799+
julia> cumprod([fill(1//3, 2, 2) for i in 1:3])
800+
3-element Array{Array{Rational{Int64},2},1}:
801+
Rational{Int64}[1//3 1//3; 1//3 1//3]
802+
Rational{Int64}[2//9 2//9; 2//9 2//9]
803+
Rational{Int64}[4//27 4//27; 4//27 4//27]
804+
```
805+
"""
806+
cumprod(x::AbstractVector) = cumprod(x, 1)
754807

755808
"""
756-
cumprod!(B, A, dim::Integer=1)
809+
cumprod!(B, A, dim::Integer)
757810
758-
Cumulative product of `A` along a dimension, storing the result in `B`.
811+
Cumulative product of `A` along the dimension `dim`, storing the result in `B`.
759812
See also [`cumprod`](@ref).
760813
"""
761-
cumprod!(B, A, axis::Integer=1) = accumulate!(*, B, A, axis)
814+
cumprod!(B, A, dim::Integer) = accumulate!(*, B, A, dim)
762815

763816
"""
764-
accumulate(op, A, dim=1)
817+
cumprod!(y::AbstractVector, x::AbstractVector)
765818
766-
Cumulative operation `op` along a dimension `dim`. See also
819+
Cumulative product of a vector `x`, storing the result in `y`.
820+
See also [`cumprod`](@ref).
821+
"""
822+
cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, 1)
823+
824+
"""
825+
accumulate(op, A, dim::Integer)
826+
827+
Cumulative operation `op` along the dimension `dim`. See also
828+
[`accumulate!`](@ref) to use a preallocated output array, both for performance and
829+
to control the precision of the output (e.g. to avoid overflow). For common operations
830+
there are specialized variants of `accumulate`, see:
831+
[`cumsum`](@ref), [`cumprod`](@ref)
832+
833+
```jldoctest
834+
julia> accumulate(+, fill(1, 3, 3), 1)
835+
3×3 Array{Int64,2}:
836+
1 1 1
837+
2 2 2
838+
3 3 3
839+
840+
julia> accumulate(+, fill(1, 3, 3), 2)
841+
3×3 Array{Int64,2}:
842+
1 2 3
843+
1 2 3
844+
1 2 3
845+
```
846+
"""
847+
function accumulate(op, A, dim::Integer)
848+
out = similar(A, rcum_promote_type(op, eltype(A)))
849+
accumulate!(op, out, A, dim)
850+
end
851+
852+
"""
853+
accumulate(op, x::AbstractVector)
854+
855+
Cumulative operation `op` on a vector. See also
767856
[`accumulate!`](@ref) to use a preallocated output array, both for performance and
768857
to control the precision of the output (e.g. to avoid overflow). For common operations
769858
there are specialized variants of `accumulate`, see:
@@ -783,14 +872,67 @@ julia> accumulate(*, [1,2,3])
783872
6
784873
```
785874
"""
786-
function accumulate(op, A, axis::Integer=1)
787-
out = similar(A, rcum_promote_type(op, eltype(A)))
788-
accumulate!(op, out, A, axis)
875+
accumulate(op, x::AbstractVector) = accumulate(op, x, 1)
876+
877+
"""
878+
accumulate!(op, B, A, dim::Integer)
879+
880+
Cumulative operation `op` on `A` along the dimension `dim`, storing the result in `B`.
881+
See also [`accumulate`](@ref).
882+
"""
883+
function accumulate!(op, B, A, dim::Integer)
884+
dim > 0 || throw(ArgumentError("dim must be a positive integer"))
885+
inds_t = indices(A)
886+
indices(B) == inds_t || throw(DimensionMismatch("shape of B must match A"))
887+
dim > ndims(A) && return copy!(B, A)
888+
isempty(inds_t[dim]) && return B
889+
if dim == 1
890+
# We can accumulate to a temporary variable, which allows
891+
# register usage and will be slightly faster
892+
ind1 = inds_t[1]
893+
@inbounds for I in CartesianRange(tail(inds_t))
894+
tmp = convert(eltype(B), A[first(ind1), I])
895+
B[first(ind1), I] = tmp
896+
for i_1 = first(ind1)+1:last(ind1)
897+
tmp = op(tmp, A[i_1, I])
898+
B[i_1, I] = tmp
899+
end
900+
end
901+
else
902+
R1 = CartesianRange(indices(A)[1:dim-1]) # not type-stable
903+
R2 = CartesianRange(indices(A)[dim+1:end])
904+
_accumulate!(op, B, A, R1, inds_t[dim], R2) # use function barrier
905+
end
906+
return B
789907
end
790908

909+
"""
910+
accumulate!(op, y, x::AbstractVector)
791911
912+
Cumulative operation `op` on a vector `x`, storing the result in `y`.
913+
See also [`accumulate`](@ref).
792914
"""
793-
accumulate(op, v0, A)
915+
function accumulate!(op::Op, y, x::AbstractVector) where Op
916+
isempty(x) && return y
917+
v1 = first(x)
918+
_accumulate1!(op, y, v1, x, 1)
919+
end
920+
921+
@noinline function _accumulate!(op, B, A, R1, ind, R2)
922+
# Copy the initial element in each 1d vector along dimension `dim`
923+
ii = first(ind)
924+
@inbounds for J in R2, I in R1
925+
B[I, ii, J] = A[I, ii, J]
926+
end
927+
# Accumulate
928+
@inbounds for J in R2, i in first(ind)+1:last(ind), I in R1
929+
B[I, i, J] = op(B[I, i-1, J], A[I, i, J])
930+
end
931+
B
932+
end
933+
934+
"""
935+
accumulate(op, v0, x::AbstractVector)
794936
795937
Like `accumulate`, but using a starting element `v0`. The first entry of the result will be
796938
`op(v0, first(A))`.
@@ -810,30 +952,23 @@ julia> accumulate(min, 0, [1,2,-1])
810952
-1
811953
```
812954
"""
813-
function accumulate(op, v0, A, axis::Integer=1)
814-
T = rcum_promote_type(op, typeof(v0), eltype(A))
815-
out = similar(A, T)
816-
accumulate!(op, out, v0, A, 1)
817-
end
818-
819-
function accumulate!(op::Op, B, A::AbstractVector, axis::Integer=1) where Op
820-
isempty(A) && return B
821-
v1 = first(A)
822-
_accumulate1!(op, B, v1, A, axis)
955+
function accumulate(op, v0, x::AbstractVector)
956+
T = rcum_promote_type(op, typeof(v0), eltype(x))
957+
out = similar(x, T)
958+
accumulate!(op, out, v0, x)
823959
end
824960

825-
function accumulate!(op, B, v0, A::AbstractVector, axis::Integer=1)
826-
isempty(A) && return B
827-
v1 = op(v0, first(A))
828-
_accumulate1!(op, B, v1, A, axis)
961+
function accumulate!(op, y, v0, x::AbstractVector)
962+
isempty(x) && return y
963+
v1 = op(v0, first(x))
964+
_accumulate1!(op, y, v1, x, 1)
829965
end
830966

831-
832-
function _accumulate1!(op, B, v1, A::AbstractVector, axis::Integer=1)
833-
axis > 0 || throw(ArgumentError("axis must be a positive integer"))
967+
function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer)
968+
dim > 0 || throw(ArgumentError("dim must be a positive integer"))
834969
inds = linearindices(A)
835970
inds == linearindices(B) || throw(DimensionMismatch("linearindices of A and B don't match"))
836-
axis > 1 && return copy!(B, A)
971+
dim > 1 && return copy!(B, A)
837972
i1 = inds[1]
838973
cur_val = v1
839974
B[i1] = cur_val
@@ -844,51 +979,6 @@ function _accumulate1!(op, B, v1, A::AbstractVector, axis::Integer=1)
844979
return B
845980
end
846981

847-
"""
848-
accumulate!(op, B, A, dim=1)
849-
850-
Cumulative operation `op` on `A` along a dimension, storing the result in `B`.
851-
See also [`accumulate`](@ref).
852-
"""
853-
function accumulate!(op, B, A, axis::Integer=1)
854-
axis > 0 || throw(ArgumentError("axis must be a positive integer"))
855-
inds_t = indices(A)
856-
indices(B) == inds_t || throw(DimensionMismatch("shape of B must match A"))
857-
axis > ndims(A) && return copy!(B, A)
858-
isempty(inds_t[axis]) && return B
859-
if axis == 1
860-
# We can accumulate to a temporary variable, which allows
861-
# register usage and will be slightly faster
862-
ind1 = inds_t[1]
863-
@inbounds for I in CartesianRange(tail(inds_t))
864-
tmp = convert(eltype(B), A[first(ind1), I])
865-
B[first(ind1), I] = tmp
866-
for i_1 = first(ind1)+1:last(ind1)
867-
tmp = op(tmp, A[i_1, I])
868-
B[i_1, I] = tmp
869-
end
870-
end
871-
else
872-
R1 = CartesianRange(indices(A)[1:axis-1]) # not type-stable
873-
R2 = CartesianRange(indices(A)[axis+1:end])
874-
_accumulate!(op, B, A, R1, inds_t[axis], R2) # use function barrier
875-
end
876-
return B
877-
end
878-
879-
@noinline function _accumulate!(op, B, A, R1, ind, R2)
880-
# Copy the initial element in each 1d vector along dimension `axis`
881-
ii = first(ind)
882-
@inbounds for J in R2, I in R1
883-
B[I, ii, J] = A[I, ii, J]
884-
end
885-
# Accumulate
886-
@inbounds for J in R2, i in first(ind)+1:last(ind), I in R1
887-
B[I, i, J] = op(B[I, i-1, J], A[I, i, J])
888-
end
889-
B
890-
end
891-
892982
### from abstractarray.jl
893983

894984
"""

0 commit comments

Comments
 (0)