radiate-core 1.3.1

Core traits and interfaces for the Radiate genetic algorithm library.
Documentation
use crate::{MetricSet, error::RadiateResult};
use radiate_error::radiate_bail;
use radiate_expr::Expr;
use radiate_utils::SmallStr;
use smallvec::SmallVec;

const DEFAULT_VALUE: f32 = 1.0;

#[derive(Clone)]
pub struct RateSet {
    pub control: Expr,
    pub internal: Vec<Expr>,
    pub rate_cache: SmallVec<[f32; 8]>,
    pub last_update_index: usize,
    pub has_updated: bool,
}

impl RateSet {
    pub fn new(control: impl Into<Expr>) -> Self {
        Self {
            control: control.into(),
            internal: Vec::new(),
            rate_cache: SmallVec::new(),
            last_update_index: 0,
            has_updated: false,
        }
    }

    pub fn calculate_rates(
        &mut self,
        generation: usize,
        metrics: &MetricSet,
    ) -> RadiateResult<&[f32]> {
        if generation > self.last_update_index || !self.has_updated {
            self.rate_cache.clear();

            let control_rate = Self::try_eval_rate(metrics, &mut self.control)?;
            self.rate_cache.push(control_rate);

            for expr in &mut self.internal {
                let rate = Self::try_eval_rate(metrics, expr)?;
                self.rate_cache.push(rate);
            }

            self.last_update_index = generation;
            self.has_updated = true;
        }

        Ok(&self.rate_cache)
    }

    pub fn calculate_control_rate(
        &mut self,
        generation: usize,
        metrics: &MetricSet,
    ) -> RadiateResult<f32> {
        self.calculate_rates(generation, metrics)?;
        Ok(self.rate_cache[0])
    }

    pub fn rates(&self) -> &[f32] {
        &self.rate_cache
    }

    pub fn alias(mut self, name: impl Into<SmallStr>) -> Self {
        let name = name.into();
        self.control = self.control.clone().alias(name);
        self
    }

    pub fn push(mut self, expr: impl Into<Expr>) -> Self {
        self.internal.push(expr.into());
        self
    }

    fn try_eval_rate(metrics: &MetricSet, expr: &mut Expr) -> RadiateResult<f32> {
        if let Some(metric) = metrics.get(expr.name()) {
            return Ok(metric.last_value());
        }

        let output = expr.evaluate(metrics)?;
        match output.extract::<f32>() {
            Some(rate) => Ok(rate),
            None => {
                radiate_bail!(Expr:
                    "Failed to evaluate rate expression for alterer: expected f32 value, got {:?}",
                    output
                );
            }
        }
    }
}

impl Default for RateSet {
    fn default() -> Self {
        Self {
            control: Expr::lit(DEFAULT_VALUE),
            internal: Vec::new(),
            rate_cache: SmallVec::new(),
            last_update_index: 0,
            has_updated: false,
        }
    }
}

impl From<Expr> for RateSet {
    fn from(expr: Expr) -> Self {
        Self::new(expr)
    }
}