3.10 Richardson extrapolation

fx = function(x) x*x       # any real scalar valued function
x = 1                      # gradient at this point
h = max(1e-4, 1e-4*abs(x)) # small h
a = vector(length=4)       # for approximate gradients

# four central differences with consecutively smaller h
a[1] = (fx(x + h/2^1) - fx(x - h/2^1))/(2*h/2^1)
a[2] = (fx(x + h/2^2) - fx(x - h/2^2))/(2*h/2^2)
a[3] = (fx(x + h/2^3) - fx(x - h/2^3))/(2*h/2^3)
a[4] = (fx(x + h/2^4) - fx(x - h/2^4))/(2*h/2^4)

# first round refinement
a[1] = (a[2]*4^1 - a[1])/(4^1 - 1)
a[2] = (a[3]*4^1 - a[2])/(4^1 - 1)
a[3] = (a[4]*4^1 - a[3])/(4^1 - 1)

# second round refinement
a[1] = (a[2]*4^2 - a[1])/(4^2 - 1)
a[2] = (a[3]*4^2 - a[2])/(4^2 - 1)

# third (final) refinement
a[1] = (a[2]*4^3 - a[1])/(4^3 - 1)
a[1] # Answer
## [1] 2
# with i and j for the looping
i = 1 ; a[i] = (fx(x + h/2^i) - fx(x - h/2^i))/(2*h/2^i)
i = 2 ; a[i] = (fx(x + h/2^i) - fx(x - h/2^i))/(2*h/2^i)
i = 3 ; a[i] = (fx(x + h/2^i) - fx(x - h/2^i))/(2*h/2^i)
i = 4 ; a[i] = (fx(x + h/2^i) - fx(x - h/2^i))/(2*h/2^i)

# new i iteration
i = 1 ; j = 1 ; a[j] = (a[j + 1]*4^i  - a[j])/(4^i - 1)
i = 1 ; j = 2 ; a[j] = (a[j + 1]*4^i  - a[j])/(4^i - 1)
i = 1 ; j = 3 ; a[j] = (a[j + 1]*4^i  - a[j])/(4^i - 1)

i = 2 ; j = 1 ; a[j] = (a[j + 1]*4^i - a[j])/(4^i - 1)
i = 2 ; j = 2 ; a[j] = (a[j + 1]*4^i - a[j])/(4^i - 1)

i = 3 ; j = 1 ; a[j] = (a[j + 1]*4^i - a[j])/(4^i - 1)
a[1]
## [1] 2
# with loop
for (i in 1:4) a[i] = (fx(x + h/2^i) - fx(x - h/2^i))/(2*h/2^i)
for (i in 1:4) for (j in 1:(4 - i)) a[j] = (a[j + 1]*4^i - a[j])/(4^i - 1)
a[1]
## [1] 2
require(math)
Deriv0
## function (fx, x) 
## {
##     if (length(x) != 1) 
##         return(NULL)
##     a = matrix(nrow = 4, ncol = 4)
##     h = max(1e-04, 1e-04 * abs(x))
##     for (i in 1:4) {
##         a[i, 1] = (fx(x + h/2^i) - fx(x - h/2^i))/(2 * h/2^i)
##     }
##     for (i in 2:4) {
##         for (j in 2:i) {
##             a[i, j] = (4^(j - 1) * a[i, j - 1] - a[i - 1, j - 
##                 1])/(4^(j - 1) - 1)
##         }
##     }
##     return(a[4, 4])
## }
## <bytecode: 0x000001e2ab9ec550>
## <environment: namespace:math>
Deriv1
## function (fx, x) 
## {
##     if (length(x) != 1) 
##         return(NULL)
##     a = vector(length = 4)
##     h = max(1e-04, 1e-04 * abs(x))
##     for (i in 1:4) {
##         a[i] = (fx(x + h/2^i) - fx(x - h/2^i))/(2 * h/2^i)
##     }
##     for (i in 1:3) {
##         for (j in 1:(4 - i)) {
##             a[j] = (4^i * a[j + 1] - a[j])/(4^i - 1)
##         }
##     }
##     return(a[1])
## }
## <bytecode: 0x000001e2ab949028>
## <environment: namespace:math>
fx0 = function(x) sum(x*x)

Deriv0(fx0, 1)
## [1] 2
Deriv1(fx0, 1)
## [1] 2
Grad
## function (func, x) 
## {
##     n = length(x)
##     x1 = vector(length = n)
##     x2 = vector(length = n)
##     ga = vector(length = 4)
##     gr = vector(length = n)
##     for (i in 2:n) x1[i] = x2[i] = x[i]
##     for (i in 1:n) {
##         axi = abs(x[i])
##         if (axi <= 1) 
##             hi = 1e-04
##         else hi = 1e-04 * axi
##         for (k in 1:4) {
##             x1[i] = x[i] - hi
##             x2[i] = x[i] + hi
##             ga[k] = (func(x2) - func(x1))/(2 * hi)
##             hi = hi/2
##         }
##         ga[1] = (ga[2] * 4 - ga[1])/3
##         ga[2] = (ga[3] * 4 - ga[2])/3
##         ga[3] = (ga[4] * 4 - ga[3])/3
##         ga[1] = (ga[2] * 16 - ga[1])/15
##         ga[2] = (ga[3] * 16 - ga[2])/15
##         gr[i] = (ga[2] * 64 - ga[1])/63
##         x1[i] = x2[i] = x[i]
##     }
##     return(gr)
## }
## <bytecode: 0x000001e2a47625b8>
## <environment: namespace:math>
Grad(fx0, 1:3)
## [1] 2 4 6

Explore and enhance the following R script for derivatives of multi-variable function

Deriv2
## function (fx, x) 
## {
##     n = length(x)
##     x1 = vector(length = n)
##     x2 = vector(length = n)
##     ga = vector(length = 4)
##     gr = vector(length = n)
##     if (n > 1) 
##         for (i in 2:n) x1[i] = x2[i] = x[i]
##     for (i in 1:n) {
##         axi = abs(x[i])
##         if (axi <= 1) 
##             hi = 1e-04
##         else hi = 1e-04 * axi
##         for (k in 1:4) {
##             x1[i] = x[i] - hi
##             x2[i] = x[i] + hi
##             ga[k] = (fx(x2) - fx(x1))/(2 * hi)
##             hi = hi/2
##         }
##         for (j in 1:3) {
##             for (k in 1:(4 - j)) {
##                 ga[k] = (4^j * ga[k + 1] - ga[k])/(4^j - 1)
##             }
##         }
##         gr[i] = ga[1]
##         x1[i] = x2[i] = x[i]
##     }
##     return(gr)
## }
## <bytecode: 0x000001e2ac777100>
## <environment: namespace:math>
fx2 = function(x) sum(x^2)
Deriv2(fx2, c(1,1))
## [1] 2 2

Similarly hessian can be calculated.

Hessian
## function (fx, x) 
## {
##     n = length(x)
##     h0 = vector(length = n)
##     x1 = vector(length = n)
##     x2 = vector(length = n)
##     ha = vector(length = 4)
##     H = matrix(NA, nrow = n, ncol = n)
##     f0 = fx(x)
##     for (i in 1:n) {
##         x1[i] = x2[i] = x[i]
##         axi = abs(x[i])
##         if (axi < 1) 
##             h0[i] = 1e-04
##         else h0[i] = 1e-04 * axi
##     }
##     for (i in 1:n) {
##         for (j in i:1) {
##             hi = h0[i]
##             if (i == j) {
##                 for (k in 1:4) {
##                   x1[i] = x[i] - hi
##                   x2[i] = x[i] + hi
##                   ha[k] = (fx(x1) - 2 * f0 + fx(x2))/(hi * hi)
##                   hi = hi/2
##                 }
##             }
##             else {
##                 hj = h0[j]
##                 for (k in 1:4) {
##                   x1[i] = x[i] - hi
##                   x1[j] = x[j] - hj
##                   x2[i] = x[i] + hi
##                   x2[j] = x[j] + hj
##                   ha[k] = (fx(x1) - 2 * f0 + fx(x2) - H[i, i] * 
##                     hi * hi - H[j, j] * hj * hj)/(2 * hi * hj)
##                   hi = hi/2
##                   hj = hj/2
##                 }
##             }
##             w = 4
##             for (m in 1:2) {
##                 for (k in 1:(4 - m)) ha[k] = (ha[k + 1] * w - 
##                   ha[k])/(w - 1)
##                 w = w * 4
##             }
##             H[i, j] = (ha[2] * 64 - ha[1])/63
##             if (i != j) 
##                 H[j, i] = H[i, j]
##             x1[j] = x2[j] = x[j]
##         }
##         x1[i] = x2[i] = x[i]
##     }
##     return(H)
## }
## <bytecode: 0x000001e2abea3f08>
## <environment: namespace:math>

A little more sophiscated version of gradient and hessian function can be found in the numDeriv package.

library("numDeriv") 

grad(sin, pi)
## [1] -1
grad(sin, (0:10)*2*pi/10)
##  [1]  1.000  0.809  0.309 -0.309 -0.809 -1.000 -0.809 -0.309  0.309  0.809  1.000
func0 = function(x){ sum(sin(x))  }
grad(func0 , (0:10)*2*pi/10)
##  [1]  1.000  0.809  0.309 -0.309 -0.809 -1.000 -0.809 -0.309  0.309  0.809  1.000
func1 = function(x){ sin(10*x) - exp(-x) }
curve(func1,from=0,to=5)

x = 2.04
numd1 = grad(func1, x)
exact = 10*cos(10*x) + exp(-x)
c(numd1, exact, (numd1 - exact)/exact)
## [1] 3.335e-01 3.335e-01 8.469e-12
x = c(1:10)
numd1 = grad(func1, x)
exact = 10*cos(10*x) + exp(-x)
cbind(numd1, exact, (numd1 - exact)/exact)
##        numd1  exact           
##  [1,] -8.023 -8.023  6.118e-12
##  [2,]  4.216  4.216  6.271e-12
##  [3,]  1.592  1.592 -3.507e-12
##  [4,] -6.651 -6.651  6.698e-12
##  [5,]  9.656  9.656 -7.892e-13
##  [6,] -9.522 -9.522 -4.610e-12
##  [7,]  6.334  6.334  1.243e-11
##  [8,] -1.104 -1.104  6.093e-12
##  [9,] -4.481 -4.481  1.524e-12
## [10,]  8.623  8.623 -8.120e-13
func2 = function(x) c(sin(x), cos(x))
x = (0:1)*2*pi
jacobian(func2, x)
##      [,1] [,2]
## [1,]    1    0
## [2,]    0    1
## [3,]    0    0
## [4,]    0    0
x = 0.25 * pi
hessian(sin, x) 
##         [,1]
## [1,] -0.7071
fun1e = function(x) sum(exp(2*x))
x = c(1, 3, 5)
hessian(fun1e, x, method.args=list(d=0.01))
##           [,1]      [,2]      [,3]
## [1,] 2.956e+01 2.583e-15 1.620e-11
## [2,] 2.583e-15 1.614e+03 5.401e-12
## [3,] 1.620e-11 5.401e-12 8.811e+04
func = function(x){c(x[1], x[1], x[2]^2)}
z = genD(func, c(2,2,5))
z
## $D
##      [,1] [,2] [,3]      [,4]       [,5] [,6]      [,7]      [,8] [,9]
## [1,]    1    0    0 4.276e-08 -6.302e-25    0 2.101e-25 0.000e+00    0
## [2,]    1    0    0 4.276e-08 -6.302e-25    0 2.101e-25 0.000e+00    0
## [3,]    0    4    0 0.000e+00  1.377e-16    2 0.000e+00 5.506e-17    0
## 
## $p
## [1] 3
## 
## $f0
## [1] 2 2 4
## 
## $func
## function(x){c(x[1], x[1], x[2]^2)}
## <bytecode: 0x000001e2a4154010>
## 
## $x
## [1] 2 2 5
## 
## $d
## [1] 1e-04
## 
## $method
## [1] "Richardson"
## 
## $method.args
## $method.args$eps
## [1] 1e-04
## 
## $method.args$d
## [1] 1e-04
## 
## $method.args$zero.tol
## [1] 1.781e-05
## 
## $method.args$r
## [1] 4
## 
## $method.args$v
## [1] 2
## 
## 
## attr(,"class")
## [1] "Darray"