pub fn centered_rank<B: Backend>(
fitness: Tensor<B, 1>,
device: &<B as BackendTypes>::Device,
) -> Result<Tensor<B, 1>, ShapingError>Expand description
Returns centered ranks: the largest fitness maps to +0.5, the
smallest to -0.5, with linear spacing in between.
Under the crate’s maximise convention (canonical: higher is better)
this assigns the best (highest-fitness) individual the highest
utility +0.5 and the worst the lowest -0.5, which is the sign a
gradient-style ES update expects — no negation at the call site.
Centered ranks are standard in modern ES (e.g. OpenAI-ES) because they remove outlier fitness magnitudes and keep the signal scale-free across generations. Implemented host-side because the argsort pathway is easier to reason about; swap in a tensor-native implementation if this ever shows up on a profile.
An empty input returns an empty tensor.
§Examples
use burn::backend::Flex;
use burn::tensor::Tensor;
use rlevo_evolution::shaping::centered_rank;
let device = Default::default();
let t = Tensor::<Flex, 1>::from_floats([10.0f32, 20.0, 30.0, 40.0], &device);
let r = centered_rank(t, &device).unwrap();
let values = r.into_data().into_vec::<f32>().expect("shaped tensor must be readable as f32");
// Smallest value maps to -0.5, largest to +0.5.
assert!((values[0] - (-0.5)).abs() < 1e-6);
assert!((values[3] - 0.5).abs() < 1e-6);§Errors
Returns ShapingError::NonFloatData if the tensor’s element data cannot be
read as f32 — for example, when using a backend that stores integer-typed
tensors and into_vec::<f32>() fails.