Solve a symmetric linear system using an LDL factorization.
Given produced by ldl_factor,
solves
sytrf's is a product of
elimination steps, each preceded by its own row interchange. Moving
every interchange to the left gives
with unit-lower-triangular — with each
column carrying the interchanges of the steps after it, as LAPACK's
syconv arranges it — so the solve is a permutation, two triangular
solves around the / blocks of
(solved in closed form, as sytrs does), and the inverse
permutation.
Parameters
LDTensorldl_factor, shape (*, n, n).
Only its lower triangle is read.pivotsTensorldl_factor, shape (*, n)
(1-based, LAPACK convention). Both and
Bunch-Kaufman blocks are supported.(*, n, k) (or (n,)). Its batch
dimensions broadcast with those of LD.Returns
TensorSolution , shaped like B broadcast against LD's
batch.
Raises
ValueErrorLD is not square, pivots does not match it, or a pivot
is not one sytrf could have written.TypeErrorpivots is not an integer tensor.Notes
Supports indefinite symmetric (unlike Cholesky), so it is appropriate for KKT / saddle-point systems where Cholesky would fail. Cost per solve is once the LDL factor is in hand: two triangular solves, and for . The pivots are replayed on the host in per matrix; only when an interchange reaches earlier columns of is an gather index built for ( host work).
The interchanges are applied in the order sytrf performed them
(LAPACK sytrs). Applying them as a single up-front row
permutation of — as this function once did — is right only
when no later step swaps rows an earlier column of has
entries in, and silently wrong otherwise. The pivots are read on the
host with tolist; the arithmetic stays on LD's device and is
differentiable in LD and B.
Examples
>>> import lucid
>>> from lucid.linalg import ldl_factor, ldl_solve
>>> A = lucid.tensor([[4.0, 1.0], [1.0, 3.0]])
>>> LD, piv = ldl_factor(A)
>>> b = lucid.tensor([[5.0], [4.0]])
>>> ldl_solve(LD, piv, b)
tensor([[1.], [1.]])