Skip to main content

Module zernike

Module zernike 

Source
Expand description

The batch Zernike kernel: every mode through degree L at one point, in Cartesian coordinates.

§The Cartesian substitution

The polar definition $Z_n^m = R_n^{|m|}(\rho)\cos(m\theta)$ suggests a sin_cos per mode and a powi per mode. Both disappear under one substitution.

Write $s = x^2 + y^2 = \rho^2$. The radial polynomial factors as

R_n^{|m|}(\rho) = \rho^{|m|}\, Q_{k,|m|}(s), \qquad
Q_{k,m}(s) = P_k^{(0,m)}(2s - 1), \qquad k = \tfrac{n - |m|}{2}

so the $\rho^{|m|}$ is the only place an odd power of $\rho$ appears, and $Q$ is an honest polynomial in s. Meanwhile

(x + iy)^m = \rho^m\left(\cos m\theta + i \sin m\theta\right)

so $\rho^{|m|}\cos(m\theta)$ and $\rho^{|m|}\sin(m\theta)$ are exactly the real and imaginary parts of $(x+iy)^{|m|}$, which come off a two-line complex ladder. The $\rho^{|m|}$ the radial part needed and the $\rho^{|m|}$ the angular part produced are the same factor, so they never have to be formed separately:

Z_n^m = Q_{k,|m|}(s) \times \begin{cases}\operatorname{Re}(x+iy)^{m} & m \ge 0\\
                                         \operatorname{Im}(x+iy)^{|m|} & m < 0\end{cases}

The whole basis is therefore pure polynomial arithmetic in (x, y): no atan2, no sqrt, no trigonometry, no division, $O(L^2)$ FMAs total, and no singularity at the pupil centre (which the polar form has, in $\partial_\theta Z / \rho$).

Taking (x, y) rather than $(\rho, \theta)$ is thus not a convenience: a polar entry point would make the caller pay an atan2 per sample to build an angle this kernel immediately destroys. Pupil samples arrive as Cartesian coordinates anyway.

§The recurrence

$Q_{k,m}$ is the Jacobi three-term recurrence rewritten in s rather than $t = 2s-1$, which folds the change of variable into the coefficients instead of spending an operation on it per mode:

Q_{0,m} = 1,\qquad Q_{1,m}(s) = (m+2)s - (m+1)
Q_{k,m} = (A_{k,m}\,s + B_{k,m})\,Q_{k-1,m} - C_{k,m}\,Q_{k-2,m}

with, writing $c = 2k(k+m)(2k+m-2)$,

A = \frac{2(2k+m-1)(2k+m)(2k+m-2)}{c},\quad
B = \frac{-(2k+m-1)(m^2 + (2k+m)(2k+m-2))}{c},\quad
C = \frac{2(k-1)(k+m-1)(2k+m)}{c}

Every coefficient is a ratio of small integers - the largest through L = 16 is 3360, comfortably exact in f32 - and at stamped literal (k, m) they fold to .rodata constants. Three operations per mode: one FMA for As + B, one multiply, one FMA.

Running the recurrence in k at fixed m is what makes this $O(L^2)$ rather than the $O(L^3)$ of calling the single-mode entry point per mode, which restarts the recurrence from k = 0 every time.

§Layout

out[j] for the ANSI Z80.28 / OSA index j = (n(n+2) + m)/2, so N must be (L+1)(L+2)/2. ANSI is the layout rather than Noll or Fringe because it is the scheme whose index is a closed form and whose degree truncation is contiguous; noll_to_ansi and fringe_to_ansi gather from it.

Nothing normalizes (x, y) onto the unit disc, exactly as the spherical-harmonic kernels do not renormalize their direction. Outside it the polynomials are still evaluated correctly and simply are not orthogonal.

Constants§

MAX_DEGREE
Highest degree the stamped ladder below covers. Beyond it the kernel takes the rolled path, which is correct at any degree but computes its coefficients at runtime and does not unroll. Extending the ladder is mechanical: append literals to the m list and, every two degrees, to the k list.

Functions§

reduced_radial_impl
The reduced radial polynomial $Q_{k,m}(s) = P_k^{(0,m)}(2s - 1)$ at runtime (k, m).
zernike_basis_d_impl
zernike_basis_impl plus the Cartesian gradient of every mode.
zernike_basis_impl
Every Zernike mode through degree L at the Cartesian point (x, y).
Last built: 2026-09-08 21:35:55 UTC