pub trait Scalar:
num_traits::Float + num_traits::FromPrimitive + std::iter::Sum + std::fmt::Debug + Default + 'static
{
}
#[cfg(all(feature = "parallel", any(feature = "faer", feature = "nalgebra")))]
pub(crate) trait MaybeSend: Send {}
#[cfg(all(feature = "parallel", any(feature = "faer", feature = "nalgebra")))]
impl<T: Send + ?Sized> MaybeSend for T {}
#[cfg(all(not(feature = "parallel"), any(feature = "faer", feature = "nalgebra")))]
pub(crate) trait MaybeSend {}
#[cfg(all(not(feature = "parallel"), any(feature = "faer", feature = "nalgebra")))]
impl<T: ?Sized> MaybeSend for T {}
#[cfg(all(feature = "parallel", any(feature = "faer", feature = "nalgebra")))]
pub(crate) trait MaybeSync: Sync {}
#[cfg(all(feature = "parallel", any(feature = "faer", feature = "nalgebra")))]
impl<T: Sync + ?Sized> MaybeSync for T {}
#[cfg(all(not(feature = "parallel"), any(feature = "faer", feature = "nalgebra")))]
pub(crate) trait MaybeSync {}
#[cfg(all(not(feature = "parallel"), any(feature = "faer", feature = "nalgebra")))]
impl<T: ?Sized> MaybeSync for T {}
#[cfg(all(feature = "parallel", any(feature = "faer", feature = "nalgebra")))]
pub(crate) fn collect_columns<T, Map>(ncols: usize, map: Map) -> Vec<T>
where
T: MaybeSend,
Map: Fn(usize) -> T + MaybeSend + MaybeSync,
{
use rayon::prelude::*;
(0..ncols).into_par_iter().map(map).collect()
}
#[cfg(all(not(feature = "parallel"), any(feature = "faer", feature = "nalgebra")))]
pub(crate) fn collect_columns<T, Map>(ncols: usize, map: Map) -> Vec<T>
where
T: MaybeSend,
Map: Fn(usize) -> T + MaybeSend + MaybeSync,
{
(0..ncols).map(map).collect()
}
impl<F> Scalar for F where
F: num_traits::Float
+ num_traits::FromPrimitive
+ std::iter::Sum
+ std::fmt::Debug
+ Default
+ 'static
{
}
#[cfg(any(feature = "faer", feature = "nalgebra"))]
pub(crate) fn sparse_column_sd<F: Scalar>(values: &[F], nrows: usize) -> F {
let n = F::from_usize(nrows).unwrap();
let mean = values.iter().copied().sum::<F>() / n;
let stored_squared_deviations = values
.iter()
.map(|&value| {
let deviation = value - mean;
deviation * deviation
})
.sum::<F>();
let implicit_count = F::from_usize(nrows - values.len()).unwrap();
let variance = (stored_squared_deviations + implicit_count * mean * mean) / n;
variance.sqrt()
}
#[cfg(any(feature = "faer", feature = "nalgebra"))]
pub(crate) fn max_or_nan<F: Scalar>(values: impl Iterator<Item = F>) -> F {
values.fold(F::zero(), |maximum, value| {
if maximum.is_nan() || value.is_nan() {
F::nan()
} else if value > maximum {
value
} else {
maximum
}
})
}
#[cfg(any(feature = "faer", feature = "nalgebra"))]
pub(crate) fn min_or_nan<F: Scalar>(values: impl Iterator<Item = F>) -> F {
values
.fold(None, |minimum: Option<F>, value| {
Some(match minimum {
None => value,
Some(minimum) if minimum.is_nan() || value.is_nan() => F::nan(),
Some(minimum) => minimum.min(value),
})
})
.unwrap_or_else(F::nan)
}
#[cfg(any(feature = "faer", feature = "nalgebra"))]
pub(crate) fn range_or_nan<F: Scalar>(values: impl Iterator<Item = F>) -> F {
values
.fold(None, |extrema: Option<(F, F)>, value| {
Some(match extrema {
None => (value, value),
Some((minimum, maximum))
if minimum.is_nan() || maximum.is_nan() || value.is_nan() =>
{
(F::nan(), F::nan())
}
Some((minimum, maximum)) => (minimum.min(value), maximum.max(value)),
})
})
.map_or_else(F::nan, |(minimum, maximum)| maximum - minimum)
}
pub trait MatrixShape {
fn nrows(&self) -> usize;
fn ncols(&self) -> usize;
}
pub trait VectorView<F: Scalar> {
fn len(&self) -> usize;
fn get(&self, index: usize) -> F;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn sum(&self) -> F {
(0..self.len()).map(|i| self.get(i)).sum()
}
}
pub trait VectorViewMut<F: Scalar>: VectorView<F> {
fn set(&mut self, index: usize, value: F);
}
impl<F: Scalar> VectorView<F> for [F] {
fn len(&self) -> usize {
<[F]>::len(self)
}
fn get(&self, index: usize) -> F {
self[index]
}
}
impl<F: Scalar> VectorViewMut<F> for [F] {
fn set(&mut self, index: usize, value: F) {
self[index] = value;
}
}
impl<F: Scalar> VectorView<F> for Vec<F> {
fn len(&self) -> usize {
Vec::len(self)
}
fn get(&self, index: usize) -> F {
self[index]
}
}
impl<F: Scalar> VectorViewMut<F> for Vec<F> {
fn set(&mut self, index: usize, value: F) {
self[index] = value;
}
}
impl<F: Scalar, const N: usize> VectorView<F> for [F; N] {
fn len(&self) -> usize {
N
}
fn get(&self, index: usize) -> F {
self[index]
}
}
impl<F: Scalar, const N: usize> VectorViewMut<F> for [F; N] {
fn set(&mut self, index: usize, value: F) {
self[index] = value;
}
}
pub trait RawColumn<F: Scalar> {
fn len(&self) -> usize;
fn stored_len(&self) -> usize;
fn for_each_stored(&self, f: impl FnMut(usize, F));
fn is_empty(&self) -> bool {
self.len() == 0
}
fn raw_sum(&self) -> F {
let mut sum = F::zero();
self.for_each_stored(|_, value| sum = sum + value);
sum
}
fn affine_add_to<V>(&self, raw_multiplier: F, offset: F, destination: &mut V)
where
V: VectorViewMut<F> + ?Sized,
{
assert_eq!(
destination.len(),
self.len(),
"destination length must equal column length"
);
if offset != F::zero() {
for row in 0..destination.len() {
destination.set(row, destination.get(row) + offset);
}
}
self.for_each_stored(|row, value| {
destination.set(row, destination.get(row) + raw_multiplier * value);
});
}
}
pub trait RawColumns<F: Scalar>: MatrixShape {
type Column<'a>: RawColumn<F>
where
Self: 'a;
fn raw_column(&self, j: usize) -> Self::Column<'_>;
}
pub trait LogicalColumn<F: Scalar> {
fn len(&self) -> usize;
fn center(&self) -> F;
fn scale(&self) -> F;
fn sum(&self) -> F;
fn norm_squared(&self) -> F;
fn dot<V: VectorView<F> + ?Sized>(&self, vector: &V) -> F;
fn dot_with_sum<V: VectorView<F> + ?Sized>(&self, vector: &V, vector_sum: F) -> F;
fn weighted_dot<V, W>(&self, vector: &V, weights: &W) -> F
where
V: VectorView<F> + ?Sized,
W: VectorView<F> + ?Sized;
fn weighted_dot_with_sum<V, W>(&self, vector: &V, weights: &W, weighted_vector_sum: F) -> F
where
V: VectorView<F> + ?Sized,
W: VectorView<F> + ?Sized;
fn weighted_norm_squared<W: VectorView<F> + ?Sized>(&self, weights: &W) -> F;
fn weighted_norm_squared_with_sum<W: VectorView<F> + ?Sized>(
&self,
weights: &W,
weight_sum: F,
) -> F;
fn scaled_add_to<V: VectorViewMut<F> + ?Sized>(&self, alpha: F, destination: &mut V);
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub trait Columns<F: Scalar>: MatrixShape {
type Column<'a>: LogicalColumn<F>
where
Self: 'a;
fn column(&self, j: usize) -> Self::Column<'_>;
}
pub trait MatVec<V>: MatrixShape {
fn matvec(&self, x: &V) -> V;
}
pub trait MatTransposeVec<V>: MatrixShape {
fn mat_transpose_vec(&self, x: &V) -> V;
}
pub trait DotProduct<F: Scalar> {
fn dot(&self, other: &Self) -> F;
}
pub trait L2Norm<F: Scalar> {
fn norm_l2(&self) -> F;
}
pub trait ScaledAddAssign<F: Scalar> {
fn scaled_add_assign(&mut self, alpha: F, other: &Self);
}
pub trait ScaleAssign<F: Scalar> {
fn scale_assign(&mut self, alpha: F);
}
pub trait SparseColumns<F: Scalar>: MatrixShape {
fn sparse_column(&self, j: usize) -> (&[usize], &[F]);
}
impl<M: MatrixShape + ?Sized> MatrixShape for &M {
fn nrows(&self) -> usize {
(**self).nrows()
}
fn ncols(&self) -> usize {
(**self).ncols()
}
}
impl<M, V> MatVec<V> for &M
where
M: MatVec<V> + ?Sized,
{
fn matvec(&self, x: &V) -> V {
(**self).matvec(x)
}
}
impl<M, V> MatTransposeVec<V> for &M
where
M: MatTransposeVec<V> + ?Sized,
{
fn mat_transpose_vec(&self, x: &V) -> V {
(**self).mat_transpose_vec(x)
}
}
impl<M, F> RawColumns<F> for &M
where
M: RawColumns<F> + ?Sized,
F: Scalar,
{
type Column<'a>
= M::Column<'a>
where
Self: 'a;
fn raw_column(&self, j: usize) -> Self::Column<'_> {
(**self).raw_column(j)
}
}
impl<M, F> SparseColumns<F> for &M
where
M: SparseColumns<F> + ?Sized,
F: Scalar,
{
fn sparse_column(&self, j: usize) -> (&[usize], &[F]) {
(**self).sparse_column(j)
}
}
pub trait ElemDivAssign<F: Scalar> {
fn elem_div_assign(&mut self, coeffs: &[F]);
}
pub trait DotSlice<F: Scalar> {
fn dot_slice(&self, coeffs: &[F]) -> F;
}
pub trait SubScalarAssign<F: Scalar> {
fn sub_scalar_assign(&mut self, k: F);
}
pub trait SumEntries<F: Scalar> {
fn sum_entries(&self) -> F;
}
pub trait ScaledSubSlice<F: Scalar> {
fn scaled_sub_slice(&mut self, k: F, coeffs: &[F]);
}
pub trait ColumnStats<F: Scalar> {
fn col_means(&self) -> Vec<F>;
fn col_sds(&self) -> Vec<F>;
fn col_mins(&self) -> Vec<F>;
fn col_ranges(&self) -> Vec<F>;
fn col_maxabs(&self) -> Vec<F>;
fn col_l1(&self) -> Vec<F>;
fn col_l2(&self) -> Vec<F>;
fn col_l2_centered(&self, centers: &[F]) -> Vec<F>;
fn col_l1_centered(&self, centers: &[F]) -> Vec<F>;
fn col_maxabs_centered(&self, centers: &[F]) -> Vec<F>;
}
impl<M, F> ColumnStats<F> for &M
where
M: ColumnStats<F> + ?Sized,
F: Scalar,
{
fn col_means(&self) -> Vec<F> {
(**self).col_means()
}
fn col_sds(&self) -> Vec<F> {
(**self).col_sds()
}
fn col_mins(&self) -> Vec<F> {
(**self).col_mins()
}
fn col_ranges(&self) -> Vec<F> {
(**self).col_ranges()
}
fn col_maxabs(&self) -> Vec<F> {
(**self).col_maxabs()
}
fn col_l1(&self) -> Vec<F> {
(**self).col_l1()
}
fn col_l2(&self) -> Vec<F> {
(**self).col_l2()
}
fn col_l2_centered(&self, centers: &[F]) -> Vec<F> {
(**self).col_l2_centered(centers)
}
fn col_l1_centered(&self, centers: &[F]) -> Vec<F> {
(**self).col_l1_centered(centers)
}
fn col_maxabs_centered(&self, centers: &[F]) -> Vec<F> {
(**self).col_maxabs_centered(centers)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Centering {
#[default]
None,
Mean,
Min,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Scaling {
#[default]
None,
Sd,
MaxAbs,
L1,
L2,
Range,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Normalization {
pub center: Centering,
pub scale: Scaling,
}
impl Normalization {
pub fn new(center: Centering, scale: Scaling) -> Self {
Self { center, scale }
}
}