0

I am trying to solve a system of n equations in R. n-1 of the equations are non-linear and the last one is linear. If it helps, this is a constrained optimization problem, with the n-1 equation being first order conditions, and the last being a budget constraint. The n-1 non-linear equations can be characterized by:non-linear equations

In case the image doesn't show, it can be defined, element-by-element like:

v_i*epsilon_i*cos(2/pi * e_i/delta_i)-lambda=0

where epsilon,v, e, and delta are vectors of n-1 dimension, and lambda is a scalar common to all equations).

The last equation is a simple linear equation of the form |e|=c. That is, the sum of all elements in e is some known c, called below parms[1,4] or "budget".

I am interested in solving for the vector e and the constant lambda, treating everything else as given.

I tried using rootSolve's multiroot. To do this, I define a single vector X, which is supposed to be the vector e, with lambda appended to it, so that multiroot solves for x, and is given the n equations as a list. All the parameters are saved in a matrix called "parms"

I first define the n-1 non-linear equations

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)
}

This equation uses the matrix notation, and works fine by itself. I then define the last, linear, equation:

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

I then tried convex_system <- function(x,parms) c(convex_focs,convex_budget ) and call:

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

This of course doesn't work, as rootSolve recognizes convex_system as two equations, but X as n-dimensions.

If I drop the last equation, and treat lambda as given (so only solving the non-linear equations) I can get a solution. But of course this isn't good, because I don't know lambda.

So my first question is: 1. How do I generate a list of functions from my vectors that rootSolve will recognize as a system? I tried using lapply, or using vignettes, to create a list of the convex_focs equations, one for every elememt in the vector, but couldn't think of a way to make it work. 2. Why would it recognize my original convex_focs function as a system of equations, but when I add convex_budget it stopped working?

I then (in desperation...) tried manually defining a set of functions, looking at only 3 non-linear, rather than n-1. This is so that my list of functions will look like the manual and other solutions I found online:

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])}
                                  )

And calling 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,]) When I run this I get the error

Error in stode(y, times, func, parms = parms, ...) : REAL() can only be applied to a 'numeric', not a 'list'

Which I really don't understand - how could the list of functions not be of class 'list'? So my second question is:

  1. How does one generate a list of functions when they are not simple one-line-functions (as those in the links above)

Finally, I'd greatly appreciate any guidance on how to better solve these types of problems in R.

Thank you for any assistance!

P_sam
  • 13
  • 2

1 Answers1

0

The main problem with your approach is the specification of the function convex_system. The way you have written it implies that it is a vector of functions and it is not being evaluated. Just try the single statement convex_system(start,parms) to see the return value.

Change this to

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

which returns the values returned for specific values of x and parms.

You have not provided any values for the constants and variables. So we can't try something.

So use fake data:

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])))

And please do not forget to show all relevant code even library statements.

I have tried two packages for solving a system of nonlinear equations: nleqslv and rootSolve.

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

resulting in

$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

See the documentation of nleqslv for the meaning of the elements of the above list. Additionally nleqslv in this case used the Broyden method which saves Jacobian computations.

Using rootSolve gives:

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

As you can see both give the same results but the results of nleqslv appear to be closer to zero for the constituent function values (compare fvec with f.root). You should be aware of the difference in convergence criteria (see documentation).

Whether this will solve your full problem is up to you to find out.

Addendum

It seems that nleqslv needs more iterations than rootSolve. This is related to the global search method used. By using the function testnslv one can look for a global method using less iterations like this

testnslv(xstart,convex_system,parm=parms)

with the following result

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

One can see that for method="Newton" the methods gline and none (pure Newton-Raphson) require the least amount of iterations. And that the Broyden method with no global search at all requires the least number of function evaluations.

Warning

To see why some of the global methods are "better" specify control=list(trace=1) as an argument to nleqslv for e.g. global="none" and global="gline". You will see that pure Newton is not decreasing the function criterion in each iteration. It is just lucky.

Bhas
  • 1,844
  • 1
  • 11
  • 9