Skip to content

Commit c2443de

Browse files
committed
add accumulate, accumulate!
- rename cumop! to accumulate! and export it - add specialized 1d method to accumulate! - add accumulate - deprecate cummin, cummax
1 parent 4ba21aa commit c2443de

File tree

8 files changed

+142
-63
lines changed

8 files changed

+142
-63
lines changed

base/abstractarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1407,7 +1407,7 @@ function typed_hvcat{T}(::Type{T}, rows::Tuple{Vararg{Int}}, as...)
14071407
T[rs...;]
14081408
end
14091409

1410-
## Reductions and scans ##
1410+
## Reductions and accumulates ##
14111411

14121412
function isequal(A::AbstractArray, B::AbstractArray)
14131413
if A === B return true end

base/arraymath.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ ctranspose{T}(x::AbstractVector{T}) = T[ ctranspose(v) for i=of_indices(x, OneTo
448448

449449
# see discussion in #18364 ... we try not to widen type of the resulting array
450450
# from cumsum or cumprod, but in some cases (+, Bool) we may not have a choice.
451-
rcum_promote_type{T<:Number}(op, ::Type{T}) = promote_op(op, T)
451+
rcum_promote_type{T<:Number}(op, ::Type{T}) = promote_op(op, T, T)
452452
rcum_promote_type{T}(op, ::Type{T}) = T
453453

454454
# handle sums of Vector{Bool} and similar. it would be nice to handle

base/deprecated.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ end
144144
@deprecate chol(A::Number, ::Type{Val{:L}}) ctranspose(chol(A))
145145
@deprecate chol(A::AbstractMatrix, ::Type{Val{:L}}) ctranspose(chol(A))
146146

147+
@deprecate cummin(A, dim=1) accumulate(min, A, dim=1)
148+
@deprecate cummax(A, dim=1) accumulate(max, A, dim=1)
149+
150+
147151
# Number updates
148152

149153
# rem1 is inconsistent for x==0: The result should both have the same

base/exports.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,11 +498,15 @@ export
498498
conj!,
499499
copy!,
500500
cummax,
501+
cummax!,
501502
cummin,
503+
cummin!,
502504
cumprod,
503505
cumprod!,
504506
cumsum,
505507
cumsum!,
508+
accumulate,
509+
accumulate!,
506510
cumsum_kbn,
507511
eachindex,
508512
extrema,

base/multidimensional.jl

Lines changed: 61 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -479,51 +479,6 @@ end
479479
end
480480
end
481481

482-
for (f, fmod, op) = ((:cummin, :_cummin!, :min), (:cummax, :_cummax!, :max))
483-
@eval function ($f)(v::AbstractVector)
484-
n = length(v)
485-
cur_val = v[1]
486-
res = similar(v, n)
487-
res[1] = cur_val
488-
for i in 2:n
489-
cur_val = ($op)(v[i], cur_val)
490-
res[i] = cur_val
491-
end
492-
return res
493-
end
494-
495-
@eval function ($f)(A::AbstractArray, axis::Integer)
496-
axis > 0 || throw(ArgumentError("axis must be a positive integer"))
497-
res = similar(A)
498-
axis > ndims(A) && return copy!(res, A)
499-
inds = indices(A)
500-
if isempty(inds[axis])
501-
return res
502-
end
503-
R1 = CartesianRange(inds[1:axis-1])
504-
R2 = CartesianRange(inds[axis+1:end])
505-
($fmod)(res, A, R1, R2, axis)
506-
end
507-
508-
@eval @noinline function ($fmod)(res, A::AbstractArray, R1::CartesianRange, R2::CartesianRange, axis::Integer)
509-
inds = indices(A, axis)
510-
i1 = first(inds)
511-
for I2 in R2
512-
for I1 in R1
513-
res[I1, i1, I2] = A[I1, i1, I2]
514-
end
515-
for i = i1+1:last(inds)
516-
for I1 in R1
517-
res[I1, i, I2] = ($op)(A[I1, i, I2], res[I1, i-1, I2])
518-
end
519-
end
520-
end
521-
res
522-
end
523-
524-
@eval ($f)(A::AbstractArray) = ($f)(A, 1)
525-
end
526-
527482
"""
528483
cumsum(A, dim=1)
529484
@@ -548,8 +503,9 @@ julia> cumsum(a,2)
548503
4 9 15
549504
```
550505
"""
551-
cumsum{T}(A::AbstractArray{T}, axis::Integer=1) = cumsum!(similar(A, Base.rcum_promote_type(+, T)), A, axis)
552-
cumsum!(B, A::AbstractArray) = cumsum!(B, A, 1)
506+
cumsum(A::AbstractArray, axis::Integer=1) = accumulate(+, A, axis)
507+
cumsum!(B, A, axis::Integer) = accumulate!(+, B, A, axis)
508+
553509
"""
554510
cumprod(A, dim=1)
555511
@@ -574,13 +530,64 @@ julia> cumprod(a,2)
574530
4 20 120
575531
```
576532
"""
577-
cumprod(A::AbstractArray, axis::Integer=1) = cumprod!(similar(A), A, axis)
578-
cumprod!(B, A) = cumprod!(B, A, 1)
533+
cumprod(A::AbstractArray, axis::Integer=1) = accumulate(*, A, axis)
534+
cumprod!(B, A, axis::Integer) = accumulate!(*, B, A, axis)
579535

580-
cumsum!(B, A, axis::Integer) = cumop!(+, B, A, axis)
581-
cumprod!(B, A, axis::Integer) = cumop!(*, B, A, axis)
536+
"""
537+
accumulate(op, A, dim=1)
538+
539+
Cumulative operation `op` along a dimension `dim` (defaults to 1). See also
540+
[`accumulate!`](:func:`accumulate!`) to use a preallocated output array, both for performance and
541+
to control the precision of the output (e.g. to avoid overflow). For common operations
542+
there are specialized variants of accumulate, see:
543+
[`cumsum`](:func:`cumsum`), [`cummin`](:func:`cummin`), [`cummax`](:func:`cummax`), [`cumprod`](:func:`cumprod`)
544+
545+
```jldoctest
546+
julia> accumulate(+, [1,2,3])
547+
3-element Array{Int64,1}:
548+
1
549+
3
550+
6
551+
552+
julia> accumulate(*, [1,2,3])
553+
3-element Array{Int64,1}:
554+
1
555+
2
556+
6
557+
558+
julia> accumulate(min, [1,2,-1])
559+
3-element Array{Int64,1}:
560+
1
561+
1
562+
-1
563+
```
564+
"""
565+
function accumulate(op, A, axis::Integer=1)
566+
out = similar(A, Base.rcum_promote_type(op, eltype(A)))
567+
accumulate!(op, out, A, axis)
568+
end
582569

583-
function cumop!(op, B, A, axis::Integer)
570+
function accumulate!(op, B, A::AbstractVector, axis::Integer=1)
571+
axis > 0 || throw(ArgumentError("axis must be a positive integer"))
572+
size(B) == size(A) || throw(DimensionMismatch("shape of B must match A"))
573+
isempty(A) && return B
574+
axis > 1 && return copy!(B, A)
575+
cur_val = A[1]
576+
B[1] = cur_val
577+
@inbounds for i in 2:length(A)
578+
cur_val = op(A[i], cur_val)
579+
B[i] = cur_val
580+
end
581+
return B
582+
end
583+
584+
"""
585+
accumulate!(op, B, A, dim=1)
586+
587+
Cumulative operation `op` on A along a dimension, storing the result in B. The dimension defaults to 1.
588+
See also [`accumulate`](:func:`accumulate`).
589+
"""
590+
function accumulate!(op, B, A, axis::Integer=1)
584591
axis > 0 || throw(ArgumentError("axis must be a positive integer"))
585592
inds_t = indices(A)
586593
indices(B) == inds_t || throw(DimensionMismatch("shape of B must match A"))
@@ -601,12 +608,12 @@ function cumop!(op, B, A, axis::Integer)
601608
else
602609
R1 = CartesianRange(indices(A)[1:axis-1]) # not type-stable
603610
R2 = CartesianRange(indices(A)[axis+1:end])
604-
_cumop!(op, B, A, R1, inds_t[axis], R2) # use function barrier
611+
_accumulate!(op, B, A, R1, inds_t[axis], R2) # use function barrier
605612
end
606613
return B
607614
end
608615

609-
@noinline function _cumop!(op, B, A, R1, ind, R2)
616+
@noinline function _accumulate!(op, B, A, R1, ind, R2)
610617
# Copy the initial element in each 1d vector along dimension `axis`
611618
i = first(ind)
612619
@inbounds for J in R2, I in R1

doc/stdlib/arrays.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,38 @@ Indexing, Assignment, and Concatenation
14631463
Array functions
14641464
---------------
14651465

1466+
.. function:: accumulate(op, A, dim=1)
1467+
1468+
.. Docstring generated from Julia source
1469+
1470+
Cumulative operation ``op`` along a dimension ``dim`` (defaults to 1). See also :func:`accumulate!` to use a preallocated output array, both for performance and to control the precision of the output (e.g. to avoid overflow). For common operations there are specialized variants of accumulate, see: :func:`cumsum`\ , :func:`cummin`\ , :func:`cummax`\ , :func:`cumprod`
1471+
1472+
.. doctest::
1473+
1474+
julia> accumulate(+, [1,2,3])
1475+
3-element Array{Int64,1}:
1476+
1
1477+
3
1478+
6
1479+
1480+
julia> accumulate(*, [1,2,3])
1481+
3-element Array{Int64,1}:
1482+
1
1483+
2
1484+
6
1485+
1486+
julia> accumulate(min, [1,2,-1])
1487+
3-element Array{Int64,1}:
1488+
1
1489+
1
1490+
-1
1491+
1492+
.. function:: accumulate!(op, B, A, dim=1)
1493+
1494+
.. Docstring generated from Julia source
1495+
1496+
Cumulative operation ``op`` on A along a dimension, storing the result in B. The dimension defaults to 1. See also :func:`accumulate`\ .
1497+
14661498
.. function:: cumprod(A, dim=1)
14671499

14681500
.. Docstring generated from Julia source

test/arrayops.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,3 +1868,42 @@ end
18681868
@test size(a) == size(b)
18691869
end
18701870
end
1871+
1872+
@testset "accumulate, accumulate!" begin
1873+
1874+
@test accumulate(+, [1,2,3]) == [1, 3, 6]
1875+
@test accumulate(min, [1 2; 3 4], 1) == [1 2; 1 2]
1876+
@test accumulate(max, [1 2; 3 0], 2) == [1 2; 3 3]
1877+
@test accumulate(+, Bool[]) == Int[]
1878+
@test accumulate(*, Bool[]) == Bool[]
1879+
@test accumulate(+, Float64[]) == Float64[]
1880+
1881+
@test accumulate(min, [1, 2, 5, -1, 3, -2]) == [1, 1, 1, -1, -1, -2]
1882+
@test accumulate(max, [1, 2, 5, -1, 3, -2]) == [1, 2, 5, 5, 5, 5]
1883+
1884+
@test accumulate(max, [1 0; 0 1], 1) == [1 0; 1 1]
1885+
@test accumulate(max, [1 0; 0 1], 2) == [1 1; 0 1]
1886+
@test accumulate(min, [1 0; 0 1], 1) == [1 0; 0 0]
1887+
@test accumulate(min, [1 0; 0 1], 2) == [1 0; 0 0]
1888+
1889+
N = 5
1890+
for arr in [rand(Float64, N), rand(Bool, N), rand(-2:2, N)]
1891+
for (op, cumop) in [(+, cumsum), (*, cumprod)]
1892+
@inferred accumulate(op, arr)
1893+
accumulate_arr = accumulate(op, arr)
1894+
@test accumulate_arr cumop(arr)
1895+
@test accumulate_arr[end] reduce(op, arr)
1896+
@test accumulate_arr[1] arr[1]
1897+
@test accumulate(op, arr, 10) arr
1898+
1899+
if eltype(arr) in [Int, Float64] # eltype of out easy
1900+
out = similar(arr)
1901+
@test out accumulate_arr
1902+
@test accumulate!(op, out, arr) accumulate_arr
1903+
@test out accumulate_arr
1904+
end
1905+
1906+
end
1907+
end
1908+
1909+
end

test/reduce.jl

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,6 @@ let es = sum_kbn(z), es2 = sum_kbn(z[1:10^5])
268268
@test (es2 - cs[10^5]) < es2 * 1e-13
269269
end
270270

271-
@test isequal(cummin([1, 2, 5, -1, 3, -2]), [1, 1, 1, -1, -1, -2])
272-
@test isequal(cummax([1, 2, 5, -1, 3, -2]), [1, 2, 5, 5, 5, 5])
273-
274-
@test isequal(cummax([1 0; 0 1], 1), [1 0; 1 1])
275-
@test isequal(cummax([1 0; 0 1], 2), [1 1; 0 1])
276-
@test isequal(cummin([1 0; 0 1], 1), [1 0; 0 0])
277-
@test isequal(cummin([1 0; 0 1], 2), [1 0; 0 0])
278271

279272
@test sum(collect(map(UInt8,0:255))) == 32640
280273
@test sum(collect(map(UInt8,254:255))) == 509

0 commit comments

Comments
 (0)