Example #1
0
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()
Example #2
0
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()
Example #3
0
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()
Example #4
0
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()
Example #5
0
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()
Example #6
0
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()
Example #7
0
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()
Example #8
0
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()
Example #9
0
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()
Example #10
0
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()
Example #11
0
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()
Example #12
0
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()
Example #13
0
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()
Example #14
0
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()