4.5 Trazadores Cúbicos Natural o libre con Python

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]
..


1. Trazador Cúbico - Planteamiento

Referencia:  Burden 3.5 p105, Chapra 18.6.3 p532, Rodríguez 6.11.1 p244

Tiene como objetivo incorporar condiciones adicionales para la función en los extremos del intervalo donde se encuentran los puntos o "nodos".

Por ejemplo, si los datos son los de una trayectoria en un experimento de física, se podría disponer de la aceleración el punto inicial de las muestras (izquierda) y de salida (derecha) del intervalo de las muestras.

spline 03

El método busca obtener un polinomio de tercer grado para cada sub-intervalo o tramo entre "nodos" consecutivos (j,  j+1), de la forma:

S_j(x_j) = a_j + b_j (x-x_j) + c_j(x-xj)^2 + d_j(x-x_j)^3

para cada j = 0, 1, ..., n-1

Para n datos existen n-1 tramos, cuatro incógnitas (coeficientes) que evaluar por cada tramo, como resultado 4(n-1) incógnitas para todo el intervalo .

Para los términos (xj+1- xj) de los tramos que se usan varias veces en el desarrollo, se simplifican como hj:

h_j = x_{j+1} - x_{j} S_j(x_j) = a_j + b_j h_j + c_j h_j^2 + d_jh_j^3

Para generar el sistema de ecuaciones, se siguen los siguientes criterios:

1.  Los valores de la función deben ser iguales en los nodos interiores

S_j(x_{j+1}) = f(x_{j+1}) = S_{j+1}(x_{j+1})

2. Las primeras derivadas en los nodos interiores deben ser iguales

S'_j(x_{j+1}) = S'_{j+1}(x_{j+1})

3. Las segundas derivadas en los nodos interiores deben ser iguales

S''_j(x_{j+1}) = S''_{j+1}(x_{j+1})

4. El primer y último polinomio deben pasar a través de los puntos extremos

f(x_0) = S_0(x_0) = a_0 f(x_n) = S_n(x_n) = a_n

5. Una de las condiciones de frontera se satisface:

5a. frontera libre o natural: Las segundas derivadas en los nodos extremos son cero

S''(x_0) = S''(x_n) = 0

5b. frontera sujeta: las primeras derivadas en los nodos extremos son conocidas

S'(x_0) = f'(x_0)

S'(x_n) = f'(x_n)

Tarea: Revisar en los libros. textos el planteamiento de las ecuaciones para resolver el sistema que se genera al plantear el polinomio.

Ubicar en el texto las ecuaciones resultantes, que son las que se aplicarán en el algoritmo.

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]

..


2. Algoritmo en Python

El algoritmo parte de lo desarrollado para "trazadores lineales", donde se presentaron los bloques para:

  • construir el trazador en una tabla de polinomios por tramos
  • realizar la gráfica con los trazadores en cada tramo
  • evaluar cada uno de ellos en cada tramo con muestras suficientes para presentar una buena resolución o precisión en la gráfica

Del algoritmo básico se modifica entonces el bloque del cálculo de los polinomios de acuerdo al planteamiento de formulas y procedimientos para trazadores cúbicos naturales (enviado a revisar como tarea).

# Trazador cubico natural
import numpy as np
import sympy as sym

def traza3natural(xi,yi, vertabla=False):
    ''' trazador cubico natural,
    2da derivadas en los nodos extremos son cero
    '''
    # Vectores como arreglo, numeros reales
    xi = np.array(xi,dtype=float)
    yi = np.array(yi,dtype=float)
    n = len(xi)

    h = np.diff(xi,n=1) # h tamano de paso

    # Sistema de ecuaciones
    A = np.zeros(shape=(n-2,n-2), dtype=float)
    B = np.zeros(n-2, dtype=float)
    S = np.zeros(n, dtype=float)
    A[0,0] = 2*(h[0]+h[1])
    A[0,1] = h[1]
    B[0] = 6*((yi[2]-yi[1])/h[1] - (yi[1]-yi[0])/h[0])
    for i in range(1,n-3,1):
        A[i,i-1] = h[i]
        A[i,i] = 2*(h[i]+h[i+1])
        A[i,i+1] = h[i+1]
        B[i] = 6*((yi[i+2]-yi[i+1])/h[i+1] - (yi[i+1]-yi[i])/h[i])
    A[n-3,n-4] = h[n-3]
    A[n-3,n-3] = 2*(h[n-3]+h[n-2])
    B[n-3] = 6*((yi[n-1]-yi[n-2])/h[n-2] - (yi[n-2]-yi[n-3])/h[n-3])

    # Resolver sistema de ecuaciones, S
    r = np.linalg.solve(A,B)
    #S[0] = 0; S[n-1] = 0 # Definidos en cero
    S[1:n-1] = r[0:n-2]

    # Coeficientes
    a = np.zeros(n-1, dtype = float)
    b = np.zeros(n-1, dtype = float)
    c = np.zeros(n-1, dtype = float)
    d = np.zeros(n-1, dtype = float)
    for j in range(0,n-1,1):
        a[j] = (S[j+1]-S[j])/(6*h[j])
        b[j] = S[j]/2
        c[j] = (yi[j+1]-yi[j])/h[j] - (2*h[j]*S[j]+h[j]*S[j+1])/6
        d[j] = yi[j]

    # Polinomio trazador
    x = sym.Symbol('x')
    px_tabla = []
    for j in range(0,n-1,1):
        pxtramo = a[j]*(x-xi[j])**3 + b[j]*(x-xi[j])**2 + c[j]*(x-xi[j])+ d[j]
        px_tabla.append(pxtramo)
    
    if vertabla==True:
        print('h:',h)
        print('A:') ; print(A)
        print('B:',B) ; print('S:',S)
        print('r:',r)
        print('coeficientes[ ,tramo]:')
        print('a:',a) ; print('b:',b)
        print('c:',c) ; print('d:',d)

    return(px_tabla)

# PROGRAMA ---------------
# INGRESO
xi = [0.1 , 0.2, 0.3, 0.4]
fi = [1.45, 1.8, 1.7, 2.0]

titulo = 'Trazador cúbico natural o libre'
# natural, 2da derivadas en los nodos extremos son cero

# PROCEDIMIENTO
n = len(xi)
# Obtiene los polinomios por tramos
px_tabla = traza3natural(xi,fi,vertabla=True)

# SALIDA
print(titulo)
print('Polinomios por tramos: ')
for i in range(0,n-1,1):
    print('x = [',xi[i],',',xi[i+1],']')
    print('expresion:',px_tabla[i])
    print('simplificado:')
    sym.pprint(px_tabla[i].expand())

con lo que se obtiene el resultado por cada tramo

h: [0.1 0.1 0.1]
A:
[[0.4 0.1]
 [0.1 0.4]]
B: [-27.  24.]
r: [-88.  82.]
S: [  0. -88.  82.   0.]
coeficientes[ ,tramo]:
a: [-146.66666667  283.33333333 -136.66666667]
b: [  0. -44.  41.]
c: [4.96666667 0.56666667 0.26666667]
d: [1.45 1.8  1.7 ]
Trazador cúbico natural o libre
Polinomios por tramos: 
x = [ 0.1 , 0.2 ]
expresion: 4.96666666666667*x - 146.666666666667*(x - 0.1)**3 + 0.953333333333333
simplificado:
                    3         2                            
- 146.666666666667⋅x  + 44.0⋅x  + 0.566666666666666⋅x + 1.1
x = [ 0.2 , 0.3 ]
expresion: 0.566666666666666*x + 283.333333333333*(x - 0.2)**3 - 44.0*(x - 0.2)**2 + 1.68666666666667
simplificado:
                  3          2                            
283.333333333333⋅x  - 214.0⋅x  + 52.1666666666667⋅x - 2.34
x = [ 0.3 , 0.4 ]
expresion: 0.266666666666665*x - 136.666666666667*(x - 0.3)**3 + 41.0*(x - 0.3)**2 + 1.62
simplificado:
                    3          2                           
- 136.666666666667⋅x  + 164.0⋅x  - 61.2333333333333⋅x + 9.0
>>>

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]
..


3. Gráfica con Python

Para el trazado de la gráfica se añade al final del algoritmo:

# GRAFICA --------------
import matplotlib.pyplot as plt

muestras = 10 # entre cada par de puntos

# Puntos para grafica en cada tramo
xtraza = np.array([],dtype=float)
ytraza = np.array([],dtype=float)
i = 0
while i<n-1: # cada tramo
    xtramo = np.linspace(xi[i],xi[i+1],muestras)
    # evalua polinomio del tramo
    pxk = sym.lambdify('x',px_tabla[i])
    ytramo = pxk(xtramo)
    # vectores de trazador en x,y
    xtraza = np.concatenate((xtraza,xtramo))
    ytraza = np.concatenate((ytraza,ytramo))
    i = i + 1

# Graficar
plt.plot(xtraza,ytraza, label='trazador')
plt.plot(xi,fi,'o', label='puntos')
plt.title(titulo)
plt.xlabel('xi')
plt.ylabel('px(xi)')
plt.legend()
plt.show()

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]
..


4. Trazador cúbico natural como función en Scipy

Referenciahttps://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.CubicSpline.html#scipy.interpolate.CubicSpline

La librería Scipy dispone de una función para calcular los coeficientes de los trazadores cúbicos. Para el ejemplo del ejercicio se realiza el cálculo y los polinomios.

Trazador cúbico natural o libre
coeficientes:
a: [-146.66666667  283.33333333 -136.66666667]
b: [ 8.8817842e-15 -4.4000000e+01  4.1000000e+01]
c: [4.96666667 0.56666667 0.26666667]
d: [1.45 1.8  1.7 ]
Polinomios por tramos: 
x = [ 0.1 , 0.2 ]
expresión: 4.96666666666667*x - 146.666666666667*(x - 0.1)**3 + 8.88178419700125e-15*(x - 0.1)**2 + 0.953333333333333
simplifica:
                    3         2                            
- 146.666666666667⋅x  + 44.0⋅x  + 0.566666666666662⋅x + 1.1
x = [ 0.2 , 0.3 ]
expresión: 0.566666666666667*x + 283.333333333333*(x - 0.2)**3 - 44.0*(x - 0.2)**2 + 1.68666666666667
simplifica:
                  3          2                            
283.333333333333⋅x  - 214.0⋅x  + 52.1666666666667⋅x - 2.34
x = [ 0.3 , 0.4 ]
expresión: 0.266666666666665*x - 136.666666666667*(x - 0.3)**3 + 41.0*(x - 0.3)**2 + 1.62
simplifica:
                    3          2                           
- 136.666666666667⋅x  + 164.0⋅x  - 61.2333333333333⋅x + 9.0

Las instrucciones en Python con la librería Scipy para trazadores cúbicos natural son:

# Trazador cúbico natural o libre con Scipy
import numpy as np
import scipy as sp
import sympy as sym

# INGRESO
xi = [0.1 , 0.2, 0.3, 0.4]
fi = [1.45, 1.8, 1.7, 2.0]

# natural, 2da derivadas en los nodos extremos son cero
cs_tipo = ((2, 0.0), (2, 0.0)) # natural

# PROCEDIMIENTO
# Vectores como arreglo, numeros reales
xi = np.array(xi,dtype=float)
fi = np.array(fi,dtype=float)
n = len(xi)

# coeficientes de trazadores cúbicos
traza_cub = sp.interpolate.CubicSpline(xi,fi,bc_type=cs_tipo )
traza_coef = traza_cub.c # coeficientes
a = traza_coef[0]
b = traza_coef[1]
c = traza_coef[2]
d = traza_coef[3]

# Polinomio trazador
x = sym.Symbol('x')
px_tabla = []
for j in range(0,n-1,1):
    pxtramo = a[j]*(x-xi[j])**3 + b[j]*(x-xi[j])**2 + c[j]*(x-xi[j])+ d[j]
    px_tabla.append(pxtramo)

# SALIDA
print('coeficientes:')
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)
print('Polinomios por tramos: ')
for i in range(0,n-1,1):
    print('x = [',xi[i],',',xi[i+1],']')
    print('expresion:',px_tabla[i])
    print('simplifica:')
    sym.pprint(px_tabla[i].expand())

la gráfica se puede generar con el bloque anterior

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]


Si los polinomios no se igualan entre los nodos, tampoco sus velocidades y aceleraciones; los puntos de inflexión en los nodos podrían generar una experiencia semejante a variaciones de velocidad y aceleración en una trayectoria como la mostrada en los siguientes videos.

youtube: slingshot ride

Car sales woman scares customers. maxman.tv. 4 enero 2016

[ Trazador Cúbico ] [ Algoritmo ] [ gráfica ] [ función ]

 


Unidades