def test_adaptive(ode, y0): """ Проверяем алгоритмы выбора шага """ t0, t1 = 0, 4 * np.pi atol = 1e-6 rtol = 1e-3 tss = [] yss = [] methods = ( (ExplicitEulerMethod(), AdaptType.RUNGE), (RungeKuttaMethod(coeffs.rk4_coeffs), AdaptType.RUNGE), (EmbeddedRungeKuttaMethod(coeffs.dopri_coeffs), AdaptType.EMBEDDED), ) for method, adapt_type in methods: ode.clear_call_counter() ts, ys = adaptive_step_integration(method=method, ode=ode, y_start=y0, t_span=(t0, t1), adapt_type=adapt_type, atol=atol, rtol=rtol) print(f'{method.name} took {ode.get_call_counter()} function calls') tss.append(np.array(ts)) yss.append(ys) ts = np.array(sorted([t for ts in tss for t in ts])) exact = ode[ts].T y0 = np.array([y[0] for y in exact]) # plots fig1, ax1 = plt.subplots(num='y(t)') fig1.suptitle('test_adaptive: y(t)') ax1.set_xlabel('t'), ax1.set_ylabel('y') ax1.plot(ts, y0, 'ko-', label='exact') fig2, ax2 = plt.subplots(num='dt(t)') fig2.suptitle('test_adaptive: step sizes') ax2.set_xlabel('t'), ax2.set_ylabel('dt') fig3, ax3 = plt.subplots(num='dy(t)') fig3.suptitle('test_adaptive: accuracies') ax3.set_xlabel('t'), ax3.set_ylabel('accuracy') for (m, _), ts, ys in zip(methods, tss, yss): ax1.plot(ts, [y[0] for y in ys], '.', label=m.name) ax2.plot(ts[:-1], ts[1:] - ts[:-1], '.-', label=m.name) ax3.plot(ts, get_accuracy(ode[ts].T, ys), '.-', label=m.name) ax1.legend() ax2.legend() ax3.legend() plt.show()
def test_stiff(): """ Проверяем явные и неявные методы на жёсткой задаче https://en.wikipedia.org/wiki/Van_der_Pol_oscillator Q: почему даже метод Розенброка иногда уменьшает шаг почти до нуля? """ t0 = 0 t1 = 800 * np.pi mu = 1000 y0 = np.array([2., 0.]) f = VanDerPol(y0, mu) fig1, ax1 = plt.subplots() fig2, (ax21, ax22) = plt.subplots(1, 2) fig3, ax3 = plt.subplots() colors = 'rgbcmyk' for i, (method, adapt_type) in enumerate([ (ExplicitEulerMethod(), AdaptType.RUNGE), (ImplicitEulerMethod(), AdaptType.RUNGE), (EmbeddedRosenbrockMethod(coeffs.rosenbrock23_coeffs), AdaptType.EMBEDDED), ]): f.clear_call_counter() ts, ys = adaptive_step_integration(method, f, y0, (t0, t1), adapt_type=adapt_type, atol=1e-6, rtol=1e-3) print( f'{method.name}: {len(ts)-1} steps, {f.get_call_counter()} RHS calls' ) ax1.plot([y[0] for y in ys], [y[1] for y in ys], f'{colors[i]}.--', label=method.name) ax21.plot(ts, [y[0] for y in ys], f'{colors[i]}.--', label=method.name) ax22.plot(ts, [y[1] for y in ys], f'{colors[i]}.--', label=method.name) ax3.plot(ts[:-1], np.array(ts[1:]) - np.array(ts[:-1]), f'{colors[i]}.--', label=method.name) ax1.set_xlabel('x'), ax1.set_ylabel('y'), ax1.legend() fig1.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, y(x)') ax21.set_xlabel('t'), ax21.set_ylabel('x'), ax21.legend() ax22.set_xlabel('t'), ax22.set_ylabel('y'), ax22.legend() fig2.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, x(t)') ax3.set_xlabel('t'), ax3.set_ylabel('dt'), ax3.legend() fig3.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, dt(t)') plt.show()
def test_stiff(): """ test explicit vs implicit methods on a stiff problem """ t0 = 0 t1 = 800*np.pi mu = 1000 y0 = np.array([2., 0.]) f = VanDerPol(y0, mu) fig1, ax1 = plt.subplots() fig2, (ax21, ax22) = plt.subplots(1, 2) fig3, ax3 = plt.subplots() colors = 'rgbcmyk' for i, (method, adapt_type) in enumerate( [ (ExplicitEulerMethod(), AdaptType.RUNGE), (ImplicitEulerMethod(), AdaptType.RUNGE), (EmbeddedRosenbrockMethod(coeffs.rosenbrock23_coeffs), AdaptType.EMBEDDED), ] ): f.clear_call_counter() ts, ys = adaptive_step_integration(method, f, y0, (t0, t1), adapt_type=adapt_type, atol=1e-6, rtol=1e-3) print(f'{method.name}: {len(ts)-1} steps, {f.get_call_counter()} RHS calls') ax1.plot([y[0] for y in ys], [y[1] for y in ys], f'{colors[i]}.--', label=method.name) ax21.plot(ts, [y[0] for y in ys], f'{colors[i]}.--', label=method.name) ax22.plot(ts, [y[1] for y in ys], f'{colors[i]}.--', label=method.name) ax3.plot(ts[:-1], np.array(ts[1:]) - np.array(ts[:-1]), f'{colors[i]}.--', label=method.name) ax1.set_xlabel('x'), ax1.set_ylabel('y'), ax1.legend() fig1.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, y(x)') ax21.set_xlabel('t'), ax21.set_ylabel('x'), ax21.legend() ax22.set_xlabel('t'), ax22.set_ylabel('y'), ax22.legend() fig2.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, x(t)') ax3.set_xlabel('t'), ax3.set_ylabel('dt'), ax3.legend() fig3.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, dt(t)') plt.show()
def test_stiff(): """ Проверяем явные и неявные методы на жёсткой задаче https://en.wikipedia.org/wiki/Van_der_Pol_oscillator Q: почему даже метод Розенброка иногда уменьшает шаг почти до нуля? """ t0 = 0 t1 = 2500 mu = 1000 y0 = np.array([2., 0.]) ode = VanDerPol(y0, mu) fig, axs = plt.subplots(2, 2, figsize=(10, 8)) colors = 'rgbcmyk' for i, (method, adapt_type) in enumerate([ (ExplicitEulerMethod(), AdaptType.RUNGE), (ImplicitEulerMethod(), AdaptType.RUNGE), (EmbeddedRosenbrockMethod(coeffs.rosenbrock23_coeffs), AdaptType.EMBEDDED), ]): ode.clear_call_counter() ts, ys = adaptive_step_integration(method, ode, y0, (t0, t1), adapt_type=adapt_type, atol=1e-6, rtol=1e-3) print( f'{method.name}: {len(ts)-1} steps, {ode.get_call_counter()} RHS calls' ) axs[0, 0].plot([y[0] for y in ys], [y[1] for y in ys], f'{colors[i]}.--', label=method.name) axs[0, 1].plot(ts[:-1], np.diff(ts), f'{colors[i]}.--', label=method.name) axs[1, 0].plot(ts, [y[0] for y in ys], f'{colors[i]}.--', label=method.name) axs[1, 1].plot(ts, [y[1] for y in ys], f'{colors[i]}.--', label=method.name) axs[0, 0].legend(), axs[0, 0].set_title('y(x)') axs[0, 1].legend(), axs[0, 1].set_title('dt(t)') axs[1, 0].legend(), axs[1, 0].set_title('x(t)') axs[1, 1].legend(), axs[1, 1].set_title('y(t)') fig.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}') fig.tight_layout() plt.show()
def adaptive_step_integration(method: OneStepMethod, func, y_start, t_span, adapt_type: AdaptType, atol, rtol): """ performs adaptive-step integration using one-step method t_span: (t0, t1) adapt_type: Runge or Embedded tolerances control the error: err <= atol err <= |y| * rtol return: list of t's, list of y's """ y = y_start t, t_end = t_span ys = [y] ts = [t] p = method.p + 1 tol = atol + np.linalg.norm(y) * rtol rside0 = func(t, y) delta = (1 / max(abs(t), abs(t_end)))**(p + 1) + np.linalg.norm(rside0)**( p + 1) h1 = (tol / delta)**(1 / (p + 1)) u1 = ExplicitEulerMethod().step(func, t, y, h1) tnew = t + h1 rside0 = func(tnew, u1) delta = (1 / max(abs(t), abs(t_end)))**(p + 1) + np.linalg.norm(rside0)**( p + 1) h1new = (tol / delta)**(1 / (p + 1)) h = min(h1, h1new) while t < t_end: if t + h > t_end: h = t_end - t if adapt_type == AdaptType.RUNGE: y1 = method.step(func, t, y, h) yhalf = method.step(func, t, y, h / 2) y2 = method.step(func, t + h / 2, yhalf, h / 2) error = (y2 - y1) / (2**p - 1) ybetter = y2 + error else: ybetter, error = method.embedded_step(func, t, y, h) if np.linalg.norm(error) < tol: ys.append(ybetter) ts.append(t + h) y = ybetter t += h print(t) h *= (tol / np.linalg.norm(error))**(1 / (p + 1)) * 0.9 return ts, ys
def test_adaptive_order(): """ Проверяем сходимость Q: почему наклон линии (число в скобках) соответствует порядку метода? """ t0, t1 = 0, 2 * np.pi y0 = np.array([1., 1.]) ode = Harmonic(y0, 1, 1) methods = ( (ExplicitEulerMethod(), AdaptType.RUNGE), (RungeKuttaMethod(coeffs.rk4_coeffs), AdaptType.RUNGE), (RungeKuttaMethod(coeffs.dopri_coeffs), AdaptType.RUNGE), (EmbeddedRungeKuttaMethod(coeffs.dopri_coeffs), AdaptType.EMBEDDED), ) tols = 10.**-np.arange(3, 9) plt.figure(figsize=(9, 6)) for i, (method, adapt_type) in enumerate(methods): print(method.name) fcs = [] errs = [] for tol in tols: ode.clear_call_counter() ts, ys = adaptive_step_integration(method=method, ode=ode, y_start=y0, t_span=(t0, t1), adapt_type=adapt_type, atol=tol, rtol=tol * 1e3) err = np.linalg.norm(ys[-1] - ode[t1]) fc = ode.get_call_counter() print( f'{method.name} with {adapt_type.name}: {fc} RHS calls, err = {err:.5f}' ) errs.append(err) fcs.append(fc) x = np.log10(fcs) y = -np.log10(errs) k, b = np.polyfit(x, y, 1) plt.plot(x, k * x + b, 'k:') plt.plot(x, y, 'p', label=f'{method.name} {adapt_type} ({k:.2f})') plt.title('test_adaptive_order: check RHS evals') plt.xlabel('log10(function_calls)') plt.ylabel('accuracy') plt.legend() plt.show()
def test_arenstorf(): """ https://en.wikipedia.org/wiki/Richard_Arenstorf#The_Arenstorf_Orbit https://commons.wikimedia.org/wiki/File:Arenstorf_Orbit.gif Q: which parts of the orbit are fastest? """ problem = Arenstorf() t0, t1 = 0, 1 * problem.t_period y0 = problem.y0 atol = 1e-6 rtol = 1e-3 tss = [] yss = [] methods = ( (ExplicitEulerMethod(), AdaptType.RUNGE), (RungeKuttaMethod(coeffs.rk4_coeffs), AdaptType.RUNGE), (EmbeddedRungeKuttaMethod(coeffs.dopri_coeffs), AdaptType.EMBEDDED), ) fig1, ax1 = plt.subplots(num='traj') fig1.suptitle('Arenstorf orbit: trajectory') ax1.set_xlabel('x1'), ax1.set_ylabel('x2') fig2, ax2 = plt.subplots(num='dt(t)') fig2.suptitle('Arenstorf orbit: step sizes') ax2.set_xlabel('t'), ax2.set_ylabel('dt') for method, adapt_type in methods: ts, ys = adaptive_step_integration(method=method, func=problem, y_start=y0, t_span=(t0, t1), adapt_type=adapt_type, atol=atol, rtol=rtol) tss.append(np.array(ts)) yss.append(ys) for (m, _), ts, ys in zip(methods, tss, yss): ax1.plot([y[0] for y in ys], [y[1] for y in ys], ':', label=m.name) ax2.plot(ts[:-1], ts[1:] - ts[:-1], '.-', label=m.name) ax1.plot(0, 0, 'bo', label='Earth') ax1.plot(1, 0, '.', color='grey', label='Moon') ax1.legend() ax2.legend() plt.show()
def test_stiff(): """ test explicit vs implicit methods on a stiff problem """ t0 = 0 t1 = 8*np.pi mu = 0 y0 = np.array([2., 0.]) f = VanDerPol(y0, mu) colors = 'rgbcmyk' for i, (method, adapt_type) in enumerate( [ (ExplicitEulerMethod(), AdaptType.RUNGE), (ImplicitEulerMethod(), AdaptType.RUNGE), (RungeKuttaMethod(collection.rk4_coeffs), AdaptType.RUNGE), (EmbeddedRosenbrockMethod(collection.rosenbrock23_coeffs), AdaptType.EMBEDDED), ] ): f.clear_call_counter() ts, ys = adaptive_step_integration(method, f, y0, (t0, t1), adapt_type=adapt_type, atol=1e-6, rtol=1e-3) print(f'{method.name}: {len(ts)-1} steps, {f.get_call_counter()} RHS calls') plt.figure(1) plt.plot([y[0] for y in ys], [y[1] for y in ys], f'{colors[i]}.--', label=method.name) plt.figure(2) plt.subplot(1,2,1), plt.plot(ts, [y[0] for y in ys], f'{colors[i]}.--', label=method.name) plt.subplot(1,2,2), plt.plot(ts, [y[1] for y in ys], f'{colors[i]}.--', label=method.name) plt.figure(3) plt.plot(ts[:-1], np.array(ts[1:]) - np.array(ts[:-1]), f'{colors[i]}.--', label=method.name) plt.figure(1) plt.xlabel('x'), plt.ylabel('y'), plt.legend() plt.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, y(x)') plt.figure(2) plt.subplot(1,2,1), plt.xlabel('t'), plt.ylabel('x'), plt.legend() plt.subplot(1,2,2), plt.xlabel('t'), plt.ylabel('y'), plt.legend() plt.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, x(t)') plt.figure(3) plt.xlabel('t'), plt.ylabel('dt'), plt.legend() plt.suptitle(f'test_stiff: Van der Pol, mu={mu:.2f}, dt(t)') plt.show()
def test_adaptive_order(): """ test adaptive algorithms convergence """ t0, t1 = 0, 2 * np.pi y0 = np.array([1., 1.]) f = Harmonic(y0, 1, 1) methods = ( (ExplicitEulerMethod(), AdaptType.RUNGE), (RungeKuttaMethod(coeffs=collection.rk4_coeffs), AdaptType.RUNGE), (EmbeddedRungeKuttaMethod(coeffs=collection.dopri_coeffs), AdaptType.EMBEDDED), ) tols = 10.**-np.arange(3, 9) plt.figure() for i, (method, adapt_type) in enumerate(methods): print(method.name) fcs = [] errs = [] for tol in tols: f.clear_call_counter() ts, ys = adaptive_step_integration(method=method, func=f, y_start=y0, t_span=(t0, t1), adapt_type=adapt_type, atol=tol, rtol=tol * 1e3) err = np.linalg.norm(ys[-1] - f[t1]) fc = f.get_call_counter() print(f'{method.name}: {fc} RHS calls, err = {err:.5f}') errs.append(err) fcs.append(fc) x = np.log10(fcs) y = -np.log10(errs) k, b = np.polyfit(x, y, 1) plt.plot(x, k * x + b, 'k:') plt.plot(x, y, 'p', label=f'{method.name} ({k:.2f})') plt.suptitle('test_adaptive_order: check RHS evals') plt.xlabel('log10(function_calls)') plt.ylabel('accuracy') plt.legend() plt.show()
def test_multi_step(): """ Проверяем методы Адамса Q: сравните правые графики для обоих случаев и объясните разницу """ y0 = np.array([0., 1.]) t0 = 0 t1 = np.pi dt = 0.1 f = Harmonic(y0, 1, 1) ts = np.arange(t0, t1 + dt, dt) exact = f[ts].T for one_step_method in [ RungeKuttaMethod(collection.rk4_coeffs), ExplicitEulerMethod(), ]: fig, (ax1, ax2) = plt.subplots(1, 2) ax1.plot(ts, [e[0] for e in exact], 'k', label='Exact') for p, c in adams_coeffs.items(): f.clear_call_counter() t_adams, y_adams = adams(f, y0, ts, c, one_step_method=one_step_method) n_calls = f.get_call_counter() print( f'{p}-order multi-step with one-step {one_step_method.name}: {n_calls} function calls' ) err = get_accuracy(exact, y_adams) label = f"Adams's order {p}" ax1.plot(t_adams, [y[0] for y in y_adams], '.--', label=label) ax2.plot(t_adams, err, '.--', label=label) ax1.legend(), ax1.set_title('y(t)') ax2.legend(), ax2.set_title('accuracy') fig.suptitle( f'test_multi_step\none step method: {one_step_method.name}') fig.tight_layout() plt.show()
def test_one_step(): """ Проверяем методы Эйлера и Рунге-Кутты """ y0 = np.array([0., 1.]) t0 = 0 t1 = np.pi ode = Harmonic(y0, 1, 1) for dt in [0.1, 0.01]: ts = np.arange(t0, t1 + dt, dt) exact = ode[ts].T fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4)) ax1.plot(ts, [e[0] for e in exact], 'k', label='Exact') colors = 'rgbcmyk' for i, method in enumerate([ ExplicitEulerMethod(), ImplicitEulerMethod(), RungeKuttaMethod(collection.rk4_coeffs), RungeKuttaMethod(collection.dopri_coeffs), ]): ode.clear_call_counter() _, y = fix_step_integration(method, ode, y0, ts) n_calls = ode.get_call_counter() print( f'One-step {method.name}: {len(y)-1} steps, {n_calls} function calls' ) ax1.plot(ts, [_y[0] for _y in y], f'{colors[i]}.--', label=method.name) ax2.plot(ts, get_accuracy(exact, y), f'{colors[i]}.--', label=method.name) ax1.legend(), ax1.set_title('y(t)') ax2.legend(), ax2.set_title('accuracy') fig.suptitle(f'test_one_step, dt={dt}') fig.tight_layout() plt.show()
def test_multi_step(): """ test Adams method Q: compare the right plot for both cases and explain the difference """ y0 = np.array([0., 1.]) t0 = 0 t1 = 1. dt = 0.1 f = Harmonic(y0, 1, 1) ts = np.arange(t0, t1 + dt, dt) exact = f[ts].T for one_step_method in [ RungeKuttaMethod(collection.rk4_coeffs), ExplicitEulerMethod(), ]: _, (ax1, ax2) = plt.subplots(1, 2) ax1.plot(ts, [e[0] for e in exact], 'k', label='Exact') for p, c in adams_coeffs.items(): f.clear_call_counter() t_adams, y_adams = adams(f, y0, ts, c, one_step_method=one_step_method) n_calls = f.get_call_counter() print( f'{p}-order multi-step with one-step {one_step_method.name}: {n_calls} function calls' ) err = get_accuracy(exact, y_adams) label = f"Adams's order {p}" ax1.plot(t_adams, [y[0] for y in y_adams], '.--', label=label) ax2.plot(t_adams, err, '.--', label=label) ax1.set_xlabel('t'), ax1.set_ylabel('y'), ax1.legend() ax2.set_xlabel('t'), ax2.set_ylabel('accuracy'), ax2.legend() plt.suptitle( f'test_multi_step\none step method: {one_step_method.name}') plt.show()
def test_multi_step(): """ test Adams method Q: compare the right plot for both cases and explain the difference """ y0 = np.array([0., 1.]) t0 = 0 t1 = 1. dt = 0.1 f = Harmonic(y0, 1, 1) ts = np.arange(t0, t1 + dt, dt) exact = f[ts].T for one_step_method in [ RungeKuttaMethod(collection.rk4_coeffs), ExplicitEulerMethod(), ]: plt.figure() plt.subplot(1, 2, 1), plt.plot(ts, [e[0] for e in exact], 'k', label='Exact') for p, c in adams_coeffs.items(): t_adams, y_adams = adams(f, y0, ts, c, one_step_method=one_step_method) print(f'Function calls: {f.get_call_counter()}') err = get_log_error(exact, y_adams) label = f"Adams's order {p}" plt.subplot(1, 2, 1), plt.plot(t_adams, [y[0] for y in y_adams], '.--', label=label) plt.subplot(1, 2, 2), plt.plot(t_adams, err, '.--', label=label) plt.subplot(1, 2, 1), plt.xlabel('t'), plt.ylabel('y'), plt.legend() plt.subplot(1, 2, 2), plt.xlabel('t'), plt.ylabel('accuracy'), plt.legend() plt.suptitle( f'test_multi_step\none step method: {one_step_method.name}') plt.show()
def test_one_step(): """ test Euler and RK methods """ y0 = np.array([0., 1.]) t0 = 0 t1 = np.pi / 2 dt = 0.1 f = Harmonic(y0, 1, 1) ts = np.arange(t0, t1 + dt, dt) exact = f[ts].T _, (ax1, ax2) = plt.subplots(1, 2) ax1.plot(ts, [e[0] for e in exact], 'k', label='Exact') colors = 'rgbcmyk' for i, method in enumerate([ ExplicitEulerMethod(), ImplicitEulerMethod(), RungeKuttaMethod(collection.rk4_coeffs), RungeKuttaMethod(collection.dopri_coeffs), ]): f.clear_call_counter() _, y = fix_step_integration(method, f, y0, ts) n_calls = f.get_call_counter() print( f'One-step {method.name}: {len(y)-1} steps, {n_calls} function calls' ) ax1.plot(ts, [_y[0] for _y in y], f'{colors[i]}.--', label=method.name) ax2.plot(ts, get_accuracy(exact, y), f'{colors[i]}.--', label=method.name) ax1.set_xlabel('t'), ax1.set_ylabel('y'), ax1.legend() ax2.set_xlabel('t'), ax2.set_ylabel('accuracy'), ax2.legend() plt.suptitle('test_one_step') plt.show()
def test_one_step(): """ test Euler and RK methods """ y0 = np.array([0., 1.]) t0 = 0 t1 = np.pi / 2 dt = 0.1 f = Harmonic(y0, 1, 1) ts = np.arange(t0, t1 + dt, dt) exact = f[ts].T plt.figure() plt.subplot(1, 2, 1) plt.plot(ts, [e[0] for e in exact], 'k', label='Exact') colors = 'rgbcmyk' for i, method in enumerate([ ExplicitEulerMethod(), ImplicitEulerMethod(), RungeKuttaMethod(collection.rk4_coeffs), RungeKuttaMethod(collection.dopri_coeffs), ]): _, y = fix_step_integration(method, f, y0, ts) print(f'len(Y): {len(y)}') print(f'Function calls: {f.get_call_counter()}') plt.subplot(1, 2, 1), plt.plot(ts, [_y[0] for _y in y], f'{colors[i]}.--', label=method.name) plt.subplot(1, 2, 2), plt.plot(ts, get_log_error(exact, y), f'{colors[i]}.--', label=method.name) plt.subplot(1, 2, 1), plt.xlabel('t'), plt.ylabel('y'), plt.legend() plt.subplot(1, 2, 2), plt.xlabel('t'), plt.ylabel('accuracy'), plt.legend() plt.suptitle('test_one_step') plt.show()
def test_adaptive(f, y0): """ test adaptive step algorithms """ t0, t1 = 0, 4 * np.pi atol = 1e-6 rtol = 1e-3 tss = [] yss = [] methods = ( (ExplicitEulerMethod(), AdaptType.RUNGE), (RungeKuttaMethod(coeffs=collection.rk4_coeffs), AdaptType.RUNGE), (EmbeddedRungeKuttaMethod(coeffs=collection.dopri_coeffs), AdaptType.EMBEDDED), ) for method, adapt_type in methods: f.clear_call_counter() ts, ys = adaptive_step_integration(method=method, func=f, y_start=y0, t_span=(t0, t1), adapt_type=adapt_type, atol=atol, rtol=rtol) print(f'{method.name} took {f.get_call_counter()} function calls') tss.append(np.array(ts)) yss.append(ys) ts = np.array(sorted([t for ts in tss for t in ts])) exact = f[ts].T y0 = np.array([y[0] for y in exact]) # plots plt.figure('y(t)'), plt.suptitle('test_adaptive: y(t)'), plt.xlabel( 't'), plt.ylabel('y') plt.plot(ts, y0, 'ko-', label='exact') plt.figure('dt(t)'), plt.suptitle('test_adaptive: step sizes'), plt.xlabel( 't'), plt.ylabel('dt') plt.figure('dy(t)'), plt.suptitle('test_adaptive: accuracies'), plt.xlabel( 't'), plt.ylabel('accuracy') for (m, _), ts, ys in zip(methods, tss, yss): plt.figure('y(t)'), plt.plot(ts, [y[0] for y in ys], '.', label=m.name) plt.figure('dt(t)'), plt.plot(ts[:-1], ts[1:] - ts[:-1], '.-', label=m.name) plt.figure('dy(t)'), plt.plot(ts, get_log_error(f[ts].T, ys), '.-', label=m.name) plt.figure('y(t)'), plt.legend() plt.figure('dt(t)'), plt.legend() plt.figure('dy(t)'), plt.legend() plt.show()