Applied math¶
Self-contained numerical toolkits built over the public operators: iterative linear
algebra, an FFT carried as real/imaginary pairs, special functions, a general einsum,
and signal processing.
Linear algebra¶
linalg ¶
aneforge.linalg - iterative solvers and small-n direct factorizations on the ANE.
conjugate_gradient ¶
Solve A x = b for SPD A by CG with fixed iters (recurrence unrolls into one program); refine adds residual-correction rounds.
Source code in aneforge/linalg.py
jacobi ¶
Jacobi iteration x += D^{-1}(b - A x), fixed iters; converges for diagonally-dominant A.
Source code in aneforge/linalg.py
gauss_seidel ¶
Gauss-Seidel iteration, fixed iters; serial sweep is host-side, residual GEMV on the ANE.
Source code in aneforge/linalg.py
iterative_refine ¶
Sharpen a solve x0 by fixed-K residual correction (r = b - A x, solve A dx = r, x += dx); inner is a Richardson sweep.
Source code in aneforge/linalg.py
randomized_svd ¶
Truncated SVD of A [m,n] via Halko-Martinsson-Tropp range finding; returns (U[:, :k], S[:k], Vt[:k, :]).
Source code in aneforge/linalg.py
pca ¶
Principal components of X [samples, features] via randomized_svd of the centered data; returns (components, singular_values, mean).
Source code in aneforge/linalg.py
least_squares ¶
Solve min_x ||A x - b||_2 via the normal equations A^T A x = A^T b by CG + refinement (forming A^T A squares cond(A)).
Source code in aneforge/linalg.py
lsqr ¶
Solve min_x ||A x - b||_2 by LSQR (Golub-Kahan bidiagonalization), fully on the ANE; square systems only to cond~1e1 (use gmres beyond).
Source code in aneforge/linalg.py
dominant_eig ¶
Dominant eigenpair of symmetric A by power iteration, fully on the ANE (in-graph Rayleigh quotient); returns (lambda, eigenvector).
Source code in aneforge/linalg.py
gmres ¶
Solve A x = b for general A by one full GMRES(n) cycle, fully on the ANE; more accurate than lsqr on square systems but heavier.
Source code in aneforge/linalg.py
dominant_svd ¶
Dominant singular triple by power iteration on A^T A, fully on the ANE (half-step renorm holds fp16 range); returns (sigma1, u1, v1).
Source code in aneforge/linalg.py
eigh ¶
All eigenvalues of symmetric A by fixed-sweep cyclic Jacobi, ascending; iterate=True host-loops one compiled sweep (reaches larger n).
Source code in aneforge/linalg.py
svd ¶
All singular values of A (descending), fully on the ANE: Gram matrix via one GEMM, then its spectrum via cyclic-Jacobi eigh.
Source code in aneforge/linalg.py
svdvals_topk ¶
Top-k singular values of a large A, fully on the ANE (sketch + on-ANE qr + projection + on-ANE svd); extra power iters hurt in fp16.
Source code in aneforge/linalg.py
qr ¶
Thin QR A = Q R by modified Gram-Schmidt, fully on the ANE; returns (Q, R).
Source code in aneforge/linalg.py
cholesky ¶
Lower-triangular Cholesky factor L (A = L L^T) of SPD A, unpivoted, fully on the ANE.
Source code in aneforge/linalg.py
lu ¶
Unpivoted LU (A = L U) by Doolittle, fully on the ANE; returns (L, U).
Source code in aneforge/linalg.py
lu_pivoted ¶
Partial-pivoted LU (P A = L U) on the ANE; the per-column argmax pivot is a bridge op, so the program is segmented. Returns (P, L, U).
Source code in aneforge/linalg.py
generalized_eigh ¶
Eigenvalues of A x = lambda B x (A symmetric, B SPD), ascending, by composing on-engine kernels: B = L L^T, C = L^-1 A L^-T, eigh(C).
Source code in aneforge/linalg.py
eigvals ¶
Eigenvalues of general real A by the unshifted QR algorithm (M <- R Q -> real Schur form), fully on the ANE; returns a complex array.
Source code in aneforge/linalg.py
solve_triangular ¶
Solve T X = B for triangular T by substitution on the ANE, without forming the inverse (the routed accessor keeps large entries finite, as in cholesky/lu).
b is a vector [n] or a matrix [n, m]; the m columns are solved in one fused graph, since the
substitution is a row recurrence and each row is already a full-width slice. A 1-D b returns
shape [n], a 2-D one returns [n, m].
Source code in aneforge/linalg.py
solve ¶
Solve A x = b for general square A via the on-ANE pivoted LU (P A = L U).
The direct counterpart to the iterative conjugate_gradient/gmres: factor once, then forward-
and back-substitute. b is a vector [n] or a matrix [n, m]. The permutation is applied host-side
(P is a permutation matrix, so P b is a row gather, not a matmul worth dispatching).
Source code in aneforge/linalg.py
inv ¶
A^-1 for general square A, as the matrix solve A X = I on the on-ANE pivoted LU.
One solve against the n identity columns, not n separate solves: solve_triangular already
takes a matrix right-hand side and substitutes all columns in one fused graph, so the factorization
and both substitutions are dispatched once. Forming the inverse is worse conditioned than solving
against the right-hand side you actually have, so prefer solve when you have one.
Source code in aneforge/linalg.py
lstsq ¶
Least-squares solution of the overdetermined A x = b via the on-ANE thin QR.
A = Q R, so the normal equations collapse to R x = Q^T b with R triangular: a direct counterpart
to the iterative lsqr, and better conditioned than squaring A into A^T A the way least_squares
does. b is a vector [m] or a matrix [m, k]. Requires m >= n and full column rank.
Source code in aneforge/linalg.py
det ¶
det(A) via the on-ANE pivoted LU (P A = L U): permutation parity times prod(diag U).
slogdet ¶
(sign, log|det A|) via the on-ANE pivoted LU; the log-sum form stays finite where the raw product over/underflows. Returns (0.0, -inf) for a singular U diagonal, like numpy.
Source code in aneforge/linalg.py
pinv ¶
Rank-k pseudoinverse via randomized_svd: V diag(1/s) U^T with a relative cutoff on small singular values. Rank-k by construction - for a full-rank dense pinv use a dense library.
Source code in aneforge/linalg.py
norm ¶
Matrix norm of a 2-D A, composed on-engine as one fused graph.
order="fro" is sqrt(sum_square(A)); order=1 is the max absolute column sum and
order=inf the max absolute row sum, each an abs, a matmul against a ones vector (the
wide accumulator does the summing), and an amax. Oracle is np.linalg.norm (whose
argument is spelled ord; renamed here only to avoid shadowing the builtin).
Source code in aneforge/linalg.py
kron ¶
Kronecker product A (x) B by broadcast-multiply of expanded views; result [mp, nq].
Source code in aneforge/linalg.py
expm ¶
exp(A) by scaling and squaring: exp(A) = exp(A / 2^s)^(2^s), with the inner exponential a
Taylor sum of order terms.
The host picks s from norm(A, 1) so the scaled matrix has norm <= 1/2, which is where a
low-order Taylor sum is accurate; everything after that is on-engine gemms, O(order + s) of them.
fp16 squaring compounds error the same way matrix_power does, so this is for modest norms on
well-conditioned matrices. Oracle is scipy.linalg.expm.
Source code in aneforge/linalg.py
matrix_power ¶
A**n for integer n >= 0 by binary exponentiation over _ane_gemm, so it costs
O(log n) on-engine gemms rather than n-1. n=0 is the identity.
fp16 error compounds with each squaring, so this is for modest powers on
well-conditioned matrices; negative n needs an inverse and is rejected.
Oracle is np.linalg.matrix_power.
Source code in aneforge/linalg.py
polar ¶
Polar decomposition A = U @ P, on the ANE.
U is orthogonal for square A and semi-orthogonal for either rectangular shape (U^T U = I for tall A, U U^T = I for wide A); P is symmetric positive-semidefinite [n,n]. Composed from the on-ANE SVD: U_ S V^T -> U = U_ V^T, P = V diag(S) V^T. Oracle: scipy.linalg.polar(A, side='right').
Source code in aneforge/linalg.py
matrix_rank ¶
Numerical rank of A by counting singular values above tol, on the ANE.
Default tol is max(m, n) * 1e-5 * sigma_max, mirroring numpy's max(m, n) * eps * sigma_max but
with the measured floor of this backend in place of a machine epsilon -- the input is fp16, so
nothing here resolves a singular value below ~1e-5 of sigma_max. Pass tol explicitly if you
know your spectrum. Returns 0 for a zero matrix. Oracle: np.linalg.matrix_rank.
Source code in aneforge/linalg.py
cond ¶
2-norm condition number sigma_max / sigma_min via SVD, on the ANE.
Works for square and rectangular A. Accurate to cond ~1e3, past which the fp16 input itself stops resolving sigma_min. Returns inf for a zero or exactly singular matrix. Oracle: np.linalg.cond(A).
Source code in aneforge/linalg.py
FFT¶
fft ¶
aneforge.fft - staged Cooley-Tukey FFT on the ANE (complex carried as (re, im) pairs, each stage a matmul).
Plan ¶
A compiled staged-FFT program plus the cross-twiddle constants it threads in each call.
Source code in aneforge/fft.py
Plan2 ¶
A compiled 2-D FFT for [M,N] complex fields, one fused program (separable F_M @ X @ F_N^T, eight real GEMMs).
Source code in aneforge/fft.py
fft ¶
ifft ¶
rfft ¶
Forward FFT of a real signal (imag = 0) on the ANE; returns the full-length spectrum (X_re, X_im).
irfft ¶
Inverse real FFT of a Hermitian-symmetric spectrum on the ANE; returns the real time-domain signal of length N (the imag part is ~0 by Hermitian symmetry, and numpy.fft.irfft also returns only the real part).
Takes the FULL length-N spectrum, as rfft returns it -- not the N//2+1 half spectrum that
numpy.fft.irfft expects.
Source code in aneforge/fft.py
fft2 ¶
2-D FFT of an [M,N] complex field on the ANE (x_im=None means a real field); returns (X_re, X_im).
ifft2 ¶
magnitude ¶
Special functions¶
special ¶
aneforge.special - special functions as fused fp16 polynomial chains on the ANE; each takes and returns an aneforge.Tensor.
sin ¶
cos ¶
erfc ¶
Complementary error function erfc(x) for x in [0, ~6] (direct A&S form, no cancellation for large x).
Source code in aneforge/special.py
erf ¶
Error function erf(x) for |x| <= 2, as x*poly(x^2) (deg-5 minimax of erf(x)/x); odd, so it holds for negative x. Past |x| ~ 2 use 1 - erfc(|x|), where erfc is small and the subtraction no longer cancels.
Source code in aneforge/special.py
expm1 ¶
log1p ¶
log(1 + x) accurate near 0, as x * poly(x) (deg-7 minimax of log1p(x)/x) for x in [-0.5, 1.0].
lgamma ¶
Log-gamma log|Gamma(x)| for x in [1, 8], deg-8 minimax centered at x-4.5 (accurate in absolute terms; rel error ill-defined at the zeros x=1,2).
gamma ¶
Gamma function on x in [1, 2], deg-6 minimax centered at x-1.5; fp16-narrow (use gamma_via_lgamma for a wider range).
gamma_via_lgamma ¶
Gamma(x) = exp(lgamma(x)) for x in [1, ~7.5]; wider range than gamma at a small accuracy cost.
bessel_j0 ¶
bessel_i0 ¶
Modified Bessel I0(x) for |x| <= 3.75 (A&S 9.8.1); overflows fp16 past x~12.
bessel_k0 ¶
Modified Bessel K0(x) for 0 < x <= 2 (A&S 9.8.5): -ln(x/2) I0(x) + series((x/2)^2).
Source code in aneforge/special.py
bessel_j1 ¶
bessel_i1 ¶
Modified Bessel I1(x) for |x| <= 3.75 (A&S 9.8.3): x/2 * poly((x/3.75)^2). Overflows fp16 past x~12.
digamma ¶
Digamma (psi) function for x > 0. Shifts by one via the exact recurrence psi(x) = psi(x+1) - 1/x, then applies the asymptotic expansion log(x+1) - 1/(2(x+1)) - B2/(2(x+1)^2) - B4/(4(x+1)^4) - B6/(6*(x+1)^6). For 0 < x < 1 the -1/x term dominates and the result is still accurate.
Source code in aneforge/special.py
beta ¶
Beta(a, b) = exp(lgamma(a) + lgamma(b) - lgamma(a + b)), for a, b > 0. Most accurate when a, b >= 2.5 (away from lgamma's zeros at 1, 2); near those zeros the absolute error in lgamma (~0.2) propagates through the exp. Avoids the overflow of gamma(a)*gamma(b)/gamma(a+b).
Source code in aneforge/special.py
exp_wide ¶
exp for wide x via repeated squaring; does NOT reliably beat the native x.exp() (documented, not a win).
log_wide ¶
log for wide positive x via repeated sqrt + scale-back; matches rather than beats the native x.log().
sinh ¶
sinh(x) for |x| <= ~10; overflows fp16 near ln(65504) ~ 11 (same wall as softplus).
Accurate in absolute terms, not relative: e^x - e^-x cancels near the origin, so the relative error reaches ~2.4% for |x| < 0.01 (absolute error stays ~2e-5). Use expm1 if you need the small-argument regime; its Taylor form is only valid for |x| <= ~0.7.
Source code in aneforge/special.py
cosh ¶
asinh ¶
asinh(x) for |x| <= ~10; domain is all real x, but fp16 overflows for large |x|.
Evaluated on |x| and signed back: asinh is odd, and log(x + sqrt(x^2+1)) cancels for x << 0 (2.1% relative error at x=-10 in fp16, vs 0.0% for this form).
Source code in aneforge/special.py
acosh ¶
atanh ¶
atanh(x) for |x| < 1; singular at |x| == 1.
Accurate in absolute terms, not relative: (1+x)/(1-x) rounds to ~1 near the origin, so the relative error reaches ~4% for |x| < 0.01 (absolute error stays ~1e-4). log1p fixes the small-argument regime but is only valid for its argument in [-0.5, 1], i.e. |x| <= 1/3.
Source code in aneforge/special.py
einsum¶
einsum ¶
aneforge.einsum - numpy-style einsum lowered to aneforge graph ops as one fused ANE program.
EinsumUnsupported ¶
Bases: NotImplementedError
Raised for equations not lowerable to ANE matmul/reduce ops (diagonal-write outputs, triple-repeat indices, and ellipsis broadcast between mismatched size-1 dims). Plain ellipsis batch dims are supported.
einsum ¶
numpy-style einsum lowered to aneforge ops (matmul-reducible patterns, incl. '...' batch dims and diagonal/trace extraction); rejects diagonal-WRITE and repeated output indices.
Source code in aneforge/einsum.py
Signal processing¶
dsp ¶
aneforge.dsp - DSP toolkit on the ANE, composed from conv and the matmul-FFT (aneforge.fft).
hann ¶
Hann window (raised cosine); sym=False gives the periodic/DFT (STFT default) form.
Source code in aneforge/dsp.py
hamming ¶
Hamming window. sym=False = periodic (STFT) form.
Source code in aneforge/dsp.py
blackman ¶
Blackman window. sym=False = periodic (STFT) form.
Source code in aneforge/dsp.py
kaiser ¶
Kaiser window (I0 Bessel ratio, np.i0). Default beta=8.6 is Blackman-like. sym=False = periodic (STFT) form.
Source code in aneforge/dsp.py
bartlett ¶
Bartlett (triangular, zero endpoints) window. sym=False = periodic (STFT) form.
Source code in aneforge/dsp.py
tukey ¶
Tukey (tapered cosine) window; alpha is the taper fraction (0 = boxcar, 1 = hann). sym=False = periodic form.
Source code in aneforge/dsp.py
get_window ¶
Resolve window (name str, 'boxcar'/None for rectangular, or an array) to a length-M fp32 coefficient vector.
Source code in aneforge/dsp.py
sawtooth ¶
Periodic sawtooth/triangle waveform, period 2*pi (scipy.signal.sawtooth). width in [0,1] sets the rising fraction (1 = rising ramp, 0.5 = triangle, 0 = falling).
Source code in aneforge/dsp.py
square ¶
Periodic square wave, period 2*pi (scipy.signal.square): +1 for the first duty fraction of the cycle, -1 after. duty in [0,1].
Source code in aneforge/dsp.py
chirp ¶
Frequency-swept cosine (scipy.signal.chirp, linear sweep): frequency ramps from f0 at t=0 to f1 at t=t1. Phase = 2pi(f0t + (f1-f0)t^2/(2*t1)).
Source code in aneforge/dsp.py
fir_filter ¶
FIR filter y = x * taps (true convolution) on the ANE; taps flipped (ANE conv is correlation). mode in full/same/valid/lfilter.
Source code in aneforge/dsp.py
fft_convolve ¶
Linear convolution y = x * h via the FFT, length L+K-1; long signals use overlap-add against the cached kernel spectrum.
Source code in aneforge/dsp.py
freq_filter ¶
Brick-wall frequency-domain filter: FFT, zero the rejected bins, inverse-FFT. kind in lowpass/highpass/bandpass/bandstop.
Source code in aneforge/dsp.py
stft ¶
Short-time Fourier transform: window each frame, FFT, stack. Returns (Zr, Zi), each [n_freq, n_frames] (no 1/sum(win) scaling).
Source code in aneforge/dsp.py
spectrogram ¶
Magnitude (or power) spectrogram = |STFT|; mode in {'magnitude','power'}.
Source code in aneforge/dsp.py
correlate ¶
Cross-correlation r[k] = sum_n a[n+k] * b[n] on the ANE; short templates (Lb<=15) use the CrossCorrelation bridge, wider ones FFT. mode in valid/full/same.
Source code in aneforge/dsp.py
autocorrelate ¶
Autocorrelation r[k] for lags k=0..max_lag, on the ANE (correlation against the signal's own prefix); max_lag defaults to len(x)//4.
Source code in aneforge/dsp.py
iir_filter ¶
Arch-limited IIR via a fixed-length FIR unroll: truncate the IIR impulse response to n_taps and run as FIR (a direct IIR is the scan recurrence the ANE lacks).
Source code in aneforge/dsp.py
hilbert ¶
Analytic signal via the on-ANE FFT: z(t) = x(t) + i * Hx.
Returns (z_re, z_im) as numpy arrays: z_re == x and z_im is the Hilbert transform.
Any length >= 2 works -- the staged FFT plans for exactly N and does not pad (a prime N
degenerates to one dense N x N DFT, as everywhere else in aneforge.fft).
Oracle: scipy.signal.hilbert.
Source code in aneforge/dsp.py
welch ¶
welch(x, fs: float = 1.0, nperseg: int = 256, noverlap=None, window: str = 'hann', scaling: str = 'density')
Welch power spectral density from the on-ANE stft: window, segment, average the squared
magnitudes, normalize. Matches scipy.signal.welch(..., detrend=False) (no per-segment
detrending). scaling: 'density' (1/(fs*sum(w^2))) or 'spectrum' (1/sum(w)^2); the one-sided
spectrum doubles interior bins. Returns (f, Pxx).