From dc20816b29d27398b21a3361e37f6ca9dbeb8c2f Mon Sep 17 00:00:00 2001 From: Simon Byrne Date: Wed, 31 Jan 2018 10:02:01 -0800 Subject: [PATCH] Use destination type to determine output of cumsum! and cumprod! --- base/accumulate.jl | 33 ++++++++++++++++++++++++++------- base/reduce.jl | 27 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/base/accumulate.jl b/base/accumulate.jl index 20363362f73c74..e018c87974b8f8 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -416,15 +416,34 @@ function _accumulate_pairwise_small!(op, dest::AbstractArray{T}, itr, accv, w, i end end +""" + Base.ConvertOp{T}(op)(x,y) + +An operator which converts `x` and `y` to type `T` before performing the `op`. + +The main purpose is for use in [`cumsum!`](@ref) and [`cumprod!`](@ref), where `T` is determined by the output array. +""" +struct ConvertOp{T,O} <: Function + op::O +end +ConvertOp{T}(op::O) where {T,O} = ConvertOp{T,O}(op) +(c::ConvertOp{T})(x,y) where {T} = c.op(convert(T,x),convert(T,y)) + +reduce_first(c::ConvertOp{T},x) = reduce_first(c.op, convert(T,x)) + + function cumsum!(out, v::AbstractVector{T}) where T # we dispatch on the possibility of numerical accuracy issues cumsum!(out, v, ArithmeticStyle(T)) end -cumsum!(out, v::AbstractVector, ::ArithmeticRounds) = accumulate_pairwise!(+, out, v) -cumsum!(out, v::AbstractVector, ::ArithmeticUnknown) = accumulate_pairwise!(+, out, v) -cumsum!(out, v::AbstractVector, ::ArithmeticStyle) = accumulate!(+, out, v) +cumsum!(out::AbstractVector{T}, v::AbstractVector, ::ArithmeticRounds) where {T} = + accumulate_pairwise!(ConvertOp{T}(+), out, v) +cumsum!(out::AbstractVector{T}, v::AbstractVector, ::ArithmeticUnknown) where {T} = + accumulate_pairwise!(ConvertOp{T}(+), out, v) +cumsum!(out::AbstractVector{T}, v::AbstractVector, ::ArithmeticStyle) where {T} = + accumulate!(ConvertOp{T}(+), out, v) """ cumsum(A, dim::Integer) @@ -488,14 +507,14 @@ cumsum(v::AbstractVector, ::ArithmeticStyle) = accumulate(add_sum, v) Cumulative sum of `A` along the dimension `dim`, storing the result in `B`. See also [`cumsum`](@ref). """ -cumsum!(dest, A, dim::Integer) = accumulate!(+, dest, A, dim) +cumsum!(dest::AbstractArray{T}, A, dim::Integer) where {T} = accumulate!(ConvertOp{T}(+), dest, A, dim) """ cumsum!(y::AbstractVector, x::AbstractVector) Cumulative sum of a vector `x`, storing the result in `y`. See also [`cumsum`](@ref). """ -cumsum!(dest, itr) = accumulate!(+, dest, src) +cumsum!(dest::AbstractArray{T}, itr) = accumulate!(ConvertOp{T}(+), dest, src) """ cumprod(A, dim::Integer) @@ -555,7 +574,7 @@ cumprod(x::AbstractVector) = accumulate(mul_prod, x) Cumulative product of `A` along the dimension `dim`, storing the result in `B`. See also [`cumprod`](@ref). """ -cumprod!(dest, A, dim::Integer) = accumulate!(*, dest, A, dim) +cumprod!(dest::AbstractArray{T}, A, dim::Integer) where {T} = accumulate!(ConvertOp{T}(*), dest, A, dim) """ cumprod!(y::AbstractVector, x::AbstractVector) @@ -563,4 +582,4 @@ cumprod!(dest, A, dim::Integer) = accumulate!(*, dest, A, dim) Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). """ -cumprod!(dest, itr) = accumulate!(*, dest, itr) +cumprod!(dest::AbstractArray{T}, itr) where {T} = accumulate!(ConvertOp{T}(*), dest, itr) diff --git a/base/reduce.jl b/base/reduce.jl index cb5b8fdf870d62..1e3400cc385913 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -28,6 +28,7 @@ add_sum(x::SmallSigned) = Int(x) add_sum(x::SmallUnsigned) = UInt(x) add_sum(X::AbstractArray) = broadcast(add_sum, X) + """ Base.mul_prod(x,y) @@ -42,6 +43,32 @@ mul_prod(x) = *(x) mul_prod(x::SmallSigned) = Int(x) mul_prod(x::SmallUnsigned) = UInt(x) +""" + Base.ConvertOp{T}(op)(x,y) + +An operator which converts `x` and `y` to type `T` before performing the `op`. + +The main purpose is for use in [`cumsum!`](@ref) and [`cumprod!`](@ref), where `T` is determined by the output array. +""" +struct ConvertOp{T,O} <: Function + op::O +end +ConvertOp{T}(op::O) where {T,O} = ConvertOp{T,O}(op) +(c::ConvertOp{T})(x,y) where {T} = c.op(convert(T,x),convert(T,y)) +reduce_first(c::ConvertOp{T},x) = reduce_first(c.op, convert(T,x)) + + +""" + Base.ConvertAdd{T}()(x,y) + +An addition operator which converts `x` and `y` to type `T` before performing the addition. +The main purpose is for in [`cumsum!`](@ref), where `T` is determined by the output array. +""" +struct ConvertAdd{T} end +(::ConvertAdd{T})(x) where {T} = +(convert(T,x)) +(::ConvertAdd{T})(x,y) where {T} = convert(T,x) + convert(T,y) + + ## foldl && mapfoldl @noinline function mapfoldl_impl(f, op, v0, itr, i)