Skip to content

Commit eab56a6

Browse files
authored
Merge pull request #132 from acgetchell/feat/125-unwrap-expect-guardrails
feat(validation): guard public Rust examples against unwrap
2 parents 23bc7a4 + ac44c07 commit eab56a6

11 files changed

Lines changed: 275 additions & 72 deletions

File tree

benches/exact.rs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,17 @@
1919
use criterion::{BenchmarkGroup, Criterion, measurement::WallTime};
2020
use la_stack::{Matrix, Vector};
2121
use pastey::paste;
22+
use std::fmt::Display;
2223
use std::hint::black_box;
2324

25+
/// Return a successful benchmark operation result or panic with the named operation.
26+
fn require_ok<T, E: Display>(result: Result<T, E>, operation: &str) -> T {
27+
match result {
28+
Ok(value) => value,
29+
Err(err) => panic!("{operation} failed: {err}"),
30+
}
31+
}
32+
2433
#[inline]
2534
#[allow(clippy::cast_precision_loss)]
2635
const fn matrix_entry<const D: usize>(r: usize, c: usize) -> f64 {
@@ -134,34 +143,34 @@ fn bench_extreme_group<const D: usize>(
134143
) {
135144
group.bench_function("det_sign_exact", |bencher| {
136145
bencher.iter(|| {
137-
let sign = black_box(m)
138-
.det_sign_exact()
139-
.expect("finite matrix entries");
146+
let sign = require_ok(black_box(m).det_sign_exact(), "exact determinant sign");
140147
black_box(sign);
141148
});
142149
});
143150

144151
group.bench_function("det_exact", |bencher| {
145152
bencher.iter(|| {
146-
let det = black_box(m).det_exact().expect("finite matrix entries");
153+
let det = require_ok(black_box(m).det_exact(), "exact determinant");
147154
black_box(det);
148155
});
149156
});
150157

151158
group.bench_function("solve_exact", |bencher| {
152159
bencher.iter(|| {
153-
let x = black_box(m)
154-
.solve_exact(black_box(rhs))
155-
.expect("non-singular matrix with finite entries");
160+
let x = require_ok(
161+
black_box(m).solve_exact(black_box(rhs)),
162+
"exact linear solve",
163+
);
156164
let _ = black_box(x);
157165
});
158166
});
159167

160168
group.bench_function("solve_exact_f64", |bencher| {
161169
bencher.iter(|| {
162-
let x = black_box(m)
163-
.solve_exact_f64(black_box(rhs))
164-
.expect("solution representable in f64");
170+
let x = require_ok(
171+
black_box(m).solve_exact_f64(black_box(rhs)),
172+
"exact linear solve converted to f64",
173+
);
165174
let _ = black_box(x);
166175
});
167176
});
@@ -178,9 +187,7 @@ macro_rules! gen_exact_benches_for_dim {
178187
// === f64 baselines ===
179188
[<group_d $d>].bench_function("det", |bencher| {
180189
bencher.iter(|| {
181-
let det = black_box(a)
182-
.det()
183-
.expect("diagonally dominant matrix is non-singular");
190+
let det = require_ok(black_box(a).det(), "f64 determinant");
184191
black_box(det);
185192
});
186193
});
@@ -195,47 +202,48 @@ macro_rules! gen_exact_benches_for_dim {
195202
// === det_exact (BigRational result) ===
196203
[<group_d $d>].bench_function("det_exact", |bencher| {
197204
bencher.iter(|| {
198-
let det = black_box(a).det_exact().expect("finite matrix entries");
205+
let det = require_ok(black_box(a).det_exact(), "exact determinant");
199206
black_box(det);
200207
});
201208
});
202209

203210
// === det_exact_f64 (exact → f64) ===
204211
[<group_d $d>].bench_function("det_exact_f64", |bencher| {
205212
bencher.iter(|| {
206-
let det = black_box(a)
207-
.det_exact_f64()
208-
.expect("det representable in f64");
213+
let det = require_ok(
214+
black_box(a).det_exact_f64(),
215+
"exact determinant converted to f64",
216+
);
209217
black_box(det);
210218
});
211219
});
212220

213221
// === det_sign_exact (adaptive: fast filter + exact fallback) ===
214222
[<group_d $d>].bench_function("det_sign_exact", |bencher| {
215223
bencher.iter(|| {
216-
let sign = black_box(a)
217-
.det_sign_exact()
218-
.expect("finite matrix entries");
224+
let sign = require_ok(black_box(a).det_sign_exact(), "exact determinant sign");
219225
black_box(sign);
220226
});
221227
});
222228

223229
// === solve_exact (BigRational result) ===
224230
[<group_d $d>].bench_function("solve_exact", |bencher| {
225231
bencher.iter(|| {
226-
let x = black_box(a)
227-
.solve_exact(black_box(rhs))
228-
.expect("diagonally dominant matrix is non-singular");
232+
let x = require_ok(
233+
black_box(a).solve_exact(black_box(rhs)),
234+
"exact linear solve",
235+
);
229236
black_box(x);
230237
});
231238
});
232239

233240
// === solve_exact_f64 (exact → f64) ===
234241
[<group_d $d>].bench_function("solve_exact_f64", |bencher| {
235242
bencher.iter(|| {
236-
let x = black_box(a)
237-
.solve_exact_f64(black_box(rhs))
238-
.expect("solution representable in f64");
243+
let x = require_ok(
244+
black_box(a).solve_exact_f64(black_box(rhs)),
245+
"exact linear solve converted to f64",
246+
);
239247
black_box(x);
240248
});
241249
});

benches/vs_linalg.rs

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,22 @@ use faer::linalg::solvers::{PartialPivLu, Solve};
1212
use faer::perm::PermRef;
1313
use la_stack::{DEFAULT_PIVOT_TOL, Matrix, Vector};
1414
use pastey::paste;
15+
use std::fmt::Display;
1516
use std::hint::black_box;
1617

18+
/// Return a successful benchmark operation result or panic with the named operation.
19+
fn require_ok<T, E: Display>(result: Result<T, E>, operation: &str) -> T {
20+
match result {
21+
Ok(value) => value,
22+
Err(err) => panic!("{operation} failed: {err}"),
23+
}
24+
}
25+
26+
/// Return a present third-party benchmark result or panic with the named operation.
27+
fn require_some<T>(value: Option<T>, operation: &str) -> T {
28+
value.unwrap_or_else(|| panic!("{operation} returned no result"))
29+
}
30+
1731
fn faer_perm_sign(p: PermRef<'_, usize>) -> f64 {
1832
// Sign(det(P)) for a permutation matrix P is +1 for even permutations, -1 for odd.
1933
// Parity can be computed from the number of cycles:
@@ -145,9 +159,7 @@ macro_rules! gen_vs_linalg_benches_for_dim {
145159
let fv2 = faer::Mat::<f64>::from_fn($d, 1, |i, _| vector_entry(i, 1.0));
146160

147161
// Precompute LU once for solve-only / det-only benchmarks.
148-
let a_lu = a
149-
.lu(DEFAULT_PIVOT_TOL)
150-
.expect("matrix should be non-singular");
162+
let a_lu = require_ok(a.lu(DEFAULT_PIVOT_TOL), "precomputed la_stack LU");
151163
let na_lu = na.clone().lu();
152164
let fa_lu = fa.partial_piv_lu();
153165

@@ -156,13 +168,11 @@ macro_rules! gen_vs_linalg_benches_for_dim {
156168
// === Determinant via LU (factor + det) ===
157169
[<group_d $d>].bench_function("la_stack_det_via_lu", |bencher| {
158170
bencher.iter(|| {
159-
let lu = black_box(a)
160-
.lu(DEFAULT_PIVOT_TOL)
161-
.expect("matrix should be non-singular");
162-
let det = match lu.det() {
163-
Ok(det) => det,
164-
Err(err) => panic!("finite benchmark matrix determinant failed: {err}"),
165-
};
171+
let lu = require_ok(
172+
black_box(a).lu(DEFAULT_PIVOT_TOL),
173+
"la_stack LU factorization",
174+
);
175+
let det = require_ok(lu.det(), "la_stack LU determinant");
166176
black_box(det);
167177
});
168178
});
@@ -186,17 +196,18 @@ macro_rules! gen_vs_linalg_benches_for_dim {
186196
// === Determinant via det() (closed-form for D≤4, LU for D≥5) ===
187197
[<group_d $d>].bench_function("la_stack_det", |bencher| {
188198
bencher.iter(|| {
189-
let det = black_box(a).det().expect("matrix should be non-singular");
199+
let det = require_ok(black_box(a).det(), "la_stack determinant");
190200
black_box(det);
191201
});
192202
});
193203

194204
// === LU factorization ===
195205
[<group_d $d>].bench_function("la_stack_lu", |bencher| {
196206
bencher.iter(|| {
197-
let lu = black_box(a)
198-
.lu(DEFAULT_PIVOT_TOL)
199-
.expect("matrix should be non-singular");
207+
let lu = require_ok(
208+
black_box(a).lu(DEFAULT_PIVOT_TOL),
209+
"la_stack LU factorization",
210+
);
200211
let _ = black_box(lu);
201212
});
202213
});
@@ -218,22 +229,22 @@ macro_rules! gen_vs_linalg_benches_for_dim {
218229
// === LU solve (factor + solve) ===
219230
[<group_d $d>].bench_function("la_stack_lu_solve", |bencher| {
220231
bencher.iter(|| {
221-
let lu = black_box(a)
222-
.lu(DEFAULT_PIVOT_TOL)
223-
.expect("matrix should be non-singular");
224-
let x = lu
225-
.solve_vec(black_box(rhs))
226-
.expect("solve should succeed");
232+
let lu = require_ok(
233+
black_box(a).lu(DEFAULT_PIVOT_TOL),
234+
"la_stack LU factorization",
235+
);
236+
let x = require_ok(
237+
lu.solve_vec(black_box(rhs)),
238+
"la_stack LU solve",
239+
);
227240
let _ = black_box(x);
228241
});
229242
});
230243

231244
[<group_d $d>].bench_function("nalgebra_lu_solve", |bencher| {
232245
bencher.iter(|| {
233246
let lu = black_box(na.clone()).lu();
234-
let x = lu
235-
.solve(black_box(&nrhs))
236-
.expect("solve should succeed");
247+
let x = require_some(lu.solve(black_box(&nrhs)), "nalgebra LU solve");
237248
black_box(x);
238249
});
239250
});
@@ -249,18 +260,20 @@ macro_rules! gen_vs_linalg_benches_for_dim {
249260
// === Solve using a precomputed LU ===
250261
[<group_d $d>].bench_function("la_stack_solve_from_lu", |bencher| {
251262
bencher.iter(|| {
252-
let x = a_lu
253-
.solve_vec(black_box(rhs))
254-
.expect("solve should succeed");
263+
let x = require_ok(
264+
a_lu.solve_vec(black_box(rhs)),
265+
"precomputed la_stack LU solve",
266+
);
255267
let _ = black_box(x);
256268
});
257269
});
258270

259271
[<group_d $d>].bench_function("nalgebra_solve_from_lu", |bencher| {
260272
bencher.iter(|| {
261-
let x = na_lu
262-
.solve(black_box(&nrhs))
263-
.expect("solve should succeed");
273+
let x = require_some(
274+
na_lu.solve(black_box(&nrhs)),
275+
"precomputed nalgebra LU solve",
276+
);
264277
black_box(x);
265278
});
266279
});
@@ -275,10 +288,7 @@ macro_rules! gen_vs_linalg_benches_for_dim {
275288
// === Determinant from a precomputed LU ===
276289
[<group_d $d>].bench_function("la_stack_det_from_lu", |bencher| {
277290
bencher.iter(|| {
278-
let det = match a_lu.det() {
279-
Ok(det) => det,
280-
Err(err) => panic!("finite benchmark matrix determinant failed: {err}"),
281-
};
291+
let det = require_ok(a_lu.det(), "precomputed la_stack LU determinant");
282292
black_box(det);
283293
});
284294
});
@@ -300,7 +310,7 @@ macro_rules! gen_vs_linalg_benches_for_dim {
300310
// === Vector dot product ===
301311
[<group_d $d>].bench_function("la_stack_dot", |bencher| {
302312
bencher.iter(|| {
303-
let result = black_box(v1).dot(black_box(v2)).unwrap();
313+
let result = require_ok(black_box(v1).dot(black_box(v2)), "la_stack dot");
304314
black_box(result);
305315
});
306316
});
@@ -327,7 +337,7 @@ macro_rules! gen_vs_linalg_benches_for_dim {
327337
// === Vector norm squared ===
328338
[<group_d $d>].bench_function("la_stack_norm2_sq", |bencher| {
329339
bencher.iter(|| {
330-
let result = black_box(v1).norm2_sq().unwrap();
340+
let result = require_ok(black_box(v1).norm2_sq(), "la_stack norm2_sq");
331341
black_box(result);
332342
});
333343
});
@@ -354,7 +364,7 @@ macro_rules! gen_vs_linalg_benches_for_dim {
354364
// === Matrix infinity norm (max absolute row sum) ===
355365
[<group_d $d>].bench_function("la_stack_inf_norm", |bencher| {
356366
bencher.iter(|| {
357-
let result = black_box(a).inf_norm().unwrap();
367+
let result = require_ok(black_box(a).inf_norm(), "la_stack inf_norm");
358368
black_box(result);
359369
});
360370
});

examples/const_det_4x4.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,20 @@ const DET: f64 = match MAT.det_direct() {
2020
Err(_) => panic!("matrix entries must be finite"),
2121
};
2222

23-
fn main() {
23+
fn main() -> Result<(), LaError> {
2424
println!("4×4 matrix:");
2525
for r in 0..4 {
2626
print!(" [");
2727
for c in 0..4 {
2828
if c > 0 {
2929
print!(", ");
3030
}
31-
print!("{:5.1}", MAT.get(r, c).unwrap());
31+
print!("{:5.1}", MAT.get_checked(r, c)?);
3232
}
3333
println!("]");
3434
}
3535
println!();
3636
println!("det (computed at compile time) = {DET}");
37+
38+
Ok(())
3739
}

examples/exact_sign_3x3.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
99
use la_stack::prelude::*;
1010

11-
fn main() {
11+
fn main() -> Result<(), LaError> {
1212
// Base matrix: rows in arithmetic progression → exactly singular (det = 0).
1313
// [[1, 2, 3],
1414
// [4, 5, 6],
@@ -23,8 +23,8 @@ fn main() {
2323
[7.0, 8.0, 9.0],
2424
]);
2525

26-
let sign = m.det_sign_exact().unwrap();
27-
let det_f64 = m.det().unwrap();
26+
let sign = m.det_sign_exact()?;
27+
let det_f64 = m.det()?;
2828

2929
println!("Near-singular 3×3 matrix (perturbation = 2^-50 ≈ {perturbation:.2e}):");
3030
for r in 0..3 {
@@ -33,7 +33,7 @@ fn main() {
3333
if c > 0 {
3434
print!(", ");
3535
}
36-
print!("{:22.18}", m.get(r, c).unwrap());
36+
print!("{:22.18}", m.get_checked(r, c)?);
3737
}
3838
println!("]");
3939
}
@@ -42,4 +42,6 @@ fn main() {
4242
println!("det_sign_exact() = {sign}");
4343
println!();
4444
println!("The exact sign is −1 (negative), matching the analytical result.");
45+
46+
Ok(())
4547
}

examples/ldlt_solve_3x3.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn main() -> Result<(), LaError> {
2626
if c > 0 {
2727
print!(", ");
2828
}
29-
print!("{:5.1}", a.get(r, c).unwrap());
29+
print!("{:5.1}", a.get_checked(r, c)?);
3030
}
3131
println!("]");
3232
}

0 commit comments

Comments
 (0)