deepbox/linalg
Properties & Matrix Functions
Inverses, determinants, norms, rank, matrix functions such as expm, and special matrix constructors.
- Matrix functions
Overview
invreports a matrix as singular only when a pivot is exactly zero, as NumPy does. A matrix that is singular up to rounding returns very large entries instead of throwing. Checkcond(a)ormatrixRank(a)first, or usepinv.det,norm,condandmatrixRankreturn JavaScript numbers.traceandslogdetreturn tensors.norm(x, ord)accepts numeric orders,"fro"and"nuc".expm,logm,sqrtmandmatrixPowertake square 2-D matrices and returnfloat64.kronandblockDiagcombine matrices. The constructors (hilbert,hadamard,toeplitz,vandermonde,circulant,companion,hankel) take plain numbers and arrays rather than tensors.matrix_powerandblock_diagare deprecated aliases ofmatrixPowerandblockDiag.
inv
function inv(a: Tensor): Tensor;Compute the inverse of a matrix.
Finds matrix A^(-1) such that A * A^(-1) = I.
Parameters:
Parameters
aTensor- Square matrix of shape (N, N)
Returns: Inverse matrix
Requirements: - Matrix must be square - Matrix must be non-singular (det(A) ≠ 0)
Numerical singularity: as in NumPy, only an exactly zero pivot is reported as singular. A matrix that is singular up to rounding error, such as
[[1, 2, 3], [4, 5, 6], [7, 8, 9]], returns very large entries instead of throwing. Checkcond(a)ormatrixRank(a)first when the input may be rank deficient, or use pinv. Properties: - A * inv(A) = I - inv(inv(A)) = A - inv(AB) = inv(B) * inv(A)
Returns
Tensor
Throws
ShapeErrorIf matrix is not square or not 2DDTypeErrorIf input has string or complex dtypeDataValidationErrorIf matrix is singular or contains non-finite values
import { inv } from 'deepbox/linalg';import { tensor, matmul } from 'deepbox/ndarray'; const A = tensor([[1, 2], [3, 4]]);const Ainv = inv(A); // Verify: A * A^(-1) ≈ Iconst I = matmul(A, Ainv);pinv
function pinv(a: Tensor, rcond?: number): Tensor;Compute the Moore-Penrose pseudo-inverse.
Generalization of matrix inverse for non-square or singular matrices.
Parameters:
Parameters
aTensor- Input matrix of shape (M, N)
rcondoptionalnumber- Cutoff for small singular values, relative to the largest one. Defaults to max(M, N) * machine epsilon, as in NumPy 2.x. Returns: Pseudo-inverse of shape (N, M) Properties: - A * pinv(A) * A = A - pinv(A) * A * pinv(A) = pinv(A) - For full rank square matrix: pinv(A) = inv(A) - For overdetermined system: pinv(A) gives least squares solution Algorithm: Using SVD A = U * Σ * V^T pinv(A) = V * Σ^+ * U^T where Σ^+ is pseudo-inverse of Σ (1/s_i for s_i > rcond * s_max, else 0)
Returns
Tensor
Throws
ShapeErrorIf input is not 2D matrixDTypeErrorIf input has string or complex dtypeInvalidParameterErrorIf rcond is negative or non-finiteDataValidationErrorIf input contains non-finite values (NaN, Infinity)
import { pinv } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; // Non-square matrixconst A = tensor([[1, 2], [3, 4], [5, 6]]);const Apinv = pinv(A); console.log(Apinv.shape); // [2, 3]blockDiag
function blockDiag(...matrices: Tensor[]): Tensor;Construct a block-diagonal matrix from the provided 2-D matrices.
The blocks need not be square: for blocks of shape (r_i, c_i) the result has shape (sum r_i, sum c_i) and zeros outside the blocks. With no arguments an empty (0, 0) matrix is returned. The result is float64.
Parameters
matricesTensor[]- One or more 2-D tensors to place along the diagonal
Returns
TensorBlock-diagonal matrix
Throws
ShapeErrorIf any input is not 2-DDTypeErrorIf any input has string or complex dtype
import { blockDiag } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[1, 2], [3, 4]]);const B = tensor([[5]]);const D = blockDiag(A, B); // shape [3, 3]circulant
function circulant(c: number[]): Tensor;Create a circulant matrix from a vector.
C[i,j] = c[(i - j) mod n], so c is the first column and every column is a cyclic shift of the previous one.
Parameters
cnumber[]- First column of the circulant matrix
Returns
TensorCirculant matrix of shape (n, n)
Throws
InvalidParameterErrorIf c is empty
companion
function companion(c: number[]): Tensor;Create a companion matrix from polynomial coefficients.
The companion matrix of the polynomial p(x) = c[0]*x^n + c[1]*x^(n-1) + ... + c[n] is the n×n matrix with -c[1..n]/c[0] in the first row and ones on the sub-diagonal (the convention of scipy.linalg.companion). Its eigenvalues are the roots of p.
Parameters
cnumber[]- Polynomial coefficients (leading coefficient first), length >= 2
Returns
TensorCompanion matrix of shape (c.length - 1, c.length - 1)
Throws
InvalidParameterErrorIf c has fewer than 2 elements or c[0] is zero or not finite
import { companion } from 'deepbox/linalg'; companion([1, -10, 31, -30]); // [[10, -31, 30], [1, 0, 0], [0, 1, 0]]expm
function expm(A: Tensor): Tensor;Compute the matrix exponential exp(A).
Uses scaling and squaring with a degree-6 Padé approximant, so it also works for defective matrices and matrices with complex eigenvalues (for example a rotation generator). The result is float64.
Parameters
ATensor- Square matrix
Returns
Tensorexp(A)
Throws
ShapeErrorIf A is not a square 2-D matrixDTypeErrorIf A has string or complex dtypeDataValidationErrorIf A contains NaN or Infinity
import { expm } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[0, 1], [-1, 0]]);const E = expm(A); // [[cos 1, sin 1], [-sin 1, cos 1]]hadamard
function hadamard(n: number): Tensor;Create a Hadamard matrix of order n.
n must be a power of 2. Uses Sylvester's construction, so H[i,j] = (-1)^popcount(i & j).
Parameters
nnumber- Order of the matrix (must be power of 2)
Returns
TensorHadamard matrix of shape (n, n) with entries +1/-1
Throws
InvalidParameterErrorIf n is not a positive power of 2, or is too large to allocate
hankel
function hankel(c: number[], r?: number[]): Tensor;Create a Hankel matrix from first column and optional last row.
H[i,j] = c[i+j] for i+j < len(c), otherwise r[i+j-len(c)+1]. The first element of r is ignored (the last element of c is used instead), as in scipy.linalg.hankel.
Parameters
cnumber[]- First column
roptionalnumber[]- Last row (optional; defaults to zeros of length c.length)
Returns
TensorHankel matrix of shape (c.length, r.length)
Throws
InvalidParameterErrorIf c is empty
import { hankel } from 'deepbox/linalg'; hankel([1, 2, 3]); // [[1, 2, 3], [2, 3, 0], [3, 0, 0]]hankel([1, 2, 3], [3, 4, 5]); // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]hilbert
function hilbert(n: number): Tensor;Create a Hilbert matrix of size n.
H[i,j] = 1 / (i + j + 1)
Parameters
nnumber- Size of the matrix
Returns
TensorHilbert matrix of shape (n, n)
Throws
InvalidParameterErrorIf n is not a positive integer
kron
function kron(A: Tensor, B: Tensor): Tensor;Compute the Kronecker product of two matrices.
If A is (m, n) and B is (p, q), the result is (m*p, n*q). The result is always float64.
Parameters
ATensor- First matrix of shape (m, n)
BTensor- Second matrix of shape (p, q)
Returns
TensorKronecker product of shape (m*p, n*q)
Throws
ShapeErrorIf A or B is not 2-DDTypeErrorIf A or B has string or complex dtype
import { kron } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[1, 2], [3, 4]]);const B = tensor([[0, 5], [6, 7]]);const K = kron(A, B); // shape [4, 4]logm
function logm(A: Tensor): Tensor;Compute the principal matrix logarithm log(A), the solution L of expm(L) = A whose eigenvalues have imaginary part in (-pi, pi).
Symmetric input is handled through the symmetric eigendecomposition and requires strictly positive eigenvalues. Other input uses inverse scaling and squaring, so defective matrices and matrices with complex eigenvalues work as long as no eigenvalue lies on the closed negative real axis. The result is float64.
Parameters
ATensor- Square matrix with no eigenvalues on the closed negative real axis
Returns
Tensorlog(A)
Throws
ShapeErrorIf A is not a square 2-D matrixDTypeErrorIf A has string or complex dtypeDataValidationErrorIf A contains NaN or Infinity, or has no real principal logarithm (a zero, negative or negative-real-axis eigenvalue)
import { logm, expm } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[2, 1], [1, 2]]);const L = logm(A);// expm(L) is A up to roundingmatrixPower
function matrixPower(A: Tensor, n: number): Tensor;Raise a square matrix to an integer power.
For positive n: computes A^n via repeated squaring. For n=0: returns the identity matrix. For negative n: computes (A^{-1})^{|n|}.
The result is always float64, whatever the dtype of A.
Parameters
ATensor- Square matrix of shape (m, m)
nnumber- Integer exponent
Returns
TensorA^n of shape (m, m)
Throws
ShapeErrorIf A is not a square 2-D matrixInvalidParameterErrorIf n is not an integerDTypeErrorIf A has string or complex dtypeDataValidationErrorIf n is negative and A is singular
import { matrixPower } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[1, 2], [3, 4]]);const A3 = matrixPower(A, 3); // A * A * Asqrtm
function sqrtm(A: Tensor): Tensor;Compute the principal matrix square root sqrt(A), the solution S of S * S = A whose eigenvalues have non-negative real part.
Details
- Symmetric input is handled through the symmetric eigendecomposition. Eigenvalues that are negative only within rounding error (a positive semi-definite matrix) are treated as zero. - Other input uses the Denman-Beavers iteration, so defective matrices and matrices with complex eigenvalues work as long as the result is real. Diagonalisable singular matrices fall back to the eigendecomposition.
The result is float64.
Parameters
ATensor- Square matrix with no eigenvalues on the negative real axis
Returns
Tensorsqrt(A)
Throws
ShapeErrorIf A is not a square 2-D matrixDTypeErrorIf A has string or complex dtypeDataValidationErrorIf A contains NaN or Infinity, or has no real principal square root (for example a negative eigenvalue)
import { sqrtm } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const S = sqrtm(tensor([[4, 0], [0, 9]])); // [[2, 0], [0, 3]]toeplitz
function toeplitz(c: number[], r?: number[]): Tensor;Create a Toeplitz matrix from first column and optional first row.
A Toeplitz matrix has constant diagonals. If only c is given, the result is symmetric. If both c and r are given, c defines the first column and r defines the first row (r[0] is ignored in favor of c[0]).
Parameters
cnumber[]- First column
roptionalnumber[]- First row (optional; defaults to c for symmetric Toeplitz)
Returns
TensorToeplitz matrix of shape (c.length, r.length)
Throws
InvalidParameterErrorIf c is empty
import { toeplitz } from 'deepbox/linalg'; toeplitz([1, 2, 3]); // [[1, 2, 3], [2, 1, 2], [3, 2, 1]]toeplitz([1, 2, 3], [1, 5, 6]); // [[1, 5, 6], [2, 1, 5], [3, 2, 1]]vandermonde
function vandermonde(x: number[], N?: number, increasing?: boolean): Tensor;Create a Vandermonde matrix.
V[i,j] = x[i]^(N-1-j) by default, or V[i,j] = x[i]^j when increasing is true (same convention as numpy.vander).
Parameters
xnumber[]- Input vector of length n
Noptionalnumber- Number of columns (default: n)
increasingoptionalboolean- If true, powers increase left to right
Returns
TensorVandermonde matrix of shape (n, N)
Throws
InvalidParameterErrorIf x is empty or N is not a positive integer
import { vandermonde } from 'deepbox/linalg'; vandermonde([1, 2, 3]); // [[1, 1, 1], [4, 2, 1], [9, 3, 1]]vandermonde([1, 2, 3], 3, true); // [[1, 1, 1], [1, 2, 4], [1, 3, 9]]cond
function cond(a: Tensor, p?: number | "fro" | "nuc"): number;Condition number of a matrix.
Measures how sensitive the solution of A*x=b is to changes in b. Large condition number indicates ill-conditioned matrix.
Formula: cond(A) = ||A|| * ||A^(-1)||
Parameters:
Parameters
aTensor- Input matrix of shape (M, N)
poptionalnumber | "fro" | "nuc"- Norm order, as in
numpy.linalg.cond: - undefined or 2: ratio of the largest to the smallest singular value (any M, N) - -2: ratio of the smallest to the largest singular value (any M, N) - 'fro' or 'nuc': Frobenius or nuclear norm of A times that of its (pseudo-)inverse, computed from the singular values (any M, N) - 1, -1, Infinity, -Infinity: matrix norm of A times that of A^(-1) (square A only) Returns: Condition number (>= 1 for p != -2, Infinity for singular matrices). Singular values at or belowmax(M, N) * eps * largest singular valuecount as zero, and an empty matrix gives Infinity.
Returns
number
Throws
ShapeErrorIf input is not a 2D matrix, or is not square for p in {1, -1, Infinity, -Infinity}DTypeErrorIf input has string or complex dtypeDataValidationErrorIf input contains non-finite values (NaN, Infinity)InvalidParameterErrorIf p is unsupported
import { cond } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; cond(tensor([[1, 2], [3, 4]])); // 14.93... (2-norm)cond(tensor([[1, 2], [3, 4]]), 1); // 21norm
function norm(x: Tensor, ord?: number | "fro" | "nuc", axis?: Axis | Axis[], keepdims?: boolean): Tensor | number;Parameters
xTensorordoptionalnumber | "fro" | "nuc"axisoptionalAxis | Axis[]keepdimsoptionalboolean
Returns
Tensor | number
det
function det(a: Tensor): number;Compute the determinant of a matrix.
Uses LU decomposition with partial pivoting for numerical stability. The determinant is computed as: det(A) = pivSign * product(diag(U))
Algorithm: LU decomposition Time Complexity: O(N³) for N×N matrix Space Complexity: O(N²) for LU factorization
Parameters:
Parameters
aTensor- Square matrix of shape (N, N) Returns: Determinant value (scalar) Properties: - det(A) = 0 if and only if A is singular (non-invertible) - det(AB) = det(A) * det(B) - det(A^T) = det(A) - det(cA) = c^N * det(A) for scalar c and N×N matrix A - det(I) = 1 for identity matrix
Returns
number
Throws
ShapeErrorIf input is not a 2D square matrixDTypeErrorIf input has string or complex dtypeDataValidationErrorIf input contains non-finite values (NaN, Infinity)
import { det } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[1, 2], [3, 4]]);console.log(det(A)); // -2matrixRank
function matrixRank(a: Tensor, tol?: number): number;Compute the rank of a matrix.
Number of linearly independent rows/columns. Uses SVD to count singular values above a threshold.
Algorithm: SVD-based rank computation Time Complexity: O(min(M,N) * M * N) for M×N matrix Space Complexity: O(M*N) for SVD computation
Parameters:
Parameters
aTensor- Input matrix of shape (M, N)
toloptionalnumber- Threshold for small singular values (optional) - Default: max(M,N) * largest_singular_value * machine_epsilon, where the epsilon follows the dtype of
aas in NumPy (about 1.2e-7 for float32, 2.2e-16 for float64 and integers) - Singular values > tol are counted as non-zero Returns: Rank (integer between 0 and min(M, N)) Properties: - 0 ≤ rank(A) ≤ min(M, N) - rank(A) = rank(A^T) - rank(A) = number of non-zero singular values - Full rank: rank(A) = min(M, N) - Rank deficient: rank(A) < min(M, N)
Returns
number
Throws
ShapeErrorIf input is not a 2D matrixDTypeErrorIf input has string or complex dtypeInvalidParameterErrorIf tol is negative or non-finiteDataValidationErrorIf input contains non-finite values (NaN, Infinity)
import { matrixRank } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[1, 2], [2, 4]]); // Rank 1 (linearly dependent rows)console.log(matrixRank(A)); // 1 const B = tensor([[1, 0], [0, 1]]); // Full rankconsole.log(matrixRank(B)); // 2slogdet
function slogdet(a: Tensor): [ Tensor, Tensor ];Compute sign and natural logarithm of the determinant.
More numerically stable than det() for large matrices or matrices with very large/small determinants that would overflow/underflow.
Algorithm: LU decomposition Time Complexity: O(N³) Space Complexity: O(N²)
Parameters:
Parameters
aTensor- Square matrix Returns: [sign, logdet], both 0-D float64 tensors - sign: +1, -1, or 0 for a singular matrix - logdet: Natural log of |det(A)|, -Infinity if singular (an empty matrix gives [1, 0]) Mathematical Relation: det(A) = sign * exp(logdet) Advantages over det(): - Avoids overflow for large determinants - Avoids underflow for small determinants - More numerically stable for ill-conditioned matrices
Returns
[ Tensor, Tensor ]
Throws
ShapeErrorIf input is not a 2D square matrixDTypeErrorIf input has string or complex dtypeDataValidationErrorIf input contains non-finite values (NaN, Infinity)
import { slogdet } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[1, 2], [3, 4]]);const [sign, logdet] = slogdet(A);// det(A) = sign * exp(logdet)trace
function trace(a: Tensor, offset?: number, axis1?: Axis, axis2?: Axis): Tensor;Compute the trace of a matrix.
Sum of diagonal elements. Supports offset diagonals.
Algorithm: Direct summation Time Complexity: O(min(M, N)) where M×N is matrix size Space Complexity: O(1)
Parameters:
Parameters
aTensor- Input matrix (at least 2D)
offsetoptionalnumber- Integer offset from main diagonal: - 0: main diagonal (default) - >0: upper diagonal (k-th diagonal above main) - <0: lower diagonal (k-th diagonal below main)
axis1optionalAxis- First axis to take the diagonal from (default: 0)
axis2optionalAxis- Second axis to take the diagonal from (default: 1)
Returns: Trace values as a float64 tensor: shape
[1]for a 2-D input, otherwise one value per remaining index (the input shape withoutaxis1andaxis2) Properties: - trace(A) = sum of eigenvalues (for square matrices) - trace(AB) = trace(BA) (cyclic property) - trace(A + B) = trace(A) + trace(B) (linearity) - trace(cA) = c * trace(A) for scalar c - trace(A^T) = trace(A) NaN and Infinity entries on the summed diagonal propagate into the result.
Returns
Tensor
Throws
ShapeErrorIf input is not at least 2DInvalidParameterErrorIf axis values are invalid/identical or offset is non-integerDTypeErrorIf input has string or complex dtype
import { trace } from 'deepbox/linalg';import { tensor } from 'deepbox/ndarray'; const A = tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);console.log(trace(A).toArray()); // [15] (1 + 5 + 9)console.log(trace(A, 1).toArray()); // [8] (2 + 6, upper diagonal)console.log(trace(A, -1).toArray()); // [12] (4 + 8, lower diagonal)These older spellings still work in 1.x. New code should use the names above.
block_diagPrefer blockDiag.matrix_powerPrefer matrixPower.
import { blockDiag, cond, det, expm, hadamard, inv, matrixPower, matrixRank, norm, pinv, trace,} from "deepbox/linalg";import { tensor } from "deepbox/ndarray"; const a = tensor([[2, 0], [0, 3]]);console.log(inv(a).toString(), det(a));console.log(norm(a), norm(a, "fro"), trace(a).toString());console.log(cond(a), matrixRank(a));console.log(matrixPower(a, 3).toString());console.log(expm(a).toString());console.log(pinv(tensor([[1, 2], [2, 4]])).toString()); // works for singular inputconsole.log(blockDiag(a, tensor([[1]])).toString());console.log(hadamard(4).toString());Found a mistake? Open an issue.