test_slsqp.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. """
  2. Unit test for SLSQP optimization.
  3. """
  4. from numpy.testing import (assert_, assert_array_almost_equal,
  5. assert_allclose, assert_equal)
  6. from pytest import raises as assert_raises
  7. import pytest
  8. import numpy as np
  9. import scipy
  10. from scipy.optimize import (fmin_slsqp, minimize, Bounds, NonlinearConstraint,
  11. OptimizeResult)
  12. class MyCallBack:
  13. """pass a custom callback function
  14. This makes sure it's being used.
  15. """
  16. def __init__(self):
  17. self.been_called = False
  18. self.ncalls = 0
  19. def __call__(self, x):
  20. assert not isinstance(x, OptimizeResult)
  21. self.been_called = True
  22. self.ncalls += 1
  23. def callback2(self, intermediate_result):
  24. assert isinstance(intermediate_result, OptimizeResult)
  25. self.been_called = True
  26. self.ncalls += 1
  27. def callback3(self, intermediate_result):
  28. assert isinstance(intermediate_result, OptimizeResult)
  29. raise StopIteration
  30. class TestSLSQP:
  31. """
  32. Test SLSQP algorithm using Example 14.4 from Numerical Methods for
  33. Engineers by Steven Chapra and Raymond Canale.
  34. This example maximizes the function f(x) = 2*x*y + 2*x - x**2 - 2*y**2,
  35. which has a maximum at x=2, y=1.
  36. """
  37. def setup_method(self):
  38. self.opts = {'disp': False}
  39. def fun(self, d, sign=1.0):
  40. """
  41. Arguments:
  42. d - A list of two elements, where d[0] represents x and d[1] represents y
  43. in the following equation.
  44. sign - A multiplier for f. Since we want to optimize it, and the SciPy
  45. optimizers can only minimize functions, we need to multiply it by
  46. -1 to achieve the desired solution
  47. Returns:
  48. 2*x*y + 2*x - x**2 - 2*y**2
  49. """
  50. x = d[0]
  51. y = d[1]
  52. return sign*(2*x*y + 2*x - x**2 - 2*y**2)
  53. def jac(self, d, sign=1.0):
  54. """
  55. This is the derivative of fun, returning a NumPy array
  56. representing df/dx and df/dy.
  57. """
  58. x = d[0]
  59. y = d[1]
  60. dfdx = sign*(-2*x + 2*y + 2)
  61. dfdy = sign*(2*x - 4*y)
  62. return np.array([dfdx, dfdy], float)
  63. def fun_and_jac(self, d, sign=1.0):
  64. return self.fun(d, sign), self.jac(d, sign)
  65. def f_eqcon(self, x, sign=1.0):
  66. """ Equality constraint """
  67. return np.array([x[0] - x[1]])
  68. def fprime_eqcon(self, x, sign=1.0):
  69. """ Equality constraint, derivative """
  70. return np.array([[1, -1]])
  71. def f_eqcon_scalar(self, x, sign=1.0):
  72. """ Scalar equality constraint """
  73. return self.f_eqcon(x, sign)[0]
  74. def fprime_eqcon_scalar(self, x, sign=1.0):
  75. """ Scalar equality constraint, derivative """
  76. return self.fprime_eqcon(x, sign)[0].tolist()
  77. def f_ieqcon(self, x, sign=1.0):
  78. """ Inequality constraint """
  79. return np.array([x[0] - x[1] - 1.0])
  80. def fprime_ieqcon(self, x, sign=1.0):
  81. """ Inequality constraint, derivative """
  82. return np.array([[1, -1]])
  83. def f_ieqcon2(self, x):
  84. """ Vector inequality constraint """
  85. return np.asarray(x)
  86. def fprime_ieqcon2(self, x):
  87. """ Vector inequality constraint, derivative """
  88. return np.identity(x.shape[0])
  89. # minimize
  90. def test_minimize_unbounded_approximated(self):
  91. # Minimize, method='SLSQP': unbounded, approximated jacobian.
  92. jacs = [None, False, '2-point', '3-point']
  93. for jac in jacs:
  94. res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
  95. jac=jac, method='SLSQP',
  96. options=self.opts)
  97. assert_(res['success'], res['message'])
  98. assert_allclose(res.x, [2, 1])
  99. def test_minimize_unbounded_given(self):
  100. # Minimize, method='SLSQP': unbounded, given Jacobian.
  101. res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
  102. jac=self.jac, method='SLSQP', options=self.opts)
  103. assert_(res['success'], res['message'])
  104. assert_allclose(res.x, [2, 1])
  105. def test_minimize_bounded_approximated(self):
  106. # Minimize, method='SLSQP': bounded, approximated jacobian.
  107. jacs = [None, False, '2-point', '3-point']
  108. for jac in jacs:
  109. with np.errstate(invalid='ignore'):
  110. res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
  111. jac=jac,
  112. bounds=((2.5, None), (None, 0.5)),
  113. method='SLSQP', options=self.opts)
  114. assert_(res['success'], res['message'])
  115. assert_allclose(res.x, [2.5, 0.5])
  116. assert_(2.5 <= res.x[0])
  117. assert_(res.x[1] <= 0.5)
  118. def test_minimize_unbounded_combined(self):
  119. # Minimize, method='SLSQP': unbounded, combined function and Jacobian.
  120. res = minimize(self.fun_and_jac, [-1.0, 1.0], args=(-1.0, ),
  121. jac=True, method='SLSQP', options=self.opts)
  122. assert_(res['success'], res['message'])
  123. assert_allclose(res.x, [2, 1])
  124. def test_minimize_equality_approximated(self):
  125. # Minimize with method='SLSQP': equality constraint, approx. jacobian.
  126. jacs = [None, False, '2-point', '3-point']
  127. for jac in jacs:
  128. res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
  129. jac=jac,
  130. constraints={'type': 'eq',
  131. 'fun': self.f_eqcon,
  132. 'args': (-1.0, )},
  133. method='SLSQP', options=self.opts)
  134. assert_(res['success'], res['message'])
  135. assert_allclose(res.x, [1, 1])
  136. def test_minimize_equality_given(self):
  137. # Minimize with method='SLSQP': equality constraint, given Jacobian.
  138. res = minimize(self.fun, [-1.0, 1.0], jac=self.jac,
  139. method='SLSQP', args=(-1.0,),
  140. constraints={'type': 'eq', 'fun':self.f_eqcon,
  141. 'args': (-1.0, )},
  142. options=self.opts)
  143. assert_(res['success'], res['message'])
  144. assert_allclose(res.x, [1, 1])
  145. def test_minimize_equality_given2(self):
  146. # Minimize with method='SLSQP': equality constraint, given Jacobian
  147. # for fun and const.
  148. res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
  149. jac=self.jac, args=(-1.0,),
  150. constraints={'type': 'eq',
  151. 'fun': self.f_eqcon,
  152. 'args': (-1.0, ),
  153. 'jac': self.fprime_eqcon},
  154. options=self.opts)
  155. assert_(res['success'], res['message'])
  156. assert_allclose(res.x, [1, 1])
  157. def test_minimize_equality_given_cons_scalar(self):
  158. # Minimize with method='SLSQP': scalar equality constraint, given
  159. # Jacobian for fun and const.
  160. res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
  161. jac=self.jac, args=(-1.0,),
  162. constraints={'type': 'eq',
  163. 'fun': self.f_eqcon_scalar,
  164. 'args': (-1.0, ),
  165. 'jac': self.fprime_eqcon_scalar},
  166. options=self.opts)
  167. assert_(res['success'], res['message'])
  168. assert_allclose(res.x, [1, 1])
  169. def test_minimize_inequality_given(self):
  170. # Minimize with method='SLSQP': inequality constraint, given Jacobian.
  171. res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
  172. jac=self.jac, args=(-1.0, ),
  173. constraints={'type': 'ineq',
  174. 'fun': self.f_ieqcon,
  175. 'args': (-1.0, )},
  176. options=self.opts)
  177. assert_(res['success'], res['message'])
  178. assert_allclose(res.x, [2, 1], atol=1e-3)
  179. def test_minimize_inequality_given_vector_constraints(self):
  180. # Minimize with method='SLSQP': vector inequality constraint, given
  181. # Jacobian.
  182. res = minimize(self.fun, [-1.0, 1.0], jac=self.jac,
  183. method='SLSQP', args=(-1.0,),
  184. constraints={'type': 'ineq',
  185. 'fun': self.f_ieqcon2,
  186. 'jac': self.fprime_ieqcon2},
  187. options=self.opts)
  188. assert_(res['success'], res['message'])
  189. assert_allclose(res.x, [2, 1])
  190. def test_minimize_bounded_constraint(self):
  191. # when the constraint makes the solver go up against a parameter
  192. # bound make sure that the numerical differentiation of the
  193. # jacobian doesn't try to exceed that bound using a finite difference.
  194. # gh11403
  195. def c(x):
  196. assert 0 <= x[0] <= 1 and 0 <= x[1] <= 1, x
  197. return x[0] ** 0.5 + x[1]
  198. def f(x):
  199. assert 0 <= x[0] <= 1 and 0 <= x[1] <= 1, x
  200. return -x[0] ** 2 + x[1] ** 2
  201. cns = [NonlinearConstraint(c, 0, 1.5)]
  202. x0 = np.asarray([0.9, 0.5])
  203. bnd = Bounds([0., 0.], [1.0, 1.0])
  204. minimize(f, x0, method='SLSQP', bounds=bnd, constraints=cns)
  205. def test_minimize_bound_equality_given2(self):
  206. # Minimize with method='SLSQP': bounds, eq. const., given jac. for
  207. # fun. and const.
  208. res = minimize(self.fun, [-1.0, 1.0], method='SLSQP',
  209. jac=self.jac, args=(-1.0, ),
  210. bounds=[(-0.8, 1.), (-1, 0.8)],
  211. constraints={'type': 'eq',
  212. 'fun': self.f_eqcon,
  213. 'args': (-1.0, ),
  214. 'jac': self.fprime_eqcon},
  215. options=self.opts)
  216. assert_(res['success'], res['message'])
  217. assert_allclose(res.x, [0.8, 0.8], atol=1e-3)
  218. assert_(-0.8 <= res.x[0] <= 1)
  219. assert_(-1 <= res.x[1] <= 0.8)
  220. # fmin_slsqp
  221. def test_unbounded_approximated(self):
  222. # SLSQP: unbounded, approximated Jacobian.
  223. res = fmin_slsqp(self.fun, [-1.0, 1.0], args=(-1.0, ),
  224. iprint = 0, full_output = 1)
  225. x, fx, its, imode, smode = res
  226. assert_(imode == 0, imode)
  227. assert_array_almost_equal(x, [2, 1])
  228. def test_unbounded_given(self):
  229. # SLSQP: unbounded, given Jacobian.
  230. res = fmin_slsqp(self.fun, [-1.0, 1.0], args=(-1.0, ),
  231. fprime = self.jac, iprint = 0,
  232. full_output = 1)
  233. x, fx, its, imode, smode = res
  234. assert_(imode == 0, imode)
  235. assert_array_almost_equal(x, [2, 1])
  236. def test_equality_approximated(self):
  237. # SLSQP: equality constraint, approximated Jacobian.
  238. res = fmin_slsqp(self.fun,[-1.0,1.0], args=(-1.0,),
  239. eqcons = [self.f_eqcon],
  240. iprint = 0, full_output = 1)
  241. x, fx, its, imode, smode = res
  242. assert_(imode == 0, imode)
  243. assert_array_almost_equal(x, [1, 1])
  244. def test_equality_given(self):
  245. # SLSQP: equality constraint, given Jacobian.
  246. res = fmin_slsqp(self.fun, [-1.0, 1.0],
  247. fprime=self.jac, args=(-1.0,),
  248. eqcons = [self.f_eqcon], iprint = 0,
  249. full_output = 1)
  250. x, fx, its, imode, smode = res
  251. assert_(imode == 0, imode)
  252. assert_array_almost_equal(x, [1, 1])
  253. def test_equality_given2(self):
  254. # SLSQP: equality constraint, given Jacobian for fun and const.
  255. res = fmin_slsqp(self.fun, [-1.0, 1.0],
  256. fprime=self.jac, args=(-1.0,),
  257. f_eqcons = self.f_eqcon,
  258. fprime_eqcons = self.fprime_eqcon,
  259. iprint = 0,
  260. full_output = 1)
  261. x, fx, its, imode, smode = res
  262. assert_(imode == 0, imode)
  263. assert_array_almost_equal(x, [1, 1])
  264. def test_inequality_given(self):
  265. # SLSQP: inequality constraint, given Jacobian.
  266. res = fmin_slsqp(self.fun, [-1.0, 1.0],
  267. fprime=self.jac, args=(-1.0, ),
  268. ieqcons = [self.f_ieqcon],
  269. iprint = 0, full_output = 1)
  270. x, fx, its, imode, smode = res
  271. assert_(imode == 0, imode)
  272. assert_array_almost_equal(x, [2, 1], decimal=3)
  273. def test_bound_equality_given2(self):
  274. # SLSQP: bounds, eq. const., given jac. for fun. and const.
  275. res = fmin_slsqp(self.fun, [-1.0, 1.0],
  276. fprime=self.jac, args=(-1.0, ),
  277. bounds = [(-0.8, 1.), (-1, 0.8)],
  278. f_eqcons = self.f_eqcon,
  279. fprime_eqcons = self.fprime_eqcon,
  280. iprint = 0, full_output = 1)
  281. x, fx, its, imode, smode = res
  282. assert_(imode == 0, imode)
  283. assert_array_almost_equal(x, [0.8, 0.8], decimal=3)
  284. assert_(-0.8 <= x[0] <= 1)
  285. assert_(-1 <= x[1] <= 0.8)
  286. def test_scalar_constraints(self):
  287. # Regression test for gh-2182
  288. x = fmin_slsqp(lambda z: z**2, [3.],
  289. ieqcons=[lambda z: z[0] - 1],
  290. iprint=0)
  291. assert_array_almost_equal(x, [1.])
  292. x = fmin_slsqp(lambda z: z**2, [3.],
  293. f_ieqcons=lambda z: [z[0] - 1],
  294. iprint=0)
  295. assert_array_almost_equal(x, [1.])
  296. def test_integer_bounds(self):
  297. # This should not raise an exception
  298. fmin_slsqp(lambda z: z**2 - 1, [0], bounds=[[0, 1]], iprint=0)
  299. def test_array_bounds(self):
  300. # NumPy used to treat n-dimensional 1-element arrays as scalars
  301. # in some cases. The handling of `bounds` by `fmin_slsqp` still
  302. # supports this behavior.
  303. bounds = [(-np.inf, np.inf), (np.array([2]), np.array([3]))]
  304. x = fmin_slsqp(lambda z: np.sum(z**2 - 1), [2.5, 2.5], bounds=bounds,
  305. iprint=0)
  306. assert_array_almost_equal(x, [0, 2])
  307. def test_obj_must_return_scalar(self):
  308. # Regression test for Github Issue #5433
  309. # If objective function does not return a scalar, raises ValueError
  310. with assert_raises(ValueError):
  311. fmin_slsqp(lambda x: [0, 1], [1, 2, 3])
  312. def test_obj_returns_scalar_in_list(self):
  313. # Test for Github Issue #5433 and PR #6691
  314. # Objective function should be able to return length-1 Python list
  315. # containing the scalar
  316. fmin_slsqp(lambda x: [0], [1, 2, 3], iprint=0)
  317. def test_callback(self):
  318. # Minimize, method='SLSQP': unbounded, approximated jacobian. Check for callback
  319. callback = MyCallBack()
  320. res = minimize(self.fun, [-1.0, 1.0], args=(-1.0, ),
  321. method='SLSQP', callback=callback, options=self.opts)
  322. assert res.success
  323. assert res.message
  324. assert callback.been_called
  325. assert_equal(callback.ncalls, res['nit'])
  326. res = minimize(
  327. self.fun,
  328. [-1.0, 1.0],
  329. args=(-1.0, ),
  330. method='SLSQP',
  331. callback=callback.callback2,
  332. options=self.opts
  333. )
  334. assert res.success
  335. assert callback.been_called
  336. res = minimize(
  337. self.fun,
  338. [-1.0, 1.0],
  339. args=(-1.0, ),
  340. method='SLSQP',
  341. callback=callback.callback3,
  342. options=self.opts
  343. )
  344. assert not res.success
  345. assert res.message.startswith("`callback` raised `StopIteration`")
  346. def test_inconsistent_linearization(self):
  347. # SLSQP must be able to solve this problem, even if the
  348. # linearized problem at the starting point is infeasible.
  349. # Linearized constraints are
  350. #
  351. # 2*x0[0]*x[0] >= 1
  352. #
  353. # At x0 = [0, 1], the second constraint is clearly infeasible.
  354. # This triggers a call with n2==1 in the LSQ subroutine.
  355. x = [0, 1]
  356. def f1(x):
  357. return x[0] + x[1] - 2
  358. def f2(x):
  359. return x[0] ** 2 - 1
  360. sol = minimize(
  361. lambda x: x[0]**2 + x[1]**2,
  362. x,
  363. constraints=({'type':'eq','fun': f1},
  364. {'type':'ineq','fun': f2}),
  365. bounds=((0,None), (0,None)),
  366. method='SLSQP')
  367. x = sol.x
  368. assert_allclose(f1(x), 0, atol=1e-8)
  369. assert_(f2(x) >= -1e-8)
  370. assert_(sol.success, sol)
  371. def test_regression_5743(self):
  372. # SLSQP must not indicate success for this problem,
  373. # which is infeasible.
  374. x = [1, 2]
  375. sol = minimize(
  376. lambda x: x[0]**2 + x[1]**2,
  377. x,
  378. constraints=({'type':'eq','fun': lambda x: x[0]+x[1]-1},
  379. {'type':'ineq','fun': lambda x: x[0]-2}),
  380. bounds=((0,None), (0,None)),
  381. method='SLSQP')
  382. assert_(not sol.success, sol)
  383. def test_gh_6676(self):
  384. def func(x):
  385. return (x[0] - 1)**2 + 2*(x[1] - 1)**2 + 0.5*(x[2] - 1)**2
  386. sol = minimize(func, [0, 0, 0], method='SLSQP')
  387. assert_(sol.jac.shape == (3,))
  388. def test_invalid_bounds(self):
  389. # Raise correct error when lower bound is greater than upper bound.
  390. # See Github issue 6875.
  391. bounds_list = [
  392. ((1, 2), (2, 1)),
  393. ((2, 1), (1, 2)),
  394. ((2, 1), (2, 1)),
  395. ((np.inf, 0), (np.inf, 0)),
  396. ((1, -np.inf), (0, 1)),
  397. ]
  398. for bounds in bounds_list:
  399. with assert_raises(ValueError):
  400. minimize(self.fun, [-1.0, 1.0], bounds=bounds, method='SLSQP')
  401. def test_bounds_clipping(self):
  402. #
  403. # SLSQP returns bogus results for initial guess out of bounds, gh-6859
  404. #
  405. def f(x):
  406. return (x[0] - 1)**2
  407. sol = minimize(f, [10], method='slsqp', bounds=[(None, 0)])
  408. assert_(sol.success)
  409. assert_allclose(sol.x, 0, atol=1e-10)
  410. sol = minimize(f, [-10], method='slsqp', bounds=[(2, None)])
  411. assert_(sol.success)
  412. assert_allclose(sol.x, 2, atol=1e-10)
  413. sol = minimize(f, [-10], method='slsqp', bounds=[(None, 0)])
  414. assert_(sol.success)
  415. assert_allclose(sol.x, 0, atol=1e-10)
  416. sol = minimize(f, [10], method='slsqp', bounds=[(2, None)])
  417. assert_(sol.success)
  418. assert_allclose(sol.x, 2, atol=1e-10)
  419. sol = minimize(f, [-0.5], method='slsqp', bounds=[(-1, 0)])
  420. assert_(sol.success)
  421. assert_allclose(sol.x, 0, atol=1e-10)
  422. sol = minimize(f, [10], method='slsqp', bounds=[(-1, 0)])
  423. assert_(sol.success)
  424. assert_allclose(sol.x, 0, atol=1e-10)
  425. def test_infeasible_initial(self):
  426. # Check SLSQP behavior with infeasible initial point
  427. def f(x):
  428. x, = x
  429. return x*x - 2*x + 1
  430. cons_u = [{'type': 'ineq', 'fun': lambda x: 0 - x}]
  431. cons_l = [{'type': 'ineq', 'fun': lambda x: x - 2}]
  432. cons_ul = [{'type': 'ineq', 'fun': lambda x: 0 - x},
  433. {'type': 'ineq', 'fun': lambda x: x + 1}]
  434. sol = minimize(f, [10], method='slsqp', constraints=cons_u)
  435. assert_(sol.success)
  436. assert_allclose(sol.x, 0, atol=1e-10)
  437. sol = minimize(f, [-10], method='slsqp', constraints=cons_l)
  438. assert_(sol.success)
  439. assert_allclose(sol.x, 2, atol=1e-10)
  440. sol = minimize(f, [-10], method='slsqp', constraints=cons_u)
  441. assert_(sol.success)
  442. assert_allclose(sol.x, 0, atol=1e-10)
  443. sol = minimize(f, [10], method='slsqp', constraints=cons_l)
  444. assert_(sol.success)
  445. assert_allclose(sol.x, 2, atol=1e-10)
  446. sol = minimize(f, [-0.5], method='slsqp', constraints=cons_ul)
  447. assert_(sol.success)
  448. assert_allclose(sol.x, 0, atol=1e-10)
  449. sol = minimize(f, [10], method='slsqp', constraints=cons_ul)
  450. assert_(sol.success)
  451. assert_allclose(sol.x, 0, atol=1e-10)
  452. @pytest.mark.xfail(scipy.show_config(mode='dicts')['Compilers']['fortran']['name']
  453. == "intel-llvm",
  454. reason="Runtime warning due to floating point issues, not logic")
  455. def test_inconsistent_inequalities(self):
  456. # gh-7618
  457. def cost(x):
  458. return -1 * x[0] + 4 * x[1]
  459. def ineqcons1(x):
  460. return x[1] - x[0] - 1
  461. def ineqcons2(x):
  462. return x[0] - x[1]
  463. # The inequalities are inconsistent, so no solution can exist:
  464. #
  465. # x1 >= x0 + 1
  466. # x0 >= x1
  467. x0 = (1,5)
  468. bounds = ((-5, 5), (-5, 5))
  469. cons = (dict(type='ineq', fun=ineqcons1), dict(type='ineq', fun=ineqcons2))
  470. res = minimize(cost, x0, method='SLSQP', bounds=bounds, constraints=cons)
  471. assert_(not res.success)
  472. def test_new_bounds_type(self):
  473. def f(x):
  474. return x[0] ** 2 + x[1] ** 2
  475. bounds = Bounds([1, 0], [np.inf, np.inf])
  476. sol = minimize(f, [0, 0], method='slsqp', bounds=bounds)
  477. assert_(sol.success)
  478. assert_allclose(sol.x, [1, 0])
  479. def test_nested_minimization(self):
  480. class NestedProblem:
  481. def __init__(self):
  482. self.F_outer_count = 0
  483. def F_outer(self, x):
  484. self.F_outer_count += 1
  485. if self.F_outer_count > 1000:
  486. raise Exception("Nested minimization failed to terminate.")
  487. inner_res = minimize(self.F_inner, (3, 4), method="SLSQP")
  488. assert_(inner_res.success)
  489. assert_allclose(inner_res.x, [1, 1])
  490. return x[0]**2 + x[1]**2 + x[2]**2
  491. def F_inner(self, x):
  492. return (x[0] - 1)**2 + (x[1] - 1)**2
  493. def solve(self):
  494. outer_res = minimize(self.F_outer, (5, 5, 5), method="SLSQP")
  495. assert_(outer_res.success)
  496. assert_allclose(outer_res.x, [0, 0, 0])
  497. problem = NestedProblem()
  498. problem.solve()
  499. def test_gh1758(self):
  500. # the test suggested in gh1758
  501. # https://nlopt.readthedocs.io/en/latest/NLopt_Tutorial/
  502. # implement two equality constraints, in R^2.
  503. def fun(x):
  504. return np.sqrt(x[1])
  505. def f_eqcon(x):
  506. """ Equality constraint """
  507. return x[1] - (2 * x[0]) ** 3
  508. def f_eqcon2(x):
  509. """ Equality constraint """
  510. return x[1] - (-x[0] + 1) ** 3
  511. c1 = {'type': 'eq', 'fun': f_eqcon}
  512. c2 = {'type': 'eq', 'fun': f_eqcon2}
  513. res = minimize(fun, [8, 0.25], method='SLSQP',
  514. constraints=[c1, c2], bounds=[(-0.5, 1), (0, 8)])
  515. np.testing.assert_allclose(res.fun, 0.5443310539518)
  516. np.testing.assert_allclose(res.x, [0.33333333, 0.2962963])
  517. assert res.success
  518. def test_gh9640(self):
  519. cons = ({'type': 'ineq', 'fun': lambda x: -x[0] - x[1] - 3},
  520. {'type': 'ineq', 'fun': lambda x: x[1] + x[2] - 2})
  521. bnds = ((-2, 2), (-2, 2), (-2, 2))
  522. def target(x):
  523. return 1
  524. x0 = [-1.8869783504471584, -0.640096352696244, -0.8174212253407696]
  525. res = minimize(target, x0, method='SLSQP', bounds=bnds, constraints=cons,
  526. options={'disp':False, 'maxiter':10000})
  527. # The problem is infeasible, so it cannot succeed
  528. assert not res.success
  529. def test_parameters_stay_within_bounds(self):
  530. # gh11403. For some problems the SLSQP Fortran code suggests a step
  531. # outside one of the lower/upper bounds. When this happens
  532. # approx_derivative complains because it's being asked to evaluate
  533. # a gradient outside its domain.
  534. # gh21872, removal of random initial position, replacing with specific
  535. # starting point, because success/fail depends on the seed used.
  536. bounds = Bounds(np.array([0.1]), np.array([1.0]))
  537. x0 = np.array(bounds.lb + (bounds.ub - bounds.lb) *
  538. 0.417022004702574)
  539. def f(x):
  540. assert (x >= bounds.lb).all()
  541. return np.linalg.norm(x)
  542. # The following should not raise any warnings which was the case, with the
  543. # old Fortran code.
  544. res = minimize(f, x0, method='SLSQP', bounds=bounds)
  545. assert res.success
  546. def test_slsqp_segfault_wrong_workspace_computation():
  547. # See gh-14915
  548. # This problem is not well-defined, however should not cause a segfault.
  549. # The previous F77 workspace computation did not handle only equality-
  550. # constrained problems correctly.
  551. rng = np.random.default_rng(1742651087222879)
  552. x = rng.uniform(size=[22,365])
  553. target = np.linspace(0.9, 4.0, 50)
  554. def metric(v, weights):
  555. return [[0, 0],[1, 1]]
  556. def efficient_metric(v, target):
  557. def metric_a(weights):
  558. return metric(v, weights)[1][0]
  559. def metric_b(weights, v):
  560. return metric(v, weights)[0][0]
  561. constraints = ({'type': 'eq', 'fun': lambda x: metric_a(x) - target},
  562. {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
  563. weights = np.array([len(v)*[1./len(v)]])[0]
  564. result = minimize(metric_b,
  565. weights,
  566. args=(v,),
  567. method='SLSQP',
  568. constraints=constraints)
  569. return result
  570. efficient_metric(x, target)