Differentiation Example — Numerical Derivatives of sin(x)

This example demonstrates integrationdifferentiation.differentiate() (IMSL DERIV) using Richardson-extrapolated central differences:

  • Compute 1st, 2nd, and 3rd derivatives of \(f(x) = \sin x\)

  • Compare numerical results against analytical derivatives (\(\cos x\), \(-\sin x\), \(-\cos x\))

  • Report maximum absolute errors over \(x \in [0.5,\, 3\pi - 0.5]\)

Example Code

"""IMSL DERIV example: numerical differentiation via Richardson extrapolation.

Demonstrates :func:`integrationdifferentiation.differentiate` (IMSL DERIV):

- Compute 1st, 2nd, and 3rd derivatives of ``f(x) = sin(x)``
- Compare numerical results against analytical derivatives
- Report maximum absolute errors across a range of x values
- Produce a 2-panel SVG: function/derivative curves and per-point errors

Outputs:
- Table printed to stdout
- SVG plot saved to ``test_output/example_imsl_differentiation.svg``
"""

from __future__ import annotations

from pathlib import Path
from typing import Dict

import matplotlib.pyplot as plt
import numpy as np

from integrationdifferentiation import differentiate


def run_demo_imsl_differentiation() -> Dict[str, object]:
    """Run numerical differentiation examples for f(x) = sin(x).

    Computes 1st, 2nd, and 3rd derivatives using Richardson-extrapolated
    central differences and compares against analytical values.

    Args:
        None

    Returns:
        Dict[str, object]: Keys ``max_errors`` (dict of order → max error),
            ``plot_path`` (str).
    """
    x_vals = np.linspace(0.5, 3.0 * np.pi - 0.5, 120)

    # Numerical derivatives at each x
    d1_num = np.array([differentiate(np.sin, float(x), order=1) for x in x_vals])
    d2_num = np.array([differentiate(np.sin, float(x), order=2) for x in x_vals])
    d3_num = np.array([differentiate(np.sin, float(x), order=3) for x in x_vals])

    # Analytical derivatives:  sin′=cos, sin″=-sin, sin‴=-cos
    d1_exact = np.cos(x_vals)
    d2_exact = -np.sin(x_vals)
    d3_exact = -np.cos(x_vals)

    err1 = np.abs(d1_num - d1_exact)
    err2 = np.abs(d2_num - d2_exact)
    err3 = np.abs(d3_num - d3_exact)

    max_errors = {1: float(err1.max()), 2: float(err2.max()), 3: float(err3.max())}

    print("\nIMSL DERIV Example: Numerical Differentiation of sin(x)")
    print("=" * 65)
    print(f"{'Derivative order':<22} {'Max absolute error':>20}")
    print("-" * 65)
    for order, err in max_errors.items():
        label = f"Order {order}"
        print(f"{label:<22} {err:>20.3e}")
    print("=" * 65)
    print("\nAnalytical:  sin′(x)=cos(x),  sin″(x)=−sin(x),  sin‴(x)=−cos(x)")

    # ------------------------------------------------------------------ plot
    output_dir = Path("test_output")
    output_dir.mkdir(parents=True, exist_ok=True)
    plot_path = output_dir / "example_imsl_differentiation.svg"

    colors = {"f": "#1d4ed8", "d1": "#059669", "d2": "#d97706", "d3": "#dc2626"}

    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(11, 8), sharex=True)

    # Panel 1: function and its derivatives
    ax1.plot(x_vals, np.sin(x_vals), color=colors["f"], lw=2.0, label=r"$f(x)=\sin x$")
    ax1.plot(x_vals, d1_num, color=colors["d1"], lw=1.8, ls="--", label=r"$f'$ (numerical)")
    ax1.plot(x_vals, d1_exact, color=colors["d1"], lw=0.8, ls="-", alpha=0.45, label=r"$f'$ (exact)")
    ax1.plot(x_vals, d2_num, color=colors["d2"], lw=1.8, ls="--", label=r"$f''$ (numerical)")
    ax1.plot(x_vals, d2_exact, color=colors["d2"], lw=0.8, ls="-", alpha=0.45, label=r"$f''$ (exact)")
    ax1.plot(x_vals, d3_num, color=colors["d3"], lw=1.8, ls="--", label=r"$f'''$ (numerical)")
    ax1.plot(x_vals, d3_exact, color=colors["d3"], lw=0.8, ls="-", alpha=0.45, label=r"$f'''$ (exact)")
    ax1.axhline(0, color="black", lw=0.6)
    ax1.set_ylabel("Value")
    ax1.set_title(r"$f(x)=\sin x$ and its numerical derivatives (Richardson-extrapolated)")
    ax1.legend(ncol=4, fontsize=8, loc="upper right")
    ax1.grid(True, alpha=0.3)

    # Panel 2: absolute errors
    ax2.semilogy(x_vals, err1 + 1e-20, color=colors["d1"], lw=1.8, label=r"$|e_{f'}|$")
    ax2.semilogy(x_vals, err2 + 1e-20, color=colors["d2"], lw=1.8, label=r"$|e_{f''}|$")
    ax2.semilogy(x_vals, err3 + 1e-20, color=colors["d3"], lw=1.8, label=r"$|e_{f'''}|$")
    ax2.set_xlabel("x")
    ax2.set_ylabel("Absolute error (log scale)")
    ax2.set_title("Absolute errors vs analytical derivatives")
    ax2.legend(fontsize=9)
    ax2.grid(True, alpha=0.3, which="both")

    fig.suptitle("IMSL DERIV: Numerical Differentiation of sin(x)", fontsize=13, fontweight="bold")
    fig.tight_layout()
    fig.savefig(plot_path, format="svg")
    plt.close(fig)

    return {"max_errors": max_errors, "plot_path": str(plot_path)}


if __name__ == "__main__":
    run_demo_imsl_differentiation()

Plot Output

Two-panel plot showing f(x)=sin(x) and its derivatives compared to analytical values, plus absolute error curves

Top panel: \(f(x) = \sin x\) and its 1st, 2nd, and 3rd numerical derivatives overlaid on the analytical curves. Bottom panel: per-point absolute errors on a log scale.

Console Output

IMSL DERIV Example: Numerical Differentiation of sin(x)
=================================================================
Derivative order         Max absolute error
-----------------------------------------------------------------
Order 1                           2.198e-12
Order 2                           6.243e-08
Order 3                           9.123e-04
=================================================================

Analytical:  sin′(x)=cos(x),  sin″(x)=−sin(x),  sin‴(x)=−cos(x)