@@ -69,7 +69,7 @@ def _setUp(self, double=False, expand=False):
6969class TestGenCandidates (TestBaseCandidateGeneration ):
7070 def test_gen_candidates (self , gen_candidates = gen_candidates_scipy , options = None ):
7171 options = options or {}
72- options = {** options , "maxiter" : 5 }
72+ options = {** options , "maxiter" : options . get ( "maxiter" , 5 ) }
7373 for double in (True , False ):
7474 self ._setUp (double = double )
7575 acqfs = [
@@ -125,19 +125,14 @@ def test_gen_candidates_with_none_fixed_features(
125125 ics = self .initial_conditions
126126 if isinstance (acqf , qKnowledgeGradient ):
127127 ics = ics .repeat (5 , 1 )
128- # we are getting a warning that this fails with status 1:
129- # 'STOP: TOTAL NO. of ITERATIONS REACHED LIMIT'
130- # This is expected since we have set "maxiter" low, so suppress
131- # the warning
132- with warnings .catch_warnings (record = True ):
133- candidates , _ = gen_candidates (
134- initial_conditions = ics ,
135- acquisition_function = acqf ,
136- lower_bounds = 0 ,
137- upper_bounds = 1 ,
138- fixed_features = {1 : None },
139- options = options or {},
140- )
128+ candidates , _ = gen_candidates (
129+ initial_conditions = ics ,
130+ acquisition_function = acqf ,
131+ lower_bounds = 0 ,
132+ upper_bounds = 1 ,
133+ fixed_features = {1 : None },
134+ options = options or {},
135+ )
141136 if isinstance (acqf , qKnowledgeGradient ):
142137 candidates = acqf .extract_candidates (candidates )
143138 candidates = candidates .squeeze (0 )
@@ -166,19 +161,14 @@ def test_gen_candidates_with_fixed_features(
166161 ics = self .initial_conditions
167162 if isinstance (acqf , qKnowledgeGradient ):
168163 ics = ics .repeat (5 , 1 )
169- # we are getting a warning that this fails with status 1:
170- # 'STOP: TOTAL NO. of ITERATIONS REACHED LIMIT'
171- # This is expected since we have set "maxiter" low, so suppress
172- # the warning
173- with warnings .catch_warnings (record = True ):
174- candidates , _ = gen_candidates (
175- initial_conditions = ics ,
176- acquisition_function = acqf ,
177- lower_bounds = 0 ,
178- upper_bounds = 1 ,
179- fixed_features = {1 : 0.25 },
180- options = options ,
181- )
164+ candidates , _ = gen_candidates (
165+ initial_conditions = ics ,
166+ acquisition_function = acqf ,
167+ lower_bounds = 0 ,
168+ upper_bounds = 1 ,
169+ fixed_features = {1 : 0.25 },
170+ options = options ,
171+ )
182172
183173 if isinstance (acqf , qKnowledgeGradient ):
184174 candidates = acqf .extract_candidates (candidates )
@@ -192,20 +182,16 @@ def test_gen_candidates_scipy_with_fixed_features_inequality_constraints(self):
192182 for double in (True , False ):
193183 self ._setUp (double = double , expand = True )
194184 qEI = qExpectedImprovement (self .model , best_f = self .f_best )
195- # we are getting a warning that this fails with status 9:
196- # "Iteration limit reached." This is expected since we have set
197- # "maxiter" low, so suppress the warning.
198- with warnings .catch_warnings (record = True ):
199- candidates , _ = gen_candidates_scipy (
200- initial_conditions = self .initial_conditions .reshape (1 , 1 , - 1 ),
201- acquisition_function = qEI ,
202- inequality_constraints = [
203- (torch .tensor ([0 ]), torch .tensor ([1 ]), 0 ),
204- (torch .tensor ([1 ]), torch .tensor ([- 1 ]), - 1 ),
205- ],
206- fixed_features = {1 : 0.25 },
207- options = options ,
208- )
185+ candidates , _ = gen_candidates_scipy (
186+ initial_conditions = self .initial_conditions .reshape (1 , 1 , - 1 ),
187+ acquisition_function = qEI ,
188+ inequality_constraints = [
189+ (torch .tensor ([0 ]), torch .tensor ([1 ]), 0 ),
190+ (torch .tensor ([1 ]), torch .tensor ([- 1 ]), - 1 ),
191+ ],
192+ fixed_features = {1 : 0.25 },
193+ options = options ,
194+ )
209195 # candidates is of dimension 1 x 1 x 2
210196 # so we are squeezing all the singleton dimensions
211197 candidates = candidates .squeeze ()
@@ -227,6 +213,27 @@ def test_gen_candidates_scipy_warns_opt_failure(self):
227213 )
228214 self .assertTrue (expected_warning_raised )
229215
216+ def test_gen_candidates_scipy_maxiter_behavior (self ):
217+ # Check that no warnings are raised & log produced on hitting maxiter.
218+ for method in ("SLSQP" , "L-BFGS-B" ):
219+ with warnings .catch_warnings (record = True ) as ws , self .assertLogs (
220+ "botorch" , level = "INFO"
221+ ) as logs :
222+ self .test_gen_candidates (options = {"maxiter" : 1 , "method" : method })
223+ self .assertFalse (
224+ any (issubclass (w .category , OptimizationWarning ) for w in ws )
225+ )
226+ self .assertTrue ("iteration limit" in logs .output [- 1 ])
227+ # Check that we handle maxfun as well.
228+ with warnings .catch_warnings (record = True ) as ws , self .assertLogs (
229+ "botorch" , level = "INFO"
230+ ) as logs :
231+ self .test_gen_candidates (
232+ options = {"maxiter" : 100 , "maxfun" : 1 , "method" : "L-BFGS-B" }
233+ )
234+ self .assertFalse (any (issubclass (w .category , OptimizationWarning ) for w in ws ))
235+ self .assertTrue ("function evaluation limit" in logs .output [- 1 ])
236+
230237 def test_gen_candidates_scipy_warns_opt_no_res (self ):
231238 ckwargs = {"dtype" : torch .float , "device" : self .device }
232239
0 commit comments