4
votes

I have a function that evaluates the gradient and output simultaneously. I want to optimize it with respect to an objective function. How do I pass the objective and gradient as a list to optimx? The example below illustrates the problem:

Suppose I want to find the smallest non-negative root of the polynomial x^4 - 3*x^2 + 2*x + 3. Its gradient is 4*x^3 - 6*x + 2. I use the method nlminb in optimx, as shown below.

optimx(par = 100, method = "nlminb", fn = function(x) x^4 - 3*x^2 + 2*x + 3, 
                                     gr=function(x) 4*x^3 - 6*x + 2, lower = 0)

This works fine, and I get the following output:

       p1 value fevals gevals niter convcode kkt1 kkt2 xtimes
nlminb  1     3     27     24    23        0 TRUE TRUE      0

Now suppose I define the function fngr, which returns both the objective and gradient as a list:

fngr <- function(x) {
  fn <- x^4 - 3*x^2 + 2*x + 3
  gr <- 4*x^3 - 6*x + 2
  return (list(fn = fn, gr = gr))
}

I tried to call optimx as follows:

do.call(optimx, c(list(par = 100, lower = 0, method="nlminb"), fngr))

This returned the following error:

Error in optimx.check(par, optcfg$ufn, optcfg$ugr, optcfg$uhess, lower,  : 
  Function provided is not returning a scalar number

What is the right way to define fngr and the call to optimx when I want to pass the objective and gradient as a list?

Thanks.

1
I don't think you can really get away from defining fn and gr separately (even if this is obscured like in @42-'s answer). In general, optimisation subroutines will call each function independently of the other, possibly with different arguments. You can't assume that each call to fn can be matched to exactly one call to gr.Hong Ooi
Noted, thanks for the input.user3294195
way late, but I would consider whether it is worth memoizing the functions (there is a memoise package, I think)Ben Bolker

1 Answers

2
votes

Define a parameter-less function which can deliver the two functions with suitable names ... when called:

> fngr <- function() {
+   fn <- function(x) {x^4 - 3*x^2 + 2*x + 3}
+   gr <- function(x) {4*x^3 - 6*x + 2}
+   return (list(fn = fn, gr = gr))
+ }
> do.call(optimx, c(list(par = 100, lower = 0, method="nlminb"), fngr() ))
                                    notice the need to call it ------^^
       p1 value fevals gevals niter convcode kkt1 kkt2 xtimes
nlminb  1     3     27     24    23        0 TRUE TRUE  0.002

Looking at this 5 years later I can fully understand the confusion. The way that @ user3294195 did it was much more typical. This is not a typical way of passing function objects in R.