Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/integrate/_ivp/ivp.py: 11%

167 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1import inspect 

2import numpy as np 

3from .bdf import BDF 

4from .radau import Radau 

5from .rk import RK23, RK45, DOP853 

6from .lsoda import LSODA 

7from scipy.optimize import OptimizeResult 

8from .common import EPS, OdeSolution 

9from .base import OdeSolver 

10 

11 

12METHODS = {'RK23': RK23, 

13 'RK45': RK45, 

14 'DOP853': DOP853, 

15 'Radau': Radau, 

16 'BDF': BDF, 

17 'LSODA': LSODA} 

18 

19 

20MESSAGES = {0: "The solver successfully reached the end of the integration interval.", 

21 1: "A termination event occurred."} 

22 

23 

24class OdeResult(OptimizeResult): 

25 pass 

26 

27 

28def prepare_events(events): 

29 """Standardize event functions and extract is_terminal and direction.""" 

30 if callable(events): 

31 events = (events,) 

32 

33 if events is not None: 

34 is_terminal = np.empty(len(events), dtype=bool) 

35 direction = np.empty(len(events)) 

36 for i, event in enumerate(events): 

37 try: 

38 is_terminal[i] = event.terminal 

39 except AttributeError: 

40 is_terminal[i] = False 

41 

42 try: 

43 direction[i] = event.direction 

44 except AttributeError: 

45 direction[i] = 0 

46 else: 

47 is_terminal = None 

48 direction = None 

49 

50 return events, is_terminal, direction 

51 

52 

53def solve_event_equation(event, sol, t_old, t): 

54 """Solve an equation corresponding to an ODE event. 

55 

56 The equation is ``event(t, y(t)) = 0``, here ``y(t)`` is known from an 

57 ODE solver using some sort of interpolation. It is solved by 

58 `scipy.optimize.brentq` with xtol=atol=4*EPS. 

59 

60 Parameters 

61 ---------- 

62 event : callable 

63 Function ``event(t, y)``. 

64 sol : callable 

65 Function ``sol(t)`` which evaluates an ODE solution between `t_old` 

66 and `t`. 

67 t_old, t : float 

68 Previous and new values of time. They will be used as a bracketing 

69 interval. 

70 

71 Returns 

72 ------- 

73 root : float 

74 Found solution. 

75 """ 

76 from scipy.optimize import brentq 

77 return brentq(lambda t: event(t, sol(t)), t_old, t, 

78 xtol=4 * EPS, rtol=4 * EPS) 

79 

80 

81def handle_events(sol, events, active_events, is_terminal, t_old, t): 

82 """Helper function to handle events. 

83 

84 Parameters 

85 ---------- 

86 sol : DenseOutput 

87 Function ``sol(t)`` which evaluates an ODE solution between `t_old` 

88 and `t`. 

89 events : list of callables, length n_events 

90 Event functions with signatures ``event(t, y)``. 

91 active_events : ndarray 

92 Indices of events which occurred. 

93 is_terminal : ndarray, shape (n_events,) 

94 Which events are terminal. 

95 t_old, t : float 

96 Previous and new values of time. 

97 

98 Returns 

99 ------- 

100 root_indices : ndarray 

101 Indices of events which take zero between `t_old` and `t` and before 

102 a possible termination. 

103 roots : ndarray 

104 Values of t at which events occurred. 

105 terminate : bool 

106 Whether a terminal event occurred. 

107 """ 

108 roots = [solve_event_equation(events[event_index], sol, t_old, t) 

109 for event_index in active_events] 

110 

111 roots = np.asarray(roots) 

112 

113 if np.any(is_terminal[active_events]): 

114 if t > t_old: 

115 order = np.argsort(roots) 

116 else: 

117 order = np.argsort(-roots) 

118 active_events = active_events[order] 

119 roots = roots[order] 

120 t = np.nonzero(is_terminal[active_events])[0][0] 

121 active_events = active_events[:t + 1] 

122 roots = roots[:t + 1] 

123 terminate = True 

124 else: 

125 terminate = False 

126 

127 return active_events, roots, terminate 

128 

129 

130def find_active_events(g, g_new, direction): 

131 """Find which event occurred during an integration step. 

132 

133 Parameters 

134 ---------- 

135 g, g_new : array_like, shape (n_events,) 

136 Values of event functions at a current and next points. 

137 direction : ndarray, shape (n_events,) 

138 Event "direction" according to the definition in `solve_ivp`. 

139 

140 Returns 

141 ------- 

142 active_events : ndarray 

143 Indices of events which occurred during the step. 

144 """ 

145 g, g_new = np.asarray(g), np.asarray(g_new) 

146 up = (g <= 0) & (g_new >= 0) 

147 down = (g >= 0) & (g_new <= 0) 

148 either = up | down 

149 mask = (up & (direction > 0) | 

150 down & (direction < 0) | 

151 either & (direction == 0)) 

152 

153 return np.nonzero(mask)[0] 

154 

155 

156def solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False, 

157 events=None, vectorized=False, args=None, **options): 

158 """Solve an initial value problem for a system of ODEs. 

159 

160 This function numerically integrates a system of ordinary differential 

161 equations given an initial value:: 

162 

163 dy / dt = f(t, y) 

164 y(t0) = y0 

165 

166 Here t is a 1-D independent variable (time), y(t) is an 

167 N-D vector-valued function (state), and an N-D 

168 vector-valued function f(t, y) determines the differential equations. 

169 The goal is to find y(t) approximately satisfying the differential 

170 equations, given an initial value y(t0)=y0. 

171 

172 Some of the solvers support integration in the complex domain, but note 

173 that for stiff ODE solvers, the right-hand side must be 

174 complex-differentiable (satisfy Cauchy-Riemann equations [11]_). 

175 To solve a problem in the complex domain, pass y0 with a complex data type. 

176 Another option always available is to rewrite your problem for real and 

177 imaginary parts separately. 

178 

179 Parameters 

180 ---------- 

181 fun : callable 

182 Right-hand side of the system. The calling signature is ``fun(t, y)``. 

183 Here `t` is a scalar, and there are two options for the ndarray `y`: 

184 It can either have shape (n,); then `fun` must return array_like with 

185 shape (n,). Alternatively, it can have shape (n, k); then `fun` 

186 must return an array_like with shape (n, k), i.e., each column 

187 corresponds to a single column in `y`. The choice between the two 

188 options is determined by `vectorized` argument (see below). The 

189 vectorized implementation allows a faster approximation of the Jacobian 

190 by finite differences (required for stiff solvers). 

191 t_span : 2-member sequence 

192 Interval of integration (t0, tf). The solver starts with t=t0 and 

193 integrates until it reaches t=tf. Both t0 and tf must be floats 

194 or values interpretable by the float conversion function. 

195 y0 : array_like, shape (n,) 

196 Initial state. For problems in the complex domain, pass `y0` with a 

197 complex data type (even if the initial value is purely real). 

198 method : string or `OdeSolver`, optional 

199 Integration method to use: 

200 

201 * 'RK45' (default): Explicit Runge-Kutta method of order 5(4) [1]_. 

202 The error is controlled assuming accuracy of the fourth-order 

203 method, but steps are taken using the fifth-order accurate 

204 formula (local extrapolation is done). A quartic interpolation 

205 polynomial is used for the dense output [2]_. Can be applied in 

206 the complex domain. 

207 * 'RK23': Explicit Runge-Kutta method of order 3(2) [3]_. The error 

208 is controlled assuming accuracy of the second-order method, but 

209 steps are taken using the third-order accurate formula (local 

210 extrapolation is done). A cubic Hermite polynomial is used for the 

211 dense output. Can be applied in the complex domain. 

212 * 'DOP853': Explicit Runge-Kutta method of order 8 [13]_. 

213 Python implementation of the "DOP853" algorithm originally 

214 written in Fortran [14]_. A 7-th order interpolation polynomial 

215 accurate to 7-th order is used for the dense output. 

216 Can be applied in the complex domain. 

217 * 'Radau': Implicit Runge-Kutta method of the Radau IIA family of 

218 order 5 [4]_. The error is controlled with a third-order accurate 

219 embedded formula. A cubic polynomial which satisfies the 

220 collocation conditions is used for the dense output. 

221 * 'BDF': Implicit multi-step variable-order (1 to 5) method based 

222 on a backward differentiation formula for the derivative 

223 approximation [5]_. The implementation follows the one described 

224 in [6]_. A quasi-constant step scheme is used and accuracy is 

225 enhanced using the NDF modification. Can be applied in the 

226 complex domain. 

227 * 'LSODA': Adams/BDF method with automatic stiffness detection and 

228 switching [7]_, [8]_. This is a wrapper of the Fortran solver 

229 from ODEPACK. 

230 

231 Explicit Runge-Kutta methods ('RK23', 'RK45', 'DOP853') should be used 

232 for non-stiff problems and implicit methods ('Radau', 'BDF') for 

233 stiff problems [9]_. Among Runge-Kutta methods, 'DOP853' is recommended 

234 for solving with high precision (low values of `rtol` and `atol`). 

235 

236 If not sure, first try to run 'RK45'. If it makes unusually many 

237 iterations, diverges, or fails, your problem is likely to be stiff and 

238 you should use 'Radau' or 'BDF'. 'LSODA' can also be a good universal 

239 choice, but it might be somewhat less convenient to work with as it 

240 wraps old Fortran code. 

241 

242 You can also pass an arbitrary class derived from `OdeSolver` which 

243 implements the solver. 

244 t_eval : array_like or None, optional 

245 Times at which to store the computed solution, must be sorted and lie 

246 within `t_span`. If None (default), use points selected by the solver. 

247 dense_output : bool, optional 

248 Whether to compute a continuous solution. Default is False. 

249 events : callable, or list of callables, optional 

250 Events to track. If None (default), no events will be tracked. 

251 Each event occurs at the zeros of a continuous function of time and 

252 state. Each function must have the signature ``event(t, y)`` and return 

253 a float. The solver will find an accurate value of `t` at which 

254 ``event(t, y(t)) = 0`` using a root-finding algorithm. By default, all 

255 zeros will be found. The solver looks for a sign change over each step, 

256 so if multiple zero crossings occur within one step, events may be 

257 missed. Additionally each `event` function might have the following 

258 attributes: 

259 

260 terminal: bool, optional 

261 Whether to terminate integration if this event occurs. 

262 Implicitly False if not assigned. 

263 direction: float, optional 

264 Direction of a zero crossing. If `direction` is positive, 

265 `event` will only trigger when going from negative to positive, 

266 and vice versa if `direction` is negative. If 0, then either 

267 direction will trigger event. Implicitly 0 if not assigned. 

268 

269 You can assign attributes like ``event.terminal = True`` to any 

270 function in Python. 

271 vectorized : bool, optional 

272 Whether `fun` is implemented in a vectorized fashion. Default is False. 

273 args : tuple, optional 

274 Additional arguments to pass to the user-defined functions. If given, 

275 the additional arguments are passed to all user-defined functions. 

276 So if, for example, `fun` has the signature ``fun(t, y, a, b, c)``, 

277 then `jac` (if given) and any event functions must have the same 

278 signature, and `args` must be a tuple of length 3. 

279 **options 

280 Options passed to a chosen solver. All options available for already 

281 implemented solvers are listed below. 

282 first_step : float or None, optional 

283 Initial step size. Default is `None` which means that the algorithm 

284 should choose. 

285 max_step : float, optional 

286 Maximum allowed step size. Default is np.inf, i.e., the step size is not 

287 bounded and determined solely by the solver. 

288 rtol, atol : float or array_like, optional 

289 Relative and absolute tolerances. The solver keeps the local error 

290 estimates less than ``atol + rtol * abs(y)``. Here `rtol` controls a 

291 relative accuracy (number of correct digits), while `atol` controls 

292 absolute accuracy (number of correct decimal places). To achieve the 

293 desired `rtol`, set `atol` to be smaller than the smallest value that 

294 can be expected from ``rtol * abs(y)`` so that `rtol` dominates the 

295 allowable error. If `atol` is larger than ``rtol * abs(y)`` the 

296 number of correct digits is not guaranteed. Conversely, to achieve the 

297 desired `atol` set `rtol` such that ``rtol * abs(y)`` is always smaller 

298 than `atol`. If components of y have different scales, it might be 

299 beneficial to set different `atol` values for different components by 

300 passing array_like with shape (n,) for `atol`. Default values are 

301 1e-3 for `rtol` and 1e-6 for `atol`. 

302 jac : array_like, sparse_matrix, callable or None, optional 

303 Jacobian matrix of the right-hand side of the system with respect 

304 to y, required by the 'Radau', 'BDF' and 'LSODA' method. The 

305 Jacobian matrix has shape (n, n) and its element (i, j) is equal to 

306 ``d f_i / d y_j``. There are three ways to define the Jacobian: 

307 

308 * If array_like or sparse_matrix, the Jacobian is assumed to 

309 be constant. Not supported by 'LSODA'. 

310 * If callable, the Jacobian is assumed to depend on both 

311 t and y; it will be called as ``jac(t, y)``, as necessary. 

312 For 'Radau' and 'BDF' methods, the return value might be a 

313 sparse matrix. 

314 * If None (default), the Jacobian will be approximated by 

315 finite differences. 

316 

317 It is generally recommended to provide the Jacobian rather than 

318 relying on a finite-difference approximation. 

319 jac_sparsity : array_like, sparse matrix or None, optional 

320 Defines a sparsity structure of the Jacobian matrix for a finite- 

321 difference approximation. Its shape must be (n, n). This argument 

322 is ignored if `jac` is not `None`. If the Jacobian has only few 

323 non-zero elements in *each* row, providing the sparsity structure 

324 will greatly speed up the computations [10]_. A zero entry means that 

325 a corresponding element in the Jacobian is always zero. If None 

326 (default), the Jacobian is assumed to be dense. 

327 Not supported by 'LSODA', see `lband` and `uband` instead. 

328 lband, uband : int or None, optional 

329 Parameters defining the bandwidth of the Jacobian for the 'LSODA' 

330 method, i.e., ``jac[i, j] != 0 only for i - lband <= j <= i + uband``. 

331 Default is None. Setting these requires your jac routine to return the 

332 Jacobian in the packed format: the returned array must have ``n`` 

333 columns and ``uband + lband + 1`` rows in which Jacobian diagonals are 

334 written. Specifically ``jac_packed[uband + i - j , j] = jac[i, j]``. 

335 The same format is used in `scipy.linalg.solve_banded` (check for an 

336 illustration). These parameters can be also used with ``jac=None`` to 

337 reduce the number of Jacobian elements estimated by finite differences. 

338 min_step : float, optional 

339 The minimum allowed step size for 'LSODA' method. 

340 By default `min_step` is zero. 

341 

342 Returns 

343 ------- 

344 Bunch object with the following fields defined: 

345 t : ndarray, shape (n_points,) 

346 Time points. 

347 y : ndarray, shape (n, n_points) 

348 Values of the solution at `t`. 

349 sol : `OdeSolution` or None 

350 Found solution as `OdeSolution` instance; None if `dense_output` was 

351 set to False. 

352 t_events : list of ndarray or None 

353 Contains for each event type a list of arrays at which an event of 

354 that type event was detected. None if `events` was None. 

355 y_events : list of ndarray or None 

356 For each value of `t_events`, the corresponding value of the solution. 

357 None if `events` was None. 

358 nfev : int 

359 Number of evaluations of the right-hand side. 

360 njev : int 

361 Number of evaluations of the Jacobian. 

362 nlu : int 

363 Number of LU decompositions. 

364 status : int 

365 Reason for algorithm termination: 

366 

367 * -1: Integration step failed. 

368 * 0: The solver successfully reached the end of `tspan`. 

369 * 1: A termination event occurred. 

370 

371 message : string 

372 Human-readable description of the termination reason. 

373 success : bool 

374 True if the solver reached the interval end or a termination event 

375 occurred (``status >= 0``). 

376 

377 References 

378 ---------- 

379 .. [1] J. R. Dormand, P. J. Prince, "A family of embedded Runge-Kutta 

380 formulae", Journal of Computational and Applied Mathematics, Vol. 6, 

381 No. 1, pp. 19-26, 1980. 

382 .. [2] L. W. Shampine, "Some Practical Runge-Kutta Formulas", Mathematics 

383 of Computation,, Vol. 46, No. 173, pp. 135-150, 1986. 

384 .. [3] P. Bogacki, L.F. Shampine, "A 3(2) Pair of Runge-Kutta Formulas", 

385 Appl. Math. Lett. Vol. 2, No. 4. pp. 321-325, 1989. 

386 .. [4] E. Hairer, G. Wanner, "Solving Ordinary Differential Equations II: 

387 Stiff and Differential-Algebraic Problems", Sec. IV.8. 

388 .. [5] `Backward Differentiation Formula 

389 <https://en.wikipedia.org/wiki/Backward_differentiation_formula>`_ 

390 on Wikipedia. 

391 .. [6] L. F. Shampine, M. W. Reichelt, "THE MATLAB ODE SUITE", SIAM J. SCI. 

392 COMPUTE., Vol. 18, No. 1, pp. 1-22, January 1997. 

393 .. [7] A. C. Hindmarsh, "ODEPACK, A Systematized Collection of ODE 

394 Solvers," IMACS Transactions on Scientific Computation, Vol 1., 

395 pp. 55-64, 1983. 

396 .. [8] L. Petzold, "Automatic selection of methods for solving stiff and 

397 nonstiff systems of ordinary differential equations", SIAM Journal 

398 on Scientific and Statistical Computing, Vol. 4, No. 1, pp. 136-148, 

399 1983. 

400 .. [9] `Stiff equation <https://en.wikipedia.org/wiki/Stiff_equation>`_ on 

401 Wikipedia. 

402 .. [10] A. Curtis, M. J. D. Powell, and J. Reid, "On the estimation of 

403 sparse Jacobian matrices", Journal of the Institute of Mathematics 

404 and its Applications, 13, pp. 117-120, 1974. 

405 .. [11] `Cauchy-Riemann equations 

406 <https://en.wikipedia.org/wiki/Cauchy-Riemann_equations>`_ on 

407 Wikipedia. 

408 .. [12] `Lotka-Volterra equations 

409 <https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations>`_ 

410 on Wikipedia. 

411 .. [13] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential 

412 Equations I: Nonstiff Problems", Sec. II. 

413 .. [14] `Page with original Fortran code of DOP853 

414 <http://www.unige.ch/~hairer/software.html>`_. 

415 

416 Examples 

417 -------- 

418 Basic exponential decay showing automatically chosen time points. 

419 

420 >>> import numpy as np 

421 >>> from scipy.integrate import solve_ivp 

422 >>> def exponential_decay(t, y): return -0.5 * y 

423 >>> sol = solve_ivp(exponential_decay, [0, 10], [2, 4, 8]) 

424 >>> print(sol.t) 

425 [ 0. 0.11487653 1.26364188 3.06061781 4.81611105 6.57445806 

426 8.33328988 10. ] 

427 >>> print(sol.y) 

428 [[2. 1.88836035 1.06327177 0.43319312 0.18017253 0.07483045 

429 0.03107158 0.01350781] 

430 [4. 3.7767207 2.12654355 0.86638624 0.36034507 0.14966091 

431 0.06214316 0.02701561] 

432 [8. 7.5534414 4.25308709 1.73277247 0.72069014 0.29932181 

433 0.12428631 0.05403123]] 

434 

435 Specifying points where the solution is desired. 

436 

437 >>> sol = solve_ivp(exponential_decay, [0, 10], [2, 4, 8], 

438 ... t_eval=[0, 1, 2, 4, 10]) 

439 >>> print(sol.t) 

440 [ 0 1 2 4 10] 

441 >>> print(sol.y) 

442 [[2. 1.21305369 0.73534021 0.27066736 0.01350938] 

443 [4. 2.42610739 1.47068043 0.54133472 0.02701876] 

444 [8. 4.85221478 2.94136085 1.08266944 0.05403753]] 

445 

446 Cannon fired upward with terminal event upon impact. The ``terminal`` and 

447 ``direction`` fields of an event are applied by monkey patching a function. 

448 Here ``y[0]`` is position and ``y[1]`` is velocity. The projectile starts 

449 at position 0 with velocity +10. Note that the integration never reaches 

450 t=100 because the event is terminal. 

451 

452 >>> def upward_cannon(t, y): return [y[1], -0.5] 

453 >>> def hit_ground(t, y): return y[0] 

454 >>> hit_ground.terminal = True 

455 >>> hit_ground.direction = -1 

456 >>> sol = solve_ivp(upward_cannon, [0, 100], [0, 10], events=hit_ground) 

457 >>> print(sol.t_events) 

458 [array([40.])] 

459 >>> print(sol.t) 

460 [0.00000000e+00 9.99900010e-05 1.09989001e-03 1.10988901e-02 

461 1.11088891e-01 1.11098890e+00 1.11099890e+01 4.00000000e+01] 

462 

463 Use `dense_output` and `events` to find position, which is 100, at the apex 

464 of the cannonball's trajectory. Apex is not defined as terminal, so both 

465 apex and hit_ground are found. There is no information at t=20, so the sol 

466 attribute is used to evaluate the solution. The sol attribute is returned 

467 by setting ``dense_output=True``. Alternatively, the `y_events` attribute 

468 can be used to access the solution at the time of the event. 

469 

470 >>> def apex(t, y): return y[1] 

471 >>> sol = solve_ivp(upward_cannon, [0, 100], [0, 10], 

472 ... events=(hit_ground, apex), dense_output=True) 

473 >>> print(sol.t_events) 

474 [array([40.]), array([20.])] 

475 >>> print(sol.t) 

476 [0.00000000e+00 9.99900010e-05 1.09989001e-03 1.10988901e-02 

477 1.11088891e-01 1.11098890e+00 1.11099890e+01 4.00000000e+01] 

478 >>> print(sol.sol(sol.t_events[1][0])) 

479 [100. 0.] 

480 >>> print(sol.y_events) 

481 [array([[-5.68434189e-14, -1.00000000e+01]]), array([[1.00000000e+02, 1.77635684e-15]])] 

482 

483 As an example of a system with additional parameters, we'll implement 

484 the Lotka-Volterra equations [12]_. 

485 

486 >>> def lotkavolterra(t, z, a, b, c, d): 

487 ... x, y = z 

488 ... return [a*x - b*x*y, -c*y + d*x*y] 

489 ... 

490 

491 We pass in the parameter values a=1.5, b=1, c=3 and d=1 with the `args` 

492 argument. 

493 

494 >>> sol = solve_ivp(lotkavolterra, [0, 15], [10, 5], args=(1.5, 1, 3, 1), 

495 ... dense_output=True) 

496 

497 Compute a dense solution and plot it. 

498 

499 >>> t = np.linspace(0, 15, 300) 

500 >>> z = sol.sol(t) 

501 >>> import matplotlib.pyplot as plt 

502 >>> plt.plot(t, z.T) 

503 >>> plt.xlabel('t') 

504 >>> plt.legend(['x', 'y'], shadow=True) 

505 >>> plt.title('Lotka-Volterra System') 

506 >>> plt.show() 

507 

508 """ 

509 if method not in METHODS and not ( 

510 inspect.isclass(method) and issubclass(method, OdeSolver)): 

511 raise ValueError("`method` must be one of {} or OdeSolver class." 

512 .format(METHODS)) 

513 

514 t0, tf = map(float, t_span) 

515 

516 if args is not None: 

517 # Wrap the user's fun (and jac, if given) in lambdas to hide the 

518 # additional parameters. Pass in the original fun as a keyword 

519 # argument to keep it in the scope of the lambda. 

520 try: 

521 _ = [*(args)] 

522 except TypeError as exp: 

523 suggestion_tuple = ( 

524 "Supplied 'args' cannot be unpacked. Please supply `args`" 

525 f" as a tuple (e.g. `args=({args},)`)" 

526 ) 

527 raise TypeError(suggestion_tuple) from exp 

528 

529 fun = lambda t, x, fun=fun: fun(t, x, *args) 

530 jac = options.get('jac') 

531 if callable(jac): 

532 options['jac'] = lambda t, x: jac(t, x, *args) 

533 

534 if t_eval is not None: 

535 t_eval = np.asarray(t_eval) 

536 if t_eval.ndim != 1: 

537 raise ValueError("`t_eval` must be 1-dimensional.") 

538 

539 if np.any(t_eval < min(t0, tf)) or np.any(t_eval > max(t0, tf)): 

540 raise ValueError("Values in `t_eval` are not within `t_span`.") 

541 

542 d = np.diff(t_eval) 

543 if tf > t0 and np.any(d <= 0) or tf < t0 and np.any(d >= 0): 

544 raise ValueError("Values in `t_eval` are not properly sorted.") 

545 

546 if tf > t0: 

547 t_eval_i = 0 

548 else: 

549 # Make order of t_eval decreasing to use np.searchsorted. 

550 t_eval = t_eval[::-1] 

551 # This will be an upper bound for slices. 

552 t_eval_i = t_eval.shape[0] 

553 

554 if method in METHODS: 

555 method = METHODS[method] 

556 

557 solver = method(fun, t0, y0, tf, vectorized=vectorized, **options) 

558 

559 if t_eval is None: 

560 ts = [t0] 

561 ys = [y0] 

562 elif t_eval is not None and dense_output: 

563 ts = [] 

564 ti = [t0] 

565 ys = [] 

566 else: 

567 ts = [] 

568 ys = [] 

569 

570 interpolants = [] 

571 

572 events, is_terminal, event_dir = prepare_events(events) 

573 

574 if events is not None: 

575 if args is not None: 

576 # Wrap user functions in lambdas to hide the additional parameters. 

577 # The original event function is passed as a keyword argument to the 

578 # lambda to keep the original function in scope (i.e., avoid the 

579 # late binding closure "gotcha"). 

580 events = [lambda t, x, event=event: event(t, x, *args) 

581 for event in events] 

582 g = [event(t0, y0) for event in events] 

583 t_events = [[] for _ in range(len(events))] 

584 y_events = [[] for _ in range(len(events))] 

585 else: 

586 t_events = None 

587 y_events = None 

588 

589 status = None 

590 while status is None: 

591 message = solver.step() 

592 

593 if solver.status == 'finished': 

594 status = 0 

595 elif solver.status == 'failed': 

596 status = -1 

597 break 

598 

599 t_old = solver.t_old 

600 t = solver.t 

601 y = solver.y 

602 

603 if dense_output: 

604 sol = solver.dense_output() 

605 interpolants.append(sol) 

606 else: 

607 sol = None 

608 

609 if events is not None: 

610 g_new = [event(t, y) for event in events] 

611 active_events = find_active_events(g, g_new, event_dir) 

612 if active_events.size > 0: 

613 if sol is None: 

614 sol = solver.dense_output() 

615 

616 root_indices, roots, terminate = handle_events( 

617 sol, events, active_events, is_terminal, t_old, t) 

618 

619 for e, te in zip(root_indices, roots): 

620 t_events[e].append(te) 

621 y_events[e].append(sol(te)) 

622 

623 if terminate: 

624 status = 1 

625 t = roots[-1] 

626 y = sol(t) 

627 

628 g = g_new 

629 

630 if t_eval is None: 

631 ts.append(t) 

632 ys.append(y) 

633 else: 

634 # The value in t_eval equal to t will be included. 

635 if solver.direction > 0: 

636 t_eval_i_new = np.searchsorted(t_eval, t, side='right') 

637 t_eval_step = t_eval[t_eval_i:t_eval_i_new] 

638 else: 

639 t_eval_i_new = np.searchsorted(t_eval, t, side='left') 

640 # It has to be done with two slice operations, because 

641 # you can't slice to 0th element inclusive using backward 

642 # slicing. 

643 t_eval_step = t_eval[t_eval_i_new:t_eval_i][::-1] 

644 

645 if t_eval_step.size > 0: 

646 if sol is None: 

647 sol = solver.dense_output() 

648 ts.append(t_eval_step) 

649 ys.append(sol(t_eval_step)) 

650 t_eval_i = t_eval_i_new 

651 

652 if t_eval is not None and dense_output: 

653 ti.append(t) 

654 

655 message = MESSAGES.get(status, message) 

656 

657 if t_events is not None: 

658 t_events = [np.asarray(te) for te in t_events] 

659 y_events = [np.asarray(ye) for ye in y_events] 

660 

661 if t_eval is None: 

662 ts = np.array(ts) 

663 ys = np.vstack(ys).T 

664 elif ts: 

665 ts = np.hstack(ts) 

666 ys = np.hstack(ys) 

667 

668 if dense_output: 

669 if t_eval is None: 

670 sol = OdeSolution(ts, interpolants) 

671 else: 

672 sol = OdeSolution(ti, interpolants) 

673 else: 

674 sol = None 

675 

676 return OdeResult(t=ts, y=ys, sol=sol, t_events=t_events, y_events=y_events, 

677 nfev=solver.nfev, njev=solver.njev, nlu=solver.nlu, 

678 status=status, message=message, success=status >= 0)