SciML / Optimization.jl

Mathematical Optimization in Julia. Local, global, gradient-based and derivative-free. Linear, Quadratic, Convex, Mixed-Integer, and Nonlinear Optimization in one simple, fast, and differentiable interface.
https://docs.sciml.ai/Optimization/stable/
MIT License
691 stars 75 forks source link

OptimizationMOI 0.2.0, cannot assign Symbolics.Num to MArray #655

Closed pitx-perf closed 6 months ago

pitx-perf commented 6 months ago

Describe the example

OptimizationMOI from 0.1.16 to 0.2.0 introduces the use of Symbolics. Symbolics.Num cannot be assigned to a MArray from StaticArrays because they are non-isbitstype.

Minimal Reproducible Example 👇

using Optimization, OptimizationMOI
using Ipopt
using ForwardDiff

using StaticArrays

mutable struct MyStruct{TF, # type of number
    Tarr<:AbstractVector{TF}}

    x::Tarr
    p::Vector{Float64}

    obj::TF
    constr::Tarr
end

function MyStruct(x, p)
    TF = promote_type(eltype(x))

    xArr = @MArray zeros(TF, 2)
    pArr = zeros(Float64, 2)

    obj = TF(0.0)
    constr = @MArray zeros(TF, 2)

    return MyStruct(xArr, pArr, obj, constr)
end

function build_struct(x, p)
    ms = MyStruct(x, p)

    ms.x[1] = x[1]
    ms.x[2] = x[2]
    ms.p[1] = p[1]
    ms.p[2] = p[2]

    return ms
end

function myfunction(ms::MyStruct)
    ms.obj = (ms.p[1] - ms.x[1])^2 + ms.p[2] * (ms.x[2] - ms.x[1]^2)^2
    ms.constr[1] = ms.x[1]^2 + ms.x[2]^2
    ms.constr[2] = ms.x[1] * ms.x[2]
end

function opt_obj(x, p)

    ms = build_struct(x, p)
    myfunction(ms)

    return ms.obj
    # return (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2
end

function con_all(R, x, p)
    ms = build_struct(x, p)
    myfunction(ms)

    R[1] = ms.constr[1]
    R[2] = ms.constr[2]
    # R[1] = x[1]^2 + x[2]^2
    # R[2] = x[1] * x[2]
end

x0 = [0.0, 0.0]
lx = [-1.0, -1.0]
ux = [1.0, 1.0]
_p = [1.0, 1.0]

ms = MyStruct(x0, _p)

## Problem construction
optprob = OptimizationFunction(opt_obj, Optimization.AutoForwardDiff(), cons=con_all)
prob = OptimizationProblem(optprob, x0, _p, lcons=[-Inf, -1.0], ucons=[0.8, 2.0], lb=lx, ub=ux)

## Choose optimizer
opt = OptimizationMOI.MOI.OptimizerWithAttributes(
    Ipopt.Optimizer,
    "hessian_approximation" => "limited-memory",
)

## Solve
sol = solve(prob, opt);
# 0.7519644567000239
# 0.4843030722596713

Error & Stacktrace ⚠️

ERROR: setindex!() with non-isbitstype eltype is not supported by StaticArrays. Consider using SizedArray.
Stacktrace:
  [1] error(s::String)
    @ Base .\error.jl:35
  [2] setindex!
    @ C:\Users\xxx\.julia\packages\StaticArrays\PLKkM\src\MArray.jl:39 [inlined]
  [3] build_struct(x::Vector{Symbolics.Num}, p::Vector{Symbolics.Num})
    @ Main c:\Users\xxx\Documents\Ju\test\dev_optimbug.jl:33
  [4] opt_obj(x::Vector{Symbolics.Num}, p::Vector{Symbolics.Num})
    @ Main c:\Users\xxx\Documents\Ju\test\dev_optimbug.jl:49
  [5] (::OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing})(::Vector{Symbolics.Num}, ::Vararg{Vector{Symbolics.Num}})
    @ SciMLBase C:\Users\xxx\.julia\packages\SciMLBase\2HZ5m\src\scimlfunctions.jl:3811
  [6] modelingtoolkitize(prob::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ ModelingToolkit C:\Users\xxx\.julia\packages\ModelingToolkit\Gpzyo\src\systems\optimization\modelingtoolkitize.jl:19
  [7] modelingtoolkitize(prob::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}})
    @ ModelingToolkit C:\Users\xxx\.julia\packages\ModelingToolkit\Gpzyo\src\systems\optimization\modelingtoolkitize.jl:6
  [8] OptimizationMOI.MOIOptimizationNLPCache(prob::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}, opt::MathOptInterface.OptimizerWithAttributes; callback::Nothing, kwargs::Base.Pairs{Symbol, Nothing, NTuple{4, Symbol}, NamedTuple{(:maxiters, :maxtime, :abstol, :reltol), NTuple{4, Nothing}}})
    @ OptimizationMOI C:\Users\xxx\.julia\packages\OptimizationMOI\goTrw\src\nlp.jl:142
  [9] __init(prob::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}, opt::MathOptInterface.OptimizerWithAttributes; maxiters::Nothing, maxtime::Nothing, abstol::Nothing, reltol::Nothing, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ OptimizationMOI C:\Users\xxx\.julia\packages\OptimizationMOI\goTrw\src\OptimizationMOI.jl:282
 [10] __init(prob::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}, opt::MathOptInterface.OptimizerWithAttributes)
    @ OptimizationMOI C:\Users\xxx\.julia\packages\OptimizationMOI\goTrw\src\OptimizationMOI.jl:274
 [11] init(::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}, ::MathOptInterface.OptimizerWithAttributes; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ SciMLBase C:\Users\xxx\.julia\packages\SciMLBase\2HZ5m\src\solve.jl:163
 [12] init(::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}, ::MathOptInterface.OptimizerWithAttributes)
    @ SciMLBase C:\Users\xxx\.julia\packages\SciMLBase\2HZ5m\src\solve.jl:161
 [13] solve(::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}, ::MathOptInterface.OptimizerWithAttributes; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ SciMLBase C:\Users\xxx\.julia\packages\SciMLBase\2HZ5m\src\solve.jl:94
 [14] solve(::OptimizationProblem{true, OptimizationFunction{true, AutoForwardDiff{nothing, Nothing}, typeof(opt_obj), Nothing, Nothing, Nothing, typeof(con_all), Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED_NO_TIME), Nothing, Nothing, SymbolicIndexingInterface.SymbolCache{Nothing, Nothing, Nothing}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}, Nothing, Vector{Float64}, Vector{Float64}, Nothing, Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}}, ::MathOptInterface.OptimizerWithAttributes)
    @ SciMLBase C:\Users\xxx\.julia\packages\SciMLBase\2HZ5m\src\solve.jl:91

Not Working Environment (please complete the following information):

  [f6369f11] ForwardDiff v0.10.36
  [b6b21f68] Ipopt v1.5.1
⌃ [7f7a1694] Optimization v3.20.2
  [fd9f6733] OptimizationMOI v0.2.0
  [90137ffa] StaticArrays v1.9.0

Working Environment (please complete the following information):

  [f6369f11] ForwardDiff v0.10.36
  [b6b21f68] Ipopt v1.5.1
⌃ [7f7a1694] Optimization v3.20.2
  [fd9f6733] OptimizationMOI v0.1.16
  [90137ffa] StaticArrays v1.9.0
ChrisRackauckas commented 6 months ago

MOI can use symbolic information, and we should use it when we can. The best solution here is proably to try/catch then warn, and if it warns just have it omit the symbolic information. That will be slower but will make more code work (with an appropriate performance warning)

dmetivie commented 6 months ago

In a similar spirit,

Describe the example

OptimizationMOI from 0.1.16 to 0.2.0 introduces the use of Symbolics. Symbolics.Num is not compatible with Distributions.jl argument check.

Minimal Reproducible Example 👇

using Distributions
using Ipopt
using Optimization, OptimizationMOI
d = 1
T = 400

y = sin.(2π/T*1:T) + cos.(2π/T*1:T) .^ 2 .* rand(Gamma(), T)

f(θ) = Gamma(θ[1], θ[2])
f(t, θ) = f([cos(2π / T * t * θ[1])^2, cos(2π / T * t * θ[2])^2])

θσ10 = zeros(2d + 1)
θσ20 = zeros(2d + 1)
θ0 = hcat(θσ10, θσ20)

ℓ(θ, x) = -sum(logpdf(f(t / T, θ), x[t]) for t in eachindex(x)) # = -loglikelihood
OptFunc = OptimizationFunction(ℓ, Optimization.AutoForwardDiff())
prob = OptimizationProblem(OptFunc, vec(θ0), y)

sol = solve(prob, Ipopt.Optimizer())

Error & Stacktrace ⚠️

ERROR: TypeError: non-boolean (Symbolics.Num) used in boolean context
Stacktrace:
  [1] #271
    @ Distributions C:\Users\metivier\.julia\packages\Distributions\UaWBm\src\univariate\continuous\gamma.jl:34 [inlined]
  [2] check_args
    @ Distributions C:\Users\metivier\.julia\packages\Distributions\UaWBm\src\utils.jl:89 [inlined]
  [3] #Gamma#270
    @ Distributions C:\Users\metivier\.julia\packages\Distributions\UaWBm\src\univariate\continuous\gamma.jl:34 [inlined]
  [4] Gamma
    @ Distributions C:\Users\metivier\.julia\packages\Distributions\UaWBm\src\univariate\continuous\gamma.jl:33 [inlined]
  [5] f(θ::Vector{Symbolics.Num})
    @ Main .\Untitled-1:10
  [6] f(t::Float64, θ::Vector{Symbolics.Num})
    @ Main .\Untitled-1:11
  [7] (::var"#5#6"{Vector{Symbolics.Num}, Vector{Symbolics.Num}})(t::Int64)
    @ Main .\none:0
  [8] (::Base.MappingRF)(acc::Any, x::Any)
    @ Base .\reduce.jl:100 [inlined]
  [9] _foldl_impl(op::Base.MappingRF{var"#5#6"{…}, Base.BottomRF{…}}, init::Base._InitialValue, itr::Base.OneTo{Int64})
    @ Base .\reduce.jl:58
 [10] foldl_impl
    @ .\reduce.jl:48 [inlined]
 [11] mapfoldl_impl
    @ .\reduce.jl:44 [inlined]
 [12] mapfoldl
    @ .\reduce.jl:175 [inlined]
 [13] mapreduce
    @ .\reduce.jl:307 [inlined]
 [14] sum
    @ .\reduce.jl:535 [inlined]
 [15] sum
    @ .\reduce.jl:564 [inlined]
 [16] ℓ(θ::Vector{Symbolics.Num}, x::Vector{Symbolics.Num})
    @ Main .\Untitled-1:18
 [17] OptimizationFunction
    @ C:\Users\metivier\.julia\packages\SciMLBase\dpafx\src\scimlfunctions.jl:3811 [inlined]
 [18] modelingtoolkitize(prob::OptimizationProblem{…}; kwargs::@Kwargs{})
    @ ModelingToolkit C:\Users\metivier\.julia\packages\ModelingToolkit\Gpzyo\src\systems\optimization\modelingtoolkitize.jl:19
 [19] modelingtoolkitize
    @ C:\Users\metivier\.julia\packages\ModelingToolkit\Gpzyo\src\systems\optimization\modelingtoolkitize.jl:6 [inlined]
 [20] OptimizationMOI.MOIOptimizationNLPCache(prob::OptimizationProblem{…}, opt::Ipopt.Optimizer; callback::Nothing, kwargs::@Kwargs{…})
    @ OptimizationMOI C:\Users\metivier\.julia\packages\OptimizationMOI\HC25i\src\nlp.jl:143
 [21] MOIOptimizationNLPCache
    @ C:\Users\metivier\.julia\packages\OptimizationMOI\HC25i\src\nlp.jl:108 [inlined]
 [22] #__init#38
    @ C:\Users\metivier\.julia\packages\OptimizationMOI\HC25i\src\OptimizationMOI.jl:278 [inlined]
 [23] __init
    @ C:\Users\metivier\.julia\packages\OptimizationMOI\HC25i\src\OptimizationMOI.jl:270 [inlined]
 [24] #init#598
    @ C:\Users\metivier\.julia\packages\SciMLBase\dpafx\src\solve.jl:165 [inlined]
 [25] init
    @ C:\Users\metivier\.julia\packages\SciMLBase\dpafx\src\solve.jl:163 [inlined]
 [26] solve(::OptimizationProblem{…}, ::Ipopt.Optimizer; kwargs::@Kwargs{})
    @ SciMLBase C:\Users\metivier\.julia\packages\SciMLBase\dpafx\src\solve.jl:96
 [27] solve(::OptimizationProblem{…}, ::Ipopt.Optimizer)
    @ SciMLBase C:\Users\metivier\.julia\packages\SciMLBase\dpafx\src\solve.jl:93
 [28] top-level scope
    @ Untitled-1:22
Some type information was truncated. Use `show(err)` to see complete types.

Not Working Environment (please complete the following information):

  [7f7a1694] Optimization v3.21.1
⌃ [fd9f6733] OptimizationMOI v0.3.1

Working Environment (please complete the following information):

  [7f7a1694] Optimization v3.21.1
⌃ [fd9f6733] OptimizationMOI v0.1.16

Actually this MWE run with these versions but the solver fail (not error), maybe the MWE is ill posed. In my full problem, it worked.

Vaibhavdixit02 commented 6 months ago

It should be fixed in the latest release which I created yesterday, try it out and let me know if it doesn't work!

ChrisRackauckas commented 6 months ago

I'm not sure about having it on by default without a time cap. There are many cases that would just stall which would otherwise work.

Vaibhavdixit02 commented 6 months ago

It's off by default now

dmetivie commented 6 months ago

Seems like it is working! Thanks.

pitx-perf commented 6 months ago

Fixed for me as well. Thanks!