Skip to content

Commit d9a0134

Browse files
committed
Added theory & code for non-uniform grid spacing
1 parent f47402f commit d9a0134

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

inst/REFERENCES.bib

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,17 @@ @article{oliver1975selection
216216
doi={10.1007/BF01933661}
217217
}
218218

219+
@article{oliver1980selection,
220+
title={The selection of interpolation points in the numerical approximation of second and third order derivatives},
221+
author={Oliver, John},
222+
journal={Journal of Computational and Applied Mathematics},
223+
volume={6},
224+
number={3},
225+
pages={229--239},
226+
year={1980},
227+
doi={10.1016/0771-050x(80)90030-3}
228+
}
229+
219230
@article{aitken1938application,
220231
title={The Application of Quadratic Extrapolation to the Evaluation of Derivatives, and to Inverse Interpolation},
221232
volume={58},
@@ -289,3 +300,15 @@ @article{ridders1982accurate
289300
doi = {https://doi.org/10.1016/S0141-1195(82)80057-0}
290301
}
291302

303+
@article{rutishauser1963ausdehnung,
304+
title={Ausdehnung des {R}ombergschen prinzips},
305+
author={Rutishauser, H.},
306+
journal={Numerische Mathematik},
307+
volume={5},
308+
number={1},
309+
pages={48--54},
310+
year={1963},
311+
publisher={Springer},
312+
doi = {10.1007/BF01385877}
313+
}
314+

vignettes/step-size-selection.Rmd

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,137 @@ Near the optimum, store
374374

375375
\textcolor{red}{!!! TO BE COMPLETED!}
376376

377+
# Non-uniform grid spacing
378+
379+
So far, only uniform spacing for second-order-accurate numerical approximation of $f'(x)$ has been concerned.
380+
However, in many situations, higher-order accuracy is required, e.g. in computing Hessians for subsequent inversion to obtain asymptotic variance for statistical inference.
381+
Usually, uniform grids of the form $\{x_0, x_0\pm h, x_0\pm2h, x_0\pm3h, \ldots\}$ or $\{x_0\pm h, x_0\pm3h, x_0\pm5h, \ldots\}$ are used to evaluate $f$ and obtain an interpolating polynomial of the desired degree.
382+
Nevertheless, the choice of uniform grids is mostly a historical artefact rather than a modelling choice.
383+
384+
## First-order derivatives
385+
386+
@oliver1975selection proposed a method of selection of interpolation points on a non-uniform grid for first derivatives, and @oliver1980selection extended it to second and third derivatives.
387+
It is, indeed, not clear, which grid is better for fourth-order derivatives: $\{-3, -1, 1, 3\}$, $\{-2, -1, 1, 2\}$, or $\{-1.01, -1, 1, 1.01\}$.
388+
On one hand, shrinking the step size reduces numerical error; on the other hand, if $\pm h$ if contaminated by a certain level of rounding noise, then, $\pm 1.01 h$ does not seem ample enough to reduce the effect of the machine-related error.
389+
Therefore, there should be a trade-off between the truncation and rounding error as a non-trivial function of the grid spacing magnitude.
390+
391+
**Problem.** Given the desired accuracy order of a first derivative, find the optimal symmetric grid $x_0 \pm h, x_0 \pm c_1 h, x_0 \pm c_2 h$, $1 < c_1 < c_2 < \ldots$ for finite differencing to minimise the expected combined rounding and truncation errors.
392+
393+
**Solution.** There are many mathematically equivalent ways of writing down the derivative $f'(x_0)$ as the derivative of an approximating polynomial.
394+
Earlier authors, e.g. @rutishauser1963ausdehnung deemed that the most convenient approach, due to its simplicity, is Romberg-like iterative extrapolation.
395+
Modern textbooks, like @sauer2017numerical, prefer deriving the total error by bounding Fornberg sums.
396+
397+
Firstly, assume that the first two grid points are $x \pm h$, and the task consists in finding the scaling factors $1 < c_1, c_2, \ldots$ for $h$ to obtain the optimal grid.
398+
As in previous analyses, it is somewhat impractical to minimise the sum of two errors $|\varepsilon_{\mathrm{t}}| + \varepsilon_{\mathrm{r}}|$ directly as $\varepsilon_{\mathrm{t}}(h) = \frac{f^{d+a}(\xi)}{6}h^{a}$ ($\alpha\in[-1, 1]$) is not observed directly. (Note that $\xi = x + \alpha h$, $\alpha \in [0, 1]$ for $f'_{\mathrm{CD}}(x_0)$, but this is not true for higher-order derivatives due to the fact that the Generalised Intermediate Value Theorem yields more than one term.)
399+
400+
To shorten notation for the grid around the point of interest $x_0$, denote $x_{2i-1} = x_0 + c_i h$, $x_{2i} = x_0 - c_i h$, $i = 1, \ldots, a/2$, so $\{x_0, \ldots, x_n\}$ becomes the full evaluation grid.
401+
402+
It is possible to make two observations:
403+
404+
1. If the truncation error is valid, then, it is proportional to $(x_0 - x_1)\cdots(x_0 - x_n) / (n+1)! f^{d+a}(\xi)$;
405+
2. Moving $c_1$ closer to unity reduced the truncation error but increases the rounding error $|R| \le M\cdot E$ by a magnitude factor
406+
\[
407+
M = \sum_{i=1}^n |L'_i(x_0)|, \quad E = \max\{|\varepsilon_i|, \quad i = 1, \ldots, n\},
408+
\]
409+
where $L_i$'s are the Lagrange basis polynomials and $\epsilon_i := f(x_i) - \hat f(x_i)$ is the individual rounding error.
410+
411+
To remove the dependence of $\xi$ on $\{x_1, \ldots, x_n\}$, assume that $f^{(d+a)}(x)$ is relatively constant over the interval containing the points $x_1,\ldots, x_n$ -- require that $\Psi = (x_0 - x_1) \cdots (x_0 - x_n)$ is constant.
412+
Then, the scaling factor for the error is equal to
413+
\[
414+
S = M \sqrt[n]{\Psi} = (c_1 \dots c_{n/2})^{2/n} \sum_{i=1}^{n/2} \left|c_i^{-1} \prod_{j\ne i}^{n/2} \frac{r_j^2}{r_j^2 - r_i^2} \right|
415+
\]
416+
417+
Now we implement this objective function in R and minimise it.
418+
419+
```{r}
420+
S <- function(r) {
421+
if (is.unsorted(r)) r <- sort(r)
422+
n_2 <- length(r)
423+
n <- length(r) * 2
424+
s <- numeric(n_2)
425+
for (k in 1:n_2) {
426+
j <- setdiff(1:n_2, k)
427+
s[k] <- prod(abs(r[j]^2 / (r[k]^2 - r[j]^2))) / r[k]
428+
}
429+
prod(r)^(1/n_2) * sum(s)
430+
}
431+
```
432+
433+
At first, we can establish that $(-2, -1, 1, 2)$ is marginally inferior to $(-3, -1, 1, 3)$:
434+
```{r}
435+
S(c(1, 2))
436+
S(c(1, 3)) # Better
437+
S(c(1, 2.6)) # Even better
438+
```
439+
440+
Here is how the curve looks for a 4-point grid, suggesting that the optimal $c_1 \approx 2.6$:
441+
```{r}
442+
ff <- Vectorize(function(x) S(c(1, x)))
443+
oldpar <- par(mar = c(4, 4, 0, 0) + .1)
444+
curve(ff, 1.2, 5, bty = "n", xlab = expression(c[1]), ylab = "S")
445+
```
446+
447+
Now we reproduce Table 2b from @oliver1975selection:
448+
```{r}
449+
optim(par = 2, fn = ff, method = "BFGS")$par
450+
# THE optimal length-4 grid is x0 + (-2.62, -1, 1, 2.62)h
451+
452+
# Finding the optimum for multiple lengths
453+
nn <- 9
454+
res <- vector("list", nn)
455+
for (i in seq_along(res)) {
456+
set.seed(i)
457+
n <- 2 + 2*i
458+
# init.val <- (1:i)*2
459+
ff <- function(x) S(c(1, x))
460+
lower <- 1:i
461+
upper <- (1+(1:i)*3)^0.9
462+
init.pop <- sapply(1:i, function(j) runif(n*20, lower[j], upper[j]))
463+
init.pop <- apply(init.pop, 1, sort)
464+
if (NCOL(init.pop) == 1) init.pop <- matrix(init.pop) else
465+
if (ncol(init.pop) > nrow(init.pop)) init.pop <- t(init.pop)
466+
ff0 <- apply(init.pop, 1, ff)
467+
x0 <- init.pop[which.min(ff0), ]
468+
res[[i]] <- sort(optim(par = x0, fn = ff, method = "BFGS",
469+
control = list(reltol = 1e-8, maxit = 100))$par)
470+
}
471+
472+
# Table 2b from Oliver & Ruffhead (1975)
473+
tab <- matrix(nrow = length(res), ncol = nn)
474+
for (i in 1:nn) tab[i, 1:i] <- res[[i]]
475+
```
476+
477+
If one needs to devise a fixed-size grid, collapsing a triangular array into a one-size-fits-all may also be useful.
478+
```{r}
479+
bmean <- round(colMeans(tab, na.rm = TRUE), 2)
480+
print(bmean)
481+
ff(bmean) # Close to 10
482+
ff(bmean[1]) # Close to 2
483+
```
484+
485+
The optimal sequences can be visualised, which reveals the shrinkage of two subsequent points, implying that $c_{i+1} - c_{i} < c_{i} - c_{i-1}$ and $c_{i+1}/c_i < c_i / c_{i-1}$.
486+
```{r}
487+
par(mar = c(4, 4, 0, 0) + .1)
488+
plot(NULL, NULL, xlim = c(0, max(res[[i]]) + .5), ylim = c(0, nn+1),
489+
xlab = "Evaluation points", ylab = expression(n/2-1), bty = "n")
490+
for (i in 1:9) points(c(1, res[[i]]), rep(i, length(res[[i]])+1), pch = 16, type = "b")
491+
for (i in 1:9) points(sapply(res, "[", i), rep(1:nn), pch = 16, type = "b")
492+
abline(v = 0, lty = 2)
493+
abline(v = seq(1, max(bmean)+1), lty = 3, col = "#00000044")
494+
```
495+
496+
Finally, the near-optimal sequence of points can be generated by the estimated non-linear relationship $c_{i-1} \approx -4.37 + 5.03 i^{0.55}$ for $i = 2, \ldots, n/2$:
497+
```{r}
498+
b <- c(1, bmean) # Nodes
499+
x <- 1:10
500+
plot(x, b, bty = "n", xlab = expression(n/2-1), ylim = c(0, max(bmean)+1),
501+
ylab = expression("Approximate"~c[i]))
502+
ft <- nls(b ~ d + c*x^a, start = c(d = 0, c = 2.5, a = 0.75), weights = 1:10)
503+
lines(x, -4.37 + 5.03 * x^0.55)
504+
lines(x, predict(ft), col = 2)
505+
par(oldpar)
506+
```
507+
377508

378509
# References
379510

0 commit comments

Comments
 (0)