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