rk.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. import numpy as np
  2. from .base import OdeSolver, DenseOutput
  3. from .common import (validate_max_step, validate_tol, select_initial_step,
  4. norm, warn_extraneous, validate_first_step)
  5. from . import dop853_coefficients
  6. # Multiply steps computed from asymptotic behaviour of errors by this.
  7. SAFETY = 0.9
  8. MIN_FACTOR = 0.2 # Minimum allowed decrease in a step size.
  9. MAX_FACTOR = 10 # Maximum allowed increase in a step size.
  10. def rk_step(fun, t, y, f, h, A, B, C, K):
  11. """Perform a single Runge-Kutta step.
  12. This function computes a prediction of an explicit Runge-Kutta method and
  13. also estimates the error of a less accurate method.
  14. Notation for Butcher tableau is as in [1]_.
  15. Parameters
  16. ----------
  17. fun : callable
  18. Right-hand side of the system.
  19. t : float
  20. Current time.
  21. y : ndarray, shape (n,)
  22. Current state.
  23. f : ndarray, shape (n,)
  24. Current value of the derivative, i.e., ``fun(x, y)``.
  25. h : float
  26. Step to use.
  27. A : ndarray, shape (n_stages, n_stages)
  28. Coefficients for combining previous RK stages to compute the next
  29. stage. For explicit methods the coefficients at and above the main
  30. diagonal are zeros.
  31. B : ndarray, shape (n_stages,)
  32. Coefficients for combining RK stages for computing the final
  33. prediction.
  34. C : ndarray, shape (n_stages,)
  35. Coefficients for incrementing time for consecutive RK stages.
  36. The value for the first stage is always zero.
  37. K : ndarray, shape (n_stages + 1, n)
  38. Storage array for putting RK stages here. Stages are stored in rows.
  39. The last row is a linear combination of the previous rows with
  40. coefficients
  41. Returns
  42. -------
  43. y_new : ndarray, shape (n,)
  44. Solution at t + h computed with a higher accuracy.
  45. f_new : ndarray, shape (n,)
  46. Derivative ``fun(t + h, y_new)``.
  47. References
  48. ----------
  49. .. [1] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential
  50. Equations I: Nonstiff Problems", Sec. II.4.
  51. """
  52. K[0] = f
  53. for s, (a, c) in enumerate(zip(A[1:], C[1:]), start=1):
  54. dy = np.dot(K[:s].T, a[:s]) * h
  55. K[s] = fun(t + c * h, y + dy)
  56. y_new = y + h * np.dot(K[:-1].T, B)
  57. f_new = fun(t + h, y_new)
  58. K[-1] = f_new
  59. return y_new, f_new
  60. class RungeKutta(OdeSolver):
  61. """Base class for explicit Runge-Kutta methods."""
  62. C: np.ndarray = NotImplemented
  63. A: np.ndarray = NotImplemented
  64. B: np.ndarray = NotImplemented
  65. E: np.ndarray = NotImplemented
  66. P: np.ndarray = NotImplemented
  67. order: int = NotImplemented
  68. error_estimator_order: int = NotImplemented
  69. n_stages: int = NotImplemented
  70. def __init__(self, fun, t0, y0, t_bound, max_step=np.inf,
  71. rtol=1e-3, atol=1e-6, vectorized=False,
  72. first_step=None, **extraneous):
  73. warn_extraneous(extraneous)
  74. super().__init__(fun, t0, y0, t_bound, vectorized,
  75. support_complex=True)
  76. self.y_old = None
  77. self.max_step = validate_max_step(max_step)
  78. self.rtol, self.atol = validate_tol(rtol, atol, self.n)
  79. self.f = self.fun(self.t, self.y)
  80. if first_step is None:
  81. self.h_abs = select_initial_step(
  82. self.fun, self.t, self.y, t_bound, max_step, self.f, self.direction,
  83. self.error_estimator_order, self.rtol, self.atol)
  84. else:
  85. self.h_abs = validate_first_step(first_step, t0, t_bound)
  86. self.K = np.empty((self.n_stages + 1, self.n), dtype=self.y.dtype)
  87. self.error_exponent = -1 / (self.error_estimator_order + 1)
  88. self.h_previous = None
  89. def _estimate_error(self, K, h):
  90. return np.dot(K.T, self.E) * h
  91. def _estimate_error_norm(self, K, h, scale):
  92. return norm(self._estimate_error(K, h) / scale)
  93. def _step_impl(self):
  94. t = self.t
  95. y = self.y
  96. max_step = self.max_step
  97. rtol = self.rtol
  98. atol = self.atol
  99. min_step = 10 * np.abs(np.nextafter(t, self.direction * np.inf) - t)
  100. if self.h_abs > max_step:
  101. h_abs = max_step
  102. elif self.h_abs < min_step:
  103. h_abs = min_step
  104. else:
  105. h_abs = self.h_abs
  106. step_accepted = False
  107. step_rejected = False
  108. while not step_accepted:
  109. if h_abs < min_step:
  110. return False, self.TOO_SMALL_STEP
  111. h = h_abs * self.direction
  112. t_new = t + h
  113. if self.direction * (t_new - self.t_bound) > 0:
  114. t_new = self.t_bound
  115. h = t_new - t
  116. h_abs = np.abs(h)
  117. y_new, f_new = rk_step(self.fun, t, y, self.f, h, self.A,
  118. self.B, self.C, self.K)
  119. scale = atol + np.maximum(np.abs(y), np.abs(y_new)) * rtol
  120. error_norm = self._estimate_error_norm(self.K, h, scale)
  121. if error_norm < 1:
  122. if error_norm == 0:
  123. factor = MAX_FACTOR
  124. else:
  125. factor = min(MAX_FACTOR,
  126. SAFETY * error_norm ** self.error_exponent)
  127. if step_rejected:
  128. factor = min(1, factor)
  129. h_abs *= factor
  130. step_accepted = True
  131. else:
  132. h_abs *= max(MIN_FACTOR,
  133. SAFETY * error_norm ** self.error_exponent)
  134. step_rejected = True
  135. self.h_previous = h
  136. self.y_old = y
  137. self.t = t_new
  138. self.y = y_new
  139. self.h_abs = h_abs
  140. self.f = f_new
  141. return True, None
  142. def _dense_output_impl(self):
  143. Q = self.K.T.dot(self.P)
  144. return RkDenseOutput(self.t_old, self.t, self.y_old, Q)
  145. class RK23(RungeKutta):
  146. """Explicit Runge-Kutta method of order 3(2).
  147. This uses the Bogacki-Shampine pair of formulas [1]_. The error is controlled
  148. assuming accuracy of the second-order method, but steps are taken using the
  149. third-order accurate formula (local extrapolation is done). A cubic Hermite
  150. polynomial is used for the dense output.
  151. Can be applied in the complex domain.
  152. Parameters
  153. ----------
  154. fun : callable
  155. Right-hand side of the system: the time derivative of the state ``y``
  156. at time ``t``. The calling signature is ``fun(t, y)``, where ``t`` is a
  157. scalar and ``y`` is an ndarray with ``len(y) = len(y0)``. ``fun`` must
  158. return an array of the same shape as ``y``. See `vectorized` for more
  159. information.
  160. t0 : float
  161. Initial time.
  162. y0 : array_like, shape (n,)
  163. Initial state.
  164. t_bound : float
  165. Boundary time - the integration won't continue beyond it. It also
  166. determines the direction of the integration.
  167. first_step : float or None, optional
  168. Initial step size. Default is ``None`` which means that the algorithm
  169. should choose.
  170. max_step : float, optional
  171. Maximum allowed step size. Default is np.inf, i.e., the step size is not
  172. bounded and determined solely by the solver.
  173. rtol, atol : float and array_like, optional
  174. Relative and absolute tolerances. The solver keeps the local error
  175. estimates less than ``atol + rtol * abs(y)``. Here `rtol` controls a
  176. relative accuracy (number of correct digits), while `atol` controls
  177. absolute accuracy (number of correct decimal places). To achieve the
  178. desired `rtol`, set `atol` to be smaller than the smallest value that
  179. can be expected from ``rtol * abs(y)`` so that `rtol` dominates the
  180. allowable error. If `atol` is larger than ``rtol * abs(y)`` the
  181. number of correct digits is not guaranteed. Conversely, to achieve the
  182. desired `atol` set `rtol` such that ``rtol * abs(y)`` is always smaller
  183. than `atol`. If components of y have different scales, it might be
  184. beneficial to set different `atol` values for different components by
  185. passing array_like with shape (n,) for `atol`. Default values are
  186. 1e-3 for `rtol` and 1e-6 for `atol`.
  187. vectorized : bool, optional
  188. Whether `fun` may be called in a vectorized fashion. False (default)
  189. is recommended for this solver.
  190. If ``vectorized`` is False, `fun` will always be called with ``y`` of
  191. shape ``(n,)``, where ``n = len(y0)``.
  192. If ``vectorized`` is True, `fun` may be called with ``y`` of shape
  193. ``(n, k)``, where ``k`` is an integer. In this case, `fun` must behave
  194. such that ``fun(t, y)[:, i] == fun(t, y[:, i])`` (i.e. each column of
  195. the returned array is the time derivative of the state corresponding
  196. with a column of ``y``).
  197. Setting ``vectorized=True`` allows for faster finite difference
  198. approximation of the Jacobian by methods 'Radau' and 'BDF', but
  199. will result in slower execution for this solver.
  200. Attributes
  201. ----------
  202. n : int
  203. Number of equations.
  204. status : string
  205. Current status of the solver: 'running', 'finished' or 'failed'.
  206. t_bound : float
  207. Boundary time.
  208. direction : float
  209. Integration direction: +1 or -1.
  210. t : float
  211. Current time.
  212. y : ndarray
  213. Current state.
  214. t_old : float
  215. Previous time. None if no steps were made yet.
  216. step_size : float
  217. Size of the last successful step. None if no steps were made yet.
  218. nfev : int
  219. Number evaluations of the system's right-hand side.
  220. njev : int
  221. Number of evaluations of the Jacobian.
  222. Is always 0 for this solver as it does not use the Jacobian.
  223. nlu : int
  224. Number of LU decompositions. Is always 0 for this solver.
  225. References
  226. ----------
  227. .. [1] P. Bogacki, L.F. Shampine, "A 3(2) Pair of Runge-Kutta Formulas",
  228. Appl. Math. Lett. Vol. 2, No. 4. pp. 321-325, 1989.
  229. """
  230. order = 3
  231. error_estimator_order = 2
  232. n_stages = 3
  233. C = np.array([0, 1/2, 3/4])
  234. A = np.array([
  235. [0, 0, 0],
  236. [1/2, 0, 0],
  237. [0, 3/4, 0]
  238. ])
  239. B = np.array([2/9, 1/3, 4/9])
  240. E = np.array([5/72, -1/12, -1/9, 1/8])
  241. P = np.array([[1, -4 / 3, 5 / 9],
  242. [0, 1, -2/3],
  243. [0, 4/3, -8/9],
  244. [0, -1, 1]])
  245. class RK45(RungeKutta):
  246. """Explicit Runge-Kutta method of order 5(4).
  247. This uses the Dormand-Prince pair of formulas [1]_. The error is controlled
  248. assuming accuracy of the fourth-order method accuracy, but steps are taken
  249. using the fifth-order accurate formula (local extrapolation is done).
  250. A quartic interpolation polynomial is used for the dense output [2]_.
  251. Can be applied in the complex domain.
  252. Parameters
  253. ----------
  254. fun : callable
  255. Right-hand side of the system. The calling signature is ``fun(t, y)``.
  256. Here ``t`` is a scalar, and there are two options for the ndarray ``y``:
  257. It can either have shape (n,); then ``fun`` must return array_like with
  258. shape (n,). Alternatively it can have shape (n, k); then ``fun``
  259. must return an array_like with shape (n, k), i.e., each column
  260. corresponds to a single column in ``y``. The choice between the two
  261. options is determined by `vectorized` argument (see below).
  262. t0 : float
  263. Initial time.
  264. y0 : array_like, shape (n,)
  265. Initial state.
  266. t_bound : float
  267. Boundary time - the integration won't continue beyond it. It also
  268. determines the direction of the integration.
  269. first_step : float or None, optional
  270. Initial step size. Default is ``None`` which means that the algorithm
  271. should choose.
  272. max_step : float, optional
  273. Maximum allowed step size. Default is np.inf, i.e., the step size is not
  274. bounded and determined solely by the solver.
  275. rtol, atol : float and array_like, optional
  276. Relative and absolute tolerances. The solver keeps the local error
  277. estimates less than ``atol + rtol * abs(y)``. Here `rtol` controls a
  278. relative accuracy (number of correct digits), while `atol` controls
  279. absolute accuracy (number of correct decimal places). To achieve the
  280. desired `rtol`, set `atol` to be smaller than the smallest value that
  281. can be expected from ``rtol * abs(y)`` so that `rtol` dominates the
  282. allowable error. If `atol` is larger than ``rtol * abs(y)`` the
  283. number of correct digits is not guaranteed. Conversely, to achieve the
  284. desired `atol` set `rtol` such that ``rtol * abs(y)`` is always smaller
  285. than `atol`. If components of y have different scales, it might be
  286. beneficial to set different `atol` values for different components by
  287. passing array_like with shape (n,) for `atol`. Default values are
  288. 1e-3 for `rtol` and 1e-6 for `atol`.
  289. vectorized : bool, optional
  290. Whether `fun` is implemented in a vectorized fashion. Default is False.
  291. Attributes
  292. ----------
  293. n : int
  294. Number of equations.
  295. status : string
  296. Current status of the solver: 'running', 'finished' or 'failed'.
  297. t_bound : float
  298. Boundary time.
  299. direction : float
  300. Integration direction: +1 or -1.
  301. t : float
  302. Current time.
  303. y : ndarray
  304. Current state.
  305. t_old : float
  306. Previous time. None if no steps were made yet.
  307. step_size : float
  308. Size of the last successful step. None if no steps were made yet.
  309. nfev : int
  310. Number evaluations of the system's right-hand side.
  311. njev : int
  312. Number of evaluations of the Jacobian.
  313. Is always 0 for this solver as it does not use the Jacobian.
  314. nlu : int
  315. Number of LU decompositions. Is always 0 for this solver.
  316. References
  317. ----------
  318. .. [1] J. R. Dormand, P. J. Prince, "A family of embedded Runge-Kutta
  319. formulae", Journal of Computational and Applied Mathematics, Vol. 6,
  320. No. 1, pp. 19-26, 1980.
  321. .. [2] L. W. Shampine, "Some Practical Runge-Kutta Formulas", Mathematics
  322. of Computation,, Vol. 46, No. 173, pp. 135-150, 1986.
  323. """
  324. order = 5
  325. error_estimator_order = 4
  326. n_stages = 6
  327. C = np.array([0, 1/5, 3/10, 4/5, 8/9, 1])
  328. A = np.array([
  329. [0, 0, 0, 0, 0],
  330. [1/5, 0, 0, 0, 0],
  331. [3/40, 9/40, 0, 0, 0],
  332. [44/45, -56/15, 32/9, 0, 0],
  333. [19372/6561, -25360/2187, 64448/6561, -212/729, 0],
  334. [9017/3168, -355/33, 46732/5247, 49/176, -5103/18656]
  335. ])
  336. B = np.array([35/384, 0, 500/1113, 125/192, -2187/6784, 11/84])
  337. E = np.array([-71/57600, 0, 71/16695, -71/1920, 17253/339200, -22/525,
  338. 1/40])
  339. # Corresponds to the optimum value of c_6 from [2]_.
  340. P = np.array([
  341. [1, -8048581381/2820520608, 8663915743/2820520608,
  342. -12715105075/11282082432],
  343. [0, 0, 0, 0],
  344. [0, 131558114200/32700410799, -68118460800/10900136933,
  345. 87487479700/32700410799],
  346. [0, -1754552775/470086768, 14199869525/1410260304,
  347. -10690763975/1880347072],
  348. [0, 127303824393/49829197408, -318862633887/49829197408,
  349. 701980252875 / 199316789632],
  350. [0, -282668133/205662961, 2019193451/616988883, -1453857185/822651844],
  351. [0, 40617522/29380423, -110615467/29380423, 69997945/29380423]])
  352. class DOP853(RungeKutta):
  353. """Explicit Runge-Kutta method of order 8.
  354. This is a Python implementation of "DOP853" algorithm originally written
  355. in Fortran [1]_, [2]_. Note that this is not a literal translation, but
  356. the algorithmic core and coefficients are the same.
  357. Can be applied in the complex domain.
  358. Parameters
  359. ----------
  360. fun : callable
  361. Right-hand side of the system. The calling signature is ``fun(t, y)``.
  362. Here, ``t`` is a scalar, and there are two options for the ndarray ``y``:
  363. It can either have shape (n,); then ``fun`` must return array_like with
  364. shape (n,). Alternatively it can have shape (n, k); then ``fun``
  365. must return an array_like with shape (n, k), i.e. each column
  366. corresponds to a single column in ``y``. The choice between the two
  367. options is determined by `vectorized` argument (see below).
  368. t0 : float
  369. Initial time.
  370. y0 : array_like, shape (n,)
  371. Initial state.
  372. t_bound : float
  373. Boundary time - the integration won't continue beyond it. It also
  374. determines the direction of the integration.
  375. first_step : float or None, optional
  376. Initial step size. Default is ``None`` which means that the algorithm
  377. should choose.
  378. max_step : float, optional
  379. Maximum allowed step size. Default is np.inf, i.e. the step size is not
  380. bounded and determined solely by the solver.
  381. rtol, atol : float and array_like, optional
  382. Relative and absolute tolerances. The solver keeps the local error
  383. estimates less than ``atol + rtol * abs(y)``. Here `rtol` controls a
  384. relative accuracy (number of correct digits), while `atol` controls
  385. absolute accuracy (number of correct decimal places). To achieve the
  386. desired `rtol`, set `atol` to be smaller than the smallest value that
  387. can be expected from ``rtol * abs(y)`` so that `rtol` dominates the
  388. allowable error. If `atol` is larger than ``rtol * abs(y)`` the
  389. number of correct digits is not guaranteed. Conversely, to achieve the
  390. desired `atol` set `rtol` such that ``rtol * abs(y)`` is always smaller
  391. than `atol`. If components of y have different scales, it might be
  392. beneficial to set different `atol` values for different components by
  393. passing array_like with shape (n,) for `atol`. Default values are
  394. 1e-3 for `rtol` and 1e-6 for `atol`.
  395. vectorized : bool, optional
  396. Whether `fun` is implemented in a vectorized fashion. Default is False.
  397. Attributes
  398. ----------
  399. n : int
  400. Number of equations.
  401. status : string
  402. Current status of the solver: 'running', 'finished' or 'failed'.
  403. t_bound : float
  404. Boundary time.
  405. direction : float
  406. Integration direction: +1 or -1.
  407. t : float
  408. Current time.
  409. y : ndarray
  410. Current state.
  411. t_old : float
  412. Previous time. None if no steps were made yet.
  413. step_size : float
  414. Size of the last successful step. None if no steps were made yet.
  415. nfev : int
  416. Number evaluations of the system's right-hand side.
  417. njev : int
  418. Number of evaluations of the Jacobian. Is always 0 for this solver
  419. as it does not use the Jacobian.
  420. nlu : int
  421. Number of LU decompositions. Is always 0 for this solver.
  422. References
  423. ----------
  424. .. [1] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential
  425. Equations I: Nonstiff Problems", Sec. II.
  426. .. [2] `Page with original Fortran code of DOP853
  427. <http://www.unige.ch/~hairer/software.html>`_.
  428. """
  429. n_stages = dop853_coefficients.N_STAGES
  430. order = 8
  431. error_estimator_order = 7
  432. A = dop853_coefficients.A[:n_stages, :n_stages]
  433. B = dop853_coefficients.B
  434. C = dop853_coefficients.C[:n_stages]
  435. E3 = dop853_coefficients.E3
  436. E5 = dop853_coefficients.E5
  437. D = dop853_coefficients.D
  438. A_EXTRA = dop853_coefficients.A[n_stages + 1:]
  439. C_EXTRA = dop853_coefficients.C[n_stages + 1:]
  440. def __init__(self, fun, t0, y0, t_bound, max_step=np.inf,
  441. rtol=1e-3, atol=1e-6, vectorized=False,
  442. first_step=None, **extraneous):
  443. super().__init__(fun, t0, y0, t_bound, max_step, rtol, atol,
  444. vectorized, first_step, **extraneous)
  445. self.K_extended = np.empty((dop853_coefficients.N_STAGES_EXTENDED,
  446. self.n), dtype=self.y.dtype)
  447. self.K = self.K_extended[:self.n_stages + 1]
  448. def _estimate_error(self, K, h): # Left for testing purposes.
  449. err5 = np.dot(K.T, self.E5)
  450. err3 = np.dot(K.T, self.E3)
  451. denom = np.hypot(np.abs(err5), 0.1 * np.abs(err3))
  452. correction_factor = np.ones_like(err5)
  453. mask = denom > 0
  454. correction_factor[mask] = np.abs(err5[mask]) / denom[mask]
  455. return h * err5 * correction_factor
  456. def _estimate_error_norm(self, K, h, scale):
  457. err5 = np.dot(K.T, self.E5) / scale
  458. err3 = np.dot(K.T, self.E3) / scale
  459. err5_norm_2 = np.linalg.norm(err5)**2
  460. err3_norm_2 = np.linalg.norm(err3)**2
  461. if err5_norm_2 == 0 and err3_norm_2 == 0:
  462. return 0.0
  463. denom = err5_norm_2 + 0.01 * err3_norm_2
  464. return np.abs(h) * err5_norm_2 / np.sqrt(denom * len(scale))
  465. def _dense_output_impl(self):
  466. K = self.K_extended
  467. h = self.h_previous
  468. for s, (a, c) in enumerate(zip(self.A_EXTRA, self.C_EXTRA),
  469. start=self.n_stages + 1):
  470. dy = np.dot(K[:s].T, a[:s]) * h
  471. K[s] = self.fun(self.t_old + c * h, self.y_old + dy)
  472. F = np.empty((dop853_coefficients.INTERPOLATOR_POWER, self.n),
  473. dtype=self.y_old.dtype)
  474. f_old = K[0]
  475. delta_y = self.y - self.y_old
  476. F[0] = delta_y
  477. F[1] = h * f_old - delta_y
  478. F[2] = 2 * delta_y - h * (self.f + f_old)
  479. F[3:] = h * np.dot(self.D, K)
  480. return Dop853DenseOutput(self.t_old, self.t, self.y_old, F)
  481. class RkDenseOutput(DenseOutput):
  482. def __init__(self, t_old, t, y_old, Q):
  483. super().__init__(t_old, t)
  484. self.h = t - t_old
  485. self.Q = Q
  486. self.order = Q.shape[1] - 1
  487. self.y_old = y_old
  488. def _call_impl(self, t):
  489. x = (t - self.t_old) / self.h
  490. if t.ndim == 0:
  491. p = np.tile(x, self.order + 1)
  492. p = np.cumprod(p)
  493. else:
  494. p = np.tile(x, (self.order + 1, 1))
  495. p = np.cumprod(p, axis=0)
  496. y = self.h * np.dot(self.Q, p)
  497. if y.ndim == 2:
  498. y += self.y_old[:, None]
  499. else:
  500. y += self.y_old
  501. return y
  502. class Dop853DenseOutput(DenseOutput):
  503. def __init__(self, t_old, t, y_old, F):
  504. super().__init__(t_old, t)
  505. self.h = t - t_old
  506. self.F = F
  507. self.y_old = y_old
  508. def _call_impl(self, t):
  509. x = (t - self.t_old) / self.h
  510. if t.ndim == 0:
  511. y = np.zeros_like(self.y_old)
  512. else:
  513. x = x[:, None]
  514. y = np.zeros((len(x), len(self.y_old)), dtype=self.y_old.dtype)
  515. for i, f in enumerate(reversed(self.F)):
  516. y += f
  517. if i % 2 == 0:
  518. y *= x
  519. else:
  520. y *= 1 - x
  521. y += self.y_old
  522. return y.T