use std::fmt::Debug;
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
use crate::error::LaError;
use crate::scalar::Scale;
use crate::{Context, IndexType, Scalar, Vector, VectorIndex, VectorViewMut};
use extract_block::combine;
use num_traits::{One, Zero};
use sparsity::{Dense, MatrixSparsity, MatrixSparsityRef};
#[cfg(feature = "cuda")]
#[allow(deprecated)]
pub mod cuda;
#[cfg(feature = "cuda-oxide")]
pub mod cuda_oxide;
#[cfg(feature = "nalgebra")]
pub mod dense_nalgebra_serial;
#[cfg(feature = "faer")]
pub mod dense_faer_serial;
#[cfg(feature = "faer")]
pub mod sparse_faer;
pub mod default_solver;
pub mod extract_block;
pub mod sparsity;
pub trait MatrixCommon: Sized + Debug {
type V: Vector<T = Self::T, C = Self::C, Index: VectorIndex<C = Self::C>>;
type T: Scalar;
type C: Context;
type Inner;
fn nrows(&self) -> IndexType;
fn ncols(&self) -> IndexType;
fn inner(&self) -> &Self::Inner;
}
impl<M> MatrixCommon for &M
where
M: MatrixCommon,
{
type T = M::T;
type V = M::V;
type C = M::C;
type Inner = M::Inner;
fn nrows(&self) -> IndexType {
M::nrows(*self)
}
fn ncols(&self) -> IndexType {
M::ncols(*self)
}
fn inner(&self) -> &Self::Inner {
M::inner(*self)
}
}
impl<M> MatrixCommon for &mut M
where
M: MatrixCommon,
{
type T = M::T;
type V = M::V;
type C = M::C;
type Inner = M::Inner;
fn ncols(&self) -> IndexType {
M::ncols(*self)
}
fn nrows(&self) -> IndexType {
M::nrows(*self)
}
fn inner(&self) -> &Self::Inner {
M::inner(*self)
}
}
pub trait MatrixOpsByValue<Rhs = Self, Output = Self>:
MatrixCommon + Add<Rhs, Output = Output> + Sub<Rhs, Output = Output>
{
}
impl<M, Rhs, Output> MatrixOpsByValue<Rhs, Output> for M where
M: MatrixCommon + Add<Rhs, Output = Output> + Sub<Rhs, Output = Output>
{
}
pub trait MatrixMutOpsByValue<Rhs = Self>: MatrixCommon + AddAssign<Rhs> + SubAssign<Rhs> {}
impl<M, Rhs> MatrixMutOpsByValue<Rhs> for M where M: MatrixCommon + AddAssign<Rhs> + SubAssign<Rhs> {}
pub trait MatrixRef<M: MatrixCommon>: Mul<Scale<M::T>, Output = M> {}
impl<RefT, M: MatrixCommon> MatrixRef<M> for RefT where RefT: Mul<Scale<M::T>, Output = M> {}
pub trait Matrix:
MatrixCommon + Mul<Scale<Self::T>, Output = Self> + Clone + Send + 'static
{
type Sparsity: MatrixSparsity<Self>;
type SparsityRef<'a>: MatrixSparsityRef<'a, Self>
where
Self: 'a;
fn sparsity(&self) -> Option<Self::SparsityRef<'_>>;
fn context(&self) -> &Self::C;
fn inner_mut(&mut self) -> &mut Self::Inner;
fn is_sparse() -> bool {
Self::zeros(1, 1, Default::default()).sparsity().is_some()
}
fn partition_indices_by_zero_diagonal(
&self,
) -> (<Self::V as Vector>::Index, <Self::V as Vector>::Index);
fn gemv(&self, alpha: Self::T, x: &Self::V, beta: Self::T, y: &mut Self::V);
fn copy_from(&mut self, other: &Self);
fn zeros(nrows: IndexType, ncols: IndexType, ctx: Self::C) -> Self;
fn new_from_sparsity(
nrows: IndexType,
ncols: IndexType,
sparsity: Option<Self::Sparsity>,
ctx: Self::C,
) -> Self;
fn from_diagonal(v: &Self::V) -> Self;
fn set_column(&mut self, j: IndexType, v: &Self::V);
fn add_column_to_vector(&self, j: IndexType, v: &mut Self::V);
fn add_columns_to_batched_vector(&self, v: &mut Self::V);
fn set_data_with_indices(
&mut self,
dst_indices: &<Self::V as Vector>::Index,
src_indices: &<Self::V as Vector>::Index,
data: &Self::V,
);
fn gather(&mut self, other: &Self, indices: &<Self::V as Vector>::Index);
fn split(
&self,
algebraic_indices: &<Self::V as Vector>::Index,
) -> [(Self, <Self::V as Vector>::Index); 4] {
match self.sparsity() {
Some(sp) => sp.split(algebraic_indices).map(|(sp, src_indices)| {
let mut m = Self::new_from_sparsity(
sp.nrows(),
sp.ncols(),
Some(sp),
self.context().clone(),
);
m.gather(self, &src_indices);
(m, src_indices)
}),
None => Dense::<Self>::new(self.nrows(), self.ncols())
.split(algebraic_indices)
.map(|(sp, src_indices)| {
let mut m = Self::new_from_sparsity(
sp.nrows(),
sp.ncols(),
None,
self.context().clone(),
);
m.gather(self, &src_indices);
(m, src_indices)
}),
}
}
fn combine(
ul: &Self,
ur: &Self,
ll: &Self,
lr: &Self,
algebraic_indices: &<Self::V as Vector>::Index,
) -> Self {
combine(ul, ur, ll, lr, algebraic_indices)
}
fn scale_add_and_assign(&mut self, x: &Self, beta: Self::T, y: &Self);
fn triplet_iter(
&self,
) -> (
impl Iterator<Item = (IndexType, IndexType)> + '_,
impl Iterator<Item = Self::T> + '_,
);
fn try_from_triplets(
nrows: IndexType,
ncols: IndexType,
indices: Vec<(IndexType, IndexType)>,
values: Vec<Self::T>,
ctx: Self::C,
) -> Result<Self, LaError>;
}
pub const MAX_SMALL_COLS: usize = 8;
pub trait DenseMatrix:
Matrix + for<'b> MatrixOpsByValue<&'b Self, Self> + for<'b> MatrixMutOpsByValue<&'b Self>
{
fn update_backward_diff(&mut self, order: IndexType, d: &Self::V);
fn update_backward_diff_transpose(&mut self, order: IndexType, d_bar: &mut Self::V) {
let n = order + 3;
assert!(n <= MAX_SMALL_COLS, "order out of bounds");
let ones = [Self::T::one(); MAX_SMALL_COLS];
self.gemv_cols(0, n, Self::T::one(), &ones, Self::T::zero(), d_bar);
let mut t = [Self::T::zero(); MAX_SMALL_COLS * MAX_SMALL_COLS];
for j in 0..=order {
for i in 0..=j {
t[j * n + i] = Self::T::one();
}
}
t[(order + 1) * n + order + 2] = -Self::T::one();
self.mul_cols_by(n, &t[..n * n]);
}
fn fill(&mut self, value: Self::T) {
let col = Self::V::from_element(self.nrows(), value, self.context().clone());
for j in 0..self.ncols() {
self.column_mut(j).copy_from(&col);
}
}
fn mul_cols_by_transpose(&mut self, ncols: IndexType, rhs: &[Self::T]) {
assert!(ncols <= MAX_SMALL_COLS, "ncols out of bounds");
assert_eq!(rhs.len(), ncols * ncols, "rhs must be ncols x ncols");
let mut rhs_t = [Self::T::zero(); MAX_SMALL_COLS * MAX_SMALL_COLS];
for j in 0..ncols {
for i in 0..ncols {
rhs_t[j * ncols + i] = rhs[i * ncols + j];
}
}
self.mul_cols_by(ncols, &rhs_t[..ncols * ncols]);
}
fn mul_cols_by(&mut self, ncols: IndexType, rhs: &[Self::T]);
fn gemv_cols(
&self,
start: IndexType,
end: IndexType,
alpha: Self::T,
x: &[Self::T],
beta: Self::T,
y: &mut Self::V,
);
fn ger_cols(
&mut self,
start: IndexType,
end: IndexType,
alpha: Self::T,
x: &[Self::T],
y: &Self::V,
) {
assert!(
x.len() >= end.saturating_sub(start),
"ger_cols needs a coefficient per column in the range"
);
for j in start..end {
self.column_mut(j)
.axpy(alpha * x[j - start], y, Self::T::one());
}
}
fn column(&self, i: IndexType) -> <Self::V as Vector>::View<'_>;
fn column_mut(&mut self, i: IndexType) -> <Self::V as Vector>::ViewMut<'_>;
fn set_index(&mut self, i: IndexType, j: IndexType, value: Self::T);
fn set_index_batch(&mut self, batch: IndexType, i: IndexType, j: IndexType, value: Self::T);
fn get_index(&self, i: IndexType, j: IndexType) -> Self::T;
fn resize_cols(&mut self, ncols: IndexType);
fn from_vec(nrows: IndexType, ncols: IndexType, data: Vec<Self::T>, ctx: Self::C) -> Self;
}
#[cfg(test)]
#[allow(clippy::items_after_test_module)]
pub(crate) mod tests {
use std::ops::{Add, Mul, Sub};
use super::{
DenseMatrix, Matrix, MatrixCommon, MatrixSparsity, MatrixSparsityRef, MAX_SMALL_COLS,
};
use crate::scalar::Scale;
use crate::{scalar::IndexType, Context, Vector, VectorIndex, VectorView, VectorViewMut};
use num_traits::{FromPrimitive, One, Zero};
fn f<M: Matrix>(x: f64) -> M::T {
M::T::from_f64(x).unwrap()
}
fn at<M: DenseMatrix>(a: &M, batch: usize, i: IndexType, j: IndexType) -> M::T {
a.column(j).into_owned().get_batch(batch).get_index(i)
}
pub(crate) fn triplet_values<M: Matrix>(m: &M) -> Vec<M::T> {
let (_, vals) = m.triplet_iter();
vals.collect()
}
fn triplet_indices<M: Matrix>(m: &M) -> Vec<(IndexType, IndexType)> {
let (idx, _) = m.triplet_iter();
idx.collect()
}
pub fn test_partition_indices_by_zero_diagonal<M: Matrix>() {
let indices = vec![(0, 0), (1, 1), (3, 3)];
let values = vec![M::T::one(), M::T::from_f64(2.0).unwrap(), M::T::one()];
let m = M::try_from_triplets(4, 4, indices, values, Default::default()).unwrap();
let (zero_diagonal_indices, non_zero_diagonal_indices) =
m.partition_indices_by_zero_diagonal();
assert_eq!(zero_diagonal_indices.clone_as_vec(), vec![2]);
assert_eq!(non_zero_diagonal_indices.clone_as_vec(), vec![0, 1, 3]);
let indices = vec![(0, 0), (1, 1), (2, 2), (3, 3)];
let values = vec![
M::T::one(),
M::T::from_f64(2.0).unwrap(),
M::T::zero(),
M::T::one(),
];
let m = M::try_from_triplets(4, 4, indices, values, Default::default()).unwrap();
let (zero_diagonal_indices, non_zero_diagonal_indices) =
m.partition_indices_by_zero_diagonal();
assert_eq!(zero_diagonal_indices.clone_as_vec(), vec![2]);
assert_eq!(non_zero_diagonal_indices.clone_as_vec(), vec![0, 1, 3]);
let indices = vec![(0, 0), (1, 1), (2, 2), (3, 3)];
let values = vec![
M::T::one(),
M::T::from_f64(2.0).unwrap(),
M::T::from_f64(3.0).unwrap(),
M::T::one(),
];
let m = M::try_from_triplets(4, 4, indices, values, Default::default()).unwrap();
let (zero_diagonal_indices, non_zero_diagonal_indices) =
m.partition_indices_by_zero_diagonal();
assert_eq!(
zero_diagonal_indices.clone_as_vec(),
Vec::<IndexType>::new()
);
assert_eq!(non_zero_diagonal_indices.clone_as_vec(), vec![0, 1, 2, 3]);
let indices = vec![(3, 2)];
let values = vec![M::T::one()];
let m = M::try_from_triplets(4, 4, indices, values, Default::default()).unwrap();
let (zero_diagonal_indices, non_zero_diagonal_indices) =
m.partition_indices_by_zero_diagonal();
assert_eq!(zero_diagonal_indices.clone_as_vec(), vec![0, 1, 2, 3]);
assert_eq!(
non_zero_diagonal_indices.clone_as_vec(),
Vec::<IndexType>::new()
);
}
pub fn test_zeros<M: Matrix>() {
let a = M::zeros(2, 3, Default::default());
assert_eq!(a.nrows(), 2);
assert_eq!(a.ncols(), 3);
let vals = triplet_values(&a);
assert!(vals.is_empty() || vals.iter().all(|v| v.is_zero()));
assert_eq!(M::is_sparse(), a.sparsity().is_some());
}
pub fn test_sparsity<M: Matrix>() {
let a = M::Sparsity::try_from_indices(2, 2, vec![(0, 0), (1, 1)]).unwrap();
assert_eq!(MatrixSparsity::<M>::nrows(&a), 2);
assert_eq!(MatrixSparsity::<M>::ncols(&a), 2);
assert_eq!(
M::is_sparse(),
<M::Sparsity as MatrixSparsity<M>>::is_sparse()
);
let _ = a.indices();
let diag = M::Sparsity::new_diagonal(3);
assert_eq!(MatrixSparsity::<M>::nrows(&diag), 3);
assert_eq!(MatrixSparsity::<M>::ncols(&diag), 3);
let b = M::Sparsity::try_from_indices(2, 2, vec![(0, 1)]).unwrap();
let unioned = a.clone().union(b.as_ref()).unwrap();
assert_eq!(MatrixSparsity::<M>::nrows(&unioned), 2);
assert_eq!(MatrixSparsity::<M>::ncols(&unioned), 2);
let idx = a.get_index(&[(0, 0), (1, 1)], Default::default());
assert_eq!(idx.len(), 2);
let a_ref = a.as_ref();
assert_eq!(MatrixSparsityRef::<M>::nrows(&a_ref), 2);
assert_eq!(MatrixSparsityRef::<M>::ncols(&a_ref), 2);
assert_eq!(
M::is_sparse(),
<M::SparsityRef<'_> as MatrixSparsityRef<M>>::is_sparse()
);
let _ = a_ref.indices();
let owned = a_ref.to_owned();
assert_eq!(MatrixSparsity::<M>::nrows(&owned), 2);
let algebraic = <M::V as Vector>::Index::from_vec(vec![1], Default::default());
let [(ul, _), (_, _), (_, _), (lr, _)] = a_ref.split(&algebraic);
assert_eq!(MatrixSparsity::<M>::nrows(&ul), 1);
assert_eq!(MatrixSparsity::<M>::nrows(&lr), 1);
}
pub fn test_matrix_common_by_ref<M: Matrix>() {
fn via_common<T: MatrixCommon>(m: T) -> (IndexType, IndexType) {
let dims = (m.nrows(), m.ncols());
let _ = m.inner();
dims
}
let mut a = M::zeros(2, 3, Default::default());
assert_eq!(via_common(&a), (2, 3));
assert_eq!(via_common(&mut a), (2, 3));
}
pub fn test_from_diagonal<M: Matrix>() {
let v = M::V::from_vec(
vec![f::<M>(2.0), f::<M>(3.0), f::<M>(5.0)],
Default::default(),
);
let a = M::from_diagonal(&v);
assert_eq!(a.nrows(), 3);
assert_eq!(a.ncols(), 3);
let idx = triplet_indices(&a);
let vals = triplet_values(&a);
for &(i, j) in &idx {
let pos = idx.iter().position(|&x| x == (i, j)).unwrap();
if i == j {
assert!(
vals[pos] != M::T::zero(),
"diagonal entry should be non-zero"
);
} else {
assert!(vals[pos].is_zero(), "off-diagonal entry should be zero");
}
}
}
pub fn test_from_diagonal_dense<M: DenseMatrix>() {
let v = M::V::from_vec(
vec![f::<M>(2.0), f::<M>(3.0), f::<M>(5.0)],
Default::default(),
);
let a = M::from_diagonal(&v);
assert_eq!(a.nrows(), 3);
assert_eq!(a.ncols(), 3);
assert_eq!(a.get_index(0, 0), f::<M>(2.0));
assert_eq!(a.get_index(1, 1), f::<M>(3.0));
assert_eq!(a.get_index(2, 2), f::<M>(5.0));
assert_eq!(a.get_index(0, 1), f::<M>(0.0));
assert_eq!(a.get_index(1, 0), f::<M>(0.0));
}
pub fn test_gemv<M: Matrix>() {
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)];
let a = M::try_from_triplets(2, 2, indices, values, Default::default()).unwrap();
let x = M::V::from_vec(vec![f::<M>(1.0), f::<M>(2.0)], Default::default());
let mut y = M::V::zeros(2, Default::default());
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(5.0), f::<M>(11.0)]);
a.gemv(f::<M>(1.0), &x, f::<M>(1.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(10.0), f::<M>(22.0)]);
}
pub fn test_set_column<M: Matrix>() {
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(0.0), f::<M>(0.0), f::<M>(0.0), f::<M>(0.0)];
let mut a = M::try_from_triplets(2, 2, indices, values, Default::default()).unwrap();
let v = M::V::from_vec(vec![f::<M>(7.0), f::<M>(8.0)], Default::default());
a.set_column(1, &v);
let idx = triplet_indices(&a);
let vals = triplet_values(&a);
assert_eq!(idx, vec![(0, 0), (1, 0), (0, 1), (1, 1)]);
assert_eq!(
vals,
vec![f::<M>(0.0), f::<M>(0.0), f::<M>(7.0), f::<M>(8.0)]
);
}
pub fn test_copy_from<M: Matrix>() {
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
let a = M::try_from_triplets(2, 2, indices, values, Default::default()).unwrap();
let mut b = M::zeros(2, 2, Default::default());
b.copy_from(&a);
let vals = triplet_values(&b);
assert_eq!(
vals,
vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)]
);
}
pub fn test_scale_add_and_assign<M: Matrix>() {
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let x_vals = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
let y_vals = vec![f::<M>(10.0), f::<M>(20.0), f::<M>(30.0), f::<M>(40.0)];
let x = M::try_from_triplets(2, 2, indices.clone(), x_vals, Default::default()).unwrap();
let y = M::try_from_triplets(2, 2, indices, y_vals, Default::default()).unwrap();
let mut result = M::zeros(2, 2, Default::default());
result.copy_from(&x);
result.scale_add_and_assign(&x, f::<M>(2.0), &y);
let vals = triplet_values(&result);
assert_eq!(
vals,
vec![f::<M>(21.0), f::<M>(42.0), f::<M>(63.0), f::<M>(84.0)]
);
}
pub fn test_sparsity_dense_errors<M: DenseMatrix>() {
assert!(M::Sparsity::try_from_indices(0, 3, vec![]).is_err());
let a = M::Sparsity::try_from_indices(2, 2, vec![]).unwrap();
let b = M::Sparsity::try_from_indices(3, 3, vec![]).unwrap();
assert!(a.union(b.as_ref()).is_err());
}
pub fn test_update_backward_diff<M: DenseMatrix>() {
let order = 1;
let mut a = M::from_vec(
2,
4,
vec![
f::<M>(1.0),
f::<M>(10.0),
f::<M>(2.0),
f::<M>(20.0),
f::<M>(3.0),
f::<M>(30.0),
f::<M>(0.0),
f::<M>(0.0),
],
Default::default(),
);
let d = M::V::from_vec(vec![f::<M>(100.0), f::<M>(1000.0)], Default::default());
a.update_backward_diff(order, &d);
assert_eq!(a.get_index(0, 3), f::<M>(97.0));
assert_eq!(a.get_index(1, 3), f::<M>(970.0));
assert_eq!(a.get_index(0, 2), f::<M>(100.0));
assert_eq!(a.get_index(1, 2), f::<M>(1000.0));
assert_eq!(a.get_index(0, 1), f::<M>(102.0));
assert_eq!(a.get_index(1, 1), f::<M>(1020.0));
assert_eq!(a.get_index(0, 0), f::<M>(103.0));
assert_eq!(a.get_index(1, 0), f::<M>(1030.0));
}
fn rand_mat<M: DenseMatrix>(nrows: usize, ncols: usize, seed: f64) -> M {
let mut m = M::zeros(nrows, ncols, Default::default());
for j in 0..ncols {
let col = M::V::rand(nrows, seed * (j + 1) as f64, Default::default());
m.column_mut(j).copy_from(&col);
}
m
}
fn mat_dot<M: DenseMatrix>(a: &M, b: &M, cols: std::ops::Range<usize>) -> M::T {
cols.fold(M::T::zero(), |acc, j| {
acc + a.column(j).into_owned().dot(&b.column(j).into_owned())
})
}
fn assert_cols_eq<M: DenseMatrix>(a: &M, b: &M, cols: std::ops::Range<usize>) {
assert_eq!(a.nrows(), b.nrows());
for j in cols {
a.column(j)
.into_owned()
.assert_eq_st(&b.column(j).into_owned(), M::T::zero());
}
}
fn assert_mat_eq<M: DenseMatrix>(a: &M, b: &M) {
assert_eq!(a.ncols(), b.ncols());
assert_cols_eq(a, b, 0..a.ncols());
}
fn assert_close<M: Matrix>(a: M::T, b: M::T) {
use num_traits::Signed;
let scale = if a.abs() > b.abs() { a.abs() } else { b.abs() };
let tol = f::<M>(1e-4) * (M::T::one() + scale);
assert!((a - b).abs() <= tol, "{a} != {b}");
}
pub fn test_update_backward_diff_transpose<M: DenseMatrix>() {
let nrows = 3;
let ncols = MAX_SMALL_COLS;
for order in 1..=5 {
let d = M::V::rand(nrows, 0.7, Default::default());
let d_mat = rand_mat::<M>(nrows, ncols, 1.3);
let c0 = rand_mat::<M>(nrows, ncols, 2.9);
let mut u = d_mat.clone();
u.update_backward_diff(order, &d);
let lhs = mat_dot(&c0, &u, 0..ncols);
let mut c = c0.clone();
let mut d_bar = M::V::zeros(nrows, Default::default());
c.update_backward_diff_transpose(order, &mut d_bar);
let rhs = mat_dot(&c, &d_mat, 0..ncols) + d_bar.dot(&d);
assert_close::<M>(lhs, rhs);
assert_cols_eq(&c, &c0, order + 3..ncols);
}
}
pub fn test_mul_cols_by_transpose<M: DenseMatrix>() {
let nrows = 3;
let ncols = 5;
let ntot = 7;
let r = M::V::rand(ncols * ncols, 0.37, Default::default()).clone_as_vec();
let mut r_t = r.clone();
for j in 0..ncols {
for i in 0..ncols {
r_t[j * ncols + i] = r[i * ncols + j];
}
}
let d = rand_mat::<M>(nrows, ntot, 1.1);
let c = rand_mat::<M>(nrows, ntot, 2.3);
let mut a = d.clone();
let mut b = d.clone();
a.mul_cols_by_transpose(ncols, &r);
b.mul_cols_by(ncols, &r_t);
assert_mat_eq(&a, &b);
let mut dr = d.clone();
dr.mul_cols_by(ncols, &r);
let mut cr = c.clone();
cr.mul_cols_by_transpose(ncols, &r);
assert_close::<M>(mat_dot(&c, &dr, 0..ncols), mat_dot(&cr, &d, 0..ncols));
assert_cols_eq(&cr, &c, ncols..ntot);
}
pub fn test_ger_cols<M: DenseMatrix>() {
let mk = || {
M::from_vec(
2,
4,
(1..=4)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
Default::default(),
)
};
let y = M::V::from_vec(vec![f::<M>(1.0), f::<M>(-1.0)], Default::default());
let mut a = mk();
a.ger_cols(1, 3, f::<M>(2.0), &[f::<M>(3.0), f::<M>(5.0)], &y);
let expected = M::from_vec(
2,
4,
[1.0, 10.0, 8.0, 14.0, 13.0, 20.0, 4.0, 40.0]
.into_iter()
.map(f::<M>)
.collect(),
Default::default(),
);
assert_mat_eq(&a, &expected);
let mut a = mk();
a.ger_cols(2, 2, f::<M>(2.0), &[], &y);
assert_mat_eq(&a, &mk());
}
pub fn test_ger_cols_transpose<M: DenseMatrix>() {
let (nrows, ncols, start, end) = (3, 6, 1, 5);
let a = rand_mat::<M>(nrows, ncols, 0.9);
let y = M::V::rand(nrows, 1.7, Default::default());
let x = M::V::rand(end - start, 2.3, Default::default()).clone_as_vec();
let alpha = f::<M>(-0.6);
let mut update = M::zeros(nrows, ncols, Default::default());
update.ger_cols(start, end, alpha, &x, &y);
let mut ax = M::V::zeros(nrows, Default::default());
a.gemv_cols(start, end, alpha, &x, f::<M>(0.0), &mut ax);
assert_close::<M>(mat_dot(&update, &a, 0..ncols), y.dot(&ax));
}
pub fn test_gemv_cols<M: DenseMatrix>() {
let a = M::from_vec(
2,
4,
(1..=4)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
Default::default(),
);
let ones = |n: usize| vec![f::<M>(1.0); n];
let mut y = M::V::from_vec(vec![f::<M>(-1.0), f::<M>(-1.0)], Default::default());
a.gemv_cols(0, 3, f::<M>(1.0), &ones(3), f::<M>(0.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(6.0), f::<M>(60.0)]);
let w4 = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
a.gemv_cols(0, 4, f::<M>(1.0), &w4, f::<M>(0.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(30.0), f::<M>(300.0)]);
let w23 = vec![f::<M>(2.0), f::<M>(3.0)];
a.gemv_cols(2, 4, f::<M>(1.0), &w23, f::<M>(0.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(18.0), f::<M>(180.0)]);
a.gemv_cols(2, 4, f::<M>(2.0), &w23, f::<M>(0.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(36.0), f::<M>(360.0)]);
let half = vec![f::<M>(0.5)];
a.gemv_cols(1, 2, f::<M>(1.0), &half, f::<M>(0.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(1.0), f::<M>(10.0)]);
a.gemv_cols(1, 2, f::<M>(1.0), &half, f::<M>(1.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(2.0), f::<M>(20.0)]);
a.gemv_cols(1, 2, f::<M>(1.0), &half, f::<M>(3.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(7.0), f::<M>(70.0)]);
a.gemv_cols(1, 1, f::<M>(1.0), &ones(0), f::<M>(2.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(14.0), f::<M>(140.0)]);
a.gemv_cols(1, 1, f::<M>(1.0), &ones(0), f::<M>(1.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(14.0), f::<M>(140.0)]);
a.gemv_cols(1, 1, f::<M>(1.0), &ones(0), f::<M>(0.0), &mut y);
assert_eq!(y.clone_as_vec(), vec![f::<M>(0.0), f::<M>(0.0)]);
}
fn mat_at<M: DenseMatrix>(m: &M, b: usize, i: usize, j: usize) -> M::T {
m.column(j).into_owned().clone_as_vec()[b * m.nrows() + i]
}
pub fn test_mul_cols_by<M: DenseMatrix>() {
let mk = || {
M::from_vec(
2,
4,
(1..=4)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
Default::default(),
)
};
let untouched = mk();
let mut a = mk();
a.mul_cols_by(2, &[f::<M>(1.0), f::<M>(0.0), f::<M>(0.0), f::<M>(1.0)]);
assert_mat_eq(&a, &untouched);
let mut a = mk();
a.mul_cols_by(0, &[]);
assert_mat_eq(&a, &untouched);
let mut a = mk();
a.mul_cols_by(1, &[f::<M>(3.0)]);
let expected = M::from_vec(
2,
4,
vec![
f::<M>(3.0),
f::<M>(30.0),
f::<M>(2.0),
f::<M>(20.0),
f::<M>(3.0),
f::<M>(30.0),
f::<M>(4.0),
f::<M>(40.0),
],
Default::default(),
);
assert_mat_eq(&a, &expected);
let mut a = mk();
a.mul_cols_by(2, &[f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)]);
let expected = M::from_vec(
2,
4,
vec![
f::<M>(7.0),
f::<M>(70.0),
f::<M>(10.0),
f::<M>(100.0),
f::<M>(3.0),
f::<M>(30.0),
f::<M>(4.0),
f::<M>(40.0),
],
Default::default(),
);
assert_mat_eq(&a, &expected);
let ncols = 6;
let mut wide = M::from_vec(
2,
ncols,
(1..=ncols)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
Default::default(),
);
let wide_before = wide.clone();
let mut id = vec![f::<M>(0.0); ncols * ncols];
for j in 0..ncols {
id[j * ncols + j] = f::<M>(1.0);
}
wide.mul_cols_by(ncols, &id);
assert_mat_eq(&wide, &wide_before);
let nrows = 700;
let mut tall = M::from_vec(
nrows,
2,
(0..2 * nrows).map(|i| f::<M>(i as f64)).collect(),
Default::default(),
);
tall.mul_cols_by(2, &[f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)]);
let c0 = |i: usize| f::<M>(i as f64);
let c1 = |i: usize| f::<M>((nrows + i) as f64);
let expected = M::from_vec(
nrows,
2,
(0..nrows)
.map(|i| c0(i) + f::<M>(3.0) * c1(i))
.chain((0..nrows).map(|i| f::<M>(2.0) * c0(i) + f::<M>(4.0) * c1(i)))
.collect(),
Default::default(),
);
assert_mat_eq(&tall, &expected);
}
pub fn test_fill<M: DenseMatrix>(ctx: M::C) {
let (nrows, ncols) = (3, 4);
let mut m = M::zeros(nrows, ncols, ctx.clone());
for j in 0..ncols {
let col = M::V::rand(nrows, (j + 1) as f64, ctx.clone());
m.column_mut(j).copy_from(&col);
}
m.fill(f::<M>(2.5));
assert_eq!((m.nrows(), m.ncols()), (nrows, ncols));
let expected = M::V::from_element(nrows, f::<M>(2.5), ctx);
for j in 0..ncols {
m.column(j)
.into_owned()
.assert_eq_st(&expected, f::<M>(0.0));
}
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_mul_cols_by<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let mut a = M::from_vec(
2,
2,
(1..=4)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
ctx.clone(),
);
a.mul_cols_by(2, &[f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)]);
let expected = M::from_vec(
2,
2,
[7.0, 70.0, 10.0, 100.0, 15.0, 150.0, 22.0, 220.0]
.into_iter()
.map(f::<M>)
.collect(),
ctx,
);
assert_mat_eq(&a, &expected);
}
pub fn test_gemv_cols_many_columns<M: DenseMatrix>() {
let ncols = MAX_SMALL_COLS;
let a = M::from_vec(
2,
ncols,
(1..=ncols)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
Default::default(),
);
let x = vec![f::<M>(1.0); ncols];
let mut y = M::V::from_vec(vec![f::<M>(-1.0), f::<M>(-1.0)], Default::default());
a.gemv_cols(0, ncols, f::<M>(1.0), &x, f::<M>(0.0), &mut y);
let total = f::<M>((ncols * (ncols + 1) / 2) as f64);
assert_eq!(y.clone_as_vec(), vec![total, f::<M>(10.0) * total]);
}
pub fn test_batched_ger_cols<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let mut a = M::from_vec(
2,
2,
(1..=4)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
ctx.clone(),
);
let y = M::V::from_vec(
vec![f::<M>(1.0), f::<M>(0.0), f::<M>(0.0), f::<M>(1.0)],
ctx.clone(),
);
a.ger_cols(0, 2, f::<M>(1.0), &[f::<M>(2.0), f::<M>(3.0)], &y);
let expected = M::from_vec(
2,
2,
[3.0, 10.0, 5.0, 20.0, 3.0, 32.0, 4.0, 43.0]
.into_iter()
.map(f::<M>)
.collect(),
ctx,
);
assert_mat_eq(&a, &expected);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gemv_cols_m<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let a = M::from_vec(
2,
2,
(1..=4)
.flat_map(|j| [f::<M>(j as f64), f::<M>(10.0 * j as f64)])
.collect(),
ctx.clone(),
);
let mut y = M::V::from_vec(
vec![f::<M>(-1.0), f::<M>(-1.0), f::<M>(-1.0), f::<M>(-1.0)],
ctx,
);
let ones = vec![f::<M>(1.0), f::<M>(1.0)];
a.gemv_cols(0, 2, f::<M>(1.0), &ones, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(3.0), f::<M>(30.0), f::<M>(7.0), f::<M>(70.0)]
);
let w23 = vec![f::<M>(2.0), f::<M>(3.0)];
a.gemv_cols(0, 2, f::<M>(1.0), &w23, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(8.0), f::<M>(80.0), f::<M>(18.0), f::<M>(180.0)]
);
let half = vec![f::<M>(0.5)];
a.gemv_cols(1, 2, f::<M>(1.0), &half, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(1.0), f::<M>(10.0), f::<M>(2.0), f::<M>(20.0)]
);
a.gemv_cols(1, 2, f::<M>(1.0), &half, f::<M>(1.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(2.0), f::<M>(20.0), f::<M>(4.0), f::<M>(40.0)]
);
}
pub fn test_resize_cols<M: DenseMatrix>() {
let mut a = M::zeros(2, 2, Default::default());
a.set_index(0, 0, M::T::one());
a.set_index(0, 1, M::T::from_f64(2.0).unwrap());
a.set_index(1, 0, M::T::from_f64(3.0).unwrap());
a.set_index(1, 1, M::T::from_f64(4.0).unwrap());
a.resize_cols(2);
assert_eq!(a.ncols(), 2);
assert_eq!(a.get_index(0, 0), M::T::one());
a.resize_cols(3);
assert_eq!(a.ncols(), 3);
assert_eq!(a.nrows(), 2);
assert_eq!(a.get_index(0, 0), M::T::one());
assert_eq!(a.get_index(0, 1), M::T::from_f64(2.0).unwrap());
assert_eq!(a.get_index(1, 0), M::T::from_f64(3.0).unwrap());
assert_eq!(a.get_index(1, 1), M::T::from_f64(4.0).unwrap());
a.set_index(0, 2, M::T::from_f64(5.0).unwrap());
a.set_index(1, 2, M::T::from_f64(6.0).unwrap());
assert_eq!(a.get_index(0, 2), M::T::from_f64(5.0).unwrap());
assert_eq!(a.get_index(1, 2), M::T::from_f64(6.0).unwrap());
a.resize_cols(2);
assert_eq!(a.ncols(), 2);
assert_eq!(a.nrows(), 2);
assert_eq!(a.get_index(0, 0), M::T::one());
assert_eq!(a.get_index(0, 1), M::T::from_f64(2.0).unwrap());
assert_eq!(a.get_index(1, 0), M::T::from_f64(3.0).unwrap());
assert_eq!(a.get_index(1, 1), M::T::from_f64(4.0).unwrap());
}
pub fn test_from_vec<M: DenseMatrix>() {
let a = M::from_vec(
2,
2,
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)],
Default::default(),
);
assert_eq!(a.nrows(), 2);
assert_eq!(a.ncols(), 2);
assert_eq!(a.get_index(0, 0), f::<M>(1.0));
assert_eq!(a.get_index(1, 0), f::<M>(3.0));
assert_eq!(a.get_index(0, 1), f::<M>(2.0));
assert_eq!(a.get_index(1, 1), f::<M>(4.0));
}
pub fn test_column_view<M: DenseMatrix>() {
let a = M::from_vec(
2,
2,
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)],
Default::default(),
);
let col = a.column(1);
use crate::VectorView;
assert_eq!(col.get_index(0), f::<M>(2.0));
assert_eq!(col.get_index(1), f::<M>(4.0));
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_zeros_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let a = M::zeros(2, 3, ctx);
assert_eq!(a.nrows(), 2);
assert_eq!(a.ncols(), 3);
let vals = triplet_values(&a);
assert!(vals.is_empty() || vals.iter().all(|v| v.is_zero()));
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gemv_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![
f::<M>(1.0),
f::<M>(3.0),
f::<M>(2.0),
f::<M>(4.0), f::<M>(5.0),
f::<M>(7.0),
f::<M>(6.0),
f::<M>(8.0), ];
let a = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let x = M::V::from_vec(
vec![f::<M>(1.0), f::<M>(2.0), f::<M>(1.0), f::<M>(1.0)],
ctx.clone(),
);
let mut y = M::V::zeros(2, ctx);
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(5.0), f::<M>(11.0), f::<M>(11.0), f::<M>(15.0)]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gemv_broadcast_x_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![
f::<M>(1.0),
f::<M>(3.0),
f::<M>(2.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(7.0),
f::<M>(6.0),
f::<M>(8.0),
];
let a = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let x = M::V::from_vec(vec![f::<M>(1.0), f::<M>(2.0)], Default::default());
let mut y = M::V::zeros(2, ctx);
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(5.0), f::<M>(11.0), f::<M>(17.0), f::<M>(23.0)]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gemv_broadcast_mat_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)];
let a =
M::try_from_triplets(2, 2, indices, values, ctx.clone_with_nbatch(1).unwrap()).unwrap();
let x = M::V::from_vec(
vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)],
ctx.clone(),
);
let mut y = M::V::zeros(2, ctx);
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(5.0), f::<M>(11.0), f::<M>(11.0), f::<M>(25.0)]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_from_diagonal_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let v = M::V::from_vec(
vec![f::<M>(2.0), f::<M>(3.0), f::<M>(4.0), f::<M>(5.0)],
ctx,
);
let a = M::from_diagonal(&v);
assert_eq!(a.nrows(), 2);
assert_eq!(a.ncols(), 2);
let idx = triplet_indices(&a);
let vals = triplet_values(&a);
for &(i, j) in &idx {
let pos = idx.iter().position(|&x| x == (i, j)).unwrap();
if i == j {
assert!(
vals[pos] != M::T::zero(),
"diagonal entry should be non-zero"
);
} else {
assert!(vals[pos].is_zero(), "off-diagonal entry should be zero");
}
}
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_copy_from_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0),
];
let a = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let mut b = M::zeros(2, 2, ctx);
b.copy_from(&a);
let vals = triplet_values(&b);
assert_eq!(
vals,
vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_copy_from_broadcast_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
let a = M::try_from_triplets(2, 2, indices, values, M::C::default()).unwrap();
let mut b = M::zeros(2, 2, ctx);
b.copy_from(&a);
let vals = triplet_values(&b);
assert_eq!(
vals,
vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_set_column_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
];
let mut a = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let v = M::V::from_vec(
vec![f::<M>(5.0), f::<M>(6.0), f::<M>(7.0), f::<M>(8.0)],
ctx,
);
a.set_column(0, &v);
let vals = triplet_values(&a);
assert_eq!(
vals,
vec![
f::<M>(5.0),
f::<M>(6.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(7.0),
f::<M>(8.0),
f::<M>(0.0),
f::<M>(0.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_scale_add_and_assign_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let x_vals = vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0),
];
let y_vals = vec![
f::<M>(10.0),
f::<M>(20.0),
f::<M>(30.0),
f::<M>(40.0),
f::<M>(50.0),
f::<M>(60.0),
f::<M>(70.0),
f::<M>(80.0),
];
let x = M::try_from_triplets(2, 2, indices.clone(), x_vals, ctx.clone()).unwrap();
let y = M::try_from_triplets(2, 2, indices, y_vals, ctx.clone()).unwrap();
let mut result = M::zeros(2, 2, ctx);
result.copy_from(&x);
result.scale_add_and_assign(&x, f::<M>(2.0), &y);
let vals = triplet_values(&result);
assert_eq!(
vals,
vec![
f::<M>(21.0),
f::<M>(42.0),
f::<M>(63.0),
f::<M>(84.0),
f::<M>(105.0),
f::<M>(126.0),
f::<M>(147.0),
f::<M>(168.0),
]
);
}
pub fn test_owned_rhs_m<M>()
where
M: DenseMatrix,
for<'a> &'a M: Add<M, Output = M> + Sub<M, Output = M>,
{
let values = vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)];
let a = M::from_vec(
2,
2,
vec![f::<M>(10.0), f::<M>(30.0), f::<M>(20.0), f::<M>(40.0)],
Default::default(),
);
let b = M::from_vec(2, 2, values.clone(), Default::default());
let c = &a - b;
assert_eq!(c.get_index(0, 0), f::<M>(9.0));
assert_eq!(c.get_index(1, 1), f::<M>(36.0));
let b = M::from_vec(2, 2, values, Default::default());
let c = &a + b;
assert_eq!(c.get_index(0, 0), f::<M>(11.0));
assert_eq!(c.get_index(1, 1), f::<M>(44.0));
}
pub fn test_batched_owned_rhs_broadcast_m<M>(ctx: M::C)
where
M: Matrix + DenseMatrix,
for<'a> &'a M: Sub<M, Output = M>,
{
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let one_batch = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
let two_batches = vec![
f::<M>(10.0),
f::<M>(20.0),
f::<M>(30.0),
f::<M>(40.0),
f::<M>(50.0),
f::<M>(60.0),
f::<M>(70.0),
f::<M>(80.0),
];
let a = M::try_from_triplets(2, 2, indices.clone(), one_batch.clone(), M::C::default())
.unwrap();
let b =
M::try_from_triplets(2, 2, indices.clone(), two_batches.clone(), ctx.clone()).unwrap();
let c = &a - b;
assert_eq!(c.context().nbatch(), 2);
assert_eq!(
triplet_values(&c),
vec![
f::<M>(-9.0),
f::<M>(-18.0),
f::<M>(-27.0),
f::<M>(-36.0),
f::<M>(-49.0),
f::<M>(-58.0),
f::<M>(-67.0),
f::<M>(-76.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_owned_rhs_narrow_m<M>(ctx: M::C)
where
M: Matrix + DenseMatrix,
for<'a> &'a M: Sub<M, Output = M>,
{
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let wide = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let narrow = (1..=4).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let a = M::try_from_triplets(2, 2, indices.clone(), wide, ctx).unwrap();
let b = M::try_from_triplets(2, 2, indices, narrow, M::C::default()).unwrap();
let _ = &a - b;
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_owned_lhs_broadcast_m<M>(ctx: M::C)
where
M: Matrix + for<'a> Sub<&'a M, Output = M>,
{
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let one_batch = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
let two_batches = vec![
f::<M>(10.0),
f::<M>(20.0),
f::<M>(30.0),
f::<M>(40.0),
f::<M>(50.0),
f::<M>(60.0),
f::<M>(70.0),
f::<M>(80.0),
];
let a =
M::try_from_triplets(2, 2, indices.clone(), two_batches.clone(), ctx.clone()).unwrap();
let b = M::try_from_triplets(2, 2, indices.clone(), one_batch.clone(), M::C::default())
.unwrap();
let c = a - &b;
assert_eq!(c.context().nbatch(), 2);
assert_eq!(
triplet_values(&c),
vec![
f::<M>(9.0),
f::<M>(18.0),
f::<M>(27.0),
f::<M>(36.0),
f::<M>(49.0),
f::<M>(58.0),
f::<M>(67.0),
f::<M>(76.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_owned_lhs_narrow_m<M>(ctx: M::C)
where
M: Matrix + for<'a> Sub<&'a M, Output = M>,
{
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let narrow = (1..=4).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let wide = (1..=8).map(|i| f::<M>(10.0 * i as f64)).collect::<Vec<_>>();
let a = M::try_from_triplets(2, 2, indices.clone(), narrow, M::C::default()).unwrap();
let b = M::try_from_triplets(2, 2, indices, wide, ctx).unwrap();
let _ = a - &b;
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_scale_add_and_assign_broadcast_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let x_vals = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
let y_vals = vec![f::<M>(10.0), f::<M>(20.0), f::<M>(30.0), f::<M>(40.0)];
let x =
M::try_from_triplets(2, 2, indices.clone(), x_vals.clone(), M::C::default()).unwrap();
let y = M::try_from_triplets(2, 2, indices.clone(), y_vals, M::C::default()).unwrap();
let mut result_vals = x_vals.clone();
result_vals.extend(x_vals);
let mut result = M::try_from_triplets(2, 2, indices, result_vals, ctx).unwrap();
result.scale_add_and_assign(&x, f::<M>(2.0), &y);
let vals = triplet_values(&result);
assert_eq!(
vals,
vec![
f::<M>(21.0),
f::<M>(42.0),
f::<M>(63.0),
f::<M>(84.0),
f::<M>(21.0),
f::<M>(42.0),
f::<M>(63.0),
f::<M>(84.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_mul_scalar_ref_m<M>(ctx: M::C)
where
M: Matrix,
for<'a> &'a M: Mul<Scale<M::T>, Output = M>,
{
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let a = M::try_from_triplets(2, 2, indices, values, ctx).unwrap();
let result = &a * Scale(f::<M>(2.0));
assert_eq!(result.context().nbatch(), 2);
assert_eq!(
triplet_values(&result),
(1..=8).map(|i| f::<M>(2.0 * i as f64)).collect::<Vec<_>>()
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_add_assign_broadcast_m<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let mut a = M::from_vec(
2,
2,
(1..=8).map(|i| f::<M>(i as f64)).collect(),
ctx.clone(),
);
let b = M::from_vec(
2,
2,
(1..=4).map(|i| f::<M>(10.0 * i as f64)).collect(),
M::C::default(),
);
a += &b;
let expected = (1..=4)
.map(|i| f::<M>(i as f64 + 10.0 * i as f64))
.chain((5..=8).map(|i| f::<M>(i as f64 + 10.0 * (i - 4) as f64)))
.collect::<Vec<_>>();
assert_eq!(triplet_values(&a), expected);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_owned_lhs_add_broadcast_m<M>(ctx: M::C)
where
M: Matrix + for<'a> Add<&'a M, Output = M>,
{
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let narrow = M::try_from_triplets(
2,
2,
indices.clone(),
(1..=4).map(|i| f::<M>(i as f64)).collect(),
M::C::default(),
)
.unwrap();
let wide = M::try_from_triplets(
2,
2,
indices,
(1..=8).map(|i| f::<M>(10.0 * i as f64)).collect(),
ctx,
)
.unwrap();
let c = wide + &narrow;
assert_eq!(c.context().nbatch(), 2);
let expected = (1..=4)
.map(|i| f::<M>(10.0 * i as f64 + i as f64))
.chain((5..=8).map(|i| f::<M>(10.0 * i as f64 + (i - 4) as f64)))
.collect::<Vec<_>>();
assert_eq!(triplet_values(&c), expected);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gemv_beta_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(1.0), f::<M>(2.0), f::<M>(2.0)];
let a = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let x = M::V::from_vec(
vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)],
ctx.clone(),
);
let mut y = M::V::from_vec(
vec![f::<M>(1.0), f::<M>(1.0), f::<M>(1.0), f::<M>(1.0)],
ctx,
);
a.gemv(f::<M>(1.0), &x, f::<M>(2.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(3.0), f::<M>(4.0), f::<M>(8.0), f::<M>(10.0)]
);
}
fn ctx4<M: Matrix>(ctx2: &M::C) -> M::C {
assert_eq!(ctx2.nbatch(), 2);
ctx2.clone_with_nbatch(4).unwrap()
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_gemv_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(1.0), f::<M>(2.0), f::<M>(2.0)];
let a = M::try_from_triplets(2, 2, indices, values, ctx2).unwrap();
let x = M::V::from_vec((1..=8).map(|i| f::<M>(i as f64)).collect(), wide.clone());
let mut y = M::V::zeros(2, wide);
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(10.0),
f::<M>(12.0),
f::<M>(14.0),
f::<M>(16.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_gemv_x_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 1)];
let values = (1..=4).flat_map(|i| [f::<M>(i as f64); 2]).collect();
let a = M::try_from_triplets(2, 2, indices, values, wide.clone()).unwrap();
let x = M::V::from_vec((1..=4).map(|i| f::<M>(i as f64)).collect(), ctx2);
let mut y = M::V::zeros(2, wide);
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(2.0),
f::<M>(4.0),
f::<M>(9.0),
f::<M>(12.0),
f::<M>(12.0),
f::<M>(16.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_copy_from_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let a = M::try_from_triplets(2, 2, indices, values, ctx2).unwrap();
let mut b = M::zeros(2, 2, wide);
b.copy_from(&a);
let group0 = (1..=4).map(|i| f::<M>(i as f64));
let group1 = (5..=8).map(|i| f::<M>(i as f64));
let expected = group0
.clone()
.chain(group0)
.chain(group1.clone())
.chain(group1)
.collect::<Vec<_>>();
assert_eq!(triplet_values(&b), expected);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_scale_add_and_assign_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let x_vals = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let y_vals = (1..=8).map(|i| f::<M>(10.0 * i as f64)).collect::<Vec<_>>();
let x = M::try_from_triplets(2, 2, indices.clone(), x_vals, ctx2.clone()).unwrap();
let y = M::try_from_triplets(2, 2, indices.clone(), y_vals, ctx2).unwrap();
let dst_vals = (0..16).map(|_| f::<M>(0.0)).collect::<Vec<_>>();
let mut result = M::try_from_triplets(2, 2, indices, dst_vals, wide).unwrap();
result.scale_add_and_assign(&x, f::<M>(2.0), &y);
let group0 = (1..=4).map(|i| f::<M>(21.0 * i as f64));
let group1 = (5..=8).map(|i| f::<M>(21.0 * i as f64));
let expected = group0
.clone()
.chain(group0)
.chain(group1.clone())
.chain(group1)
.collect::<Vec<_>>();
assert_eq!(triplet_values(&result), expected);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_add_assign_m<M: DenseMatrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let mut a = M::from_vec(2, 2, (0..16).map(|_| f::<M>(0.0)).collect(), wide);
let b = M::from_vec(2, 2, (1..=8).map(|i| f::<M>(i as f64)).collect(), ctx2);
a += &b;
let group0 = (1..=4).map(|i| f::<M>(i as f64));
let group1 = (5..=8).map(|i| f::<M>(i as f64));
let expected = group0
.clone()
.chain(group0)
.chain(group1.clone())
.chain(group1)
.collect::<Vec<_>>();
assert_eq!(triplet_values(&a), expected);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_owned_lhs_add_m<M>(ctx2: M::C)
where
M: Matrix + for<'a> Add<&'a M, Output = M>,
{
let wide_ctx = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let narrow = M::try_from_triplets(
2,
2,
indices.clone(),
(1..=8).map(|i| f::<M>(i as f64)).collect(),
ctx2,
)
.unwrap();
let wide = M::try_from_triplets(
2,
2,
indices,
(0..16).map(|_| f::<M>(0.0)).collect(),
wide_ctx,
)
.unwrap();
let c = wide + &narrow;
assert_eq!(c.context().nbatch(), 4);
let group0 = (1..=4).map(|i| f::<M>(i as f64));
let group1 = (5..=8).map(|i| f::<M>(i as f64));
let expected = group0
.clone()
.chain(group0)
.chain(group1.clone())
.chain(group1)
.collect::<Vec<_>>();
assert_eq!(triplet_values(&c), expected);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_owned_lhs_narrow_m<M>(ctx2: M::C)
where
M: Matrix + for<'a> Add<&'a M, Output = M>,
{
let wide_ctx = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let narrow = M::try_from_triplets(
2,
2,
indices.clone(),
(1..=8).map(|i| f::<M>(i as f64)).collect(),
ctx2,
)
.unwrap();
let wide = M::try_from_triplets(
2,
2,
indices,
(0..16).map(|_| f::<M>(0.0)).collect(),
wide_ctx,
)
.unwrap();
let _ = narrow + &wide;
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_narrow_dest_gemv_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 1)];
let values = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let a = M::try_from_triplets(2, 2, indices, values, wide.clone()).unwrap();
let x = M::V::zeros(2, wide);
let mut y = M::V::zeros(2, ctx2);
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_narrow_dest_copy_from_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 1)];
let values = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let a = M::try_from_triplets(2, 2, indices, values, wide).unwrap();
let mut b = M::zeros(2, 2, ctx2);
b.copy_from(&a);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_narrow_dest_scale_add_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 1)];
let vals = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let x = M::try_from_triplets(2, 2, indices.clone(), vals.clone(), wide.clone()).unwrap();
let y = M::try_from_triplets(2, 2, indices.clone(), vals, wide).unwrap();
let dst = (1..=4).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let mut result = M::try_from_triplets(2, 2, indices, dst, ctx2).unwrap();
result.scale_add_and_assign(&x, f::<M>(2.0), &y);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_narrow_dest_gather_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 1)];
let values = (1..=8).map(|i| f::<M>(i as f64)).collect::<Vec<_>>();
let src = M::try_from_triplets(2, 2, indices.clone(), values, wide).unwrap();
let zeros = (0..4).map(|_| f::<M>(0.0)).collect::<Vec<_>>();
let mut dst = M::try_from_triplets(2, 2, indices, zeros, ctx2).unwrap();
let gather_indices = <M::V as Vector>::Index::from_vec(vec![0, 1], Default::default());
dst.gather(&src, &gather_indices);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_narrow_dest_set_data_with_indices_m<M: Matrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let indices = vec![(0, 0), (1, 1)];
let zeros = (0..4).map(|_| f::<M>(0.0)).collect::<Vec<_>>();
let mut mat = M::try_from_triplets(2, 2, indices, zeros, ctx2).unwrap();
let dst_indices = <M::V as Vector>::Index::from_vec(vec![0, 1], Default::default());
let src_indices = <M::V as Vector>::Index::from_vec(vec![0, 1], Default::default());
let data = M::V::from_vec((1..=8).map(|i| f::<M>(i as f64)).collect(), wide);
mat.set_data_with_indices(&dst_indices, &src_indices, &data);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_narrow_dest_add_assign_m<M: DenseMatrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let src = M::from_vec(2, 2, (0..16).map(|_| f::<M>(1.0)).collect(), wide);
let mut dst = M::zeros(2, 2, ctx2);
dst += &src;
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_narrow_dest_gemv_cols_m<M: DenseMatrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let a = M::from_vec(2, 2, (0..16).map(|_| f::<M>(1.0)).collect(), wide);
let mut y = M::V::zeros(2, ctx2);
let ones = vec![f::<M>(1.0), f::<M>(1.0)];
a.gemv_cols(0, 2, f::<M>(1.0), &ones, f::<M>(0.0), &mut y);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_grouped_gemv_cols_m<M: DenseMatrix>(ctx2: M::C) {
let wide = ctx4::<M>(&ctx2);
let a = M::from_vec(
2,
2,
vec![
f::<M>(1.0),
f::<M>(1.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(2.0),
f::<M>(2.0),
f::<M>(0.0),
f::<M>(0.0),
],
ctx2,
);
let mut y = M::V::zeros(2, wide);
let ones = vec![f::<M>(1.0), f::<M>(1.0)];
a.gemv_cols(0, 2, f::<M>(1.0), &ones, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![
f::<M>(1.0),
f::<M>(1.0),
f::<M>(1.0),
f::<M>(1.0),
f::<M>(2.0),
f::<M>(2.0),
f::<M>(2.0),
f::<M>(2.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_set_index_batch<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let mut a = M::zeros(2, 2, ctx);
let val = |b: usize, i: usize, j: usize| f::<M>((100 * b + 10 * i + j) as f64);
for b in 0..2 {
for i in 0..2 {
for j in 0..2 {
a.set_index_batch(b, i, j, val(b, i, j));
}
}
}
for b in 0..2 {
for i in 0..2 {
for j in 0..2 {
assert_eq!(mat_at(&a, b, i, j), val(b, i, j), "batch {b}, ({i}, {j})");
}
}
}
}
pub fn test_batched_from_vec<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let a = M::from_vec(
2,
2,
vec![
f::<M>(1.0),
f::<M>(3.0),
f::<M>(2.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(7.0),
f::<M>(6.0),
f::<M>(8.0),
],
ctx,
);
assert_eq!(a.nrows(), 2);
assert_eq!(a.ncols(), 2);
assert_eq!(at(&a, 0, 0, 0), f::<M>(1.0));
assert_eq!(at(&a, 0, 1, 0), f::<M>(3.0));
assert_eq!(at(&a, 0, 0, 1), f::<M>(2.0));
assert_eq!(at(&a, 0, 1, 1), f::<M>(4.0));
assert_eq!(at(&a, 1, 0, 0), f::<M>(5.0));
assert_eq!(at(&a, 1, 1, 0), f::<M>(7.0));
assert_eq!(at(&a, 1, 0, 1), f::<M>(6.0));
assert_eq!(at(&a, 1, 1, 1), f::<M>(8.0));
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gemv_o_broadcast_x<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let diff = M::from_vec(
2,
3,
vec![
f::<M>(1.0),
f::<M>(4.0),
f::<M>(2.0),
f::<M>(5.0),
f::<M>(3.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(10.0),
f::<M>(8.0),
f::<M>(11.0),
f::<M>(9.0),
f::<M>(12.0),
],
ctx.clone(),
);
let x = vec![f::<M>(1.0), f::<M>(1.0)];
let mut y = M::V::zeros(2, ctx);
diff.gemv_cols(0, 2, f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(3.0), f::<M>(9.0), f::<M>(15.0), f::<M>(21.0)]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gemv_incompatible<M: DenseMatrix>(ctx2: M::C, ctx3: M::C) {
assert_eq!(ctx2.nbatch(), 2);
assert_eq!(ctx3.nbatch(), 3);
let a = M::zeros(2, 2, ctx2.clone());
let x = M::V::zeros(2, ctx3);
let mut y = M::V::zeros(2, ctx2);
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_resize_cols<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let mut a = M::from_vec(
2,
2,
vec![
f::<M>(1.0),
f::<M>(3.0),
f::<M>(2.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(7.0),
f::<M>(6.0),
f::<M>(8.0),
],
ctx.clone(),
);
a.resize_cols(3);
assert_eq!(a.ncols(), 3);
assert_eq!(a.nrows(), 2);
assert_eq!(at(&a, 0, 0, 0), f::<M>(1.0));
assert_eq!(at(&a, 0, 1, 0), f::<M>(3.0));
assert_eq!(at(&a, 0, 0, 1), f::<M>(2.0));
assert_eq!(at(&a, 0, 1, 1), f::<M>(4.0));
assert_eq!(at(&a, 1, 0, 0), f::<M>(5.0));
assert_eq!(at(&a, 1, 1, 0), f::<M>(7.0));
assert_eq!(at(&a, 1, 0, 1), f::<M>(6.0));
assert_eq!(at(&a, 1, 1, 1), f::<M>(8.0));
assert_eq!(at(&a, 0, 0, 2), f::<M>(0.0));
assert_eq!(at(&a, 0, 1, 2), f::<M>(0.0));
assert_eq!(at(&a, 1, 0, 2), f::<M>(0.0));
assert_eq!(at(&a, 1, 1, 2), f::<M>(0.0));
let x = M::V::from_vec(
vec![
f::<M>(1.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(1.0),
f::<M>(0.0),
f::<M>(0.0),
],
ctx.clone(),
);
let mut y = M::V::zeros(2, ctx.clone());
a.gemv(f::<M>(1.0), &x, f::<M>(0.0), &mut y);
assert_eq!(
y.clone_as_vec(),
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(5.0), f::<M>(7.0)]
);
a.resize_cols(1);
assert_eq!(a.ncols(), 1);
assert_eq!(at(&a, 0, 0, 0), f::<M>(1.0));
assert_eq!(at(&a, 0, 1, 0), f::<M>(3.0));
assert_eq!(at(&a, 1, 0, 0), f::<M>(5.0));
assert_eq!(at(&a, 1, 1, 0), f::<M>(7.0));
let x2 = M::V::from_vec(vec![f::<M>(1.0), f::<M>(1.0)], ctx.clone());
let mut y2 = M::V::zeros(2, ctx);
a.gemv(f::<M>(1.0), &x2, f::<M>(0.0), &mut y2);
assert_eq!(
y2.clone_as_vec(),
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(5.0), f::<M>(7.0)]
);
}
pub fn test_mul_scalar<M: Matrix>() {
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)];
let a = M::try_from_triplets(2, 2, indices, values, Default::default()).unwrap();
let result = a * Scale(f::<M>(2.0));
let (_, vals) = result.triplet_iter();
let vals: Vec<_> = vals.collect();
assert_eq!(
vals,
vec![f::<M>(2.0), f::<M>(6.0), f::<M>(4.0), f::<M>(8.0)]
);
}
pub fn test_add_column_to_vector<M: Matrix>() {
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0)];
let mat = M::try_from_triplets(2, 2, indices, values, Default::default()).unwrap();
let mut v = M::V::zeros(2, Default::default());
mat.add_column_to_vector(1, &mut v);
assert_eq!(v.clone_as_vec(), vec![f::<M>(3.0), f::<M>(4.0)]);
}
pub fn test_add<M: DenseMatrix>() {
let a = M::from_vec(
2,
2,
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)],
Default::default(),
);
let b = M::from_vec(
2,
2,
vec![f::<M>(5.0), f::<M>(7.0), f::<M>(6.0), f::<M>(8.0)],
Default::default(),
);
let result = a + &b;
assert_eq!(result.get_index(0, 0), f::<M>(6.0));
assert_eq!(result.get_index(1, 1), f::<M>(12.0));
}
pub fn test_sub<M: DenseMatrix>() {
let a = M::from_vec(
2,
2,
vec![f::<M>(5.0), f::<M>(7.0), f::<M>(6.0), f::<M>(8.0)],
Default::default(),
);
let b = M::from_vec(
2,
2,
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)],
Default::default(),
);
let result = a - &b;
assert_eq!(result.get_index(0, 0), f::<M>(4.0));
assert_eq!(result.get_index(1, 1), f::<M>(4.0));
}
pub fn test_add_assign<M: DenseMatrix>() {
let mut a = M::from_vec(
2,
2,
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)],
Default::default(),
);
let b = M::from_vec(
2,
2,
vec![f::<M>(5.0), f::<M>(7.0), f::<M>(6.0), f::<M>(8.0)],
Default::default(),
);
a += &b;
assert_eq!(a.get_index(0, 0), f::<M>(6.0));
assert_eq!(a.get_index(1, 1), f::<M>(12.0));
}
pub fn test_sub_assign<M: DenseMatrix>() {
let mut a = M::from_vec(
2,
2,
vec![f::<M>(5.0), f::<M>(7.0), f::<M>(6.0), f::<M>(8.0)],
Default::default(),
);
let b = M::from_vec(
2,
2,
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)],
Default::default(),
);
a -= &b;
assert_eq!(a.get_index(0, 0), f::<M>(4.0));
assert_eq!(a.get_index(1, 1), f::<M>(4.0));
}
pub fn test_inner_mut<M: DenseMatrix>() {
let mut a = M::zeros(2, 2, Default::default());
let _ = a.inner_mut();
}
pub fn test_column_mut<M: DenseMatrix>() {
let mut a = M::from_vec(
2,
2,
vec![f::<M>(1.0), f::<M>(3.0), f::<M>(2.0), f::<M>(4.0)],
Default::default(),
);
a.column_mut(1).set_index(0, f::<M>(20.0));
assert_eq!(a.get_index(0, 1), f::<M>(20.0));
assert_eq!(a.get_index(1, 1), f::<M>(4.0));
}
pub fn test_gather<M: DenseMatrix>() {
let mat1 = M::from_vec(
3,
3,
vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0),
f::<M>(9.0),
],
Default::default(),
);
let mut mat2 = M::zeros(2, 2, Default::default());
let indices = <M::V as Vector>::Index::from_vec(vec![0, 1, 3, 4], Default::default());
mat2.gather(&mat1, &indices);
assert_eq!(mat2.get_index(0, 0), f::<M>(1.0));
assert_eq!(mat2.get_index(1, 0), f::<M>(2.0));
assert_eq!(mat2.get_index(0, 1), f::<M>(4.0));
assert_eq!(mat2.get_index(1, 1), f::<M>(5.0));
}
pub fn test_set_data_with_indices<M: DenseMatrix>() {
let mut mat = M::zeros(2, 2, Default::default());
let dst_indices = <M::V as Vector>::Index::from_vec(vec![0, 3], Default::default());
let src_indices = <M::V as Vector>::Index::from_vec(vec![0, 1], Default::default());
let data = M::V::from_vec(vec![f::<M>(5.0), f::<M>(6.0)], Default::default());
mat.set_data_with_indices(&dst_indices, &src_indices, &data);
assert_eq!(mat.get_index(0, 0), f::<M>(5.0));
assert_eq!(mat.get_index(1, 1), f::<M>(6.0));
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_combine<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
#[rustfmt::skip]
let data: Vec<M::T> = vec![
f::<M>(1.0), f::<M>(2.0), f::<M>(3.0), f::<M>(4.0),
f::<M>(5.0), f::<M>(6.0), f::<M>(7.0), f::<M>(8.0),
f::<M>(9.0), f::<M>(10.0), f::<M>(11.0), f::<M>(12.0),
f::<M>(13.0), f::<M>(14.0), f::<M>(15.0), f::<M>(16.0),
f::<M>(101.0), f::<M>(102.0), f::<M>(103.0), f::<M>(104.0),
f::<M>(105.0), f::<M>(106.0), f::<M>(107.0), f::<M>(108.0),
f::<M>(109.0), f::<M>(110.0), f::<M>(111.0), f::<M>(112.0),
f::<M>(113.0), f::<M>(114.0), f::<M>(115.0), f::<M>(116.0),
];
let m = M::from_vec(4, 4, data, ctx.clone());
let alg_indices = <M::V as Vector>::Index::from_vec(vec![1, 3], Default::default());
let [(ul, _), (ur, _), (ll, _), (lr, _)] = m.split(&alg_indices);
let recombined = M::combine(&ul, &ur, &ll, &lr, &alg_indices);
let (_orig_idx, orig_vals) = m.triplet_iter();
let (_recom_idx, recom_vals) = recombined.triplet_iter();
let orig_vals: Vec<_> = orig_vals.collect();
let recom_vals: Vec<_> = recom_vals.collect();
assert_eq!(orig_vals, recom_vals);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_add_column_to_vector_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0),
];
let mat = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let mut v = M::V::zeros(2, ctx);
mat.add_column_to_vector(1, &mut v);
assert_eq!(
v.clone_as_vec(),
vec![f::<M>(3.0), f::<M>(4.0), f::<M>(7.0), f::<M>(8.0)]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_add_columns_to_batched_vector_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0),
];
let mat = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let ctx4 = ctx.clone_with_nbatch(4).unwrap();
let mut v = M::V::zeros(2, ctx4);
mat.add_columns_to_batched_vector(&mut v);
assert_eq!(
v.clone_as_vec(),
vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0)
]
);
mat.add_columns_to_batched_vector(&mut v);
assert_eq!(v.clone_as_vec()[0], f::<M>(2.0));
assert_eq!(v.clone_as_vec()[7], f::<M>(16.0));
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_set_data_with_indices_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let zero_values = vec![
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
];
let mut mat = M::try_from_triplets(2, 2, indices, zero_values, ctx.clone()).unwrap();
let dst_indices = <M::V as Vector>::Index::from_vec(vec![0, 3], Default::default());
let src_indices = <M::V as Vector>::Index::from_vec(vec![0, 1], Default::default());
let data = M::V::from_vec(
vec![f::<M>(5.0), f::<M>(6.0), f::<M>(50.0), f::<M>(60.0)],
ctx,
);
mat.set_data_with_indices(&dst_indices, &src_indices, &data);
let (_, vals) = mat.triplet_iter();
let vals: Vec<_> = vals.collect();
assert_eq!(
vals,
vec![
f::<M>(5.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(6.0),
f::<M>(50.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(60.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_gather_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices: Vec<(IndexType, IndexType)> =
(0..3).flat_map(|j| (0..3).map(move |i| (i, j))).collect();
let values = vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(3.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(6.0),
f::<M>(7.0),
f::<M>(8.0),
f::<M>(9.0),
f::<M>(10.0),
f::<M>(20.0),
f::<M>(30.0),
f::<M>(40.0),
f::<M>(50.0),
f::<M>(60.0),
f::<M>(70.0),
f::<M>(80.0),
f::<M>(90.0),
];
let mat1 = M::try_from_triplets(3, 3, indices, values, ctx.clone()).unwrap();
let dest_indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let zero_values = vec![
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(0.0),
];
let mut mat2 = M::try_from_triplets(2, 2, dest_indices, zero_values, ctx).unwrap();
let gather_indices =
<M::V as Vector>::Index::from_vec(vec![0, 1, 3, 4], Default::default());
mat2.gather(&mat1, &gather_indices);
let (_, vals) = mat2.triplet_iter();
let vals: Vec<_> = vals.collect();
assert_eq!(
vals,
vec![
f::<M>(1.0),
f::<M>(2.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(10.0),
f::<M>(20.0),
f::<M>(40.0),
f::<M>(50.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_mul_scalar_m<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![
f::<M>(1.0),
f::<M>(3.0),
f::<M>(2.0),
f::<M>(4.0),
f::<M>(5.0),
f::<M>(7.0),
f::<M>(6.0),
f::<M>(8.0),
];
let a = M::try_from_triplets(2, 2, indices, values, ctx.clone()).unwrap();
let result = a * Scale(f::<M>(2.0));
let (_, vals) = result.triplet_iter();
let vals: Vec<_> = vals.collect();
assert_eq!(
vals,
vec![
f::<M>(2.0),
f::<M>(6.0),
f::<M>(4.0),
f::<M>(8.0),
f::<M>(10.0),
f::<M>(14.0),
f::<M>(12.0),
f::<M>(16.0),
]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_partition_indices<M: Matrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let zero_val = M::T::zero();
let one_val = f::<M>(1.0);
let two_val = f::<M>(2.0);
let indices = vec![(0, 0), (1, 1), (2, 2)];
let values = vec![one_val, zero_val, one_val, two_val, zero_val, two_val];
let m = M::try_from_triplets(3, 3, indices, values, ctx).unwrap();
let (zero_idx, nonzero_idx) = m.partition_indices_by_zero_diagonal();
assert_eq!(zero_idx.clone_as_vec(), vec![1]);
assert_eq!(nonzero_idx.clone_as_vec(), vec![0, 2]);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_update_backward_diff<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let order = 1;
let mut a = M::from_vec(
2,
4,
vec![
f::<M>(1.0),
f::<M>(10.0),
f::<M>(2.0),
f::<M>(20.0),
f::<M>(3.0),
f::<M>(30.0),
f::<M>(0.0),
f::<M>(0.0),
f::<M>(5.0),
f::<M>(50.0),
f::<M>(6.0),
f::<M>(60.0),
f::<M>(7.0),
f::<M>(70.0),
f::<M>(0.0),
f::<M>(0.0),
],
ctx.clone(),
);
let d = M::V::from_vec(
vec![f::<M>(100.0), f::<M>(1000.0), f::<M>(200.0), f::<M>(2000.0)],
ctx,
);
a.update_backward_diff(order, &d);
assert_eq!(
a.column(3).into_owned().clone_as_vec(),
vec![f::<M>(97.0), f::<M>(970.0), f::<M>(193.0), f::<M>(1930.0)]
);
assert_eq!(
a.column(2).into_owned().clone_as_vec(),
vec![f::<M>(100.0), f::<M>(1000.0), f::<M>(200.0), f::<M>(2000.0)]
);
assert_eq!(
a.column(1).into_owned().clone_as_vec(),
vec![f::<M>(102.0), f::<M>(1020.0), f::<M>(206.0), f::<M>(2060.0)]
);
assert_eq!(
a.column(0).into_owned().clone_as_vec(),
vec![f::<M>(103.0), f::<M>(1030.0), f::<M>(211.0), f::<M>(2110.0)]
);
}
#[cfg_attr(not(feature = "cuda"), allow(dead_code))]
pub fn test_batched_from_diagonal_dense<M: DenseMatrix>(ctx: M::C) {
assert_eq!(ctx.nbatch(), 2);
let v = M::V::from_vec(
vec![f::<M>(2.0), f::<M>(3.0), f::<M>(4.0), f::<M>(5.0)],
ctx,
);
let a = M::from_diagonal(&v);
assert_eq!(a.nrows(), 2);
assert_eq!(a.ncols(), 2);
assert_eq!(at(&a, 0, 0, 0), f::<M>(2.0));
assert_eq!(at(&a, 0, 1, 1), f::<M>(3.0));
assert_eq!(at(&a, 0, 0, 1), f::<M>(0.0));
assert_eq!(at(&a, 0, 1, 0), f::<M>(0.0));
assert_eq!(at(&a, 1, 0, 0), f::<M>(4.0));
assert_eq!(at(&a, 1, 1, 1), f::<M>(5.0));
assert_eq!(at(&a, 1, 0, 1), f::<M>(0.0));
assert_eq!(at(&a, 1, 1, 0), f::<M>(0.0));
}
pub fn test_try_from_triplets_wrong_length<M: Matrix>() {
let indices = vec![(0, 0), (1, 0), (0, 1), (1, 1)];
let values = vec![f::<M>(1.0), f::<M>(2.0), f::<M>(3.0)];
let _ = M::try_from_triplets(2, 2, indices, values, Default::default());
}
}
#[cfg(test)]
macro_rules! generate_matrix_tests_nonbatched {
($suffix:ident, $M:ty) => {
paste::paste! {
#[test]
fn [<test_zeros_ $suffix>]() {
$crate::matrix::tests::test_zeros::<$M>();
}
#[test]
fn [<test_sparsity_ $suffix>]() {
$crate::matrix::tests::test_sparsity::<$M>();
}
#[test]
fn [<test_matrix_common_by_ref_ $suffix>]() {
$crate::matrix::tests::test_matrix_common_by_ref::<$M>();
}
#[test]
fn [<test_from_diagonal_ $suffix>]() {
$crate::matrix::tests::test_from_diagonal::<$M>();
}
#[test]
fn [<test_gemv_ $suffix>]() {
$crate::matrix::tests::test_gemv::<$M>();
}
#[test]
fn [<test_set_column_ $suffix>]() {
$crate::matrix::tests::test_set_column::<$M>();
}
#[test]
fn [<test_copy_from_ $suffix>]() {
$crate::matrix::tests::test_copy_from::<$M>();
}
#[test]
fn [<test_scale_add_and_assign_ $suffix>]() {
$crate::matrix::tests::test_scale_add_and_assign::<$M>();
}
#[test]
fn [<test_partition_indices_ $suffix>]() {
$crate::matrix::tests::test_partition_indices_by_zero_diagonal::<$M>();
}
#[test]
fn [<test_mul_scalar_ $suffix>]() {
$crate::matrix::tests::test_mul_scalar::<$M>();
}
#[test]
fn [<test_add_column_to_vector_ $suffix>]() {
$crate::matrix::tests::test_add_column_to_vector::<$M>();
}
#[test]
#[should_panic]
fn [<test_try_from_triplets_wrong_length_ $suffix>]() {
$crate::matrix::tests::test_try_from_triplets_wrong_length::<$M>();
}
}
};
}
#[cfg(test)]
#[cfg_attr(not(feature = "cuda"), allow(unused_macros))]
macro_rules! generate_matrix_tests_batched {
($suffix:ident, $M:ty, $ctx1:expr, $ctx2:expr) => {
paste::paste! {
#[test]
fn [<test_batched_add_column_to_vector_ $suffix>]() {
$crate::matrix::tests::test_batched_add_column_to_vector_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_add_columns_to_batched_vector_ $suffix>]() {
$crate::matrix::tests::test_batched_add_columns_to_batched_vector_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_set_data_with_indices_ $suffix>]() {
$crate::matrix::tests::test_batched_set_data_with_indices_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_gather_ $suffix>]() {
$crate::matrix::tests::test_batched_gather_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_mul_scalar_ $suffix>]() {
$crate::matrix::tests::test_batched_mul_scalar_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_partition_indices_ $suffix>]() {
$crate::matrix::tests::test_batched_partition_indices::<$M>($ctx2);
}
#[test]
fn [<test_batched_zeros_ $suffix>]() {
$crate::matrix::tests::test_batched_zeros_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_gemv_ $suffix>]() {
$crate::matrix::tests::test_batched_gemv_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_gemv_broadcast_x_ $suffix>]() {
$crate::matrix::tests::test_batched_gemv_broadcast_x_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_gemv_broadcast_mat_ $suffix>]() {
$crate::matrix::tests::test_batched_gemv_broadcast_mat_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_from_diagonal_ $suffix>]() {
$crate::matrix::tests::test_batched_from_diagonal_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_copy_from_ $suffix>]() {
$crate::matrix::tests::test_batched_copy_from_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_copy_from_broadcast_ $suffix>]() {
$crate::matrix::tests::test_batched_copy_from_broadcast_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_set_column_ $suffix>]() {
$crate::matrix::tests::test_batched_set_column_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_scale_add_ $suffix>]() {
$crate::matrix::tests::test_batched_scale_add_and_assign_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_owned_lhs_broadcast_ $suffix>]() {
$crate::matrix::tests::test_batched_owned_lhs_broadcast_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_scale_add_broadcast_ $suffix>]() {
$crate::matrix::tests::test_batched_scale_add_and_assign_broadcast_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_mul_scalar_ref_ $suffix>]() {
$crate::matrix::tests::test_batched_mul_scalar_ref_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_owned_lhs_add_broadcast_ $suffix>]() {
$crate::matrix::tests::test_batched_owned_lhs_add_broadcast_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_gemv_beta_ $suffix>]() {
$crate::matrix::tests::test_batched_gemv_beta_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_batched_owned_lhs_narrow_ $suffix>]() {
$crate::matrix::tests::test_batched_owned_lhs_narrow_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_grouped_owned_lhs_narrow_ $suffix>]() {
$crate::matrix::tests::test_grouped_owned_lhs_narrow_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_narrow_dest_gemv_ $suffix>]() {
$crate::matrix::tests::test_narrow_dest_gemv_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_narrow_dest_copy_from_ $suffix>]() {
$crate::matrix::tests::test_narrow_dest_copy_from_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_narrow_dest_scale_add_ $suffix>]() {
$crate::matrix::tests::test_narrow_dest_scale_add_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_narrow_dest_gather_ $suffix>]() {
$crate::matrix::tests::test_narrow_dest_gather_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_narrow_dest_set_data_with_indices_ $suffix>]() {
$crate::matrix::tests::test_narrow_dest_set_data_with_indices_m::<$M>($ctx2);
}
#[test]
fn [<test_grouped_gemv_ $suffix>]() {
$crate::matrix::tests::test_grouped_gemv_m::<$M>($ctx2);
}
#[test]
fn [<test_grouped_gemv_x_ $suffix>]() {
$crate::matrix::tests::test_grouped_gemv_x_m::<$M>($ctx2);
}
#[test]
fn [<test_grouped_copy_from_ $suffix>]() {
$crate::matrix::tests::test_grouped_copy_from_m::<$M>($ctx2);
}
#[test]
fn [<test_grouped_owned_lhs_add_ $suffix>]() {
$crate::matrix::tests::test_grouped_owned_lhs_add_m::<$M>($ctx2);
}
#[test]
fn [<test_grouped_scale_add_ $suffix>]() {
$crate::matrix::tests::test_grouped_scale_add_and_assign_m::<$M>($ctx2);
}
}
};
}
#[cfg(test)]
macro_rules! generate_dense_matrix_tests_nonbatched {
($suffix:ident, $M:ty) => {
paste::paste! {
#[test]
fn [<test_from_vec_ $suffix>]() {
$crate::matrix::tests::test_from_vec::<$M>();
}
#[test]
fn [<test_from_diagonal_dense_ $suffix>]() {
$crate::matrix::tests::test_from_diagonal_dense::<$M>();
}
#[test]
fn [<test_column_view_ $suffix>]() {
$crate::matrix::tests::test_column_view::<$M>();
}
#[test]
fn [<test_sparsity_dense_errors_ $suffix>]() {
$crate::matrix::tests::test_sparsity_dense_errors::<$M>();
}
#[test]
fn [<test_update_backward_diff_ $suffix>]() {
$crate::matrix::tests::test_update_backward_diff::<$M>();
}
#[test]
fn [<test_update_backward_diff_transpose_ $suffix>]() {
$crate::matrix::tests::test_update_backward_diff_transpose::<$M>();
}
#[test]
fn [<test_mul_cols_by_transpose_ $suffix>]() {
$crate::matrix::tests::test_mul_cols_by_transpose::<$M>();
}
#[test]
fn [<test_gemv_cols_ $suffix>]() {
$crate::matrix::tests::test_gemv_cols::<$M>();
}
#[test]
fn [<test_ger_cols_ $suffix>]() {
$crate::matrix::tests::test_ger_cols::<$M>();
}
#[test]
fn [<test_ger_cols_transpose_ $suffix>]() {
$crate::matrix::tests::test_ger_cols_transpose::<$M>();
}
#[test]
fn [<test_gemv_cols_many_columns_ $suffix>]() {
$crate::matrix::tests::test_gemv_cols_many_columns::<$M>();
}
#[test]
fn [<test_mul_cols_by_ $suffix>]() {
$crate::matrix::tests::test_mul_cols_by::<$M>();
}
#[test]
fn [<test_fill_ $suffix>]() {
$crate::matrix::tests::test_fill::<$M>(Default::default());
}
#[test]
fn [<test_resize_cols_ $suffix>]() {
$crate::matrix::tests::test_resize_cols::<$M>();
}
#[test]
fn [<test_add_ $suffix>]() {
$crate::matrix::tests::test_add::<$M>();
}
#[test]
fn [<test_sub_ $suffix>]() {
$crate::matrix::tests::test_sub::<$M>();
}
#[test]
fn [<test_inner_mut_ $suffix>]() {
$crate::matrix::tests::test_inner_mut::<$M>();
}
#[test]
fn [<test_column_mut_ $suffix>]() {
$crate::matrix::tests::test_column_mut::<$M>();
}
#[test]
fn [<test_add_assign_ $suffix>]() {
$crate::matrix::tests::test_add_assign::<$M>();
}
#[test]
fn [<test_sub_assign_ $suffix>]() {
$crate::matrix::tests::test_sub_assign::<$M>();
}
#[test]
fn [<test_gather_ $suffix>]() {
$crate::matrix::tests::test_gather::<$M>();
}
#[test]
fn [<test_set_data_with_indices_ $suffix>]() {
$crate::matrix::tests::test_set_data_with_indices::<$M>();
}
}
};
}
#[cfg(test)]
#[cfg_attr(not(feature = "cuda"), allow(unused_macros))]
macro_rules! generate_dense_matrix_tests_batched {
($suffix:ident, $M:ty, $ctx1:expr, $ctx2:expr) => {
paste::paste! {
#[test]
fn [<test_batched_update_backward_diff_ $suffix>]() {
$crate::matrix::tests::test_batched_update_backward_diff::<$M>($ctx2);
}
#[test]
fn [<test_batched_gemv_cols_ $suffix>]() {
$crate::matrix::tests::test_batched_gemv_cols_m::<$M>($ctx2);
}
#[test]
fn [<test_batched_ger_cols_ $suffix>]() {
$crate::matrix::tests::test_batched_ger_cols::<$M>($ctx2);
}
#[test]
fn [<test_batched_mul_cols_by_ $suffix>]() {
$crate::matrix::tests::test_batched_mul_cols_by::<$M>($ctx2);
}
#[test]
fn [<test_batched_fill_ $suffix>]() {
$crate::matrix::tests::test_fill::<$M>($ctx2);
}
#[test]
fn [<test_batched_from_diagonal_dense_ $suffix>]() {
$crate::matrix::tests::test_batched_from_diagonal_dense::<$M>($ctx2);
}
#[test]
fn [<test_batched_from_vec_ $suffix>]() {
$crate::matrix::tests::test_batched_from_vec::<$M>($ctx2);
}
#[test]
fn [<test_batched_set_index_batch_ $suffix>]() {
$crate::matrix::tests::test_batched_set_index_batch::<$M>($ctx2);
}
#[test]
fn [<test_batched_gemv_o_broadcast_x_ $suffix>]() {
$crate::matrix::tests::test_batched_gemv_o_broadcast_x::<$M>($ctx2);
}
#[test]
fn [<test_batched_resize_cols_ $suffix>]() {
$crate::matrix::tests::test_batched_resize_cols::<$M>($ctx2);
}
#[test]
fn [<test_batched_combine_ $suffix>]() {
$crate::matrix::tests::test_batched_combine::<$M>($ctx2);
}
#[test]
fn [<test_batched_add_assign_broadcast_ $suffix>]() {
$crate::matrix::tests::test_batched_add_assign_broadcast_m::<$M>($ctx2);
}
#[test]
fn [<test_grouped_add_assign_ $suffix>]() {
$crate::matrix::tests::test_grouped_add_assign_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_narrow_dest_gemv_cols_ $suffix>]() {
$crate::matrix::tests::test_narrow_dest_gemv_cols_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_narrow_dest_add_assign_ $suffix>]() {
$crate::matrix::tests::test_narrow_dest_add_assign_m::<$M>($ctx2);
}
#[test]
fn [<test_grouped_gemv_cols_ $suffix>]() {
$crate::matrix::tests::test_grouped_gemv_cols_m::<$M>($ctx2);
}
#[test]
#[should_panic(expected = "incompatible nbatch")]
fn [<test_batched_gemv_incompatible_ $suffix>]() {
$crate::matrix::tests::test_batched_gemv_incompatible::<$M>($ctx2, $ctx1.clone_with_nbatch(3).unwrap());
}
}
};
}
#[cfg(test)]
#[cfg_attr(not(feature = "cuda"), allow(unused_imports))]
pub(crate) use generate_dense_matrix_tests_batched;
#[cfg(test)]
pub(crate) use generate_dense_matrix_tests_nonbatched;
#[cfg(test)]
#[cfg_attr(not(feature = "cuda"), allow(unused_imports))]
pub(crate) use generate_matrix_tests_batched;
#[cfg(test)]
pub(crate) use generate_matrix_tests_nonbatched;