Usage of `@.`

Hi,

I am trying to generate random vector with {-1, +1} on length n.

The following code works

y = 2 .* (rand(n) .> 0.50) .- 1

When I try to use @., it throws error

y = @. 2 * (rand(n) > 0.50) - 1
ERROR: MethodError: no method matching isless(::Float64, ::Vector{Float64})
Closest candidates are:
  isless(::T, ::T) where T<:Union{Float16, Float32, Float64} at float.jl:424
  isless(::AbstractVector, ::AbstractVector) at abstractarray.jl:2612
  isless(::AbstractFloat, ::AbstractFloat) at operators.jl:184
  ...
Stacktrace:
  [1] <(x::Float64, y::Vector{Float64})
    @ Base ./operators.jl:356
  [2] >(x::Vector{Float64}, y::Float64)
    @ Base ./operators.jl:382
  [3] _broadcast_getindex_evalf
    @ ./broadcast.jl:670 [inlined]
  [4] _broadcast_getindex
    @ ./broadcast.jl:643 [inlined]
  [5] _getindex
    @ ./broadcast.jl:667 [inlined]
  [6] _getindex
    @ ./broadcast.jl:666 [inlined]
  [7] _broadcast_getindex
    @ ./broadcast.jl:642 [inlined]
  [8] _getindex
    @ ./broadcast.jl:666 [inlined]
  [9] _broadcast_getindex
    @ ./broadcast.jl:642 [inlined]
 [10] getindex
    @ ./broadcast.jl:597 [inlined]
 [11] copy
    @ ./broadcast.jl:875 [inlined]
 [12] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(-), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(*), Tuple{Int64, Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(>), Tuple{Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(rand), Tuple{Int64}}, Float64}}}}, Int64}})
    @ Base.Broadcast ./broadcast.jl:860
 [13] top-level scope
    @ REPL[51]:1

But, if I create the random vector first and then use @., it works again

u = rand(n)
y = @. 2 * (u > 0.50) - 1

I could like to know why I cannot use rand() with @.?

@. rand(n) > 0.5 is equivalent to rand.(2) .> 0.5 and not rand(2) .> 0.5. The former is equivalent to

julia> for i in 2
           rand(i) > 0.5
       end

Perhaps you want something like

julia> @. (i -> 2 * (rand() > 0.50) - 1)(1:4)
4-element Vector{Int64}:
 -1
 -1
 -1
 -1

or, more clearly,

julia> [2 * (rand() > 0.50) - 1 for i in 1:4]
4-element Vector{Int64}:
  1
 -1
  1
  1

You could also solve this particular problem as:

julia> rand([-1, 1], 6)
6-element Vector{Int64}:
 -1
  1
  1
  1
 -1
  1

The version, rand(v, n), will pick random elements from v to fill up a vector of length n, so you can use it to generate more complicated sequences as well.

6 Likes

Since you only need two values, generating random Bools (instead of Float64s like in your original code) is faster:

julia> @btime 2 .* (rand(100_000) .> .5) .- 1;
  256.643 μs (4 allocations: 1.53 MiB)

julia> @btime rand($([-1,1]), 100_000);
  668.837 μs (2 allocations: 781.30 KiB)

julia> @btime rand($(-1:2:1), 100_000);
  647.847 μs (2 allocations: 781.30 KiB)

julia> @btime 2 .* rand(Bool, 100_000) .- 1;
  79.452 μs (4 allocations: 879.03 KiB)
6 Likes