Source code for pyopus.optimizer.hj

"""
.. inheritance-diagram:: pyopus.optimizer.hj
    :parts: 1

**Box constrained Hooke-Jeeves optimizer (PyOPUS subsystem name: HJOPT)**

An extended version of coordinate search where so-called **speculative steps** 
are taken from time to time. These steps are hoped to speed up the search. 

The convergence theory applied to coordinate search can also be applied to 
Hooke-Jeeves algorithm if the speculative step length factor is an integer. 

The algorithm (unconstrained version) was first published in [hj]_.  

.. [hj] Hooke R., Jeeves T. A., Direct Search Solution of Numerical and Statistical 
        Problems. Journal of the ACM (JACM), vol. 8, pp. 212-229, 1961.
"""

from ..misc.debug import DbgMsgOut, DbgMsg

from .coordinate import CoordinateSearch

from numpy import where, isinf, where, abs, max, array

__all__ = [ 'HookeJeeves' ]


[docs]class HookeJeeves(CoordinateSearch): """ Hooke-Jeeves optimizer class *speculative* is the step length factor for the speculative step. See the :class:`~pyopus.optimizer.base.coordinate.CoordinateSearch` class for more information. """ def __init__(self, function, xlo=None, xhi=None, debug=0, fstop=None, maxiter=None, stepup=1.0, stepdn=0.5, step0=None, minstep=None, speculative=2.0): # It has all members of a coordinate search CoordinateSearch.__init__(self, function, xlo, xhi, debug, fstop, maxiter, stepup=stepup, stepdn=stepdn, step0=step0, minstep=minstep) # Speculative step length (factor) self.speculative=speculative # Speculative step flag self.isSpeculative=None # Trial step origin self.xt=None self.ft=None # Fallback origin self.xo=None self.fo=None
[docs] def check(self): """ Checks the optimization algorithm's settings and raises an exception if something is wrong. """ CoordinateSearch.check(self) if (self.step0<=0).any(): raise Exception(DbgMsg("HJOPT", "Initial step must be greater than 0.")) if (self.stepup<1): raise Exception(DbgMsg("HJOPT", "Step increase must be greater or equal 1.")) if (self.stepdn>=1): raise Exception(DbgMsg("HJOPT", "Step decrease must be less than 1."))
[docs] def reset(self, x0): """ Puts the optimizer in its initial state and sets the initial point to be the 1-dimensional array or list *x0*. The length of the array becomes the dimension of the optimization problem (:attr:`ndim` member). The shape of *x0* must match that of *xlo* and *xhi*. """ CoordinateSearch.reset(self, x0) # Debug message if self.debug: DbgMsgOut("HJOPT", "Resetting Hooke-Jeeves.") self.step=self.step0.copy() self.isSpeculative=False self.xt=array(x0) self.ft=None self.xo=array(x0) self.fo=None # Debug message if self.debug: DbgMsgOut("HJOPT", "Done.")
[docs] def run(self): """ Runs the optimization algorithm. """ # Debug message if self.debug: DbgMsgOut("HJOPT", "Starting a Hooke-Jeeves run at i="+str(self.niter)) # Reset stop flag self.stop=False # Check and evaluate initial point self.check() # Evaluate initial point (if needed) if self.f is None: self.f=self.fun(self.x) if self.ft is None: self.ft=self.f if self.fo is None: self.fo=self.f # Prepare x=self.x.copy() f=self.f.copy() xt=self.xt ft=self.ft xo=self.xo fo=self.fo step=self.step isSpeculative=self.isSpeculative # Main loop while not self.stop: # Speculative step if isSpeculative: xspec=(xt-xo)*self.speculative self.bound(xspec) xo=xt fo=ft xt=xspec if self.debug: DbgMsgOut("HJOPT", "Iteration i="+str(self.niter)+": speculative step") # Trial steps i=0 while i<self.ndim: # Origin of trial steps in one dimension xtmp=xt.copy() ftmp=ft.copy() if step.size==1: delta=step else: delta=step[i] # +step xnew=xtmp.copy() xnew[i]=xtmp[i]+delta self.bound(xnew) fnew=self.fun(xnew) # Debug message if self.debug: DbgMsgOut("HJOPT", "Iteration i="+str(self.niter)+": f="+str(ft)+" step="+str(max(abs(step)))) if fnew<ft: xt=xnew ft=fnew if self.stop: break; # -step xnew=xtmp.copy() xnew[i]=xtmp[i]-delta self.bound(xnew) fnew=self.fun(xnew) # Debug message if self.debug: DbgMsgOut("HJOPT", "Iteration i="+str(self.niter)+": f="+str(ft)+" step="+str(max(abs(step)))) if fnew<ft: xt=xnew ft=fnew if self.stop: break; # Speculative step failed if isSpeculative and not (ft<fo): break # Next dimension i+=1 if self.stop: break; # Check if we need to change the step if isSpeculative: # Speculative step if ft>=fo: # Failed # Return to previous origin, next one is ordinary isSpeculative=False xt=xo ft=fo if self.debug: DbgMsgOut("HJOPT", "Iteration i="+str(self.niter)+": speculative step FAILED") else: # OK, increase step step*=self.stepup None else: # Ordinary step if ft>=fo: # Failed, reduce step step*=self.stepdn else: # OK, increase step, next one is speculative isSpeculative=True step*=self.stepup # Check step size if self.minstep is not None and (step<self.minstep).all(): if self.debug: DbgMsgOut("HJOPT", "Iteration i="+str(self.niter)+": step small enough, stopping") break; if self.debug: DbgMsgOut("HJOPT", "Hooke-Jeeves run finished.") self.step=step