Broadcast operations with ranges

Say I want to use a sliding window of width = N, such that in the first iteration I would write it as the range 1:N, and the start and stop values of the range would move j units after the j-th iteration. My first try would be:

for j=0:m
    w = j+(1:N)
    # etc
end

But testing against Julia 0.7 this issues the following warning:

Warning: `a::Number + b::AbstractArray` is deprecated, use `broadcast(+, a, b)` instead.

Following that advice is bad for performance (at least in Julia 0.6)

julia> @time 6 + (1:1e7);
  0.000016 seconds (6 allocations: 288 bytes)

julia> @time 6 .+ (1:1e7);
  0.128881 seconds (5.36 k allocations: 76.562 MiB, 2.51% gc time)

So I guess I should do:

for j=0:m
    w = (j+1):(j+N)
    # etc
end

with the small extra cost of writing j two times (but perhaps the benefit of clearer code).

Am I right? Or is there some other smarter way to do it? Or is this performance issue not happening in 0.7?

The two things you benchmark dows not compute the same thing

julia> typeof(6 + (1:1e7)) # range
StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}

julia> typeof(broadcast(+, 6, 1:1e7)) # range
StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}

julia> typeof(6 .+ (1:1e7)) # array
Array{Float64,1}

Thanks, that’s right. So I can rewrite my question as:

Is there a “nicer” (and non-deprecated) way to write broadcast(+, j, a:b), which gives the same result, as j + (a:b) did before 0.7?

In this small example I’m using fixed start and stop values. If possible I would also like to do it for shifting a range that is in a variable x, whose start and stop values (and perhaps step too) may be “unknown” beforehand.

I think the answer is: don’t use ().

julia> typeof(6 .+ 1:1e3)                                                                                                                                                
StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}                                                                                          

julia> typeof(6 .+ (1:1e3))                                                                                                                                              
Array{Float64,1}                                                                                                                                                         

But I wonder why the parenthesis make a difference. Is this a bug?

That’s just the same as

(6 .+ 1):1e3

+ has higher precedence than :

Doh. So, @heliosdrm’s question still stands: How does one add a number to a range and get a range back?

Right now the answer is in a big PR that changes lots of broadcast functionality. I hope to get back to it soon.

3 Likes

To expand on mbaumann, I think this is a weird bug involving the handling of literals.

r=1:10; z=1.2;
@show z.+r;
#z .+ r = 2.2:1.0:11.2
@show 1.2 .+ r;
#1.2 .+ r = [2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 8.2, 9.2, 10.2, 11.2]
@show broadcast(+, 1.2, r);
#broadcast(+, 1.2, r) = 2.2:1.0:11.2

edit: Afaik broadcast-plus is the intended way to shift ranges, modulo transient bugs.

2 Likes