use core::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ComplexityClass {
Logarithmic,
PolyLogarithmic,
SubLinear,
Linear,
QuasiLinear,
SubQuadratic,
Polynomial(u8),
SuperPolynomial,
SubExponential,
Exponential,
Factorial,
DoubleExponential,
Adaptive {
default: &'static ComplexityClass,
worst: &'static ComplexityClass,
},
}
impl ComplexityClass {
pub const fn rank(&self) -> u16 {
match self {
ComplexityClass::Logarithmic => 100,
ComplexityClass::PolyLogarithmic => 200,
ComplexityClass::SubLinear => 300,
ComplexityClass::Linear => 400,
ComplexityClass::QuasiLinear => 500,
ComplexityClass::SubQuadratic => 600,
ComplexityClass::Polynomial(degree) => 700u16 + *degree as u16,
ComplexityClass::SuperPolynomial => 800,
ComplexityClass::SubExponential => 900,
ComplexityClass::Exponential => 1000,
ComplexityClass::Factorial => 1100,
ComplexityClass::DoubleExponential => 1200,
ComplexityClass::Adaptive { worst, .. } => worst.rank(),
}
}
pub const fn short_label(&self) -> &'static str {
match self {
ComplexityClass::Logarithmic => "O(log n)",
ComplexityClass::PolyLogarithmic => "O((log n)^k)",
ComplexityClass::SubLinear => "O(n^c), c<1",
ComplexityClass::Linear => "O(n)",
ComplexityClass::QuasiLinear => "O(n log n)",
ComplexityClass::SubQuadratic => "O(n^{2-ε})",
ComplexityClass::Polynomial(2) => "O(n^2)",
ComplexityClass::Polynomial(3) => "O(n^3)",
ComplexityClass::Polynomial(_) => "O(n^k)",
ComplexityClass::SuperPolynomial => "superpoly",
ComplexityClass::SubExponential => "subexp",
ComplexityClass::Exponential => "O(2^n)",
ComplexityClass::Factorial => "O(n!)",
ComplexityClass::DoubleExponential => "O(2^{2^n})",
ComplexityClass::Adaptive { .. } => "adaptive",
}
}
pub const fn is_edge_safe(&self) -> bool {
self.rank() < ComplexityClass::Linear.rank()
}
}
impl PartialOrd for ComplexityClass {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ComplexityClass {
fn cmp(&self, other: &Self) -> Ordering {
self.rank().cmp(&other.rank())
}
}
pub trait Complexity {
const CLASS: ComplexityClass;
const DETAIL: &'static str = "";
}
pub trait ComplexityIntrospect {
fn complexity_class(&self) -> ComplexityClass;
fn complexity_detail(&self) -> &'static str {
""
}
}
impl<T: Complexity> ComplexityIntrospect for T {
fn complexity_class(&self) -> ComplexityClass {
T::CLASS
}
fn complexity_detail(&self) -> &'static str {
T::DETAIL
}
}
impl Complexity for crate::solver::neumann::NeumannSolver {
const CLASS: ComplexityClass = ComplexityClass::Linear;
const DETAIL: &'static str =
"O(k · nnz(A)) per iter; k bounded by series_tolerance + max_terms (default 200).";
}
impl Complexity for crate::optimized_solver::OptimizedConjugateGradientSolver {
const CLASS: ComplexityClass = ComplexityClass::Linear;
const DETAIL: &'static str = "O(k · nnz(A)) per iter; k ≈ √κ(A) on SPD inputs.";
}
impl Complexity for crate::sublinear::sublinear_neumann::SublinearNeumannSolver {
const CLASS: ComplexityClass = ComplexityClass::Adaptive {
default: &ComplexityClass::Logarithmic,
worst: &ComplexityClass::Linear,
};
const DETAIL: &'static str =
"O(log n) per single-entry query on diagonally-dominant systems via JL + recursive Neumann; \
O(n) base case at n ≤ base_case_threshold.";
}
impl Complexity for crate::sublinear::johnson_lindenstrauss::JLEmbedding {
const CLASS: ComplexityClass = ComplexityClass::Linear;
const DETAIL: &'static str =
"O(d · k) per project_vector; k = target_dim, capped at original_dim - 1.";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ordering_is_by_asymptotic_growth() {
assert!(ComplexityClass::Logarithmic < ComplexityClass::PolyLogarithmic);
assert!(ComplexityClass::PolyLogarithmic < ComplexityClass::SubLinear);
assert!(ComplexityClass::SubLinear < ComplexityClass::Linear);
assert!(ComplexityClass::Linear < ComplexityClass::QuasiLinear);
assert!(ComplexityClass::QuasiLinear < ComplexityClass::SubQuadratic);
assert!(ComplexityClass::SubQuadratic < ComplexityClass::Polynomial(2));
assert!(ComplexityClass::Polynomial(2) < ComplexityClass::Polynomial(3));
assert!(ComplexityClass::Polynomial(3) < ComplexityClass::SuperPolynomial);
assert!(ComplexityClass::SuperPolynomial < ComplexityClass::SubExponential);
assert!(ComplexityClass::SubExponential < ComplexityClass::Exponential);
assert!(ComplexityClass::Exponential < ComplexityClass::Factorial);
assert!(ComplexityClass::Factorial < ComplexityClass::DoubleExponential);
}
#[test]
fn edge_safe_predicate_matches_adr() {
assert!(ComplexityClass::Logarithmic.is_edge_safe());
assert!(ComplexityClass::PolyLogarithmic.is_edge_safe());
assert!(ComplexityClass::SubLinear.is_edge_safe());
assert!(!ComplexityClass::Linear.is_edge_safe());
assert!(!ComplexityClass::QuasiLinear.is_edge_safe());
assert!(!ComplexityClass::Exponential.is_edge_safe());
}
#[test]
fn adaptive_ranks_by_worst_case() {
static QUASILIN: ComplexityClass = ComplexityClass::QuasiLinear;
static POLY3: ComplexityClass = ComplexityClass::Polynomial(3);
let adaptive = ComplexityClass::Adaptive {
default: &QUASILIN,
worst: &POLY3,
};
assert!(adaptive > ComplexityClass::QuasiLinear);
assert_eq!(adaptive.rank(), POLY3.rank());
}
#[test]
fn short_label_format() {
assert_eq!(ComplexityClass::Logarithmic.short_label(), "O(log n)");
assert_eq!(ComplexityClass::Polynomial(2).short_label(), "O(n^2)");
assert_eq!(ComplexityClass::Polynomial(3).short_label(), "O(n^3)");
assert_eq!(ComplexityClass::Polynomial(4).short_label(), "O(n^k)");
assert_eq!(ComplexityClass::Exponential.short_label(), "O(2^n)");
}
#[test]
fn introspect_blanket_impl_matches_const() {
struct Dummy;
impl Complexity for Dummy {
const CLASS: ComplexityClass = ComplexityClass::SubLinear;
const DETAIL: &'static str = "test dummy";
}
let d = Dummy;
assert_eq!(d.complexity_class(), ComplexityClass::SubLinear);
assert_eq!(d.complexity_detail(), "test dummy");
assert_eq!(<Dummy as Complexity>::CLASS, ComplexityClass::SubLinear);
}
}