1use std::ops::{Add, AddAssign, Index, IndexMut, Mul, Sub, SubAssign};
2
3use nalgebra::{
4 Const, DMatrix, DVectorView, MatrixView as NaMatrixView, MatrixViewMut as NaMatrixViewMut,
5};
6
7use super::default_solver::DefaultSolver;
8use super::sparsity::{Dense, DenseRef};
9use std::mem::MaybeUninit;
10
11use crate::context::broadcast_batch;
12use crate::error::LaError;
13use crate::{scalar::Scale, Context, NalgebraScalar, Vector};
14use crate::{
15 DenseMatrix, Matrix, MatrixCommon, NalgebraContext, NalgebraLU, NalgebraVec, NalgebraVecMut,
16 NalgebraVecRef, VectorIndex,
17};
18
19#[derive(Clone, Debug, PartialEq)]
24pub struct NalgebraMat<T: NalgebraScalar> {
25 pub(crate) data: DMatrix<T>,
26 pub(crate) context: NalgebraContext,
27}
28impl<T: NalgebraScalar> NalgebraMat<T> {
29 fn logical_ncols(&self) -> usize {
30 self.data.ncols() / self.context.nbatch()
31 }
32 fn col(&self, b: usize, j: usize) -> usize {
34 debug_assert!(b < self.context.nbatch(), "batch out of bounds");
35 b * self.logical_ncols() + j
36 }
37 fn col_bcast(&self, b: usize, j: usize, nbatch: usize) -> usize {
40 self.col(broadcast_batch(b, self.context.nbatch(), nbatch), j)
41 }
42}
43const MUL_COLS_TILE: usize = 1024;
53
54impl<T: NalgebraScalar> DefaultSolver for NalgebraMat<T> {
55 type LS = NalgebraLU<T>;
56}
57
58impl<T: NalgebraScalar> MatrixCommon for NalgebraMat<T> {
59 type T = T;
60 type V = NalgebraVec<T>;
61 type C = NalgebraContext;
62 type Inner = DMatrix<T>;
63 fn nrows(&self) -> usize {
64 self.data.nrows()
65 }
66 fn ncols(&self) -> usize {
67 self.logical_ncols()
68 }
69 fn inner(&self) -> &Self::Inner {
70 &self.data
71 }
72}
73macro_rules! binary_owned_lhs {
76 ($tr:ident, $fn:ident, $assign_tr:ident, $assign:ident, $rhs:ty) => {
77 impl<T: NalgebraScalar> $tr<$rhs> for NalgebraMat<T> {
78 type Output = NalgebraMat<T>;
79
80 fn $fn(mut self, rhs: $rhs) -> Self::Output {
81 $assign_tr::$assign(&mut self, rhs);
82 self
83 }
84 }
85 };
86}
87binary_owned_lhs!(Add, add, AddAssign, add_assign, &NalgebraMat<T>);
88binary_owned_lhs!(Sub, sub, SubAssign, sub_assign, &NalgebraMat<T>);
89
90macro_rules! binary_owned_rhs {
96 (commutes, $tr:ident, $fn:ident, $assign_tr:ident, $assign:ident, $lhs:ty, $op:tt,
97 $combine:expr) => {
98 impl<T: NalgebraScalar> $tr<NalgebraMat<T>> for $lhs {
99 type Output = NalgebraMat<T>;
100
101 fn $fn(self, mut rhs: NalgebraMat<T>) -> Self::Output {
102 $assign_tr::$assign(&mut rhs, self);
103 rhs
104 }
105 }
106 };
107 (noncommutes, $tr:ident, $fn:ident, $assign_tr:ident, $assign:ident, $lhs:ty, $op:tt, $combine:expr) => {
108 impl<T: NalgebraScalar> $tr<NalgebraMat<T>> for $lhs {
109 type Output = NalgebraMat<T>;
110
111 fn $fn(self, mut rhs: NalgebraMat<T>) -> Self::Output {
112 rhs.context
113 .assert_broadcastable_into(self.context.nbatch(), stringify!($fn));
114 let nc = self.ncols();
115 let nb = rhs.context.nbatch();
116 for b in 0..nb {
117 let mut columns = rhs.data.columns_mut(rhs.col(b, 0), nc);
118 columns.zip_apply(&self.data.columns(self.col_bcast(b, 0, nb), nc), $combine);
119 }
120 rhs
121 }
122 }
123 };
124}
125binary_owned_rhs!(
126 commutes, Add, add, AddAssign, add_assign, &NalgebraMat<T>, +=, |rhs: &mut T, lhs: T| *rhs += lhs
127);
128binary_owned_rhs!(
129 noncommutes, Sub, sub, SubAssign, sub_assign, &NalgebraMat<T>, -=,
130 |rhs: &mut T, lhs: T| *rhs = lhs - *rhs
131);
132macro_rules! assign {
133 ($tr:ident, $fn:ident, $l:ty, $r:ty, $op:tt) => {
134 impl<T: NalgebraScalar> $tr<$r> for $l {
135 fn $fn(&mut self, rhs: $r) {
136 self.context
137 .assert_broadcastable_into(rhs.context.nbatch(), stringify!($fn));
138 let nb = self.context.nbatch();
139 let nc = self.ncols();
140 if nb == rhs.context.nbatch()
141 && self.data.ncols() == nc * nb
142 && rhs.data.ncols() == nc * nb
143 {
144 self.data $op &rhs.data;
145 return;
146 }
147 for b in 0..nb {
148 let mut columns = self.data.columns_mut(self.col(b, 0), nc);
149 columns $op &rhs.data.columns(rhs.col_bcast(b, 0, nb), nc);
150 }
151 }
152 }
153 };
154}
155assign!(AddAssign, add_assign, NalgebraMat<T>, &NalgebraMat<T>, +=);
156assign!(SubAssign, sub_assign, NalgebraMat<T>, &NalgebraMat<T>, -=);
157impl<T: NalgebraScalar> Mul<Scale<T>> for NalgebraMat<T> {
158 type Output = Self;
159 fn mul(mut self, r: Scale<T>) -> Self {
160 self.data *= r.value();
161 self
162 }
163}
164macro_rules! scale_ref {
165 ($t:ty) => {
166 impl<T: NalgebraScalar> Mul<Scale<T>> for $t {
167 type Output = NalgebraMat<T>;
168 fn mul(self, r: Scale<T>) -> Self::Output {
169 NalgebraMat {
172 data: &self.data * r.value(),
173 context: self.context,
174 }
175 }
176 }
177 };
178}
179scale_ref!(&NalgebraMat<T>);
180macro_rules! ind {
181 ($t:ty) => {
182 impl<T: NalgebraScalar> Index<(usize, usize)> for $t {
183 type Output = T;
184 fn index(&self, x: (usize, usize)) -> &T {
185 assert_eq!(
186 self.context.nbatch(),
187 1,
188 "indexing not supported for batched matrices"
189 );
190 &self.data[(x.0, self.col(0, x.1))]
191 }
192 }
193 };
194}
195ind!(NalgebraMat<T>);
196impl<T: NalgebraScalar> IndexMut<(usize, usize)> for NalgebraMat<T> {
197 fn index_mut(&mut self, x: (usize, usize)) -> &mut T {
198 assert_eq!(
199 self.context.nbatch(),
200 1,
201 "indexing not supported for batched matrices"
202 );
203 let c = self.col(0, x.1);
204 &mut self.data[(x.0, c)]
205 }
206}
207
208macro_rules! gemv_data {
217 ($self:ident, $a:ident, $x:ident, $beta:ident, $y:ident, $op:literal) => {{
218 $y.context
219 .assert_broadcastable_into($self.context.nbatch(), $op);
220 $y.context
221 .assert_broadcastable_into($x.context.nbatch(), $op);
222 let nc = $self.ncols();
223 if $y.data.ncols() == 1 {
226 $y.data.column_mut(0).gemv(
227 $a,
228 &$self.data.columns($self.col(0, 0), nc),
229 &$x.data.column(0),
230 $beta,
231 );
232 return;
233 }
234 let nb = $y.data.ncols();
235 for b in 0..nb {
236 $y.data.column_mut(b).gemv(
237 $a,
238 &$self.data.columns($self.col_bcast(b, 0, nb), nc),
239 &$x.data.column($x.batch(b, nb)),
240 $beta,
241 );
242 }
243 }};
244}
245
246macro_rules! gemv_cols_data {
250 ($self:ident, $start:expr, $nc:expr, $a:ident, $x:ident, $beta:ident, $y:ident, $op:literal) => {{
251 $y.context
252 .assert_broadcastable_into($self.context.nbatch(), $op);
253 let (start, nc) = ($start, $nc);
254 debug_assert!(
258 start + nc <= $self.ncols(),
259 concat!($op, ": column range out of bounds")
260 );
261 let x = DVectorView::from_slice(&$x[..nc], nc);
262 if $y.data.ncols() == 1 {
263 $y.data
264 .column_mut(0)
265 .gemv($a, &$self.data.columns($self.col(0, start), nc), &x, $beta);
266 return;
267 }
268 let nb = $y.data.ncols();
269 for b in 0..nb {
270 $y.data.column_mut(b).gemv(
271 $a,
272 &$self.data.columns($self.col_bcast(b, start, nb), nc),
273 &x,
274 $beta,
275 );
276 }
277 }};
278}
279
280impl<T: NalgebraScalar> Matrix for NalgebraMat<T> {
281 type Sparsity = Dense<Self>;
282 type SparsityRef<'a> = DenseRef<'a, Self>;
283 fn sparsity(&self) -> Option<Self::SparsityRef<'_>> {
284 None
285 }
286 fn context(&self) -> &Self::C {
287 &self.context
288 }
289 fn inner_mut(&mut self) -> &mut Self::Inner {
290 &mut self.data
291 }
292 fn set_data_with_indices(
293 &mut self,
294 dst: &crate::vector::nalgebra_serial::NalgebraIndex,
295 src: &crate::vector::nalgebra_serial::NalgebraIndex,
296 data: &NalgebraVec<T>,
297 ) {
298 self.context
299 .assert_broadcastable_into(data.context.nbatch(), "set_data_with_indices");
300 let nb = self.context.nbatch();
301 for (d, s) in dst.data.iter().zip(src.data.iter()) {
302 let i = d % self.nrows();
303 let j = d / self.nrows();
304 for b in 0..nb {
305 let c = self.col(b, j);
306 self.data[(i, c)] = data.data[(*s, data.batch(b, nb))]
307 }
308 }
309 }
310 fn gather(&mut self, o: &Self, idx: &crate::vector::nalgebra_serial::NalgebraIndex) {
311 self.context
312 .assert_broadcastable_into(o.context.nbatch(), "gather");
313 let nb = self.context.nbatch();
314 for b in 0..nb {
315 for (d, s) in idx.data.iter().enumerate() {
316 let i = d % self.nrows();
317 let j = d / self.nrows();
318 let oi = s % o.nrows();
319 let oj = s / o.nrows();
320 let c = self.col(b, j);
321 self.data[(i, c)] = o.data[(oi, o.col_bcast(b, oj, nb))]
322 }
323 }
324 }
325 fn partition_indices_by_zero_diagonal(
326 &self,
327 ) -> (
328 crate::vector::nalgebra_serial::NalgebraIndex,
329 crate::vector::nalgebra_serial::NalgebraIndex,
330 ) {
331 let (mut z, mut nz) = (Vec::new(), Vec::new());
332 for i in 0..self.nrows() {
333 if self.data[(i, self.col(0, i))].is_zero() {
334 z.push(i)
335 } else {
336 nz.push(i)
337 }
338 }
339 (
340 crate::vector::nalgebra_serial::NalgebraIndex::from_vec(z, self.context),
341 crate::vector::nalgebra_serial::NalgebraIndex::from_vec(nz, self.context),
342 )
343 }
344 fn add_column_to_vector(&self, j: usize, v: &mut NalgebraVec<T>) {
345 v.context
346 .assert_broadcastable_into(self.context.nbatch(), "add_column_to_vector");
347 if self.context.nbatch() == 1 && v.context.nbatch() == 1 {
348 v.data
349 .column_mut(0)
350 .axpy(T::one(), &self.data.column(j), T::one());
351 return;
352 }
353 let nb = v.context.nbatch();
354 for b in 0..nb {
355 v.data.column_mut(b).axpy(
356 T::one(),
357 &self.data.column(self.col_bcast(b, j, nb)),
358 T::one(),
359 );
360 }
361 }
362 fn add_columns_to_batched_vector(&self, v: &mut NalgebraVec<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;
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: DMatrix::zeros(nr, nc * ctx.nbatch()),
410 context: ctx,
411 }
412 }
413 fn from_diagonal(v: &NalgebraVec<T>) -> Self {
414 if v.context.nbatch() == 1 {
415 return Self {
416 data: DMatrix::from_diagonal(&v.data.column(0)),
417 context: v.context,
418 };
419 }
420 let mut out = Self::zeros(v.len(), v.len(), v.context);
421 for b in 0..v.context.nbatch() {
422 out.data
423 .columns_mut(out.col(b, 0), out.ncols())
424 .set_diagonal(&v.data.column(b));
425 }
426 out
427 }
428 fn gemv(&self, a: T, x: &NalgebraVec<T>, beta: T, y: &mut NalgebraVec<T>) {
429 gemv_data!(self, a, x, beta, y, "gemv")
430 }
431 fn copy_from(&mut self, o: &Self) {
432 self.context
433 .assert_broadcastable_into(o.context.nbatch(), "copy_from");
434 if self.context.nbatch() == o.context.nbatch() {
435 self.data.copy_from(&o.data);
436 return;
437 }
438 let nb = self.context.nbatch();
439 for b in 0..nb {
440 let nc = self.ncols();
441 self.data
442 .columns_mut(self.col(b, 0), nc)
443 .copy_from(&o.data.columns(o.col_bcast(b, 0, nb), nc));
444 }
445 }
446 fn set_column(&mut self, j: usize, v: &NalgebraVec<T>) {
447 self.context
448 .assert_broadcastable_into(v.context.nbatch(), "set_column");
449 if self.context.nbatch() == 1 && v.context.nbatch() == 1 {
451 self.data.column_mut(j).copy_from(&v.data.column(0));
452 return;
453 }
454 let nb = self.context.nbatch();
455 for b in 0..nb {
456 self.data
457 .column_mut(self.col(b, j))
458 .copy_from(&v.data.column(v.batch(b, nb)));
459 }
460 }
461 fn scale_add_and_assign(&mut self, x: &Self, b: T, y: &Self) {
462 self.context
463 .assert_broadcastable_into(x.context.nbatch(), "scale_add_and_assign");
464 self.context
465 .assert_broadcastable_into(y.context.nbatch(), "scale_add_and_assign");
466 let nc = self.ncols();
467 let nb = self.context.nbatch();
468 if nb == 1 {
471 let mut columns = self.data.columns_mut(0, nc);
472 columns.copy_from(&x.data.columns(0, nc));
473 columns.zip_apply(&y.data.columns(0, nc), |s, y| {
474 *s += b * y;
475 });
476 return;
477 }
478 for batch in 0..nb {
479 let mut columns = self.data.columns_mut(self.col(batch, 0), nc);
480 columns.copy_from(&x.data.columns(x.col_bcast(batch, 0, nb), nc));
481 columns.zip_apply(&y.data.columns(y.col_bcast(batch, 0, nb), nc), |s, y| {
482 *s += b * y;
483 });
484 }
485 }
486 fn new_from_sparsity(nr: usize, nc: usize, _: Option<Self::Sparsity>, ctx: Self::C) -> Self {
487 Self::zeros(nr, nc, ctx)
488 }
489}
490impl<T: NalgebraScalar> DenseMatrix for NalgebraMat<T> {
491 fn resize_cols(&mut self, nc: usize) {
492 let old = self.ncols();
493 if old == nc {
494 return;
495 }
496 let mut d = DMatrix::zeros(self.nrows(), nc * self.context.nbatch());
497 for b in 0..self.context.nbatch() {
498 d.columns_mut(b * nc, old.min(nc))
499 .copy_from(&self.data.columns(self.col(b, 0), old.min(nc)));
500 }
501 self.data = d
502 }
503 fn get_index(&self, i: usize, j: usize) -> T {
504 assert_eq!(
505 self.context.nbatch(),
506 1,
507 "get_index not supported for batched matrices"
508 );
509 self.data[(i, self.col(0, j))]
510 }
511 fn gemv_cols(
512 &self,
513 start: usize,
514 end: usize,
515 alpha: T,
516 x: &[T],
517 beta: T,
518 y: &mut NalgebraVec<T>,
519 ) {
520 assert!(start <= end, "gemv_cols: column range start > end");
521 assert!(
522 end - start <= crate::matrix::MAX_SMALL_COLS,
523 "gemv_cols: column range exceeds MAX_SMALL_COLS"
524 );
525 assert!(
526 x.len() >= end - start,
527 "gemv_cols: x must hold at least end - start values"
528 );
529 gemv_cols_data!(self, start, end - start, alpha, x, beta, y, "gemv_cols")
530 }
531 fn from_vec(nr: usize, nc: usize, d: Vec<T>, ctx: Self::C) -> Self {
532 assert_eq!(d.len(), nr * nc * ctx.nbatch());
533 Self {
534 data: DMatrix::from_vec(nr, nc * ctx.nbatch(), d),
535 context: ctx,
536 }
537 }
538 fn column_mut(&mut self, i: usize) -> NalgebraVecMut<'_, T> {
539 if self.context.nbatch() == 1 {
540 return NalgebraVecMut {
541 data: self.data.columns_mut(i, 1),
542 context: self.context,
543 };
544 }
545 let nr = self.nrows();
546 let nc = self.ncols();
547 let nb = self.context.nbatch();
548 assert!(i < nc, "column index out of bounds");
549 let data = NaMatrixViewMut::from_slice_with_strides_generic(
551 &mut self.data.as_mut_slice()[i * nr..],
552 nalgebra::Dyn(nr),
553 nalgebra::Dyn(nb),
554 Const,
555 nalgebra::Dyn(nr * nc),
556 );
557 NalgebraVecMut {
558 data,
559 context: self.context,
560 }
561 }
562 fn set_index(&mut self, i: usize, j: usize, v: T) {
563 assert_eq!(
564 self.context.nbatch(),
565 1,
566 "set_index not supported for batched matrices"
567 );
568 let c = self.col(0, j);
569 self.data[(i, c)] = v;
570 }
571 fn set_index_batch(&mut self, batch: usize, i: usize, j: usize, value: T) {
572 let c = self.col(batch, j);
573 self.data[(i, c)] = value;
574 }
575 fn column(&self, i: usize) -> NalgebraVecRef<'_, T> {
576 if self.context.nbatch() == 1 {
577 return NalgebraVecRef {
578 data: self.data.columns(i, 1),
579 context: self.context,
580 };
581 }
582 let nr = self.nrows();
583 let nc = self.ncols();
584 let nb = self.context.nbatch();
585 assert!(i < nc, "column index out of bounds");
586 let data = NaMatrixView::from_slice_with_strides_generic(
588 &self.data.as_slice()[i * nr..],
589 nalgebra::Dyn(nr),
590 nalgebra::Dyn(nb),
591 Const,
592 nalgebra::Dyn(nr * nc),
593 );
594 NalgebraVecRef {
595 data,
596 context: self.context,
597 }
598 }
599 fn mul_cols_by(&mut self, ncols: usize, rhs: &[T]) {
600 assert!(
601 ncols <= self.logical_ncols(),
602 "mul_cols_by: column range out of bounds"
603 );
604 assert_eq!(
605 rhs.len(),
606 ncols * ncols,
607 "mul_cols_by: rhs must hold ncols * ncols values"
608 );
609 if ncols == 0 {
610 return;
611 }
612 assert!(
613 ncols <= crate::matrix::MAX_SMALL_COLS,
614 "mul_cols_by: ncols exceeds MAX_SMALL_COLS"
615 );
616 let nrows = self.nrows();
617 let nbatch = self.context.nbatch();
618 let logical_ncols = self.logical_ncols();
619 let tile = (MUL_COLS_TILE / ncols).max(1);
623 let mut buf: [MaybeUninit<T>; MUL_COLS_TILE] =
627 [const { MaybeUninit::uninit() }; MUL_COLS_TILE];
628 let data = self.data.as_mut_slice();
629 for b in 0..nbatch {
630 let base = b * logical_ncols * nrows;
631 let mut row = 0;
632 while row < nrows {
633 let rows = tile.min(nrows - row);
634 for j in 0..ncols {
635 let src = &data[base + j * nrows + row..][..rows];
636 unsafe {
640 std::ptr::copy_nonoverlapping(
641 src.as_ptr(),
642 buf.as_mut_ptr().add(j * rows) as *mut T,
643 rows,
644 );
645 }
646 }
647 let buf =
651 unsafe { &*(&buf[..rows * ncols] as *const [MaybeUninit<T>] as *const [T]) };
652 for j in 0..ncols {
656 let w = &rhs[j * ncols..][..ncols];
657 let dst = base + j * nrows + row;
658 for i in 0..rows {
659 let mut acc = T::zero();
660 for l in 0..ncols {
661 acc += buf[l * rows + i] * w[l];
662 }
663 data[dst + i] = acc;
664 }
665 }
666 row += rows;
667 }
668 }
669 }
670
671 fn update_backward_diff(&mut self, order: usize, d: &NalgebraVec<T>) {
672 assert!(order + 2 < self.logical_ncols(), "order out of bounds");
673 self.context
674 .assert_broadcastable_into(d.context.nbatch(), "update_backward_diff");
675 let nb = self.context.nbatch();
676 for b in 0..nb {
677 let base = self.col(b, 0);
678 let d_col = d.batch(b, nb);
679 {
681 let (src, mut dst) = self.data.columns_range_pair_mut(
682 base + order + 1..base + order + 2,
683 base + order + 2..base + order + 3,
684 );
685 dst.column_mut(0).copy_from(&d.data.column(d_col));
686 dst.column_mut(0).axpy(-T::one(), &src.column(0), T::one());
687 }
688 for i in (0..=order + 1).rev() {
690 let (mut dst, src) = self
691 .data
692 .columns_range_pair_mut(base + i..base + i + 1, base + i + 1..base + i + 2);
693 dst.column_mut(0).axpy(T::one(), &src.column(0), T::one());
694 }
695 }
696 }
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702
703 #[test]
704 fn test_update_backward_diff() {
705 super::super::tests::test_update_backward_diff::<NalgebraMat<f64>>();
706 }
707
708 #[test]
709 fn test_index_operator() {
710 let mut mat =
711 NalgebraMat::from_vec(2, 2, vec![1.0, 3.0, 2.0, 4.0], NalgebraContext::default());
712 assert_eq!(mat[(1, 0)], 3.0);
713 mat[(1, 0)] = 30.0;
714 assert_eq!(mat.get_index(1, 0), 30.0);
715 }
716
717 #[test]
718 fn test_partition_indices_by_zero_diagonal() {
719 super::super::tests::test_partition_indices_by_zero_diagonal::<NalgebraMat<f64>>();
720 }
721
722 #[test]
723 fn test_resize_cols() {
724 super::super::tests::test_resize_cols::<NalgebraMat<f64>>();
725 }
726
727 #[test]
728 fn test_owned_rhs() {
729 super::super::tests::test_owned_rhs_m::<NalgebraMat<f64>>();
730 }
731
732 #[test]
733 fn test_batched_owned_rhs_broadcast() {
734 super::super::tests::test_batched_owned_rhs_broadcast_m::<NalgebraMat<f64>>(
735 NalgebraContext::with_nbatch(2),
736 );
737 }
738
739 #[test]
740 #[should_panic(expected = "incompatible nbatch")]
741 fn test_batched_owned_rhs_narrow() {
742 super::super::tests::test_batched_owned_rhs_narrow_m::<NalgebraMat<f64>>(
743 NalgebraContext::with_nbatch(2),
744 );
745 }
746
747 super::super::generate_matrix_tests_nonbatched!(nalgebra, NalgebraMat<f64>);
748 super::super::generate_matrix_tests_batched!(
749 nalgebra,
750 NalgebraMat<f64>,
751 NalgebraContext::default(),
752 NalgebraContext::with_nbatch(2)
753 );
754 super::super::generate_dense_matrix_tests_nonbatched!(nalgebra, NalgebraMat<f64>);
755 super::super::generate_dense_matrix_tests_batched!(
756 nalgebra,
757 NalgebraMat<f64>,
758 NalgebraContext::default(),
759 NalgebraContext::with_nbatch(2)
760 );
761}