Funciones regulares definidas matemáticamente#

Regla del trapecio#

Para obtener la regla trapezoidal para aproximar \(\int_{a}^{b}f(x)~\mathrm{d}x\), sea \(x_{0} = a\), \(x_{1} = b\), \(h = b − a\) se utiliza el polinomio de Lagrange

\[ P_{1} = \frac{(x-x_{1})}{(x_{0}-x_{1})}~f(x_{0}) + \frac{(x-x_{0})}{(x_{1}-x_{0})}~f(x_{1}). \]

Entonces

\[ \int_{a}^{b}f(x)~\mathrm{d}x = \int_{x_{0}}^{x_{1}}\left[ \frac{(x-x_{1})}{(x_{0}-x_{1})}~f(x_{0}) + \frac{(x-x_{0})}{(x_{1}-x_{0})}~f(x_{1}) \right]~\mathrm{d}x + \frac{1}{2}\int_{x_{0}}^{x_{1}} f''(\xi(x))(x-x_{0})(x-x_{1})~\mathrm{d}x. \]

Aplicando el teorema del valor medio ponderado para integrales al término del error para algún \(\xi \in (x_{0} , x_{1} )\)

\[\begin{split} \begin{aligned} \int_{x_{0}}^{x_{1}} f''(\xi(x))(x-x_{0})(x-x_{1})~\mathrm{d}x &= f''(\xi) \int_{x_{0}}^{x_{1}}(x-x_{0})(x-x_{1})~\mathrm{d}x, \\ &= f''(\xi) \left[ \frac{x^{3}}{3} - \frac{(x_{1} + x_{0})}{2}x^{2} + x_{0}x_{1}x \right]_{x_{0}}^{x_{1}}, \\ &= - \frac{h^{3}}{6}f''(\xi). \end{aligned} \end{split}\]

Esto implica que

\[\begin{split} \begin{aligned} \int_{a}^{b}f(x)~\mathrm{d}x &= \left[ \frac{(x-x_{1})^{2}}{2(x_{0}-x_{1})}~f(x_{0}) + \frac{(x-x_{0})^{2}}{2(x_{1}-x_{0})}~f(x_{1}) \right]_{x_{0}}^{x_{1}} - \frac{h^{3}}{12}f''(\xi), \\ &= \frac{(x_{1}-x_{0})}{2}\left[f(x_{0}) + f(x_{1}) \right] - \frac{h^{3}}{12}f''(\xi). \end{aligned} \end{split}\]

Definiendo la variable \(h:=x_{1}-x_{0}\) obtenemos la siguiente regla

\[ \int_{a}^{b}f(x)~\mathrm{d}x = \frac{h}{2}\left[f(x_{0}) + f(x_{1}) \right] - \frac{h^{3}}{12}f''(\xi). \]

Regla de Simpson#

La regla de Simpson resulta de integrar en el intervalo \([a, b]\) el segundo polinomio de Lagrange con nodos equiespaciados \(x_{0} = a\), \(x_{2} = b\) y \(x_{1} = a + h\), donde \(h:= (b − a)/2\). Por lo tanto

\[ \int_{a}^{b}f(x)~\mathrm{d}x = \int_{x_{0}}^{x_{2}} \left[ \frac{(x-x_{1})(x-x_{2})}{(x_{0}-x_{1})(x_{0}-x_{2})}~f(x_{0}) + \frac{(x-x_{0})(x-x_{2})}{(x_{1}-x_{0})(x_{1}-x_{2})}~f(x_{1}) + \frac{(x-x_{0})(x-x_{1})}{(x_{2}-x_{0})(x_{2}-x_{1})}~f(x_{2}) \right]~\mathrm{d}x + \int_{x_{0}}^{x_{2}} \frac{(x-x_{0})(x-x_{1})(x-x_{2})}{6}~f^{(3)}(\xi(x))~\mathrm{d}x. \]

Esta ecuación permite obtener un término de error de orden \(O(h^{4})\) que involucra a \(f^{(3)}\). No obstante, es posible deducir un término de orden superior que involucra a \(f^{(4)}\). Para ello suponga que \(f\) se expande en el tercer polinomio de Taylor alrededor de \(x_{1}\). Entonces, para cada \(x\) en \([x_{0}, x_{2}]\), existe un número \(\xi(x)\) en \((x_{0}, x_{2})\) con

\[ f(x) = f(x_{1}) + f'(x_{1})(x-x_{1}) + \frac{f''(x_{1})}{2}(x-x_{1})^{2} + \frac{f'''(x_{1})}{6}(x-x_{1})^{3} + \frac{f^{(4)}(\xi(x))}{24}(x-x_{1})^{4}, \]

y

\[ \int_{x_{0}}^{x_{2}}f(x)~\mathrm{d}x = \left[f(x_{1})(x-x_{1}) + \frac{f'(x_{1})}{2}(x-x_{1})^{2} + \frac{f''(x_{1})}{6}(x-x_{1})^{3} + \frac{f'''(x_{1})}{24}(x-x_{1})^{4} \right]_{x_{0}}^{x_{2}} + \frac{1}{24} \int_{x_{0}}^{x_{2}} f^{(4)}(\xi(x))(x-x_{1})^{4}~\mathrm{d}x. \]

Para algún número \(x_{1} \in (x_{0},x_{2})\), el teorema de valor medio ponderado para integrales implica que

\[ \frac{1}{24} \int_{x_{0}}^{x_{2}} f^{(4)}(\xi(x))(x-x_{1})^{4}~\mathrm{d}x = \frac{f^{(4)}(\xi_{1})}{24}\int_{x_{0}}^{x_{2}} (x-x_{1})^{4}~\mathrm{d}x = \left. \frac{f^{(4)}(\xi_{1})}{120}(x-x_{1})^{5} \right|_{x_{0}}^{x_{2}}. \]

Sin embargo, \(h=x_{2}-x_{1}=x_{1}-x_{0}\) entonces

\[ (x_{2}-x_{1})^{2}-(x_{0}-x_{1})^{2} = (x_{2}-x_{1})^{4} - (x_{0}-x_{1})^{4} = 0, \]

mientras que

\[ (x_{2}-x_{1})^{3}-(x_{0}-x_{1})^{3} = 2h^{3}, \quad (x_{2}-x_{1})^{5}-(x_{0}-x_{1})^{5} = 2h^{5}. \]

Por consiguiente

\[ \int_{x_{0}}^{x_{2}}f(x)~\mathrm{d}x = 2hf(x_{1}) + \frac{h^{3}}{3}f''(x_{1}) + \frac{f^{(4)}(\xi_{1})}{60}h^{5}. \]

Reemplazando \(f''(x_{1})\), tenemos

\[\begin{split} \begin{aligned} \int_{x_{0}}^{x_{2}}f(x)~\mathrm{d}x &= 2hf(x_{1}) + \frac{h^{3}}{3} \left\{\frac{1}{h^{2}} \left[f(x_{0}) - 2f(x_{1}) + f(x_{2}) \right] - \frac{h^{2}}{12}f^{(4)}(\xi_{2}) \right\} + \frac{f^{(4)}(\xi_{1})}{60}h^{5}, \\ &= \frac{h}{3}\left[ f(x_{0}) + 4f(x_{1}) + f(x_{2}) \right] - \frac{h^{5}}{12}\left[ \frac{1}{3}f^{(4)}(\xi_{2}) - \frac{1}{5}f^{(4)}(\xi_{1}) \right] \end{aligned} \end{split}\]

Finalmente, considerando que los valores \(\xi_{1}\) y \(\xi_{2}\) de esta expresión pueden sustituirse por un valor común \(\xi \in (x_{0}, x_{2})\), tenemos

\[ \int_{x_{0}}^{x_{2}}f(x)~\mathrm{d}x = \frac{h}{3}\left[ f(x_{0}) + 4f(x_{1}) + f(x_{2}) \right] - \frac{h^{5}}{90}f^{(4)}(\xi). \]
import numpy as np
import matplotlib.pyplot as plt

# Suprimir advertencias menores de integración durante los bucles de optimización
import warnings
warnings.filterwarnings("ignore")

# Configurar el estilo de graficación para una mejor visualización
plt.style.use('ggplot')
def regla_trapecio(f, a, b, n=1):
    """
    Aproxima la integral definida de f(x) desde a hasta b usando la regla del trapecio.
    - Si n=1, utiliza la regla simple: h/2 * [f(x0) + f(x1)]
    - Si n>1, utiliza la regla compuesta.
    """
    h = (b - a) / n
    x = np.linspace(a, b, n + 1)
    y = f(x)
    
    if n == 1:
        return (h / 2) * (y[0] + y[1])
    else:
        # Forma compuesta: h/2 * [f(x_0) + 2*f(x_1) + ... + 2*f(x_{n-1}) + f(x_n)]
        return (h / 2) * (y[0] + 2 * np.sum(y[1:-1]) + y[-1])


def regla_simpson(f, a, b, n=2):
    """
    Aproxima la integral definida de f(x) desde a hasta b usando la regla de Simpson 1/3.
    - 'n' debe ser un número par. 
    - Si n=2, utiliza la regla simple: h/3 * [f(x0) + 4*f(x1) + f(x2)]
    """
    if n % 2 != 0:
        raise ValueError("El número de subintervalos 'n' debe ser par para la regla de Simpson.")
        
    h = (b - a) / n
    x = np.linspace(a, b, n + 1)
    y = f(x)
    
    if n == 2:
        return (h / 3) * (y[0] + 4 * y[1] + y[2])
    else:
        # Forma compuesta: separando los términos pares de los impares
        suma_impares = np.sum(y[1:-1:2])
        suma_pares = np.sum(y[2:-2:2])
        return (h / 3) * (y[0] + 4 * suma_impares + 2 * suma_pares + y[-1])


def f_test(x):
        return np.exp(-x**2)
a, b = 0.0, 1.0

print("Integrando f(x) = e^(-x^2) en el intervalo [0, 1]\n")

# 1. Aplicando las Reglas Simples
integral_trap_simple = regla_trapecio(f_test, a, b, n=1)
integral_simp_simple = regla_simpson(f_test, a, b, n=2)

print("--- Reglas Simples ---")
print(f"Trapecio simple (n=1): {integral_trap_simple:.6f}")
print(f"Simpson simple  (n=2): {integral_simp_simple:.6f}\n")

# 2. Aplicando las Reglas Compuestas
n_tramos = 10
integral_trap_comp = regla_trapecio(f_test, a, b, n=n_tramos)
integral_simp_comp = regla_simpson(f_test, a, b, n=n_tramos)

print(f"--- Reglas Compuestas (n={n_tramos}) ---")
print(f"Trapecio compuesta: {integral_trap_comp:.6f}")
print(f"Simpson compuesta:  {integral_simp_comp:.6f}\n")

# 3. Valor de referencia exacto usando scipy
valor_real, error_estimado = quad(f_test, a, b)
print("--- Referencia ---")
print(f"Valor real (scipy.integrate.quad): {valor_real:.6f}")
Integrando f(x) = e^(-x^2) en el intervalo [0, 1]

--- Reglas Simples ---
Trapecio simple (n=1): 0.683940
Simpson simple  (n=2): 0.747180

--- Reglas Compuestas (n=10) ---
Trapecio compuesta: 0.746211
Simpson compuesta:  0.746825

--- Referencia ---
Valor real (scipy.integrate.quad): 0.746824

Aplicaciones en sistemas de primer orden#

Considere un problema de valor inicial de la forma

(2)#\[\begin{equation} \dot{y} = f(t,y), \end{equation}\]

integrando por ambos lados de la igualdad en el intervalo \([t_{n}, t_{n+1}]\), tenemos

(3)#\[\begin{equation} y\left(t_{n+1} \right) = y\left(t_{n} \right) + \int_{t_{n}}^{t_{n+1}}f(t,y(t))~\mathrm{d}t. \end{equation}\]

Método del trapecio implícito (Adams-Moulton de 2do orden)#

Aplicando la regla del trapecio a la integral anterior, obtenemos

(4)#\[\begin{equation} y_{n+1} = y_{n} + \frac{h}{2}\left[ f\left(t_{n},y_{n} \right) + f\left(t_{n+1},y_{n+1} \right) \right]. \end{equation}\]

Dado que \(y_{n+1}\) aparece en ambos lados (implícito) es necesario encontrar un algoritmo de búsqueda de raíces como Newton-Raphson para despejarlo.

Para resolver un sistema mediante el método del trapecio implícito, necesitamos encontrar la raíz de una ecuación no lineal en cada paso de tiempo. Para encontrar \(y_{n+1}\) definimos una función residual \(G\left(y_{n+1} \right)\) que debe ser igual a cero

(5)#\[\begin{equation} G\left(y_{n+1} \right) = y_{n+1} - y_{n} - \frac{h}{2}\left[ f\left(t_{n},y_{n} \right) + f\left(t_{n+1},y_{n+1} \right) \right] = 0. \end{equation}\]

Aplicando el método de Newton-Raphson multivariable, la iteración para actualizar la suposición inicial \(y_{n+1}^{(k)}\) es

(6)#\[\begin{equation} y_{n+1}^{(k+1)} = y_{n+1}^{(k)} - J_{G}^{-1}G\left(y_{n+1}^{(k)} \right), \end{equation}\]

donde \(J_{G}\) es la matriz Jacobiana del residual con respecto a \(y_{n+1}\)

(7)#\[\begin{equation} J_{G} = I - \frac{h}{2}J_{f}\left(t_{n+1},y_{n+1} \right). \end{equation}\]
def newton_raphson(f, Jf, tn, yn, h, tol=1e-6, max_iter=50):
    t_next = tn + h
    I = np.eye(len(yn))

    # Implementamos una parte predictora
    # Metodo de Euler hacia adelante
    y_next = yn + h * f(tn, yn)
    f_n = f(tn, yn)

    for _ in range(max_iter):
        f_next = f(t_next, y_next)

        # Evaluamos el residual G(y_{n+1})
        G = y_next - yn - (h/2) * (f_n + f_next)

        # Evaluamos el Jacobiano de G
        JG = I - (h/2) * Jf(t_next, y_next)

        # Computacionalmente mas eficiente que invertir G
        # JG * delta_y = -G
        delta_y = np.linalg.solve(JG, -G)

        # Actualizamos
        # y_next = y_next + delta_y
        y_next = y_next + delta_y

        # Criterio de convergencia
        if np.linalg.norm(delta_y) < tol:
            return y_next

    print(f"Newton-Raphson no converge en t={t_next}")
    return y_next

def trapecio_implicito(f, Jf, tspan, y0, h):
    t0, tfin = tspan
    t = np.arange(t0, tfin + h, h)

    # Reservamos espacio de memoria
    y = np.zeros((len(t), len(y0)))
    y[0] = y0

    for k in range(len(t)-1):
        y[k+1] = newton_raphson(f, Jf, t[k], y[k], h)

    return t, y

def van_der_pol(t, x):
    # x[0] = posicion
    # x[1] = velocidad
    dx1 = x[1]
    dx2 = mu * (1 - x[0]**2) * x[1] - x[0]

    return np.array([dx1, dx2])

def Jacobiano_vanderpol(t, x):
    J = np.zeros((2, 2))
    J[0, 0] = 0.0
    J[0, 1] = 1.0
    J[1, 0] = -2.0 * mu * x[0] * x[1] - 1.0
    J[1, 1] = mu * (1 - x[0]**2)

    return J
# Oscilador de Van der Pol
mu = 5 # parametro de rigidez (mu >>)

# Condiciones iniciales
y0 = np.array([2.0, 0.0])
tspan = [0, 50]
h = 1e-2

# Ejecutamos el integrador
t_eval, y_eval = trapecio_implicito(
    f=van_der_pol,
    Jf=Jacobiano_vanderpol,
    tspan=tspan,
    y0=y0,
    h=h)

plt.figure()
plt.plot(t_eval, y_eval[:,0])
plt.plot(t_eval, y_eval[:,1])


plt.figure()
plt.plot(y_eval[:,0], y_eval[:,1])

plt.show()
../_images/e4aa89ea443938aeccaeffdfad57db2dc0c620b89524a65a37e4236c60cd0b71.png ../_images/0b29375f4572b254309b1e11eea7439c43ce0cd794b12e964bd47cd4b58b1aa9.png