[Python] SCIPY BPoly.from_derivatives compared with numpy

Discussão em 'Python' iniciado por Stack, Maio 17, 2025.

  1. Stack

    Stack Membro Participativo

    I implemented this comparison between numpy and scipy for doing the same function interpolation. The results show how numpy crushes scipy. Varying on system, I get somewhere between 4x to 10x speedup with numpy. This is not the first time I encounter this. So in general, I wonder:

    why this huge difference in perf? should we do well in viewing scipy as just a reference implementation, and go to other pathways for peformance (numpy, numba)?

    # BPoly.from_derivatives example with 10 000 sinusoidal points and timing comparison
    """
    Creates a sinusoidal dataset of 10 000 points over [0, 2π].
    Interpolates using SciPy's BPoly.from_derivatives and a custom pure NumPy quintic Hermite.
    Verifies exact match, compares timing for coefficient computation and evaluation, and visualizes both.
    """
    import numpy as np
    import time
    from scipy.interpolate import BPoly

    # Generate 10 000 sample points over one period
    n = 10_000
    x = np.linspace(0.0, 2*np.pi, n)
    # Analytical sinusoidal values and derivatives
    y = np.sin(x) # y(x)
    v = np.cos(x) # y'(x)
    a = -np.sin(x) # y''(x)

    # === SciPy implementation with timing ===
    y_and_derivatives = np.column_stack((y, v, a))

    t0 = time.perf_counter()
    bp = BPoly.from_derivatives(x, y_and_derivatives)
    t1 = time.perf_counter()
    scipy_coeff_time = t1 - t0

    # Evaluation timing
    t0 = time.perf_counter()
    y_scipy = bp(x)
    t1 = time.perf_counter()
    scipy_eval_time = t1 - t0

    # === Pure NumPy implementation ===
    def compute_quintic_coeffs(x, y, v, a):
    """
    Compute quintic Hermite coefficients on each interval [x, x[i+1]].
    Returns coeffs of shape (6, n-1).
    """
    m = len(x) - 1
    coeffs = np.zeros((6, m))
    for i in range(m):
    h = x[i+1] - x
    A = np.array([
    [1, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0],
    [0, 0, 2, 0, 0, 0],
    [1, h, h**2, h**3, h**4, h**5],
    [0, 1, 2*h, 3*h**2, 4*h**3, 5*h**4],
    [0, 0, 2, 6*h, 12*h**2, 20*h**3],
    ])
    b = np.array([y, v, a, y[i+1], v[i+1], a[i+1]])
    coeffs[:, i] = np.linalg.solve(A, b)
    return coeffs


    def interp_quintic(x, coeffs, xx):
    """
    Evaluate quintic Hermite using precomputed coeffs.
    x: breakpoints (n,), coeffs: (6, n-1), xx: query points.
    """
    idx = np.searchsorted(x, xx) - 1
    idx = np.clip(idx, 0, len(x) - 2)
    dx = xx - x[idx]
    yy = np.zeros_like(xx)
    for j in range(6):
    yy += coeffs[j, idx] * dx**j
    return yy

    # Coefficient estimation timing for custom
    t0 = time.perf_counter()
    custom_coeffs = compute_quintic_coeffs(x, y, v, a)
    t1 = time.perf_counter()
    cust_coeff_time = t1 - t0

    # Evaluation timing for custom
    t0 = time.perf_counter()
    y_custom = interp_quintic(x, custom_coeffs, x)
    t1 = time.perf_counter()
    cust_eval_time = t1 - t0

    # Verify exact match
    assert np.allclose(y_scipy, y_custom, atol=1e-12), "Custom interp deviates"
    print("Custom NumPy interpolation matches SciPy BPoly for 10 000 points.")

    # Print timing results
    print(f"SciPy coeff time: {scipy_coeff_time:.6f} s")
    print(f"SciPy eval time : {scipy_eval_time:.6f} s")
    print(f"Custom coeff time: {cust_coeff_time:.6f} s")
    print(f"Custom eval time : {cust_eval_time:.6f} s")

    # Visualization

    import matplotlib.pyplot as plt
    plt.plot(x, y_scipy, label='SciPy BPoly')
    plt.plot(x, y_custom, '--', label='Custom NumPy')
    plt.legend()
    plt.title('Sinusoidal interpolation: SciPy vs NumPy Quintic')
    plt.xlabel('x')
    plt.ylabel('y(x) = sin(x)')
    plt.show()

    Continue reading...

Compartilhe esta Página