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