feat: Add Bunch–Kaufman LDL Decomposition#1591
feat: Add Bunch–Kaufman LDL Decomposition#1591awinick wants to merge 2 commits intodimforge:mainfrom
Conversation
|
This work was inspired in part by the implementation efforts in #1515 by @ajefweiss. For the problem class I was working on, I needed clear and reliable handling of indefinite Hermitian matrices, which motivated implementing the classical Bunch–Kaufman LDL factorization. While there is overlap in goals, this PR focuses specifically on the symmetric-indefinite Hermitian case with explicit pivot handling and block-diagonal |
|
I'll try to take a look soon ish. I've kicked off the checks and they show a few problems in the derive macros. Could you fix them if you have time? |
geo-ant
left a comment
There was a problem hiding this comment.
Hey, I'm sorry that I didn't have time to take a super in-depth look yet. I need to brush up on the Bunch-Kaufman factorization.
Did you use LAPACKS zhetrf function as your reference? If not, what other references should I use to review the implementation. I'm aware of:
- Bunch & Kaufmann 77, which present a couple of algorithms. Which one should I be looking at?
- Jones & Patrick 89, which present an algorithm in section 4 of their paper.
Let me know what would be a good reference for me to check your implementation. I see you've added tests which is already fantastic, I'd just like to be able to review your implementation as well.
I've made some comments in the code for some API related things. If you want to take care of them, could you also run cargo fmt on your code so it'll pass the lint?
| /// | ||
| /// This uses the LAPACK `ipiv` convention: | ||
| /// a positive entry denotes a 1x1 pivot block, while a repeated negative entry | ||
| /// denotes a 2x2 pivot block. The stored pivot indices are 1-based. |
There was a problem hiding this comment.
I like LAPACK as much as the next guy, but to my mind this isn't going to feel intuitive for Rust users.
- Is there a reason to expose this API at all?
- If so, would you mind abstracting it into a Pivot structure? This could expose an API for applying
P A P^T, either as a function or using lazily overloaded operators depending how overengineered you'd like it to be... 😅 - I'd much prefer 0-based indices, which is what
nalgebrauses everywhere else.
| /// | ||
| /// This follows the LAPACK convention and is therefore 1-based. | ||
| /// A value of `None` means no zero pivot was detected. | ||
| #[inline] |
There was a problem hiding this comment.
same comment as above, this could better be abstracted into a Pivot structure, if it's needed at all...
| /// | ||
| /// P A P^T = L * D * L^H | ||
| /// | ||
| /// where L is a product of permutation and unit lower triangular matrices, U^H is the |
There was a problem hiding this comment.
where L is a product of permutation and unit lower triangular matrices, U^H is the
I think this is copied from LAPACK's ?{he,sy}trf family of functions, but lapack uses the form A = L D L^H, whereas you give P A P^T = L D L^H. In your case L should indeed be unit lower triangular, right?
| self.zero_pivot | ||
| } | ||
|
|
||
| /// The permutation-aware factor of this decomposition. |
There was a problem hiding this comment.
Is this essentially P^T L? If so could you just state that in the comment?
This PR adds an LDL decomposition for Hermitian matrices using the Bunch–Kaufman symmetric pivoting algorithm.
The factorization computed is
where
This corresponds to the class of algorithms implemented in LAPACK’s *HETRF family.
Implementation notes
The factorization is stored in-place in a single matrix following the standard LAPACK layout:
Tests
The tests are intentionally compact but exercise a wide variety of pivot behaviors.
During development I inspected pivot structures and found that matrices constructed as
where U is Haar-random unitary and D has alternating signs consistently generate rich mixtures of 1×1 and 2×2 pivot blocks.
Two spectra are used.
Alternating ±1 spectrum
This produces a diverse set of pivot patterns across matrix sizes and verifies
Alternating geometric spectrum
This stresses the algorithm under extreme scaling, which is particularly important for
Empirically this spectrum produces a wide variety of pivot configurations while remaining numerically well-behaved.
Determinant accuracy
During testing it was observed that computing determinants through this LDL factorization produces significantly more accurate real-valued results for Hermitian matrices than the current generic approach.
Because the decomposition explicitly respects Hermitian structure, the determinant is obtained as the product of real block determinants, which reduces complex roundoff artifacts.
Benchmarking
Benchmarks were performed comparing this implementation against LAPACK (
zhetrf) and the existing QR decomposition in nalgebra.All results below correspond to 1000 factorizations.
The pure Rust implementation is typically within roughly a factor of two of hardware-accelerated LAPACK. Given that LAPACK implementations rely on highly tuned BLAS kernels and architecture-specific optimizations, this level of performance is reasonable for a portable pure-Rust implementation.
For comparison, QR factorization, the current best available method, is substantially slower. QR does not exploit Hermitian structure and therefore performs significantly more work than an LDL factorization.