Linalg — symmetric eigendecomposition and graph Laplacian
linalg is Pleco-Xa’s small, exact linear-algebra corner: a symmetric-matrix eigensolver
and the normalized graph Laplacian. These are the two dependency-free primitives that the
Laplacian segmentation
pipeline surfaced — pure JavaScript, no native BLAS, and numerically matched to SciPy’s
output so the structural-clustering demo produces the same boundaries in Node and the
browser.
Both were validated against reference fixtures during development: eigh reconstructs to
1e-16 with eigenvalues within 1e-6, and laplacian matches
scipy.sparse.csgraph.laplacian to 1e-9.
Key functions
Section titled “Key functions”Verified against the built barrel (linalg namespace):
eigh(input, opts)→{ values, vectors }. Symmetric eigendecomposition via cyclic Jacobi (Golub & Van Loan §8.4), matchingscipy.linalg.eigh: eigenvalues ascending, eigenvectors orthonormal.vectors[i][k]is componentiof the eigenvector forvalues[k](SciPy’s column convention).laplacian(W, opts)→Float64Array[].normed: true(default) returns the symmetric normalized LaplacianI − D^{-1/2} W D^{-1/2};normed: falsereturns the combinatorialD − W.
Example
Section titled “Example”import { linalg } from 'pleco-xa'
// A weighted adjacency matrix (symmetric):const W = [ [0, 1, 1], [1, 0, 1], [1, 1, 0],]
const L = linalg.laplacian(W, { normed: true })const { values, vectors } = linalg.eigh(L)// values are ascending; the Fiedler vector is column 1 of `vectors`const fiedler = vectors.map((row) => row[1])- Input is never shape-guessed. Pass a 2D matrix (array of rows) or a flat row-major
array with explicit
{ flat, n }. Non-square input throws. eighrequires a genuinely symmetric matrix and throws on the worst offending pair if the two triangles disagree beyond tolerance — SciPy reads only one triangle, but Pleco-Xa verifies the full matrix so the returned decomposition actually reconstructs your input. Eigenvector signs are not canonicalised (the sign gauge is not part of the spectrum).laplacianfollows SciPy’s conventions exactly: the input diagonal is ignored (self-weights zeroed before the degree sum), and isolated nodes (degree 0) get a scale factor of 1, so their diagonal entry becomes0rather than1.- Jacobi throws rather than returning an unconverged result if it exceeds
maxSweeps(default 100) — raise it for ill-conditioned matrices.
API reference
Section titled “API reference”Full signatures: linalg namespace — namely
eigh and
laplacian.