Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example evaluating rational function #99

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,129 @@ Using a polynomial with interval coefficients guarantees that all arithmetic
operations involving `t` are conservative, or rigorous, with respect to floating
point arithmetic.

## Rigorous function evaluation

We can use Taylor series and Taylor models to evaluate a function where naive
evaluation using floating-point arithmetic would give incorrect results.
For example, consider the following function:

```math
g : \mathbb{R} \to \mathbb{R},\qquad g(\delta) = \frac{2(1 - δe^{-\delta} - e^{-\delta})}{(1 - e^{-\delta})^2}
```

Using calculus we can prove that $g$ is continuous on its domain and $g(0) = 1$.
However, the $0/0$ limit for $\delta \to 0$ can easily get floating-point computations wrong:

```@example eval_uni
g(δ) = 2(1 - δ*exp(-δ) - exp(-δ)) / (1 - exp(-δ))^2

[g(1/10^x) for x in 7:9]
```

Note that `g(1e-7)` gives approximately `0.9992`, while `g(1e-8)` gives `0.0`.
This is a common problem known as [catastrophic cancellation](https://en.wikipedia.org/wiki/Catastrophic_cancellation)
and it is mainly due to limited accuarcy of double precision floating point arithmetic.

```@example eval_uni
using Plots

fig = plot(ylab="g(δ)", xlab="δ")

dt = range(-10, 10, length=100)
plot!(fig, dt, g, color=:black, lab="", lw=2.0)
```

We can use a Taylor series with rational coefficients to do Taylor series manipulations
and more accurately evaluate $g$ near zero. First we define a Taylor (series) variable
of the desired maximum order:

```@example eval_uni
using TaylorSeries

n = 16 # order
t = Taylor1(Rational{Int}, n) # the coeffs of this Taylor series are rationals
```

Now we define a truncated exponential $u_n = \lfloor e^{-t} \rfloor_n := \sum_{i=0}^{n} \frac{(-t)^i}{i!} + \mathcal{O}(t^{n+1})$:

```@example eval_uni
u = sum((-t)^i / factorial(i) for i in 0:n)
```

Finally, we compute the Taylor series of $g(\delta)$ of the desired order:

```@example eval_uni
g_taylor = 2*(1 - t*u -u) / (1 - u)^2
```

```@example eval_uni
[evaluate(g_taylor, 1/10^x) for x in 7:9]
```

The evaluation for small values of $\delta$ gives a result that is closer to zero, as expected.
We can use the coefficients obtained to make a function that uses the Taylor
series expansion for small $\delta$, and the Julia's built-in function `expm1`
(which accurately computes $e^x - 1$) for larger $\delta$. As a rule of thumb, we take
$\delta = 0.2$ to decide which method to use. To evaluate the Taylor series we use the
`@evalpoly` macro that evaluates a polynomial using Horner's method.

```@example eval_uni
const coeffs = Tuple(Float64.(g_taylor.coeffs))
```

!!! note
We have chosen to convert the array to a tuple. This way `@evalpoly` unrolls the loops
of Horner's method at compile time because the number of coefficients of a tuple is statically known.

```@example eval_uni
@evalpoly(1/10^7, coeffs...)
```

```@example eval_uni
function g_poly(δ)
if abs(δ) < 0.2
return @evalpoly(δ, coeffs...)
else
y = expm1(-δ) # exp(-δ) - 1
return -2(y + δ*exp(-δ)) / y^2
end
end

[g_poly(1/10^x) for x in 7:9]
```

Let's now build a [rational function](https://en.wikipedia.org/wiki/Rational_function)
whose numerator and denominator are Taylor models, with guaranteed remainder error bounds.
We take the approach of using a Taylor model variable on the domain
$D = [-0.2, 0.2] \subseteq \mathbb{R}$ of order 16.

```@example eval_uni
using TaylorModels

tm = TaylorModel1(16, interval(0), -0.1..0.1)
```

```@example eval_uni
num(δ) = 2(1 - δ*exp(-δ) - exp(-δ))
denom(δ) = (1 - exp(-δ))^2
```

```@example eval_uni
N = num(tm)
```

```@example eval_uni
remainder(N)
```

```@example eval_uni
D = denom(tm)
```

```@example eval_uni
remainder(D)
```

### Authors
- [Luis Benet](http://www.cicc.unam.mx/~benet/), Instituto de Ciencias Físicas, Universidad Nacional Autónoma de México (UNAM)
- [David P. Sanders](http://sistemas.fciencias.unam.mx/~dsanders), Departamento de Física, Facultad de Ciencias, Universidad Nacional Autónoma de México (UNAM)
Expand Down