Skip to content

Instantly share code, notes, and snippets.

@brandondube
Created October 11, 2020 03:35
Show Gist options
  • Save brandondube/b95df13ccbb0de462659e8ea52c4e557 to your computer and use it in GitHub Desktop.
Save brandondube/b95df13ccbb0de462659e8ea52c4e557 to your computer and use it in GitHub Desktop.
recursive_zernike.jl
function abc(n, α, β, x)
# the parens emphasize the terms, not PEMDAS
a = (2n) * (n + α + β) * (2n + α + β - 2)
b1 = (2n + α + β -1)
b2 = (2n + α + β)
b2 = b2 * (b2 - 2)
b = b1 * (b2 * x + α^2 - β^2)
c = 2 * (n + α - 1) * (n + β - 1) * (2n + α + β)
return a, b, c
end
"""
jacobi(n, α, β, x)
Compute the Jacobi polynomial of order n and weights α,β at point x.
This function uses a recurrence relation and is numerical stable to very high
order. The computation time is linear w.r.t. the order n. jacobi.(n, a, b, x)
is also linear w.r.t. the size of argument x.
"""
function jacobi(n, α, β, x)
if n == 0
return 1
elseif n == 1
return (α + 1) + (α + β + 2) * ((x-1)/2)
end
# this uses a loop to avoid recursion
# we init P of n-2, n-1, and n=2.
# the latter is the first recursive term.
# then loop from 3 up to n, updating
# Pnm2, Pnm1, a, b, c, Pn
Pnm2 = jacobi(0, α, β, x)
Pnm1 = jacobi(1, α, β, x)
a, b, c = abc(2, α, β, x)
Pn = ((b * Pnm1) - (c * Pnm2)) / a
if n == 2
return Pn
end
for i = 3:n
Pnm2 = Pnm1
Pnm1 = Pn
a, b, c = abc(n, α, β, x)
Pn = ((b * Pnm1) - (c * Pnm2)) / a
end
return Pn
end
"""
kronecker(i,j)
1 if i==j, else 0; mathematical kronecker function
"""
function kronecker(i,j)
return i==j ? 1 : 0
end
"""
sign(x)
-1 if x < 0, else 1. scalar multiple to indicate sign of x.
"""
function sign(x)
return x<0 ? -1 : 1
end
"""
zernike_norm(n, m)
Norm of Zernike polynomial of radial order n, azimuthal order m.
The norm is the average squared distance to zero. By multiplying a zernike
value by the norm, the term is given unit stdev or RMS.
"""
function zernike_norm(n, m)
num = √(2 * (n+1)) / (1 + kronecker(m, 0))
end
"""
zernike(n, m, ρ, θ[; norm])
Zernike polynomial of radial order n and azimuthal order m, evaluated at the
point (ρ, θ). No normalization is required of (ρ, θ), though the polynomials
are orthogonal only over the unit disk.
norm is a boolean flag indicating whether the result should be orthonormalized
(scaled to unit RMS) or not.
"""
function zernike(n, m, ρ, θ; norm::Bool=true)
n_j = (n - m) / 2
am = abs(m)
# α=0, β=|m|
# there is a second syntax where you have x reversed, 1 - ρ^2,
# in which ase you swap α and β. It makes no difference
out = jacobi(n_j, 0, am, ρ^2 - 1)
if m != 0
if sign(m) == -1
f = sin
else
f = cos
end
out *= (ρ^am * f(m*θ))
end
if norm
out *= zernike_norm(n,m)
end
return out
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment