Как решить n нелинейных уравнений в R на основе векторов/матриц

Я пытаюсь решить систему из n уравнений в R. n-1 уравнений нелинейны, а последнее линейно. Если это поможет, это задача оптимизации с ограничениями, где уравнение n-1 является условием первого порядка, а последнее — бюджетным ограничением. Нелинейные уравнения n-1 можно охарактеризовать: нелинейными уравнениями

Если изображение не отображается, его можно определить поэлементно, например:

v_i*epsilon_i*cos(2/pi * e_i/delta_i)-лямбда=0

где эпсилон, v, e и дельта — векторы размерности n-1, а лямбда — скаляр, общий для всех уравнений).

Последнее уравнение представляет собой простое линейное уравнение вида |e|=c. То есть сумма всех элементов в e есть некоторое известное c, называемое ниже parms[1,4] или «бюджет».

Меня интересует решение для вектора e и постоянной лямбды, рассматривая все остальное как данность.

Я попытался использовать мультирут rootSolve. Для этого я определяю единственный вектор X, который должен быть вектором e, с добавленной к нему лямбдой, так что многокорень решает для x и получает n уравнений в виде списка. Все параметры сохраняются в матрице под названием «parms».

Сначала я определяю n-1 нелинейных уравнений

convex_focs <- function(x = numeric(),parms = numeric()){
deltas = parms[,1]
v = parms[,2]
lambda = x[1]
e = x[2:length(x)]
epsilon_2 = exp(parms[,3]) - parms[,1]
return(epsilon_2*cos((pi/2)*(e/deltas))-lambda)
}

В этом уравнении используется матричная запись, и оно прекрасно работает само по себе. Затем я определяю последнее линейное уравнение:

convex_budget <- function(x = numeric(),parms = numeric()){
e = x[2:length(x)]
return(sum(e)-parms[1,4])
}

Затем я попробовал convex_system <- function(x,parms) c(convex_focs,convex_budget ) и позвонил:

multiroot(f = convex_system, maxiter = 500000, start =  c(0,rep(budget/length(parms[,1]),length(parms[,1]))), parms = parms[,])

Это, конечно, не работает, так как rootSolve распознает convex_system как два уравнения, а X как n-мерное.

Если я отброшу последнее уравнение и буду считать лямбду заданным (то есть решу только нелинейные уравнения), я смогу получить решение. Но, конечно, это нехорошо, потому что я не знаю лямбда.

Итак, мой первый вопрос: 1. Как мне сгенерировать список функций из моих векторов, которые rootSolve распознает как систему? Я пытался использовать lapply или использовать виньетки, чтобы создать список convex_focs уравнений, по одному для каждого элемента в векторе, но не смог придумать, как заставить его работать. 2. Почему он распознает мою исходную функцию convex_focs как систему уравнений, но когда я добавляю convex_budget, она перестает работать?

Затем я (в отчаянии...) попытался вручную определить набор функций, глядя только на 3 нелинейных, а не на n-1. Это сделано для того, чтобы мой список функций выглядел как руководство и другие решения, которые я нашел в Интернете:

convex_system <- function(x,parms) c(F1 = 
                                     function(x =x,parms = parms){
                                       deltas = parms[1,1]
                                       v = parms[1,2]
                                       lambda = x[1]
                                       e = x[2]
                                       epsilon_2 = exp(parms[1,3]) - parms[1,1]
                                       return(v*epsilon_2*cos((pi/2)*(e/deltas))-lambda)
                                     }
                                     ,
                                   F2 = 
                                     function(x = x,parms = parms){
                                       deltas = parms[2,1]
                                       v = parms[2,2]
                                       lambda = x[1]
                                       e = x[3]
                                       epsilon_2 = exp(parms[2,3]) - parms[2,1]
                                       return(v*epsilon_2*cos((pi/2)*(e/deltas))-lambda)
                                     }
                                   ,
                                   F3 = 
                                     function(x = x,parms = parms){
                                       deltas = parms[3,1]
                                       v = parms[3,2]
                                       lambda = x[1]
                                       e = x[4]
                                       epsilon_2 = exp(parms[3,3]) - parms[3,1]
                                       return(v*epsilon_2*cos((pi/2)*(e/deltas))-lambda)
                                     }
                                     ,
                                   F_budget = function(x = x,parms = parms){
                                       e = x[2:length(x)]
                                       return(sum(e)-parms[1,4])}
                                  )

И вызов multiroot(f = convex_system, maxiter = 500000, start = c(0,rep(budget/length(parms[1:3,1]),length(parms[1:3,1]))), parms = parms[1:3,]) Когда я запускаю это, я получаю сообщение об ошибке

Ошибка в stode(y, times, func, parms = parms, ...): REAL() может применяться только к «числовому», а не к «списку»

Чего я действительно не понимаю - как список функций может не относиться к классу "список"? Итак, мой второй вопрос:

  1. Как создать список функций, если они не являются простыми однострочными функциями (как в ссылках выше)

Наконец, я был бы очень признателен за любые рекомендации о том, как лучше решать эти типы проблем в R.

Спасибо за любую помощь!


person P_sam    schedule 04.05.2020    source источник


Ответы (1)


Основная проблема с вашим подходом - спецификация функции convex_system. То, как вы это написали, подразумевает, что это вектор функций, и он не оценивается. Просто попробуйте один оператор convex_system(start,parms), чтобы увидеть возвращаемое значение.

Измените это на

convex_system <- function(x,parms) c(convex_focs(x,parms),convex_budget(x,parms) )

который возвращает значения, возвращенные для определенных значений x и parms.

Вы не указали значения констант и переменных. Так что мы не можем попробовать что-то.

Поэтому используйте поддельные данные:

budget <- 100
lambda <- 5

parms <- matrix(c(1,2,3,
                  2,3,4,
                  3,4,5,
                  105,0,0), ncol=4,byrow=FALSE)

parms
xstart <- c(0,rep(budget/length(parms[1:3,1]),length(parms[1:3,1])))

И, пожалуйста, не забудьте показать весь соответствующий код, даже library операторов.

Я попробовал два пакета для решения системы нелинейных уравнений: nleqslv и rootSolve.

library(nleqslv)
nleqslv(xstart,convex_system,parm=parms)

в результате чего

$x
[1] -18.07036  37.79143  34.44652  32.76205

$fvec
[1]  6.578382e-10 -4.952128e-11 -1.673328e-12  0.000000e+00

$termcd
[1] 1

$message
[1] "Function criterion near zero"

$scalex
[1] 1 1 1 1

$nfcnt
[1] 146

$njcnt
[1] 7

$iter
[1] 92

См. документацию nleqslv, чтобы узнать значение элементов приведенного выше списка. Кроме того, nleqslv в этом случае использовал метод Бройдена, который экономит вычисления якобиана.

Использование rootSolve дает:

library(rootSolve)
multiroot(f = convex_system, maxiter = 500000, start = xstart, parms=parms)
$root
[1] -18.07036  37.79143  34.44652  32.76205

$f.root
[1] 1.650808e-06 4.383290e-08 8.365979e-08 1.250555e-11

$iter
[1] 10

$estim.precis
[1] 4.445782e-07

Как вы можете видеть, оба дают одинаковые результаты, но результаты nleqslv кажутся ближе к нулю для значений составляющих функций (сравните fvec с f.root). Вы должны знать о разнице в критериях сходимости (см. документацию).

Решит ли это всю вашу проблему, решать вам.

Дополнение

Кажется, что nleqslv нужно больше итераций, чем rootSolve. Это связано с используемым методом глобального поиска. Используя функцию testnslv, можно искать глобальный метод, используя меньше итераций, как это

testnslv(xstart,convex_system,parm=parms)

со следующим результатом

Call:
testnslv(x = xstart, fn = convex_system, parm = parms)

Results:
    Method Global termcd Fcnt Jcnt Iter Message     Fnorm
1   Newton  cline      1   21   12   12   Fcrit 1.770e-24
2   Newton  qline      1   21   12   12   Fcrit 2.652e-24
3   Newton  gline      1   17    8    8   Fcrit 5.717e-25
4   Newton pwldog      1   45   31   31   Fcrit 9.837e-24
5   Newton dbldog      1   34   26   26   Fcrit 1.508e-25
6   Newton   hook      1   65   40   40   Fcrit 2.025e-26
7   Newton   none      1   10   10   10   Fcrit 7.208e-25
8  Broyden  cline      1   19    2   12   Fcrit 1.775e-19
9  Broyden  qline      1   19    2   12   Fcrit 1.768e-19
10 Broyden  gline      1   43    3   13   Fcrit 9.725e-18
11 Broyden pwldog      1  161    4  105   Fcrit 1.028e-19
12 Broyden dbldog      1  168    5  111   Fcrit 9.817e-21
13 Broyden   hook      1  121    7   67   Fcrit 5.138e-25
14 Broyden   none      1   11    1   11   Fcrit 7.487e-22

Видно, что для method="Newton" методы gline и none (чистый Ньютон-Рафсон) требуют наименьшего количества итераций. И что метод Бройдена вообще без глобального поиска требует наименьшего количества вычислений функции.

Предупреждение

Чтобы понять, почему некоторые глобальные методы «лучше», укажите control=list(trace=1) в качестве аргумента для nleqslv, например. global="none" и global="gline". Вы увидите, что чистый Ньютон не уменьшает критерий функции на каждой итерации. Это просто повезло.

person Bhas    schedule 05.05.2020