3535import numpy as np
3636import scipy .linalg as LA
3737
38- from .hessian import Hessian
3938from .trust_region import trsbox_geometry
4039from .util import sumsq
4140
@@ -119,7 +118,7 @@ def fopt(self):
119118 def xpt (self , k , abs_coordinates = False ):
120119 assert 0 <= k < self .npt (), "Invalid index %g" % k
121120 if not abs_coordinates :
122- return self .points [k , :].copy ()
121+ return np . minimum ( np . maximum ( self .sl , self . points [k , :].copy ()), self . su )
123122 else :
124123 # Apply bounds and convert back to absolute coordinates
125124 return self .xbase + np .minimum (np .maximum (self .sl , self .points [k , :]), self .su )
@@ -285,20 +284,23 @@ def factorise_geom_system(self):
285284 return
286285
287286 def solve_geom_system (self , rhs ):
287+ # To do preconditioning below, we will need to scale each column of A elementwise by the entries of some vector
288+ col_scale = lambda A , scale : (A .T * scale ).T # Uses the trick that A*x scales the 0th column of A by x[0], etc.
289+
288290 if self .factorisation_current :
289291 if self .qr_of_transpose :
290292 # Growing case: solve underdetermined system W*x=rhs with W.T = Q*R
291293 # Golub & Van Loan (3rd edn), Algorithm 5.7.2
292- Rb = LA .solve_triangular (self .R , rhs * self .left_scaling , trans = 'T' ) # R.T \ rhs
293- return np .dot (self .Q , Rb ) * self .right_scaling # minimal norm solution
294+ Rb = LA .solve_triangular (self .R , col_scale ( rhs , self .left_scaling ) , trans = 'T' ) # R.T \ rhs
295+ return col_scale ( np .dot (self .Q , Rb ), self .right_scaling ) # minimal norm solution
294296 else :
295297 # Normal case: solve overdetermined system W*x=rhs with W=Q*R
296- Qb = np .dot (self .Q .T , rhs * self .left_scaling )
297- return LA .solve_triangular (self .R , Qb ) * self .right_scaling
298+ Qb = np .dot (self .Q .T , col_scale ( rhs , self .left_scaling ) )
299+ return col_scale ( LA .solve_triangular (self .R , Qb ), self .right_scaling )
298300 else :
299301 logging .warning ("model.solve_geom_system not using factorisation" )
300302 W , left_scaling , right_scaling = self .interpolation_matrix ()
301- return LA .lstsq (W , rhs * left_scaling )[0 ] * right_scaling
303+ return col_scale ( LA .lstsq (W , col_scale ( rhs * left_scaling )) [0 ], right_scaling )
302304
303305 def interpolate_mini_models_svd (self , verbose = False , make_full_rank = False , min_sing_val = 1e-6 , sing_val_frac = 1.0 , max_jac_cond = 1e8 ,
304306 get_chg_J = False ):
@@ -314,31 +316,26 @@ def interpolate_mini_models_svd(self, verbose=False, make_full_rank=False, min_s
314316 norm_J_error = 0.0
315317 linalg_resid = 0.0
316318
317-
318319 if make_full_rank :
319320 # Remove old full-rank components of Jacobian
320321 Y = self .xpt_directions (include_kopt = False ).T
321322 Qy , Ry = LA .qr (Y , mode = 'full' ) # Qy is (n,n), Ry is (n,npt-1)=(n,p)
322323 Qhat = Qy [:, :Y .shape [1 ]]
323324 self .model_jac = np .dot (self .model_jac , np .dot (Qhat , Qhat .T ))
324- for m1 in range (self .m ()):
325- g_old = self .model_jac [m1 , :].copy ()
326- rhs = self .fval_v [fval_row_idx , m1 ] # length (npt)
327- try :
328- dg = self .solve_geom_system (rhs )
329- except LA .LinAlgError :
330- return False , None , None , None , None # flag error
331- except ValueError :
332- return False , None , None , None , None # flag error (e.g. inf or NaN encountered)
333-
334- if not np .all (np .isfinite (dg )): # another check for inf or NaN
335- return False , None , None , None , None # flag error
336325
337- self .model_jac [m1 , :] = dg [1 :]
338- self .model_const [m1 ] = dg [0 ] - np .dot (self .model_jac [m1 , :], xopt ) # shift base to xbase
339- if verbose or get_chg_J :
340- norm_J_error += sumsq (dg [1 :] - g_old )
341- linalg_resid += sumsq (np .dot (W , dg ) - rhs )
326+ rhs = self .fval_v [fval_row_idx , :] # size npt * m
327+ try :
328+ dg = self .solve_geom_system (rhs ) # size (n+1)*m
329+ except LA .LinAlgError :
330+ return False , None , None , None , None # flag error
331+ except ValueError :
332+ return False , None , None , None , None # flag error (e.g. inf or NaN encountered)
333+ J_old = self .model_jac .copy ()
334+ self .model_jac = dg [1 :,:].T
335+ self .model_const = dg [0 ,:] - np .dot (self .model_jac , xopt ) # shift base to xbase
336+ if verbose or get_chg_J :
337+ norm_J_error = np .linalg .norm (self .model_jac - J_old , ord = 'fro' )** 2
338+ linalg_resid = np .linalg .norm (W .dot (dg ) - rhs )** 2
342339
343340 if make_full_rank :
344341 try :
@@ -368,20 +365,29 @@ def build_full_model(self):
368365
369366 # Apply scaling based on convention for objective - this code uses sumsq(rvec) not 0.5*sumsq(rvec)
370367 g = 2.0 * np .dot (J .T , r ) # n-vector
371- hess = Hessian ( self . n (), vals = 2.0 * np .dot (J .T , J ) )
372- return g , hess
368+ H = 2.0 * np .dot (J .T , J )
369+ return g , H
373370
374- def lagrange_gradient (self , k , factorise_first = True ):
375- assert 0 <= k < self .npt (), "Invalid index %g" % k
371+ def lagrange_gradient (self , k = None , factorise_first = True ):
376372 if factorise_first :
377373 self .factorise_geom_system ()
378374
379- rhs = np .zeros ((self .npt (),))
380- rhs [k ] = 1.0
375+ if k is not None :
376+ assert 0 <= k < self .npt (), "Invalid index %g" % k
377+ rhs = np .zeros ((self .npt (),))
378+ rhs [k ] = 1.0
379+ else :
380+ rhs = np .eye (self .npt ()) # find all Lagrange polynomials
381381 soln = self .solve_geom_system (rhs )
382- c = soln [0 ]
383- g = soln [1 :]
384- return c , g # constant, gradient [all based at xopt]
382+
383+ if k is not None :
384+ c = soln [0 ]
385+ g = soln [1 :]
386+ return c , g # constant, gradient [all based at xopt]
387+ else :
388+ cs = soln [0 , :]
389+ gs = soln [1 :, :]
390+ return cs , gs # constant terms in each entry and gradient terms in each col [all based at xopt]
385391
386392 def poisedness_constant (self , delta , xbase = None , xbase_in_abs_coords = True ):
387393 # Calculate the poisedness constant of the current interpolation set in B(xbase, delta)
@@ -391,8 +397,13 @@ def poisedness_constant(self, delta, xbase=None, xbase_in_abs_coords=True):
391397 xbase = self .xopt ()
392398 elif xbase_in_abs_coords :
393399 xbase = xbase - self .xbase # shift to correct position
400+ # Calculate all Lagrange polynomials at once
401+ self .factorise_geom_system ()
402+ rhs = np .eye (self .npt ()) # values to interpolate
403+ soln = self .solve_geom_system (rhs )
394404 for k in range (self .npt ()):
395- c , g = self .lagrange_gradient (k , factorise_first = True )
405+ # Extract Lagrange poly from soln matrix (based at xopt)
406+ c = soln [0 ,k ]; g = soln [1 :, k ]
396407 newc = c + np .dot (g , xbase - self .xopt ()) # based at xbase
397408 # Solve problem: bounds are sl <= x <= su, and ||x-xopt|| <= delta
398409 xmax = trsbox_geometry (xbase , newc , g , self .sl , self .su , delta )
0 commit comments