use super::*;
use crate::ATol;
use ::approx::AbsDiffEq;
impl AbsDiffEq for DecisionVariable {
type Epsilon = ATol;
fn default_epsilon() -> Self::Epsilon {
crate::ATol::default()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
if !self.bound.abs_diff_eq(&other.bound, epsilon) {
return false;
}
if self.kind == other.kind {
return true;
}
if let Some(lower) = self.bound.is_point(epsilon) {
if lower.abs() < epsilon {
return true;
}
return same_kind_class(
self.kind,
other.kind,
&[Kind::Binary, Kind::Integer, Kind::Continuous],
&[Kind::SemiContinuous, Kind::SemiInteger],
);
}
if self.bound.contains(0.0, epsilon) {
same_kind_class(
self.kind,
other.kind,
&[Kind::Binary, Kind::Integer, Kind::SemiInteger],
&[Kind::Continuous, Kind::SemiContinuous],
)
} else {
matches!(
(self.kind, other.kind),
(Kind::Binary, Kind::Integer) | (Kind::Integer, Kind::Binary)
)
}
}
}
fn same_kind_class(a: Kind, b: Kind, class1: &[Kind], class2: &[Kind]) -> bool {
(class1.contains(&a) && class1.contains(&b)) || (class2.contains(&a) && class2.contains(&b))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Bound, DecisionVariable};
use ::approx::{assert_abs_diff_eq, assert_abs_diff_ne};
#[test]
fn test_equal_rows_are_equal_independent_of_table_key() {
let var1 = DecisionVariable::new(
Kind::Continuous,
Bound::new(0.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let var2 = DecisionVariable::new(
Kind::Continuous,
Bound::new(0.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(var1, var2);
}
#[test]
fn test_different_bounds_not_equal() {
let var1 = DecisionVariable::new(
Kind::Continuous,
Bound::new(0.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let var2 = DecisionVariable::new(
Kind::Continuous,
Bound::new(0.0, 2.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_ne!(var1, var2);
}
#[test]
fn test_same_kind_and_bound_equal() {
let var1 = DecisionVariable::new(
Kind::Integer,
Bound::new(0.0, 10.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let var2 = DecisionVariable::new(
Kind::Integer,
Bound::new(0.0, 10.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(var1, var2);
}
#[test]
fn test_point_bound_continuous_integer_equal() {
let var1 = DecisionVariable::new(
Kind::Continuous,
Bound::new(5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let var2 = DecisionVariable::new(
Kind::Integer,
Bound::new(5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(var1, var2);
}
#[test]
fn test_point_bound_zero_all_kinds_equal() {
let continuous = DecisionVariable::new(
Kind::Continuous,
Bound::new(0.0, 0.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let integer = DecisionVariable::new(
Kind::Integer,
Bound::new(0.0, 0.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let binary = DecisionVariable::new(
Kind::Binary,
Bound::new(0.0, 0.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let semi_continuous = DecisionVariable::new(
Kind::SemiContinuous,
Bound::new(0.0, 0.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let semi_integer = DecisionVariable::new(
Kind::SemiInteger,
Bound::new(0.0, 0.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(continuous, integer);
assert_abs_diff_eq!(continuous, binary);
assert_abs_diff_eq!(continuous, semi_continuous);
assert_abs_diff_eq!(continuous, semi_integer);
assert_abs_diff_eq!(integer, semi_integer);
}
#[test]
fn test_point_bound_nonzero_semi_not_equal() {
let continuous = DecisionVariable::new(
Kind::Continuous,
Bound::new(5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let semi_continuous = DecisionVariable::new(
Kind::SemiContinuous,
Bound::new(5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_ne!(continuous, semi_continuous);
}
#[test]
fn test_bound_contains_zero_semi_equal() {
let integer = DecisionVariable::new(
Kind::Integer,
Bound::new(-5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let semi_integer = DecisionVariable::new(
Kind::SemiInteger,
Bound::new(-5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let continuous = DecisionVariable::new(
Kind::Continuous,
Bound::new(-5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let semi_continuous = DecisionVariable::new(
Kind::SemiContinuous,
Bound::new(-5.0, 5.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(integer, semi_integer);
assert_abs_diff_eq!(continuous, semi_continuous);
let binary = DecisionVariable::new(
Kind::Binary,
Bound::new(0.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let integer_01 = DecisionVariable::new(
Kind::Integer,
Bound::new(0.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(binary, integer_01);
}
#[test]
fn test_point_bound_nonzero_all_basic_kinds_equal() {
let binary = DecisionVariable::new(
Kind::Binary,
Bound::new(1.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let integer = DecisionVariable::new(
Kind::Integer,
Bound::new(1.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
let continuous = DecisionVariable::new(
Kind::Continuous,
Bound::new(1.0, 1.0).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(binary, integer);
assert_abs_diff_eq!(binary, continuous);
assert_abs_diff_eq!(integer, continuous);
}
#[test]
fn test_tolerance_in_point_bound() {
let var1 = DecisionVariable::new(
Kind::Continuous,
Bound::new(1.0, 1.0 + 1e-10).unwrap(),
crate::ATol::default(),
)
.unwrap();
let var2 = DecisionVariable::new(
Kind::Integer,
Bound::new(1.0, 1.0 + 1e-10).unwrap(),
crate::ATol::default(),
)
.unwrap();
assert_abs_diff_eq!(var1, var2);
}
}