Contenido bajo licencia Creative Commons BY 4.0 y código bajo licencia MIT. © Juan Gómez y Nicolás Guarín-Zapata 2020. Este material es parte del curso Modelación Computacional en el programa de Ingeniería Civil de la Universidad EAFIT.
El problema de interpolación o de predecir funciones a partir de un número determinado de datos representativos de la función aparece con bastante frecuencia en la interpretación de datos experimentales. De otro lado, las técnicas de aproximación de funciones por medio de métodos de interpolación son la base para la formulación de los métodos más importantes de la mecánica computacional como lo son los elementos finitos y los elementos de frontera. En este Notebook se discutirán algunos aspectos básicos y fundamentales de la teoría de interpolación. El notebook se describe en términos del desarrollo completo de un problema ejemplo incluyendo su implementación en Python.
Al completar este notebook usted debería estar en la capacidad de:
Reconocer el problema de interpolación de funciones como uno de aproximación de una función desconocida en términos de valores discretos de la misma función.
Identificar las propiedades fundamentales de los polinomios de interpolación de Lagrange.
Usar polinomios de interpolación de Lagrange para proponer aproximaciones a una función dado un conjunto de N valores conocidos de la misma.
Reconocer la diferencia entre variables primarias y secundarias en un esquema de interpolación.
El problema de interpolación consiste en la determinación del valor de una función f(x) en un punto arbitrario x∈[x1,xn], dados valores conocidos de la función al interior de un dominio de solución. De acuerdo con el teorema de interpolación de Lagrange la aproximación ˆf(x) a la función f(x) se construye como:
ˆf(x)=n∑I=1LI(x)fIdonde LI es el I−ésimo polinomio de orden n−1 y f1,f2,⋯,fn son los n valores conocidos de la función. El I−ésimo polinomio de Lagrange se calcula siguiendo la siguiente expresión:
LI(x)=n∏J=1,J≠I(x−xJ)(xI−xJ).En varios problemas de ingeniería es necesario usar técnicas de interpolación para encontrar valores de las derivadas de la variable primaria o principal. Por ejemplo, en problemas de mecánica de sólidos y teoría de elasticidad, en los cuales la variable primaria es el campo de desplazamientos, uno puede estar interesado en determinar las deformaciones unitarias las cuales son derivadas espaciales de los desplazamientos. Considerando que solo se dispone de valores discretos de los desplazamientos se tiene que las derivadas que se requieren se pueden encontrar usando estos valores discretos. Lo anterior equivale a derivar ˆf(x) directamente como sigue:
dˆfdx=dLI(x)dxfIHaciendo,
BI(x)=dLI(x)dx,podemos escribir el esquema de interpolación como:
dˆfdx=n∑I=1BI(x)fI.Formule un esquema de interpolación para encontrar un valore de la función
f(x)=x3+4x2−10en un punto arbitrario x en el intervalo [−1,1] asumiendo que se conoce el valor exacto de la función en los puntos x=−1.0, x=+1.0 y x=0.0.
En este ejemplo se conoce la función y aparentemente no tiene mucho sentido buscar una aproximación de la misma resolviendo un problema de interpolación. Sin embargo es conveniente seleccionar una función conocida para poder asimilar el problema numérico y sus limitaciones. En este contexto asumiremos que en una aplicación real conocemos los valores de la función en un conjunto de puntos x=−1.0, x=+1.0 and x=0.0 los cuales se denominan puntos nodales o simplemente nodos.
El proceso de interpolación involucra 2 pasos fundamentales:
Determinar los polinomios de interpolación LI usando la productoria.
Usar la combinación lineal para construir el polinomio interpolante o la aproximación a la función ˆf(x).
Veamos estos pasos entonces.
Preguntas
En el método de los elementos finitos es común denominar a los polinomios de interpolación como funciones de forma.
El dominio de calculo es el intervalo de tamaño 2 comprendido entre x=−1.0 y x=+1.0. Como se discutirá mas adelante, por facilidades en la programación en la implementación de métodos de elementos finitos los dominios de tamaño diferente son transformados al dominio de tamaño 2.
En los siguientes bloques de código mostramos el paso a paso en la construcción del polinomio de interpolación final f(x) usando Python. Para seguir el Notebook se recomienda que de manera simultánea se implementen los bloques de código en un script independiente o que añada comentarios a las instrucciones más relevantes.
En la escritura de scripts en Python es necesario importar módulos o bibliotecas (algunas personas usan la palabra librería como mala traducción de library del inglés) que contienen funciones de Python predefinidas. En este caso importaremos los módulos:
numpy
el cual es una biblioteca de funciones para realizar operaciones con matrices similar a Matlab.matplotlib
el cual es una biblioteca de graficación.scipy
el cual es una biblioteca fundamental para computación científica.sympy
el cual es una biblioteca para realizar matemáticas simbólicas.Python importa los módulos usando la palabra reservada import
seguida del nombre del módulo y un nombre corto para ser usado como prefijo en referencias posteriores a las funciones contenidas en ese módulo.
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
import sympy as sym
sym.init_printing()
En un programa de computador una función (o también llamada subrutina) es un bloque de código que realiza una tarea específica múltiples veces dentro de un programa o probablemente en diferentes programas. En Python estas funciones se definen por medio de la palabra clave def
seguida del nombre dado a la función.
Conjuntamente con el nombre, encerrado entre paréntesis, la definición de la función debe incluir también una lista de parámetros (o argumentos) a ser usados por la función cuando esta realice tareas especificas.
En el ejemplo definiremos una función de Python para generar el polinomio de Lagrange usando la productoria definida previamente. Le daremos a esta función el nombre lagrange_poly
. Sus parámetros de entrada serán la variable independiente x a ser usada en el polinomio; el orden del polinomio definido por order
; y el punto i
asociado al polinomio. La función será usada posteriormente desde el programa principal.
def lagrange_poly(x, order, i, xi=None):
if xi == None:
xi = sym.symbols('x:%d'%(order+1))
index = list(range(order+1))
index.pop(i)
return sym.prod([(x - xi[j])/(xi[i] - xi[j]) for j in index])
Alternativamente a la definición de una función usando la palabra clave def
Python también permite la definición de funciones en el sentido del Calculo, es decir definición de relaciones que permiten transformar escalares o vectores en otros escalares o vectores. Esto es posible a través de la opción lambda
. En este ejemplo usaremos la opción lambda
para crear la función:
Preguntas
Use una terminal o un script independiente para probar el uso de la opción lambda
en la definición de una función. Intente con diferentes funciones.
fun = lambda x: x**3 + 4.0*x**2 - 10.0
Una vez creada la función f(x) usando la opción lambda
pasamos a definir un conjunto de puntos de evaluación. El numero de puntos se define por medio de la variable npts
y usamos la función linspace
del módulo numpy
para crear una distribución equidistante de puntos entre x=−1.0 y x=+1.0. Simultáneamente, el arreglo nulo yy
almacenará los valores de la función en los puntos almacenados en xx
.
Note que Python inicia desde la posición 0 el conteo de elementos en arreglos y otras estructuras de datos, de manera que empezamos a contar desde cero para mantener la consistencia con la implementación.
npts = 200
x_pts = np.linspace(-1, 1, npts)
Con la función ahora disponible podemos calcular los valores (conocidos) listos para ser usados en el esquema de interpolación. Estos valores se almacenarán en el arreglo fd()
. Para calcular cada valor de la función usamos el comando (ya disponible) fx
correspondiente al nombre que usamos en la declaración de la función usando la opción lambda
.
Preguntas
Intente usar nombres diferentes en la definición de la función usando la opción lambda
y ensaye si su código aún funciona.
pts = np.array([-1, 1, 0])
fd = fun(pts)
Ahora evalúe la función en los npts
puntos y grafique la misma:
plt.figure()
y_pts = fx(x_pts)
plt.plot(x_pts , y_pts)
plt.plot(pts, fd, 'ko')
[<matplotlib.lines.Line2D at 0x7f982bdebbe0>]
Calculemos ahora los polinomios de Lagrange invocando la función lagrange_poly()
. Crearemos una lista vacía que llamaremos pol
y cada que determinemos un nuevo polinomio lo adicionaremos a la lista usando para ello el método .append()
. En ese momento los polinomios almacenados en la lista pol=[]
existirán en notación simbólica (como si estuviéramos resolviendo el problema a mano) y estarán listos para ser evaluados en valores específicos de x.
x = sym.symbols('x')
pol = []
pol.append(sym.simplify(lagrange_poly(x, 2, 0, [-1,1,0])))
pol.append(sym.simplify(lagrange_poly(x, 2, 1, [-1,1,0])))
pol.append(sym.simplify(lagrange_poly(x, 2, 2, [-1,1,0])))
pol
Preguntas
Grafique los 3 polinomios de Lagrange de orden 2 y verifique que satisfacen la propiedad LI(xJ)=δIJ.
Adicione más puntos al dominio de interpolación y grafique los polinomios resultantes. ¿Cuál es el efecto en los polinomios?
El método subs
del módulo sympy
substituye el valor de la variable independiente x por el valor almacenado en xx[i].
plt.figure()
for k in range(3):
for i in range(npts):
yy[i] = pol[k].subs([(x, xx[i])])
plt.plot(xx, yy)
Construyamos ahora el polinomio de aproximación completo ˆf(x) de acuerdo con la superposición lineal
ˆf(x)=L0(x)f(x0)+L1(x)f(x1)+L2(x)f(x2),y utilizando la lista (ya disponible) pol
que almacena los 3 polinomios generados. Solo para efectos de ilustrar primero lo mostramos con display
. La versión evaluada de ˆf(x) será almacenada en el arreglo yy[i]
.
display(pol[0]*fd[0] + pol[1]*fd[1] + pol[2]*fd[2])
plt.figure()
for i in range(npts):
yy[i] = fd[0]*pol[0].subs([(x, xx[i])]) + fd[1]*pol[1].subs([(x, xx[i])]) \
+ fd[2]*pol[2].subs([(x, xx[i])])
zz = fx(xx)
plt.plot([-1, 1, 0], fd, 'ko')
plt.plot(xx, yy , 'r--')
plt.plot(xx, zz)
[<matplotlib.lines.Line2D at 0x7f982bea99e8>]
Note que debido a la diferencia en orden entre la aproximación ˆf(x) y la función f(x) el polinomio de interpolación no coincide completamente con la función aunque si reproduce los valores correctos en los puntos nodales.
Pregunta
¿Cómo podemos mejorar la aproximación ˆf(x) a la función f(x)?
Usemos la opción lambda
una vez más para definir una nueva función fdx
correspondiente a la primera derivada:
Las derivadas en los puntos nodales se almacenarán en el arreglo fc
fdx = lambda x: 3*x**2+8.0*x
fc = np.array([fdx(-1.0), fdx(1.0) ,fdx(0.0)])
La interpolación de las derivadas se calculan de acuerdo con
ˆf′(x)=dL0(x)dxf0+dL1(x)dxf1+dL2(x)dxf2.dpol = []
dpol.append(sym.diff(pol[0],x))
dpol.append(sym.diff(pol[1],x))
dpol.append(sym.diff(pol[2],x))
display(dpol)
display(dpol[0]*fd[0] + dpol[1]*fd[1] + dpol[2]*fd[2])
plt.figure()
yy= fdx(xx)
plt.plot(xx, yy ,'r--')
plt.plot([-1, 1, 0], fc, 'ko')
for i in range(npts):
yy[i] = fd[0]*dpol[0].subs([(x, xx[i])]) + fd[1]*dpol[1].subs([(x, xx[i])])\
+ fd[2]*dpol[2].subs([(x, xx[i])])
plt.plot(xx, yy)
[<matplotlib.lines.Line2D at 0x7f982be09c18>]
Fórmula de productoria: Expresión usada para calcular los polinomios de interpolación de Lagrange.
Función de forma: Nombre dado a un polinomio de interpolación en el contexto del método de los elementos finitos.
Punto nodal: (También nodo). Nombre dado a los puntos específicos donde se conoce el valor de una función y usado en la construcción del esquema de interpolación.
Subrutina: Bloque de código independiente que realiza una tarea de cómputo determinada dentro de un programa principal.
Matriz de interpolación: Arreglo unidimensional o bidimensional que almacena las funciones de forma en un esquema de interpolación determinado.
El propósito de esta actividad es familiarizar al estudiante con el uso del método de interpolación de Lagrange en un contexto particular de la ingeniería, como el método de los elementos finitos. El método de elementos finitos utiliza dominios de interpolación de tamaño constante (llamados elementos) permitiendo el uso de funciones de interpolación fijos y al mismo tiempo favoreciendo la automatización en programas de computador. En una aplicación típica de elementos finitos los valores nodales de una función (por ejemplo el desplazamiento) son determinados tras resolver un sistema de ecuaciones algebraicas. Estos valores nodales son posteriormente usados para encontrar el desplazamiento a lo largo de todo el dominio del problema, y también para realizar cálculos posteriores y obtener información adicional del problema físico.
Siga los pasos que se indican a continuación para implementar, en un notebook independiente, un esquema de interpolación típico del método de elementos finitos. El esquema de interpolación resultante será el correspondiente a un solo elemento
Asumiendo que un dominio de interpolación constante se define como x∈[−1.0,+1.0] con puntos nodales correspondientes a x0=−1.0, x1=+1.0, x2=0.0, x3=−0.25 y x4=+0.25 use la función lagrange_poly()
para generar las funciones de interpolación asociadas a estos 5 puntos nodales. Presente los polinomios resultantes en una gráfica.
Utilice las funciones de interpolación encontradas en el paso anterior, para definir una matriz [N(x)], que se denominará en adelante Matriz de interpolación, de manera que la operación de interpolar se pueda realizar usando operaciones matriciales como:
ˆf(x)=[N(x)]{F},y en la cual F es un vector que almacena los valores nodales de la función.
En su Notebook imprima la versión simbólica de la matriz de interpolación.
En su Notebook grafique una función f(x) (representando el desplazamiento de una barra) y su versión aproximada ˆf(x) usando el esquema matricial desarrollado en el numeral anterior. El código también debe graficar la primera derivada de la función.
Para realizar la interpolación en el Notebook programe una función o subrutina de Python que reciba como parámetros de entrada las coordenadas x donde se desea calcular una aproximación de la función y el vector de valores nodales de la función de desplazamientos y entregue después de su ejecución el valor interpolado de la función.
La siguiente celda cambia el formato del Notebook.
from IPython.core.display import HTML
def css_styling():
styles = open('./nb_style.css', 'r').read()
return HTML(styles)
css_styling()