Changes to evalFunction. and more extensible calls in startup and endIteration

This commit is contained in:
Rowan Cockett
2013-11-07 12:27:07 -08:00
parent 318ca555c8
commit ded738b077
3 changed files with 39 additions and 25 deletions
+5 -5
View File
@@ -23,13 +23,13 @@ class Inversion(object):
if not haveInserted: if not haveInserted:
self.opt.printers.insert(1,{"title": "beta", self.opt.printers.insert(1,{"title": "beta",
"value": lambda M: M.parent._beta, "value": lambda M: M.parent._beta,
"width": 13, "format": "%1.2e"}) "width": 10, "format": "%1.2e"})
self.opt.printers.insert(2,{"title": "phi_d", self.opt.printers.insert(2,{"title": "phi_d",
"value": lambda M: M.parent._phi_d_last, "value": lambda M: M.parent._phi_d_last,
"width": 13, "format": "%1.2e"}) "width": 10, "format": "%1.2e"})
self.opt.printers.insert(3,{"title": "phi_m", self.opt.printers.insert(3,{"title": "phi_m",
"value": lambda M: M.parent._phi_m_last, "value": lambda M: M.parent._phi_m_last,
"width": 13, "format": "%1.2e"}) "width": 10, "format": "%1.2e"})
def setKwargs(self, **kwargs): def setKwargs(self, **kwargs):
"""Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist.""" """Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist."""
@@ -80,8 +80,8 @@ class Inversion(object):
while True: while True:
self._beta = self.getBeta() self._beta = self.getBeta()
m = self.opt.minimize(self.evalFunction,m) m = self.opt.minimize(self.evalFunction,m)
if self.stoppingCriteria(): break
self._iter += 1 self._iter += 1
if self.stoppingCriteria(): break
return m return m
beta0 = 1.e2 beta0 = 1.e2
@@ -127,7 +127,7 @@ class Inversion(object):
operator = sp.linalg.LinearOperator( (m.size, m.size), H_fun, dtype=float ) operator = sp.linalg.LinearOperator( (m.size, m.size), H_fun, dtype=float )
out += (operator,) out += (operator,)
return out return out if len(out) > 1 else out[0]
def dataObj(self, m, u=None): def dataObj(self, m, u=None):
+31 -18
View File
@@ -33,7 +33,9 @@ class Minimize(object):
tolX = 1e-1 tolX = 1e-1
tolG = 1e-1 tolG = 1e-1
eps = 1e-5 eps = 1e-5
debug = True
debug = False
debugLS = False
def __init__(self, **kwargs): def __init__(self, **kwargs):
self._id = int(np.random.rand()*1e6) # create a unique identifier to this program to be used in pubsub self._id = int(np.random.rand()*1e6) # create a unique identifier to this program to be used in pubsub
@@ -79,17 +81,17 @@ class Minimize(object):
self.printers = [{ self.printers = [{
"title": "#", "title": "#",
"value": lambda M: M._iter, "value": lambda M: M._iter,
"width": 10, "width": 5,
"format": "%3d" "format": "%3d"
},{ },{
"title": "f", "title": "f",
"value": lambda M: self.f, "value": lambda M: self.f,
"width": 14, "width": 10,
"format": "%1.2e" "format": "%1.2e"
},{ },{
"title": "|g|", "title": "|g|",
"value": lambda M: norm(M.g), "value": lambda M: norm(M.g),
"width": 14, "width": 10,
"format": "%1.2e" "format": "%1.2e"
},{ },{
"title": "LS", "title": "LS",
@@ -101,17 +103,17 @@ class Minimize(object):
self.printersLS = [{ self.printersLS = [{
"title": "#", "title": "#",
"value": lambda M: (M._iter, M._iterLS), "value": lambda M: (M._iter, M._iterLS),
"width": 10, "width": 5,
"format": "%3d.%d" "format": "%3d.%d"
},{ },{
"title": "t", "title": "t",
"value": lambda M: M._LS_t, "value": lambda M: M._LS_t,
"width": 14, "width": 10,
"format": "%0.5f" "format": "%0.5f"
},{ },{
"title": "ft", "title": "ft",
"value": lambda M: M._LS_ft, "value": lambda M: M._LS_ft,
"width": 14, "width": 10,
"format": "%1.2e" "format": "%1.2e"
},{ },{
"title": "f + alp*g.T*p", "title": "f + alp*g.T*p",
@@ -143,6 +145,14 @@ class Minimize(object):
(f[, g][, H]) = evalFunction(x, return_g=False, return_H=False ) (f[, g][, H]) = evalFunction(x, return_g=False, return_H=False )
def evalFunction(x, return_g=False, return_H=False):
out = (f,)
if return_g:
out += (g,)
if return_H:
out += (H,)
return out if len(out) > 1 else out[0]
Events are fired with the following inputs via pypubsub:: Events are fired with the following inputs via pypubsub::
@@ -192,6 +202,7 @@ class Minimize(object):
while True: while True:
self.f, self.g, self.H = evalFunction(self.xc, return_g=True, return_H=True) self.f, self.g, self.H = evalFunction(self.xc, return_g=True, return_H=True)
if doPub: pub.sendMessage('Minimize.evalFunction', minimize=self, f=self.f, g=self.g, H=self.H) if doPub: pub.sendMessage('Minimize.evalFunction', minimize=self, f=self.f, g=self.g, H=self.H)
self.printIter()
if self.stoppingCriteria(): break if self.stoppingCriteria(): break
p = self.findSearchDirection() p = self.findSearchDirection()
if doPub: pub.sendMessage('Minimize.searchDirection', minimize=self, p=p) if doPub: pub.sendMessage('Minimize.searchDirection', minimize=self, p=p)
@@ -204,7 +215,6 @@ class Minimize(object):
if not caught: return self.xc if not caught: return self.xc
self.doEndIteration(xt) self.doEndIteration(xt)
if doPub: pub.sendMessage('Minimize.endIteration', minimize=self, xt=xt) if doPub: pub.sendMessage('Minimize.endIteration', minimize=self, xt=xt)
self.printIter()
self.printDone() self.printDone()
@@ -232,17 +242,19 @@ class Minimize(object):
If you have things that also need to run on startup, you can create a method:: If you have things that also need to run on startup, you can create a method::
def _startup(self, x0): def _startup*(self, x0):
pass pass
If present, _startup will be called at the start of the default startup call. Where the * can be any string. If present, _startup* will be called at the start of the default startup call.
You may also completely overwrite this function. You may also completely overwrite this function.
:param numpy.ndarray x0: initial x :param numpy.ndarray x0: initial x
:rtype: None :rtype: None
:return: None :return: None
""" """
if hasattr(self,'_startup'): self._startup(x0) if method in [posible for posible in dir(self) if '_startup' in posible]:
if self.debug: print 'startup is calling self.'+method
getattr(self,method)(xt)
self._iter = 0 self._iter = 0
self._iterLS = 0 self._iterLS = 0
@@ -417,16 +429,16 @@ class Minimize(object):
self._iterLS = 0 self._iterLS = 0
while self._iterLS < self.maxIterLS: while self._iterLS < self.maxIterLS:
self._LS_xt = self.projection(self.xc + self._LS_t*p) self._LS_xt = self.projection(self.xc + self._LS_t*p)
self._LS_ft = self.evalFunction(self._LS_xt, return_g=False, return_H=False)[0] self._LS_ft = self.evalFunction(self._LS_xt, return_g=False, return_H=False)
self._LS_descent = np.inner(self.g, self._LS_xt - self.xc) # this takes into account multiplying by t, but is important for projection. self._LS_descent = np.inner(self.g, self._LS_xt - self.xc) # this takes into account multiplying by t, but is important for projection.
if self.stoppingCriteria(inLS=True): break if self.stoppingCriteria(inLS=True): break
self._iterLS += 1 self._iterLS += 1
self._LS_t = self.LSshorten*self._LS_t self._LS_t = self.LSshorten*self._LS_t
if self.debug: if self.debugLS:
if self._iterLS == 1: self.printInit(inLS=True) if self._iterLS == 1: self.printInit(inLS=True)
self.printIter(inLS=True) self.printIter(inLS=True)
if self.debug and self._iterLS > 0: self.printDone(inLS=True) if self.debugLS and self._iterLS > 0: self.printDone(inLS=True)
return self._LS_xt, self._iterLS < self.maxIterLS return self._LS_xt, self._iterLS < self.maxIterLS
@@ -460,18 +472,19 @@ class Minimize(object):
If you have things that also need to run at the end of every iteration, you can create a method:: If you have things that also need to run at the end of every iteration, you can create a method::
def _doEndIteration(self, xt): def _doEndIteration*(self, xt):
pass pass
If present, _doEndIteration will be called at the start of the default doEndIteration call. Where the * can be any string. If present, _doEndIteration* will be called at the start of the default doEndIteration call.
You may also completely overwrite this function. You may also completely overwrite this function.
:param numpy.ndarray xt: tested new iterate that ensures a descent direction. :param numpy.ndarray xt: tested new iterate that ensures a descent direction.
:rtype: None :rtype: None
:return: None :return: None
""" """
if method in [posible for posible in dir(self) if '_doEndIteration' in posible]:
if hasattr(self,'_doEndIteration'): self._doEndIteration(xt) if self.debug: print 'doEndIteration is calling self.'+method
getattr(self,method)(xt)
# store old values # store old values
+3 -2
View File
@@ -5,6 +5,7 @@ from SimPEG.utils import mkvc, sdiag
from SimPEG import utils from SimPEG import utils
from SimPEG.mesh import TensorMesh, LogicallyOrthogonalMesh from SimPEG.mesh import TensorMesh, LogicallyOrthogonalMesh
import numpy as np import numpy as np
import scipy.sparse as sp
import unittest import unittest
import inspect import inspect
@@ -179,14 +180,14 @@ def Rosenbrock(x, return_g=True, return_H=True):
f = 100*(x[1]-x[0]**2)**2+(1-x[0])**2 f = 100*(x[1]-x[0]**2)**2+(1-x[0])**2
g = np.array([2*(200*x[0]**3-200*x[0]*x[1]+x[0]-1), 200*(x[1]-x[0]**2)]) g = np.array([2*(200*x[0]**3-200*x[0]*x[1]+x[0]-1), 200*(x[1]-x[0]**2)])
H = np.array([[-400*x[1]+1200*x[0]**2+2, -400*x[0]], [-400*x[0], 200]]) H = sp.csr_matrix(np.array([[-400*x[1]+1200*x[0]**2+2, -400*x[0]], [-400*x[0], 200]]))
out = (f,) out = (f,)
if return_g: if return_g:
out += (g,) out += (g,)
if return_H: if return_H:
out += (H,) out += (H,)
return out return out if len(out) > 1 else out[0]
def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None): def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None):
""" """