def test_gauss_vs_cq(): """ check quad_gauss() versus composite_quad() on the same number of function evaluations """ x0, x1 = 0, np.pi/2 p = Harmonic(0, 1) y0 = p[x0, x1] n_nodes = 2 Y_gauss = [] Y_cquad = [] n_intervals = np.arange(1, 256, 5) for n in n_intervals: n_evals = n * n_nodes Y_gauss.append(quad_gauss(p, x0, x1, n_evals)) Y_cquad.append(composite_quad(p, x0, x1, n, n_nodes)) accuracy_gauss = get_log_error(Y_gauss, y0 * np.ones_like(Y_gauss)) accuracy_gauss[accuracy_gauss > 17] = 17 accuracy_cquad = get_log_error(Y_cquad, y0 * np.ones_like(Y_cquad)) accuracy_cquad[accuracy_cquad > 17] = 17 plt.plot(np.log10(n_intervals), accuracy_gauss, '.:', label=f'gauss') plt.plot(np.log10(n_intervals), accuracy_cquad, '.:', label=f'2-node CQ') plt.legend() plt.ylabel('accuracy') plt.xlabel('log10(n_evals)') plt.suptitle(f'test gauss vs CQ') plt.show()
def test_multi_step(): """ test Adams method """ 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 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=RungeKuttaMethod(collection.rk4_coeffs)) 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('multi-step methods') plt.show()
def test_quad_gauss_degree(): """ check gaussian quadrature degree """ x0, x1 = 0, 1 max_degree = 8 for deg in range(2, max_degree): p = Monome(deg) y0 = p[x0, x1] max_node_count = range(2, 6) Y = [ quad_gauss(p, x0, x1, node_count) for node_count in max_node_count ] accuracy = get_log_error(Y, y0 * np.ones_like(Y)) accuracy[np.isinf(accuracy)] = 17 # check accuracy is good enough for node_count, acc in zip(max_node_count, accuracy): if 2 * node_count >= deg + 1: assert acc > 6 plt.plot(max_node_count, accuracy, '.:', label=f'x^{deg}') plt.legend() plt.ylabel('accuracy') plt.xlabel('node count') plt.suptitle(f'test quad gauss') plt.show()
def tes_quad_degree(): """ check quadrature degree Q: why in some cases x^n integrated perfectly with only n nodes? """ x0, x1 = 0, 1 max_degree = 7 for deg in range(1, max_degree): p = Monome(deg) y0 = p[x0, x1] max_node_count = range(1, max_degree + 1) Y = [ quad(p, x0, x1, np.linspace(x0, x1, node_count)) for node_count in max_node_count ] # Y = [quad(p, x0, x1, x0 + (x1-x0) * np.random.random(node_count)) for node_count in max_node_count] accuracy = get_log_error(Y, y0 * np.ones_like(Y)) accuracy[np.isinf(accuracy)] = 17 # check accuracy is good enough for node_count, acc in zip(max_node_count, accuracy): if node_count >= deg + 1: assert acc > 6 plt.plot(max_node_count, accuracy, '.:', label=f'x^{deg}') plt.legend() plt.ylabel('accuracy') plt.xlabel('node_count') plt.suptitle(f'test quad') 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.rk4_coeffs), AdaptType.RUNGE), (EmbeddedRungeKuttaMethod(coeffs.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 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_log_error(f[ts].T, ys), '.-', label=m.name) ax1.legend() ax2.legend() ax3.legend() plt.show()
def test_composite_quad(): plt.figure() x0, x1 = 0, 1 L = 2 n_intervals = [L**q for q in range(0, 8)] n_nodes = 3 for i, degree in enumerate((5, 6)): p = Monome(degree) Y = [ composite_quad(p, x0, x1, n_intervals=n, n_nodes=n_nodes) for n in n_intervals ] accuracy = get_log_error(Y, p[x0, x1] * np.ones_like(Y)) x = np.log10(n_intervals) # check convergence ind = np.isfinite(x) & np.isfinite(accuracy) k, b = np.polyfit(x[ind], accuracy[ind], 1) aitken_degree = aitken(*Y[0:6:2], L**2) plt.subplot(1, 2, i + 1) plt.plot(x, k * x + b, 'b:', label=f'{k:.2f}*x+{b:.2f}') plt.plot(x, aitken_degree * x + b, 'm:', label=f'aitken estimation') plt.plot(x, accuracy, 'kh', label=f'accuracy for x^{degree}') plt.xlabel('log10(node count)') plt.ylabel('accuracy') plt.legend() assert np.abs(aitken_degree - k) < 0.5, \ f'Aitken estimation {aitken_degree:.2f} is too far from actual {k:.2f}' plt.show()
def test_composite_quad(n_nodes): """ test composite 2-, 3-, 5-node quads Q: explain converge speed for each case """ fig, ax = plt.subplots(1, 2) x0, x1 = 0, 1 L = 2 n_intervals = [L ** q for q in range(0, 8)] for i, degree in enumerate((5, 6)): p = Monome(degree) Y = [composite_quad(p, x0, x1, n_intervals=n, n_nodes=n_nodes) for n in n_intervals] accuracy = get_log_error(Y, p[x0, x1] * np.ones_like(Y)) x = np.log10(n_intervals) # check convergence ind = np.isfinite(x) & np.isfinite(accuracy) k, b = np.polyfit(x[ind], accuracy[ind], 1) aitken_degree = aitken(*Y[0:6:2], L ** 2) ax[i].plot(x, k*x+b, 'b:', label=f'{k:.2f}*x+{b:.2f}') ax[i].plot(x, aitken_degree*x+b, 'm:', label=f'aitken ({aitken_degree:.2f})') ax[i].plot(x, accuracy, 'kh', label=f'accuracy for x^{degree}') ax[i].set_title(f'{n_nodes}-node CQ for x^{degree}') ax[i].set_xlabel('log10(n_intervals)') ax[i].set_ylabel('accuracy') ax[i].legend() if n_nodes < degree: assert np.abs(aitken_degree - k) < 0.5, \ f'Aitken estimation {aitken_degree:.2f} is too far from actual {k:.2f}' plt.show()
def test_composite_quad_degree(v): """ Q: convergence maybe somewhat between 3 and 4, why? """ from .variants import params fig, (ax1, ax2) = plt.subplots(1, 2) a, b, alpha, beta, f = params(v) x0, x1 = a, b # a, b = -10, 10 exact = sp_quad(lambda x: f(x) / (x - a)**alpha / (b - x)**beta, x0, x1)[0] # plot weights xs = np.linspace(x0, x1, 101) ys = 1 / ((xs - a)**alpha * (b - xs)**beta) ax1.plot(xs, ys, label='weights') ax = list(ax1.axis()) ax[2] = 0. ax1.axis(ax) ax1.set_xlabel('x') ax1.set_ylabel('p(x)') ax1.legend() L = 2 n_intervals = [L**q for q in range(2, 10)] n_nodes = 4 Y = [ composite_quad(f, x0, x1, n_intervals=n, n_nodes=n_nodes, a=a, b=b, alpha=alpha, beta=beta) for n in n_intervals ] accuracy = get_log_error(Y, exact * np.ones_like(Y)) x = np.log10(n_intervals) k, b = np.polyfit(x, accuracy, 1) assert k > 1, 'composite quad did not converge!' aitken_degree = aitken(*Y[5:8], L) # plot acc ax2.plot(x, accuracy, 'kh') ax2.plot(x, k * x + b, 'b:', label=f'{k:.2f}*x+{b:.2f}') ax2.set_xlabel('log10(n_intervals)') ax2.set_ylabel('accuracy') ax2.legend() fig.suptitle(f'variant #{v} (alpha={alpha:4.2f}, beta={beta:4.2f})\n' f'aitken estimation: {aitken_degree:.2f}') plt.show()
def test_composite_quad_degree(v): """ Q: convergence maybe somewhat between 3 and 4, why? """ from .variants import params from .variants import exactint plt.figure() a, b, alpha, beta, f = params(v) x0, x1 = a, b #a, b = -10, 10 exact = exactint( v) #sp_quad(lambda x: f(x) / (x-a)**alpha / (b-x)**beta, x0, x1)[0] # plot weights xs = np.linspace(x0, x1, 101) ys = 1 / (xs - a)**alpha / (b - xs)**beta plt.subplot(1, 2, 1) plt.plot(xs, ys, label='weights') ax = list(plt.axis()) ax[2] = 0. plt.axis(ax) plt.xlabel('x') plt.ylabel('p(x)') plt.legend() L = 2 n_intervals = [L**q for q in range(2, 10)] n_nodes = 3 Y = [ composite_quad(f, x0, x1, n_intervals=n, n_nodes=n_nodes, a=a, b=b, alpha=alpha, beta=beta) for n in n_intervals ] accuracy = get_log_error(Y, exact * np.ones_like(Y)) x = np.log10(n_intervals) aitken_degree = aitken(*Y[5:8], L) # plot acc plt.subplot(1, 2, 2) plt.plot(x, accuracy, 'kh') plt.xlabel(Y) plt.ylabel(exact) plt.suptitle(f'variant #{v} (alpha={alpha:4.2f}, beta={beta:4.2f})\n' f'aitken estimation: {aitken_degree:.2f}') 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_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_log_error(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_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_log_error(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_nc_degree(): """Newton-Cotes algebraic degree of accuracy check""" # 1.a. Применение для формулы с единичным весом a, b, alpha, beta, f = params(variant) alpha, beta = 0, 0 max_s = 7 # проверяем для одночленов степени до 6 включительно for s in range(max_s): f = lambda x: x**s # одночлен x^s J = (b**(s + 1) - a**(s + 1)) / (s + 1) # точное значение интеграла n_range = range(1, 7) # число узлов от 1 до 6 S = [quad(f, a, b, equidist(n, a, b)) for n in n_range] # применяем ИКФ на равномерной сетке # S = [quad(f, a, b, a + (b - a) * np.random.random(n)) for n in n_range] accuracy = get_log_error(S, J * np.ones_like(S)) # считаем точную погрешность accuracy[np.isinf( accuracy )] = -17 # если погрешность 0, то выводим точность в 17 знаков # check accuracy is good enough for n, acc in zip(n_range, accuracy): if n >= s + ( s + 1 ) % 2: # с нечётным числом n узлов АСТ должно быть равно n, иначе n-1 assert acc < -6 plt.plot(n_range, accuracy, '.:', label=f'x^{s}') plt.legend() plt.ylabel('accuracy') plt.xlabel('node_count') plt.suptitle(f'Accuracy test for constant weight') plt.show() # 1.b. Применение для формулы с конкретным весом, стемящимся к бесконечности в a или в b a, b, alpha, beta, f = params(variant) max_s = 7 # проверяем для одночленов степени до 6 включительно J = moments(max_s - 1, a, b, a, b, alpha, beta) # точные значение интегралов for s in range(max_s): f = lambda x: x**s # одночлен x^s n_range = range(1, 7) # число узлов от 1 до 6 S = [ quad(f, a, b, equidist(n, a, b), a, b, alpha, beta) for n in n_range ] # применяем ИКФ на равномерной сетке accuracy = get_log_error(S, J[s]) # считаем точную погрешность accuracy[np.isinf( accuracy )] = -17 # если погрешность 0, то выводим точность в 17 знаков # check accuracy is good enough for n, acc in zip(n_range, accuracy): # АСТ должно быть равно n-1 if n >= s + 1: assert acc < -6 plt.plot(n_range, accuracy, '.:', label=f'x^{s}') plt.legend() plt.ylabel('accuracy') plt.xlabel('node_count') plt.suptitle(f'Accurace test for non-symmetric weight') plt.show()