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
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
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.
Uh oh!
There was an error while loading. Please reload this page.
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