use crate::base::budget::BudgetKind;
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SymplexError {
#[error("contradictory assumptions for '{symbol}': cannot be both {a} and {b}")]
ContradictoryAssumptions {
symbol: String,
a: String,
b: String,
},
#[error("precision exhausted: requested {requested} digits, achieved {achieved}")]
PrecisionExhausted {
requested: u32,
achieved: u32,
},
#[error("expression contains free symbol '{name}'")]
FreeSymbol {
name: String,
},
#[error("cannot evaluate: {reason}")]
Unevaluable {
reason: String,
},
#[error("{operation} could not be computed: {reason}")]
ComputationFailed {
operation: &'static str,
reason: String,
},
#[error("not implemented: {0}")]
NotImplemented(String),
#[error("{operation} diverges: {reason}")]
Divergent {
operation: &'static str,
reason: String,
},
#[error("{operation}: no solution ({reason})")]
NoSolution {
operation: &'static str,
reason: String,
},
#[error("{operation}: infinitely many solutions ({reason})")]
InfiniteSolutions {
operation: &'static str,
reason: String,
},
#[error("{operation}: invalid argument: {reason}")]
InvalidArgument {
operation: &'static str,
reason: String,
},
#[error("{operation} could not be computed: {}", kind.reason())]
BudgetExhausted {
operation: &'static str,
kind: BudgetKind,
},
#[error(
"{operation} could not be computed: expression swell: intermediate result exceeds the \
budget of {nodes} expression nodes; simplify the input or substitute numeric values \
first"
)]
ExpressionSwell {
operation: &'static str,
nodes: usize,
},
#[error("{operation} could not be computed: matrix is not diagonalizable: {reason}")]
NotDiagonalizable {
operation: &'static str,
reason: String,
},
#[error("{operation}: degenerate data ({}): {reason}", kind.describe())]
DegenerateData {
operation: &'static str,
kind: Degeneracy,
reason: String,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Degeneracy {
Collinear,
Separation,
ConstantResponse,
ZeroVariance,
NoEvents,
}
impl Degeneracy {
#[must_use]
pub const fn describe(self) -> &'static str {
match self {
Degeneracy::Collinear => "collinear design",
Degeneracy::Separation => "separation",
Degeneracy::ConstantResponse => "constant response",
Degeneracy::ZeroVariance => "zero variance",
Degeneracy::NoEvents => "no events",
}
}
}
impl SymplexError {
pub fn invalid_argument(operation: &'static str, reason: impl Into<String>) -> Self {
SymplexError::InvalidArgument {
operation,
reason: reason.into(),
}
}
pub fn computation_failed(operation: &'static str, reason: impl Into<String>) -> Self {
SymplexError::ComputationFailed {
operation,
reason: reason.into(),
}
}
pub fn degenerate(
operation: &'static str,
kind: Degeneracy,
reason: impl Into<String>,
) -> Self {
SymplexError::DegenerateData {
operation,
kind,
reason: reason.into(),
}
}
#[must_use]
pub fn degeneracy(&self) -> Option<Degeneracy> {
match self {
SymplexError::DegenerateData { kind, .. } => Some(*kind),
_ => None,
}
}
}