Практические руководства

Как решить задачу оптимизации ограничений в R

Оптимизация нелинейных ограничений в R с использованием nloptr

Вступление

Часто в исследованиях в области физических наук мы сталкиваемся с трудной проблемой оптимизации функции (называемой объективной), которая должна удовлетворять ряду ограничений - линейным или нелинейным равенствам и неравенствам. Оптимизаторы обычно также должны придерживаться верхней и нижней границы. Недавно я работал над аналогичной проблемой в квантовой информатике (QIS), где я попытался оптимизировать нелинейную функцию на основе нескольких ограничений, продиктованных правилами физики и математики. Заинтересованные читатели могут найти мою работу Оптимизация созвездия для когерентных состояний с фазовой манипуляцией с приемником смещения для максимизации взаимной информации, в которой я оптимизировал взаимную информацию для четырехфазной манипуляции (QPSK) на основе набора ограничений.



Преследуя проблему нелинейной оптимизации с набором ограничений, я обнаружил, что не все процедуры оптимизации созданы одинаково. На разных языках доступно несколько библиотек, таких как python (scipy.optimize), Matlab (fmincon), C ++ (robotim, nlopt), и R (nloptr). Хотя мой список процедур оптимизации не является исчерпывающим, некоторые из них более надежны, чем другие, некоторые обеспечивают более быстрое выполнение, чем другие, а некоторые имеют лучшую документацию. Основываясь на нескольких ключевых факторах, я считаю, что nloptr, реализованный на языке R, наиболее подходит для нелинейной оптимизации. nloptr использует nlopt, реализованный на C ++, в качестве серверной части. В результате он обеспечивает элегантность языка R и скорость C ++. Процедура оптимизации выполняется быстро за доли секунды даже с допуском порядка 10e-15.

Задача нелинейной оптимизации

Общая задача нелинейной оптимизации обычно имеет вид

где f - целевая функция, g определяет набор ограничений неравенства, h - набор ограничений равенства. xL и xU - нижняя и верхняя границы соответственно. В литературе представлено несколько алгоритмов оптимизации. Например, MMA (Метод движущихся асимптот) ¹ поддерживает произвольные ограничения нелинейного неравенства, (COBYLA) Оптимизация с ограничениями посредством линейного приближения², (ORIG_DRIECT) алгоритм DIRECT³. Алгоритмы оптимизации, которые также поддерживают ограничения нелинейного равенства, включают ISRES (улучшенная стратегия эволюции стохастического ранжирования) ⁴, (AUGLAG) Augmented Lagrangian Algorithm⁵. Полный список таких методов можно найти на справочной странице nlopt C ++ по адресу https://nlopt.readthedocs.io/en/latest/NLopt_Reference/. nloptr использует один из этих методов для решения данной задачи оптимизации.

«MMA (Метод движущихся асимптот) поддерживает произвольные ограничения нелинейного неравенства, (COBYLA) Оптимизация с ограничениями посредством линейного приближения, (ORIG_DRIECT) алгоритм DIRECT. Алгоритмы оптимизации, которые также поддерживают ограничения нелинейного равенства, включают ISRES (улучшенную стратегию эволюции стохастического ранжирования), (AUGLAG) расширенный лагранжев алгоритм ».

В оставшейся части статьи я приведу несколько примеров решения проблемы оптимизации ограничений с помощью R. Лично я использую R Studio, объединяющую компилятор и редактор R. R Studio также предоставляет инструмент knitr, который отлично подходит для написания документации или статей со встроенным кодом, который также может генерировать исходный латексный код и файл pdf. Большая часть представленного здесь примера была изменена из наборов тестов, используемых для проверки функций в пакете nloptr R.

Установка и загрузка библиотеки

Установка nloptr в R довольно проста.

install.packages(“nloptr”)
library(‘nloptr’)

Пример 1: Оптимизация с явным градиентом

В первом примере мы минимизируем функцию Rosenbrock Banana

чей градиент задается

Однако не все алгоритмы в nlopt требуют явного градиента, как мы увидим в следующих примерах. Давайте сначала определим целевую функцию и ее градиент:

eval_f <- function(x)
{
    return ( 100 * (x[2] - x[1] * x[1])^2 + (1 - x[1])^2 )
}
eval_grad_f <- function(x) {
return( c( -400 * x[1] * (x[2] - x[1] * x[1]) - 2 * (1 - x[1]),
200 * (x[2] - x[1] * x[1]) ) )
}

Также нам потребуются начальные значения оптимизаторов:

x0 <- c( -1.2, 1 )

Перед тем, как запустить процедуру минимизации, нам нужно указать, какой алгоритм мы будем использовать. Это можно сделать следующим образом:

opts <- list("algorithm"="NLOPT_LD_LBFGS",
"xtol_rel"=1.0e-8)

Здесь мы будем использовать алгоритм L-BFGS. Теперь мы готовы запустить процедуру оптимизации.

# solve Rosenbrock Banana function
res <- nloptr( x0=x0,
eval_f=eval_f,
eval_grad_f=eval_grad_f,
opts=opts)

Мы можем увидеть результат, набрав

print(res)

Функция оптимизирована на (1,1), что является основной истиной. Проверьте себя, запустив код в R Studio.

Пример 2: Минимизация с ограничением неравенства без градиентов

Проблема минимизации:

с a1 = 2, b1 = 0, a2 = −1 и b2 = 1. Мы переупорядочиваем ограничения, чтобы они имели вид g (x) ≤ 0:

Сначала определите целевую функцию

# objective function
eval_f0 <- function( x, a, b ){
return( sqrt(x[2]) )
}

и ограничения

# constraint function
eval_g0 <- function( x, a, b ) {
return( (a*x[1] + b)^3 - x[2] )
}

Определить параметры

# define parameters
a <- c(2,-1)
b <- c(0, 1)

Теперь решите, используя NLOPT_LN_COBYLA без информации о градиенте

# Solve using NLOPT_LN_COBYLA without gradient information
res1 <- nloptr( x0=c(1.234,5.678),
eval_f=eval_f0,
lb = c(-Inf,0),
ub = c(Inf,Inf),
eval_g_ineq = eval_g0,
opts = list("algorithm"="NLOPT_LN_COBYLA",
"xtol_rel"=1.0e-8),
a = a,
b = b )
print( res1 )

Пример 3: Минимизация с ограничениями равенства и неравенства без градиентов

Мы хотим решить следующую задачу оптимизации ограничений

с учетом ограничений

В этом примере оптимальное решение достигается при (1.00000000, 4.74299963, 3.82114998, 1.37940829).

# Objective Function
eval_f <- function(x)
{
return (x[1]*x[4]*(x[1] +x[2] + x[3] ) + x[3] )
}
# Inequality constraints
eval_g_ineq <- function(x)
{
return (25 - x[1]*x[2]*x[3]*x[4])
}
# Equality constraints
eval_g_eq <- function(x)
{
return ( x[1]^2 + x[2]^2 + x[3]^2 + x[4]^2 - 40 )
}
# Lower and upper bounds
lb <- c(1,1,1,1)
ub <- c(5,5,5,5)
#initial values
x0 <- c(1,5,5,1)

Нам также нужно будет определить параметры для оптимизации.

# Set optimization options.
local_opts <- list( "algorithm" = "NLOPT_LD_MMA", "xtol_rel" = 1.0e-15 )
opts <- list( "algorithm"= "NLOPT_GN_ISRES",
"xtol_rel"= 1.0e-15,
"maxeval"= 160000,
"local_opts" = local_opts,
"print_level" = 0 )

Мы используем NL_OPT_LD_MMA для локальной оптимизации и NL_OPT_GN_ISRES для общей оптимизации. Вы можете установить очень низкий допуск, чтобы получить наилучший результат. Количество итераций задается с помощью maxeval. Установка действительно низкого допуска или очень большого количества итераций может привести к наилучшему приближению за счет увеличения времени вычислений. Наконец, оптимизируем

res <- nloptr ( x0 = x0,
                eval_f = eval_f,
                lb = lb,
                ub = ub,
                eval_g_ineq = eval_g_ineq,
                eval_g_eq = eval_g_eq,
                opts = opts
)
print(res)

В моем случае результат оказался (1 4,768461 3,78758 1,384204), что довольно близко к истине.

Пример 4: Минимизация с множественными ограничениями неравенства без градиентов

Наша целевая функция в этом случае

при условии

с оценками переменных как

Для этой задачи оптимальное решение достигается на (1, 1). Теперь напишем код.

# Objective function
eval_f <- function(x)
{
return ( x[1]^2 + x[2]^2 )
}
# Inequality constraints
eval_g_ineq <- function (x) {
constr <- c(1 - x[1] - x[2],
1 - x[1]^2 - x[2]^2,
9 - 9*x[1]^2 - x[2]^2,
x[2] - x[1]^2,
x[1] - x[2]^2)
return (constr)
}
# Lower and upper bounds
lb <- c(-50, -50)
ub <- c(50, 50)
# Initial values
x0 <- c(3, 1)

Наконец, определите параметры для nloptr следующим образом:

opts <- list( "algorithm"
= "NLOPT_GN_ISRES",
"xtol_rel"
= 1.0e-15,
"maxeval"= 160000,
"tol_constraints_ineq" = rep( 1.0e-10, 5 ))

а затем выполните оптимизацию

res <- nloptr(
        x0          = x0,
        eval_f      = eval_f,
        lb          = lb,
        ub          = ub,
        eval_g_ineq = eval_g_ineq,
        opts        = opts )
print(res)

При указанном допуске и количестве итераций мне удалось достичь оптимального решения (1, 1).

Хотя я не приводил примеры с множественными ограничениями равенства, они очень похожи на Пример 4. Однако не забудьте выбрать алгоритм оптимизации как NLOPT_GN_ISRES.

Ссылки

  1. К. Сванберг, Метод движущихся асимптот - новый метод структурной оптимизации, Международный журнал численных методов в инженерии, 1987, 24, 359–373.
  2. Пауэлл М.Дж. (1994) Достижения в области оптимизации и численного анализа, Kluwer Academic, Dordrecht, Метод прямой поисковой оптимизации, моделирующий целевые функции и функции ограничений с помощью линейной интерполяции, стр. 51–67.
  3. Https://people.cs.vt.edu/~ltw/lecture_notes/HPCS08tut.pdf
  4. Томас П. Рунарссон и Синь Яо, «Стохастическое ранжирование для эволюционной оптимизации с ограничениями», IEEE Trans. Эволюционные вычисления, т. 4 (№ 3), с. 284–294 (2000).
  5. Https://www.him.uni-bonn.de/fileadmin/him/Section6_HIM_v1.pdf
  6. Https://people.kth.se/~krille/mmagcmma.pdf
  7. Https://cran.r-project.org/web/packages/nloptr/vignettes/nloptr.pdf
  8. Http://faculty.cas.usf.edu/jkwilde/mathcamp/Constrained_Optimization.pdf
  9. Https://nlopt.readthedocs.io/en/latest/

Если эта статья принесет вам пользу, пожалуйста, используйте следующие цитаты, чтобы ссылаться на мою работу:

Rahul Bhadani. Nonlinear Optimization in R using nlopt. arXiv preprint arXiv:2101.02912, 2021.

or

@article{bhadani2021nonlinear,
    title={Nonlinear Optimization in R using nlopt},
    author={Rahul Bhadani},
    year={2021},
    eprint={2101.02912},
    archivePrefix={arXiv},
    primaryClass={math.OC},
  journal={arXiv preprint arXiv:2101.02912},
}