5

I have a function that I am optimizing using the optimx function in R (I'm also open to using optim, since I'm not sure it will make a difference for what I'm trying to do). I have a gradient that I am passing to optimx for (hopefully) faster convergence compared to not using a gradient. Both the function and the gradient use many of the same quantities that are computed from each new parameter set. One of these quantities in particular is very computationally costly, and it's redundant to have to compute this quantity twice for each iteration - once for the function, and again for the gradient. I'm trying to find a way to compute this quantity once, then pass it to the function and the gradient.

So here is what I am doing. So far this works, but it is inefficient:

optfunc<-function(paramvec){
    quant1<-costlyfunction(paramvec) 
    #costlyfunction is a separate function that takes a while to run

    loglikelihood<-sum(quant1)**2 
    #not really squared, but the log likelihood uses quant1 in its calculation

    return(loglikelihood)
}

optgr<-function(paramvec){
    quant1<-costlyfunction(paramvec)
    mygrad<-sum(quant1) #again not the real formula, just for illustration
    return(mygrad)
}

optimx(par=paramvec,fn=optfunc,gr=optgr,method="BFGS")

I am trying to find a way to calculate quant1 only once with each iteration of optimx. It seems the first step would be to combine fn and gr into a single function. I thought the answer to this question may help me, and so I recoded the optimization as:

optfngr<-function(){
    quant1<-costlyfunction(paramvec)
    optfunc<-function(paramvec){
        loglikelihood<-sum(quant1)**2
        return(loglikelihood)
    }
    optgr<-function(paramvec){
        mygrad<-sum(quant1)
        return(mygrad)
    }
    return(list(fn = optfunc, gr = optgr))
}

do.call(optimx, c(list(par=paramvec,method="BFGS",optfngr() )))

Here, I receive the error: "Error in optimx.check(par, optcfg$ufn, optcfg$ugr, optcfg$uhess, lower, : Cannot evaluate function at initial parameters." Of course, there are obvious problems with my code here. So, I'm thinking answering any or all of the following questions may shed some light:

  1. I passed paramvec as the only arguments to optfunc and optgr so that optimx knows that paramvec is what needs to be iterated over. However, I don't know how to pass quant1 to optfunc and optgr. Is it true that if I try to pass quant1, then optimx will not properly identify the parameter vector?

  2. I wrapped optfunc and optgr into one function, so that the quantity quant1 will exist in the same function space as both functions. Perhaps I can avoid this if I can find a way to return quant1 from optfunc, and then pass it to optgr. Is this possible? I'm thinking it's not, since the documentation for optimx is pretty clear that the function needs to return a scalar.

  3. I'm aware that I might be able to use the dots arguments to optimx as extra parameter arguments, but I understand that these are for fixed parameters, and not arguments that will change with each iteration. Unless there is also a way to manipulate this?

Thanks in advance!

2
  • One simple approach: keep track of the x vector. If changed then recompute intermediate results. Commented Jun 17, 2018 at 15:15
  • Do you mind elaborating a bit? Is x vector the parameters that are changing? What are the "intermediate results"? Commented Jun 17, 2018 at 17:28

1 Answer 1

6

Your approach is close to what you want, but not quite right. You want to call costlyfunction(paramvec) from within optfn(paramvec) or optgr(paramvec), but only when paramvec has changed. Then you want to save its value in the enclosing frame, as well as the value of paramvec that was used to do it. That is, something like this:

optfngr<-function(){
    quant1 <- NULL
    prevparam <- NULL

    updatecostly <- function(paramvec) {
      if (!identical(paramvec, prevparam)) {
        quant1 <<- costlyfunction(paramvec)
        prevparam <<- paramvec
      }
    }
    optfunc<-function(paramvec){
        updatecostly(paramvec)
        loglikelihood<-sum(quant1)**2
        return(loglikelihood)
    }
    optgr<-function(paramvec){
        updatecostly(paramvec)
        mygrad<-sum(quant1)
        return(mygrad)
    }
    return(list(fn = optfunc, gr = optgr))
}

do.call(optimx, c(list(par=paramvec,method="BFGS"),optfngr() ))

I used <<- to make assignments to the enclosing frame, and fixed up your do.call second argument.

Doing this is called "memoization" (or "memoisation" in some locales; see http://en.wikipedia.org/wiki/Memoization), and there's a package called memoise that does it. It keeps track of lots of (or all of?) the previous results of calls to costlyfunction, so would be especially good if paramvec only takes on a small number of values. But I think it won't be so good in your situation because you'll likely only make a small number of repeated calls to costlyfunction and then never use the same paramvec again.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.