#[macro_export]
macro_rules! fitness_fn {
($($c:expr),+ $(,)?) => {
|genes: &ndarray::Array2<f64>| -> ndarray::Array2<f64> {
let cols = vec![ $( ($c)(genes) ),+ ];
let views: Vec<_> = cols
.iter()
.map(|v| v.view().insert_axis(ndarray::Axis(1)))
.collect();
ndarray::concatenate(ndarray::Axis(1), &views)
.expect("concatenate along axis 1")
}
};
}
#[cfg(test)]
mod tests {
use ndarray::{Array1, Array2, Axis, array};
fn f_sphere(genes: &Array2<f64>) -> Array1<f64> {
genes.map_axis(Axis(1), |row| row.dot(&row))
}
fn f_sum(genes: &Array2<f64>) -> Array1<f64> {
genes.map_axis(Axis(1), |row| row.sum())
}
fn f_diff(genes: &Array2<f64>) -> Array1<f64> {
genes.map_axis(Axis(1), |row| row[0] - row[1])
}
#[test]
fn two_objectives() {
let eval = fitness_fn!(f_sphere, f_sum);
let genes = array![[1.0, 0.0], [2.0, 2.0]];
let out = eval(&genes);
assert_eq!(out.shape(), &[2, 2]);
assert_eq!(out, array![[1.0, 1.0], [8.0, 4.0]]);
}
#[test]
fn three_objectives() {
let eval = fitness_fn!(f_sphere, f_sum, f_diff);
let genes = array![[1.0, 0.0], [2.0, 3.0]];
let out = eval(&genes);
assert_eq!(out.shape(), &[2, 3]);
let expected = array![
[1.0, 1.0, 1.0], [13.0, 5.0, -1.0], ];
assert_eq!(out, expected);
}
#[test]
fn reuse_closure() {
let eval = fitness_fn!(f_sum);
let g1 = array![[1.0, 2.0]];
let g2 = array![[0.0, 0.0]];
assert_eq!(eval(&g1)[[0, 0]], 3.0);
assert_eq!(eval(&g2)[[0, 0]], 0.0);
}
}