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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Return type shared across all driver entry points.
use c_int;
use crateError;
/// The eigenpair returned by a driver, plus the diagnostic counters
/// ARPACK writes back into `iparam` during the iteration.
///
/// Returned only when ARPACK reaches full convergence (`info == 0`
/// from `*aupd_c`); the `max_iter`-reached case is reported through
/// [`crate::Error::MaxIterReached`] instead, which preserves the
/// same iparam counters but signals that no usable Ritz pair was
/// extracted. Callers thus see this struct only when the eigenpair
/// is meaningful.
///
/// The fields beyond `eigenvalue` / `eigenvector` let callers:
///
/// - tell fast convergence apart from a near-cap run (`iters`);
/// - confirm full convergence at a glance (`nconv >= nev`);
/// - account the cost of operator applications (`n_matvec`).
/// Multi-eigenpair result returned by the `eigenpairs_*` drivers.
///
/// Carries the converged eigenpairs plus the same diagnostic
/// counters as [`EigSolution`]. The crate distinguishes
/// `EigSolution` (singular, returned by `smallest_eigenpair_*`)
/// from `MultiEigSolution` (plural, returned by `eigenpairs_*`)
/// so the single-eigenpair API can stay ergonomic while the
/// multi-eigenpair API exposes per-vector storage.
///
/// `eigenvalues` and `eigenvectors` both have length
/// `min(nconv, nev_requested)`. ARPACK only guarantees the
/// first `nconv` slots of its output are converged; slots
/// beyond that count are undefined and never propagated. The
/// extra `min` against `nev_requested` accommodates the rare
/// case where ARPACK reports `nconv > nev_requested` (bonus
/// Ritz values that satisfied the convergence bound) — the
/// extraction buffer is only `nev_requested` long per the
/// documented `*eupd` interface, so any bonus values are
/// recorded in `nconv` as a diagnostic but not in the
/// eigenpair arrays. Callers verify `nconv >= nev_requested`
/// themselves if they need full convergence.
///
/// # Ordering
///
/// The real-symmetric Lanczos drivers (`{s,d}{sa,se}upd_c`)
/// return eigenvalues in ascending algebraic order regardless of
/// the `Which` selector — `Which::LargestAlgebraic` with `nev = 3`
/// on a Laplacian-style matrix yields the three largest
/// eigenvalues sorted ascending (smallest of the three first).
///
/// The complex Arnoldi drivers (`{c,z}{na,ne}upd_c`) do **not**
/// apply a final sort; the order depends on ARPACK's internal
/// selection state. Callers that need a stable order must sort
/// the returned vectors themselves.
/// Convert a non-negative `iparam` writeback (the only kind ARPACK
/// produces for these slots) into `usize`. Values are inherently
/// non-negative — `iters`, `nconv`, and matvec counts cannot be
/// negative — so a negative reading would be a wrapper bug, not user
/// input; clamp at 0 and let downstream invariants catch the mismatch.
pub
/// Collapse the `nev = 1` result of a general driver into the
/// single-eigenpair [`EigSolution`] the convenience wrappers return.
pub
/// Narrow a `usize` extent to the `c_int` ARPACK expects, surfacing
/// the overflow as a typed error rather than a silent wrap.
pub
/// Narrow `Options::tol` (always stored as `f64`) to a driver's
/// working float precision. The per-family macros select one of these
/// by name so the f64 path is the identity `tol_as_f64` rather than an
/// `f64 as f64` that would trip `clippy::unnecessary_cast`; the f32
/// path does the real `as f32` narrowing.
pub
pub