test__shgo.py 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. import logging
  2. import sys
  3. import warnings
  4. import numpy as np
  5. import time
  6. from multiprocessing import Pool
  7. from numpy.testing import assert_allclose, IS_PYPY
  8. import pytest
  9. from pytest import raises as assert_raises, warns
  10. from scipy.optimize import (shgo, Bounds, minimize_scalar, minimize, rosen,
  11. rosen_der, rosen_hess, NonlinearConstraint, OptimizeWarning)
  12. from scipy.optimize._constraints import new_constraint_to_old
  13. from scipy.optimize._shgo import SHGO
  14. from scipy.optimize.tests.test_minimize_constrained import MaratosTestArgs
  15. class StructTestFunction:
  16. def __init__(self, bounds, expected_x, expected_fun=None,
  17. expected_xl=None, expected_funl=None):
  18. self.bounds = bounds
  19. self.expected_x = expected_x
  20. self.expected_fun = expected_fun
  21. self.expected_xl = expected_xl
  22. self.expected_funl = expected_funl
  23. def wrap_constraints(g):
  24. cons = []
  25. if g is not None:
  26. if not isinstance(g, tuple | list):
  27. g = (g,)
  28. else:
  29. pass
  30. for g in g:
  31. cons.append({'type': 'ineq',
  32. 'fun': g})
  33. cons = tuple(cons)
  34. else:
  35. cons = None
  36. return cons
  37. class StructTest1(StructTestFunction):
  38. def f(self, x):
  39. return x[0] ** 2 + x[1] ** 2
  40. def g(x):
  41. return -(np.sum(x, axis=0) - 6.0)
  42. cons = wrap_constraints(g)
  43. test1_1 = StructTest1(bounds=[(-1, 6), (-1, 6)],
  44. expected_x=[0, 0])
  45. test1_2 = StructTest1(bounds=[(0, 1), (0, 1)],
  46. expected_x=[0, 0])
  47. test1_3 = StructTest1(bounds=[(None, None), (None, None)],
  48. expected_x=[0, 0])
  49. class StructTest2(StructTestFunction):
  50. """
  51. Scalar function with several minima to test all minimiser retrievals
  52. """
  53. def f(self, x):
  54. return (x - 30) * np.sin(x)
  55. def g(x):
  56. return 58 - np.sum(x, axis=0)
  57. cons = wrap_constraints(g)
  58. test2_1 = StructTest2(bounds=[(0, 60)],
  59. expected_x=[1.53567906],
  60. expected_fun=-28.44677132,
  61. # Important: test that funl return is in the correct
  62. # order
  63. expected_xl=np.array([[1.53567906],
  64. [55.01782167],
  65. [7.80894889],
  66. [48.74797493],
  67. [14.07445705],
  68. [42.4913859],
  69. [20.31743841],
  70. [36.28607535],
  71. [26.43039605],
  72. [30.76371366]]),
  73. expected_funl=np.array([-28.44677132, -24.99785984,
  74. -22.16855376, -18.72136195,
  75. -15.89423937, -12.45154942,
  76. -9.63133158, -6.20801301,
  77. -3.43727232, -0.46353338])
  78. )
  79. test2_2 = StructTest2(bounds=[(0, 4.5)],
  80. expected_x=[1.53567906],
  81. expected_fun=[-28.44677132],
  82. expected_xl=np.array([[1.53567906]]),
  83. expected_funl=np.array([-28.44677132])
  84. )
  85. class StructTest3(StructTestFunction):
  86. """
  87. Hock and Schittkowski 18 problem (HS18). Hoch and Schittkowski (1981)
  88. http://www.ai7.uni-bayreuth.de/test_problem_coll.pdf
  89. Minimize: f = 0.01 * (x_1)**2 + (x_2)**2
  90. Subject to: x_1 * x_2 - 25.0 >= 0,
  91. (x_1)**2 + (x_2)**2 - 25.0 >= 0,
  92. 2 <= x_1 <= 50,
  93. 0 <= x_2 <= 50.
  94. Approx. Answer:
  95. f([(250)**0.5 , (2.5)**0.5]) = 5.0
  96. """
  97. # amended to test vectorisation of constraints
  98. def f(self, x):
  99. return 0.01 * (x[0]) ** 2 + (x[1]) ** 2
  100. def g1(x):
  101. return x[0] * x[1] - 25.0
  102. def g2(x):
  103. return x[0] ** 2 + x[1] ** 2 - 25.0
  104. # g = (g1, g2)
  105. # cons = wrap_constraints(g)
  106. def g(x):
  107. return x[0] * x[1] - 25.0, x[0] ** 2 + x[1] ** 2 - 25.0
  108. # this checks that shgo can be sent new-style constraints
  109. __nlc = NonlinearConstraint(g, 0, np.inf)
  110. cons = (__nlc,)
  111. test3_1 = StructTest3(bounds=[(2, 50), (0, 50)],
  112. expected_x=[250 ** 0.5, 2.5 ** 0.5],
  113. expected_fun=5.0
  114. )
  115. class StructTest4(StructTestFunction):
  116. """
  117. Hock and Schittkowski 11 problem (HS11). Hoch and Schittkowski (1981)
  118. NOTE: Did not find in original reference to HS collection, refer to
  119. Henderson (2015) problem 7 instead. 02.03.2016
  120. """
  121. def f(self, x):
  122. return ((x[0] - 10) ** 2 + 5 * (x[1] - 12) ** 2 + x[2] ** 4
  123. + 3 * (x[3] - 11) ** 2 + 10 * x[4] ** 6 + 7 * x[5] ** 2 + x[
  124. 6] ** 4
  125. - 4 * x[5] * x[6] - 10 * x[5] - 8 * x[6]
  126. )
  127. def g1(x):
  128. return -(2 * x[0] ** 2 + 3 * x[1] ** 4 + x[2] + 4 * x[3] ** 2
  129. + 5 * x[4] - 127)
  130. def g2(x):
  131. return -(7 * x[0] + 3 * x[1] + 10 * x[2] ** 2 + x[3] - x[4] - 282.0)
  132. def g3(x):
  133. return -(23 * x[0] + x[1] ** 2 + 6 * x[5] ** 2 - 8 * x[6] - 196)
  134. def g4(x):
  135. return -(4 * x[0] ** 2 + x[1] ** 2 - 3 * x[0] * x[1] + 2 * x[2] ** 2
  136. + 5 * x[5] - 11 * x[6])
  137. g = (g1, g2, g3, g4)
  138. cons = wrap_constraints(g)
  139. test4_1 = StructTest4(bounds=[(-10, 10), ] * 7,
  140. expected_x=[2.330499, 1.951372, -0.4775414,
  141. 4.365726, -0.6244870, 1.038131, 1.594227],
  142. expected_fun=680.6300573
  143. )
  144. class StructTest5(StructTestFunction):
  145. def f(self, x):
  146. return (
  147. -(x[1] + 47.0)*np.sin(np.sqrt(abs(x[0]/2.0 + (x[1] + 47.0))))
  148. - x[0]*np.sin(np.sqrt(abs(x[0] - (x[1] + 47.0))))
  149. )
  150. g = None
  151. cons = wrap_constraints(g)
  152. test5_1 = StructTest5(bounds=[(-512, 512), (-512, 512)],
  153. expected_fun=[-959.64066272085051],
  154. expected_x=[512., 404.23180542])
  155. class StructTestLJ(StructTestFunction):
  156. """
  157. LennardJones objective function. Used to test symmetry constraints
  158. settings.
  159. """
  160. def f(self, x, *args):
  161. print(f'x = {x}')
  162. self.N = args[0]
  163. k = int(self.N / 3)
  164. s = 0.0
  165. for i in range(k - 1):
  166. for j in range(i + 1, k):
  167. a = 3 * i
  168. b = 3 * j
  169. xd = x[a] - x[b]
  170. yd = x[a + 1] - x[b + 1]
  171. zd = x[a + 2] - x[b + 2]
  172. ed = xd * xd + yd * yd + zd * zd
  173. ud = ed * ed * ed
  174. if ed > 0.0:
  175. s += (1.0 / ud - 2.0) / ud
  176. return s
  177. g = None
  178. cons = wrap_constraints(g)
  179. N = 6
  180. boundsLJ = list(zip([-4.0] * 6, [4.0] * 6))
  181. testLJ = StructTestLJ(bounds=boundsLJ,
  182. expected_fun=[-1.0],
  183. expected_x=None,
  184. # expected_x=[-2.71247337e-08,
  185. # -2.71247337e-08,
  186. # -2.50000222e+00,
  187. # -2.71247337e-08,
  188. # -2.71247337e-08,
  189. # -1.50000222e+00]
  190. )
  191. class StructTestS(StructTestFunction):
  192. def f(self, x):
  193. return ((x[0] - 0.5) ** 2 + (x[1] - 0.5) ** 2
  194. + (x[2] - 0.5) ** 2 + (x[3] - 0.5) ** 2)
  195. g = None
  196. cons = wrap_constraints(g)
  197. test_s = StructTestS(bounds=[(0, 2.0), ] * 4,
  198. expected_fun=0.0,
  199. expected_x=np.ones(4) - 0.5
  200. )
  201. class StructTestTable(StructTestFunction):
  202. def f(self, x):
  203. if x[0] == 3.0 and x[1] == 3.0:
  204. return 50
  205. else:
  206. return 100
  207. g = None
  208. cons = wrap_constraints(g)
  209. test_table = StructTestTable(bounds=[(-10, 10), (-10, 10)],
  210. expected_fun=[50],
  211. expected_x=[3.0, 3.0])
  212. class StructTestInfeasible(StructTestFunction):
  213. """
  214. Test function with no feasible domain.
  215. """
  216. def f(self, x, *args):
  217. return x[0] ** 2 + x[1] ** 2
  218. def g1(x):
  219. return x[0] + x[1] - 1
  220. def g2(x):
  221. return -(x[0] + x[1] - 1)
  222. def g3(x):
  223. return -x[0] + x[1] - 1
  224. def g4(x):
  225. return -(-x[0] + x[1] - 1)
  226. g = (g1, g2, g3, g4)
  227. cons = wrap_constraints(g)
  228. test_infeasible = StructTestInfeasible(bounds=[(2, 50), (-1, 1)],
  229. expected_fun=None,
  230. expected_x=None
  231. )
  232. @pytest.mark.skip("Not a test")
  233. def run_test(test, args=(), test_atol=1e-5, n=100, iters=None,
  234. callback=None, minimizer_kwargs=None, options=None,
  235. sampling_method='sobol', workers=1):
  236. res = shgo(test.f, test.bounds, args=args, constraints=test.cons,
  237. n=n, iters=iters, callback=callback,
  238. minimizer_kwargs=minimizer_kwargs, options=options,
  239. sampling_method=sampling_method, workers=workers)
  240. print(f'res = {res}')
  241. logging.info(f'res = {res}')
  242. if test.expected_x is not None:
  243. np.testing.assert_allclose(res.x, test.expected_x,
  244. rtol=test_atol,
  245. atol=test_atol)
  246. # (Optional tests)
  247. if test.expected_fun is not None:
  248. np.testing.assert_allclose(res.fun,
  249. test.expected_fun,
  250. atol=test_atol)
  251. if test.expected_xl is not None:
  252. np.testing.assert_allclose(res.xl,
  253. test.expected_xl,
  254. atol=test_atol)
  255. if test.expected_funl is not None:
  256. np.testing.assert_allclose(res.funl,
  257. test.expected_funl,
  258. atol=test_atol)
  259. return
  260. # Base test functions:
  261. class TestShgoSobolTestFunctions:
  262. """
  263. Global optimisation tests with Sobol sampling:
  264. """
  265. # Sobol algorithm
  266. def test_f1_1_sobol(self):
  267. """Multivariate test function 1:
  268. x[0]**2 + x[1]**2 with bounds=[(-1, 6), (-1, 6)]"""
  269. run_test(test1_1)
  270. def test_f1_2_sobol(self):
  271. """Multivariate test function 1:
  272. x[0]**2 + x[1]**2 with bounds=[(0, 1), (0, 1)]"""
  273. run_test(test1_2)
  274. def test_f1_3_sobol(self):
  275. """Multivariate test function 1:
  276. x[0]**2 + x[1]**2 with bounds=[(None, None),(None, None)]"""
  277. options = {'disp': True}
  278. run_test(test1_3, options=options)
  279. def test_f2_1_sobol(self):
  280. """Univariate test function on
  281. f(x) = (x - 30) * sin(x) with bounds=[(0, 60)]"""
  282. run_test(test2_1)
  283. def test_f2_2_sobol(self):
  284. """Univariate test function on
  285. f(x) = (x - 30) * sin(x) bounds=[(0, 4.5)]"""
  286. run_test(test2_2)
  287. def test_f3_sobol(self):
  288. """NLP: Hock and Schittkowski problem 18"""
  289. run_test(test3_1)
  290. @pytest.mark.slow
  291. def test_f4_sobol(self):
  292. """NLP: (High dimensional) Hock and Schittkowski 11 problem (HS11)"""
  293. options = {'infty_constraints': False}
  294. # run_test(test4_1, n=990, options=options)
  295. run_test(test4_1, n=990 * 2, options=options)
  296. def test_f5_1_sobol(self):
  297. """NLP: Eggholder, multimodal"""
  298. # run_test(test5_1, n=30)
  299. run_test(test5_1, n=60)
  300. def test_f5_2_sobol(self):
  301. """NLP: Eggholder, multimodal"""
  302. # run_test(test5_1, n=60, iters=5)
  303. run_test(test5_1, n=60, iters=5)
  304. # def test_t911(self):
  305. # """1D tabletop function"""
  306. # run_test(test11_1)
  307. class TestShgoSimplicialTestFunctions:
  308. """
  309. Global optimisation tests with Simplicial sampling:
  310. """
  311. def test_f1_1_simplicial(self):
  312. """Multivariate test function 1:
  313. x[0]**2 + x[1]**2 with bounds=[(-1, 6), (-1, 6)]"""
  314. run_test(test1_1, n=1, sampling_method='simplicial')
  315. def test_f1_2_simplicial(self):
  316. """Multivariate test function 1:
  317. x[0]**2 + x[1]**2 with bounds=[(0, 1), (0, 1)]"""
  318. run_test(test1_2, n=1, sampling_method='simplicial')
  319. def test_f1_3_simplicial(self):
  320. """Multivariate test function 1: x[0]**2 + x[1]**2
  321. with bounds=[(None, None),(None, None)]"""
  322. run_test(test1_3, n=5, sampling_method='simplicial')
  323. def test_f2_1_simplicial(self):
  324. """Univariate test function on
  325. f(x) = (x - 30) * sin(x) with bounds=[(0, 60)]"""
  326. options = {'minimize_every_iter': False}
  327. run_test(test2_1, n=200, iters=7, options=options,
  328. sampling_method='simplicial')
  329. def test_f2_2_simplicial(self):
  330. """Univariate test function on
  331. f(x) = (x - 30) * sin(x) bounds=[(0, 4.5)]"""
  332. run_test(test2_2, n=1, sampling_method='simplicial')
  333. def test_f3_simplicial(self):
  334. """NLP: Hock and Schittkowski problem 18"""
  335. run_test(test3_1, n=1, sampling_method='simplicial')
  336. @pytest.mark.slow
  337. def test_f4_simplicial(self):
  338. """NLP: (High dimensional) Hock and Schittkowski 11 problem (HS11)"""
  339. run_test(test4_1, n=1, sampling_method='simplicial')
  340. def test_lj_symmetry_old(self):
  341. """LJ: Symmetry-constrained test function"""
  342. options = {'symmetry': True,
  343. 'disp': True}
  344. args = (6,) # Number of atoms
  345. run_test(testLJ, args=args, n=300,
  346. options=options, iters=1,
  347. sampling_method='simplicial')
  348. def test_f5_1_lj_symmetry(self):
  349. """LJ: Symmetry constrained test function"""
  350. options = {'symmetry': [0, ] * 6,
  351. 'disp': True}
  352. args = (6,) # No. of atoms
  353. run_test(testLJ, args=args, n=300,
  354. options=options, iters=1,
  355. sampling_method='simplicial')
  356. def test_f5_2_cons_symmetry(self):
  357. """Symmetry constrained test function"""
  358. options = {'symmetry': [0, 0],
  359. 'disp': True}
  360. run_test(test1_1, n=200,
  361. options=options, iters=1,
  362. sampling_method='simplicial')
  363. @pytest.mark.fail_slow(10)
  364. def test_f5_3_cons_symmetry(self):
  365. """Asymmetrically constrained test function"""
  366. options = {'symmetry': [0, 0, 0, 3],
  367. 'disp': True}
  368. run_test(test_s, n=10000,
  369. options=options,
  370. iters=1,
  371. sampling_method='simplicial')
  372. @pytest.mark.skip("Not a test")
  373. def test_f0_min_variance(self):
  374. """Return a minimum on a perfectly symmetric problem, based on
  375. gh10429"""
  376. avg = 0.5 # Given average value of x
  377. cons = {'type': 'eq', 'fun': lambda x: np.mean(x) - avg}
  378. # Minimize the variance of x under the given constraint
  379. res = shgo(np.var, bounds=6 * [(0, 1)], constraints=cons)
  380. assert res.success
  381. assert_allclose(res.fun, 0, atol=1e-15)
  382. assert_allclose(res.x, 0.5)
  383. @pytest.mark.skip("Not a test")
  384. def test_f0_min_variance_1D(self):
  385. """Return a minimum on a perfectly symmetric 1D problem, based on
  386. gh10538"""
  387. def fun(x):
  388. return x * (x - 1.0) * (x - 0.5)
  389. bounds = [(0, 1)]
  390. res = shgo(fun, bounds=bounds)
  391. ref = minimize_scalar(fun, bounds=bounds[0])
  392. assert res.success
  393. assert_allclose(res.fun, ref.fun)
  394. assert_allclose(res.x, ref.x, rtol=1e-6)
  395. # Argument test functions
  396. class TestShgoArguments:
  397. def test_1_1_simpl_iter(self):
  398. """Iterative simplicial sampling on TestFunction 1 (multivariate)"""
  399. run_test(test1_2, n=None, iters=2, sampling_method='simplicial')
  400. def test_1_2_simpl_iter(self):
  401. """Iterative simplicial on TestFunction 2 (univariate)"""
  402. options = {'minimize_every_iter': False}
  403. run_test(test2_1, n=None, iters=9, options=options,
  404. sampling_method='simplicial')
  405. def test_2_1_sobol_iter(self):
  406. """Iterative Sobol sampling on TestFunction 1 (multivariate)"""
  407. run_test(test1_2, n=None, iters=1, sampling_method='sobol')
  408. def test_2_2_sobol_iter(self):
  409. """Iterative Sobol sampling on TestFunction 2 (univariate)"""
  410. res = shgo(test2_1.f, test2_1.bounds, constraints=test2_1.cons,
  411. n=None, iters=1, sampling_method='sobol')
  412. np.testing.assert_allclose(res.x, test2_1.expected_x, rtol=1e-5, atol=1e-5)
  413. np.testing.assert_allclose(res.fun, test2_1.expected_fun, atol=1e-5)
  414. def test_3_1_disp_simplicial(self):
  415. """Iterative sampling on TestFunction 1 and 2 (multi and univariate)
  416. """
  417. def callback_func(x):
  418. print("Local minimization callback test")
  419. for test in [test1_1, test2_1]:
  420. shgo(test.f, test.bounds, iters=1,
  421. sampling_method='simplicial',
  422. callback=callback_func, options={'disp': True})
  423. shgo(test.f, test.bounds, n=1, sampling_method='simplicial',
  424. callback=callback_func, options={'disp': True})
  425. def test_3_2_disp_sobol(self):
  426. """Iterative sampling on TestFunction 1 and 2 (multi and univariate)"""
  427. def callback_func(x):
  428. print("Local minimization callback test")
  429. for test in [test1_1, test2_1]:
  430. shgo(test.f, test.bounds, iters=1, sampling_method='sobol',
  431. callback=callback_func, options={'disp': True})
  432. shgo(test.f, test.bounds, n=1, sampling_method='simplicial',
  433. callback=callback_func, options={'disp': True})
  434. def test_args_gh14589(self):
  435. """Using `args` used to cause `shgo` to fail; see #14589, #15986,
  436. #16506"""
  437. res = shgo(func=lambda x, y, z: x * z + y, bounds=[(0, 3)], args=(1, 2)
  438. )
  439. ref = shgo(func=lambda x: 2 * x + 1, bounds=[(0, 3)])
  440. assert_allclose(res.fun, ref.fun)
  441. assert_allclose(res.x, ref.x)
  442. def test_args_gh23517(self):
  443. """
  444. Checks that using `args` for func, jac and hess works
  445. """
  446. obj = MaratosTestArgs("a", 234)
  447. obj.bounds = Bounds([-5, -5], [5, 5])
  448. res2 = minimize(
  449. obj.fun,
  450. [4.0, 4.0],
  451. constraints=obj.constr,
  452. bounds=obj.bounds,
  453. method='trust-constr',
  454. args=("a", 234),
  455. jac=obj.grad
  456. )
  457. assert_allclose(res2.x, obj.x_opt, atol=1e-6)
  458. obj = MaratosTestArgs("a", 234)
  459. obj.bounds = Bounds([-10., -10.], [10., 10.])
  460. with warnings.catch_warnings():
  461. # warnings are from initialization of NonlinearConstraint and
  462. # poor initialization of the constraint by shgo
  463. warnings.simplefilter(
  464. "ignore",
  465. (OptimizeWarning, RuntimeWarning)
  466. )
  467. res = shgo(
  468. func=obj.fun,
  469. bounds=obj.bounds,
  470. args=("a", 234),
  471. minimizer_kwargs={
  472. "method": 'trust-constr',
  473. "constraints": obj.constr,
  474. "bounds": obj.bounds,
  475. "jac": obj.grad,
  476. "args": ("a", 234),
  477. },
  478. constraints=obj.constr,
  479. sampling_method='sobol',
  480. )
  481. assert_allclose(res.x, obj.x_opt, atol=1e-6)
  482. res = shgo(
  483. func=obj.fun,
  484. bounds=obj.bounds,
  485. args=("a", 234),
  486. minimizer_kwargs={
  487. "method": 'trust-constr',
  488. "constraints": obj.constr,
  489. "bounds": obj.bounds,
  490. "jac": obj.grad,
  491. "hess": obj.hess,
  492. },
  493. constraints=obj.constr,
  494. sampling_method='sobol',
  495. )
  496. assert_allclose(res.x, obj.x_opt, atol=1e-6)
  497. @pytest.mark.slow
  498. def test_4_1_known_f_min(self):
  499. """Test known function minima stopping criteria"""
  500. # Specify known function value
  501. options = {'f_min': test4_1.expected_fun,
  502. 'f_tol': 1e-6,
  503. 'minimize_every_iter': True}
  504. # TODO: Make default n higher for faster tests
  505. run_test(test4_1, n=None, test_atol=1e-5, options=options,
  506. sampling_method='simplicial')
  507. @pytest.mark.slow
  508. def test_4_2_known_f_min(self):
  509. """Test Global mode limiting local evaluations"""
  510. options = { # Specify known function value
  511. 'f_min': test4_1.expected_fun,
  512. 'f_tol': 1e-6,
  513. # Specify number of local iterations to perform
  514. 'minimize_every_iter': True,
  515. 'local_iter': 1}
  516. run_test(test4_1, n=None, test_atol=1e-5, options=options,
  517. sampling_method='simplicial')
  518. def test_4_4_known_f_min(self):
  519. """Test Global mode limiting local evaluations for 1D funcs"""
  520. options = { # Specify known function value
  521. 'f_min': test2_1.expected_fun,
  522. 'f_tol': 1e-6,
  523. # Specify number of local iterations to perform+
  524. 'minimize_every_iter': True,
  525. 'local_iter': 1,
  526. 'infty_constraints': False}
  527. res = shgo(test2_1.f, test2_1.bounds, constraints=test2_1.cons,
  528. n=None, iters=None, options=options,
  529. sampling_method='sobol')
  530. np.testing.assert_allclose(res.x, test2_1.expected_x, rtol=1e-5, atol=1e-5)
  531. def test_5_1_simplicial_argless(self):
  532. """Test Default simplicial sampling settings on TestFunction 1"""
  533. res = shgo(test1_1.f, test1_1.bounds, constraints=test1_1.cons)
  534. np.testing.assert_allclose(res.x, test1_1.expected_x, rtol=1e-5, atol=1e-5)
  535. def test_5_2_sobol_argless(self):
  536. """Test Default sobol sampling settings on TestFunction 1"""
  537. res = shgo(test1_1.f, test1_1.bounds, constraints=test1_1.cons,
  538. sampling_method='sobol')
  539. np.testing.assert_allclose(res.x, test1_1.expected_x, rtol=1e-5, atol=1e-5)
  540. def test_6_1_simplicial_max_iter(self):
  541. """Test that maximum iteration option works on TestFunction 3"""
  542. options = {'max_iter': 2}
  543. res = shgo(test3_1.f, test3_1.bounds, constraints=test3_1.cons,
  544. options=options, sampling_method='simplicial')
  545. np.testing.assert_allclose(res.x, test3_1.expected_x, rtol=1e-5, atol=1e-5)
  546. np.testing.assert_allclose(res.fun, test3_1.expected_fun, atol=1e-5)
  547. def test_6_2_simplicial_min_iter(self):
  548. """Test that maximum iteration option works on TestFunction 3"""
  549. options = {'min_iter': 2}
  550. res = shgo(test3_1.f, test3_1.bounds, constraints=test3_1.cons,
  551. options=options, sampling_method='simplicial')
  552. np.testing.assert_allclose(res.x, test3_1.expected_x, rtol=1e-5, atol=1e-5)
  553. np.testing.assert_allclose(res.fun, test3_1.expected_fun, atol=1e-5)
  554. def test_7_1_minkwargs(self):
  555. """Test the minimizer_kwargs arguments for solvers with constraints"""
  556. # Test solvers
  557. for solver in ['COBYLA', 'COBYQA', 'SLSQP']:
  558. # Note that passing global constraints to SLSQP is tested in other
  559. # unittests which run test4_1 normally
  560. minimizer_kwargs = {'method': solver,
  561. 'constraints': test3_1.cons}
  562. run_test(test3_1, n=100, test_atol=1e-3,
  563. minimizer_kwargs=minimizer_kwargs,
  564. sampling_method='sobol')
  565. def test_7_2_minkwargs(self):
  566. """Test the minimizer_kwargs default inits"""
  567. minimizer_kwargs = {'ftol': 1e-5}
  568. options = {'disp': True} # For coverage purposes
  569. SHGO(test3_1.f, test3_1.bounds, constraints=test3_1.cons[0],
  570. minimizer_kwargs=minimizer_kwargs, options=options)
  571. def test_7_3_minkwargs(self):
  572. """Test minimizer_kwargs arguments for solvers without constraints"""
  573. for solver in ['Nelder-Mead', 'Powell', 'CG', 'BFGS', 'Newton-CG',
  574. 'L-BFGS-B', 'TNC', 'dogleg', 'trust-ncg', 'trust-exact',
  575. 'trust-krylov']:
  576. def jac(x):
  577. return np.array([2 * x[0], 2 * x[1]]).T
  578. def hess(x):
  579. return np.array([[2, 0], [0, 2]])
  580. minimizer_kwargs = {'method': solver,
  581. 'jac': jac,
  582. 'hess': hess}
  583. logging.info(f"Solver = {solver}")
  584. logging.info("=" * 100)
  585. run_test(test1_1, n=100, test_atol=1e-3,
  586. minimizer_kwargs=minimizer_kwargs,
  587. sampling_method='sobol')
  588. def test_8_homology_group_diff(self):
  589. options = {'minhgrd': 1,
  590. 'minimize_every_iter': True}
  591. run_test(test1_1, n=None, iters=None, options=options,
  592. sampling_method='simplicial')
  593. def test_9_cons_g(self):
  594. """Test single function constraint passing"""
  595. SHGO(test3_1.f, test3_1.bounds, constraints=test3_1.cons[0])
  596. @pytest.mark.xfail(IS_PYPY and sys.platform == 'win32',
  597. reason="Failing and fix in PyPy not planned (see gh-18632)")
  598. def test_10_finite_time(self):
  599. """Test single function constraint passing"""
  600. options = {'maxtime': 1e-15}
  601. def f(x):
  602. time.sleep(1e-14)
  603. return 0.0
  604. res = shgo(f, test1_1.bounds, iters=5, options=options)
  605. # Assert that only 1 rather than 5 requested iterations ran:
  606. assert res.nit == 1
  607. def test_11_f_min_0(self):
  608. """Test to cover the case where f_lowest == 0"""
  609. options = {'f_min': 0.0,
  610. 'disp': True}
  611. res = shgo(test1_2.f, test1_2.bounds, n=10, iters=None,
  612. options=options, sampling_method='sobol')
  613. np.testing.assert_equal(0, res.x[0])
  614. np.testing.assert_equal(0, res.x[1])
  615. # @nottest
  616. @pytest.mark.skip(reason="no way of currently testing this")
  617. def test_12_sobol_inf_cons(self):
  618. """Test to cover the case where f_lowest == 0"""
  619. # TODO: This test doesn't cover anything new, it is unknown what the
  620. # original test was intended for as it was never complete. Delete or
  621. # replace in the future.
  622. options = {'maxtime': 1e-15,
  623. 'f_min': 0.0}
  624. res = shgo(test1_2.f, test1_2.bounds, n=1, iters=None,
  625. options=options, sampling_method='sobol')
  626. np.testing.assert_equal(0.0, res.fun)
  627. def test_13_high_sobol(self):
  628. """Test init of high-dimensional sobol sequences"""
  629. def f(x):
  630. return 0
  631. bounds = [(None, None), ] * 41
  632. SHGOc = SHGO(f, bounds, sampling_method='sobol')
  633. # SHGOc.sobol_points(2, 50)
  634. SHGOc.sampling_function(2, 50)
  635. def test_14_local_iter(self):
  636. """Test limited local iterations for a pseudo-global mode"""
  637. options = {'local_iter': 4}
  638. run_test(test5_1, n=60, options=options)
  639. def test_15_min_every_iter(self):
  640. """Test minimize every iter options and cover function cache"""
  641. options = {'minimize_every_iter': True}
  642. run_test(test1_1, n=1, iters=7, options=options,
  643. sampling_method='sobol')
  644. def test_16_disp_bounds_minimizer(self, capsys):
  645. """Test disp=True with minimizers that do not support bounds """
  646. options = {'disp': True}
  647. minimizer_kwargs = {'method': 'nelder-mead'}
  648. run_test(test1_2, sampling_method='simplicial',
  649. options=options, minimizer_kwargs=minimizer_kwargs)
  650. def test_17_custom_sampling(self):
  651. """Test the functionality to add custom sampling methods to shgo"""
  652. def sample(n, d):
  653. return np.random.uniform(size=(n, d))
  654. run_test(test1_1, n=30, sampling_method=sample)
  655. def test_18_bounds_class(self):
  656. # test that new and old bounds yield same result
  657. def f(x):
  658. return np.square(x).sum()
  659. lb = [-6., 1., -5.]
  660. ub = [-1., 3., 5.]
  661. bounds_old = list(zip(lb, ub))
  662. bounds_new = Bounds(lb, ub)
  663. res_old_bounds = shgo(f, bounds_old)
  664. res_new_bounds = shgo(f, bounds_new)
  665. assert res_new_bounds.nfev == res_old_bounds.nfev
  666. assert res_new_bounds.message == res_old_bounds.message
  667. assert res_new_bounds.success == res_old_bounds.success
  668. x_opt = np.array([-1., 1., 0.])
  669. np.testing.assert_allclose(res_new_bounds.x, x_opt)
  670. np.testing.assert_allclose(res_new_bounds.x, res_old_bounds.x)
  671. @pytest.mark.fail_slow(10)
  672. def test_19_parallelization(self):
  673. """Test the functionality to add custom sampling methods to shgo"""
  674. with Pool(2) as p:
  675. run_test(test1_1, n=30, workers=p.map) # Constrained
  676. run_test(test1_1, n=30, workers=map) # Constrained
  677. with Pool(2) as p:
  678. run_test(test_s, n=30, workers=p.map) # Unconstrained
  679. run_test(test_s, n=30, workers=map) # Unconstrained
  680. def test_20_constrained_args(self):
  681. """Test that constraints can be passed to arguments"""
  682. def eggholder(x):
  683. return (
  684. -(x[1] + 47.0)*np.sin(np.sqrt(abs(x[0] / 2.0 + (x[1] + 47.0))))
  685. - x[0]*np.sin(np.sqrt(abs(x[0] - (x[1] + 47.0))))
  686. )
  687. def f(x): # (cattle-feed)
  688. return 24.55 * x[0] + 26.75 * x[1] + 39 * x[2] + 40.50 * x[3]
  689. bounds = [(0, 1.0), ] * 4
  690. def g1_modified(x, i):
  691. return i * 2.3 * x[0] + i * 5.6 * x[1] + 11.1 * x[2] + 1.3 * x[
  692. 3] - 5 # >=0
  693. def g2(x):
  694. return (
  695. 12*x[0] + 11.9*x[1] + 41.8*x[2] + 52.1*x[3] - 21
  696. - 1.645*np.sqrt(
  697. 0.28*x[0]**2 + 0.19*x[1]**2 + 20.5*x[2]**2 + 0.62*x[3]**2
  698. )
  699. ) # >=0
  700. def h1(x):
  701. return x[0] + x[1] + x[2] + x[3] - 1 # == 0
  702. cons = ({'type': 'ineq', 'fun': g1_modified, "args": (0,)},
  703. {'type': 'ineq', 'fun': g2},
  704. {'type': 'eq', 'fun': h1})
  705. shgo(f, bounds, n=300, iters=1, constraints=cons)
  706. # using constrain with arguments AND sampling method sobol
  707. shgo(f, bounds, n=300, iters=1, constraints=cons,
  708. sampling_method='sobol')
  709. def test_21_1_jac_true(self):
  710. """Test that shgo can handle objective functions that return the
  711. gradient alongside the objective value. Fixes gh-13547"""
  712. # previous
  713. def func(x):
  714. return np.sum(np.power(x, 2)), 2 * x
  715. min_kwds = {"method": "SLSQP", "jac": True}
  716. opt_kwds = {"jac": True}
  717. shgo(
  718. func,
  719. bounds=[[-1, 1], [1, 2]],
  720. n=100, iters=5,
  721. sampling_method="sobol",
  722. minimizer_kwargs=min_kwds,
  723. options=opt_kwds
  724. )
  725. assert min_kwds['jac'] is True
  726. assert "jac" in opt_kwds
  727. # new
  728. def func(x):
  729. return np.sum(x ** 2), 2 * x
  730. bounds = [[-1, 1], [1, 2], [-1, 1], [1, 2], [0, 3]]
  731. res = shgo(func, bounds=bounds, sampling_method="sobol",
  732. minimizer_kwargs={'method': 'SLSQP', 'jac': True})
  733. ref = minimize(func, x0=[1, 1, 1, 1, 1], bounds=bounds,
  734. jac=True)
  735. assert res.success
  736. assert_allclose(res.fun, ref.fun)
  737. assert_allclose(res.x, ref.x, atol=1e-15)
  738. # Testing the passing of jac via options dict
  739. res = shgo(func, bounds=bounds, sampling_method="sobol",
  740. minimizer_kwargs={'method': 'SLSQP'},
  741. options={'jac': True})
  742. assert res.success
  743. assert_allclose(res.fun, ref.fun)
  744. assert_allclose(res.x, ref.x, atol=1e-15)
  745. @pytest.mark.parametrize('derivative', ['jac', 'hess', 'hessp'])
  746. def test_21_2_derivative_options(self, derivative):
  747. """shgo used to raise an error when passing `options` with 'jac'
  748. # see gh-12963. check that this is resolved
  749. """
  750. def objective(x):
  751. return 3 * x[0] * x[0] + 2 * x[0] + 5
  752. def gradient(x):
  753. return 6 * x[0] + 2
  754. def hess(x):
  755. return 6
  756. def hessp(x, p):
  757. return 6 * p
  758. derivative_funcs = {'jac': gradient, 'hess': hess, 'hessp': hessp}
  759. options = {derivative: derivative_funcs[derivative]}
  760. minimizer_kwargs = {'method': 'trust-constr'}
  761. bounds = [(-100, 100)]
  762. res = shgo(objective, bounds, minimizer_kwargs=minimizer_kwargs,
  763. options=options)
  764. ref = minimize(objective, x0=[0], bounds=bounds, **minimizer_kwargs,
  765. **options)
  766. assert res.success
  767. np.testing.assert_allclose(res.fun, ref.fun)
  768. np.testing.assert_allclose(res.x, ref.x)
  769. def test_21_3_hess_options_rosen(self):
  770. """Ensure the Hessian gets passed correctly to the local minimizer
  771. routine. Previous report gh-14533.
  772. """
  773. bounds = [(0, 1.6), (0, 1.6), (0, 1.4), (0, 1.4), (0, 1.4)]
  774. options = {'jac': rosen_der, 'hess': rosen_hess}
  775. minimizer_kwargs = {'method': 'Newton-CG'}
  776. res = shgo(rosen, bounds, minimizer_kwargs=minimizer_kwargs,
  777. options=options)
  778. ref = minimize(rosen, np.zeros(5), method='Newton-CG',
  779. **options)
  780. assert res.success
  781. assert_allclose(res.fun, ref.fun)
  782. assert_allclose(res.x, ref.x, atol=1e-15)
  783. def test_21_arg_tuple_sobol(self):
  784. """shgo used to raise an error when passing `args` with Sobol sampling
  785. # see gh-12114. check that this is resolved"""
  786. def fun(x, k):
  787. return x[0] ** k
  788. constraints = ({'type': 'ineq', 'fun': lambda x: x[0] - 1})
  789. bounds = [(0, 10)]
  790. res = shgo(fun, bounds, args=(1,), constraints=constraints,
  791. sampling_method='sobol')
  792. ref = minimize(fun, np.zeros(1), bounds=bounds, args=(1,),
  793. constraints=constraints)
  794. assert res.success
  795. assert_allclose(res.fun, ref.fun)
  796. assert_allclose(res.x, ref.x)
  797. # Failure test functions
  798. class TestShgoFailures:
  799. def test_1_maxiter(self):
  800. """Test failure on insufficient iterations"""
  801. options = {'maxiter': 2}
  802. res = shgo(test4_1.f, test4_1.bounds, n=2, iters=None,
  803. options=options, sampling_method='sobol')
  804. np.testing.assert_equal(False, res.success)
  805. # np.testing.assert_equal(4, res.nfev)
  806. np.testing.assert_equal(4, res.tnev)
  807. def test_2_sampling(self):
  808. """Rejection of unknown sampling method"""
  809. assert_raises(ValueError, shgo, test1_1.f, test1_1.bounds,
  810. sampling_method='not_Sobol')
  811. def test_3_1_no_min_pool_sobol(self):
  812. """Check that the routine stops when no minimiser is found
  813. after maximum specified function evaluations"""
  814. options = {'maxfev': 10,
  815. # 'maxev': 10,
  816. 'disp': True}
  817. res = shgo(test_table.f, test_table.bounds, n=3, options=options,
  818. sampling_method='sobol')
  819. np.testing.assert_equal(False, res.success)
  820. # np.testing.assert_equal(9, res.nfev)
  821. np.testing.assert_equal(12, res.nfev)
  822. def test_3_2_no_min_pool_simplicial(self):
  823. """Check that the routine stops when no minimiser is found
  824. after maximum specified sampling evaluations"""
  825. options = {'maxev': 10,
  826. 'disp': True}
  827. res = shgo(test_table.f, test_table.bounds, n=3, options=options,
  828. sampling_method='simplicial')
  829. np.testing.assert_equal(False, res.success)
  830. def test_4_1_bound_err(self):
  831. """Specified bounds ub > lb"""
  832. bounds = [(6, 3), (3, 5)]
  833. assert_raises(ValueError, shgo, test1_1.f, bounds)
  834. def test_4_2_bound_err(self):
  835. """Specified bounds are of the form (lb, ub)"""
  836. bounds = [(3, 5, 5), (3, 5)]
  837. assert_raises(ValueError, shgo, test1_1.f, bounds)
  838. def test_5_1_1_infeasible_sobol(self):
  839. """Ensures the algorithm terminates on infeasible problems
  840. after maxev is exceeded. Use infty constraints option"""
  841. options = {'maxev': 100,
  842. 'disp': True}
  843. res = shgo(test_infeasible.f, test_infeasible.bounds,
  844. constraints=test_infeasible.cons, n=100, options=options,
  845. sampling_method='sobol')
  846. np.testing.assert_equal(False, res.success)
  847. def test_5_1_2_infeasible_sobol(self):
  848. """Ensures the algorithm terminates on infeasible problems
  849. after maxev is exceeded. Do not use infty constraints option"""
  850. options = {'maxev': 100,
  851. 'disp': True,
  852. 'infty_constraints': False}
  853. res = shgo(test_infeasible.f, test_infeasible.bounds,
  854. constraints=test_infeasible.cons, n=100, options=options,
  855. sampling_method='sobol')
  856. np.testing.assert_equal(False, res.success)
  857. def test_5_2_infeasible_simplicial(self):
  858. """Ensures the algorithm terminates on infeasible problems
  859. after maxev is exceeded."""
  860. options = {'maxev': 1000,
  861. 'disp': False}
  862. res = shgo(test_infeasible.f, test_infeasible.bounds,
  863. constraints=test_infeasible.cons, n=100, options=options,
  864. sampling_method='simplicial')
  865. np.testing.assert_equal(False, res.success)
  866. def test_6_1_lower_known_f_min(self):
  867. """Test Global mode limiting local evaluations with f* too high"""
  868. options = { # Specify known function value
  869. 'f_min': test2_1.expected_fun + 2.0,
  870. 'f_tol': 1e-6,
  871. # Specify number of local iterations to perform+
  872. 'minimize_every_iter': True,
  873. 'local_iter': 1,
  874. 'infty_constraints': False}
  875. args = (test2_1.f, test2_1.bounds)
  876. kwargs = {'constraints': test2_1.cons,
  877. 'n': None,
  878. 'iters': None,
  879. 'options': options,
  880. 'sampling_method': 'sobol'
  881. }
  882. warns(UserWarning, shgo, *args, **kwargs)
  883. def test(self):
  884. from scipy.optimize import rosen, shgo
  885. bounds = [(0, 2), (0, 2), (0, 2), (0, 2), (0, 2)]
  886. def fun(x):
  887. fun.nfev += 1
  888. return rosen(x)
  889. fun.nfev = 0
  890. result = shgo(fun, bounds)
  891. print(result.x, result.fun, fun.nfev) # 50
  892. # Returns
  893. class TestShgoReturns:
  894. def test_1_nfev_simplicial(self):
  895. bounds = [(0, 2), (0, 2), (0, 2), (0, 2), (0, 2)]
  896. def fun(x):
  897. fun.nfev += 1
  898. return rosen(x)
  899. fun.nfev = 0
  900. result = shgo(fun, bounds)
  901. np.testing.assert_equal(fun.nfev, result.nfev)
  902. def test_1_nfev_sobol(self):
  903. bounds = [(0, 2), (0, 2), (0, 2), (0, 2), (0, 2)]
  904. def fun(x):
  905. fun.nfev += 1
  906. return rosen(x)
  907. fun.nfev = 0
  908. result = shgo(fun, bounds, sampling_method='sobol')
  909. np.testing.assert_equal(fun.nfev, result.nfev)
  910. def test_vector_constraint():
  911. # gh15514
  912. def quad(x):
  913. x = np.asarray(x)
  914. return [np.sum(x ** 2)]
  915. nlc = NonlinearConstraint(quad, [2.2], [3])
  916. oldc = new_constraint_to_old(nlc, np.array([1.0, 1.0]))
  917. res = shgo(rosen, [(0, 10), (0, 10)], constraints=oldc, sampling_method='sobol')
  918. assert np.all(np.sum((res.x)**2) >= 2.2)
  919. assert np.all(np.sum((res.x) ** 2) <= 3.0)
  920. assert res.success
  921. @pytest.mark.filterwarnings("ignore:delta_grad")
  922. def test_trust_constr():
  923. def quad(x):
  924. x = np.asarray(x)
  925. return [np.sum(x ** 2)]
  926. nlc = NonlinearConstraint(quad, [2.6], [3])
  927. minimizer_kwargs = {'method': 'trust-constr'}
  928. # note that we don't supply the constraints in minimizer_kwargs,
  929. # so if the final result obeys the constraints we know that shgo
  930. # passed them on to 'trust-constr'
  931. res = shgo(
  932. rosen,
  933. [(0, 10), (0, 10)],
  934. constraints=nlc,
  935. sampling_method='sobol',
  936. minimizer_kwargs=minimizer_kwargs
  937. )
  938. assert np.all(np.sum((res.x)**2) >= 2.6)
  939. assert np.all(np.sum((res.x) ** 2) <= 3.0)
  940. assert res.success
  941. def test_equality_constraints():
  942. # gh16260
  943. bounds = [(0.9, 4.0)] * 2 # Constrain probabilities to 0 and 1.
  944. def faulty(x):
  945. return x[0] + x[1]
  946. nlc = NonlinearConstraint(faulty, 3.9, 3.9)
  947. res = shgo(rosen, bounds=bounds, constraints=nlc)
  948. assert_allclose(np.sum(res.x), 3.9)
  949. def faulty(x):
  950. return x[0] + x[1] - 3.9
  951. constraints = {'type': 'eq', 'fun': faulty}
  952. res = shgo(rosen, bounds=bounds, constraints=constraints)
  953. assert_allclose(np.sum(res.x), 3.9)
  954. bounds = [(0, 1.0)] * 4
  955. # sum of variable should equal 1.
  956. def faulty(x):
  957. return x[0] + x[1] + x[2] + x[3] - 1
  958. # options = {'minimize_every_iter': True, 'local_iter':10}
  959. constraints = {'type': 'eq', 'fun': faulty}
  960. res = shgo(
  961. lambda x: - np.prod(x),
  962. bounds=bounds,
  963. constraints=constraints,
  964. sampling_method='sobol'
  965. )
  966. assert_allclose(np.sum(res.x), 1.0)
  967. def test_gh16971():
  968. def cons(x):
  969. return np.sum(x**2) - 0
  970. c = {'fun': cons, 'type': 'ineq'}
  971. minimizer_kwargs = {
  972. 'method': 'COBYLA',
  973. 'options': {'rhobeg': 5, 'tol': 5e-1, 'catol': 0.05}
  974. }
  975. s = SHGO(
  976. rosen, [(0, 10)]*2, constraints=c, minimizer_kwargs=minimizer_kwargs
  977. )
  978. assert s.minimizer_kwargs['method'].lower() == 'cobyla'
  979. assert s.minimizer_kwargs['options']['catol'] == 0.05