Docs / nablatensor-tensor / com.nablatensor.tensor.linalg

final class

DenseLinalg

Reference dense factorizations for row-major float[] matrices: Cholesky, LU with partial pivoting, Householder QR, symmetric eigen (cyclic Jacobi) and thin SVD (one-sided Jacobi), plus the triangular solves built on them.

These are the custom kernels behind Linalg — no BLAS/LAPACK. They are unblocked (O(n³) with poor cache reuse for large n); the point is that nablatensor has factorizations at all, on every backend, via a host round-trip. Every accumulation is done in double and rounded to float only on write, so the F32 results are about as accurate as F32 can represent.

Methods

static float[] cholesky(float[] a, int n)

A (n×n, symmetric positive-definite) → lower-triangular L (n×n) with A = L·Lᵀ. Only the lower triangle of A is read.

throws
IllegalArgumentExceptionif a pivot is non-positive (not SPD)
static float[][] luDecompose(float[] a, int n)

LU factorization of A (n×n) with partial pivoting.

returns
[packed, pivots]packed holds the unit-lower L strictly below the diagonal and U on and above it; pivots[i] is the original row index now at position i (so P·A has row i equal to row pivots[i] of A). Split packed with luLower/luUpper.
static float[] luLower(float[] packed, int n)

Unit-lower L (1s on the diagonal) from a luDecompose packed result.

static float[] luUpper(float[] packed, int n)

Upper-triangular U from a luDecompose packed result.

static float[][] qrDecompose(float[] a, int m, int n)

Full Householder QR of A (m×n), m >= n.

returns
[Q, R]Q orthogonal (m×m), R upper (m×n), with A = Q·R.
static float[] solve(float[] a, int n, float[] b, int p)

Solve A·X = B for X; A (n×n), B (n×p), row-major.

static float[] luSolve(float[] packed, float[] pivots, int n, float[] b, int p)

Solve using an existing luDecompose result.

static double det(float[] a, int n)

det(A) via LU: product of the U diagonal times the permutation sign.

static float[][] eighSymmetric(float[] a, int n)

Eigendecomposition of a symmetric A (n×n): Householder reduction to tridiagonal form followed by the implicit-shift QL iteration (the classic tred2/tql2). O(n³) once for the reduction and the eigenvector accumulation, O(n²) per QL step — the same shape as LAPACK's ssyev, not the O(sweeps·n³) of plain Jacobi.

returns
[values, vectors]values length n ascending, vectors n×n with eigenvector i in column i, so A = V·diag(values)·Vᵀ.
static float[][] svdThin(float[] a, int m, int n)

Thin SVD of A (m×n, m >= n) via the symmetric eigendecomposition of AᵀA = V·Σ²·Vᵀ, then U = A·V·Σ⁻¹.

This reuses the fast eighSymmetric and is far quicker than a one-sided Jacobi SVD, at the cost of squaring the condition number — the smallest singular values of a badly-scaled A lose about half their digits. For a well-conditioned A the reconstruction is accurate to F32.

returns
[u, s, v]u m×n with orthonormal columns, s length n descending and non-negative, v n×n orthogonal, so A = U·diag(s)·Vᵀ.