Skip to content

docs: strengthen LDLT symmetry precondition and add is_symmetric API (#84)#93

Merged
acgetchell merged 3 commits intomainfrom
docs/84-ldlt-symmetry-precondition
Apr 20, 2026
Merged

docs: strengthen LDLT symmetry precondition and add is_symmetric API (#84)#93
acgetchell merged 3 commits intomainfrom
docs/84-ldlt-symmetry-precondition

Conversation

@acgetchell
Copy link
Copy Markdown
Owner

@acgetchell acgetchell commented Apr 20, 2026

Closes #84.

Summary by CodeRabbit

  • New Features

    • Added two new methods to validate matrix symmetry: check if a matrix is symmetric or retrieve the first asymmetric element location.
  • Documentation

    • Enhanced guidance for the LDLT decomposition API with explicit symmetry preconditions and recommendations for pre-validation.
  • Tests

    • Expanded test coverage for error handling in matrix operations and symmetry validation scenarios.

LDLT factorization assumes the input matrix is symmetric, but the contract
was only implied by one sentence in the module and struct docs. Asymmetric
inputs silently produce mathematically meaningless factorizations in
release builds (the `debug_assert_symmetric` check is compiled out), and
callers had no supported way to validate symmetry up front.

Documentation:
- `Matrix::ldlt`: add a prominent `# Preconditions` section spelling out
  the contract, the debug-vs-release split, and pointers to the new
  `is_symmetric` / `first_asymmetry` predicates and to `lu()` as the
  fallback for inputs that may not be symmetric at all.
- `src/ldlt.rs`: promote the precondition into a module-level and
  struct-level `# Preconditions` section with back-links.
- `README.md`: add a warning call-out under the LDLT example linking the
  new predicates.

New public API (`Matrix<D>`):
- `is_symmetric(&self, rel_tol: f64) -> bool` — infallible predicate
  sharing the `|A[r][c] - A[c][r]| <= rel_tol * max(1, inf_norm)`
  convention used internally by LDLT.
- `first_asymmetry(&self, rel_tol: f64) -> Option<(usize, usize)>` —
  returns the lexicographically first off-diagonal pair that violates
  symmetry, or `None` for symmetric matrices. Used by the debug-build
  check for pinpointed panic messages.
- Both `debug_assert!(rel_tol >= 0.0)`, matching `lu(tol)` / `ldlt(tol)`.
- NaN off-diagonals are explicitly reported as asymmetric.

Refactor:
- `debug_assert_symmetric` now delegates to `first_asymmetry`, so the
  runtime check and the documented contract share one implementation
  and cannot drift apart.

Tests:
- Dimension-generic (D=2..=5): identity, zero, `A = M + Mᵀ`,
  perturbed off-diagonal asymmetric, NaN off-diagonal asymmetric.
- Scalar: tolerance scaling with inf_norm, lexicographic-first pair on
  D=3, debug-only panic on negative `rel_tol`.
- `#[cfg(debug_assertions)] #[should_panic]` test for the LDLT
  debug-build panic still passes against the refactored assertion.

No functional change in release builds of existing APIs.

Tests: `just ci` — 123 lib, 26 doc, 284 exact-feature, 101 Python,
all examples, all linters/validators.

Closes #84

Co-Authored-By: Oz <oz-agent@warp.dev>
@acgetchell acgetchell added this to the v0.4.1 milestone Apr 20, 2026
@acgetchell acgetchell added documentation Improvements or additions to documentation enhancement New feature or request rust Pull requests that update rust code labels Apr 20, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Warning

Rate limit exceeded

@acgetchell has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 17 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 44 minutes and 17 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16b437a1-d1e8-43ef-9fd9-859606525ab0

📥 Commits

Reviewing files that changed from the base of the PR and between 87d426f and 1805779.

📒 Files selected for processing (1)
  • src/matrix.rs
📝 Walkthrough

Walkthrough

The PR addresses issue #84 by strengthening LDLT symmetry precondition documentation and validation. It introduces two new public matrix methods (is_symmetric and first_asymmetry) for detecting matrix asymmetry, refactors internal symmetry checking to use these methods with improved error reporting, and expands documentation across README, LDLT, and Matrix modules to explicitly warn about symmetry requirements and undefined behavior in release builds.

Changes

Cohort / File(s) Summary
Documentation Updates
README.md
Added warning block documenting the symmetry precondition for Matrix::ldlt, noting symmetry is only checked via debug_assert! in debug builds and instructing users to pre-validate with is_symmetric/first_asymmetry or fall back to lu().
Matrix Public API
src/matrix.rs
Added two new public methods: is_symmetric(rel_tol: f64) -> bool and first_asymmetry(rel_tol: f64) -> Option<(usize, usize)> for symmetry validation; updated ldlt() documentation to explicitly state symmetry as a correctness precondition; included comprehensive test suite covering dimensions 2–5, tolerance scaling, NaN handling, and lexicographic ordering.
LDLT Refactoring
src/ldlt.rs
Expanded module and struct-level documentation with explicit symmetry preconditions; refactored debug_assert_symmetric to use first_asymmetry() and report offending index pairs; added debug-gated test for asymmetric input panic and defensive tests for corrupted factors (NaN/sub-tolerance diagonals) returning appropriate LaError variants.
LU Defensive Tests
src/lu.rs
Added new unit tests that directly construct Lu<D> with zero U-diagonal entries and verify solve_vec returns LaError::Singular { pivot_col } via defensive back-substitution guards; generated for dimensions 2–5 using a macro.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #11: Directly modifies LDLT/matrix symmetry checking code, with main PR refactoring debug assertions to call newly added Matrix::first_asymmetry/is_symmetric.
  • PR #38: Modifies src/ldlt.rs and tests around error reporting and defensive error returns (LaError::NonFinite), may have overlapping test or error-handling expectations.

Suggested labels

testing, api

Poem

🐰 Symmetry springs forth with care,
Matrix checks laid plain and fair,
Debug asserts won't let you slide,
Release builds—validate with pride!
Warning flags in docs so bright,

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: strengthening LDLT symmetry documentation and adding public symmetry-checking APIs.
Linked Issues check ✅ Passed The PR fully addresses issue #84 by strengthening LDLT symmetry documentation, adding is_symmetric/first_asymmetry APIs, refactoring debug_assert_symmetric, and documenting debug-vs-release behavior.
Out of Scope Changes check ✅ Passed All changes are scoped to issue #84: documentation updates, new symmetry-checking APIs, test coverage for symmetry validation and defensive paths, with no unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/84-ldlt-symmetry-precondition

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

❌ Patch coverage is 76.19048% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.96%. Comparing base (b60a734) to head (1805779).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/ldlt.rs 57.14% 3 Missing ⚠️
src/matrix.rs 85.71% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #93      +/-   ##
==========================================
+ Coverage   88.65%   90.96%   +2.30%     
==========================================
  Files           5        5              
  Lines         485      498      +13     
==========================================
+ Hits          430      453      +23     
+ Misses         55       45      -10     
Flag Coverage Δ
unittests 90.96% <76.19%> (+2.30%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

Add unit tests to exercise internal safety nets in the LU and LDLT
diagonal solve routines. These tests manually construct factors with
invalid diagonals (NaN or sub-tolerance) to verify that solve_vec
correctly surfaces NonFinite and Singular errors, even though these
states are unreachable via the standard factorization APIs.

Refs: #84
@acgetchell
Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/ldlt.rs (1)

220-227: Avoid constant-false debug_assert! to prevent linter warnings.

The code currently uses debug_assert!(false, ...) which strict Clippy configurations flag as a constant assertion. Extract the asymmetry check into a variable and use its negation to make the assertion condition non-constant while preserving the same debug-only panic behavior:

♻️ Proposed refactor
+    let asymmetry = a.first_asymmetry(1e-12);
+    if let Some((r, c)) = asymmetry {
-    if let Some((r, c)) = a.first_asymmetry(1e-12) {
         let diff = (a.rows[r][c] - a.rows[c][r]).abs();
         let eps = 1e-12 * a.inf_norm().max(1.0);
         debug_assert!(
-            false,
+            asymmetry.is_none(),
             "matrix must be symmetric (diff={diff}, eps={eps}) at ({r}, {c}); \
              pre-validate with Matrix::is_symmetric before calling ldlt"
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ldlt.rs` around lines 220 - 227, The debug_assert! currently uses a
constant false which triggers linter warnings; change it to assert the
non-constant asymmetry condition instead by computing a boolean like let
asymmetric = diff > eps (using the existing diff and eps calculations from the
first_asymmetry branch) and then call debug_assert!(asymmetric, "matrix must be
symmetric (diff={diff}, eps={eps}) at ({r}, {c}); pre-validate with
Matrix::is_symmetric before calling ldlt") or, better, use
debug_assert!(!asymmetric, ...) depending on the intended panic on symmetry
violation—update the assertion in the block that handles a.first_asymmetry so
the condition references that computed boolean (and preserve the same message
and use of r, c, diff, eps).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/matrix.rs`:
- Around line 227-236: The asymmetry check in first_asymmetry uses eps = rel_tol
* self.inf_norm().max(1.0), which becomes +∞ when inf_norm() is infinite and
then masks infinite diffs; update first_asymmetry to handle infinite norms by
either (a) capping/using a finite fallback for eps when eps.is_infinite() (e.g.
use rel_tol or rel_tol * 1.0) or (b) explicitly treat diff.is_infinite() as an
asymmetry regardless of eps; also add the suggested regression test that
constructs Matrix::<2> with an ∞ off-diagonal and asserts first_asymmetry(1e-12)
returns Some((0,1)) and is_symmetric(1e-12) is false to prevent regressions.

---

Nitpick comments:
In `@src/ldlt.rs`:
- Around line 220-227: The debug_assert! currently uses a constant false which
triggers linter warnings; change it to assert the non-constant asymmetry
condition instead by computing a boolean like let asymmetric = diff > eps (using
the existing diff and eps calculations from the first_asymmetry branch) and then
call debug_assert!(asymmetric, "matrix must be symmetric (diff={diff},
eps={eps}) at ({r}, {c}); pre-validate with Matrix::is_symmetric before calling
ldlt") or, better, use debug_assert!(!asymmetric, ...) depending on the intended
panic on symmetry violation—update the assertion in the block that handles
a.first_asymmetry so the condition references that computed boolean (and
preserve the same message and use of r, c, diff, eps).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b247411b-c6c2-4ee7-99f7-8e3749983ed8

📥 Commits

Reviewing files that changed from the base of the PR and between b60a734 and 87d426f.

📒 Files selected for processing (4)
  • README.md
  • src/ldlt.rs
  • src/lu.rs
  • src/matrix.rs

Comment thread src/matrix.rs
Update Matrix::first_asymmetry to flag any non-finite difference between
off-diagonal pairs as an asymmetry. This prevents cases where a single
infinite entry paired with a finite entry would incorrectly pass as
symmetric because the matrix's infinite norm blew the tolerance up to
infinity, making the comparison `diff > eps` return false.

Refs: #84
@acgetchell acgetchell merged commit aba1dc7 into main Apr 20, 2026
9 checks passed
@acgetchell acgetchell deleted the docs/84-ldlt-symmetry-precondition branch April 20, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs: strengthen LDLT symmetry precondition documentation

1 participant