# BarycentricInterpolation

This Julia package implements the Barycentric formula for polynomial interpolation on equispaced points and Chebyshev points of the first and second kind. The formulae used are taken from the paper of Berrut and Trefethen, SIAM Review, 2004.

This is not a general purpose interpolation package but is intended to be used as a base for other numerical methods, such as numerical collocation. For a general use interpolation package see Interpolations.jl

## Usage

There are various types of polynomials defined based on the locations of their nodes (zeros).

- Equispaced (
`Equispaced{N}()`

) — a common choice when data is equispaced but suffers from Runge phenomenon for high degree polynomials. When used as part of a collocation scheme with Gauss-Legendre collocation points, they provide the benefit of super-convergence. By default the nodes are equispaced over [-1, +1]. - Chebyshev type 1 (
`Chebyshev1{N}()`

) — nodes distributed according to cos(π(2j + 1)/(2N + 2)) where N is the degree of the polynomial, for j in [0, N]. - Chebyshev type 2 (
`Chebyshev2{N}()`

) — nodes distributed according to cos(πj/N) where N is the degree of the polynomial, for j in [0, N]. - Legendre (
`Legendre{N}()`

) — nodes distributed according to the zeros of the corresponding Legendre polynomial where N is the degree of the polynomial. - Arbitrary nodes (
`ArbitraryPolynomial(nodes)`

) — nodes distributed as specified.

By default, each of the polynomials are defined over the range [-1, +1].
This can be modified by specifying a start and stop for the range, e.g.,
`Equispaced{10}(0, 1)`

will generate a 10th order polynomial with equispaced
nodes over the range [0, 1].

Polynomials with nodes asymptotically clustered towards the end points (such as Chebyshev) are optimal for avoiding the Runge phenomenon (see Trefethen, Spectral Methods in MATLAB, SIAM 2000).

Once a polynomial has been defined it can be used with the `nodes(poly)`

and
`weights(poly)`

functions to return the locations of the nodes and the values
of the Barycentric weights respectively. To interpolate a set of `y`

values
(defined at the nodes) use `interpolate(poly, y, x_new)`

; `x_new`

can be
either a scalar or a vector. If `x_new`

is omitted, the `interpolate`

function
returns a function `y(x)`

which can be used to evaluate the interpolant at any
point.

To obtain the interpolant as a linear combination of the `y`

values, use
`interpolation_matrix(poly, x)`

; this returns a matrix which can be multiplied
by a vector of `y`

values to calculate the interpolated value.

Finally, the derivative of the polynomial at the nodes can be obtained using
`differentiation_matrix(poly)`

. Similar to `interpolation_matrix`

, this returns
a matrix which can be multiplied by a vector of `y`

values to calculate the
derivative of `y`

.

## Simple example

```
using BarycentricInterpolation
p = Chebyshev2{20}() # create a Chebyshev type 2 polynomial of order 20
x = nodes(p) # get the nodes
y = sinpi.(x) # generate y values at the nodes
x_new = rand()*2 -1 # a random number in [-1, +1]
println(interpolate(p, y, x_new) ≈ sinpi(x_new)) # hopefully true!
D = differentiation_matrix(p) # get the differentiation matrix
println(interpolate(p, D*y, x_new) ≈ pi*cospi(x_new)) # hopefully true!
```

## More complicated example

For an example with Barycentric.jl applied to the simulation of a PDE (in combination with DifferentialEquations.jl) see https://cityinthesky.co.uk/posts/2018/barycentricinterpolation.jl/.

## License

Written by David A.W. Barton (david.barton@bristol.ac.uk) 2016-2021 and released under the MIT license https://opensource.org/licenses/MIT.