pub trait Observation {
fn features(&self) -> &[f64];
fn target(&self) -> f64;
fn weight(&self) -> f64 {
1.0
}
}
#[derive(Debug, Clone, Copy)]
pub struct SampleRef<'a> {
pub features: &'a [f64],
pub target: f64,
pub weight: f64,
}
impl<'a> SampleRef<'a> {
#[inline]
pub fn new(features: &'a [f64], target: f64) -> Self {
Self {
features,
target,
weight: 1.0,
}
}
#[inline]
pub fn weighted(features: &'a [f64], target: f64, weight: f64) -> Self {
Self {
features,
target,
weight,
}
}
}
impl<'a> Observation for SampleRef<'a> {
#[inline]
fn features(&self) -> &[f64] {
self.features
}
#[inline]
fn target(&self) -> f64 {
self.target
}
#[inline]
fn weight(&self) -> f64 {
self.weight
}
}
impl Observation for (&[f64], f64) {
#[inline]
fn features(&self) -> &[f64] {
self.0
}
#[inline]
fn target(&self) -> f64 {
self.1
}
}
#[cfg(feature = "alloc")]
impl Observation for (alloc::vec::Vec<f64>, f64) {
#[inline]
fn features(&self) -> &[f64] {
&self.0
}
#[inline]
fn target(&self) -> f64 {
self.1
}
}
#[cfg(feature = "alloc")]
impl Observation for (&alloc::vec::Vec<f64>, f64) {
#[inline]
fn features(&self) -> &[f64] {
self.0
}
#[inline]
fn target(&self) -> f64 {
self.1
}
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Sample {
pub features: alloc::vec::Vec<f64>,
pub target: f64,
pub weight: f64,
}
#[cfg(feature = "alloc")]
impl Sample {
#[inline]
pub fn new(features: alloc::vec::Vec<f64>, target: f64) -> Self {
Self {
features,
target,
weight: 1.0,
}
}
#[inline]
pub fn weighted(features: alloc::vec::Vec<f64>, target: f64, weight: f64) -> Self {
Self {
features,
target,
weight,
}
}
#[inline]
pub fn n_features(&self) -> usize {
self.features.len()
}
}
#[cfg(feature = "alloc")]
impl Observation for Sample {
#[inline]
fn features(&self) -> &[f64] {
&self.features
}
#[inline]
fn target(&self) -> f64 {
self.target
}
#[inline]
fn weight(&self) -> f64 {
self.weight
}
}
#[cfg(feature = "alloc")]
impl Observation for &Sample {
#[inline]
fn features(&self) -> &[f64] {
&self.features
}
#[inline]
fn target(&self) -> f64 {
self.target
}
#[inline]
fn weight(&self) -> f64 {
self.weight
}
}
impl<'a> From<(&'a [f64], f64)> for SampleRef<'a> {
fn from((features, target): (&'a [f64], f64)) -> Self {
SampleRef::new(features, target)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sample_ref_new() {
let features = [1.0, 2.0, 3.0];
let obs = SampleRef::new(&features, 42.0);
assert_eq!(obs.features(), &[1.0, 2.0, 3.0]);
assert_eq!(obs.target(), 42.0);
assert!((obs.weight() - 1.0).abs() < 1e-12);
}
#[test]
fn test_sample_ref_weighted() {
let features = [1.0, 2.0];
let obs = SampleRef::weighted(&features, 5.0, 0.5);
assert_eq!(obs.features(), &[1.0, 2.0]);
assert_eq!(obs.target(), 5.0);
assert!((obs.weight() - 0.5).abs() < 1e-12);
}
#[test]
fn test_tuple_slice_observation() {
let features = [1.0, 2.0, 3.0];
let obs = (&features[..], 42.0);
assert_eq!(obs.features(), &[1.0, 2.0, 3.0]);
assert_eq!(obs.target(), 42.0);
assert!((obs.weight() - 1.0).abs() < 1e-12);
}
#[test]
fn test_from_tuple_to_sample_ref() {
let features = [1.0, 2.0];
let obs: SampleRef = (&features[..], 5.0).into();
assert_eq!(obs.features(), &[1.0, 2.0]);
assert_eq!(obs.target(), 5.0);
}
#[cfg(feature = "alloc")]
#[test]
fn test_vec_tuple_observation() {
let obs = (alloc::vec![1.0, 2.0, 3.0], 42.0);
assert_eq!(obs.features(), &[1.0, 2.0, 3.0]);
assert_eq!(obs.target(), 42.0);
}
#[cfg(feature = "alloc")]
#[test]
fn test_vec_ref_tuple_observation() {
let v = alloc::vec![1.0, 2.0, 3.0];
let obs = (&v, 42.0);
assert_eq!(obs.features(), &[1.0, 2.0, 3.0]);
assert_eq!(obs.target(), 42.0);
}
}