3

I am working on an optimization task where cost function evaluations are very expensive, and some error can be tolerated. I'm using some pre-packaged scipy methods from scipy.optimize to get started. The first one I'm working with is fmin, which implements the nelder mead simplex algorithm.

This function has two convergence related parameters xtol and ftol, which (as I understand it) specifiy a convergence criteria where if x or f (the parameter set, and the cost respectively) change by less than xtol or ftol on an iteration, the function returns.

However, since cost functions are so expensive for me, I want to also be able to specify a cost threshold where it will return immediately if it finds a point with less cost than the threshold.

Is it possible to specify this threshold for scipy.optimize.fmin?

Bonus question: I haven't looked in detail at many other methods, but it doesn't look like this threshold option exists for those either. Is this typical for scipy optimization methods? Would it be valuable for me to try to contribute this functionality?

Community
  • 1
  • 1
RJTK
  • 349
  • 3
  • 10

1 Answers1

4

It is possible to stop the iteration for any criterion that can be expressed as a function of x. The idea here is to hijack the callback method and use exceptions for flow control. Here are two solutions that exploit this idea:

from scipy.optimize import fmin_bfgs
import numpy as np
f = lambda x: np.linalg.norm(x**2)
x0 = np.random.rand(100)

Solution 1:

global result

class SmallEnoughException(Exception):
    pass

def check_conv_criteria(xk):
    global result
    if np.linalg.norm(xk) < 0.1:
        result = xk
        raise SmallEnoughException()

try:
    x, _, _ = fmin_bfgs(f, x0, callback=check_conv_criteria)
except SmallEnoughException:
    x = result

Solution 2:

class StopOptimizingException(Exception):
    pass

class CallbackCollector:

    def __init__(self, f, thresh):
        self._f  = f
        self._thresh = thresh

    def __call__(self, xk):
        if self._f(xk) < self._thresh:
            self.x_opt = xk
            raise StopOptimizingException()

try:
    cb = CallbackCollector(f, thresh=0.2)
    x, _, _ = fmin_bfgs(f, x0, callback=cb)
except StopOptimizingException:
    x = cb.x_opt
cel
  • 30,017
  • 18
  • 97
  • 117
  • Wouldn't this force one to evaluate the cost function on each callback? Defeating the purpose of stopping early to try to reduce the number of times the cost function is called. – RJTK May 22 '15 at 13:14
  • @Jyan - yes, but I don't see a way around that. I guess at some point you have to calculate `f(x)` to be able to compare it to the threshold. Do you see any way you could avoid that? If `f(x)` will be calculated multiple times, you could improve drastically by caching the evaluations of your function. – cel May 22 '15 at 13:46
  • Yeah I realized my comment was kind of dumb. I actually think the best way to solve this problem would be to put the stopping criteria inside the cost function, so that when the cost function is evaluated on a point with low enough cost it raises the exception - no need to use the callback. Thanks for the idea. – RJTK May 22 '15 at 18:39