# barycentric-rational
An interactive illustration of
> M. S. Floater and K. Hormann, **Barycentric rational interpolation with no poles
> and high rates of approximation**, *Numerische Mathematik* **107** (2007) 315–331.
> [doi:10.1007/s00211-007-0093-y](https://doi.org/10.1007/s00211-007-0093-y)
The method itself is a MATLAB-syntax script you can edit in the page. It runs in
your browser through [numbl](https://numbl.org); there is no server and nothing
to install.
## What the paper says
Interpolating a function at a given set of points is easy to do badly. The
degree-*n* polynomial through *n*+1 equally spaced points diverges as *n* grows,
which is Runge's example. Classical rational interpolation, fitting a quotient
pM/qN with M + N = n, often approximates better but offers
no control over where the poles land, and they land inside the interval.
Floater and Hormann's construction is short. Fix an integer *d* with 0 ≤ *d* ≤ *n*.
For each *i* let pi be the polynomial of degree at most *d* through the
*d*+1 points xi, …, xi+d, and blend those local polynomials
together:
$$r(x) = \frac{\sum_{i=0}^{n-d} \lambda_i(x)\, p_i(x)}{\sum_{i=0}^{n-d} \lambda_i(x)},
\qquad \lambda_i(x) = \frac{(-1)^i}{(x - x_i)\cdots(x - x_{i+d})}.$$
The results are that *r* has **no poles anywhere on the real line** for any *d*
(Theorem 1), that its error is **O(hd+1)** for *d* ≥ 1 **whatever the
node distribution**, as long as *f* is smooth enough (Theorem 2), and that *r* can
be rewritten in the barycentric form
$$r(x) = \sum_{k=0}^{n} \frac{w_k}{x - x_k} f(x_k) \Big/ \sum_{k=0}^{n} \frac{w_k}{x - x_k}$$
with explicit weights (equation 18), which is cheap to evaluate. On a uniform mesh
those weights are integers, nearly all equal, differing only near the two ends:
1, 4, 7, 8, …, 8, 7, 4, 1 for *d* = 3. That small change at the ends is what lifts
the approximation order from O(h) to O(h4).
The *d* = 0 case is Berrut's earlier interpolant, and *d* = *n* is ordinary
polynomial interpolation, so the family interpolates between the two.
## What the page shows
Four tabs, all driven by the same script:
- **Interpolant** — *f*, the rational interpolant *r*, and the nodes, with the
degree-*n* polynomial and a clamped C² cubic spline as optional overlays, plus
the pointwise error underneath.
- **Blending & weights** — the *n*−*d*+1 local polynomials and the normalised
blending functions λi, which sum to 1 everywhere but have oscillating
tails and no local support; and a stem plot of the barycentric weights, with the
integers of Section 4 read off when the mesh is uniform.
- **Poles** — the denominator of *r* on the real line, drawn as a signed *n*-th
root so that its sign and zeros survive the enormous dynamic range; all of its
roots plotted in the complex plane, none of them touching the real axis; and the
classical pM/qN alongside, with the poles it does put in
the interval.
- **Convergence** — max error against *n* on log-log axes for a range of *d*, with
the measured orders tabulated. With Runge's function on uniform nodes this
reproduces Table 1 of the paper, and with the spline turned on, Tables 3 and 4.
## The script
The editor holds the whole method. The app only asks it for two functions, and
optionally a third:
```matlab
w = bary_weights(x, d) % the weights, equation (18)
r = bary_eval(x, y, w, t) % the barycentric form, equation (1)
[P, L] = local_blend(x, y, d, t) % the blend of (4) and (5) [optional]
```
Anything that satisfies that contract will drive all four tabs, which is the point
of the alternative scripts in the **method** dropdown:
| script | what it does |
|---|---|
| Floater-Hormann | the paper |
| Berrut (d = 0) | weights (−1)k; set the nodes to **paired** to see why Theorem 3 needs a bounded mesh ratio |
| Integer weights | Section 4's closed form; identical to the first script while the mesh stays uniform, and not otherwise |
| Polynomial (d = n) | the Lagrange weights of equation (2); no poles, but Runge divergence |
| Equal weights | drop the alternating signs and a pole appears in every interval |
| Random weights | the generic barycentric rational interpolant: interpolates, has poles |
The last two are the counterpart to Schneider and Werner's theorem, quoted in the
paper, that a pole-free barycentric rational interpolant must have weights that
alternate in sign.
## Running it locally
```bash
npm install
npm run dev # http://localhost:5173
npm run build # type-check and bundle to dist/
```
Two test suites, neither of which needs a browser to be watched:
```bash
npm run test:matlab # the .m layer against the paper's tables, via the numbl CLI
npm run test:matlab -- --full # also n = 640
npm run test:browser # the built app in headless Chrome
```
`test:matlab` runs the MATLAB layer outside the browser through a clone of
[numbl](https://github.com/flatironinstitute/numbl) (set `NUMBL_DIR`; it defaults
to `~/src/numbl`) and checks it against the published numbers: the integer weight
patterns of Section 4 for *d* = 0…4, the error columns of Tables 1, 3 and 4, the
absence of real roots for every *d* across five node distributions, and the
identity r = Σ Li pi relating equations (1) and (4).
One entry of the paper's Table 1 does not reproduce: the sine row at *n* = 20 is
printed as 3.9e−05, but the order 5.5 printed beside it implies 1.7e−2 / 25.5
= 3.8e−04, which is what we get, and every other entry in the row matches to two
figures. We take it as a misprint.
## How it is put together
- `src/matlab/driver.m` — reads `params.json`, calls the method script, writes
`out.json`. It is prepended to whatever is in the editor, so the script's
functions become its local functions.
- `src/matlab/lib/*.m` — the parts that are not the method: node distributions,
test functions, the cubic spline, the classical rational interpolant.
- `src/methods/*.m` — the scripts in the dropdown.
- `src/engine/` — the numbl session. One session per script; parameter changes
only rewrite `params.json` and re-run, which is fast enough to drive a slider.
- `src/plot/` — a small SVG plotting layer. The palette is documented and was
checked with a colour-vision validator rather than by eye.
- `src/panels/` — one component per tab.
## License
Apache-2.0, matching numbl.