User defined objective function from external pkl file

You can convert a PyObject to a Function yourself by something like

func1(x) = func(tuple(x...))  
func2(x,y,z,w) = func((x,y,z,w))

but then you’ll just get the error that ForwardDiff can’t differentiate your python function:

caused by: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4})
Closest candidates are:
  (::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat at /usr/local/julia-1.7.3/share/julia/base/rounding.jl:200
  (::Type{T})(::T) where T<:Number at /usr/local/julia-1.7.3/share/julia/base/boot.jl:770
  (::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number} at /usr/local/julia-1.7.3/share/julia/base/char.jl:50
  ...
Stacktrace:
  [1] convert(#unused#::Type{Float64}, x::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4})
    @ Base ./number.jl:7
  [2] cconvert(T::Type, x::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4})
    @ Base ./essentials.jl:417
  [3] macro expansion
    @ ~/.julia/packages/PyCall/7a7w0/src/exception.jl:95 [inlined]
  [4] PyObject(r::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4})
    @ PyCall ~/.julia/packages/PyCall/7a7w0/src/conversions.jl:23
  [5] PyObject(t::NTuple{4, ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4}})
    @ PyCall ~/.julia/packages/PyCall/7a7w0/src/conversions.jl:196
  [6] _pycall!(ret::PyObject, o::PyObject, args::Tuple{NTuple{4, ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4}}}, nargs::Int64, kw::Ptr{Nothing})
    @ PyCall ~/.julia/packages/PyCall/7a7w0/src/pyfncall.jl:24
  [7] _pycall!
    @ ~/.julia/packages/PyCall/7a7w0/src/pyfncall.jl:11 [inlined]
  [8] #_#114
    @ ~/.julia/packages/PyCall/7a7w0/src/pyfncall.jl:86 [inlined]
  [9] (::PyObject)(args::NTuple{4, ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4}})
    @ PyCall ~/.julia/packages/PyCall/7a7w0/src/pyfncall.jl:86
 [10] func2(x::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4}, y::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4}, z::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4}, w::ForwardDiff.Dual{ForwardDiff.Tag{JuMP.var"#142#143"{typeof(func2)}, Float64}, Float64, 4})

That’s because register tries to differentiate your function with ForwardDiff, and ForwardDiff requires your function to be a generic Julia function. Earlier thread: Does ForwardDiff work with Python functions? It’s not clear to me whether there is any way to auto-differentiate a python function in julia, maybe it’s easier to implement the gradient yourself, even in python, maybe with a python autodiff tool, and then pass it separately.

2 Likes