use std::fmt;
use std::ops::{Add, Div, Mul, Neg, Sub};
#[derive(Debug, Clone, PartialEq)]
pub enum IntervalError {
DivisionByZeroInterval,
EmptyInterval,
IterationLimitExceeded,
NonConvergence,
DomainError(String),
SingularSystem,
}
impl fmt::Display for IntervalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IntervalError::DivisionByZeroInterval => {
write!(f, "division by interval containing zero")
}
IntervalError::EmptyInterval => write!(f, "empty interval"),
IntervalError::IterationLimitExceeded => write!(f, "iteration limit exceeded"),
IntervalError::NonConvergence => write!(f, "method did not converge"),
IntervalError::DomainError(msg) => write!(f, "domain error: {}", msg),
IntervalError::SingularSystem => write!(f, "singular or ill-conditioned system"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Interval {
pub lo: f64,
pub hi: f64,
}
impl Interval {
pub fn new(lo: f64, hi: f64) -> Option<Self> {
if lo > hi {
None
} else {
Some(Interval { lo, hi })
}
}
pub fn new_unchecked(lo: f64, hi: f64) -> Self {
Interval {
lo: lo.min(hi),
hi: lo.max(hi),
}
}
pub fn point(x: f64) -> Self {
Interval { lo: x, hi: x }
}
pub fn zero() -> Self {
Interval { lo: 0.0, hi: 0.0 }
}
pub fn one() -> Self {
Interval { lo: 1.0, hi: 1.0 }
}
pub fn width(self) -> f64 {
self.hi - self.lo
}
pub fn mid(self) -> f64 {
self.lo / 2.0 + self.hi / 2.0 }
pub fn radius(self) -> f64 {
(self.hi - self.lo) / 2.0
}
pub fn contains(self, x: f64) -> bool {
self.lo <= x && x <= self.hi
}
pub fn contains_zero(self) -> bool {
self.lo <= 0.0 && 0.0 <= self.hi
}
pub fn contains_interval(self, other: Interval) -> bool {
self.lo <= other.lo && other.hi <= self.hi
}
pub fn intersect(self, other: Interval) -> Option<Interval> {
let lo = self.lo.max(other.lo);
let hi = self.hi.min(other.hi);
if lo <= hi {
Some(Interval { lo, hi })
} else {
None
}
}
pub fn hull(self, other: Interval) -> Interval {
Interval {
lo: self.lo.min(other.lo),
hi: self.hi.max(other.hi),
}
}
pub fn abs(self) -> Interval {
if self.lo >= 0.0 {
self
} else if self.hi <= 0.0 {
Interval {
lo: -self.hi,
hi: -self.lo,
}
} else {
Interval {
lo: 0.0,
hi: self.lo.abs().max(self.hi.abs()),
}
}
}
pub fn square(self) -> Interval {
let a = self.abs();
Interval {
lo: a.lo * a.lo,
hi: a.hi * a.hi,
}
}
pub fn sqrt(self) -> Result<Interval, IntervalError> {
if self.lo < 0.0 {
return Err(IntervalError::DomainError(
"sqrt of negative interval".to_string(),
));
}
Ok(Interval {
lo: self.lo.sqrt(),
hi: self.hi.sqrt(),
})
}
pub fn exp(self) -> Interval {
Interval {
lo: self.lo.exp(),
hi: self.hi.exp(),
}
}
pub fn ln(self) -> Result<Interval, IntervalError> {
if self.lo <= 0.0 {
return Err(IntervalError::DomainError(
"ln of non-positive interval".to_string(),
));
}
Ok(Interval {
lo: self.lo.ln(),
hi: self.hi.ln(),
})
}
pub fn sin(self) -> Interval {
let width = self.width();
if width >= 2.0 * std::f64::consts::PI {
return Interval { lo: -1.0, hi: 1.0 };
}
let mut min_val = self.lo.sin().min(self.hi.sin());
let mut max_val = self.lo.sin().max(self.hi.sin());
let pi = std::f64::consts::PI;
let half_pi = pi / 2.0;
let k_min = (self.lo / half_pi).ceil() as i64;
let k_max = (self.hi / half_pi).floor() as i64;
for k in k_min..=k_max {
let x = k as f64 * half_pi;
let v = x.sin();
min_val = min_val.min(v);
max_val = max_val.max(v);
}
Interval {
lo: min_val,
hi: max_val,
}
}
pub fn cos(self) -> Interval {
let shifted = Interval {
lo: self.lo - std::f64::consts::FRAC_PI_2,
hi: self.hi - std::f64::consts::FRAC_PI_2,
};
shifted.sin()
}
pub fn powi(self, n: i32) -> Interval {
if n == 0 {
return Interval::one();
}
if n < 0 {
let pos = self.powi(-n);
return Interval {
lo: 1.0 / pos.hi,
hi: 1.0 / pos.lo,
};
}
if n % 2 == 0 {
self.square() } else {
Interval {
lo: self.lo.powi(n),
hi: self.hi.powi(n),
}
}
}
}
impl Add for Interval {
type Output = Interval;
fn add(self, rhs: Interval) -> Interval {
Interval {
lo: self.lo + rhs.lo,
hi: self.hi + rhs.hi,
}
}
}
impl Sub for Interval {
type Output = Interval;
fn sub(self, rhs: Interval) -> Interval {
Interval {
lo: self.lo - rhs.hi,
hi: self.hi - rhs.lo,
}
}
}
impl Neg for Interval {
type Output = Interval;
fn neg(self) -> Interval {
Interval {
lo: -self.hi,
hi: -self.lo,
}
}
}
impl Mul for Interval {
type Output = Interval;
fn mul(self, rhs: Interval) -> Interval {
let products = [
self.lo * rhs.lo,
self.lo * rhs.hi,
self.hi * rhs.lo,
self.hi * rhs.hi,
];
let lo = products.iter().cloned().fold(f64::INFINITY, f64::min);
let hi = products.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
Interval { lo, hi }
}
}
impl fmt::Display for Interval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}, {}]", self.lo, self.hi)
}
}
pub fn interval_div(a: Interval, b: Interval) -> Result<Interval, IntervalError> {
if b.contains_zero() {
return Err(IntervalError::DivisionByZeroInterval);
}
let recip = Interval {
lo: 1.0 / b.hi,
hi: 1.0 / b.lo,
};
Ok(a * recip)
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct KaucherInterval {
pub a: f64,
pub b: f64,
}
impl KaucherInterval {
pub fn new(a: f64, b: f64) -> Self {
KaucherInterval { a, b }
}
pub fn is_proper(self) -> bool {
self.a <= self.b
}
pub fn is_improper(self) -> bool {
self.a > self.b
}
pub fn to_classical(self) -> Option<Interval> {
if self.is_proper() {
Some(Interval {
lo: self.a,
hi: self.b,
})
} else {
None
}
}
pub fn dual(self) -> KaucherInterval {
KaucherInterval {
a: self.b,
b: self.a,
}
}
pub fn width(self) -> f64 {
self.b - self.a
}
pub fn pro(self) -> Interval {
Interval {
lo: self.a.min(self.b),
hi: self.a.max(self.b),
}
}
}
impl Add for KaucherInterval {
type Output = KaucherInterval;
fn add(self, rhs: KaucherInterval) -> KaucherInterval {
KaucherInterval {
a: self.a + rhs.a,
b: self.b + rhs.b,
}
}
}
impl Sub for KaucherInterval {
type Output = KaucherInterval;
fn sub(self, rhs: KaucherInterval) -> KaucherInterval {
KaucherInterval {
a: self.a - rhs.b,
b: self.b - rhs.a,
}
}
}
impl Neg for KaucherInterval {
type Output = KaucherInterval;
fn neg(self) -> KaucherInterval {
KaucherInterval {
a: -self.b,
b: -self.a,
}
}
}
impl fmt::Display for KaucherInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_proper() {
write!(f, "[{}, {}]", self.a, self.b)
} else {
write!(f, "⟨{}, {}⟩", self.a, self.b) }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IntervalVector {
pub components: Vec<Interval>,
}
impl IntervalVector {
pub fn new(components: Vec<Interval>) -> Self {
IntervalVector { components }
}
pub fn zero(n: usize) -> Self {
IntervalVector {
components: vec![Interval::zero(); n],
}
}
pub fn dim(&self) -> usize {
self.components.len()
}
pub fn add(&self, other: &IntervalVector) -> Option<IntervalVector> {
if self.dim() != other.dim() {
return None;
}
Some(IntervalVector {
components: self
.components
.iter()
.zip(other.components.iter())
.map(|(&a, &b)| a + b)
.collect(),
})
}
pub fn widths(&self) -> Vec<f64> {
self.components.iter().map(|i| i.width()).collect()
}
pub fn max_width(&self) -> f64 {
self.components
.iter()
.map(|i| i.width())
.fold(0.0f64, f64::max)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct IntervalMatrix {
pub rows: usize,
pub cols: usize,
pub data: Vec<Interval>,
}
impl IntervalMatrix {
pub fn new(rows: usize, cols: usize, data: Vec<Interval>) -> Option<Self> {
if data.len() != rows * cols {
return None;
}
Some(IntervalMatrix { rows, cols, data })
}
pub fn zero(rows: usize, cols: usize) -> Self {
IntervalMatrix {
rows,
cols,
data: vec![Interval::zero(); rows * cols],
}
}
pub fn get(&self, i: usize, j: usize) -> Option<Interval> {
self.data.get(i * self.cols + j).copied()
}
pub fn set(&mut self, i: usize, j: usize, val: Interval) -> bool {
let idx = i * self.cols + j;
if idx < self.data.len() {
self.data[idx] = val;
true
} else {
false
}
}
pub fn mul_vec(&self, v: &IntervalVector) -> Option<IntervalVector> {
if self.cols != v.dim() {
return None;
}
let mut result = Vec::with_capacity(self.rows);
for i in 0..self.rows {
let mut sum = Interval::zero();
for j in 0..self.cols {
let entry = self.get(i, j)?;
sum = sum + entry * v.components[j];
}
result.push(sum);
}
Some(IntervalVector::new(result))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InclusionFunctionType {
Natural,
MeanValue,
Taylor,
Slope,
}
#[derive(Debug, Clone)]
pub struct InclusionFunctionResult {
pub input: Interval,
pub enclosure: Interval,
pub function_type: InclusionFunctionType,
pub overestimation_factor: f64,
}
#[derive(Debug, Clone)]
pub struct RootEnclosure {
pub enclosure: Interval,
pub iterations: usize,
pub existence_verified: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct RootFindingConfig {
pub tolerance: f64,
pub max_iterations: usize,
pub use_newton: bool,
}
impl RootFindingConfig {
pub fn default() -> Self {
RootFindingConfig {
tolerance: 1e-10,
max_iterations: 100,
use_newton: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DualInterval {
pub value: Interval,
pub deriv: Interval,
}
impl DualInterval {
pub fn constant(v: Interval) -> Self {
DualInterval {
value: v,
deriv: Interval::zero(),
}
}
pub fn variable(x: Interval) -> Self {
DualInterval {
value: x,
deriv: Interval::one(),
}
}
pub fn add(self, other: DualInterval) -> DualInterval {
DualInterval {
value: self.value + other.value,
deriv: self.deriv + other.deriv,
}
}
pub fn sub(self, other: DualInterval) -> DualInterval {
DualInterval {
value: self.value - other.value,
deriv: self.deriv - other.deriv,
}
}
pub fn mul(self, other: DualInterval) -> DualInterval {
DualInterval {
value: self.value * other.value,
deriv: self.deriv * other.value + self.value * other.deriv,
}
}
pub fn div(self, other: DualInterval) -> Result<DualInterval, IntervalError> {
if other.value.contains_zero() {
return Err(IntervalError::DivisionByZeroInterval);
}
let v_sq = other.value * other.value;
let denom = interval_div(Interval::one(), v_sq)?;
let deriv = (self.deriv * other.value - self.value * other.deriv) * denom;
let value = interval_div(self.value, other.value)?;
Ok(DualInterval { value, deriv })
}
pub fn sqrt(self) -> Result<DualInterval, IntervalError> {
let sv = self.value.sqrt()?;
let two = Interval::point(2.0);
let denom = interval_div(Interval::one(), two * sv)?;
Ok(DualInterval {
value: sv,
deriv: self.deriv * denom,
})
}
pub fn exp(self) -> DualInterval {
let ev = self.value.exp();
DualInterval {
value: ev,
deriv: self.deriv * ev,
}
}
}
impl fmt::Display for DualInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({} + {}ε)", self.value, self.deriv)
}
}
#[derive(Debug, Clone)]
pub struct IntervalLinearSystem {
pub matrix: IntervalMatrix,
pub rhs: IntervalVector,
}
impl IntervalLinearSystem {
pub fn new(matrix: IntervalMatrix, rhs: IntervalVector) -> Option<Self> {
if matrix.rows != rhs.dim() || matrix.rows != matrix.cols {
return None;
}
Some(IntervalLinearSystem { matrix, rhs })
}
}
#[derive(Debug, Clone)]
pub struct LinearSystemResult {
pub enclosure: IntervalVector,
pub unique_solution_verified: bool,
pub residual_width: f64,
}
#[derive(Debug, Clone)]
pub struct DependencyAnalysis {
pub variable: String,
pub occurrences: usize,
pub input_width: f64,
pub overestimation_ratio: f64,
pub recommended_strategy: String,
}