Dot broadcast macro not working as expected

I’m not sure if this is a bug in my understanding or an issue with the macro:

I’m creating a complex array and then scrambling the phases. Here rand(N) is the only vector-valued thing, so I’d expect all the rest of the operations to automatically dot-broadcast

julia> N = 15
15

julia> X = ones(Complex128, N)
15-element Array{Complex{Float64},1}:
 1.0+0.0im
    ⋮


julia> @. X *= exp(im*π*(rand(N)*2-1))
WARNING: exp(x::AbstractArray{T}) where T <: Number is deprecated, use exp.(x) instead.
Stacktrace:
 [1] depwarn(::String, ::Symbol) at .\deprecated.jl:70
 [2] exp(::Array{Complex{Float64},1}) at .\deprecated.jl:57
 [3] (::##71#72)(::Complex{Float64}, ::Complex{Bool}, ::Irrational{:π}, ::Int64) at .\<missing>:0
 [4] macro expansion at .\broadcast.jl:155 [inlined]
    ⋮

ERROR: MethodError: Cannot `convert` an object of type Array{Complex{Float64},1} to an object of type Complex{Float64}
This may have arisen from a call to the constructor Complex{Float64}(...),
since type constructors fall back to convert methods.
Stacktrace:
 [1] setindex! at .\multidimensional.jl:300 [inlined]
 [2] macro expansion at .\broadcast.jl:156 [inlined]
 [3] macro expansion at .\simdloop.jl:73 [inlined]
 [4] macro expansion at .\broadcast.jl:149 [inlined]
 [5] _broadcast!(::##71#72, ::Array{Complex{Float64},1}, 
    ⋮

Explicitly putting the dots: .*= and in exp.(...) works, but I’d thought that the @. macro should do it for me.

I think you want @. X *= cis(π*(rand()*2-1)). This will avoid allocating the random vector and will just call rand() separately for each element, and cis(x) should be faster than exp(im*x).

The problem with @. and rand(N) is that it is equivalent rand.(N), which calls rand(N) for each element of your X array.

1 Like

Some people may disagree with this advice, but I prefer not to use the @. macro unless it is abundantly obvious to me exactly what it’s going to do. In this case the presence of the rand(N) would probably be enough to stop me from using it here (as I wouldn’t necessarily expect rand.(N) to produce the same class of results as rand(N) even though it does). In my opinion putting in a few extra . characters is a small sacrifice for clarity.

7 Likes

Ah, that’s right - for some reason I wasn’t thinking about the fact that I’d get rand.(...) and was thinking about rand(...) as a unit. Probably supporting evidence for @ExpandingMan’s point. :slight_smile:

Good point about not allocating the random vector, and thanks for introducing me to cis.

-s

Just in case you didn’t know, you also have the option of @. X *= cis(π*($rand(N)*2-1)) . The $ stops the . that @. would have added there. That said, the other solution is definitely better here.

2 Likes