Skip to content

Loose signature of push! fallback (results in confusing MethodError) #12919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
samuelpowell opened this issue Sep 2, 2015 · 8 comments
Open
Labels
error messages Better, more actionable error messages

Comments

@samuelpowell
Copy link
Member

I am struggling to find a minimal test case for this problem.

The following code:

using Optim

function f_gd(x)
  Float32((x[1] - 5.0)^2)
end

function g_gd(x, storage)
  storage[1] = 2.0 * (x[1] - 5.0)
end

initial_x = [0.0]

d = DifferentiableFunction(f_gd, g_gd)

results = Optim.gradient_descent(d, initial_x)

Fails with the following error:

ERROR: MethodError: `push!` has no method matching push!(::Optim.LineSearchResults{Float64}, ::Float64)
Closest candidates are:
  push!{T}(::Optim.LineSearchResults{T}, ::T, ::T, ::T)
  push!(::Any, ::Any, ::Any)
  push!(::Any, ::Any, ::Any, ::Any...)
  ...
 in push! at abstractarray.jl:1365
 in gradient_descent at /Users/samuelpowell/.julia/v0.4/Optim/src/gradient_descent.jl:90

The error suggests that push! is being called with two arguments, but in fact, the offending line calls push! correctly with 4 arguments: push!(lsr, zero(T), f_x, dphi0).

An error should be thrown, since I have forced f_x to single precision (it is the output of function f_gd(x)), but this should have read:

ERROR: MethodError: `push!` has no method matching push!(::Optim.LineSearchResults{Float64}, ::Float64, ::Float32, ::Float64)

For some reason, the last two argument types are missing.

I can reproduce this behaviour on a 0.4-dev and a 0.4-pre on three different architectures (including JuliaBox)

@johnmyleswhite
Copy link
Member

I hit something like this recently. There's some implicit redirection of one push! to another push! method, but I forget quite where it happens.

@yuyichao
Copy link
Contributor

yuyichao commented Sep 2, 2015

The one that causes the MethodError is called with 2 arguments. You can see that in the backtrace

@yuyichao
Copy link
Contributor

yuyichao commented Sep 2, 2015

The fallback method is printed in the backtrace in push! at abstractarray.jl:1365 so you should be able to figure out why that happens. I'm not sure what we can do to improve this other than making sure inlined functions don't mess up the backtrace #12544

@johnmyleswhite
Copy link
Member

One thing we could do here is to remove push!(::Any, ::Any, ::Any) and their kind, forcing people to use push!(Any, Tuple{Any}) instead. Since I've hit this exact issue myself, I'd argue that push!(::Any, ::Any, ::Any) might be excessively generic -- it implicitly enrolls ever single type in Julia into a contract that says that push!(x, y, z) can always be reinterpreted as push!(push!(x, y), z).

@johnmyleswhite
Copy link
Member

Just to make the tradeoffs clear, the alternative perspective is that push!(::Optim.LineSearchResults{Float64}, ::Float64, ::Float64, ::Float64) is excessively specific, so reasonable arguments end up failing to match that signature, but instead end getting matched by push!(Any, Any, Any, Any) instead.

@yuyichao
Copy link
Contributor

yuyichao commented Sep 2, 2015

I don't think push!(::Any, ::Tuple{Vararg{Any}}) would work because it would be anbigious with normal normal push!. Maybe limit the vararg definition to push(::AbstractArray, ::Any...)?

@johnmyleswhite
Copy link
Member

Yeah, I think enforcing an AbstractArray solution is good, although it might need to be something like Union{AbstractArray, Associative} since we allow using push! to mutate dicts.

@samuelpowell
Copy link
Member Author

@johnmyleswhite, @yuyichao, thank you for looking into this. I understand now what was happening, and as you say, this is pretty obvious when looking at the push! methods in abstractarray.jl. I was thrown off guard by the error message.

I have updated the issue accordingly.

The (::Optim.LineSearchResults{T}, ::T, ::T, ::T) method signature, whilst specific, is reasonably sane: in most cases one would expect an objective function and its derivative to be of the same type.

The suggested tightening of the signature in Base seems like a good solution, providing it doesn't cause breakage.

@samuelpowell samuelpowell changed the title MethodError lacking arguments Loose signature of push! fallback results in confusing MethodError Sep 3, 2015
@samuelpowell samuelpowell changed the title Loose signature of push! fallback results in confusing MethodError Loose signature of push! fallback (results in confusing MethodError) Sep 3, 2015
@brenhinkeller brenhinkeller added the error messages Better, more actionable error messages label Nov 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error messages Better, more actionable error messages
Projects
None yet
Development

No branches or pull requests

4 participants