@. x-y? x : y broadcasting does not work?

I don’t quite get why these broadcasting operations do not work:

julia> x = rand(3); y = rand(3);

julia> f(x,y) = @. (x-y > 0.5 ? x : y )
f (generic function with 1 method)

julia> f(x,y)
ERROR: TypeError: non-boolean (BitVector) used in boolean context
Stacktrace:
 [1] f(x::Vector{Float64}, y::Vector{Float64})
   @ Main ./REPL[38]:1
 [2] top-level scope
   @ REPL[39]:1

Because it’s not supported. It’s the same as trying to do

@. if x-y > 0.5
    x
else
    y
end

You can use @. ifelse(x-y > 0.5, x, y) instead.

5 Likes

This could be supported in the same way that .&& and .|| support was added, but it just has not yet been attempted as far as I’m aware.

1 Like

Maaaaaybe. It’s a little stranger than .&& and .||. It (currently) is syntacticly identical to the if statement, and we don’t support if. or .if. Are a .? b : c and a ? b .: c errors? We also don’t support [1,2,3] .: [8,9,10], and I think there’s strange parsing ambiguities around .: in general.

1 Like

It seems that I have to add a dot to the minus there:

julia> f(x,y) = ifelse.(x - y > 0.5, x, y)
f (generic function with 1 method)

julia> f(x,y)
ERROR: MethodError: no method matching isless(::Float64, ::SVector{3, Float64})

while

julia> f(x,y) = ifelse.(x - y .> 0.5, x, y)
f (generic function with 1 method)

julia> f(x,y)
3-element SVector{3, Float64} with indices SOneTo(3):
 -0.636700731442766
  0.16919692261464525
 -0.3143986886849526

Yeah, sorry, I should’ve just @.'ed the entire expression. Edited above.

2 Likes

Actually what I was trying to do is this:

julia> x = @SVector(rand(3)); y = @SVector(rand(3)); period = @SVector(rand(3));

julia> function getdx(x,y,period)
           delta = x - y
           ifelse.(delta .> 0.5 .* period ? delta - period : delta) .* sign.(x-y)
       end
getdx (generic function with 1 method)

julia> getdx(x,y,period)
ERROR: TypeError: non-boolean (SVector{3, Bool}) used in boolean context

I can’t find the possible combination of dots and not-dots or @. that makes this work as a one-liner.

1 Like

Change your ? and : to commas.

a && b was syntactically identical to

if a; b end

and

if !(a); b end

and nobody was arguing in that pr that it meant that we had to add support for if.

That said, yeah expressions like a .? b .: c are pretty ugly

1 Like

:flushed: :flushed: :flushed:

oh, sorry, I think changed from one to the other syntax so many times that I got lost

If you want a macro that does this for you, you could always just do something like

julia> using MLStyle

julia> macro (..)(ex)
           f = @λ begin
               :($a ? $b : $c) => :($Base.ifelse($(f(a)), $(f(b)), $(f(c))))
               x => x
           end
           esc(:($Base.@. $(f(ex))))
       end
@.. (macro with 1 method)

julia> let  x = rand(3), y = rand(3);
           @.. (x-y > 0.5 ? x : y )
       end
3-element Vector{Float64}:
 0.18240422481232943
 0.8391572489988877
 0.43624514306257667

This works correctly on nested expressions:

julia> let  x = rand(3), y = rand(3);
           @.. (x-y > 0.5 ? (x > y ? y : x) : y)
       end
3-element Vector{Float64}:
 0.7793973727797479
 0.3033699172383637
 0.625107294185069
1 Like

But won’t this macro always evaluate both branches?

You can write something like this:

broadcast(xs,ys) do x,y
    x-y>0.5 ? x : y
end

where in reality, surely the expressions will be a bit longer: If you don’t want ifelse, there must be some work you’d like not to happen when the condition is not met, so it can’t just be one symbol.

You can also write g(x,y) = @cast _[i] := x[i]-y[i] > 0.5 ? x[i] : y[i] using TensorCast, after someone complained that it didn’t work…

2 Likes

It’s superficial, but they were and are syntactically different:

julia> Meta.show_sexpr(:(a && b))
(:&&, :a, :b)
julia> Meta.show_sexpr(:(if a; b; end))
(:if, :a, (:block,
    :(#= REPL[7]:1 =#),
    :b
  ))

Whereas a ? b : c parses directly to an :if head. Like I said, just little things all around that can change, but the above is what I was referencing. The fact that a && b has its own special parsed form made it significantly easier to achieve a .&& b.

4 Likes

Aha, I see what you mean now. Thanks!

1 Like