@@ -76,25 +76,29 @@ Solve a 5×5 system via LU:
7676``` rust
7777use la_stack :: prelude :: * ;
7878
79- // This system requires pivoting (a[0][0] = 0), so it's a good LU demo.
80- // A = J - I: zeros on diagonal, ones elsewhere.
81- let a = Matrix :: <5 >:: from_rows ([
82- [0.0 , 1.0 , 1.0 , 1.0 , 1.0 ],
83- [1.0 , 0.0 , 1.0 , 1.0 , 1.0 ],
84- [1.0 , 1.0 , 0.0 , 1.0 , 1.0 ],
85- [1.0 , 1.0 , 1.0 , 0.0 , 1.0 ],
86- [1.0 , 1.0 , 1.0 , 1.0 , 0.0 ],
87- ]);
88-
89- let b = Vector :: <5 >:: new ([14.0 , 13.0 , 12.0 , 11.0 , 10.0 ]);
90-
91- let lu = a . lu (DEFAULT_PIVOT_TOL ). unwrap ();
92- let x = lu . solve_vec (b ). unwrap (). into_array ();
93-
94- // Floating-point rounding is expected; compare with a tolerance.
95- let expected = [1.0 , 2.0 , 3.0 , 4.0 , 5.0 ];
96- for (x_i , e_i ) in x . iter (). zip (expected . iter ()) {
97- assert! ((* x_i - * e_i ). abs () <= 1 e - 12 );
79+ fn main () -> Result <(), LaError > {
80+ // This system requires pivoting (a[0][0] = 0), so it's a good LU demo.
81+ // A = J - I: zeros on diagonal, ones elsewhere.
82+ let a = Matrix :: <5 >:: from_rows ([
83+ [0.0 , 1.0 , 1.0 , 1.0 , 1.0 ],
84+ [1.0 , 0.0 , 1.0 , 1.0 , 1.0 ],
85+ [1.0 , 1.0 , 0.0 , 1.0 , 1.0 ],
86+ [1.0 , 1.0 , 1.0 , 0.0 , 1.0 ],
87+ [1.0 , 1.0 , 1.0 , 1.0 , 0.0 ],
88+ ]);
89+
90+ let b = Vector :: <5 >:: new ([14.0 , 13.0 , 12.0 , 11.0 , 10.0 ]);
91+
92+ let lu = a . lu (DEFAULT_PIVOT_TOL )? ;
93+ let x = lu . solve_vec (b )? . into_array ();
94+
95+ // Floating-point rounding is expected; compare with a tolerance.
96+ let expected = [1.0 , 2.0 , 3.0 , 4.0 , 5.0 ];
97+ for (x_i , e_i ) in x . iter (). zip (expected . iter ()) {
98+ assert! ((* x_i - * e_i ). abs () <= 1 e - 12 );
99+ }
100+
101+ Ok (())
98102}
99103```
100104
@@ -106,17 +110,21 @@ For symmetric positive-definite matrices, `LDL^T` is essentially a square-root-f
106110``` rust
107111use la_stack :: prelude :: * ;
108112
109- // This matrix is symmetric positive-definite (A = L*L^T) so LDLT works without pivoting.
110- let a = Matrix :: <5 >:: from_rows ([
111- [1.0 , 1.0 , 0.0 , 0.0 , 0.0 ],
112- [1.0 , 2.0 , 1.0 , 0.0 , 0.0 ],
113- [0.0 , 1.0 , 2.0 , 1.0 , 0.0 ],
114- [0.0 , 0.0 , 1.0 , 2.0 , 1.0 ],
115- [0.0 , 0.0 , 0.0 , 1.0 , 2.0 ],
116- ]);
117-
118- let det = a . ldlt (DEFAULT_SINGULAR_TOL ). unwrap (). det ();
119- assert! ((det - 1.0 ). abs () <= 1 e - 12 );
113+ fn main () -> Result <(), LaError > {
114+ // This matrix is symmetric positive-definite (A = L*L^T) so LDLT works without pivoting.
115+ let a = Matrix :: <5 >:: from_rows ([
116+ [1.0 , 1.0 , 0.0 , 0.0 , 0.0 ],
117+ [1.0 , 2.0 , 1.0 , 0.0 , 0.0 ],
118+ [0.0 , 1.0 , 2.0 , 1.0 , 0.0 ],
119+ [0.0 , 0.0 , 1.0 , 2.0 , 1.0 ],
120+ [0.0 , 0.0 , 0.0 , 1.0 , 2.0 ],
121+ ]);
122+
123+ let det = a . ldlt (DEFAULT_SINGULAR_TOL )? . det ()? ;
124+ assert! ((det - 1.0 ). abs () <= 1 e - 12 );
125+
126+ Ok (())
127+ }
120128```
121129
122130> ⚠️ ** LDLT invariant:** The input matrix must be ** symmetric** . Asymmetric
@@ -133,23 +141,23 @@ assert!((det - 1.0).abs() <= 1e-12);
133141
134142` det_direct() ` is a ` const fn ` providing closed-form determinants for D=0–4,
135143using fused multiply-add where applicable. ` Matrix::<0>::zero().det_direct() `
136- returns ` Some(1.0) ` (the empty-product convention). For D=1–4, cofactor
144+ returns ` Ok( Some(1.0) )` (the empty-product convention). For D=1–4, cofactor
137145expansion bypasses LU factorization entirely. This enables compile-time
138146evaluation when inputs are known:
139147
140148``` rust
141149use la_stack :: prelude :: * ;
142150
143151// Evaluated entirely at compile time — no runtime cost.
144- const DET : Option <f64 > = {
152+ const DET : Result < Option <f64 >, LaError > = {
145153 let m = Matrix :: <3 >:: from_rows ([
146154 [2.0 , 0.0 , 0.0 ],
147155 [0.0 , 3.0 , 0.0 ],
148156 [0.0 , 0.0 , 5.0 ],
149157 ]);
150158 m . det_direct ()
151159};
152- assert_eq! (DET , Some (30.0 ));
160+ assert_eq! (DET , Ok ( Some (30.0 ) ));
153161```
154162
155163The public ` det() ` method automatically dispatches through the closed-form path
@@ -181,23 +189,27 @@ la-stack = { version = "0.4.1", features = ["exact"] }
181189``` rust,ignore
182190use la_stack::prelude::*;
183191
184- // Exact determinant
185- let m = Matrix::<3>::from_rows([
186- [1.0, 2.0, 3.0],
187- [4.0, 5.0, 6.0],
188- [7.0, 8.0, 9.0],
189- ]);
190- assert_eq!(m.det_sign_exact().unwrap(), 0); // exactly singular
191-
192- let det = m.det_exact().unwrap();
193- assert_eq!(det, BigRational::from_integer(0.into())); // exact zero
194-
195- // Exact linear system solve
196- let a = Matrix::<2>::from_rows([[1.0, 2.0], [3.0, 4.0]]);
197- let b = Vector::<2>::new([5.0, 11.0]);
198- let x = a.solve_exact_f64(b).unwrap().into_array();
199- assert!((x[0] - 1.0).abs() <= f64::EPSILON);
200- assert!((x[1] - 2.0).abs() <= f64::EPSILON);
192+ fn main() -> Result<(), LaError> {
193+ // Exact determinant
194+ let m = Matrix::<3>::from_rows([
195+ [1.0, 2.0, 3.0],
196+ [4.0, 5.0, 6.0],
197+ [7.0, 8.0, 9.0],
198+ ]);
199+ assert_eq!(m.det_sign_exact()?, 0); // exactly singular
200+
201+ let det = m.det_exact()?;
202+ assert_eq!(det, BigRational::from_integer(0.into())); // exact zero
203+
204+ // Exact linear system solve
205+ let a = Matrix::<2>::from_rows([[1.0, 2.0], [3.0, 4.0]]);
206+ let b = Vector::<2>::new([5.0, 11.0]);
207+ let x = a.solve_exact_f64(b)?.into_array();
208+ assert!((x[0] - 1.0).abs() <= f64::EPSILON);
209+ assert!((x[1] - 2.0).abs() <= f64::EPSILON);
210+
211+ Ok(())
212+ }
201213```
202214
203215With the ` exact ` feature enabled, ` BigInt ` and ` BigRational ` are re-exported
@@ -222,19 +234,24 @@ adaptive-precision logic for geometric predicates:
222234``` rust,ignore
223235use la_stack::prelude::*;
224236
225- let m = Matrix::<3>::identity();
226- if let Some(bound) = m.det_errbound() {
227- let det = m.det_direct().unwrap();
228- if det.abs() > bound {
229- // f64 sign is guaranteed correct
230- let sign = det.signum() as i8;
237+ fn main() -> Result<(), LaError> {
238+ let m = Matrix::<3>::identity();
239+ if let Some(bound) = m.det_errbound()? {
240+ if let Some(det) = m.det_direct()? {
241+ if det.abs() > bound {
242+ // f64 sign is guaranteed correct
243+ let sign = det.signum() as i8;
244+ } else {
245+ // Fall back to exact arithmetic (requires `exact` feature)
246+ let sign = m.det_sign_exact()?;
247+ }
248+ }
231249 } else {
232- // Fall back to exact arithmetic (requires `exact` feature)
233- let sign = m.det_sign_exact().unwrap() ;
250+ // D ≥ 5: no fast filter, use exact directly (requires `exact` feature)
251+ let sign = m.det_sign_exact()? ;
234252 }
235- } else {
236- // D ≥ 5: no fast filter, use exact directly (requires `exact` feature)
237- let sign = m.det_sign_exact().unwrap();
253+
254+ Ok(())
238255}
239256```
240257
0 commit comments