From ded738b0778fdde2881791dbef6a3ca95fab9fb5 Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Thu, 7 Nov 2013 12:27:07 -0800 Subject: [PATCH] Changes to evalFunction. and more extensible calls in startup and endIteration --- SimPEG/inverse/Inversion.py | 10 ++++---- SimPEG/inverse/Optimize.py | 49 +++++++++++++++++++++++-------------- SimPEG/tests/TestUtils.py | 5 ++-- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/SimPEG/inverse/Inversion.py b/SimPEG/inverse/Inversion.py index 9b13dacd..cff5ac0d 100644 --- a/SimPEG/inverse/Inversion.py +++ b/SimPEG/inverse/Inversion.py @@ -23,13 +23,13 @@ class Inversion(object): if not haveInserted: self.opt.printers.insert(1,{"title": "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", "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", "value": lambda M: M.parent._phi_m_last, - "width": 13, "format": "%1.2e"}) + "width": 10, "format": "%1.2e"}) def setKwargs(self, **kwargs): """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: self._beta = self.getBeta() m = self.opt.minimize(self.evalFunction,m) - if self.stoppingCriteria(): break self._iter += 1 + if self.stoppingCriteria(): break return m beta0 = 1.e2 @@ -127,7 +127,7 @@ class Inversion(object): operator = sp.linalg.LinearOperator( (m.size, m.size), H_fun, dtype=float ) out += (operator,) - return out + return out if len(out) > 1 else out[0] def dataObj(self, m, u=None): diff --git a/SimPEG/inverse/Optimize.py b/SimPEG/inverse/Optimize.py index f9345d90..7629c072 100644 --- a/SimPEG/inverse/Optimize.py +++ b/SimPEG/inverse/Optimize.py @@ -33,7 +33,9 @@ class Minimize(object): tolX = 1e-1 tolG = 1e-1 eps = 1e-5 - debug = True + + debug = False + debugLS = False def __init__(self, **kwargs): 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 = [{ "title": "#", "value": lambda M: M._iter, - "width": 10, + "width": 5, "format": "%3d" },{ "title": "f", "value": lambda M: self.f, - "width": 14, + "width": 10, "format": "%1.2e" },{ "title": "|g|", "value": lambda M: norm(M.g), - "width": 14, + "width": 10, "format": "%1.2e" },{ "title": "LS", @@ -101,17 +103,17 @@ class Minimize(object): self.printersLS = [{ "title": "#", "value": lambda M: (M._iter, M._iterLS), - "width": 10, + "width": 5, "format": "%3d.%d" },{ "title": "t", "value": lambda M: M._LS_t, - "width": 14, + "width": 10, "format": "%0.5f" },{ "title": "ft", "value": lambda M: M._LS_ft, - "width": 14, + "width": 10, "format": "%1.2e" },{ "title": "f + alp*g.T*p", @@ -143,6 +145,14 @@ class Minimize(object): (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:: @@ -192,6 +202,7 @@ class Minimize(object): while 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) + self.printIter() if self.stoppingCriteria(): break p = self.findSearchDirection() if doPub: pub.sendMessage('Minimize.searchDirection', minimize=self, p=p) @@ -204,7 +215,6 @@ class Minimize(object): if not caught: return self.xc self.doEndIteration(xt) if doPub: pub.sendMessage('Minimize.endIteration', minimize=self, xt=xt) - self.printIter() 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:: - def _startup(self, x0): + def _startup*(self, x0): 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. :param numpy.ndarray x0: initial x :rtype: 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._iterLS = 0 @@ -417,16 +429,16 @@ class Minimize(object): self._iterLS = 0 while self._iterLS < self.maxIterLS: 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. if self.stoppingCriteria(inLS=True): break self._iterLS += 1 self._LS_t = self.LSshorten*self._LS_t - if self.debug: + if self.debugLS: if self._iterLS == 1: self.printInit(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 @@ -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:: - def _doEndIteration(self, xt): + def _doEndIteration*(self, xt): 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. :param numpy.ndarray xt: tested new iterate that ensures a descent direction. :rtype: None :return: None """ - - if hasattr(self,'_doEndIteration'): self._doEndIteration(xt) + if method in [posible for posible in dir(self) if '_doEndIteration' in posible]: + if self.debug: print 'doEndIteration is calling self.'+method + getattr(self,method)(xt) # store old values diff --git a/SimPEG/tests/TestUtils.py b/SimPEG/tests/TestUtils.py index 05b31685..48ec2ae8 100644 --- a/SimPEG/tests/TestUtils.py +++ b/SimPEG/tests/TestUtils.py @@ -5,6 +5,7 @@ from SimPEG.utils import mkvc, sdiag from SimPEG import utils from SimPEG.mesh import TensorMesh, LogicallyOrthogonalMesh import numpy as np +import scipy.sparse as sp import unittest 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 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,) if return_g: out += (g,) if return_H: out += (H,) - return out + return out if len(out) > 1 else out[0] def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None): """