How to broadcast this function call?

I want to broadcast the function g(u,t) over the argument u:

module mwe

t = 1.0
xs = rand(4)
u = sin.(xs) # therefore, length(xs) is always equal to length(u)

function Φ(x, t)
    return 2 * exp(t) + exp(t) * (1 - x) * x - (1 + exp(2 * t) * (1 - x)^2 * x^2)^(-1)
end

function g(u, t)
    return 1 / (1 + u^2) + Φ.(xs, t)
end

@show g.(u, t)

end

However, it returns

ERROR: MethodError: no method matching +(::Float64, ::Vector{Float64})
For element-wise addition, use broadcasting with dot syntax: scalar .+ array
The function `+` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...)
   @ Base operators.jl:596
  +(::ChainRulesCore.ZeroTangent, ::Any)
   @ ChainRulesCore ~/.julia/packages/ChainRulesCore/6Pucz/src/tangent_arithmetic.jl:99
  +(::Any, ::ChainRulesCore.NotImplemented)
   @ ChainRulesCore ~/.julia/packages/ChainRulesCore/6Pucz/src/tangent_arithmetic.jl:25
  ...

Stacktrace:
 [1] g(u::Float64, t::Float64)
   @ Main.mwe ~/examples/mwe2.jl:11
 [2] _broadcast_getindex_evalf
   @ ./broadcast.jl:673 [inlined]
 [3] _broadcast_getindex
   @ ./broadcast.jl:646 [inlined]
 [4] getindex
   @ ./broadcast.jl:605 [inlined]
 [5] copy
   @ ./broadcast.jl:906 [inlined]
 [6] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{…}, Nothing, typeof(Main.mwe.g), Tuple{…}})
   @ Base.Broadcast ./broadcast.jl:867
 [7] macro expansion
   @ show.jl:1232 [inlined]
 [8] top-level scope
   @ ~/examples/mwe2.jl:14
Some type information was truncated. Use `show(err)` to see complete types.

How can I fix it?

The problem isn’t the broadcast of g, but this here. Since you’re broadcasting \Phi, you get a vector back, and then the addition with 1 / (1 + u^2) fails since that is a scalar. Dot the + there as well, or use @..

1 Like

The former doesn’t really work (returns a vector of vectors or an error), the latter does.

Thanks.

A good way to solve would be to pass xs as a parameter to g:

function g(x, u, t)
    return 1 / (1 + u^2) + Φ(x, t)
end

@show g.(xs, u, t)
7 Likes

(It’s also good practice in general to avoid passing data via global variables, and to use parameters instead.)

2 Likes

This is reasonable, but it’s not very convenient to have semantically close functions with different signatures like (x,u,t) and (u,t). Adding dummy variable x,... to all the rest of the functions with u,t doesn’t seem to be an elegant solution.

But are they really that similar?

You’re just hiding the fact that g is a function of 3 arguments by silently depending on a global variable. This makes the similarity perhaps a bit artificial.

This is not only misleading, but it’s also highly inefficient and can cause catastrophic bugs if xs is ever mutated

3 Likes