radiate_core/objectives/
optimize.rs1use super::Scored;
2
3#[derive(Clone, Debug, PartialEq)]
4pub enum Objective {
5 Single(Optimize),
6 Multi(Vec<Optimize>),
7}
8
9impl Objective {
10 pub fn is_single(&self) -> bool {
11 matches!(self, Objective::Single(_))
12 }
13
14 pub fn is_multi(&self) -> bool {
15 matches!(self, Objective::Multi(_))
16 }
17
18 pub fn cmp<T>(&self, a: &T, b: &T) -> std::cmp::Ordering
19 where
20 T: PartialOrd,
21 {
22 match self {
23 Objective::Single(opt) => {
24 if opt.is_better(a, b) {
25 std::cmp::Ordering::Less
26 } else if opt.is_better(b, a) {
27 std::cmp::Ordering::Greater
28 } else {
29 std::cmp::Ordering::Equal
30 }
31 }
32 Objective::Multi(opts) => {
33 for &opt in opts {
34 if opt.is_better(a, b) {
35 return std::cmp::Ordering::Less;
36 } else if opt.is_better(b, a) {
37 return std::cmp::Ordering::Greater;
38 }
39 }
40 std::cmp::Ordering::Equal
41 }
42 }
43 }
44
45 pub fn sort<T: AsMut<[K]>, K: Scored + PartialOrd>(&self, population: &mut T) {
46 match self {
47 Objective::Single(opt) => opt.sort(population),
48 Objective::Multi(_) => population.as_mut().sort_by(|one, two| {
49 if let (Some(score_one), Some(score_two)) = (one.score(), two.score()) {
50 self.dominance_cmp(score_one.as_ref(), score_two.as_ref())
51 } else {
52 std::cmp::Ordering::Equal
53 }
54 }),
55 }
56 }
57
58 fn dominance_cmp<T>(&self, a: &[T], b: &[T]) -> std::cmp::Ordering
59 where
60 T: PartialOrd,
61 {
62 match self {
63 Objective::Single(opt) => {
64 if opt.is_better(&a[0], &b[0]) {
65 std::cmp::Ordering::Less
66 } else if opt.is_better(&b[0], &a[0]) {
67 std::cmp::Ordering::Greater
68 } else {
69 std::cmp::Ordering::Equal
70 }
71 }
72 Objective::Multi(opts) => {
73 for ((a, b), opt) in a.iter().zip(b.iter()).zip(opts) {
74 if opt.is_better(a, b) {
75 return std::cmp::Ordering::Less;
76 } else if opt.is_better(b, a) {
77 return std::cmp::Ordering::Greater;
78 }
79 }
80 std::cmp::Ordering::Equal
81 }
82 }
83 }
84
85 pub fn is_better<T>(&self, a: &T, b: &T) -> bool
86 where
87 T: PartialOrd,
88 {
89 match self {
90 Objective::Single(opt) => opt.is_better(a, b),
91 Objective::Multi(opts) => {
92 for &opt in opts {
93 if !opt.is_better(a, b) {
94 return false;
95 }
96 }
97 true
98 }
99 }
100 }
101}
102
103impl AsRef<[Optimize]> for Objective {
104 fn as_ref(&self) -> &[Optimize] {
105 match self {
106 Objective::Single(opt) => std::slice::from_ref(opt),
107 Objective::Multi(opts) => opts.as_slice(),
108 }
109 }
110}
111
112#[derive(Clone, Copy, Debug, PartialEq)]
113pub enum Optimize {
114 Minimize,
115 Maximize,
116}
117
118impl Optimize {
119 pub fn sort<T: AsMut<[K]>, K: PartialOrd>(&self, population: &mut T) {
120 match self {
121 Optimize::Minimize => population
122 .as_mut()
123 .sort_by(|a, b| a.partial_cmp(b).unwrap()),
124 Optimize::Maximize => population
125 .as_mut()
126 .sort_by(|a, b| b.partial_cmp(a).unwrap()),
127 }
128 }
129
130 pub fn is_better<T>(&self, a: &T, b: &T) -> bool
131 where
132 T: PartialOrd,
133 {
134 match self {
135 Optimize::Minimize => a < b,
136 Optimize::Maximize => a > b,
137 }
138 }
139}
140
141impl From<Vec<String>> for Objective {
142 fn from(optimize: Vec<String>) -> Self {
143 if optimize.len() == 1 {
144 match optimize[0].as_str() {
145 "min" => Objective::Single(Optimize::Minimize),
146 "max" => Objective::Single(Optimize::Maximize),
147 _ => panic!("Invalid optimization direction: {}", optimize[0]),
148 }
149 } else {
150 let opts = optimize
151 .into_iter()
152 .map(|s| match s.as_str() {
153 "min" => Optimize::Minimize,
154 "max" => Optimize::Maximize,
155 _ => panic!("Invalid optimization direction: {}", s),
156 })
157 .collect();
158 Objective::Multi(opts)
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_optimize_is_better() {
169 assert!(Optimize::Minimize.is_better(&1, &2));
170 assert!(!Optimize::Minimize.is_better(&2, &1));
171 assert!(Optimize::Maximize.is_better(&2, &1));
172 assert!(!Optimize::Maximize.is_better(&1, &2));
173 }
174}