param-opt 0.2.0

Parameter optimization
Documentation
use std::{collections::HashMap, num::NonZeroUsize, sync::Arc};

use thiserror::Error;

use super::{
    dataset::{ParameterDataset, ParameterValue},
    ParameterSpace,
};

#[derive(Debug, Clone, PartialEq)]
pub struct ParameterTable {
    named: HashMap<String, ParameterDataset>,
    vectored: Arc<[String]>,
}
impl ParameterTable {
    pub fn new(
        named: HashMap<String, ParameterDataset>,
        vectored: Arc<[String]>,
    ) -> Result<Self, ParameterTableError> {
        if named.len() != vectored.len() {
            return Err(ParameterTableError::InconsistentLength);
        }
        for key in vectored.as_ref() {
            if named.get(key).is_none() {
                return Err(ParameterTableError::KeyNotExists {
                    key: key.to_owned(),
                });
            }
        }
        Ok(Self { named, vectored })
    }

    pub fn new_unordered(parameters: HashMap<String, ParameterDataset>) -> Self {
        let vectored = parameters.keys().cloned().collect();
        Self {
            named: parameters,
            vectored,
        }
    }

    pub fn spaces(&self) -> impl Iterator<Item = NonZeroUsize> + '_ {
        self.vectored
            .iter()
            .map(|name| &self.named[name])
            .map(ParameterSpace::space_size)
    }

    pub fn values(
        &self,
        indices: impl Iterator<Item = usize>,
    ) -> impl Iterator<Item = (&String, ParameterValue)> {
        indices
            .enumerate()
            .map(|(i, index)| (&self.vectored[i], index))
            .map(|(name, index)| (name, self.named[name].value(index)))
    }
}

#[derive(Debug, Clone, PartialEq, Error)]
pub enum ParameterTableError {
    #[error("`named` and `vectored` should be of the same size")]
    InconsistentLength,
    #[error("`{key}` does not exist in `named`")]
    KeyNotExists { key: String },
}