Approximation on domains
The AAA algorithm can be used to approximate functions on other domains as defined in the ComplexRegions
package.
Unit circle and disk
The domain unit_circle
is predefined. Here's a function approximated on the unit circle:
using RationalFunctionApproximation, CairoMakie, DomainColoring
const shg = current_figure
f = z -> (z^3 - 1) / sin(z - 0.9 - 1im)
r = approximate(f, unit_circle)
Barycentric rational function of type (9,9) on the domain: ClosedPath with 1 curve
This approximation is accurate to 13 digits, as we can see by plotting the error around the circle:
errorplot(r)

Here is how the approximation looks in the complex plane (using a black cross to mark the pole):
using ComplexRegions, ComplexPlots
domaincolor(r, [-1.5, 1.5, -1.5, 1.5], abs=true)
lines!(unit_circle, color=:white, linewidth=4)
scatter!(poles(r), markersize=16, color=:black, marker=:xcross)
limits!(-1.5, 1.5, -1.5, 1.5)
shg()

Above, you can also see the zeros at roots of unity.
This next function has infinitely many poles and an essential singularity inside the unit disk:
f = z -> tan(1 / z^4)
r = approximate(f, unit_circle)
domaincolor(r, [-1.5, 1.5, -1.5, 1.5], abs=true)
lines!(unit_circle, color=:white, linewidth=4)
shg()

We can request an approximation that is analytic in a region. In this case, it would not make sense to request one on the unit disk, since the singularities are necessary:
r = approximate(f, unit_disk)
Barycentric rational function of type (0,0) on the domain: Region interior to Circle(0.0+0.0im,1.0,ccw)
In the result above, the approximation is simply a constant function, as the algorithm could do no better. However, if we request analyticity in the region exterior to the circle, everything works out:
r = approximate(f, exterior(unit_circle))
z, err = check(r)
maximum(abs, err)
9.94812025285098e-14
Other shapes
We are not limited to intervals and circles! There are other shapes available in ComplexRegions.Shapes
:
import ComplexRegions.Shapes
r = approximate(z -> log(0.35 + 0.4im - z), interior(Shapes.cross))
domaincolor(r, [-1.5, 1.5, -1.5, 1.5], abs=true)
lines!(boundary(r.domain), color=:white, linewidth=4)
shg()

c = Shapes.hypo(5)
r = approximate(z -> (z+4)^(-3.5), interior(c))
domaincolor(r, [-5, 5, -5, 5], abs=true)
lines!(c, color=:white, linewidth=4)
shg()

Here are the predefined shapes:
shapes = [
Shapes.circle Shapes.ellipse(2, 1) Shapes.squircle;
Shapes.square Shapes.triangle Shapes.cross;
Shapes.hypo(3) Shapes.star Shapes.spiral(2, 0.7)
]
fig = Figure(size=(400, 400))
for i in 1:3, j in 1:3
ax, _ = lines(fig[i, j], shapes[i, j], linewidth=2, axis=(autolimitaspect=1,))
hidedecorations!(ax); hidespines!(ax)
end
resize_to_layout!(fig)
shg()

Unbounded domains
It's also possible to approximate on unbounded domains, but this capability is not yet automated. For example, the function
f = z -> 1 / sqrt(z - (-1 + 3im))
#9 (generic function with 1 method)
is analytic on the right half of the complex plane. In order to produce an approximation on that domain, we can transplant it to the unit disk via a Möbius transformation $\phi$:
z = cispi.(range(-1, 1, length=90)) # points on the unit circle
φ = Mobius( [-1, -1im, 1], [1im, 0, -1im]) # unit circle ↦ imag axis
extrema(real, φ.(z))
(-1.1158266984625186e-14, 3.135304997405465e-13)
By composing $f$ with $\phi$, we can approximate within the disk while $f$ is evaluated only on its native domain:
r = approximate(f ∘ φ, interior(unit_circle))
domaincolor(r, [-2, 2, -2, 2], abs=true)
lines!(unit_circle, color=:white, linewidth=4)
scatter!(nodes(r.fun), color=:black, markersize=8)
shg()

Above, the black markers show the nodes of the interpolant. We can view the same approximation within the right half-plane by composing $r$ with $\phi^{-1}$:
φ⁻¹ = inv(φ)
domaincolor(r ∘ φ⁻¹, [-8, 8, -8, 8], abs=true)
lines!([(0, 8), (0, -8)], color=:white, linewidth=4)
scatter!(φ.(nodes(r.fun)), color=:black, markersize=8)
limits!(-8, 8, -8, 8)
shg()
