Документация Engee

Ipopt.jl

Ipopt.jl — это оболочка для решателя Ipopt.

Принадлежность

Эта оболочка поддерживается сообществом JuMP и не является проектом COIN-OR.

Использование с JuMP

Ipopt используется с JuMP следующим образом:

using JuMP, Ipopt
model = Model(Ipopt.Optimizer)
set_attribute(model, "max_cpu_time", 60.0)
set_attribute(model, "print_level", 0)

API MathOptInterface

Оптимизатор Ipopt поддерживает следующие ограничения и атрибуты.

Список поддерживаемых целевых функций:

  • MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}

  • MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}

  • MOI.ObjectiveFunction{MOI.VariableIndex}

Список поддерживаемых типов переменных:

  • MOI.Reals

Список поддерживаемых типов ограничений:

  • MOI.ScalarAffineFunction{Float64} в MOI.EqualTo{Float64}

  • MOI.ScalarAffineFunction{Float64} в MOI.GreaterThan{Float64}

  • MOI.ScalarAffineFunction{Float64} в MOI.LessThan{Float64}

  • MOI.ScalarQuadraticFunction{Float64} в MOI.EqualTo{Float64}

  • MOI.ScalarQuadraticFunction{Float64} в MOI.GreaterThan{Float64}

  • MOI.ScalarQuadraticFunction{Float64} в MOI.LessThan{Float64}

  • MOI.VariableIndex в MOI.EqualTo{Float64}

  • MOI.VariableIndex в MOI.GreaterThan{Float64}

  • MOI.VariableIndex в MOI.LessThan{Float64}

Список поддерживаемых атрибутов моделей:

  • MOI.NLPBlock()

  • MOI.NLPBlockDualStart()

  • MOI.Name()

  • MOI.ObjectiveSense()

Параметры

Поддерживаемые параметры приведены в документации по Ipopt.

Обратные вызовы, характерные для решателей

Ipopt предоставляет обратный вызов, который можно использовать для регистрации статуса оптимизации во время решения. Он также может использоваться для завершения оптимизации, возвращая false. Вот пример:

using JuMP, Ipopt, Test
model = Model(Ipopt.Optimizer)
set_silent(model)
@variable(model, x >= 1)
@objective(model, Min, x + 0.5)
x_vals = Float64[]
function my_callback(
   alg_mod::Cint,
   iter_count::Cint,
   obj_value::Float64,
   inf_pr::Float64,
   inf_du::Float64,
   mu::Float64,
   d_norm::Float64,
   regularization_size::Float64,
   alpha_du::Float64,
   alpha_pr::Float64,
   ls_trials::Cint,
)
   push!(x_vals, callback_value(model, x))
   @test isapprox(obj_value, 1.0 * x_vals[end] + 0.5, atol = 1e-1)
   # возвращает `true` для продолжения работы или `false` для завершения оптимизации.
   return iter_count < 1
end
MOI.set(model, Ipopt.CallbackFunction(), my_callback)
optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INTERRUPTED
@test length(x_vals) == 2

Объяснение аргументов обратного вызова см. в документации по Ipopt. Они идентичны выходным данным, содержащимся в таблице ведения журналов, выводимой на экран.

Чтобы получить доступ к текущему решению и первичным, двойственным и комплементарным нарушениям каждой итерации, используйте Ipopt.GetIpoptCurrentViolations и Ipopt.GetIpoptCurrentIterate. Эти две функции идентичны функциям в интерфейсе C для Ipopt.

API для C

Ipopt.jl заключает в оболочку интерфейс C для Ipopt с минимальными изменениями.

Полный пример доступен в файле test/C_wrapper.jl.

Для простоты скажем, что пять обратных вызовов, требуемых Ipopt, немного отличаются от интерфейса C. Вот они:

"""
   eval_f(x::Vector{Float64})::Float64

Returns the objective value `f(x)`.
"""
function eval_f end

"""
   eval_grad_f(x::Vector{Float64}, grad_f::Vector{Float64})::Nothing

Fills `grad_f` in-place with the gradient of the objective function evaluated at
`x`.
"""
function eval_grad_f end

"""
   eval_g(x::Vector{Float64}, g::Vector{Float64})::Nothing

Fills `g` in-place with the value of the constraints evaluated at `x`.
"""
function eval_g end

"""
   eval_jac_g(
      x::Vector{Float64},
      rows::Vector{Cint},
      cols::Vector{Cint},
      values::Union{Nothing,Vector{Float64}},
   )::Nothing

Compute the Jacobian matrix.

* If `values === nothing`
   - Fill `rows` and `cols` with the 1-indexed sparsity structure
* Otherwise:
   - Fill `values` with the elements of the Jacobian matrix according to the
     sparsity structure.

[WARNING]
====
If `values === nothing`, `x` is an undefined object. Accessing any elements
====
    in it will cause Julia to segfault.
"""
function eval_jac_g end

"""
   eval_h(
      x::Vector{Float64},
      rows::Vector{Cint},
      cols::Vector{Cint},
      obj_factor::Float64,
      lambda::Float64,
      values::Union{Nothing,Vector{Float64}},
   )::Nothing

Compute the Hessian-of-the-Lagrangian matrix.

* If `values === nothing`
   - Fill `rows` and `cols` with the 1-indexed sparsity structure
* Otherwise:
   - Fill `values` with the Hessian matrix according to the sparsity structure.

[WARNING]
====
If `values === nothing`, `x` is an undefined object. Accessing any elements
====
    in it will cause Julia to segfault.
"""
function eval_h end

Ошибка INVALID_MODEL

Вывод состояния завершения MOI.INVALID_MODEL, вероятно, связан с тем, что в вашей модели есть какое-то неопределенное значение, например деление на нуль. Исправьте ошибку, удалив деление или наложив границы переменной так, чтобы отсечь неопределенную область.

Вместо

model = Model(Ipopt.Optimizer)
@variable(model, x)
@NLobjective(model, 1 / x)

выполните

model = Model(Ipopt.Optimizer)
@variable(model, x >= 0.0001)
@NLobjective(model, 1 / x)

Линейные решатели

В целях повышения производительности Ipopt поддерживает ряд линейных решателей.

HSL

Получите лицензию и скачайте HSL_jll.jl со страницы https://licences.stfc.ac.uk/product/julia-hsl.

Доступны две версии: LBT и OpenBLAS. LBT является рекомендуемым вариантом для версий Julia не ниже 1.9.

Установите скачанные файлы в текущую среду:

import Pkg
Pkg.develop(path = "/full/path/to/HSL_jll.jl")

Затем используйте линейный решатель в HSL, задав атрибуты hsllib и linear_solver:

using JuMP, Ipopt
import HSL_jll
model = Model(Ipopt.Optimizer)
set_attribute(model, "hsllib", HSL_jll.libhsl_path)
set_attribute(model, "linear_solver", "ma86")

Для пользователей macOS

В связи с политикой безопасности macOS пользователям компьютеров Mac может потребоваться удалить атрибут карантина в ZIP-архиве перед извлечением. Например:

xattr -d com.apple.quarantine lbt_HSL_jll.jl-2023.5.26.zip
xattr -d com.apple.quarantine openblas_HSL_jll.jl-2023.5.26.zip

Pardiso

Скачайте Pardiso с веб-сайта https://www.pardiso-project.org. Сохраните библиотеку общего доступа в каком-нибудь месте и запишите имя файла.

Затем используйте Pardiso, задав атрибуты pardisolib и linear_solver:

using JuMP, Ipopt
model = Model(Ipopt.Optimizer)
set_attribute(model, "pardisolib", "/full/path/to/libpardiso")
set_attribute(model, "linear_solver", "pardiso")

SPRAL

Если вы используете Ipopt.jl с Julia версии не ниже 1.9, доступен линейный решатель SPRAL. Его можно использовать, задав атрибут linear_solver:

using JuMP, Ipopt
model = Model(Ipopt.Optimizer)
set_attribute(model, "linear_solver", "spral")

Обратите внимание, что перед запуском Julia должны быть заданы следующие переменные среды:

export OMP_CANCELLATION=TRUE
export OMP_PROC_BIND=TRUE

BLAS и LAPACK

В Julia версии 1.9 или более поздней Ipopt и линейные решатели MUMPS (по умолчанию), SPRAL и HSL компилируются с библиотекой libblastrampoline (LBT), которая может переключаться между бэкендами BLAS и LAPACK во время выполнения.

Бэкендом BLAS и LAPACK по умолчанию является OpenBLAS.

Используя LBT, можно также динамически переключаться на другие бэкенды BLAS, такие как Intel MKL и Apple Accelerate. Поскольку Ipopt и линейные решатели в значительной степени зависят от процедур BLAS и LAPACK, использование оптимизированного для конкретной платформы бэкенда может повысить производительность.

MKL

Если у вас установлен MKL.jl, переключитесь на MKL, добавив using MKL в код:

using MKL  # Замена OpenBLAS на Intel MKL
using Ipopt

AppleAccelerate

Если вы используете macOS версии не ниже 13.4 и у вас установлен AppleAccelerate.jl, добавьте using AppleAccelerate в код:

using AppleAccelerate  # Замена OpenBLAS на Apple Accelerate
using Ipopt

Бэкенды отображения

Проверьте, какие бэкенды загружены:

import LinearAlgebra
LinearAlgebra.BLAS.lbt_get_config()