1use std::ops::{Add, AddAssign, Index, IndexMut, Mul, Sub, SubAssign};
2
3use super::default_solver::DefaultSolver;
4use super::sparsity::{Dense, DenseRef};
5use std::mem::MaybeUninit;
6
7use crate::context::broadcast_batch;
8use crate::error::LaError;
9use crate::{scalar::Scale, Context, FaerScalar, Vector};
10use crate::{
11 DenseMatrix, FaerContext, FaerLU, FaerVec, FaerVecIndex, FaerVecMut, FaerVecRef, Matrix,
12 MatrixCommon, VectorIndex,
13};
14
15use faer::reborrow::{Reborrow, ReborrowMut};
16use faer::{linalg::matmul::matmul, unzip, zip, Accum, Mat, MatMut, MatRef};
17
18#[derive(Clone, Debug, PartialEq)]
23pub struct FaerMat<T: FaerScalar> {
24 pub(crate) data: Mat<T>,
25 pub(crate) context: FaerContext,
26}
27impl<T: FaerScalar> FaerMat<T> {
28 fn logical_ncols(&self) -> usize {
29 self.data.ncols() / self.context.nbatch()
30 }
31 fn col(&self, b: usize, j: usize) -> usize {
33 debug_assert!(b < self.context.nbatch(), "batch out of bounds");
34 b * self.logical_ncols() + j
35 }
36 fn col_bcast(&self, b: usize, j: usize, nbatch: usize) -> usize {
39 self.col(broadcast_batch(b, self.context.nbatch(), nbatch), j)
40 }
41}
42const MUL_COLS_TILE: usize = 1024;
52
53impl<T: FaerScalar> DefaultSolver for FaerMat<T> {
54 type LS = FaerLU<T>;
55}
56
57impl<T: FaerScalar> MatrixCommon for FaerMat<T> {
58 type T = T;
59 type V = FaerVec<T>;
60 type C = FaerContext;
61 type Inner = Mat<T>;
62 fn nrows(&self) -> usize {
63 self.data.nrows()
64 }
65 fn ncols(&self) -> usize {
66 self.logical_ncols()
67 }
68 fn inner(&self) -> &Self::Inner {
69 &self.data
70 }
71}
72macro_rules! binary_owned_lhs {
75 ($tr:ident, $fn:ident, $assign_tr:ident, $assign:ident, $rhs:ty) => {
76 impl<T: FaerScalar> $tr<$rhs> for FaerMat<T> {
77 type Output = FaerMat<T>;
78
79 fn $fn(mut self, rhs: $rhs) -> Self::Output {
80 $assign_tr::$assign(&mut self, rhs);
81 self
82 }
83 }
84 };
85}
86binary_owned_lhs!(Add, add, AddAssign, add_assign, &FaerMat<T>);
87binary_owned_lhs!(Sub, sub, SubAssign, sub_assign, &FaerMat<T>);
88
89macro_rules! binary_owned_rhs {
95 (commutes, $tr:ident, $fn:ident, $assign_tr:ident, $assign:ident, $lhs:ty, $op:tt,
96 $combine:expr) => {
97 impl<T: FaerScalar> $tr<FaerMat<T>> for $lhs {
98 type Output = FaerMat<T>;
99
100 fn $fn(self, mut rhs: FaerMat<T>) -> Self::Output {
101 $assign_tr::$assign(&mut rhs, self);
102 rhs
103 }
104 }
105 };
106 (noncommutes, $tr:ident, $fn:ident, $assign_tr:ident, $assign:ident, $lhs:ty, $op:tt, $combine:expr) => {
107 impl<T: FaerScalar> $tr<FaerMat<T>> for $lhs {
108 type Output = FaerMat<T>;
109
110 fn $fn(self, mut rhs: FaerMat<T>) -> Self::Output {
111 rhs.context
112 .assert_broadcastable_into(self.context.nbatch(), stringify!($fn));
113 let nc = self.ncols();
114 let nb = rhs.context.nbatch();
115 for b in 0..nb {
116 let c = rhs.col(b, 0);
117 let columns = rhs.data.rb_mut().subcols_mut(c, nc);
118 zip!(
119 columns,
120 self.data.rb().subcols(self.col_bcast(b, 0, nb), nc)
121 )
122 .for_each(|unzip!(r, l)| $combine(r, *l));
123 }
124 rhs
125 }
126 }
127 };
128}
129binary_owned_rhs!(
130 commutes, Add, add, AddAssign, add_assign, &FaerMat<T>, +=, |rhs: &mut T, lhs: T| *rhs += lhs
131);
132binary_owned_rhs!(
133 noncommutes, Sub, sub, SubAssign, sub_assign, &FaerMat<T>, -=,
134 |rhs: &mut T, lhs: T| *rhs = lhs - *rhs
135);
136macro_rules! assign {
137 ($tr:ident, $fn:ident, $l:ty, $r:ty, $op:tt) => {
138 impl<T: FaerScalar> $tr<$r> for $l {
139 fn $fn(&mut self, rhs: $r) {
140 self.context
141 .assert_broadcastable_into(rhs.context.nbatch(), stringify!($fn));
142 let nb = self.context.nbatch();
143 let nc = self.ncols();
144 if nb == rhs.context.nbatch()
145 && self.data.ncols() == nc * nb
146 && rhs.data.ncols() == nc * nb
147 {
148 self.data $op rhs.data.rb();
149 return;
150 }
151 for b in 0..nb {
152 let c = self.col(b, 0);
153 let mut columns = self.data.rb_mut().subcols_mut(c, nc);
154 columns $op rhs.data.rb().subcols(rhs.col_bcast(b, 0, nb), nc);
155 }
156 }
157 }
158 };
159}
160assign!(AddAssign, add_assign, FaerMat<T>, &FaerMat<T>, +=);
161assign!(SubAssign, sub_assign, FaerMat<T>, &FaerMat<T>, -=);
162impl<T: FaerScalar> Mul<Scale<T>> for FaerMat<T> {
163 type Output = Self;
164 fn mul(mut self, r: Scale<T>) -> Self {
165 self.data *= faer::Scale(r.value());
166 self
167 }
168}
169macro_rules! scale_ref {
170 ($t:ty) => {
171 impl<T: FaerScalar> Mul<Scale<T>> for $t {
172 type Output = FaerMat<T>;
173 fn mul(self, r: Scale<T>) -> Self::Output {
174 FaerMat {
177 data: self.data.rb() * faer::Scale(r.value()),
178 context: self.context,
179 }
180 }
181 }
182 };
183}
184scale_ref!(&FaerMat<T>);
185macro_rules! ind {
186 ($t:ty) => {
187 impl<T: FaerScalar> Index<(usize, usize)> for $t {
188 type Output = T;
189 fn index(&self, x: (usize, usize)) -> &T {
190 assert_eq!(
191 self.context.nbatch(),
192 1,
193 "indexing not supported for batched matrices"
194 );
195 &self.data[(x.0, self.col(0, x.1))]
196 }
197 }
198 };
199}
200ind!(FaerMat<T>);
201impl<T: FaerScalar> IndexMut<(usize, usize)> for FaerMat<T> {
202 fn index_mut(&mut self, x: (usize, usize)) -> &mut T {
203 assert_eq!(
204 self.context.nbatch(),
205 1,
206 "indexing not supported for batched matrices"
207 );
208 let c = self.col(0, x.1);
209 &mut self.data[(x.0, c)]
210 }
211}
212
213macro_rules! gemv_data {
216 ($self:ident, $a:ident, $x:ident, $beta:ident, $y:ident, $op:literal) => {{
217 $y.context
218 .assert_broadcastable_into($self.context.nbatch(), $op);
219 $y.context
220 .assert_broadcastable_into($x.context.nbatch(), $op);
221 let nc = $self.ncols();
222 let nb = $y.data.ncols();
223 for b in 0..nb {
224 let mut column = $y.data.rb_mut().col_mut(b);
225 let accum = if $beta.is_zero() {
226 Accum::Replace
227 } else {
228 if !$beta.is_one() {
229 column *= faer::Scale($beta);
230 }
231 Accum::Add
232 };
233 matmul(
234 column,
235 accum,
236 $self.data.rb().subcols($self.col_bcast(b, 0, nb), nc),
237 $x.data.rb().col($x.batch(b, nb)),
238 $a,
239 $self.context.par,
240 );
241 }
242 }};
243}
244
245macro_rules! gemv_cols_data {
249 ($self:ident, $start:expr, $nc:expr, $a:ident, $x:ident, $beta:ident, $y:ident, $op:literal) => {{
250 $y.context
251 .assert_broadcastable_into($self.context.nbatch(), $op);
252 let (start, nc) = ($start, $nc);
253 debug_assert!(
257 start + nc <= $self.ncols(),
258 concat!($op, ": column range out of bounds")
259 );
260 if nc == 0 {
262 if $beta.is_zero() {
263 $y.fill(T::zero());
264 } else if !$beta.is_one() {
265 for b in 0..$y.data.ncols() {
266 let mut column = $y.data.rb_mut().col_mut(b);
267 column *= faer::Scale($beta);
268 }
269 }
270 } else {
271 let x = faer::ColRef::from_slice(&$x[..nc]);
272 let nb = $y.data.ncols();
273 for b in 0..nb {
274 let mut column = $y.data.rb_mut().col_mut(b);
275 let accum = if $beta.is_zero() {
276 Accum::Replace
277 } else {
278 if !$beta.is_one() {
279 column *= faer::Scale($beta);
280 }
281 Accum::Add
282 };
283 matmul(
284 column,
285 accum,
286 $self.data.rb().subcols($self.col_bcast(b, start, nb), nc),
287 x,
288 $a,
289 $self.context.par,
290 );
291 }
292 }
293 }};
294}
295
296impl<T: FaerScalar> Matrix for FaerMat<T> {
297 type Sparsity = Dense<Self>;
298 type SparsityRef<'a> = DenseRef<'a, Self>;
299 fn sparsity(&self) -> Option<Self::SparsityRef<'_>> {
300 None
301 }
302 fn context(&self) -> &Self::C {
303 &self.context
304 }
305 fn inner_mut(&mut self) -> &mut Self::Inner {
306 &mut self.data
307 }
308 fn set_data_with_indices(&mut self, dst: &FaerVecIndex, src: &FaerVecIndex, data: &FaerVec<T>) {
309 self.context
310 .assert_broadcastable_into(data.context.nbatch(), "set_data_with_indices");
311 let nb = self.context.nbatch();
312 for (d, s) in dst.data.iter().zip(src.data.iter()) {
313 let i = d % self.nrows();
314 let j = d / self.nrows();
315 for b in 0..nb {
316 let c = self.col(b, j);
317 self.data[(i, c)] = data.data[(*s, data.batch(b, nb))]
318 }
319 }
320 }
321 fn gather(&mut self, o: &Self, idx: &FaerVecIndex) {
322 self.context
323 .assert_broadcastable_into(o.context.nbatch(), "gather");
324 let nb = self.context.nbatch();
325 for b in 0..nb {
326 for (d, s) in idx.data.iter().enumerate() {
327 let i = d % self.nrows();
328 let j = d / self.nrows();
329 let oi = s % o.nrows();
330 let oj = s / o.nrows();
331 let c = self.col(b, j);
332 self.data[(i, c)] = o.data[(oi, o.col_bcast(b, oj, nb))]
333 }
334 }
335 }
336 fn partition_indices_by_zero_diagonal(&self) -> (FaerVecIndex, FaerVecIndex) {
337 let (mut z, mut nz) = (Vec::new(), Vec::new());
338 for i in 0..self.nrows() {
339 if self.data[(i, self.col(0, i))].is_zero() {
340 z.push(i)
341 } else {
342 nz.push(i)
343 }
344 }
345 (
346 FaerVecIndex::from_vec(z, self.context),
347 FaerVecIndex::from_vec(nz, self.context),
348 )
349 }
350 fn add_column_to_vector(&self, j: usize, v: &mut FaerVec<T>) {
351 v.context
352 .assert_broadcastable_into(self.context.nbatch(), "add_column_to_vector");
353 let nb = v.context.nbatch();
354 for b in 0..nb {
355 zip!(
356 v.data.rb_mut().col_mut(b),
357 self.data.rb().col(self.col_bcast(b, j, nb))
358 )
359 .for_each(|unzip!(v, column)| *v += *column);
360 }
361 }
362 fn add_columns_to_batched_vector(&self, v: &mut FaerVec<T>) {
363 assert_eq!(v.len(), self.nrows(), "row count mismatch");
364 assert_eq!(
365 v.context.nbatch(),
366 self.context.nbatch() * self.ncols(),
367 "batch count mismatch: the destination holds one lane per (batch, column)"
368 );
369 v.data += self.data.rb();
372 }
373 fn triplet_iter(
374 &self,
375 ) -> (
376 impl Iterator<Item = (usize, usize)> + '_,
377 impl Iterator<Item = T> + '_,
378 ) {
379 let pos: Vec<_> = (0..self.ncols())
380 .flat_map(|j| (0..self.nrows()).map(move |i| (i, j)))
381 .collect();
382 let val: Vec<_> = (0..self.context.nbatch())
383 .flat_map(|b| {
384 pos.iter()
385 .map(move |&(i, j)| self.data[(i, self.col(b, j))])
386 })
387 .collect();
388 (pos.into_iter(), val.into_iter())
389 }
390 fn try_from_triplets(
391 nr: usize,
392 nc: usize,
393 idx: Vec<(usize, usize)>,
394 v: Vec<T>,
395 ctx: Self::C,
396 ) -> Result<Self, LaError> {
397 assert_eq!(v.len(), idx.len() * ctx.nbatch());
398 let mut out = Self::zeros(nr, nc, ctx);
399 for b in 0..ctx.nbatch() {
400 for (k, &(i, j)) in idx.iter().enumerate() {
401 let c = out.col(b, j);
402 out.data[(i, c)] = v[b * idx.len() + k]
403 }
404 }
405 Ok(out)
406 }
407 fn zeros(nr: usize, nc: usize, ctx: Self::C) -> Self {
408 Self {
409 data: Mat::zeros(nr, nc * ctx.nbatch()),
410 context: ctx,
411 }
412 }
413 fn from_diagonal(v: &FaerVec<T>) -> Self {
414 let mut out = Self::zeros(v.len(), v.len(), v.context);
415 let nc = out.ncols();
416 for b in 0..v.context.nbatch() {
417 let c = out.col(b, 0);
418 out.data
419 .rb_mut()
420 .subcols_mut(c, nc)
421 .diagonal_mut()
422 .column_vector_mut()
423 .copy_from(v.data.rb().col(b));
424 }
425 out
426 }
427 fn gemv(&self, a: T, x: &FaerVec<T>, beta: T, y: &mut FaerVec<T>) {
428 gemv_data!(self, a, x, beta, y, "gemv")
429 }
430 fn copy_from(&mut self, o: &Self) {
431 self.context
432 .assert_broadcastable_into(o.context.nbatch(), "copy_from");
433 if self.context.nbatch() == o.context.nbatch() {
434 self.data.rb_mut().copy_from(o.data.rb());
435 return;
436 }
437 let nc = self.ncols();
438 let nb = self.context.nbatch();
439 for b in 0..nb {
440 let c = self.col(b, 0);
441 self.data
442 .rb_mut()
443 .subcols_mut(c, nc)
444 .copy_from(o.data.rb().subcols(o.col_bcast(b, 0, nb), nc));
445 }
446 }
447 fn set_column(&mut self, j: usize, v: &FaerVec<T>) {
448 self.context
449 .assert_broadcastable_into(v.context.nbatch(), "set_column");
450 let nb = self.context.nbatch();
451 for b in 0..nb {
452 let c = self.col(b, j);
453 self.data
454 .rb_mut()
455 .col_mut(c)
456 .copy_from(v.data.rb().col(v.batch(b, nb)));
457 }
458 }
459 fn scale_add_and_assign(&mut self, x: &Self, b: T, y: &Self) {
460 self.context
461 .assert_broadcastable_into(x.context.nbatch(), "scale_add_and_assign");
462 self.context
463 .assert_broadcastable_into(y.context.nbatch(), "scale_add_and_assign");
464 let nc = self.ncols();
465 let nb = self.context.nbatch();
466 for batch in 0..nb {
467 let c = self.col(batch, 0);
468 zip!(
469 self.data.rb_mut().subcols_mut(c, nc),
470 x.data.rb().subcols(x.col_bcast(batch, 0, nb), nc),
471 y.data.rb().subcols(y.col_bcast(batch, 0, nb), nc)
472 )
473 .for_each(|unzip!(s, x, y)| *s = *x + b * *y);
474 }
475 }
476 fn new_from_sparsity(nr: usize, nc: usize, _: Option<Self::Sparsity>, ctx: Self::C) -> Self {
477 Self::zeros(nr, nc, ctx)
478 }
479}
480impl<T: FaerScalar> DenseMatrix for FaerMat<T> {
481 fn fill(&mut self, value: T) {
482 self.data.rb_mut().fill(value);
483 }
484 fn resize_cols(&mut self, nc: usize) {
485 let old = self.ncols();
486 if old == nc {
487 return;
488 }
489 let keep = old.min(nc);
490 let mut d = Mat::zeros(self.nrows(), nc * self.context.nbatch());
491 for b in 0..self.context.nbatch() {
492 d.rb_mut()
493 .subcols_mut(b * nc, keep)
494 .copy_from(self.data.rb().subcols(self.col(b, 0), keep));
495 }
496 self.data = d
497 }
498 fn get_index(&self, i: usize, j: usize) -> T {
499 assert_eq!(
500 self.context.nbatch(),
501 1,
502 "get_index not supported for batched matrices"
503 );
504 self.data[(i, self.col(0, j))]
505 }
506 fn gemv_cols(&self, start: usize, end: usize, alpha: T, x: &[T], beta: T, y: &mut FaerVec<T>) {
507 assert!(start <= end, "gemv_cols: column range start > end");
508 assert!(
509 end - start <= crate::matrix::MAX_SMALL_COLS,
510 "gemv_cols: column range exceeds MAX_SMALL_COLS"
511 );
512 assert!(
513 x.len() >= end - start,
514 "gemv_cols: x must hold at least end - start values"
515 );
516 gemv_cols_data!(self, start, end - start, alpha, x, beta, y, "gemv_cols")
517 }
518 fn from_vec(nr: usize, nc: usize, d: Vec<T>, ctx: Self::C) -> Self {
519 assert_eq!(d.len(), nr * nc * ctx.nbatch());
520 Self {
521 data: Mat::from_fn(nr, nc * ctx.nbatch(), |i, j| d[j * nr + i]),
522 context: ctx,
523 }
524 }
525 fn column_mut(&mut self, i: usize) -> FaerVecMut<'_, T> {
526 let nc = self.ncols();
527 assert!(i < nc, "column index out of bounds");
528 if self.context.nbatch() == 1 {
529 return FaerVecMut {
530 data: self.data.rb_mut().subcols_mut(i, 1),
531 context: self.context,
532 };
533 }
534 let (nr, nb) = (self.nrows(), self.context.nbatch());
535 let stride = self.data.col_stride() * nc as isize;
538 let data = unsafe {
539 MatMut::from_raw_parts_mut(
540 self.data.rb_mut().ptr_at_mut(0, i),
541 nr,
542 nb,
543 self.data.row_stride(),
544 stride,
545 )
546 };
547 FaerVecMut {
548 data,
549 context: self.context,
550 }
551 }
552 fn set_index(&mut self, i: usize, j: usize, v: T) {
553 assert_eq!(
554 self.context.nbatch(),
555 1,
556 "set_index not supported for batched matrices"
557 );
558 let c = self.col(0, j);
559 self.data[(i, c)] = v;
560 }
561 fn set_index_batch(&mut self, batch: usize, i: usize, j: usize, value: T) {
562 let c = self.col(batch, j);
563 self.data[(i, c)] = value;
564 }
565 fn column(&self, i: usize) -> FaerVecRef<'_, T> {
566 let nc = self.ncols();
567 assert!(i < nc, "column index out of bounds");
568 if self.context.nbatch() == 1 {
569 return FaerVecRef {
570 data: self.data.rb().subcols(i, 1),
571 context: self.context,
572 };
573 }
574 let (nr, nb) = (self.nrows(), self.context.nbatch());
575 let stride = self.data.col_stride() * nc as isize;
578 let data = unsafe {
579 MatRef::from_raw_parts(
580 self.data.rb().ptr_at(0, i),
581 nr,
582 nb,
583 self.data.row_stride(),
584 stride,
585 )
586 };
587 FaerVecRef {
588 data,
589 context: self.context,
590 }
591 }
592 fn mul_cols_by(&mut self, ncols: usize, rhs: &[T]) {
593 assert!(
594 ncols <= self.logical_ncols(),
595 "mul_cols_by: column range out of bounds"
596 );
597 assert_eq!(
598 rhs.len(),
599 ncols * ncols,
600 "mul_cols_by: rhs must hold ncols * ncols values"
601 );
602 if ncols == 0 {
603 return;
604 }
605 assert!(
606 ncols <= crate::matrix::MAX_SMALL_COLS,
607 "mul_cols_by: ncols exceeds MAX_SMALL_COLS"
608 );
609 let nrows = self.nrows();
610 let nbatch = self.context.nbatch();
611 let tile = (MUL_COLS_TILE / ncols).max(1);
615 let mut buf: [MaybeUninit<T>; MUL_COLS_TILE] =
619 [const { MaybeUninit::uninit() }; MUL_COLS_TILE];
620 for b in 0..nbatch {
621 let base = self.col(b, 0);
622 let mut row = 0;
623 while row < nrows {
624 let rows = tile.min(nrows - row);
625 for j in 0..ncols {
626 let col = self.data.rb().col(base + j);
629 let src = &col.try_as_col_major().unwrap().as_slice()[row..][..rows];
630 unsafe {
634 std::ptr::copy_nonoverlapping(
635 src.as_ptr(),
636 buf.as_mut_ptr().add(j * rows) as *mut T,
637 rows,
638 );
639 }
640 }
641 let buf =
646 unsafe { &*(&buf[..rows * ncols] as *const [MaybeUninit<T>] as *const [T]) };
647 for j in 0..ncols {
648 let w = &rhs[j * ncols..][..ncols];
649 let mut dst = self.data.rb_mut().col_mut(base + j);
650 for i in 0..rows {
652 dst[row + i] = buf[i] * w[0];
653 }
654 for l in 1..ncols {
655 let src = &buf[l * rows..][..rows];
656 for i in 0..rows {
657 dst[row + i] += src[i] * w[l];
658 }
659 }
660 }
661 row += rows;
662 }
663 }
664 }
665
666 fn update_backward_diff(&mut self, order: usize, d: &FaerVec<T>) {
667 assert!(order + 2 < self.logical_ncols(), "order out of bounds");
668 self.context
669 .assert_broadcastable_into(d.context.nbatch(), "update_backward_diff");
670 let nb = self.context.nbatch();
671 for b in 0..nb {
672 let base = self.col(b, 0);
673 let d_col = d.batch(b, nb);
674 {
676 let src = base + order + 1;
677 let dst = base + order + 2;
678 let (left, right) = self.data.rb_mut().split_at_col_mut(dst);
679 zip!(right.col_mut(0), d.data.rb().col(d_col), left.col(src))
680 .for_each(|unzip!(out, dval, sval)| *out = *dval - *sval);
681 }
682 for i in (0..=order + 1).rev() {
684 let dst = base + i;
685 let src = base + i + 1;
686 let (left, right) = self.data.rb_mut().split_at_col_mut(src);
687 zip!(left.col_mut(dst), right.col(0)).for_each(|unzip!(d, s)| *d += *s);
688 }
689 }
690 }
691}
692
693#[cfg(test)]
694mod tests {
695 use super::*;
696
697 #[test]
698 fn test_update_backward_diff() {
699 super::super::tests::test_update_backward_diff::<FaerMat<f64>>();
700 }
701
702 #[test]
703 fn test_index_operator() {
704 let mut mat = FaerMat::from_vec(2, 2, vec![1.0, 3.0, 2.0, 4.0], FaerContext::default());
705 assert_eq!(mat[(1, 0)], 3.0);
706 mat[(1, 0)] = 30.0;
707 assert_eq!(mat.get_index(1, 0), 30.0);
708 }
709
710 #[test]
711 fn test_partition_indices_by_zero_diagonal() {
712 super::super::tests::test_partition_indices_by_zero_diagonal::<FaerMat<f64>>();
713 }
714
715 #[test]
716 fn test_resize_cols() {
717 super::super::tests::test_resize_cols::<FaerMat<f64>>();
718 }
719
720 #[test]
721 fn test_owned_rhs() {
722 super::super::tests::test_owned_rhs_m::<FaerMat<f64>>();
723 }
724
725 #[test]
726 fn test_batched_owned_rhs_broadcast() {
727 super::super::tests::test_batched_owned_rhs_broadcast_m::<FaerMat<f64>>(
728 FaerContext::with_nbatch(2),
729 );
730 }
731
732 #[test]
733 #[should_panic(expected = "incompatible nbatch")]
734 fn test_batched_owned_rhs_narrow() {
735 super::super::tests::test_batched_owned_rhs_narrow_m::<FaerMat<f64>>(
736 FaerContext::with_nbatch(2),
737 );
738 }
739
740 super::super::generate_matrix_tests_nonbatched!(faer, FaerMat<f64>);
741 super::super::generate_matrix_tests_nonbatched!(faer_f32, FaerMat<f32>);
742 super::super::generate_matrix_tests_batched!(
743 faer,
744 FaerMat<f64>,
745 FaerContext::default(),
746 FaerContext::with_nbatch(2)
747 );
748 super::super::generate_matrix_tests_batched!(
749 faer_f32,
750 FaerMat<f32>,
751 FaerContext::default(),
752 FaerContext::with_nbatch(2)
753 );
754 super::super::generate_dense_matrix_tests_nonbatched!(faer, FaerMat<f64>);
755 super::super::generate_dense_matrix_tests_nonbatched!(faer_f32, FaerMat<f32>);
756 super::super::generate_dense_matrix_tests_batched!(
757 faer,
758 FaerMat<f64>,
759 FaerContext::default(),
760 FaerContext::with_nbatch(2)
761 );
762 super::super::generate_dense_matrix_tests_batched!(
763 faer_f32,
764 FaerMat<f32>,
765 FaerContext::default(),
766 FaerContext::with_nbatch(2)
767 );
768}