@@ -299,7 +299,7 @@ def sigma2_bounds(self, kL, kU, l = None, u = None):
299299
300300
301301class BnBAlgorithm (BnBAlgorithmBase ):
302- def __init__ (self , acqf , options = {}, sync_mode = False , BOit = 0 , saveData = False ):
302+ def __init__ (self , acqf , options = {}, BOit = 0 , saveData = False ):
303303 self .acqf = acqf
304304 self .gpsurrogate = acqf .gpsurrogate
305305 super ().__init__ (x = self .gpsurrogate .training_x , y = self .gpsurrogate .training_y )
@@ -316,24 +316,30 @@ def __init__(self, acqf, options = {}, sync_mode=False, BOit=0, saveData=False):
316316 self .max_bnbtime = 12 * 60 # 12 minutes
317317 self .BOit = BOit
318318 self .saveData = saveData
319+ self .acqf_UB_opt = False
320+ self .pure_BBS = False # pure BBS search or hybrid BBS/BFS search
321+ self .sync_mode = False # synchronous or asynchronous evaluations
319322 # Set options form command
320323 self .epsilon_gap = options .get ('epsilon_gap' , self .epsilon_gap )
321324 self .epsilon_diam = options .get ('epsilon_diam' , self .epsilon_diam )
322325 self .epsilon_prune = options .get ('epsilon_prune' , self .epsilon_prune )
323326 self .max_bnbiter = options .get ('max_iter' , self .max_bnbiter )
324327 self .max_bnbtime = options .get ('max_bnbtime' , self .max_bnbtime )
325328 self .nodes_per_batch = options .get ('nodes_per_batch' , self .nodes_per_batch )
326-
327- self .sync_mode = sync_mode # synchronous or asynchronous
329+ self .acqf_UB_opt = options .get ('acqf_ub_opt' , self .acqf_UB_opt )
330+ self .pure_BBS = options .get ('pure_BBS' , self .pure_BBS )
331+ self .sync_mode = options .get ('sync_mode' , self .sync_mode )
328332
329333 if is_running_with_mpi ():
330334 num_available_workers = MPI .COMM_WORLD .Get_size () - 1
331335 if num_available_workers > 1 :
332336 # roughly evenly split workers for use in bbs and bfs evaluators
333- #num_bbs_workers = num_available_workers
334- #num_bfs_workers = 1
335- num_bbs_workers = np .ceil (num_available_workers * 3 / 4 ).astype (int )
336- num_bfs_workers = max (1 , num_available_workers - num_bbs_workers )
337+ if self .pure_BBS :
338+ num_bbs_workers = num_available_workers
339+ num_bfs_workers = 1
340+ else :
341+ num_bbs_workers = np .ceil (num_available_workers * 3 / 4 ).astype (int )
342+ num_bfs_workers = max (1 , num_available_workers - num_bbs_workers )
337343 else :
338344 # num_available_workers == 1 or num_available_workers == 0
339345 # can occur when running on one process in which root is both master and the
@@ -380,24 +386,9 @@ def compute_acqf_bounds(self, l, u):
380386 var = np .array ([var_U , var_L ])
381387 acqf_bounds = self .acqf .evaluate_meansig2 (mu , var )
382388
383- #opt_solver = "SLSQP"
384- #opt_solver_options = {'maxiter' : 200}
385- #constraints = []
386- #box_bounds = [[l[i], u[i]] for i in range(len(l))]
387- #acqf_callback = {'obj' : self.acqf.scalar_evaluate}
388- #if self.acqf.has_gradient:
389- # acqf_callback['grad'] = self.acqf.scalar_eval_g
390- #
391- #opt_evaluator = Evaluator()
392- #acqf_minimizer = minimizer_wrapper(acqf_callback, opt_solver, box_bounds, constraints, opt_solver_options)
393-
394- #x0 = [[(l[i] + u[i]) / 2. for i in range(len(u))]]
395- #opt_sol = opt_evaluator.run(acqf_minimizer.minimizer_callback, x0)[0]
396- #assert (np.all(opt_sol[0] >= l) and np.all(opt_sol[0] <= u)), f"acqf minimizer not within bounds"
397- #acqf_U = opt_sol[1]
398- if False :
399- opt_solver = "SLSQP"
400- opt_solver_options = {'maxiter' : 1000 }
389+ if self .acqf_UB_opt :
390+ opt_solver = "IPOPT"
391+ opt_solver_options = {'max_iter' : 1000 , 'tol' : 1.e-5 , 'honor_original_bounds' : 'yes' , 'print_level' : 2 }
401392 constraints = []
402393 box_bounds = [[l [i ], u [i ]] for i in range (len (l ))]
403394 acqf_callback = {'obj' : self .acqf .scalar_evaluate }
@@ -419,35 +410,9 @@ def compute_acqf_bounds(self, l, u):
419410 for i in range (n_points ):
420411 for j in range (self .gpsurrogate .ndim ):
421412 x_points [i , j ] = l [j ] + (u [j ] - l [j ]) / (s_per_dim - 1. ) * float (int (i / s_per_dim ** j ) % s_per_dim )
422- #n_points = 10
423- #sampler = qmc.LatinHypercube(len(u))
424- #x_points = sampler.random(n=n_points)
425- #qmc.scale(x_points, l, u)
426413 acqf_eval = self .acqf .evaluate (x_points )
427414 acqf_U = min (acqf_eval .flatten ())
428-
429-
430- #n_points = 1
431- #x_points = np.zeros((n_points, self.gpsurrogate.ndim))
432- #for i in range(n_points):
433- # for j in range(self.gpsurrogate.ndim):
434- # x_points[i, j] = (l[j] + u[j]) / 2.
435- #acqf_eval = self.acqf.evaluate(x_points)
436- #acqf_U = min(acqf_eval.flatten())
437- #if acqf_U < acqf_bounds[0]:
438- # print("ERROR in bound computations U < L")
439- # print(f"Acquisition function evaluations for node defined by bounds: {l} {u}")
440- # for i in range(n_points):
441- # print(f"acqf({x_points[i,:]}) = {acqf_eval[i]}")
442- # if np.any(acqf_eval >= acqf_bounds[0]):
443- # print("one point evaluation >= L")
444- # #feasible_idxs = np.argwhere(acqf_eval >= acqf_bounds[0])
445- # #acqf_eval = acqf_eval[feasible_idxs]
446- # #acqf_eval.sort()
447- # #acqf_U = min(acqf_eval)
448- # else:
449- # print("all point evaluations < L")
450- assert acqf_bounds [0 ] <= acqf_U , "acqf_U < acqf_L"
415+ assert acqf_bounds [0 ] <= acqf_U , "acqf_L > acqf_U"
451416
452417 return acqf_bounds [0 ], acqf_U
453418 def _prune_queue (self , queue , lub , eps ):
@@ -581,9 +546,6 @@ def bnboptimize(self, l_init, u_init):
581546
582547 # pre-prune
583548 children_lower_bounds = [child .aq_L for child in children ]
584- #args = np.argwhere(np.array(children_lower_bounds) < self.LUB + self.epsilon_prune).flatten()
585- #children = [children[arg] for arg in args]
586- #print(f"{len(children)} children to be appended to bbs/bfs lists")
587549
588550 # now move pruned children to data structs for (potential) future evaluation
589551 children_lower_bounds = [child .aq_L for child in children ]
@@ -597,10 +559,10 @@ def bnboptimize(self, l_init, u_init):
597559
598560 # sort children into bbs and bfs lists
599561 for child in children :
600- if True : # len(self.queue) < 10 * self.num_bbs_workers:
562+ if self . pure_BBS or len (self .queue ) < 10 * self .num_bbs_workers :
601563 heapq .heappush (self .queue , (child .aq_L , next (self ._ctr ), child ))
602564 else :
603- all_bfsnodes .append (child ) # TODO: prepend... according to number of workers
565+ all_bfsnodes .append (child ) #TODO: prepend... according to number of workers
604566 max_bbs_node_size = max (max_bbs_node_size , len (self .queue ))
605567 max_bfs_node_size = max (max_bfs_node_size , len (all_bfsnodes ))
606568
@@ -658,8 +620,10 @@ def bnboptimize(self, l_init, u_init):
658620 #if self.bbsevaluator.num_submitted_tasks() + self.bfsevaluator.num_submitted_tasks() > 10 * (self.num_bbs_workers + self.num_bfs_workers):
659621 # collect nodes to be branched on in list structure
660622 # only submit additional tasks if there aren't too many in the Evaluators queue
661- #num_bbs_tasks_to_submit = 10 * self.num_bbs_workers - self.bbsevaluator.num_submitted_tasks()
662- num_bbs_tasks_to_submit = len (self .queue )
623+ if self .sync_mode :
624+ num_bbs_tasks_to_submit = len (self .queue )
625+ else :
626+ num_bbs_tasks_to_submit = 10 * self .num_bbs_workers - self .bbsevaluator .num_submitted_tasks ()
663627 if num_bbs_tasks_to_submit > 0 :
664628 bbsnodes = []
665629 for i in range (num_bbs_tasks_to_submit ):
@@ -675,8 +639,10 @@ def bnboptimize(self, l_init, u_init):
675639 self .bbsevaluator .submit_tasks (brancher .callback , bbsnodes )
676640
677641 # only submit additional tasks if there aren't too many in the Evaluators queue
678- num_bfs_tasks_to_submit = 10 * self .num_bfs_workers - self .bfsevaluator .num_submitted_tasks ()
679- #num_bfs_tasks_to_submit = len(all_bfsnodes)
642+ if self .sync_mode :
643+ num_bfs_tasks_to_submit = len (all_bfsnodes )
644+ else :
645+ num_bfs_tasks_to_submit = 10 * self .num_bfs_workers - self .bfsevaluator .num_submitted_tasks ()
680646 if num_bfs_tasks_to_submit > 0 :
681647 bfsnodes = []
682648 for i in range (num_bfs_tasks_to_submit ):
@@ -754,13 +720,14 @@ def bnboptimize(self, l_init, u_init):
754720
755721
756722class branching_wrapper :
757- def __init__ (self , acqf , LUB = np .inf , epsilon_prune = 1.e-14 ):
723+ def __init__ (self , acqf , LUB = np .inf , epsilon_prune = 1.e-14 , acqf_UB_opt = False ):
758724 self .LUB = LUB # least upper bound
759725 self .epsilon_prune = epsilon_prune
760726 self .acqf = acqf
761727 self .gpsurrogate = acqf .gpsurrogate
762728 self .x = self .gpsurrogate .training_x
763729 self .y = self .gpsurrogate .training_y
730+ self .acqf_UB_opt = acqf_UB_opt
764731 if not (isinstance (self .acqf , LCBacquisition ) or isinstance (self .acqf , EIacquisition )):
765732 raise NotImplementedError ("Unrecognized acquisition function type" )
766733 self .sync_from_smt ()
@@ -952,9 +919,9 @@ def compute_acqf_bounds(self, l, u):
952919 var = np .array ([var_U , var_L ])
953920 acqf_bounds = self .acqf .evaluate_meansig2 (mu , var )
954921
955- if False :
956- opt_solver = "SLSQP "
957- opt_solver_options = {'maxiter ' : 1000 }
922+ if self . acqf_UB_opt :
923+ opt_solver = "IPOPT "
924+ opt_solver_options = {'max_iter ' : 1000 , 'tol' : 1.e-5 , 'honor_original_bounds' : 'yes' , 'print_level' : 2 }
958925 constraints = []
959926 box_bounds = [[l [i ], u [i ]] for i in range (len (l ))]
960927 acqf_callback = {'obj' : self .acqf .scalar_evaluate }
0 commit comments