1.6 Polinomios de Taylor y diferenciación numérica
Contents
1.6 Polinomios de Taylor y diferenciación numérica#
Notas para contenedor de docker:
Comando de docker para ejecución de la nota de forma local:
nota: cambiar <ruta a mi directorio>
por la ruta de directorio que se desea mapear a /datos
dentro del contenedor de docker y <versión imagen de docker>
por la versión más actualizada que se presenta en la documentación.
docker run --rm -v <ruta a mi directorio>:/datos --name jupyterlab_optimizacion -p 8888:8888 -d palmoreck/jupyterlab_optimizacion:<versión imagen de docker>
password para jupyterlab: qwerty
Detener el contenedor de docker:
docker stop jupyterlab_optimizacion
Documentación de la imagen de docker palmoreck/jupyterlab_optimizacion:<versión imagen de docker>
en liga.
Nota generada a partir de la liga1, liga2 e inicio de liga3.
Al final de esta nota la comunidad lectora:
Aprenderá que el método de diferenciación finita es un método inestable numéricamente respecto al redondeo.
Conocerá las expresiones de los polinomios de Taylor para funciones de varias variables.
Tendrá en su lista de programas del lenguaje R implementaciones para aproximar al gradiente y a la Hessiana de una función con los métodos de diferenciación finita.
Utilizará las fórmulas para calcular errores absolutos y relativos para valores y vectores revisadas en fórmulas para calcular errores absolutos y relativos
Problema: ¿Cómo aproximar una función en un punto ?#
Si
y se nombra aproximación de orden 1. Ver Definición de función, continuidad y derivada para definición de continuidad, diferenciabilidad y propiedades.
Comentarios
Lo anterior requiere de los valores:
. Esta aproximación tiene un error de orden pues su error es proporcional al cuadrado del ancho del intervalo: , esto es, si reducimos a la mitad entonces el error se reduce en una cuarta parte.Otra aproximación más simple sería:
lo cual sólo requiere del conocimiento de
Los errores anteriores los nombramos errores por truncamiento, ver Fuentes del error y Análisis del error para un recordatorio de tal error. Utilizamos la notación “O grande”
para escribir lo anterior:
con la variable
y se representa un error de orden
Observaciones
No confundir órdenes de una aproximación con órdenes de error.
Otras aproximaciones a una función se pueden realizar con:
Interpoladores polinomiales (representación por Vandermonde, Newton, Lagrange).
Aproximación a una función por el teorema de Taylor#
En esta sección se presenta el teorema de Taylor, el cual, bajo ciertas hipótesis nos proporciona una expansión de una función alrededor de un punto. Este teorema será utilizado en diferenciación e integración numérica. El teorema es el siguiente:
Teorema de Taylor
Sea
con
Comentarios
El teorema de Taylor nos indica que cualquier función suave (función en
se le puede aproximar por un polinomio en el intervalo , de hecho .Si el residuo no tiene una alta contribución a la suma
entonces es una buena aproximación local (alta contribución y buena aproximación depende de factores como elección de la norma y la aplicación).El teorema de Taylor es una generalización del teorema del valor medio para derivadas.
se le llama polinomio de Taylor alrededor de de orden y es llamado residuo de Taylor alrededor de de orden , tiene otras expresiones para representarlo y la que se utiliza en el enunciado anterior es en su forma de Lagrange (ver liga para otras expresiones del residuo). es un punto entre desconocido y está en función de (por eso se le escribe un subíndice).Una forma del teorema de Taylor es escribirlo definiendo a la variable
:
y si
Ejemplo#
Graficar la función y los polinomios de Taylor de grados
Solución
Obtengamos los polinomios de Taylor de orden
library(ggplot2)
options(repr.plot.width=6, repr.plot.height=6) #esta línea sólo se ejecuta para jupyterlab con R
Taylor_approx <- function(x,c,n){
'
Taylor approximation for 1/x function. Will return Taylor polynomial of degree n with
center in c and evaluated in x.
Args:
x (double): numeric vector or scalar in which Taylor polynomial will be evaluated.
c (double): scalar which represents center of Taylor polynomial of degree n.
n (integer): scalar which represents degree of Taylor polynomial.
Returns:
sum (double): scalar evaluation of Taylor polynomial of degree n with center c in x.
'
length_x <- length(x)
sum <- vector("double", length_x)
for(j in 1:length_x){
mult <- c^(-1)
sum[j] <- mult
for(k in 1:n){
mult <- -1*c^(-1)*(x[j]-c)*mult
sum[j] <- sum[j] + mult
}
}
sum #accumulated sum
}
x0 <- 1.5
x <- seq(from=1,to=2,by=.005)
n <- c(0,1,2,3,4) #degrees of Taylor polynomials
f <- function(z)1/z
y <- f(x)
y_Taylor_0 <- f(x0)*(vector("double", length(x))+1)
y_Taylor_1 <- Taylor_approx(x,x0,1)
y_Taylor_2 <- Taylor_approx(x,x0,2)
y_Taylor_3 <- Taylor_approx(x,x0,3)
y_Taylor_4 <- Taylor_approx(x,x0,4)
gg <- ggplot()
print(gg+
geom_line(aes(x=x,y=y,color='f(x)')) +
geom_line(aes(x=x,y=y_Taylor_0,color='constante'))+
geom_line(aes(x=x,y=y_Taylor_1,color='lineal')) +
geom_line(aes(x=x,y=y_Taylor_2,color='grado 2')) +
geom_line(aes(x=x,y=y_Taylor_3,color='grado 3')) +
geom_line(aes(x=x,y=y_Taylor_4,color='grado 4')) +
geom_point(aes(x=x0, y=f(x0)), color='blue',size=3))

Observación
Para cualquier aproximación calculada siempre es una muy buena idea reportar el error relativo de la aproximación si tenemos el valor del objetivo. No olvidar esto :)
Para el cálculo del error utilizamos fórmulas para calcular errores absolutos y relativos:
La siguiente función calcula un error relativo para un valor obj
:
compute_error_point_wise<-function(obj, approx){
'
Relative or absolute error between approx and obj in a point wise fashion.
'
if (abs(obj) > .Machine$double.eps*.Machine$double.xmin){
Err<-abs(obj-approx)/abs(obj)
}else
Err<-abs(obj-approx)
Err
}
x_test_point <- 1.9
objective <- f(x_test_point)
#Approximations
p1_approx <- Taylor_approx(x_test_point, x0, 1)
p2_approx <- Taylor_approx(x_test_point, x0, 2)
p3_approx <- Taylor_approx(x_test_point, x0, 3)
p4_approx <- Taylor_approx(x_test_point, x0, 4)
print('error relativo polinomio constante')
print(compute_error_point_wise(objective, 1/x0))
print('error relativo polinomio lineal')
print(compute_error_point_wise(objective, p1_approx))
print('error relativo polinomio grado 2')
print(compute_error_point_wise(objective, p2_approx))
print('error relativo polinomio grado 3')
print(compute_error_point_wise(objective, p3_approx))
print('error relativo polinomio grado 4')
print(compute_error_point_wise(objective, p4_approx))
[1] "error relativo polinomio constante"
[1] 0.2666667
[1] "error relativo polinomio lineal"
[1] 0.07111111
[1] "error relativo polinomio grado 2"
[1] 0.01896296
[1] "error relativo polinomio grado 3"
[1] 0.00505679
[1] "error relativo polinomio grado 4"
[1] 0.001348477
Ejercicio
Utilizando lenguajes de programación aproximar ggplot2
. Observa que
Teorema de Taylor para una función #
Sea
para alguna
Observación
La aproximación anterior la nombramos aproximación de orden
Si además
para alguna
Observación
La aproximación anterior la nombramos aproximación de orden
Si
Observación
La aproximación anterior la nombramos aproximación de orden
para con centro en . Para las suposiciones establecidas se tiene:
En este caso
es un tensor.
Comentario
Tomando
Si
Diferenciación numérica por diferencias finitas#
Comentario
En esta sección se revisan métodos numéricos para aproximar las derivadas. Otros métodos para el cálculo de las derivadas se realizan con el cómputo simbólico o algebraico, ver Definición de función, continuidad y derivada para ejemplos.
Las fórmulas de aproximación a las derivadas por diferencias finitas pueden obtenerse con los polinomios de Taylor, presentes en el teorema del mismo autor, por ejemplo:
Sea
con
y escribimos:
La aproximación

Con las mismas suposiciones es posible obtener la fórmula para la aproximación por diferencias hacia atrás:

Considerando
y el cociente

Observaciones
La aproximación por diferencias finitas a la primer derivada de la función tiene un error de orden
por lo que una elección de igual a generará aproximaciones con alrededor de un dígito correcto.La diferenciación numérica por diferencias finitas no es un proceso con una alta exactitud pues los problemas del redondeo de la aritmética en la máquina se hacen presentes en el mismo (ver nota Sistema de punto flotante). Como ejemplo de esta situación realicemos el siguiente ejemplo.
Ejemplo#
Realizar una gráfica de log(error relativo) vs log(h) (h en el eje horizontal) para aproximar la primera derivada de
Definimos la función
f <- function(x){
exp(-x)
}
Definimos la aproximación numérica por diferencias finitas a la primera derivada
approx_first_derivative <- function(f, x, h){
'
Numerical differentiation by finite differences. Uses forward point formula
to approximate first derivative of function.
Args:
f (function): function definition.
x (float): point where first derivative will be approximated
h (float): step size for forward differences. Tipically less than 1
Returns:
res (float): approximation to first_derivative.
'
res <- (f(x+h)-f(x))/h
res
}
Puntos donde se evaluará la aproximación:
x<-1
h<-10^(-1*(1:16))
Aproximación numérica:
approx_df <- approx_first_derivative(f,x,h)
Derivada de la función:
df<-function(x){
-exp(-x)
}
obj_df <- df(x)
Cálculo de errores:
res_relative_error <- compute_error_point_wise(obj_df, approx_df)
Gráfica:
gf <- ggplot()
print(gf+
geom_line(aes(x=log(h),y=log(res_relative_error)))+
ggtitle('Aproximación a la primera derivada por diferencias finitas'))

Ejercicio
Utilizando lenguajes de programación realizar una gráfica de log(error relativo) vs log(h) (h en el eje horizontal) con ggplot2
para aproximar la segunda derivada de
Encontrar valor(es) de
Comentario
Aproximaciones a la segunda derivada de una función
por diferencias hacia delante. por diferencias hacia atrás. por diferencias centradas.
Estas fórmulas se obtienen con el teorema de Taylor bajo las suposiciones correctas.
Análisis del error por redondeo y truncamiento en la aproximación por diferencias finitas hacia delante#
El ejemplo anterior muestra (vía una gráfica) que el método numérico de diferenciación numérica no es estable numéricamente respecto al redondeo (ver nota Condición de un problema y estabilidad de un algoritmo para definición de estabilidad de un algoritmo) y también se puede corroborar realizando un análisis del error. En esta sección consideramos la aproximación a la primer derivada por diferencias finitas hacia delante:
Suponemos que
suponiendo en el último paso que
El error relativo es:
con
Entonces la función
Si
la componente domina a la componente , la cual tiende a .Si
la componente domina a , la cual tiende a .
Por lo anterior, existe un valor de
print(h[which.min(res_relative_error)])
[1] 1e-08
Ejercicio
Obtener de forma analítica el valor de
Conclusiones y comentarios#
La componente
es el error por truncamiento, la cual resulta del teorema de Taylor. El teorema de Taylor nos indica que añadir términos en el polinomio de Taylor si la a aproximar es cercana al centro, las derivadas de son acotadas y entonces el error por truncamiento debe tender a . Lo anterior no ocurre en la implementación numérica (corroborado de forma analítica y visual) del método por diferenciación numérica para la primer derivada por la presencia de la componente en los errores. Tal componente proviene del error por redondeo.
Obsérvese que el error relativo máximo es del
lo que indica que no se tiene ninguna cifra correcta en la aproximación:
print(max(res_relative_error))
[1] 1
y esto ocurre para un valor de
print(h[which.max(res_relative_error)])
[1] 1e-16
Pregunta
¿Por qué se alcanza el máximo error relativo en el valor de
Con lo anterior se tiene que la diferenciación numérica es un método inestable numéricamente respecto al redondeo. Ver nota Condición de un problema y estabilidad de un algoritmo.
Un análisis de error similar se utiliza para el método de diferencias finitas por diferencias centradas para aproximar la primera derivada. En este caso el valor de
que minimiza a los errores es del orden .
Diferenciación numérica para una función #
Supongamos
Podemos utilizar las fórmulas de aproximación en diferenciación numérica con diferencias finitas para el caso
Para el caso del gradiente se tiene por diferenciación hacia delante:
con
Se cumple:
Ejemplo#
Aproximar
Para esta función se tiene:
Gradiente de f calculado de forma simbólica
gf<-function(x){
c(4*x[1]*(x[1]^2-x[2]^2)+2*x[1],
-4*x[2]*(x[1]^2-x[2]^2),
4*x[3]*(x[3]^2-x[4]^2)+2*x[3],
-4*x[4]*(x[3]^2-x[4]^2))
}
Punto en el que se evaluará
x_0<-c(1.5,1.5,1.5,1.5)
print(gf(x_0))
[1] 3 0 3 0
Hessiana de f calculada de forma simbólica
gf2<-function(x){
matrix(c(12*x[1]^2-4*x[2]^2+2,-8*x[1]*x[2],0,0,
-8*x[1]*x[2],-4*x[1]^2+12*x[2]^2,0,0,
0,0,12*x[3]^2-4*x[4]^2+2,-8*x[3]*x[4],
0,0,-8*x[3]*x[4],-4*x[3]^2+12*x[4]^2),nrow=4,ncol=4)
}
Evaluación de la Hessiana
print(gf2(x_0))
[,1] [,2] [,3] [,4]
[1,] 20 -18 0 0
[2,] -18 18 0 0
[3,] 0 0 20 -18
[4,] 0 0 -18 18
Definición de función y punto en el que se calculan las aproximaciones
f <- function(x){
(x[1]^2-x[2]^2)^2+x[1]^2+(x[3]^2-x[4]^2)^2+x[3]^2
}
x0 <- rep(1.5,4)
Lo siguiente calcula el gradiente y la Hessiana de forma numérica con la aproximación por diferencias hacia delante
inc_index<-function(vec,index,h){
'
Auxiliary function for gradient and Hessian computation.
Args:
vec (double): vector
index (int): index.
h (float): quantity that vec[index] will be increased.
Returns:
vec (double): vector with vec[index] increased by h.
'
vec[index]<-vec[index]+h
vec
}
gradient_approximation<-function(f,x,h=1e-8){
'
Numerical approximation of gradient for function f using forward differences.
Args:
f (function): definition of function f.
x (double): vector that holds values where gradient will be computed.
h (float): step size for forward differences, tipically h=1e-8
Returns:
gf (array): numerical approximation to gradient of f.
'
n<-length(x)
gf<-vector("double",n)
for(i in 1:n){
gf[i]=(f(inc_index(x,i,h))-f(x))
}
gf/h
}
Hessian_approximation<-function(f,x,h=1e-6){
'
Numerical approximation of Hessian for function f using forward differences.
Args:
f (function): definition of function f.
x (double): vector that holds values where Hessian will be computed.
h (float): step size for forward differences, tipically h=1e-6
Returns:
Hf (double): matrix of numerical approximation to Hessian of f.
'
n<-length(x)
Hf<-matrix(rep(0,n^2),nrow=n,ncol=n)
f_x<-f(x)
for(i in 1:n){
x_inc_in_i<-inc_index(x,i,h)
f_x_inc_in_i<-f(x_inc_in_i)
for(j in i:n){
dif<-f(inc_index(x_inc_in_i,j,h))-f_x_inc_in_i-f(inc_index(x,j,h))+f_x
Hf[i,j]<-dif
if(j!=i)
Hf[j,i]<-dif
}
}
Hf/h^2
}
Conjunto de valores de h para diferencias hacia delante
h<-10^(-1*(1:16))
Para el cálculo del error utilizamos fórmulas para calcular errores absolutos y relativos:
La siguiente función calcula un error relativo para un vector obj
:
Euclidian_norm<-function(vec){
'Compute Euclidian norm of vector'
sqrt(sum(vec*vec))
}
compute_error<-function(obj,approx){
'
Relative or absolute error between obj and approx based in Euclidian norm.
Approx is a numeric vector.
'
if (Euclidian_norm(obj) > .Machine$double.eps*.Machine$double.xmin){
Err<-Euclidian_norm(obj-approx)/Euclidian_norm(obj)
}else
Err<-Euclidian_norm(obj-approx)
Err
}
gf_numeric_approximations <- lapply(h,gradient_approximation,f=f,x=x0)
gf2_numeric_approximations <- lapply(h,Hessian_approximation,f=f,x=x0)
rel_err_gf <- sapply(gf_numeric_approximations,compute_error,obj=gf(x_0))
rel_err_gf2 <- sapply(gf2_numeric_approximations,compute_error,obj=gf2(x_0))
gg<-ggplot()
print(gg+
geom_line(aes(x=log(h),y=log(rel_err_gf)))+
ggtitle('Aproximación al gradiente por diferencias finitas'))

print(h[which.min(rel_err_gf)])
[1] 1e-08
print(gg+
geom_line(aes(x=log(h),y=log(rel_err_gf2)))+
ggtitle('Aproximación a la Hessiana por diferencias finitas'))

print(h[which.min(rel_err_gf2)])
[1] 1e-06
Ejercicio
Utilizando lenguajes de programación aproximar
Ejercicios
Resuelve los ejercicios y preguntas de la nota.
Referencias
R. L. Burden, J. D. Faires, Numerical Analysis, Brooks/Cole Cengage Learning, 2005.
M. T. Heath, Scientific Computing. An Introductory Survey, McGraw-Hill, 2002.
S. P. Boyd, L. Vandenberghe, Convex Optimization. Cambridge University Press, 2004.