Skip to main content

radiate_core/codecs/
float.rs

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