test_differentiable_functions.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  1. import pytest
  2. import platform
  3. import numpy as np
  4. from numpy.testing import (TestCase, assert_array_almost_equal,
  5. assert_array_equal, assert_, assert_allclose,
  6. assert_equal)
  7. from scipy._lib._gcutils import assert_deallocated
  8. from scipy._lib._util import MapWrapper
  9. from scipy.sparse import csr_array
  10. from scipy.sparse.linalg import LinearOperator
  11. from scipy.optimize._differentiable_functions import (ScalarFunction,
  12. VectorFunction,
  13. LinearVectorFunction,
  14. IdentityVectorFunction)
  15. from scipy.optimize import rosen, rosen_der, rosen_hess
  16. from scipy.optimize._hessian_update_strategy import BFGS
  17. class ExScalarFunction:
  18. def __init__(self):
  19. self.nfev = 0
  20. self.ngev = 0
  21. self.nhev = 0
  22. def fun(self, x):
  23. self.nfev += 1
  24. return 2*(x[0]**2 + x[1]**2 - 1) - x[0]
  25. def grad(self, x):
  26. self.ngev += 1
  27. return np.array([4*x[0]-1, 4*x[1]])
  28. def hess(self, x):
  29. self.nhev += 1
  30. return 4*np.eye(2)
  31. class TestScalarFunction(TestCase):
  32. def test_finite_difference_grad(self):
  33. ex = ExScalarFunction()
  34. nfev = 0
  35. ngev = 0
  36. x0 = [1.0, 0.0]
  37. analit = ScalarFunction(ex.fun, x0, (), ex.grad,
  38. ex.hess, None, (-np.inf, np.inf))
  39. nfev += 1
  40. ngev += 1
  41. assert_array_equal(ex.nfev, nfev)
  42. assert_array_equal(analit.nfev, nfev)
  43. assert_array_equal(ex.ngev, ngev)
  44. assert_array_equal(analit.ngev, nfev)
  45. approx = ScalarFunction(ex.fun, x0, (), '2-point',
  46. ex.hess, None, (-np.inf, np.inf))
  47. nfev += 3
  48. ngev += 1
  49. assert_array_equal(ex.nfev, nfev)
  50. assert_array_equal(analit.nfev+approx.nfev, nfev)
  51. assert_array_equal(analit.ngev+approx.ngev, ngev)
  52. assert_array_equal(analit.f, approx.f)
  53. assert_array_almost_equal(analit.g, approx.g)
  54. x = [10, 0.3]
  55. f_analit = analit.fun(x)
  56. g_analit = analit.grad(x)
  57. nfev += 1
  58. ngev += 1
  59. assert_array_equal(ex.nfev, nfev)
  60. assert_array_equal(analit.nfev+approx.nfev, nfev)
  61. assert_array_equal(analit.ngev+approx.ngev, ngev)
  62. f_approx = approx.fun(x)
  63. g_approx = approx.grad(x)
  64. nfev += 3
  65. ngev += 1
  66. assert_array_equal(ex.nfev, nfev)
  67. assert_array_equal(analit.nfev+approx.nfev, nfev)
  68. assert_array_equal(analit.ngev+approx.ngev, ngev)
  69. assert_array_almost_equal(f_analit, f_approx)
  70. assert_array_almost_equal(g_analit, g_approx)
  71. x = [2.0, 1.0]
  72. g_analit = analit.grad(x)
  73. ngev += 1
  74. assert_array_equal(ex.nfev, nfev)
  75. assert_array_equal(analit.nfev+approx.nfev, nfev)
  76. assert_array_equal(analit.ngev+approx.ngev, ngev)
  77. g_approx = approx.grad(x)
  78. nfev += 3
  79. ngev += 1
  80. assert_array_equal(ex.nfev, nfev)
  81. assert_array_equal(analit.nfev+approx.nfev, nfev)
  82. assert_array_equal(analit.ngev+approx.ngev, ngev)
  83. assert_array_almost_equal(g_analit, g_approx)
  84. x = [2.5, 0.3]
  85. f_analit = analit.fun(x)
  86. g_analit = analit.grad(x)
  87. nfev += 1
  88. ngev += 1
  89. assert_array_equal(ex.nfev, nfev)
  90. assert_array_equal(analit.nfev+approx.nfev, nfev)
  91. assert_array_equal(analit.ngev+approx.ngev, ngev)
  92. f_approx = approx.fun(x)
  93. g_approx = approx.grad(x)
  94. nfev += 3
  95. ngev += 1
  96. assert_array_equal(ex.nfev, nfev)
  97. assert_array_equal(analit.nfev+approx.nfev, nfev)
  98. assert_array_equal(analit.ngev+approx.ngev, ngev)
  99. assert_array_almost_equal(f_analit, f_approx)
  100. assert_array_almost_equal(g_analit, g_approx)
  101. x = [2, 0.3]
  102. f_analit = analit.fun(x)
  103. g_analit = analit.grad(x)
  104. nfev += 1
  105. ngev += 1
  106. assert_array_equal(ex.nfev, nfev)
  107. assert_array_equal(analit.nfev+approx.nfev, nfev)
  108. assert_array_equal(analit.ngev+approx.ngev, ngev)
  109. f_approx = approx.fun(x)
  110. g_approx = approx.grad(x)
  111. nfev += 3
  112. ngev += 1
  113. assert_array_equal(ex.nfev, nfev)
  114. assert_array_equal(analit.nfev+approx.nfev, nfev)
  115. assert_array_equal(analit.ngev+approx.ngev, ngev)
  116. assert_array_almost_equal(f_analit, f_approx)
  117. assert_array_almost_equal(g_analit, g_approx)
  118. @pytest.mark.fail_slow(5.0)
  119. def test_workers(self):
  120. x0 = np.array([2.0, 0.3])
  121. ex = ExScalarFunction()
  122. ex2 = ExScalarFunction()
  123. with MapWrapper(2) as mapper:
  124. approx = ScalarFunction(ex.fun, x0, (), '2-point',
  125. ex.hess, None, (-np.inf, np.inf),
  126. workers=mapper)
  127. approx_series = ScalarFunction(ex2.fun, x0, (), '2-point',
  128. ex2.hess, None, (-np.inf, np.inf),
  129. )
  130. assert_allclose(approx.grad(x0), ex.grad(x0))
  131. assert_allclose(approx_series.grad(x0), ex.grad(x0))
  132. assert_allclose(approx_series.hess(x0), ex.hess(x0))
  133. assert_allclose(approx.hess(x0), ex.hess(x0))
  134. assert_equal(approx.nfev, approx_series.nfev)
  135. assert_equal(approx_series.nfev, ex2.nfev)
  136. assert_equal(approx.ngev, approx_series.ngev)
  137. assert_equal(approx.nhev, approx_series.nhev)
  138. assert_equal(approx_series.nhev, ex2.nhev)
  139. ex = ExScalarFunction()
  140. ex2 = ExScalarFunction()
  141. approx = ScalarFunction(ex.fun, x0, (), '3-point',
  142. ex.hess, None, (-np.inf, np.inf),
  143. workers=mapper)
  144. approx_series = ScalarFunction(ex2.fun, x0, (), '3-point',
  145. ex2.hess, None, (-np.inf, np.inf),
  146. )
  147. assert_allclose(approx.grad(x0), ex.grad(x0))
  148. assert_allclose(approx_series.grad(x0), ex.grad(x0))
  149. assert_allclose(approx_series.hess(x0), ex.hess(x0))
  150. assert_allclose(approx.hess(x0), ex.hess(x0))
  151. assert_equal(approx.nfev, approx_series.nfev)
  152. assert_equal(approx_series.nfev, ex2.nfev)
  153. assert_equal(approx.ngev, approx_series.ngev)
  154. assert_equal(approx.nhev, approx_series.nhev)
  155. assert_equal(approx_series.nhev, ex2.nhev)
  156. ex = ExScalarFunction()
  157. ex2 = ExScalarFunction()
  158. x1 = np.array([3.0, 4.0])
  159. approx = ScalarFunction(ex.fun, x0, (), ex.grad,
  160. '3-point', None, (-np.inf, np.inf),
  161. workers=mapper)
  162. approx_series = ScalarFunction(ex2.fun, x0, (), ex2.grad,
  163. '3-point', None, (-np.inf, np.inf),
  164. )
  165. assert_allclose(approx.grad(x1), ex.grad(x1))
  166. assert_allclose(approx_series.grad(x1), ex.grad(x1))
  167. approx_series.hess(x1)
  168. approx.hess(x1)
  169. assert_equal(approx.nfev, approx_series.nfev)
  170. assert_equal(approx_series.nfev, ex2.nfev)
  171. assert_equal(approx.ngev, approx_series.ngev)
  172. assert_equal(approx_series.ngev, ex2.ngev)
  173. assert_equal(approx.nhev, approx_series.nhev)
  174. assert_equal(approx_series.nhev, ex2.nhev)
  175. def test_fun_and_grad(self):
  176. ex = ExScalarFunction()
  177. def fg_allclose(x, y):
  178. assert_allclose(x[0], y[0])
  179. assert_allclose(x[1], y[1])
  180. # with analytic gradient
  181. x0 = [2.0, 0.3]
  182. analit = ScalarFunction(ex.fun, x0, (), ex.grad,
  183. ex.hess, None, (-np.inf, np.inf))
  184. fg = ex.fun(x0), ex.grad(x0)
  185. fg_allclose(analit.fun_and_grad(x0), fg)
  186. assert analit.ngev == 1
  187. x0[1] = 1.
  188. fg = ex.fun(x0), ex.grad(x0)
  189. fg_allclose(analit.fun_and_grad(x0), fg)
  190. # with finite difference gradient
  191. x0 = [2.0, 0.3]
  192. sf = ScalarFunction(ex.fun, x0, (), '3-point',
  193. ex.hess, None, (-np.inf, np.inf))
  194. assert sf.ngev == 1
  195. fg = ex.fun(x0), ex.grad(x0)
  196. fg_allclose(sf.fun_and_grad(x0), fg)
  197. assert sf.ngev == 1
  198. x0[1] = 1.
  199. fg = ex.fun(x0), ex.grad(x0)
  200. fg_allclose(sf.fun_and_grad(x0), fg)
  201. def test_finite_difference_hess_linear_operator(self):
  202. ex = ExScalarFunction()
  203. nfev = 0
  204. ngev = 0
  205. nhev = 0
  206. x0 = [1.0, 0.0]
  207. analit = ScalarFunction(ex.fun, x0, (), ex.grad,
  208. ex.hess, None, (-np.inf, np.inf))
  209. nfev += 1
  210. ngev += 1
  211. nhev += 1
  212. assert_array_equal(ex.nfev, nfev)
  213. assert_array_equal(analit.nfev, nfev)
  214. assert_array_equal(ex.ngev, ngev)
  215. assert_array_equal(analit.ngev, ngev)
  216. assert_array_equal(ex.nhev, nhev)
  217. assert_array_equal(analit.nhev, nhev)
  218. approx = ScalarFunction(ex.fun, x0, (), ex.grad,
  219. '2-point', None, (-np.inf, np.inf))
  220. assert_(isinstance(approx.H, LinearOperator))
  221. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  222. assert_array_equal(analit.f, approx.f)
  223. assert_array_almost_equal(analit.g, approx.g)
  224. assert_array_almost_equal(analit.H.dot(v), approx.H.dot(v))
  225. nfev += 1
  226. ngev += 4
  227. assert_array_equal(ex.nfev, nfev)
  228. assert_array_equal(analit.nfev+approx.nfev, nfev)
  229. assert_array_equal(ex.ngev, ngev)
  230. assert_array_equal(analit.ngev+approx.ngev, ngev)
  231. assert_array_equal(ex.nhev, nhev)
  232. assert_array_equal(analit.nhev+approx.nhev, nhev)
  233. x = [2.0, 1.0]
  234. H_analit = analit.hess(x)
  235. nhev += 1
  236. assert_array_equal(ex.nfev, nfev)
  237. assert_array_equal(analit.nfev+approx.nfev, nfev)
  238. assert_array_equal(ex.ngev, ngev)
  239. assert_array_equal(analit.ngev+approx.ngev, ngev)
  240. assert_array_equal(ex.nhev, nhev)
  241. assert_array_equal(analit.nhev+approx.nhev, nhev)
  242. H_approx = approx.hess(x)
  243. assert_(isinstance(H_approx, LinearOperator))
  244. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  245. assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
  246. ngev += 4
  247. assert_array_equal(ex.nfev, nfev)
  248. assert_array_equal(analit.nfev+approx.nfev, nfev)
  249. assert_array_equal(ex.ngev, ngev)
  250. assert_array_equal(analit.ngev+approx.ngev, ngev)
  251. assert_array_equal(ex.nhev, nhev)
  252. assert_array_equal(analit.nhev+approx.nhev, nhev)
  253. x = [2.1, 1.2]
  254. H_analit = analit.hess(x)
  255. nhev += 1
  256. assert_array_equal(ex.nfev, nfev)
  257. assert_array_equal(analit.nfev+approx.nfev, nfev)
  258. assert_array_equal(ex.ngev, ngev)
  259. assert_array_equal(analit.ngev+approx.ngev, ngev)
  260. assert_array_equal(ex.nhev, nhev)
  261. assert_array_equal(analit.nhev+approx.nhev, nhev)
  262. H_approx = approx.hess(x)
  263. assert_(isinstance(H_approx, LinearOperator))
  264. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  265. assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
  266. ngev += 4
  267. assert_array_equal(ex.nfev, nfev)
  268. assert_array_equal(analit.nfev+approx.nfev, nfev)
  269. assert_array_equal(ex.ngev, ngev)
  270. assert_array_equal(analit.ngev+approx.ngev, ngev)
  271. assert_array_equal(ex.nhev, nhev)
  272. assert_array_equal(analit.nhev+approx.nhev, nhev)
  273. x = [2.5, 0.3]
  274. _ = analit.grad(x)
  275. H_analit = analit.hess(x)
  276. ngev += 1
  277. nhev += 1
  278. assert_array_equal(ex.nfev, nfev)
  279. assert_array_equal(analit.nfev+approx.nfev, nfev)
  280. assert_array_equal(ex.ngev, ngev)
  281. assert_array_equal(analit.ngev+approx.ngev, ngev)
  282. assert_array_equal(ex.nhev, nhev)
  283. assert_array_equal(analit.nhev+approx.nhev, nhev)
  284. _ = approx.grad(x)
  285. H_approx = approx.hess(x)
  286. assert_(isinstance(H_approx, LinearOperator))
  287. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  288. assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
  289. ngev += 4
  290. assert_array_equal(ex.nfev, nfev)
  291. assert_array_equal(analit.nfev+approx.nfev, nfev)
  292. assert_array_equal(ex.ngev, ngev)
  293. assert_array_equal(analit.ngev+approx.ngev, ngev)
  294. assert_array_equal(ex.nhev, nhev)
  295. assert_array_equal(analit.nhev+approx.nhev, nhev)
  296. x = [5.2, 2.3]
  297. _ = analit.grad(x)
  298. H_analit = analit.hess(x)
  299. ngev += 1
  300. nhev += 1
  301. assert_array_equal(ex.nfev, nfev)
  302. assert_array_equal(analit.nfev+approx.nfev, nfev)
  303. assert_array_equal(ex.ngev, ngev)
  304. assert_array_equal(analit.ngev+approx.ngev, ngev)
  305. assert_array_equal(ex.nhev, nhev)
  306. assert_array_equal(analit.nhev+approx.nhev, nhev)
  307. _ = approx.grad(x)
  308. H_approx = approx.hess(x)
  309. assert_(isinstance(H_approx, LinearOperator))
  310. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  311. assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
  312. ngev += 4
  313. assert_array_equal(ex.nfev, nfev)
  314. assert_array_equal(analit.nfev+approx.nfev, nfev)
  315. assert_array_equal(ex.ngev, ngev)
  316. assert_array_equal(analit.ngev+approx.ngev, ngev)
  317. assert_array_equal(ex.nhev, nhev)
  318. assert_array_equal(analit.nhev+approx.nhev, nhev)
  319. def test_x_storage_overlap(self):
  320. # Scalar_Function should not store references to arrays, it should
  321. # store copies - this checks that updating an array in-place causes
  322. # Scalar_Function.x to be updated.
  323. def f(x):
  324. return np.sum(np.asarray(x) ** 2)
  325. x = np.array([1., 2., 3.])
  326. sf = ScalarFunction(f, x, (), '3-point', lambda x: x, None, (-np.inf, np.inf))
  327. assert x is not sf.x
  328. assert_equal(sf.fun(x), 14.0)
  329. assert x is not sf.x
  330. x[0] = 0.
  331. f1 = sf.fun(x)
  332. assert_equal(f1, 13.0)
  333. x[0] = 1
  334. f2 = sf.fun(x)
  335. assert_equal(f2, 14.0)
  336. assert x is not sf.x
  337. # now test with a HessianUpdate strategy specified
  338. hess = BFGS()
  339. x = np.array([1., 2., 3.])
  340. sf = ScalarFunction(f, x, (), '3-point', hess, None, (-np.inf, np.inf))
  341. assert x is not sf.x
  342. assert_equal(sf.fun(x), 14.0)
  343. assert x is not sf.x
  344. x[0] = 0.
  345. f1 = sf.fun(x)
  346. assert_equal(f1, 13.0)
  347. x[0] = 1
  348. f2 = sf.fun(x)
  349. assert_equal(f2, 14.0)
  350. assert x is not sf.x
  351. # gh13740 x is changed in user function
  352. def ff(x):
  353. x *= x # overwrite x
  354. return np.sum(x)
  355. x = np.array([1., 2., 3.])
  356. sf = ScalarFunction(
  357. ff, x, (), '3-point', lambda x: x, None, (-np.inf, np.inf)
  358. )
  359. assert x is not sf.x
  360. assert_equal(sf.fun(x), 14.0)
  361. assert_equal(sf.x, np.array([1., 2., 3.]))
  362. assert x is not sf.x
  363. def test_lowest_x(self):
  364. # ScalarFunction should remember the lowest func(x) visited.
  365. x0 = np.array([2, 3, 4])
  366. sf = ScalarFunction(rosen, x0, (), rosen_der, rosen_hess,
  367. None, None)
  368. sf.fun([1, 1, 1])
  369. sf.fun(x0)
  370. sf.fun([1.01, 1, 1.0])
  371. sf.grad([1.01, 1, 1.0])
  372. assert_equal(sf._lowest_f, 0.0)
  373. assert_equal(sf._lowest_x, [1.0, 1.0, 1.0])
  374. sf = ScalarFunction(rosen, x0, (), '2-point', rosen_hess,
  375. None, (-np.inf, np.inf))
  376. sf.fun([1, 1, 1])
  377. sf.fun(x0)
  378. sf.fun([1.01, 1, 1.0])
  379. sf.grad([1.01, 1, 1.0])
  380. assert_equal(sf._lowest_f, 0.0)
  381. assert_equal(sf._lowest_x, [1.0, 1.0, 1.0])
  382. def test_float_size(self):
  383. x0 = np.array([2, 3, 4]).astype(np.float32)
  384. # check that ScalarFunction/approx_derivative always send the correct
  385. # float width
  386. def rosen_(x):
  387. assert x.dtype == np.float32
  388. return rosen(x)
  389. sf = ScalarFunction(rosen_, x0, (), '2-point', rosen_hess,
  390. None, (-np.inf, np.inf))
  391. res = sf.fun(x0)
  392. assert res.dtype == np.float32
  393. class ExVectorialFunction:
  394. def __init__(self):
  395. self.nfev = 0
  396. self.njev = 0
  397. self.nhev = 0
  398. def fun(self, x):
  399. self.nfev += 1
  400. return np.array([2*(x[0]**2 + x[1]**2 - 1) - x[0],
  401. 4*(x[0]**3 + x[1]**2 - 4) - 3*x[0]], dtype=x.dtype)
  402. def jac(self, x):
  403. self.njev += 1
  404. return np.array([[4*x[0]-1, 4*x[1]],
  405. [12*x[0]**2-3, 8*x[1]]], dtype=x.dtype)
  406. def hess(self, x, v):
  407. self.nhev += 1
  408. return v[0]*4*np.eye(2) + v[1]*np.array([[24*x[0], 0],
  409. [0, 8]])
  410. class TestVectorialFunction(TestCase):
  411. def test_finite_difference_jac(self):
  412. ex = ExVectorialFunction()
  413. nfev = 0
  414. njev = 0
  415. x0 = [1.0, 0.0]
  416. analit = VectorFunction(ex.fun, x0, ex.jac, ex.hess, None, None,
  417. (-np.inf, np.inf), None)
  418. nfev += 1
  419. njev += 1
  420. assert_array_equal(ex.nfev, nfev)
  421. assert_array_equal(analit.nfev, nfev)
  422. assert_array_equal(ex.njev, njev)
  423. assert_array_equal(analit.njev, njev)
  424. # create with defaults for the keyword arguments, to
  425. # ensure that the defaults work
  426. approx = VectorFunction(ex.fun, x0, '2-point', ex.hess)
  427. nfev += 3
  428. assert_array_equal(ex.nfev, nfev)
  429. assert_array_equal(analit.nfev+approx.nfev, nfev)
  430. assert_array_equal(ex.njev, njev)
  431. assert_array_equal(analit.njev+approx.njev, njev)
  432. assert_array_equal(analit.f, approx.f)
  433. assert_array_almost_equal(analit.J, approx.J)
  434. x = [10, 0.3]
  435. f_analit = analit.fun(x)
  436. J_analit = analit.jac(x)
  437. nfev += 1
  438. njev += 1
  439. assert_array_equal(ex.nfev, nfev)
  440. assert_array_equal(analit.nfev+approx.nfev, nfev)
  441. assert_array_equal(ex.njev, njev)
  442. assert_array_equal(analit.njev+approx.njev, njev)
  443. f_approx = approx.fun(x)
  444. J_approx = approx.jac(x)
  445. nfev += 3
  446. assert_array_equal(ex.nfev, nfev)
  447. assert_array_equal(analit.nfev+approx.nfev, nfev)
  448. assert_array_equal(ex.njev, njev)
  449. assert_array_equal(analit.njev+approx.njev, njev)
  450. assert_array_almost_equal(f_analit, f_approx)
  451. assert_array_almost_equal(J_analit, J_approx, decimal=4)
  452. x = [2.0, 1.0]
  453. J_analit = analit.jac(x)
  454. njev += 1
  455. assert_array_equal(ex.nfev, nfev)
  456. assert_array_equal(analit.nfev+approx.nfev, nfev)
  457. assert_array_equal(ex.njev, njev)
  458. assert_array_equal(analit.njev+approx.njev, njev)
  459. J_approx = approx.jac(x)
  460. nfev += 3
  461. assert_array_equal(ex.nfev, nfev)
  462. assert_array_equal(analit.nfev+approx.nfev, nfev)
  463. assert_array_equal(ex.njev, njev)
  464. assert_array_equal(analit.njev+approx.njev, njev)
  465. assert_array_almost_equal(J_analit, J_approx)
  466. x = [2.5, 0.3]
  467. f_analit = analit.fun(x)
  468. J_analit = analit.jac(x)
  469. nfev += 1
  470. njev += 1
  471. assert_array_equal(ex.nfev, nfev)
  472. assert_array_equal(analit.nfev+approx.nfev, nfev)
  473. assert_array_equal(ex.njev, njev)
  474. assert_array_equal(analit.njev+approx.njev, njev)
  475. f_approx = approx.fun(x)
  476. J_approx = approx.jac(x)
  477. nfev += 3
  478. assert_array_equal(ex.nfev, nfev)
  479. assert_array_equal(analit.nfev+approx.nfev, nfev)
  480. assert_array_equal(ex.njev, njev)
  481. assert_array_equal(analit.njev+approx.njev, njev)
  482. assert_array_almost_equal(f_analit, f_approx)
  483. assert_array_almost_equal(J_analit, J_approx)
  484. x = [2, 0.3]
  485. f_analit = analit.fun(x)
  486. J_analit = analit.jac(x)
  487. nfev += 1
  488. njev += 1
  489. assert_array_equal(ex.nfev, nfev)
  490. assert_array_equal(analit.nfev+approx.nfev, nfev)
  491. assert_array_equal(ex.njev, njev)
  492. assert_array_equal(analit.njev+approx.njev, njev)
  493. f_approx = approx.fun(x)
  494. J_approx = approx.jac(x)
  495. nfev += 3
  496. assert_array_equal(ex.nfev, nfev)
  497. assert_array_equal(analit.nfev+approx.nfev, nfev)
  498. assert_array_equal(ex.njev, njev)
  499. assert_array_equal(analit.njev+approx.njev, njev)
  500. assert_array_almost_equal(f_analit, f_approx)
  501. assert_array_almost_equal(J_analit, J_approx)
  502. def test_updating_on_initial_setup(self):
  503. # Check that memoisation works with the freshly created VectorFunction
  504. # On initialization vf.f_updated attribute wasn't being set correctly.
  505. x0 = np.array([2.5, 3.0])
  506. ex = ExVectorialFunction()
  507. vf = VectorFunction(ex.fun, x0, ex.jac, ex.hess)
  508. assert vf.f_updated
  509. assert vf.nfev == 1
  510. assert vf.njev == 1
  511. assert ex.nfev == 1
  512. assert ex.njev == 1
  513. vf.fun(x0)
  514. vf.jac(x0)
  515. assert vf.nfev == 1
  516. assert vf.njev == 1
  517. assert ex.nfev == 1
  518. assert ex.njev == 1
  519. @pytest.mark.fail_slow(5.0)
  520. def test_workers(self):
  521. x0 = np.array([2.5, 3.0])
  522. ex = ExVectorialFunction()
  523. ex2 = ExVectorialFunction()
  524. v = np.array([1.0, 2.0])
  525. with MapWrapper(2) as mapper:
  526. approx = VectorFunction(ex.fun, x0, '2-point',
  527. ex.hess, None, None, (-np.inf, np.inf),
  528. False, workers=mapper)
  529. approx_series = VectorFunction(ex2.fun, x0, '2-point',
  530. ex2.hess, None, None, (-np.inf, np.inf),
  531. False)
  532. assert_allclose(approx.jac(x0), ex.jac(x0))
  533. assert_allclose(approx_series.jac(x0), ex.jac(x0))
  534. assert_allclose(approx_series.hess(x0, v), ex.hess(x0, v))
  535. assert_allclose(approx.hess(x0, v), ex.hess(x0, v))
  536. assert_equal(approx.nfev, approx_series.nfev)
  537. assert_equal(approx_series.nfev, ex2.nfev)
  538. assert_equal(approx.njev, approx_series.njev)
  539. assert_equal(approx.nhev, approx_series.nhev)
  540. assert_equal(approx_series.nhev, ex2.nhev)
  541. ex.nfev = ex.njev = ex.nhev = 0
  542. ex2.nfev = ex2.njev = ex2.nhev = 0
  543. approx = VectorFunction(ex.fun, x0, '3-point',
  544. ex.hess, None, None, (-np.inf, np.inf),
  545. False, workers=mapper)
  546. approx_series = VectorFunction(ex2.fun, x0, '3-point',
  547. ex2.hess, None, None, (-np.inf, np.inf),
  548. False)
  549. assert_allclose(approx.jac(x0), ex.jac(x0))
  550. assert_allclose(approx_series.jac(x0), ex.jac(x0))
  551. assert_allclose(approx_series.hess(x0, v), ex.hess(x0, v))
  552. assert_allclose(approx.hess(x0, v), ex.hess(x0, v))
  553. assert_equal(approx.nfev, approx_series.nfev)
  554. assert_equal(approx_series.nfev, ex2.nfev)
  555. assert_equal(approx.njev, approx_series.njev)
  556. assert_equal(approx.nhev, approx_series.nhev)
  557. assert_equal(approx_series.nhev, ex2.nhev)
  558. # The following tests are somewhat redundant because the LinearOperator
  559. # produced by VectorFunction.hess does not use any parallelisation.
  560. # The tests are left for completeness, in case that situation changes.
  561. ex.nfev = ex.njev = ex.nhev = 0
  562. ex2.nfev = ex2.njev = ex2.nhev = 0
  563. approx = VectorFunction(ex.fun, x0, ex.jac,
  564. '2-point', None, None, (-np.inf, np.inf),
  565. False, workers=mapper)
  566. approx_series = VectorFunction(ex2.fun, x0, ex2.jac,
  567. '2-point', None, None, (-np.inf, np.inf),
  568. False)
  569. assert_allclose(approx.jac(x0), ex.jac(x0))
  570. assert_allclose(approx_series.jac(x0), ex.jac(x0))
  571. H_analit = ex2.hess(x0, v)
  572. H_approx_series = approx_series.hess(x0, v)
  573. H_approx = approx.hess(x0, v)
  574. x = [5, 2.0]
  575. assert_allclose(H_approx.dot(x), H_analit.dot(x))
  576. assert_allclose(H_approx_series.dot(x), H_analit.dot(x))
  577. assert_equal(approx.nfev, approx_series.nfev)
  578. assert_equal(approx_series.nfev, ex2.nfev)
  579. assert_equal(approx.njev, approx_series.njev)
  580. assert_equal(approx.nhev, approx_series.nhev)
  581. def test_finite_difference_hess_linear_operator(self):
  582. ex = ExVectorialFunction()
  583. nfev = 0
  584. njev = 0
  585. nhev = 0
  586. x0 = [1.0, 0.0]
  587. v0 = [1.0, 2.0]
  588. analit = VectorFunction(ex.fun, x0, ex.jac, ex.hess, None, None,
  589. (-np.inf, np.inf), None)
  590. nfev += 1
  591. njev += 1
  592. nhev += 1
  593. assert_array_equal(ex.nfev, nfev)
  594. assert_array_equal(analit.nfev, nfev)
  595. assert_array_equal(ex.njev, njev)
  596. assert_array_equal(analit.njev, njev)
  597. assert_array_equal(ex.nhev, nhev)
  598. assert_array_equal(analit.nhev, nhev)
  599. approx = VectorFunction(ex.fun, x0, ex.jac, '2-point', None, None,
  600. (-np.inf, np.inf), None)
  601. assert_(isinstance(approx.H, LinearOperator))
  602. for p in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  603. assert_array_equal(analit.f, approx.f)
  604. assert_array_almost_equal(analit.J, approx.J)
  605. assert_array_almost_equal(analit.H.dot(p), approx.H.dot(p))
  606. nfev += 1
  607. njev += 4
  608. assert_array_equal(ex.nfev, nfev)
  609. assert_array_equal(analit.nfev+approx.nfev, nfev)
  610. assert_array_equal(ex.njev, njev)
  611. assert_array_equal(analit.njev+approx.njev, njev)
  612. assert_array_equal(ex.nhev, nhev)
  613. assert_array_equal(analit.nhev+approx.nhev, nhev)
  614. x = [2.0, 1.0]
  615. H_analit = analit.hess(x, v0)
  616. nhev += 1
  617. assert_array_equal(ex.nfev, nfev)
  618. assert_array_equal(analit.nfev+approx.nfev, nfev)
  619. assert_array_equal(ex.njev, njev)
  620. assert_array_equal(analit.njev+approx.njev, njev)
  621. assert_array_equal(ex.nhev, nhev)
  622. assert_array_equal(analit.nhev+approx.nhev, nhev)
  623. H_approx = approx.hess(x, v0)
  624. assert_(isinstance(H_approx, LinearOperator))
  625. for p in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  626. assert_array_almost_equal(H_analit.dot(p), H_approx.dot(p),
  627. decimal=5)
  628. njev += 4
  629. assert_array_equal(ex.nfev, nfev)
  630. assert_array_equal(analit.nfev+approx.nfev, nfev)
  631. assert_array_equal(ex.njev, njev)
  632. assert_array_equal(analit.njev+approx.njev, njev)
  633. assert_array_equal(ex.nhev, nhev)
  634. assert_array_equal(analit.nhev+approx.nhev, nhev)
  635. x = [2.1, 1.2]
  636. v = [1.0, 1.0]
  637. H_analit = analit.hess(x, v)
  638. nhev += 1
  639. assert_array_equal(ex.nfev, nfev)
  640. assert_array_equal(analit.nfev+approx.nfev, nfev)
  641. assert_array_equal(ex.njev, njev)
  642. assert_array_equal(analit.njev+approx.njev, njev)
  643. assert_array_equal(ex.nhev, nhev)
  644. assert_array_equal(analit.nhev+approx.nhev, nhev)
  645. H_approx = approx.hess(x, v)
  646. assert_(isinstance(H_approx, LinearOperator))
  647. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  648. assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v))
  649. njev += 4
  650. assert_array_equal(ex.nfev, nfev)
  651. assert_array_equal(analit.nfev+approx.nfev, nfev)
  652. assert_array_equal(ex.njev, njev)
  653. assert_array_equal(analit.njev+approx.njev, njev)
  654. assert_array_equal(ex.nhev, nhev)
  655. assert_array_equal(analit.nhev+approx.nhev, nhev)
  656. x = [2.5, 0.3]
  657. _ = analit.jac(x)
  658. H_analit = analit.hess(x, v0)
  659. njev += 1
  660. nhev += 1
  661. assert_array_equal(ex.nfev, nfev)
  662. assert_array_equal(analit.nfev+approx.nfev, nfev)
  663. assert_array_equal(ex.njev, njev)
  664. assert_array_equal(analit.njev+approx.njev, njev)
  665. assert_array_equal(ex.nhev, nhev)
  666. assert_array_equal(analit.nhev+approx.nhev, nhev)
  667. _ = approx.jac(x)
  668. H_approx = approx.hess(x, v0)
  669. assert_(isinstance(H_approx, LinearOperator))
  670. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  671. assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v), decimal=4)
  672. njev += 4
  673. assert_array_equal(ex.nfev, nfev)
  674. assert_array_equal(analit.nfev+approx.nfev, nfev)
  675. assert_array_equal(ex.njev, njev)
  676. assert_array_equal(analit.njev+approx.njev, njev)
  677. assert_array_equal(ex.nhev, nhev)
  678. assert_array_equal(analit.nhev+approx.nhev, nhev)
  679. x = [5.2, 2.3]
  680. v = [2.3, 5.2]
  681. _ = analit.jac(x)
  682. H_analit = analit.hess(x, v)
  683. njev += 1
  684. nhev += 1
  685. assert_array_equal(ex.nfev, nfev)
  686. assert_array_equal(analit.nfev+approx.nfev, nfev)
  687. assert_array_equal(ex.njev, njev)
  688. assert_array_equal(analit.njev+approx.njev, njev)
  689. assert_array_equal(ex.nhev, nhev)
  690. assert_array_equal(analit.nhev+approx.nhev, nhev)
  691. _ = approx.jac(x)
  692. H_approx = approx.hess(x, v)
  693. assert_(isinstance(H_approx, LinearOperator))
  694. for v in ([1.0, 2.0], [3.0, 4.0], [5.0, 2.0]):
  695. assert_array_almost_equal(H_analit.dot(v), H_approx.dot(v), decimal=4)
  696. njev += 4
  697. assert_array_equal(ex.nfev, nfev)
  698. assert_array_equal(analit.nfev+approx.nfev, nfev)
  699. assert_array_equal(ex.njev, njev)
  700. assert_array_equal(analit.njev+approx.njev, njev)
  701. assert_array_equal(ex.nhev, nhev)
  702. assert_array_equal(analit.nhev+approx.nhev, nhev)
  703. # Test VectorFunction.hess_wrapped with J0=None
  704. x = np.array([1.5, 0.5])
  705. v = np.array([1.0, 2.0])
  706. njev_before = approx.hess_wrapped.njev
  707. H = approx.hess_wrapped(x, v, J0=None)
  708. assert isinstance(H, LinearOperator)
  709. # The njev counter should be incremented by exactly 1
  710. assert approx.hess_wrapped.njev == njev_before + 1
  711. def test_fgh_overlap(self):
  712. # VectorFunction.fun/jac should return copies to internal attributes
  713. ex = ExVectorialFunction()
  714. x0 = np.array([1.0, 0.0])
  715. vf = VectorFunction(ex.fun, x0, '3-point', ex.hess, None, None,
  716. (-np.inf, np.inf), None)
  717. f = vf.fun(np.array([1.1, 0.1]))
  718. J = vf.jac([1.1, 0.1])
  719. assert vf.f is not f
  720. assert vf.J is not J
  721. assert_equal(f, vf.f)
  722. assert_equal(J, vf.J)
  723. vf = VectorFunction(ex.fun, x0, ex.jac, ex.hess, None, None,
  724. (-np.inf, np.inf), None)
  725. f = vf.fun(np.array([1.1, 0.1]))
  726. J = vf.jac([1.1, 0.1])
  727. assert vf.f is not f
  728. assert vf.J is not J
  729. assert_equal(f, vf.f)
  730. assert_equal(J, vf.J)
  731. def test_x_storage_overlap(self):
  732. # VectorFunction should not store references to arrays, it should
  733. # store copies - this checks that updating an array in-place causes
  734. # Scalar_Function.x to be updated.
  735. ex = ExVectorialFunction()
  736. x0 = np.array([1.0, 0.0])
  737. vf = VectorFunction(ex.fun, x0, '3-point', ex.hess, None, None,
  738. (-np.inf, np.inf), None)
  739. assert x0 is not vf.x
  740. assert_equal(vf.fun(x0), ex.fun(x0))
  741. assert x0 is not vf.x
  742. x0[0] = 2.
  743. assert_equal(vf.fun(x0), ex.fun(x0))
  744. assert x0 is not vf.x
  745. x0[0] = 1.
  746. assert_equal(vf.fun(x0), ex.fun(x0))
  747. assert x0 is not vf.x
  748. # now test with a HessianUpdate strategy specified
  749. hess = BFGS()
  750. x0 = np.array([1.0, 0.0])
  751. vf = VectorFunction(ex.fun, x0, '3-point', hess, None, None,
  752. (-np.inf, np.inf), None)
  753. with pytest.warns(UserWarning):
  754. # filter UserWarning because ExVectorialFunction is linear and
  755. # a quasi-Newton approximation is used for the Hessian.
  756. assert x0 is not vf.x
  757. assert_equal(vf.fun(x0), ex.fun(x0))
  758. assert x0 is not vf.x
  759. x0[0] = 2.
  760. assert_equal(vf.fun(x0), ex.fun(x0))
  761. assert x0 is not vf.x
  762. x0[0] = 1.
  763. assert_equal(vf.fun(x0), ex.fun(x0))
  764. assert x0 is not vf.x
  765. def test_float_size(self):
  766. ex = ExVectorialFunction()
  767. x0 = np.array([1.0, 0.0]).astype(np.float32)
  768. vf = VectorFunction(ex.fun, x0, ex.jac, ex.hess, None, None,
  769. (-np.inf, np.inf), None)
  770. res = vf.fun(x0)
  771. assert res.dtype == np.float32
  772. res = vf.jac(x0)
  773. assert res.dtype == np.float32
  774. def test_sparse_analytic_jac(self):
  775. ex = ExVectorialFunction()
  776. x0 = np.array([1.0, 0.0])
  777. def sparse_adapter(func):
  778. def inner(x):
  779. f_x = func(x)
  780. return csr_array(f_x)
  781. return inner
  782. # jac(x) returns dense jacobian
  783. vf1 = VectorFunction(ex.fun, x0, ex.jac, ex.hess, None, None,
  784. (-np.inf, np.inf), sparse_jacobian=None)
  785. # jac(x) returns sparse jacobian, but sparse_jacobian=False requests dense
  786. vf2 = VectorFunction(ex.fun, x0, sparse_adapter(ex.jac), ex.hess, None, None,
  787. (-np.inf, np.inf), sparse_jacobian=False)
  788. res1 = vf1.jac(x0 + 1)
  789. res2 = vf2.jac(x0 + 1)
  790. assert_equal(res1, res2)
  791. def test_sparse_numerical_jac(self):
  792. ex = ExVectorialFunction()
  793. x0 = np.array([1.0, 0.0])
  794. N = len(x0)
  795. # normal dense numerical difference
  796. vf1 = VectorFunction(ex.fun, x0, '2-point', ex.hess, None, None,
  797. (-np.inf, np.inf), sparse_jacobian=None)
  798. # use sparse numerical difference, but ask it to be converted to dense
  799. finite_diff_jac_sparsity = csr_array(np.ones((N, N)))
  800. vf2 = VectorFunction(ex.fun, x0, '2-point', ex.hess, None,
  801. finite_diff_jac_sparsity, (-np.inf, np.inf),
  802. sparse_jacobian=False)
  803. res1 = vf1.jac(x0 + 1)
  804. res2 = vf2.jac(x0 + 1)
  805. assert_equal(res1, res2)
  806. def test_LinearVectorFunction():
  807. A_dense = np.array([
  808. [-1, 2, 0],
  809. [0, 4, 2]
  810. ])
  811. x0 = np.zeros(3)
  812. A_sparse = csr_array(A_dense)
  813. x = np.array([1, -1, 0])
  814. v = np.array([-1, 1])
  815. Ax = np.array([-3, -4])
  816. f1 = LinearVectorFunction(A_dense, x0, None)
  817. assert_(not f1.sparse_jacobian)
  818. f2 = LinearVectorFunction(A_dense, x0, True)
  819. assert_(f2.sparse_jacobian)
  820. f3 = LinearVectorFunction(A_dense, x0, False)
  821. assert_(not f3.sparse_jacobian)
  822. f4 = LinearVectorFunction(A_sparse, x0, None)
  823. assert_(f4.sparse_jacobian)
  824. f5 = LinearVectorFunction(A_sparse, x0, True)
  825. assert_(f5.sparse_jacobian)
  826. f6 = LinearVectorFunction(A_sparse, x0, False)
  827. assert_(not f6.sparse_jacobian)
  828. assert_array_equal(f1.fun(x), Ax)
  829. assert_array_equal(f2.fun(x), Ax)
  830. assert_array_equal(f1.jac(x), A_dense)
  831. assert_array_equal(f2.jac(x).toarray(), A_sparse.toarray())
  832. assert_array_equal(f1.hess(x, v).toarray(), np.zeros((3, 3)))
  833. def test_LinearVectorFunction_memoization():
  834. A = np.array([[-1, 2, 0], [0, 4, 2]])
  835. x0 = np.array([1, 2, -1])
  836. fun = LinearVectorFunction(A, x0, False)
  837. assert_array_equal(x0, fun.x)
  838. assert_array_equal(A.dot(x0), fun.f)
  839. x1 = np.array([-1, 3, 10])
  840. assert_array_equal(A, fun.jac(x1))
  841. assert_array_equal(x1, fun.x)
  842. assert_array_equal(A.dot(x0), fun.f)
  843. assert_array_equal(A.dot(x1), fun.fun(x1))
  844. assert_array_equal(A.dot(x1), fun.f)
  845. def test_IdentityVectorFunction():
  846. x0 = np.zeros(3)
  847. f1 = IdentityVectorFunction(x0, None)
  848. f2 = IdentityVectorFunction(x0, False)
  849. f3 = IdentityVectorFunction(x0, True)
  850. assert_(f1.sparse_jacobian)
  851. assert_(not f2.sparse_jacobian)
  852. assert_(f3.sparse_jacobian)
  853. x = np.array([-1, 2, 1])
  854. v = np.array([-2, 3, 0])
  855. assert_array_equal(f1.fun(x), x)
  856. assert_array_equal(f2.fun(x), x)
  857. assert_array_equal(f1.jac(x).toarray(), np.eye(3))
  858. assert_array_equal(f2.jac(x), np.eye(3))
  859. assert_array_equal(f1.hess(x, v).toarray(), np.zeros((3, 3)))
  860. @pytest.mark.skipif(
  861. platform.python_implementation() == "PyPy",
  862. reason="assert_deallocate not available on PyPy"
  863. )
  864. def test_ScalarFunctionNoReferenceCycle():
  865. """Regression test for gh-20768."""
  866. ex = ExScalarFunction()
  867. x0 = np.zeros(3)
  868. with assert_deallocated(lambda: ScalarFunction(ex.fun, x0, (), ex.grad,
  869. ex.hess, None, (-np.inf, np.inf))):
  870. pass
  871. @pytest.mark.skipif(
  872. platform.python_implementation() == "PyPy",
  873. reason="assert_deallocate not available on PyPy"
  874. )
  875. def test_VectorFunctionNoReferenceCycle():
  876. """Regression test for gh-20768."""
  877. ex = ExVectorialFunction()
  878. x0 = [1.0, 0.0]
  879. with assert_deallocated(lambda: VectorFunction(ex.fun, x0, ex.jac,
  880. ex.hess, None, None, (-np.inf, np.inf), None)):
  881. pass
  882. @pytest.mark.skipif(
  883. platform.python_implementation() == "PyPy",
  884. reason="assert_deallocate not available on PyPy"
  885. )
  886. def test_LinearVectorFunctionNoReferenceCycle():
  887. """Regression test for gh-20768."""
  888. A_dense = np.array([
  889. [-1, 2, 0],
  890. [0, 4, 2]
  891. ])
  892. x0 = np.zeros(3)
  893. A_sparse = csr_array(A_dense)
  894. with assert_deallocated(lambda: LinearVectorFunction(A_sparse, x0, None)):
  895. pass