1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22   
 23   
 24  """Bases classes for the minfx optimisation library (U{https://sourceforge.net/projects/minfx}). 
 25   
 26  This module contains the following base classes: 
 27      - Min:                The base class containing the main iterative minimisation loop and a few other base class functions. 
 28      - Line_search:        The base class containing the generic line search functions. 
 29      - Trust_region:       The base class containing the generic trust-region functions. 
 30      - Conjugate_gradient: The base class containing the generic conjugate gradient functions. 
 31  """ 
 32   
 33   
 34   
 35   
 36  from numpy import dot, sqrt 
 37  from numpy.linalg import inv, LinAlgError 
 38  from re import match 
 39  import sys 
 40   
 41   
 42   
 43   
 44   
 45  from minfx.line_search.backtrack import backtrack 
 46  from minfx.line_search.nocedal_wright_interpol import nocedal_wright_interpol 
 47  from minfx.line_search.nocedal_wright_wolfe import nocedal_wright_wolfe 
 48  from minfx.line_search.more_thuente import more_thuente 
 49   
 50   
 51   
 52   
 53   
 54  from minfx.hessian_mods.cholesky_mod import cholesky_mod 
 55  from minfx.hessian_mods.eigenvalue import eigenvalue 
 56  from minfx.hessian_mods.gmw81 import gmw 
 57  from minfx.hessian_mods.gmw81_old import gmw_old 
 58  from minfx.hessian_mods.se99 import se99 
 59   
 60   
 61 -def print_iter(k=None, xk=None, fk=None, print_prefix=''): 
  62      """Format and print out the iteration information. 
 63       
 64      @keyword k:             The iteration number. 
 65      @type k:                int 
 66      @keyword xk:            The parameter array. 
 67      @type xk:               numpy rank-1 array 
 68      @keyword fk:            The function value. 
 69      @type fk:               float 
 70      @keyword print_prefix:  The string to add to the start of the print out. 
 71      @type print_prefix:     str 
 72      """ 
 73   
 74       
 75      params = "[" 
 76      for i in range(len(xk)): 
 77           
 78          if i > 0: 
 79              params += ', ' 
 80   
 81           
 82          params += "%15.10g" % xk[i] 
 83      params += ']' 
 84   
 85       
 86      if fk != None: 
 87          print("%sk: %-8i xk: %s    fk: %-20s" % (print_prefix, k, params, fk)) 
 88      else: 
 89          print("%sk: %-8i xk: %s" % (print_prefix, k, params)) 
  90   
 91   
 92   
 93   
 94   
 97          """Base class containing the main minimisation iterative loop algorithm. 
 98   
 99          The algorithm is defined in the minimise function.  Also supplied are generic setup, convergence tests, and update functions. 
100          """ 
 101   
102   
104          """Default base class function for both function and gradient convergence tests. 
105   
106          Test if the minimum function tolerance between fk and fk+1 has been reached as well as if the minimum gradient tolerance has been reached. 
107          """ 
108   
109           
110          if abs(fk_new - fk) <= self.func_tol: 
111              if self.print_flag >= 2: 
112                  print("\n" + self.print_prefix + "Function tolerance reached.") 
113                  print(self.print_prefix + "fk:          " + repr(fk)) 
114                  print(self.print_prefix + "fk+1:        " + repr(fk_new)) 
115                  print(self.print_prefix + "|fk+1 - fk|: " + repr(abs(fk_new - fk))) 
116                  print(self.print_prefix + "tol:         " + repr(self.func_tol)) 
117              return 1 
118   
119           
120          elif sqrt(dot(gk, gk)) <= self.grad_tol: 
121              if self.print_flag >= 2: 
122                  print("\n" + self.print_prefix + "Gradient tolerance reached.") 
123                  print(self.print_prefix + "gk+1:     " + repr(gk)) 
124                  print(self.print_prefix + "||gk+1||: " + repr(sqrt(dot(gk, gk)))) 
125                  print(self.print_prefix + "tol:      " + repr(self.grad_tol)) 
126              return 1 
 127   
128   
130          """Default base class function for the function convergence test. 
131   
132          Test if the minimum function tolerance between fk and fk+1 has been reached. 
133          """ 
134   
135           
136          if abs(fk_new - fk) <= self.func_tol: 
137              if self.print_flag >= 2: 
138                  print("\n" + self.print_prefix + "Function tolerance reached.") 
139                  print(self.print_prefix + "fk:          " + repr(fk)) 
140                  print(self.print_prefix + "fk+1:        " + repr(fk_new)) 
141                  print(self.print_prefix + "|fk+1 - fk|: " + repr(abs(fk_new - fk))) 
142                  print(self.print_prefix + "tol:         " + repr(self.func_tol)) 
143              return 1 
 144   
145   
147          """Default base class function for the gradient convergence test. 
148   
149          Test if the minimum gradient tolerance has been reached.  Minimisation will also terminate if the function value difference between fk and fk+1 is zero.  This modification is essential for the quasi-Newton techniques. 
150          """ 
151   
152           
153          if sqrt(dot(gk, gk)) <= self.grad_tol: 
154              if self.print_flag >= 2: 
155                  print("\n" + self.print_prefix + "Gradient tolerance reached.") 
156                  print(self.print_prefix + "gk+1:     " + repr(gk)) 
157                  print(self.print_prefix + "||gk+1||: " + repr(sqrt(dot(gk, gk)))) 
158                  print(self.print_prefix + "tol:      " + repr(self.grad_tol)) 
159              return 1 
160   
161           
162          elif fk_new - fk == 0.0: 
163              if self.print_flag >= 2: 
164                  print("\n" + self.print_prefix + "Function difference of zero.") 
165                  print(self.print_prefix + "fk_new - fk = 0.0") 
166              return 1 
 167   
168   
170          """Hessian type and modification options. 
171   
172          Function for sorting out the minimisation options when either the Hessian type or Hessian modification can be selected. 
173          """ 
174   
175           
176          self.hessian_type = None 
177          self.hessian_mod = None 
178   
179           
180          if not isinstance(min_options, tuple): 
181              print(self.print_prefix + "The minimisation options " + repr(min_options) + " is not a tuple.") 
182              self.init_failure = 1 
183              return 
184   
185           
186          if len(min_options) > 2: 
187              print(self.print_prefix + "A maximum of two minimisation options is allowed (the Hessian type and Hessian modification).") 
188              self.init_failure = 1 
189              return 
190   
191           
192          for opt in min_options: 
193              if self.hessian_type == None and opt != None and (match('[Bb][Ff][Gg][Ss]', opt) or match('[Nn]ewton', opt)): 
194                  self.hessian_type = opt 
195              elif self.hessian_mod == None and self.valid_hessian_mod(opt): 
196                  self.hessian_mod = opt 
197              else: 
198                  print(self.print_prefix + "The minimisation option " + repr(opt) + " from " + repr(min_options) + " is neither a valid Hessian type nor modification.") 
199                  self.init_failure = 1 
200                  return 
201   
202           
203          if self.hessian_type == None: 
204              self.hessian_type = default_type 
205   
206           
207          if match('[Bb][Ff][Gg][Ss]', self.hessian_type) and self.hessian_mod != None: 
208              print(self.print_prefix + "When using the BFGS matrix, Hessian modifications should not be used.") 
209              self.init_failure = 1 
210              return 
211   
212           
213          if match('[Nn]ewton', self.hessian_type) and self.hessian_mod == None: 
214              self.hessian_mod = None 
215               
216   
217           
218          if self.print_flag: 
219              if match('[Bb][Ff][Gg][Ss]', self.hessian_type): 
220                  print(self.print_prefix + "Hessian type:  BFGS") 
221              else: 
222                  print(self.print_prefix + "Hessian type:  Newton") 
 223   
224   
226          """Main minimisation iterative loop algorithm. 
227   
228          This algorithm is designed to be compatible with all iterative minimisers.  The outline is: 
229   
230              - k = 0 
231              - while 1: 
232                  - New parameter function 
233                  - Convergence tests 
234                  - Update function 
235                  - k = k + 1 
236          """ 
237   
238           
239          self.k = 0 
240          if self.print_flag: 
241              self.k2 = 0 
242              print("")    
243   
244           
245          while True: 
246               
247              if self.print_flag: 
248                  out = 0 
249                  if self.print_flag >= 2: 
250                      print("\n" + self.print_prefix + "Main iteration k=" + repr(self.k)) 
251                      out = 1 
252                  else: 
253                      if self.k2 == 100: 
254                          self.k2 = 0 
255                      if self.k2 == 0: 
256                          out = 1 
257                  if out == 1: 
258                      print_iter(self.k, self.xk, self.fk, print_prefix=self.print_prefix) 
259   
260               
261              try: 
262                  self.new_param_func() 
263              except LinAlgError: 
264                  message = sys.exc_info()[1] 
265                  if isinstance(message.args[0], int): 
266                      text = message.args[1] 
267                  else: 
268                      text = message.args[0] 
269                  self.warning = "LinAlgError: " + text + " (fatal minimisation error)." 
270                  break 
271              except OverflowError: 
272                  message = sys.exc_info()[1] 
273                  if isinstance(message.args[0], int): 
274                      text = message.args[1] 
275                  else: 
276                      text = message.args[0] 
277                  self.warning = "OverflowError: " + text + " (fatal minimisation error)." 
278                  break 
279              except NameError: 
280                  message = sys.exc_info()[1] 
281                  self.warning = message.args[0] + " (fatal minimisation error)." 
282                  break 
283   
284               
285              if self.warning != None: 
286                  break 
287   
288               
289              if self.k >= self.maxiter - 1: 
290                  self.warning = "Maximum number of iterations reached" 
291                  break 
292   
293               
294              if self.conv_test(self.fk_new, self.fk, self.dfk_new): 
295                  break 
296   
297               
298              try: 
299                  self.update() 
300              except OverflowError: 
301                  message = sys.exc_info()[1] 
302                  if isinstance(message.args[0], int): 
303                      text = message.args[1] 
304                  else: 
305                      text = message.args[0] 
306                  self.warning = "OverflowError: " + text + " (fatal minimisation error)." 
307                  break 
308              except NameError: 
309                  message = sys.exc_info()[1] 
310                  if isinstance(message.args[0], int): 
311                      self.warning = message.args[1] 
312                  else: 
313                      self.warning = message.args[0] 
314                  break 
315   
316               
317              self.k = self.k + 1 
318              if self.print_flag: 
319                  self.k2 = self.k2 + 1 
320   
321          if self.full_output: 
322              try: 
323                  return self.xk_new, self.fk_new, self.k+1, self.f_count, self.g_count, self.h_count, self.warning 
324              except AttributeError: 
325                  return self.xk, self.fk, self.k, self.f_count, self.g_count, self.h_count, self.warning 
326          else: 
327              try: 
328                  return self.xk_new 
329              except AttributeError: 
330                  return self.xk 
 331   
332   
334          """Default base class for selecting the convergence tests.""" 
335   
336          if self.func_tol != None and self.grad_tol != None: 
337              self.conv_test = self.double_test 
338          elif self.func_tol != None: 
339              self.conv_test = self.func_test 
340          elif self.grad_tol != None: 
341              self.conv_test = self.grad_test 
342          else: 
343              print(self.print_prefix + "Convergence tests cannot be setup because both func_tol and grad_tol are set to None.") 
344              self.init_failure = 1 
345              return 
 346   
347   
349          """Default base class update function. 
350   
351          xk+1 is shifted to xk 
352          fk+1 is shifted to fk 
353          """ 
354   
355          self.xk = self.xk_new * 1.0 
356          self.fk = self.fk_new 
  357   
358   
359   
360   
361   
362   
363   
364   
367          """Base class containing the generic line search functions.""" 
 368   
369   
371          """Function for running the backtracking line search.""" 
372   
373          self.alpha, fc = backtrack(self.func, self.args, self.xk, self.fk, self.dfk, self.pk, a_init=self.a0) 
374          self.f_count = self.f_count + fc 
 375   
376   
378          """Line search options. 
379   
380          Function for sorting out the minimisation options when the only option can be a line search. 
381          """ 
382   
383           
384          self.line_search_algor = None 
385   
386           
387          if not isinstance(min_options, tuple): 
388              print(self.print_prefix + "The minimisation options " + repr(min_options) + " is not a tuple.") 
389              self.init_failure = 1 
390              return 
391   
392           
393          if len(min_options) > 1: 
394              print(self.print_prefix + "A maximum of one minimisation options is allowed (the line search algorithm).") 
395              self.init_failure = 1 
396              return 
397   
398           
399          for opt in min_options: 
400              if self.valid_line_search(opt): 
401                  self.line_search_algor = opt 
402              else: 
403                  print(self.print_prefix + "The minimisation option " + repr(opt) + " from " + repr(min_options) + " is not a valid line search algorithm.") 
404                  self.init_failure = 1 
405                  return 
406   
407           
408          if self.line_search_algor == None: 
409              self.line_search_algor = 'Back' 
 410   
411   
413          """Function for running the More and Thuente line search.""" 
414   
415          self.alpha, fc, gc = more_thuente(self.func, self.dfunc, self.args, self.xk, self.fk, self.dfk, self.pk, a_init=self.a0, mu=self.mu, eta=self.eta, print_flag=0) 
416          self.f_count = self.f_count + fc 
417          self.g_count = self.g_count + gc 
 418   
419   
421          """Set alpha to alpha0.""" 
422   
423          self.alpha = self.a0 
 424   
425   
427          """Function for running the Nocedal and Wright interpolation based line search.""" 
428   
429          self.alpha, fc = nocedal_wright_interpol(self.func, self.args, self.xk, self.fk, self.dfk, self.pk, a_init=self.a0, mu=self.mu, print_flag=0) 
430          self.f_count = self.f_count + fc 
 431   
432   
434          """Function for running the Nocedal and Wright line search for the Wolfe conditions.""" 
435   
436          self.alpha, fc, gc = nocedal_wright_wolfe(self.func, self.dfunc, self.args, self.xk, self.fk, self.dfk, self.pk, a_init=self.a0, mu=self.mu, eta=self.eta, print_flag=0) 
437          self.f_count = self.f_count + fc 
438          self.g_count = self.g_count + gc 
 439   
440   
442          """The line search function.""" 
443   
444          if self.line_search_algor == None: 
445              self.init_failure = 1 
446              return 
447          elif match('^[Bb]ack', self.line_search_algor): 
448              if self.print_flag: 
449                  print(self.print_prefix + "Line search:  Backtracking line search.") 
450              self.line_search = self.backline 
451          elif match('^[Nn]ocedal[ _][Ww]right[ _][Ii]nt', self.line_search_algor) or match('^[Nn][Ww][Ii]', self.line_search_algor): 
452              if self.print_flag: 
453                  print(self.print_prefix + "Line search:  Nocedal and Wright interpolation based line search.") 
454              self.line_search = self.nwi 
455          elif match('^[Nn]ocedal[ _][Ww]right[ _][Ww]olfe', self.line_search_algor) or match('^[Nn][Ww][Ww]', self.line_search_algor): 
456              if self.print_flag: 
457                  print(self.print_prefix + "Line search:  Nocedal and Wright line search for the Wolfe conditions.") 
458              self.line_search = self.nww 
459          elif match('^[Mm]ore[ _][Tt]huente$', self.line_search_algor) or match('^[Mm][Tt]', self.line_search_algor): 
460              if self.print_flag: 
461                  print(self.print_prefix + "Line search:  More and Thuente line search.") 
462              self.line_search = self.mt 
463          elif match('^[Nn]o [Ll]ine [Ss]earch$', self.line_search_algor): 
464              if self.print_flag: 
465                  print(self.print_prefix + "Line search:  No line search.") 
466              self.line_search = self.no_search 
 467   
468   
470          """Test if the string 'type' is a valid line search algorithm.""" 
471   
472          if type == None: 
473              return 0 
474          elif match('^[Bb]ack', type) or match('^[Nn]ocedal[ _][Ww]right[ _][Ii]nt', type) or match('^[Nn][Ww][Ii]', type) or match('^[Nn]ocedal[ _][Ww]right[ _][Ww]olfe', type) or match('^[Nn][Ww][Ww]', type) or match('^[Mm]ore[ _][Tt]huente$', type) or match('^[Mm][Tt]', type) or match('^[Nn]o [Ll]ine [Ss]earch$', type): 
475              return 1 
476          else: 
477              return 0 
  478   
479   
480   
481   
482   
483   
484   
485   
488          """Base class containing the generic trust-region functions.""" 
 489   
490   
492          """An algorithm for trust region radius selection. 
493   
494          Page 68 from 'Numerical Optimization' by Jorge Nocedal and Stephen J. Wright, 1999, 2nd ed. 
495   
496          First calculate rho using the formula:: 
497   
498                      f(xk) - f(xk + pk) 
499              rho  =  ------------------, 
500                        mk(0) - mk(pk) 
501   
502          where the numerator is called the actual reduction and the denominator is the predicted reduction.  Secondly choose the trust region radius for the next iteration.  Finally decide if xk+1 should be shifted to xk. 
503          """ 
504   
505           
506          act_red = self.fk - self.fk_new 
507   
508           
509          pred_red = - dot(self.dfk, self.pk) - 0.5 * dot(self.pk, dot(self.d2fk, self.pk)) 
510   
511           
512          if pred_red == 0.0: 
513              self.rho = 1e99 
514          else: 
515              self.rho = act_red / pred_red 
516   
517           
518          self.norm_pk = sqrt(dot(self.pk, self.pk)) 
519   
520          if self.print_flag >= 2: 
521              print(self.print_prefix + "Actual reduction: " + repr(act_red)) 
522              print(self.print_prefix + "Predicted reduction: " + repr(pred_red)) 
523              print(self.print_prefix + "rho: " + repr(self.rho)) 
524              print(self.print_prefix + "||pk||: " + repr(self.norm_pk)) 
525   
526           
527          if self.rho < 0.25 or pred_red < 0.0: 
528              self.delta = 0.25 * self.delta 
529              if self.print_flag >= 2: 
530                  print(self.print_prefix + "Shrinking the trust region.") 
531   
532           
533          elif self.rho > 0.75 and abs(self.norm_pk - self.delta) < 1e-5: 
534              self.delta = min(2.0*self.delta, self.delta_max) 
535              if self.print_flag >= 2: 
536                  print(self.print_prefix + "Expanding the trust region.") 
537   
538           
539          else: 
540              if self.print_flag >= 2: 
541                  print(self.print_prefix + "Trust region is unaltered.") 
542   
543          if self.print_flag >= 2: 
544              print(self.print_prefix + "New trust region: " + repr(self.delta)) 
545   
546           
547          if self.rho > self.eta and pred_red > 0.0: 
548              self.shift_flag = 1 
549              if self.print_flag >= 2: 
550                  print(self.print_prefix + "rho > eta, " + repr(self.rho) + " > " + repr(self.eta)) 
551                  print(self.print_prefix + "Moving to, self.xk_new: " + repr(self.xk_new)) 
552          else: 
553              self.shift_flag = 0 
554              if self.print_flag >= 2: 
555                  print(self.print_prefix + "rho < eta, " + repr(self.rho) + " < " + repr(self.eta)) 
556                  print(self.print_prefix + "Not moving, self.xk: " + repr(self.xk)) 
  557   
558   
559   
560   
561   
562   
563   
564   
567          """Class containing the non-specific conjugate gradient code.""" 
 568   
569   
571          """The new parameter function. 
572   
573          Do a line search then calculate xk+1, fk+1, and gk+1. 
574          """ 
575   
576           
577          self.line_search() 
578   
579           
580          self.xk_new = self.xk + self.alpha * self.pk 
581          self.fk_new, self.f_count = self.func(*(self.xk_new,)+self.args), self.f_count + 1 
582          self.dfk_new, self.g_count = self.dfunc(*(self.xk_new,)+self.args), self.g_count + 1 
583   
584          if self.print_flag >= 2: 
585              print(self.print_prefix + "New param func:") 
586              print(self.print_prefix + "\ta:    " + repr(self.alpha)) 
587              print(self.print_prefix + "\tpk:   " + repr(self.pk)) 
588              print(self.print_prefix + "\txk:   " + repr(self.xk)) 
589              print(self.print_prefix + "\txk+1: " + repr(self.xk_new)) 
590              print(self.print_prefix + "\tfk:   " + repr(self.fk)) 
591              print(self.print_prefix + "\tfk+1: " + repr(self.fk_new)) 
592              print(self.print_prefix + "\tgk:   " + repr(self.dfk)) 
593              print(self.print_prefix + "\tgk+1: " + repr(self.dfk_new)) 
 594   
595   
597          """Convergence tests. 
598   
599          This is old code implementing the conjugate gradient convergence test given on page 124 of 'Numerical Optimization' by Jorge Nocedal and Stephen J. Wright, 1999, 2nd ed.  This function is currently unused. 
600          """ 
601   
602          inf_norm = 0.0 
603          for i in range(len(self.dfk)): 
604              inf_norm = max(inf_norm, abs(self.dfk[i])) 
605          if inf_norm < self.grad_tol * (1.0 + abs(self.fk)): 
606              return 1 
607          elif self.fk_new - self.fk == 0.0: 
608              self.warning = "Function tol of zero reached." 
609              return 1 
 610   
611   
613          """Function to update the function value, gradient vector, and Hessian matrix""" 
614   
615           
616          self.dot_dfk_new = dot(self.dfk_new, self.dfk_new) 
617   
618           
619          bk_new = self.calc_bk() 
620   
621           
622          if abs(dot(self.dfk_new, self.dfk)) / self.dot_dfk_new >= 0.1: 
623              if self.print_flag >= 2: 
624                  print(self.print_prefix + "Restarting.") 
625              bk_new = 0 
626   
627           
628          self.pk_new = -self.dfk_new + bk_new * self.pk 
629   
630          if self.print_flag >= 2: 
631              print(self.print_prefix + "Update func:") 
632              print(self.print_prefix + "\tpk:     " + repr(self.pk)) 
633              print(self.print_prefix + "\tpk+1:   " + repr(self.pk_new)) 
634   
635           
636          self.xk = self.xk_new * 1.0 
637          self.fk = self.fk_new 
638          self.dfk = self.dfk_new * 1.0 
639          self.pk = self.pk_new * 1.0 
640          self.dot_dfk = self.dot_dfk_new 
  641   
642   
643   
644   
645   
646   
647   
650          """Base class containing the generic line search functions.""" 
 651   
652   
654          """Function for running the Cholesky Hessian modification.""" 
655   
656          return cholesky_mod(self.dfk, self.d2fk, self.I, self.n, self.print_prefix, self.print_flag, return_matrix) 
 657   
658   
660          """Function for running the eigenvalue Hessian modification.""" 
661   
662          return eigenvalue(self.dfk, self.d2fk, self.I, self.print_prefix, self.print_flag, return_matrix) 
 663   
664   
665 -    def gmw(self, return_matrix=0): 
 666          """Function for running the Gill, Murray, and Wright modified Cholesky algorithm.""" 
667   
668          return gmw(self.dfk, self.d2fk, self.I, self.n, self.mach_acc, self.print_prefix, self.print_flag, return_matrix) 
 669   
670   
671 -    def gmw_old(self, return_matrix=0): 
 672          """Function for running the Gill, Murray, and Wright modified Cholesky algorithm.""" 
673   
674          return gmw_old(self.dfk, self.d2fk, self.I, self.n, self.mach_acc, self.print_prefix, self.print_flag, return_matrix) 
 675   
676   
677 -    def se99(self, return_matrix=0): 
 678          """Function for running the Gill, Murray, and Wright modified Cholesky algorithm.""" 
679   
680          return se99(self.dfk, self.d2fk, self.I, self.n, self.tau, self.tau_bar, self.mu, self.print_prefix, self.print_flag, return_matrix) 
 681   
682   
684          """Initialise the Hessian modification functions.""" 
685   
686           
687          if self.hessian_mod == None or match('^[Nn]o [Hh]essian [Mm]od', self.hessian_mod): 
688              if self.print_flag: 
689                  print(self.print_prefix + "Hessian modification:  Unmodified Hessian.") 
690              self.get_pk = self.unmodified_hessian 
691   
692           
693          elif match('^[Ee]igen', self.hessian_mod): 
694              if self.print_flag: 
695                  print(self.print_prefix + "Hessian modification:  Eigenvalue modification.") 
696              self.get_pk = self.eigenvalue 
697   
698           
699          elif match('^[Cc]hol', self.hessian_mod): 
700              if self.print_flag: 
701                  print(self.print_prefix + "Hessian modification:  Cholesky with added multiple of the identity.") 
702              self.get_pk = self.cholesky_mod 
703   
704           
705          elif match('^[Gg][Mm][Ww]$', self.hessian_mod): 
706              if self.print_flag: 
707                  print(self.print_prefix + "Hessian modification:  The Gill, Murray, and Wright modified Cholesky algorithm.") 
708              self.get_pk = self.gmw 
709   
710           
711          elif match('^[Gg][Mm][Ww][ -_]old', self.hessian_mod): 
712              if self.print_flag: 
713                  print(self.print_prefix + "Hessian modification:  The Gill, Murray, and Wright modified Cholesky algorithm.") 
714              self.get_pk = self.gmw_old 
715   
716           
717          elif match('^[Ss][Ee]99', self.hessian_mod): 
718              if self.print_flag: 
719                  print(self.print_prefix + "Hessian modification:  The Schnabel and Eskow 1999 algorithm.") 
720              self.tau = self.mach_acc ** (1.0/3.0) 
721              self.tau_bar = self.mach_acc ** (2.0/3.0) 
722              self.mu = 0.1 
723              self.get_pk = self.se99 
 724   
725   
727          """Calculate the pure Newton direction.""" 
728   
729          if return_matrix: 
730              return -dot(inv(self.d2fk), self.dfk), self.d2fk 
731          else: 
732              return -dot(inv(self.d2fk), self.dfk) 
 733   
734   
736          """Test if the string 'mod' is a valid Hessian modification.""" 
737   
738          if mod == None or match('^[Ee]igen', mod) or match('^[Cc]hol', mod) or match('^[Gg][Mm][Ww]$', mod) or match('^[Gg][Mm][Ww][ -_]old', mod) or match('^[Ss][Ee]99', mod) or match('^[Nn]o [Hh]essian [Mm]od', mod): 
739              return 1 
740          else: 
741              return 0 
  742