Skip to main content

radiate_core/codecs/
float.rs

1use radiate_utils::Float;
2
3use super::Codec;
4use crate::genome::Gene;
5use crate::genome::genotype::Genotype;
6use crate::{Chromosome, FloatChromosome};
7use std::ops::Range;
8
9/// A [Codec] for a [Genotype] of `FloatGenes`. The `encode` function creates a [Genotype] with `num_chromosomes` chromosomes
10/// and `num_genes` genes per chromosome. The `decode` function creates a `Vec<Vec<f32>>` from the [Genotype] where the inner `Vec`
11/// contains the alleles of the `FloatGenes` in the chromosome - the `f32` values.
12///
13/// The lower and upper bounds of the `FloatGenes` can be set with the `with_bounds` function.
14/// The default bounds are equal to `min` and `max` values.
15#[derive(Clone)]
16pub struct FloatCodec<F: Float, T = F> {
17    num_chromosomes: usize,
18    num_genes: usize,
19    value_range: Range<F>,
20    bounds: Range<F>,
21    shapes: Option<Vec<(usize, usize)>>,
22    _marker: std::marker::PhantomData<T>,
23}
24
25impl<F: Float, T> FloatCodec<F, T> {
26    /// Set the bounds of the `FloatGenes` in the [Genotype]. The default bounds
27    /// are equal to the min and max values.
28    pub fn with_bounds(mut self, range: Range<F>) -> Self {
29        self.bounds = range;
30        self
31    }
32
33    /// Every impl of `Codec` uses the same encode function for the `FloatCodec`, just with a few
34    /// different parameters (e.g. `num_chromosomes` and `num_genes`). So, we can just use
35    /// the same function for all of them.
36    #[inline]
37    fn common_encode(&self) -> Genotype<FloatChromosome<F>> {
38        if let Some(shapes) = &self.shapes {
39            Genotype::from(
40                shapes
41                    .iter()
42                    .map(|(rows, cols)| {
43                        FloatChromosome::from((
44                            rows * cols,
45                            self.value_range.clone(),
46                            self.bounds.clone(),
47                        ))
48                    })
49                    .collect::<Vec<FloatChromosome<F>>>(),
50            )
51        } else {
52            Genotype::from(
53                (0..self.num_chromosomes)
54                    .map(|_| {
55                        FloatChromosome::from((
56                            self.num_genes,
57                            self.value_range.clone(),
58                            self.bounds.clone(),
59                        ))
60                    })
61                    .collect::<Vec<FloatChromosome<F>>>(),
62            )
63        }
64    }
65}
66
67impl<F: Float> FloatCodec<F, Vec<Vec<Vec<F>>>> {
68    pub fn tensor(shapes: Vec<(usize, usize)>, range: Range<F>) -> Self {
69        FloatCodec {
70            num_chromosomes: shapes.len(),
71            num_genes: 0,
72            value_range: range.clone(),
73            bounds: range,
74            shapes: Some(shapes),
75            _marker: std::marker::PhantomData,
76        }
77    }
78}
79
80impl<F: Float> FloatCodec<F, Vec<Vec<F>>> {
81    /// Create a new `FloatCodec` with the given number of chromosomes, genes, min, and max values.
82    /// The f_32 values for each `FloatGene` will be randomly generated between the min and max values.
83    pub fn matrix(rows: usize, cols: usize, range: Range<F>) -> Self {
84        FloatCodec {
85            num_chromosomes: rows,
86            num_genes: cols,
87            value_range: range.clone(),
88            bounds: range,
89            shapes: None,
90            _marker: std::marker::PhantomData,
91        }
92    }
93}
94
95impl<F: Float> FloatCodec<F, Vec<F>> {
96    /// Create a new `FloatCodec` with the given number of chromosomes, genes, min, and max values.
97    /// The f_32 values for each `FloatGene` will be randomly generated between the min and max values.
98    pub fn vector(count: usize, range: Range<F>) -> Self {
99        FloatCodec {
100            num_chromosomes: 1,
101            num_genes: count,
102            value_range: range.clone(),
103            bounds: range,
104            shapes: None,
105            _marker: std::marker::PhantomData,
106        }
107    }
108}
109
110impl FloatCodec<f32> {
111    /// Create a new `FloatCodec` with the given number of chromosomes, genes, min, and max values.
112    /// The f_32 values for each `FloatGene` will be randomly generated between the min and max values.
113    pub fn scalar(range: Range<f32>) -> Self {
114        FloatCodec {
115            num_chromosomes: 1,
116            num_genes: 1,
117            value_range: range.clone(),
118            bounds: range,
119            shapes: None,
120            _marker: std::marker::PhantomData,
121        }
122    }
123}
124
125/// Implement the [Codec] for a `FloatCodec` with a `Vec<Vec<Vec<f32>>>` type.
126/// Unlike the other impls, this will decode to a 3D tensor of `f32` values.
127///
128/// # Example
129/// ``` rust
130/// use radiate_core::*;
131///
132/// // Create a new FloatCodec with 2 layers:
133/// // - First layer: 2 rows and 3 columns
134/// // - Second layer: 3 rows and 4 columns
135/// let codec = FloatCodec::tensor(vec![(2, 3), (3, 4)], 0.0_f32..1.0_f32);
136/// let genotype: Genotype<FloatChromosome<f32>> = codec.encode();
137/// let decoded: Vec<Vec<Vec<f32>>> = codec.decode(&genotype);
138///
139/// assert_eq!(decoded.len(), 2);
140/// assert_eq!(decoded[0].len(), 2);
141/// assert_eq!(decoded[0][0].len(), 3);
142/// assert_eq!(decoded[1].len(), 3);
143/// assert_eq!(decoded[1][0].len(), 4);
144/// ```
145impl<F: Float> Codec<FloatChromosome<F>, Vec<Vec<Vec<F>>>> for FloatCodec<F, Vec<Vec<Vec<F>>>> {
146    #[inline]
147    fn encode(&self) -> Genotype<FloatChromosome<F>> {
148        self.common_encode()
149    }
150
151    #[inline]
152    fn decode(&self, genotype: &Genotype<FloatChromosome<F>>) -> Vec<Vec<Vec<F>>> {
153        if let Some(shapes) = &self.shapes {
154            let mut layers = Vec::new();
155            for (i, chromosome) in genotype.iter().enumerate() {
156                layers.push(
157                    chromosome
158                        .as_slice()
159                        .chunks(shapes[i].1)
160                        .map(|chunk| chunk.iter().map(|gene| *gene.allele()).collect::<Vec<F>>())
161                        .collect::<Vec<Vec<F>>>(),
162                );
163            }
164
165            layers
166        } else {
167            vec![
168                genotype
169                    .iter()
170                    .map(|chromosome| {
171                        chromosome
172                            .iter()
173                            .map(|gene| *gene.allele())
174                            .collect::<Vec<F>>()
175                    })
176                    .collect::<Vec<Vec<F>>>(),
177            ]
178        }
179    }
180}
181
182/// Implement the `Codec` trait for a `FloatCodec` with a `Vec<Vec<f32>>` type.
183/// This will decode to a matrix of `f32` values.
184/// The `encode` function creates a [Genotype] with `num_chromosomes` chromosomes
185/// and `num_genes` genes per chromosome.
186///
187/// * Example:
188/// ``` rust
189/// use radiate_core::*;
190///
191/// // Create a new FloatCodec with 3 chromosomes and 4 genes
192/// // per chromosome - a 3x4 matrix of f32 values.
193/// let codec = FloatCodec::matrix(3, 4, 0.0_f32..1.0_f32);
194/// let genotype: Genotype<FloatChromosome<f32>> = codec.encode();
195/// let decoded: Vec<Vec<f32>> = codec.decode(&genotype);
196///
197/// assert_eq!(decoded.len(), 3);
198/// assert_eq!(decoded[0].len(), 4);
199/// ```
200impl<F: Float> Codec<FloatChromosome<F>, Vec<Vec<F>>> for FloatCodec<F, Vec<Vec<F>>> {
201    #[inline]
202    fn encode(&self) -> Genotype<FloatChromosome<F>> {
203        self.common_encode()
204    }
205
206    #[inline]
207    fn decode(&self, genotype: &Genotype<FloatChromosome<F>>) -> Vec<Vec<F>> {
208        genotype
209            .iter()
210            .map(|chromosome| {
211                chromosome
212                    .iter()
213                    .map(|gene| *gene.allele())
214                    .collect::<Vec<F>>()
215            })
216            .collect::<Vec<Vec<F>>>()
217    }
218}
219
220/// Implement the `Codec` trait for a `FloatCodec` with a `Vec<f32>` type.
221/// This will decode to a vector of `f32` values.
222/// The `encode` function creates a [Genotype] with a single chromosomes
223/// and `num_genes` genes per chromosome.
224///
225/// # Example
226/// ``` rust
227/// use radiate_core::*;
228///
229/// // Create a new FloatCodec with 3 genes
230/// // per chromosome - a vector with 3 f32 values.
231/// let codec = FloatCodec::vector(3, 0.0_f32..1.0_f32);
232/// let genotype: Genotype<FloatChromosome<f32>> = codec.encode();
233/// let decoded: Vec<f32> = codec.decode(&genotype);
234///
235/// assert_eq!(decoded.len(), 3);
236/// ```
237impl<F: Float> Codec<FloatChromosome<F>, Vec<F>> for FloatCodec<F, Vec<F>> {
238    #[inline]
239    fn encode(&self) -> Genotype<FloatChromosome<F>> {
240        self.common_encode()
241    }
242
243    #[inline]
244    fn decode(&self, genotype: &Genotype<FloatChromosome<F>>) -> Vec<F> {
245        genotype
246            .iter()
247            .flat_map(|chromosome| {
248                chromosome
249                    .iter()
250                    .map(|gene| *gene.allele())
251                    .collect::<Vec<F>>()
252            })
253            .collect::<Vec<F>>()
254    }
255}
256
257/// Implement the `Codec` trait for a `FloatCodec` with a `f32` type.
258/// This will decode to a single `f32` value.
259/// The `encode` function creates a [Genotype] with a single chromosomes
260/// and a single gene per chromosome.
261///
262/// # Example
263/// ``` rust
264/// use radiate_core::*;
265///
266/// // Create a new FloatCodec with a single gene
267/// // per chromosome - a single f32 value.
268/// let codec = FloatCodec::scalar(0.0_f32..1.0_f32);
269/// let genotype: Genotype<FloatChromosome<f32>> = codec.encode();
270/// let decoded: f32 = codec.decode(&genotype);
271/// ```
272impl<F: Float> Codec<FloatChromosome<F>, F> for FloatCodec<F, F> {
273    #[inline]
274    fn encode(&self) -> Genotype<FloatChromosome<F>> {
275        self.common_encode()
276    }
277
278    #[inline]
279    fn decode(&self, genotype: &Genotype<FloatChromosome<F>>) -> F {
280        genotype
281            .iter()
282            .flat_map(|chromosome| {
283                chromosome
284                    .iter()
285                    .map(|gene| *gene.allele())
286                    .collect::<Vec<F>>()
287            })
288            .next()
289            .unwrap_or_default()
290    }
291}
292
293/// Implement the [Codec] trait for a Vec for [FloatChromosome].
294/// This is effectively the same as creating a [FloatCodec] matrix
295///
296/// # Example
297/// ``` rust
298/// use radiate_core::*;
299///
300/// let codec = vec![
301///     FloatChromosome::from((3, 0.0_f32..1.0_f32)),
302///     FloatChromosome::from((4, 0.0_f32..1.0_f32)),
303/// ];
304///
305/// let genotype: Genotype<FloatChromosome<f32>> = codec.encode();
306/// let decoded: Vec<Vec<f32>> = codec.decode(&genotype);
307///
308/// assert_eq!(decoded.len(), 2);
309/// assert_eq!(decoded[0].len(), 3);
310/// assert_eq!(decoded[1].len(), 4);
311/// ```
312impl<F: Float> Codec<FloatChromosome<F>, Vec<Vec<F>>> for Vec<FloatChromosome<F>> {
313    #[inline]
314    fn encode(&self) -> Genotype<FloatChromosome<F>> {
315        Genotype::from(
316            self.iter()
317                .map(|chromosome| {
318                    chromosome
319                        .iter()
320                        .map(|gene| gene.new_instance())
321                        .collect::<FloatChromosome<F>>()
322                })
323                .collect::<Vec<FloatChromosome<F>>>(),
324        )
325    }
326
327    #[inline]
328    fn decode(&self, genotype: &Genotype<FloatChromosome<F>>) -> Vec<Vec<F>> {
329        genotype
330            .iter()
331            .map(|chromosome| {
332                chromosome
333                    .iter()
334                    .map(|gene| *gene.allele())
335                    .collect::<Vec<F>>()
336            })
337            .collect::<Vec<Vec<F>>>()
338    }
339}
340
341/// Implement the [Codec] trait for a single [FloatChromosome].
342/// This is effectively the same as creating a [FloatCodec] vector
343///
344/// # Example
345/// ``` rust
346/// use radiate_core::*;
347///
348/// let codec = FloatChromosome::from((3, 0.0_f32..1.0_f32));
349/// let genotype: Genotype<FloatChromosome<f32>> = codec.encode();
350/// let decoded: Vec<f32> = codec.decode(&genotype);
351///
352/// assert_eq!(decoded.len(), 3);
353/// ```
354impl<F: Float> Codec<FloatChromosome<F>, Vec<F>> for FloatChromosome<F> {
355    #[inline]
356    fn encode(&self) -> Genotype<FloatChromosome<F>> {
357        Genotype::from(
358            self.iter()
359                .map(|gene| gene.new_instance())
360                .collect::<FloatChromosome<F>>(),
361        )
362    }
363
364    #[inline]
365    fn decode(&self, genotype: &Genotype<FloatChromosome<F>>) -> Vec<F> {
366        genotype
367            .iter()
368            .flat_map(|chromosome| {
369                chromosome
370                    .iter()
371                    .map(|gene| *gene.allele())
372                    .collect::<Vec<F>>()
373            })
374            .collect::<Vec<F>>()
375    }
376}