test_bvp.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. from __future__ import division, print_function, absolute_import
  2. import sys
  3. try:
  4. from StringIO import StringIO
  5. except ImportError:
  6. from io import StringIO
  7. import numpy as np
  8. from numpy.testing import (assert_, assert_array_equal, assert_allclose,
  9. assert_equal)
  10. from pytest import raises as assert_raises
  11. from scipy.sparse import coo_matrix
  12. from scipy.special import erf
  13. from scipy.integrate._bvp import (modify_mesh, estimate_fun_jac,
  14. estimate_bc_jac, compute_jac_indices,
  15. construct_global_jac, solve_bvp)
  16. def exp_fun(x, y):
  17. return np.vstack((y[1], y[0]))
  18. def exp_fun_jac(x, y):
  19. df_dy = np.empty((2, 2, x.shape[0]))
  20. df_dy[0, 0] = 0
  21. df_dy[0, 1] = 1
  22. df_dy[1, 0] = 1
  23. df_dy[1, 1] = 0
  24. return df_dy
  25. def exp_bc(ya, yb):
  26. return np.hstack((ya[0] - 1, yb[0]))
  27. def exp_bc_complex(ya, yb):
  28. return np.hstack((ya[0] - 1 - 1j, yb[0]))
  29. def exp_bc_jac(ya, yb):
  30. dbc_dya = np.array([
  31. [1, 0],
  32. [0, 0]
  33. ])
  34. dbc_dyb = np.array([
  35. [0, 0],
  36. [1, 0]
  37. ])
  38. return dbc_dya, dbc_dyb
  39. def exp_sol(x):
  40. return (np.exp(-x) - np.exp(x - 2)) / (1 - np.exp(-2))
  41. def sl_fun(x, y, p):
  42. return np.vstack((y[1], -p[0]**2 * y[0]))
  43. def sl_fun_jac(x, y, p):
  44. n, m = y.shape
  45. df_dy = np.empty((n, 2, m))
  46. df_dy[0, 0] = 0
  47. df_dy[0, 1] = 1
  48. df_dy[1, 0] = -p[0]**2
  49. df_dy[1, 1] = 0
  50. df_dp = np.empty((n, 1, m))
  51. df_dp[0, 0] = 0
  52. df_dp[1, 0] = -2 * p[0] * y[0]
  53. return df_dy, df_dp
  54. def sl_bc(ya, yb, p):
  55. return np.hstack((ya[0], yb[0], ya[1] - p[0]))
  56. def sl_bc_jac(ya, yb, p):
  57. dbc_dya = np.zeros((3, 2))
  58. dbc_dya[0, 0] = 1
  59. dbc_dya[2, 1] = 1
  60. dbc_dyb = np.zeros((3, 2))
  61. dbc_dyb[1, 0] = 1
  62. dbc_dp = np.zeros((3, 1))
  63. dbc_dp[2, 0] = -1
  64. return dbc_dya, dbc_dyb, dbc_dp
  65. def sl_sol(x, p):
  66. return np.sin(p[0] * x)
  67. def emden_fun(x, y):
  68. return np.vstack((y[1], -y[0]**5))
  69. def emden_fun_jac(x, y):
  70. df_dy = np.empty((2, 2, x.shape[0]))
  71. df_dy[0, 0] = 0
  72. df_dy[0, 1] = 1
  73. df_dy[1, 0] = -5 * y[0]**4
  74. df_dy[1, 1] = 0
  75. return df_dy
  76. def emden_bc(ya, yb):
  77. return np.array([ya[1], yb[0] - (3/4)**0.5])
  78. def emden_bc_jac(ya, yb):
  79. dbc_dya = np.array([
  80. [0, 1],
  81. [0, 0]
  82. ])
  83. dbc_dyb = np.array([
  84. [0, 0],
  85. [1, 0]
  86. ])
  87. return dbc_dya, dbc_dyb
  88. def emden_sol(x):
  89. return (1 + x**2/3)**-0.5
  90. def undefined_fun(x, y):
  91. return np.zeros_like(y)
  92. def undefined_bc(ya, yb):
  93. return np.array([ya[0], yb[0] - 1])
  94. def big_fun(x, y):
  95. f = np.zeros_like(y)
  96. f[::2] = y[1::2]
  97. return f
  98. def big_bc(ya, yb):
  99. return np.hstack((ya[::2], yb[::2] - 1))
  100. def big_sol(x, n):
  101. y = np.ones((2 * n, x.size))
  102. y[::2] = x
  103. return x
  104. def shock_fun(x, y):
  105. eps = 1e-3
  106. return np.vstack((
  107. y[1],
  108. -(x * y[1] + eps * np.pi**2 * np.cos(np.pi * x) +
  109. np.pi * x * np.sin(np.pi * x)) / eps
  110. ))
  111. def shock_bc(ya, yb):
  112. return np.array([ya[0] + 2, yb[0]])
  113. def shock_sol(x):
  114. eps = 1e-3
  115. k = np.sqrt(2 * eps)
  116. return np.cos(np.pi * x) + erf(x / k) / erf(1 / k)
  117. def test_modify_mesh():
  118. x = np.array([0, 1, 3, 9], dtype=float)
  119. x_new = modify_mesh(x, np.array([0]), np.array([2]))
  120. assert_array_equal(x_new, np.array([0, 0.5, 1, 3, 5, 7, 9]))
  121. x = np.array([-6, -3, 0, 3, 6], dtype=float)
  122. x_new = modify_mesh(x, np.array([1], dtype=int), np.array([0, 2, 3]))
  123. assert_array_equal(x_new, [-6, -5, -4, -3, -1.5, 0, 1, 2, 3, 4, 5, 6])
  124. def test_compute_fun_jac():
  125. x = np.linspace(0, 1, 5)
  126. y = np.empty((2, x.shape[0]))
  127. y[0] = 0.01
  128. y[1] = 0.02
  129. p = np.array([])
  130. df_dy, df_dp = estimate_fun_jac(lambda x, y, p: exp_fun(x, y), x, y, p)
  131. df_dy_an = exp_fun_jac(x, y)
  132. assert_allclose(df_dy, df_dy_an)
  133. assert_(df_dp is None)
  134. x = np.linspace(0, np.pi, 5)
  135. y = np.empty((2, x.shape[0]))
  136. y[0] = np.sin(x)
  137. y[1] = np.cos(x)
  138. p = np.array([1.0])
  139. df_dy, df_dp = estimate_fun_jac(sl_fun, x, y, p)
  140. df_dy_an, df_dp_an = sl_fun_jac(x, y, p)
  141. assert_allclose(df_dy, df_dy_an)
  142. assert_allclose(df_dp, df_dp_an)
  143. x = np.linspace(0, 1, 10)
  144. y = np.empty((2, x.shape[0]))
  145. y[0] = (3/4)**0.5
  146. y[1] = 1e-4
  147. p = np.array([])
  148. df_dy, df_dp = estimate_fun_jac(lambda x, y, p: emden_fun(x, y), x, y, p)
  149. df_dy_an = emden_fun_jac(x, y)
  150. assert_allclose(df_dy, df_dy_an)
  151. assert_(df_dp is None)
  152. def test_compute_bc_jac():
  153. ya = np.array([-1.0, 2])
  154. yb = np.array([0.5, 3])
  155. p = np.array([])
  156. dbc_dya, dbc_dyb, dbc_dp = estimate_bc_jac(
  157. lambda ya, yb, p: exp_bc(ya, yb), ya, yb, p)
  158. dbc_dya_an, dbc_dyb_an = exp_bc_jac(ya, yb)
  159. assert_allclose(dbc_dya, dbc_dya_an)
  160. assert_allclose(dbc_dyb, dbc_dyb_an)
  161. assert_(dbc_dp is None)
  162. ya = np.array([0.0, 1])
  163. yb = np.array([0.0, -1])
  164. p = np.array([0.5])
  165. dbc_dya, dbc_dyb, dbc_dp = estimate_bc_jac(sl_bc, ya, yb, p)
  166. dbc_dya_an, dbc_dyb_an, dbc_dp_an = sl_bc_jac(ya, yb, p)
  167. assert_allclose(dbc_dya, dbc_dya_an)
  168. assert_allclose(dbc_dyb, dbc_dyb_an)
  169. assert_allclose(dbc_dp, dbc_dp_an)
  170. ya = np.array([0.5, 100])
  171. yb = np.array([-1000, 10.5])
  172. p = np.array([])
  173. dbc_dya, dbc_dyb, dbc_dp = estimate_bc_jac(
  174. lambda ya, yb, p: emden_bc(ya, yb), ya, yb, p)
  175. dbc_dya_an, dbc_dyb_an = emden_bc_jac(ya, yb)
  176. assert_allclose(dbc_dya, dbc_dya_an)
  177. assert_allclose(dbc_dyb, dbc_dyb_an)
  178. assert_(dbc_dp is None)
  179. def test_compute_jac_indices():
  180. n = 2
  181. m = 4
  182. k = 2
  183. i, j = compute_jac_indices(n, m, k)
  184. s = coo_matrix((np.ones_like(i), (i, j))).toarray()
  185. s_true = np.array([
  186. [1, 1, 1, 1, 0, 0, 0, 0, 1, 1],
  187. [1, 1, 1, 1, 0, 0, 0, 0, 1, 1],
  188. [0, 0, 1, 1, 1, 1, 0, 0, 1, 1],
  189. [0, 0, 1, 1, 1, 1, 0, 0, 1, 1],
  190. [0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
  191. [0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
  192. [1, 1, 0, 0, 0, 0, 1, 1, 1, 1],
  193. [1, 1, 0, 0, 0, 0, 1, 1, 1, 1],
  194. [1, 1, 0, 0, 0, 0, 1, 1, 1, 1],
  195. [1, 1, 0, 0, 0, 0, 1, 1, 1, 1],
  196. ])
  197. assert_array_equal(s, s_true)
  198. def test_compute_global_jac():
  199. n = 2
  200. m = 5
  201. k = 1
  202. i_jac, j_jac = compute_jac_indices(2, 5, 1)
  203. x = np.linspace(0, 1, 5)
  204. h = np.diff(x)
  205. y = np.vstack((np.sin(np.pi * x), np.pi * np.cos(np.pi * x)))
  206. p = np.array([3.0])
  207. f = sl_fun(x, y, p)
  208. x_middle = x[:-1] + 0.5 * h
  209. y_middle = 0.5 * (y[:, :-1] + y[:, 1:]) - h/8 * (f[:, 1:] - f[:, :-1])
  210. df_dy, df_dp = sl_fun_jac(x, y, p)
  211. df_dy_middle, df_dp_middle = sl_fun_jac(x_middle, y_middle, p)
  212. dbc_dya, dbc_dyb, dbc_dp = sl_bc_jac(y[:, 0], y[:, -1], p)
  213. J = construct_global_jac(n, m, k, i_jac, j_jac, h, df_dy, df_dy_middle,
  214. df_dp, df_dp_middle, dbc_dya, dbc_dyb, dbc_dp)
  215. J = J.toarray()
  216. def J_block(h, p):
  217. return np.array([
  218. [h**2*p**2/12 - 1, -0.5*h, -h**2*p**2/12 + 1, -0.5*h],
  219. [0.5*h*p**2, h**2*p**2/12 - 1, 0.5*h*p**2, 1 - h**2*p**2/12]
  220. ])
  221. J_true = np.zeros((m * n + k, m * n + k))
  222. for i in range(m - 1):
  223. J_true[i * n: (i + 1) * n, i * n: (i + 2) * n] = J_block(h[i], p)
  224. J_true[:(m - 1) * n:2, -1] = p * h**2/6 * (y[0, :-1] - y[0, 1:])
  225. J_true[1:(m - 1) * n:2, -1] = p * (h * (y[0, :-1] + y[0, 1:]) +
  226. h**2/6 * (y[1, :-1] - y[1, 1:]))
  227. J_true[8, 0] = 1
  228. J_true[9, 8] = 1
  229. J_true[10, 1] = 1
  230. J_true[10, 10] = -1
  231. assert_allclose(J, J_true, rtol=1e-10)
  232. df_dy, df_dp = estimate_fun_jac(sl_fun, x, y, p)
  233. df_dy_middle, df_dp_middle = estimate_fun_jac(sl_fun, x_middle, y_middle, p)
  234. dbc_dya, dbc_dyb, dbc_dp = estimate_bc_jac(sl_bc, y[:, 0], y[:, -1], p)
  235. J = construct_global_jac(n, m, k, i_jac, j_jac, h, df_dy, df_dy_middle,
  236. df_dp, df_dp_middle, dbc_dya, dbc_dyb, dbc_dp)
  237. J = J.toarray()
  238. assert_allclose(J, J_true, rtol=1e-8, atol=1e-9)
  239. def test_parameter_validation():
  240. x = [0, 1, 0.5]
  241. y = np.zeros((2, 3))
  242. assert_raises(ValueError, solve_bvp, exp_fun, exp_bc, x, y)
  243. x = np.linspace(0, 1, 5)
  244. y = np.zeros((2, 4))
  245. assert_raises(ValueError, solve_bvp, exp_fun, exp_bc, x, y)
  246. fun = lambda x, y, p: exp_fun(x, y)
  247. bc = lambda ya, yb, p: exp_bc(ya, yb)
  248. y = np.zeros((2, x.shape[0]))
  249. assert_raises(ValueError, solve_bvp, fun, bc, x, y, p=[1])
  250. def wrong_shape_fun(x, y):
  251. return np.zeros(3)
  252. assert_raises(ValueError, solve_bvp, wrong_shape_fun, bc, x, y)
  253. S = np.array([[0, 0]])
  254. assert_raises(ValueError, solve_bvp, exp_fun, exp_bc, x, y, S=S)
  255. def test_no_params():
  256. x = np.linspace(0, 1, 5)
  257. x_test = np.linspace(0, 1, 100)
  258. y = np.zeros((2, x.shape[0]))
  259. for fun_jac in [None, exp_fun_jac]:
  260. for bc_jac in [None, exp_bc_jac]:
  261. sol = solve_bvp(exp_fun, exp_bc, x, y, fun_jac=fun_jac,
  262. bc_jac=bc_jac)
  263. assert_equal(sol.status, 0)
  264. assert_(sol.success)
  265. assert_equal(sol.x.size, 5)
  266. sol_test = sol.sol(x_test)
  267. assert_allclose(sol_test[0], exp_sol(x_test), atol=1e-5)
  268. f_test = exp_fun(x_test, sol_test)
  269. r = sol.sol(x_test, 1) - f_test
  270. rel_res = r / (1 + np.abs(f_test))
  271. norm_res = np.sum(rel_res**2, axis=0)**0.5
  272. assert_(np.all(norm_res < 1e-3))
  273. assert_(np.all(sol.rms_residuals < 1e-3))
  274. assert_allclose(sol.sol(sol.x), sol.y, rtol=1e-10, atol=1e-10)
  275. assert_allclose(sol.sol(sol.x, 1), sol.yp, rtol=1e-10, atol=1e-10)
  276. def test_with_params():
  277. x = np.linspace(0, np.pi, 5)
  278. x_test = np.linspace(0, np.pi, 100)
  279. y = np.ones((2, x.shape[0]))
  280. for fun_jac in [None, sl_fun_jac]:
  281. for bc_jac in [None, sl_bc_jac]:
  282. sol = solve_bvp(sl_fun, sl_bc, x, y, p=[0.5], fun_jac=fun_jac,
  283. bc_jac=bc_jac)
  284. assert_equal(sol.status, 0)
  285. assert_(sol.success)
  286. assert_(sol.x.size < 10)
  287. assert_allclose(sol.p, [1], rtol=1e-4)
  288. sol_test = sol.sol(x_test)
  289. assert_allclose(sol_test[0], sl_sol(x_test, [1]),
  290. rtol=1e-4, atol=1e-4)
  291. f_test = sl_fun(x_test, sol_test, [1])
  292. r = sol.sol(x_test, 1) - f_test
  293. rel_res = r / (1 + np.abs(f_test))
  294. norm_res = np.sum(rel_res ** 2, axis=0) ** 0.5
  295. assert_(np.all(norm_res < 1e-3))
  296. assert_(np.all(sol.rms_residuals < 1e-3))
  297. assert_allclose(sol.sol(sol.x), sol.y, rtol=1e-10, atol=1e-10)
  298. assert_allclose(sol.sol(sol.x, 1), sol.yp, rtol=1e-10, atol=1e-10)
  299. def test_singular_term():
  300. x = np.linspace(0, 1, 10)
  301. x_test = np.linspace(0.05, 1, 100)
  302. y = np.empty((2, 10))
  303. y[0] = (3/4)**0.5
  304. y[1] = 1e-4
  305. S = np.array([[0, 0], [0, -2]])
  306. for fun_jac in [None, emden_fun_jac]:
  307. for bc_jac in [None, emden_bc_jac]:
  308. sol = solve_bvp(emden_fun, emden_bc, x, y, S=S, fun_jac=fun_jac,
  309. bc_jac=bc_jac)
  310. assert_equal(sol.status, 0)
  311. assert_(sol.success)
  312. assert_equal(sol.x.size, 10)
  313. sol_test = sol.sol(x_test)
  314. assert_allclose(sol_test[0], emden_sol(x_test), atol=1e-5)
  315. f_test = emden_fun(x_test, sol_test) + S.dot(sol_test) / x_test
  316. r = sol.sol(x_test, 1) - f_test
  317. rel_res = r / (1 + np.abs(f_test))
  318. norm_res = np.sum(rel_res ** 2, axis=0) ** 0.5
  319. assert_(np.all(norm_res < 1e-3))
  320. assert_allclose(sol.sol(sol.x), sol.y, rtol=1e-10, atol=1e-10)
  321. assert_allclose(sol.sol(sol.x, 1), sol.yp, rtol=1e-10, atol=1e-10)
  322. def test_complex():
  323. # The test is essentially the same as test_no_params, but boundary
  324. # conditions are turned into complex.
  325. x = np.linspace(0, 1, 5)
  326. x_test = np.linspace(0, 1, 100)
  327. y = np.zeros((2, x.shape[0]), dtype=complex)
  328. for fun_jac in [None, exp_fun_jac]:
  329. for bc_jac in [None, exp_bc_jac]:
  330. sol = solve_bvp(exp_fun, exp_bc_complex, x, y, fun_jac=fun_jac,
  331. bc_jac=bc_jac)
  332. assert_equal(sol.status, 0)
  333. assert_(sol.success)
  334. sol_test = sol.sol(x_test)
  335. assert_allclose(sol_test[0].real, exp_sol(x_test), atol=1e-5)
  336. assert_allclose(sol_test[0].imag, exp_sol(x_test), atol=1e-5)
  337. f_test = exp_fun(x_test, sol_test)
  338. r = sol.sol(x_test, 1) - f_test
  339. rel_res = r / (1 + np.abs(f_test))
  340. norm_res = np.sum(np.real(rel_res * np.conj(rel_res)),
  341. axis=0) ** 0.5
  342. assert_(np.all(norm_res < 1e-3))
  343. assert_(np.all(sol.rms_residuals < 1e-3))
  344. assert_allclose(sol.sol(sol.x), sol.y, rtol=1e-10, atol=1e-10)
  345. assert_allclose(sol.sol(sol.x, 1), sol.yp, rtol=1e-10, atol=1e-10)
  346. def test_failures():
  347. x = np.linspace(0, 1, 2)
  348. y = np.zeros((2, x.size))
  349. res = solve_bvp(exp_fun, exp_bc, x, y, tol=1e-5, max_nodes=5)
  350. assert_equal(res.status, 1)
  351. assert_(not res.success)
  352. x = np.linspace(0, 1, 5)
  353. y = np.zeros((2, x.size))
  354. res = solve_bvp(undefined_fun, undefined_bc, x, y)
  355. assert_equal(res.status, 2)
  356. assert_(not res.success)
  357. def test_big_problem():
  358. n = 30
  359. x = np.linspace(0, 1, 5)
  360. y = np.zeros((2 * n, x.size))
  361. sol = solve_bvp(big_fun, big_bc, x, y)
  362. assert_equal(sol.status, 0)
  363. assert_(sol.success)
  364. sol_test = sol.sol(x)
  365. assert_allclose(sol_test[0], big_sol(x, n))
  366. f_test = big_fun(x, sol_test)
  367. r = sol.sol(x, 1) - f_test
  368. rel_res = r / (1 + np.abs(f_test))
  369. norm_res = np.sum(np.real(rel_res * np.conj(rel_res)), axis=0) ** 0.5
  370. assert_(np.all(norm_res < 1e-3))
  371. assert_(np.all(sol.rms_residuals < 1e-3))
  372. assert_allclose(sol.sol(sol.x), sol.y, rtol=1e-10, atol=1e-10)
  373. assert_allclose(sol.sol(sol.x, 1), sol.yp, rtol=1e-10, atol=1e-10)
  374. def test_shock_layer():
  375. x = np.linspace(-1, 1, 5)
  376. x_test = np.linspace(-1, 1, 100)
  377. y = np.zeros((2, x.size))
  378. sol = solve_bvp(shock_fun, shock_bc, x, y)
  379. assert_equal(sol.status, 0)
  380. assert_(sol.success)
  381. assert_(sol.x.size < 110)
  382. sol_test = sol.sol(x_test)
  383. assert_allclose(sol_test[0], shock_sol(x_test), rtol=1e-5, atol=1e-5)
  384. f_test = shock_fun(x_test, sol_test)
  385. r = sol.sol(x_test, 1) - f_test
  386. rel_res = r / (1 + np.abs(f_test))
  387. norm_res = np.sum(rel_res ** 2, axis=0) ** 0.5
  388. assert_(np.all(norm_res < 1e-3))
  389. assert_allclose(sol.sol(sol.x), sol.y, rtol=1e-10, atol=1e-10)
  390. assert_allclose(sol.sol(sol.x, 1), sol.yp, rtol=1e-10, atol=1e-10)
  391. def test_verbose():
  392. # Smoke test that checks the printing does something and does not crash
  393. x = np.linspace(0, 1, 5)
  394. y = np.zeros((2, x.shape[0]))
  395. for verbose in [0, 1, 2]:
  396. old_stdout = sys.stdout
  397. sys.stdout = StringIO()
  398. try:
  399. sol = solve_bvp(exp_fun, exp_bc, x, y, verbose=verbose)
  400. text = sys.stdout.getvalue()
  401. finally:
  402. sys.stdout = old_stdout
  403. assert_(sol.success)
  404. if verbose == 0:
  405. assert_(not text, text)
  406. if verbose >= 1:
  407. assert_("Solved in" in text, text)
  408. if verbose >= 2:
  409. assert_("Max residual" in text, text)