No method matching problem in optimization in nested function definition

Hello everybody,
I am new to Julia and I am struggling to understand the behaviour of the following code, in which I perform the minimization of a function using the package BlackBoxOptim.

function includeProof(z::Real, x::Real)
    RangePoints=(-5.0, 5.0)
    function ToMin(y::Real)
        (1.0 - z*x)^2 + 100.0 * (x - (y+x)^2)^2
    end
    res = bboptimize(ToMin; SearchRange = RangePoints, NumDimensions=1)
end
see = includeProof(1.1,1.1)

In particular, I would expect that this piece of code returned the output of res.
However, the following error appears

MethodError: no method matching (::var"#ToMin#2"{Float64, Float64})(::Vector{Float64})
Closest candidates are:
  (::var"#ToMin#2")(::Real) at In[4]:3

Stacktrace:
 [1] fitness(x::Vector{Float64}, p::FunctionBasedProblem{var"#ToMin#2"{Float64, Float64}, ScalarFitnessScheme{true}, ContinuousRectSearchSpace, Nothing})
   @ BlackBoxOptim ~/.julia/packages/BlackBoxOptim/I3lfp/src/problem.jl:61
 [2] setup_problem(func::Function, parameters::ParamsDictChain)
   @ BlackBoxOptim ~/.julia/packages/BlackBoxOptim/I3lfp/src/bboptimize.jl:37
 [3] bbsetup(functionOrProblem::Function, parameters::Dict{Symbol, Any}; kwargs::Base.Pairs{Symbol, Any, Tuple{Symbol, Symbol}, NamedTuple{(:SearchRange, :NumDimensions), Tuple{Tuple{Float64, Float64}, Int64}}})
   @ BlackBoxOptim ~/.julia/packages/BlackBoxOptim/I3lfp/src/bboptimize.jl:111
 [4] bboptimize(functionOrProblem::Function, parameters::Dict{Symbol, Any}; kwargs::Base.Pairs{Symbol, Any, Tuple{Symbol, Symbol}, NamedTuple{(:SearchRange, :NumDimensions), Tuple{Tuple{Float64, Float64}, Int64}}})
   @ BlackBoxOptim ~/.julia/packages/BlackBoxOptim/I3lfp/src/bboptimize.jl:92
 [5] includeProof(z::Float64, x::Float64)
   @ Main ./In[4]:6
 [6] top-level scope
   @ In[5]:1
 [7] eval
   @ ./boot.jl:368 [inlined]
 [8] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
   @ Base ./loading.jl:1428

I do not get why there is a method error and, in particular, where does this piece

 (::var"#ToMin#2"{Float64, Float64})(::Vector{Float64})

come from.
Thanks a lot!

Never worked with BlackBoxOptim.jl, but I guess the error message is telling you that it cannot work with ToMin. Maybe just try

function ToMin(y)
        (1.0 - z*x)^2 + 100.0 * (x - (y[1]+x)^2)^2
end

As a general rule for programming in Julia:

  • Keep function definitions as general as possible,
  • but make type definitions as strict as possible. (not shown in this example)

When you declare a function ToMin in the local scope of includeProof, it is actually hoisted to global scope for compilation (this is what allows returned functions to exist beyond the scope of the function that created them). To avoid name collisions, it is given an auto-generated unique identifier like var"#ToMin#2". Within local scope it’s a named function accessed by the identifier ToMin, but in global scope it’s anonymous. The identifier increments every time you compile includeProof.

Consider this simple example:

julia> function f(x)
           g() = x
       end
f (generic function with 1 method)

julia> f(42)
(::var"#g#1"{Int64}) (generic function with 1 method)

julia> typeof(f(42))
var"#g#1"{Int64}

julia> f(42)()
42

julia> f(42) === f(42)
true

julia> f(42) === f(69)
false

julia> typeof(f(42)) === typeof(f(69))
true

Notice that the type of object returned is var"#g#1"{Int64}. This signifies that the function is actually an object with a single Int64 field storing a value (in this case 42, which gets loaded into the struct whenever f is called on that value). (Notably, in Julia, functions are objects and objects are functions.)

You can actually access the type var"#g#1" from global scope. It’s not very useful, because no constructor is provided, but we can see its structure:

julia> typeof(var"#g#1"{Int64})
DataType

julia> dump(var"#g#1"{Int64})
var"#g#1"{Int64} <: Function
  x::Int64

Conceptually, it’s as if the following code had been executed:

struct var"#g#1"{T}<:Function
    x::T
end
(g::var"#g#1")() = g.x

and calling f constructs such an object and returns it.

To explore how it works, we can add methods:

julia> (g::var"#g#1"{Int64})(y) = g.x + y

julia> f(42)
(::var"#g#1"{Int64}) (generic function with 2 methods)

julia> f(42)(27)
69

we can also hack it so that it doesn’t work properly anymore:

julia> (::var"#g#1"{Int64})() = 69

julia> f(42)()
69

None to worry, if we force f to recompile then we get a new identifier and it works how it did before our hacking:

julia> function f(x)
           g() = x
       end
f (generic function with 1 method)

julia> f(42)
(::var"#g#2"{Int64}) (generic function with 1 method)

julia> f(42)()
42

You’re never going to want to do this to anonymous functions of course, because their name changes every time you recompile, but that should answer your question.

The error you see indicates that bboptimize is trying to call ToMin on a Vector of Float64’s, but you declared ToMin to instead take a single Real. Unsurprisingly, it throws an error.


As an aside, it’s considered unidiomatic to use CamelCase for the names of functions and objects; you should instead use snake_case, and reserve CamelCase for type and module names. Consider renaming your functions to include_proof and to_min, and your object to rangepoints.

1 Like