1414 print ("unable to import mpi4py" )
1515
1616import time
17-
17+ import math
1818
1919# BnBNode
2020# corners of interval [l, u]
@@ -366,15 +366,48 @@ def compute_acqf_bounds(self, l, u):
366366 #opt_sol = opt_evaluator.run(acqf_minimizer.minimizer_callback, x0)[0]
367367 #assert (np.all(opt_sol[0] >= l) and np.all(opt_sol[0] <= u)), f"acqf minimizer not within bounds"
368368 #acqf_U = opt_sol[1]
369+ if False :
370+ opt_solver = "SLSQP"
371+ opt_solver_options = {'maxiter' : 1000 }
372+ constraints = []
373+ box_bounds = [[l [i ], u [i ]] for i in range (len (l ))]
374+ acqf_callback = {'obj' : self .acqf .scalar_evaluate }
375+ if self .acqf .has_gradient :
376+ acqf_callback ['grad' ] = self .acqf .scalar_eval_g
377+
378+ opt_evaluator = Evaluator ()
379+ acqf_minimizer = minimizer_wrapper (acqf_callback , opt_solver , box_bounds , constraints , opt_solver_options )
380+
381+ x0 = [[(l [i ] + u [i ]) / 2. for i in range (len (u ))]]
382+ opt_sol = opt_evaluator .run (acqf_minimizer .minimizer_callback , x0 )[0 ]
383+ assert (np .all (opt_sol [0 ] >= l ) and np .all (opt_sol [0 ] <= u )), f"acqf minimizer not within bounds"
384+ assert opt_sol [2 ], "optimizer did not converge"
385+ acqf_U = self .acqf .evaluate (np .atleast_2d (opt_sol [0 ])).flatten ()[0 ]
386+ else :
387+ n_points = 3 ** self .gpsurrogate .ndim
388+ x_points = np .zeros ((n_points , self .gpsurrogate .ndim ))
389+ for i in range (n_points ):
390+ for j in range (self .gpsurrogate .ndim ):
391+ x_points [i , j ] = l [j ] + (u [j ] - l [j ]) / 2. * float (int (i / 3 ** j ) % 3 )
392+ #n_points = 3
393+ #x_points = np.zeros((n_points, self.gpsurrogate.ndim))
394+ ##for i in range(n_points):
395+ ## for j in range(self.gpsurrogate.ndim):
396+ ## x_points[i, j] = (l[j] + u[j]) / 2.
397+ #x_points[0, :] = l[:]
398+ #x_points[1, :] = (l[:] + u[:]) / 2.
399+ #x_points[2, :] = u[:]
400+ acqf_eval = self .acqf .evaluate (x_points )
401+ acqf_U = min (acqf_eval .flatten ())
369402
370403
371- n_points = 1
372- x_points = np .zeros ((n_points , self .gpsurrogate .ndim ))
373- for i in range (n_points ):
374- for j in range (self .gpsurrogate .ndim ):
375- x_points [i , j ] = (l [j ] + u [j ]) / 2.
376- acqf_eval = self .acqf .evaluate (x_points )
377- acqf_U = min (acqf_eval .flatten ())
404+ # n_points = 1
405+ # x_points = np.zeros((n_points, self.gpsurrogate.ndim))
406+ # for i in range(n_points):
407+ # for j in range(self.gpsurrogate.ndim):
408+ # x_points[i, j] = (l[j] + u[j]) / 2.
409+ # acqf_eval = self.acqf.evaluate(x_points)
410+ # acqf_U = min(acqf_eval.flatten())
378411 #if acqf_U < acqf_bounds[0]:
379412 # print("ERROR in bound computations U < L")
380413 # print(f"Acquisition function evaluations for node defined by bounds: {l} {u}")
@@ -388,18 +421,21 @@ def compute_acqf_bounds(self, l, u):
388421 # #acqf_U = min(acqf_eval)
389422 # else:
390423 # print("all point evaluations < L")
424+ assert acqf_bounds [0 ] <= acqf_U , "acqf_U < acqf_L"
391425
392426 return acqf_bounds [0 ], acqf_U
393427 def _prune_queue (self , queue , lub , eps ):
394428 """Keep only nodes whose lower-bound is not greater or equal least upper-bound + eps; then re-heapify."""
395429 # queue items are (L, counter, node)
396- pruned = [(L , c , n ) for (L , c , n ) in queue if L < lub + eps ]
397- heapq .heapify (pruned )
398- return pruned
430+ pruned_queue = [(L , c , n ) for (L , c , n ) in queue if L < lub + eps ]
431+ pruned_nodes = [n for (L , c , n ) in queue if L >= lub + eps ] # for now return the nodes that are pruned for analysis
432+ heapq .heapify (pruned_queue )
433+ return pruned_queue , pruned_nodes
399434 def _prune_node_list (self , node_list , lub , eps ):
400435 """Keep only nodes whose lower-bound is not greater or equal least upper-bound + eps."""
401436 pruned_node_list = [node for node in node_list if node .aq_L < lub + eps ]
402- return pruned_node_list
437+ pruned_nodes = [node for node in node_list if node .aq_L >= lub + eps ]
438+ return pruned_node_list , pruned_nodes
403439 def optimize (self ):
404440 opt = self .bnboptimize (self .gpsurrogate .xlimits [:,0 ], self .gpsurrogate .xlimits [:,1 ])
405441 lopt = opt [0 ]
@@ -454,12 +490,16 @@ def bnboptimize(self, l_init, u_init):
454490 heapq .heapify (self .queue )
455491
456492 all_bfsnodes = []
493+
494+ all_prunednodes = []
495+ total_prunedvol = 0.
457496
458497
459498 # stopping criterion should be on the total maximum number of branched nodes
460499 self .num_branches = 0
461500
462501 initial_gap = self .best_node .aq_U - self .best_node .aq_L
502+ initial_vol = math .prod (self .best_node .u - self .best_node .l )
463503
464504 max_bbs_node_size = 0
465505 max_bfs_node_size = 0
@@ -486,13 +526,9 @@ def bnboptimize(self, l_init, u_init):
486526
487527 children = bbschildren + bfschildren # join child lists
488528 if len (children ) == 0 :
489- if len (self .queue ) == 0 and len (all_bfsnodes ) == 0 :
490- if self .bbsevaluator .num_submitted_tasks () == 0 and self .bfsevaluator .num_submitted_tasks () == 0 :
491- print ("no child nodes recovered from evaluators" )
492- print ("no nodes in bfs/bbs queue lists" )
493- print ("evaluators have no tasks to be evaluated" )
494- print ("exiting" )
495- exit ()
529+ assert True , "trivial check"
530+ #if len(self.queue) == 0 and len(all_bfsnodes) == 0:
531+ # assert self.bbsevaluator.num_submitted_tasks() + self.bfsevaluator.num_submitted_tasks() > 0, "node lists empty and Evaluators have no tasks submitted"
496532 else :
497533 self .num_branches += len (children )
498534 print (f"elapsed time: { time .time () - start_time } " )
@@ -515,35 +551,42 @@ def bnboptimize(self, l_init, u_init):
515551
516552 # pre-prune
517553 children_lower_bounds = [child .aq_L for child in children ]
518- args = np .argwhere (np .array (children_lower_bounds ) < self .LUB + self .epsilon_prune ).flatten ()
519- print ( f" { len ( args ) } children to be appended to bbs/bfs lists" )
520- children = [ children [ arg ] for arg in args ]
554+ # args = np.argwhere(np.array(children_lower_bounds) < self.LUB + self.epsilon_prune).flatten()
555+ # children = [children[arg] for arg in args]
556+ #print(f"{len( children)} children to be appended to bbs/bfs lists")
521557
522558 # now move pruned children to data structs for (potential) future evaluation
523559 children_lower_bounds = [child .aq_L for child in children ]
524560 # sort the children in order of increasing acqf lower-bounds
525561 args = np .argsort (children_lower_bounds )
526562 children = [children [arg ] for arg in args ]
563+ # sort children into bbs and bfs lists
527564 for child in children :
528- if len (self .queue ) < 100 * self .num_bbs_workers :
565+ if len (self .queue ) < 10 * self .num_bbs_workers :
529566 heapq .heappush (self .queue , (child .aq_L , next (self ._ctr ), child ))
530567 else :
531568 all_bfsnodes .append (child )
532569 max_bbs_node_size = max (max_bbs_node_size , len (self .queue ))
533570 max_bfs_node_size = max (max_bfs_node_size , len (all_bfsnodes ))
534571
535572 # reprune
536- self .queue = self ._prune_queue (self .queue , self .LUB , self .epsilon_prune )
537- all_bfsnodes = self ._prune_node_list (all_bfsnodes , self .LUB , self .epsilon_prune )
538-
573+ self .queue , pruned_bbsnodes = self ._prune_queue (self .queue , self .LUB , self .epsilon_prune )
574+ all_bfsnodes , pruned_bfsnodes = self ._prune_node_list (all_bfsnodes , self .LUB , self .epsilon_prune )
575+ pruned_nodes = pruned_bbsnodes + pruned_bfsnodes
576+ for node in pruned_nodes :
577+ all_prunednodes .append (node )
578+ total_prunedvol += math .prod (node .u - node .l )
579+
539580
540581 # BnB opt progress report
541582 gap = self .best_node .aq_U - self .best_node .aq_L
542583 print (f"\n --- Total number branches { self .num_branches } ---" )
543- print (f"Best node bounds : l={ self .best_node .l } , u={ self .best_node .u } " )
584+ print (f"Corners of best node region : l={ self .best_node .l } , u={ self .best_node .u } " )
544585 print (f"Node acquisition bounds: L={ self .best_node .aq_L } , U={ self .best_node .aq_U } " )
545- print (f"Current best feasible value (LUB): { self .LUB } " )
546586 print (f"gap = { gap } " )
587+ #print(f"Current best feasible value (LUB): {self.LUB}")
588+ print (f"total pruned vol: { total_prunedvol } " )
589+ print (f"domain vol: { initial_vol } " )
547590 print (f"size of bbs queue = { len (self .queue )} " )
548591 print (f"size of bfs node list = { len (all_bfsnodes )} " )
549592 print (f"number of submitted jobs (bbs): { self .bbsevaluator .num_submitted_tasks ()} " )
@@ -565,9 +608,11 @@ def bnboptimize(self, l_init, u_init):
565608 #if self.bbsevaluator.num_submitted_tasks() + self.bfsevaluator.num_submitted_tasks() > 10 * (self.num_bbs_workers + self.num_bfs_workers):
566609 # collect nodes to be branched on in list structure
567610 # only submit additional tasks if there aren't too many in the Evaluators queue
568- if self .bbsevaluator .num_submitted_tasks () < 10 * self .num_bbs_workers :
611+ num_bbs_tasks_to_submit = 10 * self .num_bbs_workers - self .bbsevaluator .num_submitted_tasks ()
612+ #num_bbs_tasks_to_submit = len(self.queue)
613+ if num_bbs_tasks_to_submit > 0 :
569614 bbsnodes = []
570- for i in range (self . nodes_per_batch ):
615+ for i in range (num_bbs_tasks_to_submit ):
571616 if (not self .queue ):
572617 break # no more nodes available to send to evaluator for branching/bound computations
573618 _ , _ , node = heapq .heappop (self .queue )
@@ -580,9 +625,11 @@ def bnboptimize(self, l_init, u_init):
580625 self .bbsevaluator .submit_tasks (brancher .callback , bbsnodes )
581626
582627 # only submit additional tasks if there aren't too many in the Evaluators queue
583- if self .bfsevaluator .num_submitted_tasks () < 10 * self .num_bfs_workers :
628+ num_bfs_tasks_to_submit = 10 * self .num_bfs_workers - self .bfsevaluator .num_submitted_tasks ()
629+ #num_bfs_tasks_to_submit = len(all_bfsnodes)
630+ if num_bfs_tasks_to_submit > 0 :
584631 bfsnodes = []
585- for i in range (self . nodes_per_batch ):
632+ for i in range (num_bfs_tasks_to_submit ):
586633 if len (all_bfsnodes ) == 0 :
587634 break # no more nodes available to send to evaluator for branching/bound computations
588635 node = all_bfsnodes .pop (0 )
@@ -591,6 +638,10 @@ def bnboptimize(self, l_init, u_init):
591638 if len (bfsnodes ) > 0 :
592639 self .bfsevaluator .submit_tasks (brancher .callback , bfsnodes )
593640
641+ # TODO: sync step and prune
642+ # get final data
643+
644+
594645 print ("\n === Optimization Finished ===" )
595646 print (f"Total number of branches: { self .num_branches } " )
596647 print (f"Max BBS node list size: { max_bbs_node_size } " )
@@ -803,26 +854,40 @@ def compute_acqf_bounds(self, l, u):
803854 var = np .array ([var_U , var_L ])
804855 acqf_bounds = self .acqf .evaluate_meansig2 (mu , var )
805856
806- #opt_solver = "SLSQP"
807- #opt_solver_options = {'maxiter' : 200}
808- #constraints = []
809- #box_bounds = [[l[i], u[i]] for i in range(len(l))]
810- #acqf_callback = {'obj' : self.acqf.scalar_evaluate}
811- #if self.acqf.has_gradient:
812- # acqf_callback['grad'] = self.acqf.scalar_eval_g
813- #
814- #opt_evaluator = Evaluator()
815- #acqf_minimizer = minimizer_wrapper(acqf_callback, opt_solver, box_bounds, constraints, opt_solver_options)
816-
817- #x0 = [[(l[i] + u[i]) / 2. for i in range(len(u))]]
818- #opt_sol = opt_evaluator.run(acqf_minimizer.minimizer_callback, x0)[0]
819- #assert (np.all(opt_sol[0] >= l) and np.all(opt_sol[0] <= u)), f"acqf minimizer not within bounds"
820- #acqf_U = opt_sol[1]
857+ if False :
858+ opt_solver = "SLSQP"
859+ opt_solver_options = {'maxiter' : 1000 }
860+ constraints = []
861+ box_bounds = [[l [i ], u [i ]] for i in range (len (l ))]
862+ acqf_callback = {'obj' : self .acqf .scalar_evaluate }
863+ if self .acqf .has_gradient :
864+ acqf_callback ['grad' ] = self .acqf .scalar_eval_g
865+
866+ opt_evaluator = Evaluator ()
867+ acqf_minimizer = minimizer_wrapper (acqf_callback , opt_solver , box_bounds , constraints , opt_solver_options )
868+
869+ x0 = [[(l [i ] + u [i ]) / 2. for i in range (len (u ))]]
870+ opt_sol = opt_evaluator .run (acqf_minimizer .minimizer_callback , x0 )[0 ]
871+ assert (np .all (opt_sol [0 ] >= l ) and np .all (opt_sol [0 ] <= u )), f"acqf minimizer not within bounds"
872+ assert opt_sol [2 ], "optimizer did not converge"
873+ acqf_U = self .acqf .evaluate (np .atleast_2d (opt_sol [0 ])).flatten ()[0 ]
874+ else :
875+ n_points = 3 ** self .gpsurrogate .ndim
876+ x_points = np .zeros ((n_points , self .gpsurrogate .ndim ))
877+ for i in range (n_points ):
878+ for j in range (self .gpsurrogate .ndim ):
879+ x_points [i , j ] = l [j ] + (u [j ] - l [j ]) / 2. * float (int (i / 3 ** j ) % 3 )
880+ #x_points[i, j] = (l[j] + u[j]) / 2.
881+ #x_points[0, :] = l[:]
882+ #x_points[1, :] = (l[:] + u[:]) / 2.
883+ #x_points[2, :] = u[:]
884+ acqf_eval = self .acqf .evaluate (x_points )
885+ acqf_U = min (acqf_eval .flatten ())
821886 #n_points = 3 ** self.gpsurrogate.ndim
822887 #x_points = np.zeros((n_points, self.gpsurrogate.ndim))
823888 #for i in range(n_points):
824889 # for j in range(self.gpsurrogate.ndim):
825- # x_points[i, j] = l[j] + ((u[j] - l[j])/ 2.) * np.floor(i / (3**j)).astype(int) % 3
890+ # x_points[i, j] = l[j] + ((u[j] - l[j]) / 2.) * np.floor(i / (3**j)).astype(int) % 3
826891 #n_points = 1
827892 #x_points = np.zeros((n_points, self.gpsurrogate.ndim))
828893 #for j in range(self.gpsurrogate.ndim):
@@ -832,13 +897,13 @@ def compute_acqf_bounds(self, l, u):
832897 #for i in range(n_points):
833898 # for j in range(self.gpsurrogate.ndim):
834899 # x_points[i, j] = l[j] + (u[j] - l[j]) * float((i + j) % self.gpsurrogate.ndim) / float(self.gpsurrogate.ndim)
835- n_points = 1
836- x_points = np .zeros ((n_points , self .gpsurrogate .ndim ))
837- for i in range (n_points ):
838- for j in range (self .gpsurrogate .ndim ):
839- x_points [i , j ] = (l [j ] + u [j ]) / 2.
840- acqf_eval = self .acqf .evaluate (x_points )
841- acqf_U = min (acqf_eval .flatten ())
900+ # n_points = 1
901+ # x_points = np.zeros((n_points, self.gpsurrogate.ndim))
902+ # for i in range(n_points):
903+ # for j in range(self.gpsurrogate.ndim):
904+ # x_points[i, j] = (l[j] + u[j]) / 2.
905+ # acqf_eval = self.acqf.evaluate(x_points)
906+ # acqf_U = min(acqf_eval.flatten())
842907 #if acqf_bounds[0] > acqf_U:
843908 # print("ERROR in bound computations U < L")
844909 # print(f"Acquisition function evaluations for node defined by bounds: {l} {u}")
@@ -865,7 +930,7 @@ def compute_acqf_bounds(self, l, u):
865930 #opt_output = opt_evaluator.run(acqf_minimizer.minimizer_callback, x0_pts)[0]
866931 #assert opt_output[2], f"local optimizer failed"
867932
868-
933+ assert acqf_bounds [ 0 ] <= acqf_U , "acqf_U < acqf_L"
869934 return acqf_bounds [0 ], acqf_U
870935 def callback (self , nodes ):
871936 output = []
0 commit comments