PSORLab / EAGO.jl

A development environment for robust and global optimization
MIT License
140 stars 16 forks source link

Improving the Performance of IPOPT in EAGO #130

Open stumarcus314 opened 1 month ago

stumarcus314 commented 1 month ago

There is some advice here for improving the performance of the version of IPOPT used by EAGO by compiling an optimized version of IPOPT. https://psorlab.github.io/EAGO.jl/dev/optimizer/high_performance/#Ipopt-Build

Rather than compiling a new version of IPOPT, is it possible to instead improve the peformance of IPOPT in EAGO, by using set_attribute to select the linear solver, as described here? https://github.com/jump-dev/Ipopt.jl?tab=readme-ov-file#linear-solvers

Is it possible to select MKL instead of OpenBLAS for the BLAS & LAPACK backend library used by IPOPT and the linear solvers in EAGO by using the code below before creating the EAGO model? https://github.com/jump-dev/Ipopt.jl?tab=readme-ov-file#blas-and-lapack using MKL # Replace OpenBLAS by Intel MKL using Ipopt using EAGO model = Model(EAGO.Optimizer)

RXGottlieb commented 1 month ago

Based on the README for Ipopt, it looks like it should be possible as of Julia v1.9. We're still in the process of updating our overall documentation---we'll look into this as well to make sure it works as intended and update accordingly.

stumarcus314 commented 1 month ago

How would one set the IPOPT linear solver to SPRAL? Based on the IPOPT subsolver code in EAGO.jl (https://github.com/PSORLab/EAGO.jl/blob/master/src/subsolvers/ipopt.jl), the following attempt does not work.

using IntervalArithmetic; setrounding(Interval, :accurate) using EAGO, COPT eago_factory = () -> EAGO.Optimizer(SubSolvers(; r = COPT.Optimizer())) model = Model(eago_factory) MOI.set(model, MOI.RawOptimizerAttribute("linear_solver"), "spral") # Try to set IPOPT's linear solver to SPRAL. MOI.set(model, MOI.RawOptimizerAttribute("spral_use_gpu"), "yes") # Run SPRAL on the GPU.


$ julia create_model.jl Number of pulses: 256. Cardinal Optimizer v7.1.3. Build date Apr 29 2024 Copyright Cardinal Operations 2024. All Rights Reserved ERROR: LoadError: type Optimizer has no field linear_solver Stacktrace: [1] set(m::Optimizer{EAGO.Incremental{COPT.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}, p::MathOptInterface.RawOptimizerAttribute, x::String) @ EAGO ~/.julia/packages/EAGO/7BNFB/src/eago_optimizer/moi_wrapper.jl:212 [2] set @ ~/.julia/packages/MathOptInterface/2CULs/src/Bridges/bridge_optimizer.jl:955 [inlined] [3] set(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{Optimizer{EAGO.Incremental{COPT.Optimizer}, EAGO.Incremental{Ipopt.Optimizer}, EAGO.DefaultExt}}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, attr::MathOptInterface.RawOptimizerAttribute, value::String) @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/2CULs/src/Utilities/cachingoptimizer.jl:1059 [4] set(m::Model, attr::MathOptInterface.RawOptimizerAttribute, value::String) @ JuMP ~/.julia/packages/JuMP/Gwn88/src/optimizer_interface.jl:794 [5] macro expansion @ ~/julia_code/Gen7_BPM/create_model.jl:156 [inlined] [6] top-level scope @ ./timing.jl:395 in expression starting at /home/stuart/julia_code/Gen7_BPM/create_model.jl:85

RXGottlieb commented 1 month ago

EAGO sets subsolver parameters in two ways. The first is by overloading set_default_config! where the m field is the subsolver whose properties you are trying to set. The src/subsolvers/ipopt.jl file is an example of this: when EAGO solves upper problems, the settings in ipopt.jl are the settings that get passed to IPOPT. If you want to change the default parameters for IPOPT using this method, you would need to overload the set_default_config! function from ipopt.jl to reflect the different parameters you want.

The second way EAGO sets subsolver parameters is by setting the EAGOParameter called "user_solver_config" to true, and then setting parameters using MOI.set with the subsolver as the object. You need to set "user_solver_config" => true, or else your settings will be overwritten by set_default_config! when the subsolver is used. For your case, it might look something like this:

using IntervalArithmetic; setrounding(Interval, :accurate)
using JuMP, EAGO # (and COPT, if you want that as the subsolver)

eago_factory = () -> EAGO.Optimizer(SubSolvers())
model = Model(optimizer_with_attributes(eago_factory, "user_solver_config" => true))

upper_optimizer = model.moi_backend.optimizer.model.subsolver_block.upper_optimizer.optimizer.model

MOI.set(upper_optimizer, MOI.RawOptimizerAttribute("linear_solver"), "spral") # Try to set IPOPT's linear solver to SPRAL.
MOI.set(upper_optimizer, MOI.RawOptimizerAttribute("spral_use_gpu"), "yes") # Run SPRAL on the GPU.

#[variables/constraints/objective]

optimize!(model)
stumarcus314 commented 1 month ago

Thanks for the advice. I have used the second method. In the second method, are the default parameters (such as "tol" and "constr_viol_tol") set in src/subsolvers/ipopt.jl used by IPOPT, if those parameters are not set in upper_optimizer by MOI.set? That is, are the upper_optimizer parameters set after first calling set_default_config!?

RXGottlieb commented 1 month ago

The default parameters in set_default_config! are only used if "user_solver_config" is set to false (which is the default). If you want parameters such as tol to be set, and you have "user_solver_config" => true, you'll need to set those yourself as well.

stumarcus314 commented 1 month ago

If I modify the file ~/.julia/packages/EAGO/7BNFB/src/subsolvers/ipopt.jl in my Julia installation, will those changes be reflected when I use EAGO.jl to solve a JuMP model? Does the input parameter local_solver to the function set_default_config! equal true?

RXGottlieb commented 1 month ago

If you're looking to change the default behavior of EAGO, it's probably a better idea to create your own fork of EAGO and modify/use that. If you're only making changes for solving individual optimization problems, you should use one of the methods I described earlier to change subsolver parameters without changing the default EAGO operation.

The input parameter local_solver is there to indicate whether the settings are being used for a relaxed optimizer (local_solver=false) or upper optimizer (local_solver=true). IPOPT is the default upper optimizer, so the version of set_default_config! in src/subsolvers/ipopt.jl will typically be called with local_solver=true.