use std::marker::PhantomData;
use burn::tensor::{backend::Backend, Int, Tensor};
use crate::genome::{Binary, Integer, Real};
#[derive(Debug, Clone)]
pub struct Population<B: Backend, K> {
pop_size: usize,
genome_dim: usize,
_kind: PhantomData<K>,
tensor_real: Option<Tensor<B, 2>>,
tensor_int: Option<Tensor<B, 2, Int>>,
}
impl<B: Backend, K> Population<B, K> {
#[must_use]
pub fn pop_size(&self) -> usize {
self.pop_size
}
#[must_use]
pub fn genome_dim(&self) -> usize {
self.genome_dim
}
}
impl<B: Backend> Population<B, Real> {
#[must_use]
pub fn new_real(tensor: Tensor<B, 2>) -> Self {
let dims = tensor.shape().dims;
assert_eq!(dims.len(), 2, "population tensor must be rank 2");
Self {
pop_size: dims[0],
genome_dim: dims[1],
_kind: PhantomData,
tensor_real: Some(tensor),
tensor_int: None,
}
}
#[must_use]
pub fn tensor(&self) -> &Tensor<B, 2> {
self.tensor_real
.as_ref()
.expect("real population always has a tensor_real")
}
#[must_use]
pub fn into_tensor(self) -> Tensor<B, 2> {
self.tensor_real
.expect("real population always has a tensor_real")
}
}
impl<B: Backend> Population<B, Binary> {
#[must_use]
pub fn new_binary(tensor: Tensor<B, 2, Int>) -> Self {
let dims = tensor.shape().dims;
assert_eq!(dims.len(), 2, "population tensor must be rank 2");
Self {
pop_size: dims[0],
genome_dim: dims[1],
_kind: PhantomData,
tensor_real: None,
tensor_int: Some(tensor),
}
}
#[must_use]
pub fn tensor(&self) -> &Tensor<B, 2, Int> {
self.tensor_int
.as_ref()
.expect("binary population always has a tensor_int")
}
}
impl<B: Backend> Population<B, Integer> {
#[must_use]
pub fn new_integer(tensor: Tensor<B, 2, Int>) -> Self {
let dims = tensor.shape().dims;
assert_eq!(dims.len(), 2, "population tensor must be rank 2");
Self {
pop_size: dims[0],
genome_dim: dims[1],
_kind: PhantomData,
tensor_real: None,
tensor_int: Some(tensor),
}
}
#[must_use]
pub fn tensor(&self) -> &Tensor<B, 2, Int> {
self.tensor_int
.as_ref()
.expect("integer population always has a tensor_int")
}
}
#[cfg(test)]
mod tests {
use super::*;
use burn::backend::NdArray;
use burn::tensor::TensorData;
type TestBackend = NdArray;
#[test]
fn real_population_reports_shape() {
let device = Default::default();
let data = TensorData::new(vec![1.0f32, 2.0, 3.0, 4.0], [2, 2]);
let tensor = Tensor::<TestBackend, 2>::from_data(data, &device);
let pop = Population::<TestBackend, Real>::new_real(tensor);
assert_eq!(pop.pop_size(), 2);
assert_eq!(pop.genome_dim(), 2);
assert_eq!(pop.tensor().shape().dims, vec![2, 2]);
}
#[test]
fn binary_population_uses_int_tensor() {
let device = Default::default();
let data = TensorData::new(vec![0i64, 1, 1, 0, 1, 0], [2, 3]);
let tensor = Tensor::<TestBackend, 2, Int>::from_data(data, &device);
let pop = Population::<TestBackend, Binary>::new_binary(tensor);
assert_eq!(pop.pop_size(), 2);
assert_eq!(pop.genome_dim(), 3);
}
}