radiate_core/objectives/
score.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use std::fmt::Debug;
4use std::hash::Hash;
5use std::iter::Sum;
6use std::ops::{Add, Div, Mul, Sub};
7use std::sync::Arc;
8
9pub trait Scored {
10    fn score(&self) -> Option<&Score>;
11}
12
13/// A score is a value that can be used to compare the fitness of two individuals and represents
14/// the 'fitness' of an individual within the genetic algorithm.
15/// The score can be a single value or multiple values, depending on the problem being solved.
16/// For ease of use the `Score` struct provides methods
17/// to convert the score to a single value, an integer, a string, or a vector of `f32` values.
18///
19/// Note: The reason it is a Vec is for multi-objective optimization problems. This allows for multiple
20/// fitness values to be returned from the fitness function.
21#[derive(Clone, PartialEq, Default)]
22#[repr(transparent)]
23pub struct Score {
24    pub values: Arc<[f32]>,
25}
26
27impl Score {
28    pub fn from_vec(values: Vec<f32>) -> Self {
29        for value in &values {
30            if value.is_nan() {
31                panic!("Score value cannot be NaN")
32            }
33        }
34
35        Score {
36            values: Arc::from(values),
37        }
38    }
39
40    pub fn as_f32(&self) -> f32 {
41        self.values.get(0).cloned().unwrap_or(f32::NAN)
42    }
43
44    pub fn as_i32(&self) -> i32 {
45        self.values[0] as i32
46    }
47
48    pub fn as_string(&self) -> String {
49        self.values[0].to_string()
50    }
51
52    pub fn as_usize(&self) -> usize {
53        self.values[0] as usize
54    }
55}
56
57impl AsRef<[f32]> for Score {
58    fn as_ref(&self) -> &[f32] {
59        &self.values
60    }
61}
62
63impl PartialOrd for Score {
64    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
65        self.values.partial_cmp(&other.values)
66    }
67}
68
69impl Debug for Score {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        write!(f, "{:?}", self.values)
72    }
73}
74
75impl Hash for Score {
76    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
77        let mut hash: usize = 0;
78
79        for value in self.values.iter() {
80            let value_hash = value.to_bits();
81            hash = hash.wrapping_add(value_hash as usize);
82        }
83
84        hash.hash(state);
85    }
86}
87
88impl From<f32> for Score {
89    fn from(value: f32) -> Self {
90        if value.is_nan() {
91            panic!("Score value cannot be NaN")
92        }
93
94        Score {
95            values: Arc::from(vec![value]),
96        }
97    }
98}
99
100impl From<i32> for Score {
101    fn from(value: i32) -> Self {
102        Score {
103            values: Arc::from(vec![value as f32]),
104        }
105    }
106}
107
108impl From<usize> for Score {
109    fn from(value: usize) -> Self {
110        Score {
111            values: Arc::from(vec![value as f32]),
112        }
113    }
114}
115
116impl From<String> for Score {
117    fn from(value: String) -> Self {
118        Score {
119            values: Arc::from(vec![
120                value.parse::<f32>().expect("Failed to parse string to f32"),
121            ]),
122        }
123    }
124}
125
126impl From<&str> for Score {
127    fn from(value: &str) -> Self {
128        Score {
129            values: Arc::from(vec![
130                value.parse::<f32>().expect("Failed to parse string to f32"),
131            ]),
132        }
133    }
134}
135
136impl From<Vec<f32>> for Score {
137    fn from(value: Vec<f32>) -> Self {
138        Score::from_vec(value)
139    }
140}
141
142impl From<Vec<i32>> for Score {
143    fn from(value: Vec<i32>) -> Self {
144        Score::from_vec(value.into_iter().map(|v| v as f32).collect())
145    }
146}
147
148impl From<Vec<usize>> for Score {
149    fn from(value: Vec<usize>) -> Self {
150        Score::from_vec(value.into_iter().map(|v| v as f32).collect())
151    }
152}
153
154impl From<Vec<String>> for Score {
155    fn from(value: Vec<String>) -> Self {
156        Score::from_vec(
157            value
158                .into_iter()
159                .map(|v| v.parse::<f32>().unwrap())
160                .collect(),
161        )
162    }
163}
164
165impl From<Vec<&str>> for Score {
166    fn from(value: Vec<&str>) -> Self {
167        Score::from_vec(
168            value
169                .into_iter()
170                .map(|v| v.parse::<f32>().unwrap())
171                .collect(),
172        )
173    }
174}
175
176impl Add for Score {
177    type Output = Self;
178
179    fn add(self, other: Self) -> Self {
180        if self.values.is_empty() {
181            return other;
182        }
183        let values = self
184            .values
185            .iter()
186            .zip(other.values.iter())
187            .map(|(a, b)| a + b)
188            .collect();
189
190        Score { values }
191    }
192}
193
194impl Add<f32> for Score {
195    type Output = Self;
196
197    fn add(self, other: f32) -> Self {
198        if self.values.is_empty() {
199            return Score::from(other);
200        }
201
202        let values = self.values.iter().map(|a| a + other).collect();
203
204        Score { values }
205    }
206}
207
208impl Sub for Score {
209    type Output = Self;
210
211    fn sub(self, other: Self) -> Self {
212        if self.values.is_empty() {
213            return other;
214        }
215
216        let values = self
217            .values
218            .iter()
219            .zip(other.values.iter())
220            .map(|(a, b)| a - b)
221            .collect();
222
223        Score { values }
224    }
225}
226
227impl Sub<f32> for Score {
228    type Output = Self;
229
230    fn sub(self, other: f32) -> Self {
231        if self.values.is_empty() {
232            return Score::from(-other);
233        }
234
235        let values = self.values.iter().map(|a| a - other).collect();
236
237        Score { values }
238    }
239}
240
241impl Mul for Score {
242    type Output = Self;
243
244    fn mul(self, other: Self) -> Self {
245        if self.values.is_empty() {
246            return other;
247        }
248
249        let values = self
250            .values
251            .iter()
252            .zip(other.values.iter())
253            .map(|(a, b)| a * b)
254            .collect();
255
256        Score { values }
257    }
258}
259
260impl Mul<f32> for Score {
261    type Output = Self;
262
263    fn mul(self, other: f32) -> Self {
264        if self.values.is_empty() {
265            return Score::from(other);
266        }
267
268        let values = self.values.iter().map(|a| a * other).collect();
269
270        Score { values }
271    }
272}
273
274impl Div for Score {
275    type Output = Self;
276
277    fn div(self, other: Self) -> Self {
278        if self.values.is_empty() {
279            return other;
280        }
281
282        let values = self
283            .values
284            .iter()
285            .zip(other.values.iter())
286            .map(|(a, b)| a / b)
287            .collect();
288
289        Score { values }
290    }
291}
292
293impl Div<f32> for Score {
294    type Output = Self;
295
296    fn div(self, other: f32) -> Self {
297        if self.values.is_empty() {
298            return Score::from(other);
299        }
300
301        let values = self.values.iter().map(|a| a / other).collect();
302
303        Score { values }
304    }
305}
306
307impl Sum for Score {
308    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
309        let mut values = vec![];
310
311        for score in iter {
312            for (i, value) in score.values.iter().enumerate() {
313                if values.len() <= i {
314                    values.push(*value);
315                } else {
316                    values[i] += value;
317                }
318            }
319        }
320
321        Score {
322            values: Arc::from(values),
323        }
324    }
325}
326
327impl<'a> Sum<&'a Score> for Score {
328    fn sum<I: Iterator<Item = &'a Score>>(iter: I) -> Self {
329        let mut values = vec![];
330
331        for score in iter {
332            for (i, value) in score.values.iter().enumerate() {
333                if values.len() <= i {
334                    values.push(*value);
335                } else {
336                    values[i] += value;
337                }
338            }
339        }
340
341        Score {
342            values: Arc::from(values),
343        }
344    }
345}
346
347#[cfg(feature = "serde")]
348impl Serialize for Score {
349    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
350    where
351        S: serde::Serializer,
352    {
353        self.values.as_ref().serialize(serializer)
354    }
355}
356
357#[cfg(feature = "serde")]
358impl<'de> Deserialize<'de> for Score {
359    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
360    where
361        D: serde::Deserializer<'de>,
362    {
363        let vec = Vec::<f32>::deserialize(deserializer)?;
364        for value in &vec {
365            if value.is_nan() {
366                return Err(serde::de::Error::custom("Score value cannot be NaN"));
367            }
368        }
369
370        Ok(Score {
371            values: Arc::from(vec),
372        })
373    }
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379
380    #[test]
381    fn test_score_from_vec() {
382        let score = Score::from(vec![1.0, 2.0, 3.0]);
383        assert_eq!(score.values.len(), 3);
384    }
385
386    #[test]
387    fn test_score_from_usize() {
388        let score = Score::from(3);
389        assert_eq!(score.values.len(), 1);
390        assert_eq!(score.as_f32(), 3.0);
391        assert_eq!(score.as_i32(), 3);
392    }
393
394    #[test]
395    fn test_score_from_f32() {
396        let score = Score::from(1.0);
397        assert_eq!(score.as_f32(), 1.0);
398        assert_eq!(score.as_i32(), 1)
399    }
400
401    #[test]
402    fn test_score_from_i32() {
403        let score = Score::from(-5);
404        assert_eq!(score.as_f32(), -5.0);
405        assert_eq!(score.as_i32(), -5);
406    }
407
408    #[test]
409    #[cfg(feature = "serde")]
410    fn test_score_can_serialize() {
411        let score = Score::from(vec![1.0, 2.0, 3.0]);
412        let serialized = serde_json::to_string(&score).expect("Failed to serialize Score");
413        let deserialized: Score =
414            serde_json::from_str(&serialized).expect("Failed to deserialize Score");
415        assert_eq!(score, deserialized);
416    }
417}