mirror of
https://git.proxmox.com/git/proxmox-resource-scheduling
synced 2025-08-17 07:10:57 +00:00
290 lines
7.9 KiB
Rust
290 lines
7.9 KiB
Rust
use anyhow::Error;
|
|
|
|
use proxmox_resource_scheduling::topsis::{rank_alternatives, Criteria, Criterion, Matrix};
|
|
|
|
#[test]
|
|
fn test_topsis_single_criterion() -> Result<(), Error> {
|
|
let criteria_pos = Criteria::new([Criterion::new("the one and only".to_string(), 1.0)])?;
|
|
|
|
let criteria_neg = Criteria::new([Criterion::new("the one and only".to_string(), -1.0)])?;
|
|
|
|
let matrix = vec![[0.0]];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_pos)?,
|
|
vec![0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_neg)?,
|
|
vec![0],
|
|
);
|
|
|
|
let matrix = vec![[0.0], [2.0]];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_pos)?,
|
|
vec![1, 0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_neg)?,
|
|
vec![0, 1],
|
|
);
|
|
|
|
let matrix = vec![[1.0], [2.0]];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_pos)?,
|
|
vec![1, 0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_neg)?,
|
|
vec![0, 1],
|
|
);
|
|
|
|
let matrix = vec![[1.0], [2.0], [5.0], [3.0], [4.0]];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_pos)?,
|
|
vec![2, 4, 3, 1, 0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_neg)?,
|
|
vec![0, 1, 3, 4, 2],
|
|
);
|
|
|
|
let matrix = vec![[-2.0], [-5.0], [-4.0], [1.0], [-3.0]];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_pos)?,
|
|
vec![3, 0, 4, 2, 1],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_neg)?,
|
|
vec![1, 2, 4, 0, 3],
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_topsis_two_criteria() -> Result<(), Error> {
|
|
let criteria_max_min = Criteria::new([
|
|
Criterion::new("max".to_string(), 1.0),
|
|
Criterion::new("min".to_string(), -1.0),
|
|
])?;
|
|
let criteria_min_max = Criteria::new([
|
|
Criterion::new("min".to_string(), -1.0),
|
|
Criterion::new("max".to_string(), 1.0),
|
|
])?;
|
|
let criteria_min_min = Criteria::new([
|
|
Criterion::new("min1".to_string(), -1.0),
|
|
Criterion::new("min2".to_string(), -1.0),
|
|
])?;
|
|
let criteria_max_max = Criteria::new([
|
|
Criterion::new("max1".to_string(), 1.0),
|
|
Criterion::new("max2".to_string(), 1.0),
|
|
])?;
|
|
|
|
let matrix = vec![[0.0, 0.0]];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_max_min)?,
|
|
vec![0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_min_max)?,
|
|
vec![0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_min_min)?,
|
|
vec![0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_max_max)?,
|
|
vec![0],
|
|
);
|
|
|
|
#[rustfmt::skip]
|
|
let matrix = vec![
|
|
[0.0, 1.0],
|
|
[1.0, 0.0],
|
|
[1.0, -1.0],
|
|
[1.0, 1.0],
|
|
[0.0, 0.0],
|
|
];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_max_min)?,
|
|
vec![2, 1, 4, 3, 0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_min_max)?,
|
|
vec![0, 3, 4, 1, 2],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_min_min)?,
|
|
vec![2, 4, 1, 0, 3],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_max_max)?,
|
|
vec![3, 0, 1, 4, 2],
|
|
);
|
|
|
|
// This one was cross-checked with https://decision-radar.com/topsis rather than manually.
|
|
#[rustfmt::skip]
|
|
let matrix = vec![
|
|
[7.0, 4.0],
|
|
[1.0, 0.5],
|
|
[-1.0, -1.0],
|
|
[-8.5, 11.5],
|
|
[4.0, 7.0],
|
|
];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_max_min)?,
|
|
vec![0, 1, 4, 2, 3],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_min_max)?,
|
|
vec![3, 2, 4, 1, 0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_min_min)?,
|
|
vec![2, 3, 1, 0, 4],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_max_max)?,
|
|
vec![4, 0, 1, 3, 2],
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_topsis_three_criteria() -> Result<(), Error> {
|
|
let criteria_first = Criteria::new([
|
|
Criterion::new("more".to_string(), 10.0),
|
|
Criterion::new("less".to_string(), 0.2),
|
|
Criterion::new("least".to_string(), 0.1),
|
|
])?;
|
|
let criteria_second = Criteria::new([
|
|
Criterion::new("less".to_string(), 0.2),
|
|
Criterion::new("more".to_string(), 10.0),
|
|
Criterion::new("least".to_string(), 0.1),
|
|
])?;
|
|
let criteria_third = Criteria::new([
|
|
Criterion::new("less".to_string(), 0.2),
|
|
Criterion::new("least".to_string(), 0.1),
|
|
Criterion::new("more".to_string(), 10.0),
|
|
])?;
|
|
let criteria_min = Criteria::new([
|
|
Criterion::new("most".to_string(), -10.0),
|
|
Criterion::new("more".to_string(), -5.0),
|
|
Criterion::new("less".to_string(), 0.1),
|
|
])?;
|
|
|
|
#[rustfmt::skip]
|
|
let matrix = vec![
|
|
[1.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[0.0, 0.0, 1.0],
|
|
];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_first)?,
|
|
vec![0, 1, 2],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_second)?,
|
|
vec![1, 0, 2],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_third)?,
|
|
vec![2, 0, 1],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_min)?,
|
|
vec![2, 1, 0],
|
|
);
|
|
|
|
#[rustfmt::skip]
|
|
let matrix = vec![
|
|
[1.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[0.0, 0.0, 1000.0],
|
|
];
|
|
// normalization ensures that it's still the same as above
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_first)?,
|
|
vec![0, 1, 2],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_second)?,
|
|
vec![1, 0, 2],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_third)?,
|
|
vec![2, 0, 1],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_min)?,
|
|
vec![2, 1, 0],
|
|
);
|
|
|
|
#[rustfmt::skip]
|
|
let matrix = vec![
|
|
[-1.0, 0.0, 0.0],
|
|
[0.0, -1.0, 0.0],
|
|
[0.0, 0.0, 1.0],
|
|
];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_first)?,
|
|
vec![2, 1, 0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_second)?,
|
|
vec![2, 0, 1],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix.clone())?, &criteria_third)?,
|
|
vec![2, 1, 0],
|
|
);
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_min)?,
|
|
vec![0, 1, 2],
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_nan() {
|
|
#[rustfmt::skip]
|
|
let matrix = vec![
|
|
[-1.0, 0.0, 0.0],
|
|
[0.0, -1.0, 0.0],
|
|
[0.0, f64::NAN, 1.0],
|
|
];
|
|
assert!(Matrix::new(matrix).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_zero() -> Result<(), Error> {
|
|
let criteria_zero = Criteria::new([
|
|
Criterion::new("z".to_string(), 0.0),
|
|
Criterion::new("e".to_string(), 0.0),
|
|
Criterion::new("ro".to_string(), 0.0),
|
|
]);
|
|
assert!(criteria_zero.is_err());
|
|
|
|
let criteria_first = Criteria::new([
|
|
Criterion::new("more".to_string(), 10.0),
|
|
Criterion::new("less".to_string(), 0.2),
|
|
Criterion::new("least".to_string(), 0.1),
|
|
])?;
|
|
|
|
#[rustfmt::skip]
|
|
let matrix = vec![
|
|
[0.0, 0.0, 0.0],
|
|
[0.0, 1.0, 0.0],
|
|
[0.0, 0.0, 1.0],
|
|
];
|
|
assert_eq!(
|
|
rank_alternatives(&Matrix::new(matrix)?, &criteria_first)?,
|
|
vec![1, 2, 0],
|
|
);
|
|
|
|
Ok(())
|
|
}
|