rlevo_evolution/
population.rs1use std::marker::PhantomData;
12
13use burn::tensor::{backend::Backend, Int, Tensor};
14
15use crate::genome::{Binary, Integer, Real};
16
17#[derive(Debug, Clone)]
32pub struct Population<B: Backend, K> {
33 pop_size: usize,
34 genome_dim: usize,
35 _kind: PhantomData<K>,
36 tensor_real: Option<Tensor<B, 2>>,
37 tensor_int: Option<Tensor<B, 2, Int>>,
38}
39
40impl<B: Backend, K> Population<B, K> {
41 #[must_use]
43 pub fn pop_size(&self) -> usize {
44 self.pop_size
45 }
46
47 #[must_use]
49 pub fn genome_dim(&self) -> usize {
50 self.genome_dim
51 }
52}
53
54impl<B: Backend> Population<B, Real> {
55 #[must_use]
61 pub fn new_real(tensor: Tensor<B, 2>) -> Self {
62 let dims = tensor.shape().dims;
63 assert_eq!(dims.len(), 2, "population tensor must be rank 2");
64 Self {
65 pop_size: dims[0],
66 genome_dim: dims[1],
67 _kind: PhantomData,
68 tensor_real: Some(tensor),
69 tensor_int: None,
70 }
71 }
72
73 #[must_use]
79 pub fn tensor(&self) -> &Tensor<B, 2> {
80 self.tensor_real
81 .as_ref()
82 .expect("real population always has a tensor_real")
83 }
84
85 #[must_use]
91 pub fn into_tensor(self) -> Tensor<B, 2> {
92 self.tensor_real
93 .expect("real population always has a tensor_real")
94 }
95}
96
97impl<B: Backend> Population<B, Binary> {
98 #[must_use]
104 pub fn new_binary(tensor: Tensor<B, 2, Int>) -> Self {
105 let dims = tensor.shape().dims;
106 assert_eq!(dims.len(), 2, "population tensor must be rank 2");
107 Self {
108 pop_size: dims[0],
109 genome_dim: dims[1],
110 _kind: PhantomData,
111 tensor_real: None,
112 tensor_int: Some(tensor),
113 }
114 }
115
116 #[must_use]
122 pub fn tensor(&self) -> &Tensor<B, 2, Int> {
123 self.tensor_int
124 .as_ref()
125 .expect("binary population always has a tensor_int")
126 }
127}
128
129impl<B: Backend> Population<B, Integer> {
130 #[must_use]
136 pub fn new_integer(tensor: Tensor<B, 2, Int>) -> Self {
137 let dims = tensor.shape().dims;
138 assert_eq!(dims.len(), 2, "population tensor must be rank 2");
139 Self {
140 pop_size: dims[0],
141 genome_dim: dims[1],
142 _kind: PhantomData,
143 tensor_real: None,
144 tensor_int: Some(tensor),
145 }
146 }
147
148 #[must_use]
154 pub fn tensor(&self) -> &Tensor<B, 2, Int> {
155 self.tensor_int
156 .as_ref()
157 .expect("integer population always has a tensor_int")
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use burn::backend::NdArray;
165 use burn::tensor::TensorData;
166 type TestBackend = NdArray;
167
168 #[test]
169 fn real_population_reports_shape() {
170 let device = Default::default();
171 let data = TensorData::new(vec![1.0f32, 2.0, 3.0, 4.0], [2, 2]);
172 let tensor = Tensor::<TestBackend, 2>::from_data(data, &device);
173 let pop = Population::<TestBackend, Real>::new_real(tensor);
174 assert_eq!(pop.pop_size(), 2);
175 assert_eq!(pop.genome_dim(), 2);
176 assert_eq!(pop.tensor().shape().dims, vec![2, 2]);
177 }
178
179 #[test]
180 fn binary_population_uses_int_tensor() {
181 let device = Default::default();
182 let data = TensorData::new(vec![0i64, 1, 1, 0, 1, 0], [2, 3]);
183 let tensor = Tensor::<TestBackend, 2, Int>::from_data(data, &device);
184 let pop = Population::<TestBackend, Binary>::new_binary(tensor);
185 assert_eq!(pop.pop_size(), 2);
186 assert_eq!(pop.genome_dim(), 3);
187 }
188}