use std::fmt;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
InvalidParam(&'static str),
AupdFailed {
info: i32,
iters: usize,
nconv: usize,
n_matvec: usize,
},
EupdFailed {
info: i32,
iters: usize,
nconv: usize,
n_matvec: usize,
},
UnexpectedIdo(i32),
MaxIterReached {
iters: usize,
nconv: usize,
n_matvec: usize,
},
NoShiftsApplied {
iters: usize,
nconv: usize,
n_matvec: usize,
},
ArnoldiFactorizationFailed {
iters: usize,
factorization_size: usize,
n_matvec: usize,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidParam(msg) => write!(f, "invalid parameter: {msg}"),
Error::AupdFailed {
info,
iters,
nconv,
n_matvec,
} => write!(
f,
"ARPACK *aupd failed: info = {info} \
(iters = {iters}, nconv = {nconv}, n_matvec = {n_matvec})"
),
Error::EupdFailed {
info,
iters,
nconv,
n_matvec,
} => write!(
f,
"ARPACK *eupd failed: info = {info} \
(iters = {iters}, nconv = {nconv}, n_matvec = {n_matvec})"
),
Error::UnexpectedIdo(ido) => write!(f, "ARPACK requested unsupported ido = {ido}"),
Error::MaxIterReached {
iters,
nconv,
n_matvec,
} => write!(
f,
"ARPACK hit max_iter without convergence: iters = {iters}, \
nconv = {nconv}, n_matvec = {n_matvec}"
),
Error::NoShiftsApplied {
iters,
nconv,
n_matvec,
} => write!(
f,
"ARPACK could not apply any shifts during a restart cycle \
(info = 3); increase ncv relative to nev. iters = {iters}, \
nconv = {nconv}, n_matvec = {n_matvec}"
),
Error::ArnoldiFactorizationFailed {
iters,
factorization_size,
n_matvec,
} => write!(
f,
"ARPACK could not build an Arnoldi factorization (info = -9999); \
built a factorization of size {factorization_size}. Try increasing \
max_iter or ncv. iters = {iters}, n_matvec = {n_matvec}"
),
}
}
}
impl std::error::Error for Error {}
pub(crate) fn aupd_error(info: i32, iters: usize, nconv: usize, n_matvec: usize) -> Option<Error> {
match info {
0 => None,
1 if nconv == 0 => Some(Error::MaxIterReached {
iters,
nconv,
n_matvec,
}),
1 => None,
3 => Some(Error::NoShiftsApplied {
iters,
nconv,
n_matvec,
}),
-9999 => Some(Error::ArnoldiFactorizationFailed {
iters,
factorization_size: nconv,
n_matvec,
}),
info => Some(Error::AupdFailed {
info,
iters,
nconv,
n_matvec,
}),
}
}
pub(crate) fn eupd_error(info: i32, iters: usize, nconv: usize, n_matvec: usize) -> Option<Error> {
match info {
0 => None,
info => Some(Error::EupdFailed {
info,
iters,
nconv,
n_matvec,
}),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aupd_success_and_partial_proceed_to_eupd() {
assert!(aupd_error(0, 5, 2, 7).is_none());
assert!(aupd_error(1, 5, 1, 7).is_none());
}
#[test]
fn aupd_maxiter_with_zero_nconv() {
assert!(matches!(
aupd_error(1, 5, 0, 7),
Some(Error::MaxIterReached {
iters: 5,
nconv: 0,
n_matvec: 7
})
));
}
#[test]
fn aupd_named_convergence_codes() {
assert!(matches!(
aupd_error(3, 5, 2, 7),
Some(Error::NoShiftsApplied {
iters: 5,
nconv: 2,
n_matvec: 7
})
));
assert!(matches!(
aupd_error(-9999, 5, 2, 7),
Some(Error::ArnoldiFactorizationFailed {
iters: 5,
factorization_size: 2,
n_matvec: 7
})
));
}
#[test]
fn aupd_catch_all_retains_raw_code() {
assert!(matches!(
aupd_error(-8, 5, 0, 7),
Some(Error::AupdFailed { info: -8, .. })
));
assert!(matches!(
aupd_error(-9, 5, 0, 7),
Some(Error::AupdFailed { info: -9, .. })
));
}
#[test]
fn eupd_success_and_failure() {
assert!(eupd_error(0, 5, 2, 7).is_none());
assert!(matches!(
eupd_error(-17, 5, 2, 7),
Some(Error::EupdFailed { info: -17, .. })
));
assert!(matches!(
eupd_error(-15, 5, 2, 7),
Some(Error::EupdFailed { info: -15, .. })
));
}
#[test]
fn display_mentions_raw_code_and_remedy() {
let no_shifts = Error::NoShiftsApplied {
iters: 3,
nconv: 1,
n_matvec: 4,
};
let msg = no_shifts.to_string();
assert!(msg.contains("info = 3"));
assert!(msg.contains("ncv"));
let build = Error::ArnoldiFactorizationFailed {
iters: 3,
factorization_size: 1,
n_matvec: 4,
};
assert!(build.to_string().contains("-9999"));
let aupd = Error::AupdFailed {
info: -8,
iters: 3,
nconv: 0,
n_matvec: 4,
};
assert!(aupd.to_string().contains("info = -8"));
}
}