#[cfg(test)]
#[macro_use]
pub mod testing;
mod array;
mod boolean;
mod index;
mod indexed_type;
mod interval;
mod ndarray;
mod nonempty_features;
mod option;
mod power;
mod singleton;
#[cfg(test)]
mod test_derive;
mod tuple;
mod wrapper;
pub use self::ndarray::{Array1Space, Array2Space, Array3Space, NdArraySpace};
pub use array::ArraySpace;
pub use boolean::BooleanSpace;
pub use index::IndexSpace;
pub use indexed_type::{Indexed, IndexedTypeSpace};
pub use interval::IntervalSpace;
pub use nonempty_features::NonEmptyFeatures;
pub use option::OptionSpace;
pub use power::PowerSpace;
pub use singleton::SingletonSpace;
pub use tuple::{TupleSpace2, TupleSpace3, TupleSpace4, TupleSpace5};
pub use wrapper::BoxSpace;
pub use relearn_derive::{
FiniteSpace, Indexed, LogElementSpace, ProductSpace, SampleSpace, Space, SubsetOrd,
};
use crate::logging::{LogError, StatsLogger};
use crate::utils::distributions::ArrayDistribution;
use crate::utils::num_array::{BuildFromArray1D, BuildFromArray2D, NumArray1D, NumArray2D};
use ::ndarray::{ArrayBase, DataMut, Ix2};
use num_traits::Float;
use rand::distributions::Distribution;
use rand::RngCore;
use std::cmp::Ordering;
use std::iter::ExactSizeIterator;
pub trait Space {
type Element: Clone + Send;
fn contains(&self, value: &Self::Element) -> bool;
}
macro_rules! impl_wrapped_space {
($wrapper:ty) => {
impl<S> Space for $wrapper
where
S: Space + ?Sized,
{
type Element = S::Element;
#[inline]
fn contains(&self, value: &Self::Element) -> bool {
S::contains(self, value)
}
}
};
}
impl_wrapped_space!(&'_ S);
impl_wrapped_space!(Box<S>);
pub trait SubsetOrd: PartialEq<Self> {
fn subset_cmp(&self, other: &Self) -> Option<Ordering>;
#[inline]
fn strict_subset_of(&self, other: &Self) -> bool {
matches!(self.subset_cmp(other), Some(Ordering::Less))
}
#[inline]
fn subset_of(&self, other: &Self) -> bool {
matches!(
self.subset_cmp(other),
Some(Ordering::Less | Ordering::Equal)
)
}
#[inline]
fn strict_superset_of(&self, other: &Self) -> bool {
matches!(self.subset_cmp(other), Some(Ordering::Greater))
}
#[inline]
fn superset_of(&self, other: &Self) -> bool {
matches!(
self.subset_cmp(other),
Some(Ordering::Greater | Ordering::Equal)
)
}
}
macro_rules! impl_wrapped_subset_ord {
($wrapper:ty) => {
impl<S> SubsetOrd for $wrapper
where
S: SubsetOrd + ?Sized,
{
#[inline]
fn subset_cmp(&self, other: &Self) -> Option<Ordering> {
S::subset_cmp(self, other)
}
}
};
}
impl_wrapped_subset_ord!(&'_ S);
impl_wrapped_subset_ord!(Box<S>);
#[must_use]
#[inline]
pub const fn product_subset_ord(a: Ordering, b: Option<Ordering>) -> Option<Ordering> {
use Ordering::*;
match (a, b) {
(Equal, Some(x)) | (x, Some(Equal)) => Some(x),
(Less, Some(Less)) => Some(Less),
(Greater, Some(Greater)) => Some(Greater),
_ => None,
}
}
#[inline]
pub fn iter_product_subset_ord<I: IntoIterator<Item = Option<Ordering>>>(
ord_factors: I,
) -> Option<Ordering> {
ord_factors
.into_iter()
.try_fold(Ordering::Equal, product_subset_ord)
}
pub trait FiniteSpace: Space {
fn size(&self) -> usize;
fn to_index(&self, element: &Self::Element) -> usize;
#[allow(clippy::wrong_self_convention)] fn from_index(&self, index: usize) -> Option<Self::Element>;
#[inline]
#[allow(clippy::wrong_self_convention)] fn from_index_unchecked(&self, index: usize) -> Option<Self::Element> {
self.from_index(index)
}
}
macro_rules! impl_wrapped_finite_space {
($wrapper:ty) => {
impl<S> FiniteSpace for $wrapper
where
S: FiniteSpace + ?Sized,
{
#[inline]
fn size(&self) -> usize {
S::size(self)
}
#[inline]
fn to_index(&self, element: &Self::Element) -> usize {
S::to_index(self, element)
}
#[inline]
fn from_index(&self, index: usize) -> Option<Self::Element> {
S::from_index(self, index)
}
#[inline]
fn from_index_unchecked(&self, index: usize) -> Option<Self::Element> {
S::from_index_unchecked(self, index)
}
}
};
}
impl_wrapped_finite_space!(&'_ S);
impl_wrapped_finite_space!(Box<S>);
pub trait NonEmptySpace: Space {
fn some_element(&self) -> Self::Element;
}
macro_rules! impl_wrapped_non_empty_space {
($wrapper:ty) => {
impl<S> NonEmptySpace for $wrapper
where
S: NonEmptySpace + ?Sized,
{
#[inline]
fn some_element(&self) -> Self::Element {
S::some_element(self)
}
}
};
}
impl_wrapped_non_empty_space!(&'_ S);
impl_wrapped_non_empty_space!(Box<S>);
pub trait SampleSpace: NonEmptySpace {
fn sample(&self, rng: &mut dyn RngCore) -> Self::Element;
}
impl<S> SampleSpace for S
where
S: NonEmptySpace + Distribution<<Self as Space>::Element>,
{
#[inline]
fn sample(&self, rng: &mut dyn RngCore) -> Self::Element {
Distribution::sample(&self, rng)
}
}
pub trait ReprSpace<T, T0 = T>: Space {
fn repr(&self, element: &Self::Element) -> T0;
fn batch_repr<'a, I>(&self, elements: I) -> T
where
I: IntoIterator<Item = &'a Self::Element>,
I::IntoIter: ExactSizeIterator + Clone,
Self::Element: 'a;
}
macro_rules! impl_wrapped_repr_space {
($wrapper:ty) => {
impl<S, T, T0> ReprSpace<T, T0> for $wrapper
where
S: ReprSpace<T, T0> + ?Sized,
{
#[inline]
fn repr(&self, element: &Self::Element) -> T0 {
S::repr(self, element)
}
#[inline]
fn batch_repr<'a, I>(&self, elements: I) -> T
where
I: IntoIterator<Item = &'a Self::Element>,
I::IntoIter: ExactSizeIterator + Clone,
Self::Element: 'a,
{
S::batch_repr(self, elements)
}
}
};
}
impl_wrapped_repr_space!(&'_ S);
impl_wrapped_repr_space!(Box<S>);
pub trait FeatureSpace: Space {
fn num_features(&self) -> usize;
fn features_out<'a, F: Float>(
&self,
element: &Self::Element,
out: &'a mut [F],
zeroed: bool,
) -> &'a mut [F];
#[inline]
fn features<T>(&self, element: &Self::Element) -> T
where
T: BuildFromArray1D,
<T::Array as NumArray1D>::Elem: Float,
{
let mut array = T::Array::zeros(self.num_features());
self.features_out(element, array.as_slice_mut(), true);
array.into()
}
#[inline]
fn batch_features_out<'a, I, A>(&self, elements: I, out: &mut ArrayBase<A, Ix2>, zeroed: bool)
where
I: IntoIterator<Item = &'a Self::Element>,
Self::Element: 'a,
A: DataMut,
A::Elem: Float,
{
let mut rows = out.rows_mut().into_iter();
for element in elements {
let mut row = rows.next().expect("fewer rows than elements");
self.features_out(
element,
row.as_slice_mut().expect("could not view row as slice"),
zeroed,
);
}
}
#[inline]
fn batch_features<'a, I, T>(&self, elements: I) -> T
where
I: IntoIterator<Item = &'a Self::Element>,
I::IntoIter: ExactSizeIterator,
Self::Element: 'a,
T: BuildFromArray2D,
<T::Array as NumArray2D>::Elem: Float,
{
let elements = elements.into_iter();
let mut array = T::Array::zeros((elements.len(), self.num_features()));
self.batch_features_out(elements, &mut array.view_mut(), true);
array.into()
}
}
pub trait ParameterizedDistributionSpace<T, T2 = T>: ReprSpace<T, T2> {
type Distribution: ArrayDistribution<T, T>;
fn num_distribution_params(&self) -> usize;
fn sample_element(&self, params: &T) -> Self::Element;
fn distribution(&self, params: &T2) -> Self::Distribution;
}
pub trait LogElementSpace: Space {
fn log_element<L: StatsLogger + ?Sized>(
&self,
name: &'static str,
element: &Self::Element,
logger: &mut L,
) -> Result<(), LogError>;
}