# Contents of /trunk/downunder/py_src/costfunctions.py

Revision 5706 - (show annotations)
Mon Jun 29 03:41:36 2015 UTC (4 years, 2 months ago) by sshaw
File MIME type: text/x-python
File size: 11637 byte(s)
all python files now force use of python3 prints and division syntax to stop sneaky errors appearing in py3 environs
 1 2 ############################################################################## 3 # 4 # Copyright (c) 2003-2015 by The University of Queensland 5 6 # 7 # Primary Business: Queensland, Australia 8 # Licensed under the Open Software License version 3.0 9 10 # 11 # Development until 2012 by Earth Systems Science Computational Center (ESSCC) 12 # Development 2012-2013 by School of Earth Sciences 13 # Development from 2014 by Centre for Geoscience Computing (GeoComp) 14 # 15 ############################################################################## 16 17 """General cost functions for minimization""" 18 19 from __future__ import print_function, division 20 21 __copyright__="""Copyright (c) 2003-2015 by The University of Queensland 22 http://www.uq.edu.au 23 Primary Business: Queensland, Australia""" 24 __license__="""Licensed under the Open Software License version 3.0 25 26 __url__= 27 28 __all__ = [ 'CostFunction', 'MeteredCostFunction' ] 29 30 import logging 31 32 class CostFunction(object): 33 """ 34 A function *f(x)* that can be minimized (base class). 35 36 Example of usage:: 37 38 cf=DerivedCostFunction() 39 # ... calculate x ... 40 args=cf.getArguments(x) # this could be potentially expensive! 41 f=cf.getValue(x, *args) 42 # ... it could be required to update x without using the gradient... 43 # ... but then ... 44 gf=cf.getGradient(x, *args) 45 46 The class distinguishes between the representation of the solution 47 x (x-type) and the gradients (r-type). 48 49 :cvar provides_inverse_Hessian_approximation: This member should be set 50 to ``True`` in subclasses that provide a valid implementation of 51 `getInverseHessianApproximation()` 52 """ 53 54 provides_inverse_Hessian_approximation=False 55 56 def __init__(self): 57 """ 58 Constructor. Initializes logger. 59 """ 60 self.logger = logging.getLogger('inv.%s'%self.__class__.__name__) 61 62 def __call__(self, x, *args): 63 """ 64 short for `getValue(x, *args)`. 65 """ 66 return self.getValue(x, *args) 67 68 def getArguments(self, x): 69 """ 70 returns precalculated values that are shared in the calculation of 71 *f(x)* and *grad f(x)* and the Hessian operator. The default 72 implementation returns an empty tuple. 73 74 .. note:: The tuple returned by this call will be passed back to this `CostFunction` in other 75 calls(eg: `getGradient`). Its contents are not specified at this level because no code, 76 other than the `CostFunction` 77 which created it, will be interacting with it. 78 That is, the implementor can put whatever information they find useful in it. 79 80 :param x: location of derivative 81 :type x: x-type 82 :rtype: ``tuple`` 83 """ 84 return () 85 86 def getDualProduct(self, x, r): 87 """ 88 returns the dual product of ``x`` and ``r`` 89 90 :type x: x-type 91 :type r: r-type 92 :rtype: ``float`` 93 """ 94 raise NotImplementedError 95 96 def getGradient(self, x, *args): 97 """ 98 returns the gradient of *f* at *x* using the precalculated values for 99 *x*. 100 101 :param x: location of derivative 102 :type x: x-type 103 :param args: pre-calculated values for ``x`` from `getArguments()` 104 :rtype: r-type 105 """ 106 raise NotImplementedError 107 108 def getInverseHessianApproximation(self, x, r, *args): 109 """ 110 returns an approximative evaluation *p* of the inverse of the Hessian 111 operator of the cost function for a given gradient *r* at a given 112 location *x*: *H(x) p = r* 113 114 :param x: location of Hessian operator to be evaluated 115 :type x: x-type 116 :param r: a given gradient 117 :type r: r-type 118 :param args: pre-calculated values for ``x`` from `getArguments()` 119 :rtype: x-type 120 :note: In general it is assumed that the Hessian *H(x)* needs to be 121 calculated in each call for a new location *x*. However, the 122 solver may suggest that this is not required, typically when 123 the iteration is close to completeness. 124 :note: Subclasses that implement this method should set the class 125 variable `provides_inverse_Hessian_approximation` to ``True`` to 126 enable the solver to call this method. 127 """ 128 raise NotImplementedError 129 130 def getValue(self, x, *args): 131 """ 132 returns the value *f(x)* using the precalculated values for *x*. 133 134 :param x: a solution approximation 135 :type x: x-type 136 :rtype: ``float`` 137 """ 138 raise NotImplementedError 139 140 def updateHessian(self): 141 """ 142 notifies the class that the Hessian operator needs to be updated. 143 This method is called by the solver class. 144 """ 145 pass 146 147 def getNorm(self, x): 148 """ 149 returns the norm of ``x`` 150 151 :type x: x-type 152 :rtype: ``float`` 153 """ 154 raise NotImplementedError 155 156 157 class MeteredCostFunction(CostFunction): 158 """ 159 This an intrumented version of the `CostFunction` class. The function 160 calls update statistical information. 161 The actual work is done by the methods with corresponding name and a 162 leading underscore. These functions need to be overwritten for a particular 163 cost function implementation. 164 """ 165 166 def __init__(self): 167 """ 168 the base constructor initializes the counters so subclasses should 169 ensure the super class constructor is called. 170 """ 171 super(MeteredCostFunction, self).__init__() 172 self.resetCounters() 173 174 def resetCounters(self): 175 """ 176 resets all statistical counters 177 """ 178 self.DualProduct_calls=0 179 self.Value_calls=0 180 self.Gradient_calls=0 181 self.Arguments_calls=0 182 self.InverseHessianApproximation_calls=0 183 self.Norm_calls=0 184 185 def getDualProduct(self, x, r): 186 """ 187 returns the dual product of ``x`` and ``r`` 188 189 :type x: x-type 190 :type r: r-type 191 :rtype: ``float`` 192 """ 193 self.DualProduct_calls+=1 194 return self._getDualProduct(x, r) 195 196 def _getDualProduct(self, x, r): 197 """ 198 returns the dual product of ``x`` and ``r`` 199 200 :type x: x-type 201 :type r: r-type 202 :rtype: ``float`` 203 :note: This is the worker for `getDualProduct()`, needs to be overwritten. 204 """ 205 raise NotImplementedError 206 207 def getNorm(self, x): 208 """ 209 returns the norm of ``x`` 210 211 :type x: x-type 212 :rtype: ``float`` 213 """ 214 self.Norm_calls+=1 215 return self._getNorm(x) 216 217 def _getNorm(self, x): 218 """ 219 returns the norm of ``x`` 220 221 :type x: x-type 222 :rtype: ``float`` 223 :note: This is the worker for `getNorm()`, needs to be overwritten. 224 """ 225 raise NotImplementedError 226 227 def getValue(self, x, *args): 228 """ 229 returns the value *f(x)* using the precalculated values for *x*. 230 231 :param x: a solution approximation 232 :type x: x-type 233 :rtype: ``float`` 234 """ 235 self.Value_calls+=1 236 return self._getValue(x, *args) 237 238 def _getValue(self, x, *args): 239 """ 240 returns the value *f(x)* using the precalculated values for *x*. 241 242 :param x: a solution approximation 243 :type x: x-type 244 :rtype: ``float`` 245 :note: This is the worker for ``getValue()``, needs to be overwritten. 246 """ 247 raise NotImplementedError 248 249 def getGradient(self, x, *args): 250 """ 251 returns the gradient of *f* at *x* using the precalculated values for 252 *x*. 253 254 :param x: location of derivative 255 :type x: x-type 256 :param args: pre-calculated values for ``x`` from `getArguments()` 257 :rtype: r-type 258 """ 259 self.Gradient_calls+=1 260 return self._getGradient(x, *args) 261 262 def _getGradient(self, x, *args): 263 """ 264 returns the gradient of *f* at *x* using the precalculated values for 265 *x*. 266 267 :param x: location of derivative 268 :type x: x-type 269 :param args: pre-calculated values for ``x`` from `getArguments()` 270 :rtype: r-type 271 :note: This is the worker for `getGradient()`, needs to be overwritten. 272 """ 273 raise NotImplementedError 274 275 276 def getArguments(self, x): 277 """ 278 returns precalculated values that are shared in the calculation of 279 *f(x)* and *grad f(x)* and the Hessian operator 280 281 .. note:: The tuple returned by this call will be passed back to this `CostFunction` in other 282 calls(eg: ``getGradient``). Its contents are not specified at this level because no code, other than the `CostFunction` 283 which created it, will be interacting with it. 284 That is, the implementor can put whatever information they find useful in it. 285 286 :param x: location of derivative 287 :type x: x-type 288 :rtype: ``tuple`` 289 """ 290 self.Arguments_calls+=1 291 return self._getArguments(x) 292 293 def _getArguments(self, x): 294 """ 295 returns precalculated values that are shared in the calculation of 296 *f(x)* and *grad f(x)* and the Hessian operator. 297 298 299 300 :param x: location of derivative 301 :type x: x-type 302 """ 303 return () 304 305 def getInverseHessianApproximation(self, x, r,*args): 306 """ 307 returns an approximative evaluation *p* of the inverse of the Hessian 308 operator of the cost function for a given gradient *r* at a given 309 location *x*: *H(x) p = r* 310 311 .. note:: In general it is assumed that the Hessian *H(x)* needs to be 312 calculate in each call for a new location *x*. However, the 313 solver may suggest that this is not required, typically when 314 the iteration is close to completeness. 315 316 :param x: location of Hessian operator to be evaluated. 317 :type x: x-type 318 :param r: a given gradient 319 :type r: r-type 320 :param args: pre-calculated values for ``x`` from `getArguments()` 321 :rtype: x-type 322 323 """ 324 self.InverseHessianApproximation_calls+=1 325 return self._getInverseHessianApproximation(x, r, *args) 326 327 def _getInverseHessianApproximation(self, x, r, *args): 328 """ 329 returns an approximative evaluation *p* of the inverse of the Hessian 330 operator of the cost function for a given gradient *r* at a given 331 location *x*: *H(x) p = r* 332 333 :param x: location of Hessian operator to be evaluated 334 :type x: x-type 335 :param r: a given gradient 336 :type r: r-type 337 :param args: pre-calculated values for ``x`` from `getArguments()` 338 :rtype: x-type 339 :note: In general it is assumed that the Hessian *H(x)* needs to be 340 calculate in each call for a new location *x*. However, the 341 solver may suggest that this is not required, typically when 342 the iteration is close to completeness. 343 :note: This is the worker for getInverseHessianApproximation()`, needs 344 to be overwritten. 345 :note: Subclasses that implement this method should set the class 346 variable `provides_inverse_Hessian_approximation` to ``True`` to 347 enable the solver to call this method. 348 """ 349 raise NotImplementedError 350

 ViewVC Help Powered by ViewVC 1.1.26