@@ -69,7 +69,7 @@ def _setUp(self, double=False, expand=False):
69
69
class TestGenCandidates (TestBaseCandidateGeneration ):
70
70
def test_gen_candidates (self , gen_candidates = gen_candidates_scipy , options = None ):
71
71
options = options or {}
72
- options = {** options , "maxiter" : 5 }
72
+ options = {** options , "maxiter" : options . get ( "maxiter" , 5 ) }
73
73
for double in (True , False ):
74
74
self ._setUp (double = double )
75
75
acqfs = [
@@ -125,19 +125,14 @@ def test_gen_candidates_with_none_fixed_features(
125
125
ics = self .initial_conditions
126
126
if isinstance (acqf , qKnowledgeGradient ):
127
127
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
+ )
141
136
if isinstance (acqf , qKnowledgeGradient ):
142
137
candidates = acqf .extract_candidates (candidates )
143
138
candidates = candidates .squeeze (0 )
@@ -166,19 +161,14 @@ def test_gen_candidates_with_fixed_features(
166
161
ics = self .initial_conditions
167
162
if isinstance (acqf , qKnowledgeGradient ):
168
163
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
+ )
182
172
183
173
if isinstance (acqf , qKnowledgeGradient ):
184
174
candidates = acqf .extract_candidates (candidates )
@@ -192,20 +182,16 @@ def test_gen_candidates_scipy_with_fixed_features_inequality_constraints(self):
192
182
for double in (True , False ):
193
183
self ._setUp (double = double , expand = True )
194
184
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
+ )
209
195
# candidates is of dimension 1 x 1 x 2
210
196
# so we are squeezing all the singleton dimensions
211
197
candidates = candidates .squeeze ()
@@ -227,6 +213,27 @@ def test_gen_candidates_scipy_warns_opt_failure(self):
227
213
)
228
214
self .assertTrue (expected_warning_raised )
229
215
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
+
230
237
def test_gen_candidates_scipy_warns_opt_no_res (self ):
231
238
ckwargs = {"dtype" : torch .float , "device" : self .device }
232
239
0 commit comments