pounce_linsol/summary.rs
1//! Linear-solver post-mortem summary — shared shape that concrete
2//! backends populate and downstream report builders consume.
3//!
4//! Kept dep-light on purpose: no serde derives here. The CLI's solve
5//! report crate owns the serializable mirror.
6
7/// Aggregate stats accumulated over the lifetime of one linear-solver
8/// instance. All fields default to zero / `None` so a backend that
9/// declines to populate them still produces a valid summary.
10#[derive(Debug, Clone, Default)]
11pub struct LinearSolverSummary {
12 /// Short identifier of the backend that produced this summary:
13 /// `"feral"`, `"ma57"`, etc. Empty for the `Default` value.
14 pub solver_name: String,
15 /// Number of `factor()` calls completed (including those that
16 /// reused the cached symbolic factorisation).
17 pub n_factors: u64,
18 /// Of `n_factors`, how many reused the previous symbolic
19 /// factorisation (sparsity pattern unchanged). Healthy IPM workloads
20 /// expect this to dominate after the first iter.
21 pub n_pattern_reuse: u64,
22 /// Of `n_factors`, how many required a fresh symbolic factorisation
23 /// (sparsity pattern changed). Inverse of `n_pattern_reuse` modulo
24 /// the very first factor.
25 pub n_pattern_changes: u64,
26 /// Maximum `nnz(L) / nnz(A)` observed across factors. Values much
27 /// greater than ~10 on KKT-style systems indicate ordering trouble.
28 pub max_fill_ratio: Option<f64>,
29 /// Minimum `|pivot|` observed across factors. Approaches the
30 /// working-precision floor when the matrix is near-singular.
31 pub min_abs_pivot: Option<f64>,
32 /// Maximum `|pivot|` observed across factors.
33 pub max_abs_pivot: Option<f64>,
34 /// Inertia of the final factorisation as `(positive, negative, zero)`.
35 pub last_inertia: Option<(usize, usize, usize)>,
36 /// `nnz(A)` of the final factorisation's matrix.
37 pub last_nnz_a: Option<usize>,
38 /// `nnz(L)` of the final factorisation.
39 pub last_nnz_l: Option<usize>,
40}
41
42impl LinearSolverSummary {
43 /// Returns `true` if the summary carries no signal beyond the
44 /// solver name — useful for "did we collect anything?" checks.
45 pub fn is_empty(&self) -> bool {
46 self.n_factors == 0
47 }
48}