feat!(api): enforce finite Matrix and Vector construction#133
Conversation
Make finite storage a public API invariant instead of a validation step repeated by every operation. - Replace raw Matrix::from_rows and Vector::new usage with fallible try_from_rows and try_new constructors. - Change Matrix::set to return Result<(), LaError> and reject non-finite mutations before changing storage. - Carry finite Matrix/Vector and LU/LDLT factor invariants through internal proof types so operations can skip redundant validation and panic-only defensive branches. - Add Semgrep guardrails for public panic paths and raw infallible f64 constructors. - Add fixed-seed percentile exact-arithmetic benchmarks for p50/p95/p99 inputs. BREAKING CHANGE: Matrix::from_rows and Vector::new are no longer public constructors; use Matrix::try_from_rows and Vector::try_new. Matrix::set now returns Result<(), LaError> instead of Option<()>. Closes #98
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughMoves finiteness validation to fallible Matrix/Vector constructors, refactors exact/LU/LDLT internals to assume finite proofs, adds deterministic random-percentile exact benchmarks, introduces Semgrep guardrails, and updates examples, scripts, and tests to the new contracts. ChangesFinite-Entry Validation Boundary and Exact Arithmetic Refactor
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #133 +/- ##
==========================================
- Coverage 99.64% 99.31% -0.34%
==========================================
Files 5 5
Lines 2832 2757 -75
==========================================
- Hits 2822 2738 -84
- Misses 10 19 +9
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/matrix.rs (1)
346-352:⚠️ Potential issue | 🟠 Major | ⚡ Quick winKeep
TryFrom<Matrix<D>>actually validating finiteness.This conversion now accepts any
Matrix<D>, including values built throughfrom_rows_unchecked, so internal callers can manufacture aFiniteMatrix<D>proof around NaN/∞ storage and bypass the invariant every downstream algorithm now relies on.Based on learnings: Applies to src/{lib,matrix,lu,ldlt,vector,exact}.rs : Non-finite values (NaN, ±∞) always surface as `LaError::NonFinite { row, col }` with source-location metadata.Suggested fix
impl<const D: usize> TryFrom<Matrix<D>> for FiniteMatrix<D> { type Error = LaError; #[inline] fn try_from(value: Matrix<D>) -> Result<Self, Self::Error> { - Ok(Self::new(value)) + Self::from_rows(value.rows) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/matrix.rs` around lines 346 - 352, The TryFrom implementation for TryFrom<Matrix<D>> for FiniteMatrix<D> currently accepts any Matrix<D> and must instead validate every element for finiteness in try_from; update the implementation of TryFrom::try_from (for FiniteMatrix<D>) to iterate the matrix entries, return Err(LaError::NonFinite { row, col }) when a NaN or ±∞ is found (including source-location metadata as required), and only call FiniteMatrix::new and return Ok(...) if all entries are finite. Ensure the error uses the LaError::NonFinite variant and supplies the offending element coordinates so callers cannot bypass the invariant via from_rows_unchecked.src/vector.rs (1)
94-100:⚠️ Potential issue | 🟠 Major | ⚡ Quick winKeep
TryFrom<Vector<D>>actually validating finiteness.This conversion is now infallible even though crate-internal code can still build unchecked
Vector<D>values, so a NaN/∞ RHS can be wrapped asFiniteVector<D>and slip past the invariant these algorithms now assume.Based on learnings: Applies to src/{lib,matrix,lu,ldlt,vector,exact}.rs : Non-finite values (NaN, ±∞) always surface as `LaError::NonFinite { row, col }` with source-location metadata.Suggested fix
impl<const D: usize> TryFrom<Vector<D>> for FiniteVector<D> { type Error = LaError; #[inline] fn try_from(value: Vector<D>) -> Result<Self, Self::Error> { - Ok(Self::new(value)) + Self::from_array(value.data) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/vector.rs` around lines 94 - 100, The TryFrom<Vector<D>> implementation for FiniteVector<D> must validate every component and reject NaN/∞ instead of being infallible: in the impl for TryFrom<Vector<D>> for FiniteVector<D> iterate the input Vector<D> components, check each value.is_finite(), and if any component is non-finite return Err(LaError::NonFinite { row: 0, col: i }) (populate the correct row/col and include source-location metadata as other modules do); only construct and return FiniteVector (e.g., via FiniteVector::new or a checked constructor) when all components pass the is_finite check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@benches/exact.rs`:
- Around line 349-388: The current approach (percentile_input_indices ->
bench_random_percentile_operation) picks one input per percentile using a single
timing and then repeatedly benchmarks that single input, which reports
representative-input medians rather than corpus percentile timings; fix it by
computing per-input timings across the whole corpus (use time_random_operation
in a loop with enough samples per input or run multiple measurement passes),
compute the actual percentile thresholds from that timing distribution, then
change percentile_input_indices to return a set/Vec of indices per percentile
(or a predicate) instead of one index and update
bench_random_percentile_operation to benchmark across that whole subset (e.g.,
bench using an input-generator closure that selects inputs from the percentile
set or iterate bench_function over all indices in the set and aggregate),
referencing percentile_input_indices, bench_random_percentile_operation,
time_random_operation, run_random_operation and RANDOM_PERCENTILES so the code
selects and measures many inputs per percentile rather than a single
representative.
In `@tests/semgrep/src/project_rules/raw_f64_constructors.rs`:
- Around line 12-14: The public enum LaError should be marked #[non_exhaustive]
to prevent Semgrep from flagging public error enums; add the #[non_exhaustive]
attribute above the LaError declaration (and ensure any public wrapper types in
this test follow guidelines by being #[must_use] where applicable) so the enum
declaration for LaError is non-exhaustive and the test only triggers the
intended constructor rules.
---
Outside diff comments:
In `@src/matrix.rs`:
- Around line 346-352: The TryFrom implementation for TryFrom<Matrix<D>> for
FiniteMatrix<D> currently accepts any Matrix<D> and must instead validate every
element for finiteness in try_from; update the implementation of
TryFrom::try_from (for FiniteMatrix<D>) to iterate the matrix entries, return
Err(LaError::NonFinite { row, col }) when a NaN or ±∞ is found (including
source-location metadata as required), and only call FiniteMatrix::new and
return Ok(...) if all entries are finite. Ensure the error uses the
LaError::NonFinite variant and supplies the offending element coordinates so
callers cannot bypass the invariant via from_rows_unchecked.
In `@src/vector.rs`:
- Around line 94-100: The TryFrom<Vector<D>> implementation for FiniteVector<D>
must validate every component and reject NaN/∞ instead of being infallible: in
the impl for TryFrom<Vector<D>> for FiniteVector<D> iterate the input Vector<D>
components, check each value.is_finite(), and if any component is non-finite
return Err(LaError::NonFinite { row: 0, col: i }) (populate the correct row/col
and include source-location metadata as other modules do); only construct and
return FiniteVector (e.g., via FiniteVector::new or a checked constructor) when
all components pass the is_finite check.
🪄 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: 862241e8-2a61-41aa-9284-9fa28daae0a8
📒 Files selected for processing (27)
AGENTS.mdREADME.mdbenches/exact.rsbenches/vs_linalg.rsdocs/BENCHMARKING.mdexamples/const_det_4x4.rsexamples/det_5x5.rsexamples/exact_det_3x3.rsexamples/exact_sign_3x3.rsexamples/exact_solve_3x3.rsexamples/ldlt_solve_3x3.rsexamples/solve_5x5.rsscripts/bench_compare.pyscripts/tests/test_bench_compare.pysemgrep.yamlsrc/exact.rssrc/ldlt.rssrc/lib.rssrc/lu.rssrc/matrix.rssrc/vector.rstests/proptest_exact.rstests/proptest_factorizations.rstests/proptest_matrix.rstests/proptest_vector.rstests/semgrep/src/project_rules/public_api_panic_paths.rstests/semgrep/src/project_rules/raw_f64_constructors.rs
Ensure internal finite proof conversions cannot accept raw Matrix or Vector storage without checking the invariant. - Revalidate TryFrom<Matrix<D>> and TryFrom<Vector<D>> before constructing finite wrappers. - Measure exact random percentile benchmarks over repeated corpus timings and cumulative input sets. - Tighten Codecov status thresholds and extend benchmark workflow timeout. - Keep Semgrep constructor fixtures aligned with public API guardrails.
Make finite storage a public API invariant instead of a validation step repeated by every operation.
BREAKING CHANGE: Matrix::from_rows and Vector::new are no longer public constructors; use Matrix::try_from_rows and Vector::try_new. Matrix::set now returns Result<(), LaError> instead of Option<()>.
Closes #98
Summary by CodeRabbit
New Features
Documentation
Refactor