1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/// Errors returned by FERAL's public API.
#[derive(Debug)]
pub enum FeralError {
/// The matrix is numerically rank-deficient: a pivot was exactly or
/// near-zero and `ZeroPivotAction::Fail` was specified. The factorization
/// is incomplete.
NumericallyRankDeficient,
/// Input matrix dimensions are inconsistent or the matrix is not square.
InvalidInput(String),
/// The RHS vector length does not match the factored matrix dimension.
DimensionMismatch { expected: usize, got: usize },
/// An I/O or parse error occurred (e.g. reading a Matrix Market file).
IoError(String),
/// `Solver::solve` (or `solve_refined`) was called before any
/// successful factorization. Call `factor()` first.
NoFactor,
/// The SQD fast-path (`Solver::with_sqd_mode(true)`) refused a
/// diagonal pivot. Either the pivot magnitude fell at or below
/// `BunchKaufmanParams::zero_tol` (so `|d_kk| ≈ 0`), or the
/// implied L-column growth `||l_col||_∞ / sqrt(|d_kk|)` would
/// exceed `1 / sqrt(EPS) ≈ 6.7e7`, breaking the
/// Gill-Saunders-Shinnerl 1996 stability bound for diagonal
/// LDL^T on SQD matrices. The factorization aborts immediately —
/// SQD never falls back silently to BK 1x1-vs-2x2. Caller
/// must either re-factor with `with_sqd_mode(false)` (BK
/// fallback) or investigate the input (Vanderbei 1995's
/// SQD contract is not met at the reported column). See
/// `dev/research/sqd-fast-path.md` and issue #34.
SqdContractViolated { column: usize, pivot: f64 },
/// A supernode received more delayed pivots from its children at
/// numeric time than the symbolic-analysis phase budgeted for.
/// Mirrors MUMPS's `INFO(2)` workspace-overflow path: a predictable,
/// recoverable failure that bounds worst-case front growth.
/// See issue #55 and `dev/research/symbolic-delay-budget-2026-05-27.md`.
DelayBudgetExceeded {
supernode: usize,
required: usize,
capacity: usize,
},
}
impl std::fmt::Display for FeralError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FeralError::NumericallyRankDeficient => {
write!(f, "matrix is numerically rank-deficient")
}
FeralError::InvalidInput(msg) => write!(f, "invalid input: {}", msg),
FeralError::DimensionMismatch { expected, got } => {
write!(f, "dimension mismatch: expected {}, got {}", expected, got)
}
FeralError::IoError(msg) => write!(f, "I/O error: {}", msg),
FeralError::NoFactor => {
write!(f, "no factorization available; call Solver::factor() first")
}
FeralError::SqdContractViolated { column, pivot } => {
write!(
f,
"SQD contract violated at column {}: pivot = {:e} fails \
the diagonal-LDL^T stability bound (near-zero pivot or \
L-column growth above 1/sqrt(EPS))",
column, pivot
)
}
FeralError::DelayBudgetExceeded {
supernode,
required,
capacity,
} => {
write!(
f,
"delayed-pivot budget exceeded at supernode {}: \
required {} delayed columns, capacity {} (issue #55)",
supernode, required, capacity
)
}
}
}
}
impl std::error::Error for FeralError {}