Skip to content

Fix lower Cholesky of banded matrices with upper bandwidth > 0 - #512

Open
gragusa wants to merge 8 commits into
JuliaLinearAlgebra:masterfrom
gragusa:fix-lower-banded-cholesky
Open

gragusa wants to merge 8 commits into
JuliaLinearAlgebra:masterfrom
gragusa:fix-lower-banded-cholesky

Conversation

@gragusa

@gragusa gragusa commented Oct 15, 2026

Copy link
Copy Markdown

cholesky(Symmetric(A, :L)) (and Hermitian(A, :L)) throws a PosDefException for a positive definite BandedMatrix whose upper bandwidth is positive. Only bandwidths (l, 0) work:

using BandedMatrices, LinearAlgebra, Random
Random.seed!(1)
B = brand(8, 8, 2, 2); B = B + B' + 10I    # symmetric positive definite, bandwidths (2, 2)
cholesky(Symmetric(B, :U))                  # works
cholesky(Symmetric(B, :L))                  # throws PosDefException

Cause

The LowerTriangular method of banded_chol! passes bandeddata(A) to pbtrf!('L', ...). LAPACK's lower band format expects the diagonal in row 1, but a BandedMatrix with upper bandwidth u stores it in row u + 1. So LAPACK reads the top superdiagonal as the diagonal. The 'L' branch of _ldiv! passes the same storage to pbtrs!.

Fix

Both calls now pass a view of the band storage from the diagonal row down (_lower_bandeddata). The leading dimension is unchanged.

Tests

A new test factors Symmetric/Hermitian(A, :L) with bandwidths (2, 1), (2, 2) and (2, 3) for Float64, ComplexF64 and BigFloat, and checks the factor and a solve against dense results. Without this fix, the test errors; with it, it passes, and the full test suite does too. (Note: The existing :L tests use bandwidths (1, 0) where the two layouts agree, and tests did not catch the bug.

(The test compares Matrix(Ac.L) instead of Ac.L because of an unrelated issue: for BigFloat, Ac.L ≈ ... throws an UndefRefError, since the copy cholesky makes leaves the upper band storage undefined.)

Fixes #141.

cholesky(Symmetric(A, :L)) and solves with its factor passed
bandeddata(A) to LAPACK's pbtrf!/pbtrs! with uplo 'L'. LAPACK's lower
band storage starts at the diagonal, which a BandedMatrix with upper
bandwidth u stores in row u + 1, so for u > 0 LAPACK read a
superdiagonal as the diagonal. Both calls now pass the rows from the
diagonal down.

Fixes JuliaLinearAlgebra#141
@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.84%. Comparing base (70c629c) to head (e7e2645).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #512      +/-   ##
==========================================
+ Coverage   90.69%   90.84%   +0.15%     
==========================================
  Files          27       27              
  Lines        3784     3814      +30     
==========================================
+ Hits         3432     3465      +33     
+ Misses        352      349       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

cholcopy copied a Symmetric or Hermitian banded matrix into a `similar`
one, which leaves the parent's band storage outside the stored triangle
uninitialized. For BigFloat those entries are #undef, and on Julia 1.10,
where Matrix(::LowerTriangular) copies the whole parent,
Matrix(cholesky(Symmetric(A, :L)).L) threw an UndefRefError when A also
stored upper bands (likewise for :U with lower bands). cholcopy now
zeroes the parent before copying.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/symbanded/BandedCholesky.jl Resolved
gragusa and others added 4 commits October 15, 2026 22:46
Restore the full-parent fill for non-isbits types while skipping it for isbits types. Add regression coverage for both wrappers, triangle orientations, asymmetric bandwidths, and views.

Full package tests pass on Julia 1.10.12 and 1.13.0.
Co-authored-by: Sheehan Olver <solver@mac.com>
Removed unnecessary initialization and copying steps in the _banded_cholcopy function.
Initialize parent storage for non-isbits types in _banded_cholcopy function.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issues with (lower) banded data?

2 participants