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 f en un punto x1?#

Si f es continuamente diferenciable en x0 y f(1),f(2) existen y están acotadas en x0 entonces:

f(x1)f(x0)+f(1)(x0)(x1x0)

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: x0,x1,f(x0),f(1)(x0). Esta aproximación tiene un error de orden 2 pues su error es proporcional al cuadrado del ancho del intervalo: h=x1x0, esto es, si reducimos a la mitad h entonces el error se reduce en una cuarta parte.

  • Otra aproximación más simple sería:

f(x1)f(x0)

lo cual sólo requiere del conocimiento de f(x0) y se nombra aproximación de orden 0, sin embargo esta aproximación tiene un error de orden 1 pues este es proporcional a h , esto es, al reducir a la mitad h se reduce a la mitad el error.

  • 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” O() para escribir lo anterior:

f(x)f(x0)=O(h)

con la variable h=xx0. En este caso se representa a un error de orden 1. Análogamente:

f(x)(f(x0)+f(1)(x0)(xx0))=O(h2)

y se representa un error de orden 2.

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 f:RR, fCn([a,b]) tal que f(n+1) existe en [a,b]. Si x0[a,b] entonces x[a,b] se tiene: f(x)=Pn(x)+Rn(x) donde:

Pn(x)=k=0nf(k)(x0)(xx0)kk!(f(0)=f)
Rn(x)=f(n+1)(ξx)(xx0)(n+1)(n+1)!

con ξx entre x0,x y x0 se llama centro. Ver Definición de función, continuidad y derivada para definición del conjunto Cn([a,b]).

Comentarios

  • El teorema de Taylor nos indica que cualquier función suave (función en Cn) se le puede aproximar por un polinomio en el intervalo [a,b], de hecho f(x)Pn(x).

  • Si el residuo no tiene una alta contribución a la suma Pn(x)+Rn(x) 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.

  • Pn(x) se le llama polinomio de Taylor alrededor de x0 de orden n y Rn(x) es llamado residuo de Taylor alrededor de x0 de orden n+1, 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).

  • ξx es un punto entre x0,x desconocido y está en función de x (por eso se le escribe un subíndice).

  • Una forma del teorema de Taylor es escribirlo definiendo a la variable h=xx0:

f(x)=f(x0+h)=Pn(h)+Rn(h)=k=0nf(k)(x0)hkk!+f(n+1)(ξh)hn+1(n+1)!

y si f(n+1) es acotada, escribimos: Rn(h)=O(hn+1).

Ejemplo#

Graficar la función y los polinomios de Taylor de grados 0,1,2,3 y 4 en una sola gráfica para el intervalo [1,2] de la función 1x con centro en x0=1.5. ¿Cuánto es la aproximación de los polinomios en x=1.9?. Calcula el error relativo de tus aproximaciones.

Solución

Obtengamos los polinomios de Taylor de orden n con n{0,1,2,3} y centro en x0=1.5 para la función 1x en el intervalo [1,2]. Los primeros tres polinomios de Taylor son:

P0(x)=f(x0)=23(constante)
P1(x)=f(x0)+f(1)(x0)(xx0)=231x02(xx0)=2311.52(x1.5)(lineal)
P2(x)=f(x0)+f(1)(x0)(xx0)+f(2)(x0)(xx0)22=231x02(xx0)+1x03(xx0)2=2311.52(x1.5)+11.53(x1.5)2(cuadrático)
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))
../../_images/Polinomios_de_Taylor_y_diferenciacion_numerica_27_0.png

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:

ErrRel(aprox)=|aproxobj||obj|

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 f(1) con polinomios de Taylor de orden 0,1,2,3,4 si f(x)=0.1x40.15x30.5x20.25x+1.2 con centro en x0=0. Calcula los errores relativos de tus aproximaciones. Realiza las gráficas de cada polinomio en el intervalo [0,1] con ggplot2. Observa que R5(x) es cero.

Teorema de Taylor para una función f:RnR#

Sea f:RnR diferenciable en domf. Si x0,xdomf y x0+t(xx0)domf,t(0,1), entonces xdomf se tiene f(x)=P0(x)+R0(x) donde:

P0(x)=f(x0)
R0(x)=f(x0+tx(xx0))T(xx0)

para alguna tx(0,1) y f() gradiente de f, ver Definición de función, continuidad y derivada para definición del gradiente de una función.

Observación

La aproximación anterior la nombramos aproximación de orden 0 para f con centro en x0. Si f() es acotado en domf entonces se escribe: R0(x)=O(||xx0||).

Si además f es continuamente diferenciable en domf(su derivada es continua, ver Definición de función, continuidad y derivada para definición de continuidad), f(2) existe en domf, se tiene f(x)=P1(x)+R1(x) donde:

P1(x)=f(x0)+f(x0)T(xx0)
R1(x)=12(xx0)T2f(x0+tx(xx0))(xx0)

para alguna tx(0,1) y 2f() Hessiana de f (ver Definición de función, continuidad y derivada para definición de la matriz Hessiana).

Observación

La aproximación anterior la nombramos aproximación de orden 1 para f con centro en x0. Si 2f() es acotada en domf entonces se escribe: R1(x)=O(||xx0||2).

Si f(2) es continuamente diferenciable y f(3) existe y es acotada en domf, se tiene f(x)=P2(x)+R2(x) donde:

P2(x)=f(x0)+f(x0)T(xx0)+12(xx0)T2f(x0)(xx0)

Observación

  • La aproximación anterior la nombramos aproximación de orden 2 para f con centro en x0. Para las suposiciones establecidas se tiene:

R2(x)=O(||xx0||3).
  • En este caso f(3) es un tensor.

Comentario

Tomando h=xx0, se reescribe el teorema como sigue, por ejemplo para la aproximación de orden 1 incluyendo su residuo:

f(x)=f(x0+h)=f(x0)+f(x0)ThP1(h)+12hT2f(x0+txh)hR1(h).

Si f(2) es acotada en domf, escribimos: R1(h)=O(||h||2).

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 fC1([a,b]) y f(2) existe y está acotada x[a,b] entonces, si x+h[a,b] con h>0 por el teorema de Taylor:

f(x+h)=f(x)+f(1)(x)h+f(2)(ξx+h)h22

con ξx+h[x,x+h] y al despejar f(1)(x) se tiene:

f(1)(x)=f(x+h)f(x)hf(2)(ξx+h)h2.

y escribimos:

f(1)(x)=f(x+h)f(x)h+O(h).

La aproximación f(x+h)f(x)h es una fórmula por diferencias hacia delante con error de orden 1. Gráficamente se tiene:

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

f(1)(x)=f(x)f(xh)h+O(h),h>0.

Considerando fC2([a,b]),f(3) existe y está acotada x[a,b] si xh,x+h[a,b] y h>0 entonces:

f(1)(x)=f(x+h)f(xh)2h+O(h2),h>0.

y el cociente f(x+h)f(xh)2h es la aproximación por diferencias centradas con error de orden 2. Gráficamente:

Observaciones

  • La aproximación por diferencias finitas a la primer derivada de la función tiene un error de orden O(h) por lo que una elección de h igual a .1=101 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 f(x)=ex en x=1 con h{1016,1015,,101} y diferencias hacia delante. Valor a aproximar: f(1)(1)=e1.

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'))
../../_images/Polinomios_de_Taylor_y_diferenciacion_numerica_77_0.png

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 f(x)=ex en x=1 con h{1016,1014,,101} y diferencias hacia delante. Valor a aproximar: f(2)(1)=e1. Usar:

d2f(x)dx=f(x+2h)2f(x+h)+f(x)h2+O(h)

Encontrar valor(es) de h que minimiza(n) al error absoluto y relativo.

Comentario

Aproximaciones a la segunda derivada de una función f:RR se pueden obtener con las fórmulas:

  • d2f(x)dx=f(x+2h)2f(x+h)+f(x)h2+O(h) por diferencias hacia delante.

  • d2f(x)dx=f(x)2f(xh)+f(x2h)h2+O(h) por diferencias hacia atrás.

  • d2f(x)dx=f(x+h)2f(x)+f(xh)h2+O(h2) 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:

f(x+h)f(x)h

Suponemos que f^(x) aproxima a f(x) y por errores de redondeo f^(x)=f(x)(1+ϵf(x)) con |ϵf(x)|ϵmaq error de redondeo al evaluar f en x. f^(x) es la aproximación en un SPFN (ver nota Sistema de punto flotante). Además supóngase que x,x+h,hFl . Entonces en la aproximación a la primer derivada por diferencias hacia delante:

f(1)(x)=f(x+h)f(x)h+O(h) y calculando el error absoluto:

ErrAbs(f^(x+h)f^(x)h)=|f(1)(x)f^(x+h)f^(x)h|=|f(x+h)f(x)h+O(h)(f(x+h)(1+ϵf(x+h))f(x)(1+ϵf(x))h)|=|O(h)f(x+h)ϵf(x+h)f(x)ϵf(x)h|O(h)+Cϵmaqh

suponiendo en el último paso que |f(x+h)ϵf(x+h)f(x)ϵf(x)|Cϵmaq con C>0 constante que acota a la función f en el intervalo [a,b]. Obsérvese que f^(x+h)f^(x)h es la aproximación a la primer derivada por diferencias hacia delante que se obtiene en la computadora, por lo que la cantidad |f(1)(x)f^(x+h)f^(x)h| es el error absoluto de tal aproximación.

El error relativo es:

ErrRel(f^(x+h)f^(x)h)=ErrAbs(f^(x+h)f^(x)h)|f(1)(x)|O(h)+Cϵmaqh|f(1)(x)|=K1h+K21h

con K1,K2>0 constantes.

Entonces la función g(h)=O(h)+O(1h) acota al error absoluto y al error relativo y se tiene:

  • Si h0 la componente O(1h) domina a la componente O(h), la cual tiende a 0.

  • Si h la componente O(h) domina a O(1h), la cual tiende a 0.

Por lo anterior, existe un valor de h que minimiza a los errores. Tal valor se observa en las gráficas anteriores y es igual a:

print(h[which.min(res_relative_error)])
[1] 1e-08

Ejercicio

Obtener de forma analítica el valor de h que minimiza la función g(h) anterior. Tip: utilizar criterio de primera y segunda derivada para encontrar mínimo global.

Conclusiones y comentarios#

  • La componente O(h) 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 x a aproximar es cercana al centro, las derivadas de f son acotadas y h0 entonces el error por truncamiento debe tender a 0. 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 O(1h) en los errores. Tal componente proviene del error por redondeo.

  • Obsérvese que el error relativo máximo es del 100% 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 h igual a:

print(h[which.max(res_relative_error)])
[1] 1e-16

Pregunta

¿Por qué se alcanza el máximo error relativo en el valor de h=1016?.

  • 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 h que minimiza a los errores es del orden h=106.

Diferenciación numérica para una función f:RnR#

Supongamos f es dos veces diferenciable en intdomf. Si f:RnR entonces f:RnRn y 2f:RnRn×n (ver Definición de función, continuidad y derivada para definición de derivadas en funciones f:RnRm). Ambas funciones al evaluarse resultan en un vector en Rn y en una matriz en Rn×n respectivamente.

Podemos utilizar las fórmulas de aproximación en diferenciación numérica con diferencias finitas para el caso f:RR revisadas anteriormente para aproximar al gradiente y a la Hessiana.

Para el caso del gradiente se tiene por diferenciación hacia delante:

f(x)=[f(x)x1f(x)xn],f^(x)=[f(x+he1)f(x)hf(x+hen)f(x)h]Rn

con ej j-ésimo vector canónico que tiene un número 1 en la posición j y 0 en las entradas restantes para j=1,,n. Se cumple ||f(x)f^(x)||=O(h). Y para el caso de la Hessiana:

2f(x)=[2f(x)x122f(x)x2x12f(x)xnx12f(x)x1x22f(x)x222f(x)xnx22f(x)x1xn2f(x)x2xn2f(x)xn2],
2f^(x)=[f(x+2he1)2f(x+he1)+f(x)h2f(x+he1+he2)f(x+he1)f(x+he2)+f(x)h2f(x+he1+hen)f(x+he1)f(x+hen)+f(x)h2f(x+he1+he2)f(x+he2)f(x+he1)+f(x)h2f(x+2he2)2f(x+he2)+f(x)h2f(x+he2+hen)f(x+he2)f(x+hen)+f(x)h2f(x+he1+hen)f(x+hen)f(x+he1)+f(x)h2f(x+he2+hen)f(x+hen)f(x+he2)+f(x)h2f(x+2hen)2f(x+hen)+f(x)h2]

Se cumple: ||2f(x)f^2(x)||=O(h).

Ejemplo#

Aproximar f(x),2f(x) con diferencias hacia delante y h{1016,1014,,101} para f:R4R, dada por f(x)=(x12x22)2+x12+(x32x42)2+x32 en el punto x0=(1.5,1.5,1.5,1.5)T. Realizar una gráfica de log(Err_rel) vs log(h)

Para esta función se tiene:

f(x)=[4x1(x12x22)+2x14x2(x12x22)4x3(x32x42)+2x34x4(x32x42)],
2f(x)=[12x124x22+28x1x2008x1x24x12+12x22000012x324x42+28x3x4008x3x44x32+12x42]

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
f(x0)=[3030],

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
2f(x0)=[201800181800002018001818]

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:

ErrRel(aprox)=||aproxobj||||obj||

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'))
../../_images/Polinomios_de_Taylor_y_diferenciacion_numerica_132_0.png
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'))
../../_images/Polinomios_de_Taylor_y_diferenciacion_numerica_134_0.png
print(h[which.min(rel_err_gf2)])
[1] 1e-06

Ejercicio

Utilizando lenguajes de programación aproximar f(x),2f(x) con diferencias hacia delante y h{1016,1014,,101} para f:R3R, dada por f(x)=x1x2exp(x12+x325) en el punto x0=(1,3,2)T Realizar una gráfica de log(Err_rel) vs log(h).

Ejercicios

  1. Resuelve los ejercicios y preguntas de la nota.

Referencias

  1. R. L. Burden, J. D. Faires, Numerical Analysis, Brooks/Cole Cengage Learning, 2005.

  2. M. T. Heath, Scientific Computing. An Introductory Survey, McGraw-Hill, 2002.

  3. S. P. Boyd, L. Vandenberghe, Convex Optimization. Cambridge University Press, 2004.