---
title: "Circulant Embedding"
author: "Luke Tierney"
output:
  html_document
---

A Gaussian autocorrelation vector is created by

```{r}
f1 <- function(n, b) exp(- b * (0 : (n - 1)) ^ 2)
```

A circulant version is created by

```{r}
f2 <- function(n, b) exp(- b * pmin(0 : (n - 1), n : 1) ^ 2)
```

```{r}
plot(f1(16, 0.5), type = "l")
lines(f2(16, 0.5), col = "red")
```

The covariance matrix corresponding to `f1` can be created using the
`toeplitz` function:

```{r}
m1 <- toeplitz(f1(5, 0.5))
m1
```

This is identical to the upper left corner of the matrix for `f2(10, 0)`

```{r}
m2 <- toeplitz(f2(10, 0.5))
identical(m1, m2[1 : 5, 1 : 5])
```

A normal vector with covariance matrix corresponding to `f1(n, b)` can be
generated with

```{r}
gen1 <- function(n, b) {
    a <- f1(n, b)
    z <- rnorm(n)
    chol(toeplitz(a)) %*% z
}
```

An alternative is to use the larger circulant covariance matrix
corresponding for `f2(2 * n, b)`, keeping only the first half:

```{r}
gen2 <- function(n, b) {
    N <- 2 * n
    a <- f2(N, b)
    z <- rnorm(N)
    x <- Re(fft(sqrt(fft(a)) * fft(z), inverse = TRUE)) / N
    x[1 : n]
}
```

For larger sample sizes the circulant approach is much faster.

```{r}
n <- 2048
system.time(gen1(n, 0.5))
system.time(gen2(n, 0.5))
```

If `b` is constant then the Cholesky factor and the `sqrt(fft(a))`
vector can be pre-computed and re-used. This reduces but does not
eliminate the advantage of the circulant approach.

Usually the circulant embedding approach only extends the region of interest by 10% or 20%,
which is often enough for a good approximation.

Circulant embedding is particularly effective for spatial and volume data.
