mahf/problems/objective/
multi.rs1use std::{cmp::Ordering, fmt::Debug};
4
5use serde::Serialize;
6
7use crate::problems::objective::{IllegalObjective, Objective};
8
9#[derive(Clone, Serialize, PartialEq)]
26pub struct MultiObjective(Vec<f64>);
27
28impl MultiObjective {
29 pub fn is_finite(&self) -> bool {
31 self.0.iter().all(|o| o.is_finite())
32 }
33
34 pub fn value(&self) -> &[f64] {
36 &self.0
37 }
38}
39
40impl Debug for MultiObjective {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{:?}", self.0)
44 }
45}
46
47impl Eq for MultiObjective {}
48
49impl PartialOrd for MultiObjective {
50 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
65 if self == other {
67 return Some(Ordering::Equal);
68 }
69
70 if self.value().len() != other.value().len() {
72 return None;
73 }
74
75 let mut has_better = false;
77 let mut has_worse = false;
78
79 for (own, other) in self.value().iter().zip(other.value()) {
80 if own < other {
81 has_better = true;
82 } else if own > other {
83 has_worse = true;
84 }
85 }
86
87 match (has_better, has_worse) {
88 (true, false) => Some(Ordering::Less),
90 (false, true) => Some(Ordering::Greater),
92 _ => None,
94 }
95 }
96}
97
98impl Objective for MultiObjective {}
99
100impl From<MultiObjective> for Vec<f64> {
101 fn from(objective: MultiObjective) -> Self {
102 objective.0
103 }
104}
105
106impl TryFrom<Vec<f64>> for MultiObjective {
107 type Error = IllegalObjective;
108
109 fn try_from(value: Vec<f64>) -> Result<Self, IllegalObjective> {
113 match value {
114 x if x.iter().any(|o| o.is_nan()) => Err(IllegalObjective::NaN),
115 x if x.iter().any(|o| o.is_infinite() && o.is_sign_negative()) => {
116 Err(IllegalObjective::NegativeInfinity)
117 }
118 _ => Ok(MultiObjective(value)),
119 }
120 }
121}
122
123impl TryFrom<&[f64]> for MultiObjective {
124 type Error = IllegalObjective;
125
126 fn try_from(value: &[f64]) -> Result<Self, IllegalObjective> {
130 match value {
131 x if x.iter().any(|o| o.is_nan()) => Err(IllegalObjective::NaN),
132 x if x.iter().any(|o| o.is_infinite() && o.is_sign_negative()) => {
133 Err(IllegalObjective::NegativeInfinity)
134 }
135 _ => Ok(MultiObjective(value.into())),
136 }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use std::cmp::Ordering;
143
144 use super::MultiObjective;
145
146 #[test]
147 fn is_finite_returns_true_for_all_finite() {
148 assert!(MultiObjective(vec![0., 0.]).is_finite());
149 assert!(MultiObjective(vec![-1., 1.]).is_finite());
150 }
151
152 #[test]
153 fn is_finite_returns_false_for_any_infinite() {
154 assert!(!MultiObjective(vec![0., f64::INFINITY]).is_finite());
155 assert!(!MultiObjective(vec![f64::INFINITY, f64::INFINITY]).is_finite());
156 }
157
158 #[test]
159 fn partial_ord_implements_pareto_domination() {
160 let a = MultiObjective(vec![0., 0.]);
161 let b = MultiObjective(vec![1., 1.]);
162 let c = MultiObjective(vec![1., 0.]);
163 let d = MultiObjective(vec![0., 1.]);
164
165 assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal));
167 assert_eq!(b.partial_cmp(&b), Some(Ordering::Equal));
168 assert_eq!(c.partial_cmp(&c), Some(Ordering::Equal));
169 assert_eq!(d.partial_cmp(&d), Some(Ordering::Equal));
170
171 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
173 assert_eq!(a.partial_cmp(&c), Some(Ordering::Less));
174 assert_eq!(a.partial_cmp(&d), Some(Ordering::Less));
175
176 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
178 assert_eq!(b.partial_cmp(&c), Some(Ordering::Greater));
179 assert_eq!(b.partial_cmp(&d), Some(Ordering::Greater));
180
181 assert_eq!(c.partial_cmp(&a), Some(Ordering::Greater));
183 assert_eq!(c.partial_cmp(&b), Some(Ordering::Less));
184 assert_eq!(c.partial_cmp(&d), None);
185
186 assert_eq!(d.partial_cmp(&a), Some(Ordering::Greater));
188 assert_eq!(d.partial_cmp(&b), Some(Ordering::Less));
189 assert_eq!(d.partial_cmp(&c), None);
190 }
191
192 #[test]
193 fn try_from_invalid_is_err() {
194 assert!(MultiObjective::try_from(vec![f64::NAN, 0.]).is_err());
195 assert!(MultiObjective::try_from(vec![f64::NAN, f64::NEG_INFINITY]).is_err());
196
197 assert!(MultiObjective::try_from(vec![f64::NEG_INFINITY, 0.]).is_err());
198 assert!(MultiObjective::try_from(vec![f64::NEG_INFINITY, f64::NEG_INFINITY]).is_err());
199 }
200}